From b280441b7c82fede3cf4a160ece95da418afb591 Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Wed, 18 May 2022 21:16:39 +0300
Subject: [PATCH 01/14] Refactor TS code snippet to interfaces
---
.../ModelGraph/ModelGraphBuilderTests.cs | 12 +
.../CodeSnippetsReflection.OpenAPI.csproj | 1 +
.../LanguageGenerators/TypeScriptGenerator.cs | 373 ++++++------------
.../ModelGraph/CodeProperty.cs | 10 +
.../ModelGraph/ModelGraphBuilder.cs | 249 ++++++++++++
.../ModelGraph/PropertyType.cs | 25 ++
.../ModelGraph/SnippetCodeGraph.cs | 67 ++++
.../OpenApiSchemaExtensions.cs | 3 +-
8 files changed, 497 insertions(+), 243 deletions(-)
create mode 100644 CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs
create mode 100644 CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs
create mode 100644 CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
create mode 100644 CodeSnippetsReflection.OpenAPI/ModelGraph/PropertyType.cs
create mode 100644 CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
diff --git a/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs b/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs
new file mode 100644
index 000000000..213bd51ae
--- /dev/null
+++ b/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeSnippetsReflection.OpenAPI.Test.ModelGraph
+{
+ public class ModelGraphBuilderTests
+ {
+ }
+}
diff --git a/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj b/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj
index d3b8310d4..3fa375f73 100644
--- a/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj
+++ b/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj
@@ -8,6 +8,7 @@
+
diff --git a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
index 347accb98..e1e89a35b 100644
--- a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
+++ b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
@@ -5,6 +5,7 @@
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
+using CodeSnippetsReflection.OpenAPI.ModelGraph;
using CodeSnippetsReflection.StringExtensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;
@@ -18,304 +19,192 @@ public class TypeScriptGenerator : ILanguageGenerator
/// Model of the Snippets info
/// String of the snippet in Javascript code
- ///
+ ///
- private const string clientVarName = "graphServiceClient";
- private const string clientVarType = "GraphServiceClient";
+ private const string ClientVarName = "graphServiceClient";
+ private const string ClientVarType = "GraphServiceClient";
+ private const string RequestHeadersVarName = "headers";
+ private const string RequestOptionsVarName = "options";
+ private const string RequestConfigurationVarName = "configuration";
+ private const string RequestParametersVarName = "requestParameters";
+ private const string RequestBodyVarName = "requestBody";
public string GenerateCodeSnippet(SnippetModel snippetModel)
{
if (snippetModel == null) throw new ArgumentNullException("Argument snippetModel cannot be null");
- var indentManager = new IndentManager();
+
+ var codeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
var snippetBuilder = new StringBuilder(
"//THIS SNIPPET IS A PREVIEW FOR THE KIOTA BASED SDK. NON-PRODUCTION USE ONLY" + Environment.NewLine +
- $"const {clientVarName} = {clientVarType}.init({{authProvider}});{Environment.NewLine}{Environment.NewLine}");
- var (requestPayload, payloadVarName) = GetRequestPayloadAndVariableName(snippetModel, indentManager);
- snippetBuilder.Append(requestPayload);
- var responseAssignment = snippetModel.ResponseSchema == null ? string.Empty : "const result = ";
-
- // add headers
- var (requestHeadersPayload, requestHeadersVarName) = GetRequestHeaders(snippetModel, indentManager);
- if (!string.IsNullOrEmpty(requestHeadersPayload)) snippetBuilder.Append(requestHeadersPayload);
+ $"const {ClientVarName} = {ClientVarType}.init({{authProvider}});{Environment.NewLine}{Environment.NewLine}");
- // add query parameters
- var (queryParamsPayload, queryParamsVarName) = GetRequestQueryParameters(snippetModel, indentManager);
- if (!string.IsNullOrEmpty(queryParamsPayload)) snippetBuilder.Append(queryParamsPayload);
-
- // add parameters
- var parametersList = GetActionParametersList(payloadVarName, queryParamsVarName, requestHeadersVarName);
- var methodName = snippetModel.Method.ToString().ToLower();
- snippetBuilder.AppendLine($"{responseAssignment}async () => {{");
- indentManager.Indent();
- snippetBuilder.AppendLine($"{indentManager.GetIndent()}await {clientVarName}.{GetFluentApiPath(snippetModel.PathNodes)}.{methodName}({parametersList});");
- indentManager.Unindent();
- snippetBuilder.AppendLine($"}}");
+ writeSnippet(codeGraph, snippetBuilder);
return snippetBuilder.ToString();
}
- private const string RequestHeadersVarName = "headers";
- private static (string, string) GetRequestHeaders(SnippetModel snippetModel, IndentManager indentManager)
+
+ private static void writeSnippet(SnippetCodeGraph codeGraph, StringBuilder builder)
{
- var payloadSB = new StringBuilder();
- var filteredHeaders = snippetModel.RequestHeaders.Where(h => !h.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
- .ToList();
- if (filteredHeaders.Any())
- {
- payloadSB.AppendLine($"{indentManager.GetIndent()}const {RequestHeadersVarName} = {{");
- indentManager.Indent();
- filteredHeaders.ForEach(h =>
- payloadSB.AppendLine($"{indentManager.GetIndent()}\"{h.Key}\": \"{h.Value.FirstOrDefault().Replace("\"", "\\\"")}\",")
- );
- indentManager.Unindent();
- payloadSB.AppendLine($"{indentManager.GetIndent()}}};");
- return (payloadSB.ToString(), RequestHeadersVarName);
- }
- return (default, default);
+ writeHeadersAndOptions(codeGraph, builder);
+ WriteParameters(codeGraph, builder);
+ WriteBody(codeGraph, builder);
+ builder.AppendLine("");
+
+ WriteExecutionStatement(
+ codeGraph,
+ builder,
+ codeGraph.HasBody() ? RequestBodyVarName : default,
+ codeGraph.HasParameters() ? RequestParametersVarName : default,
+ codeGraph.HasHeaders() || codeGraph.HasOptions() ? RequestConfigurationVarName : default
+ );
}
- private static string GetActionParametersList(params string[] parameters)
+
+ private static void writeHeadersAndOptions(SnippetCodeGraph codeGraph, StringBuilder builder)
{
- var nonEmptyParameters = parameters.Where(p => !string.IsNullOrEmpty(p));
- if (nonEmptyParameters.Any())
- return string.Join(", ", nonEmptyParameters.Aggregate((a, b) => $"{a}, {b}"));
- else return string.Empty;
+ if (!codeGraph.HasHeaders() && !codeGraph.HasOptions()) return;
+
+ var indentManager = new IndentManager();
+ builder.AppendLine($"const {RequestConfigurationVarName} = {{");
+ indentManager.Indent();
+ WriteHeader(codeGraph, builder, indentManager);
+ WriteOptions(codeGraph, builder, indentManager);
+ indentManager.Unindent();
+ builder.AppendLine($"{indentManager.GetIndent()}}};");
}
- private const string RequestParametersVarName = "requestParameters";
- private static (string, string) GetRequestQueryParameters(SnippetModel model, IndentManager indentManager)
+
+ private static void WriteHeader(SnippetCodeGraph codeGraph, StringBuilder builder, IndentManager indentManager)
{
- var payloadSB = new StringBuilder();
- if (!string.IsNullOrEmpty(model.QueryString))
+ if (codeGraph.HasHeaders())
{
- payloadSB.AppendLine($"{indentManager.GetIndent()}let {RequestParametersVarName} = {{");
+ builder.AppendLine($"{indentManager.GetIndent()}{RequestHeadersVarName} : {{");
indentManager.Indent();
- var (queryString, replacements) = ReplaceNestedOdataQueryParameters(model.QueryString);
- foreach (var queryParam in queryString.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries))
- {
- if (queryParam.Contains("="))
- {
- var kvPair = queryParam.Split('=', StringSplitOptions.RemoveEmptyEntries);
- payloadSB.AppendLine($"{indentManager.GetIndent()}{NormalizeQueryParameterName(kvPair[0])} : {GetQueryParameterValue(kvPair[1], replacements)},");
- }
- else
- payloadSB.AppendLine($"q.{indentManager.GetIndent()}{NormalizeQueryParameterName(queryParam)} = undefined;");
- }
+ foreach (var param in codeGraph.Headers)
+ builder.AppendLine($"{indentManager.GetIndent()}\"{param.Name}\": \"{param.Value.Replace("\"", "\\\"")}\",");
indentManager.Unindent();
- payloadSB.AppendLine($"{indentManager.GetIndent()}}};");
- return (payloadSB.ToString(), RequestParametersVarName);
+ builder.AppendLine($"{indentManager.GetIndent()}}}");
}
- return (default, default);
- }
- private static Regex nestedStatementRegex = new(@"(\w+)(\([^)]+\))", RegexOptions.IgnoreCase|RegexOptions.Compiled);
- private static (string, Dictionary) ReplaceNestedOdataQueryParameters(string queryParams)
- {
- var replacements = new Dictionary();
- var matches = nestedStatementRegex.Matches(queryParams);
- if (matches.Any())
- foreach (Match match in matches)
- {
- var key = match.Groups[1].Value;
- var value = match.Groups[2].Value;
- replacements.Add(key, value);
- queryParams = queryParams.Replace(value, string.Empty);
- }
- return (queryParams, replacements);
}
- private static string GetQueryParameterValue(string originalValue, Dictionary replacements)
+ private static void WriteOptions(SnippetCodeGraph codeGraph, StringBuilder builder, IndentManager indentManager)
{
- if (originalValue.Equals("true", StringComparison.OrdinalIgnoreCase) || originalValue.Equals("false", StringComparison.OrdinalIgnoreCase))
- return originalValue.ToLowerInvariant();
- else if (int.TryParse(originalValue, out var intValue))
- return intValue.ToString();
- else
+ if (codeGraph.HasOptions())
{
- var valueWithNested = originalValue.Split(',')
- .Select(v => replacements.ContainsKey(v) ? v + replacements[v] : v)
- .Aggregate((a, b) => $"{a},{b}");
- return $"\"{valueWithNested}\"";
+ if (codeGraph.HasHeaders())
+ builder.Append(",");
+
+ builder.AppendLine($"{indentManager.GetIndent()}{RequestOptionsVarName} : {{");
+ indentManager.Indent();
+ foreach (var param in codeGraph.Options)
+ builder.AppendLine($"{indentManager.GetIndent()}\"{param.Name}\": \"{param.Value.Replace("\"", "\\\"")}\",");
+ indentManager.Unindent();
+ builder.AppendLine($"{indentManager.GetIndent()}}}");
}
}
- private static string NormalizeQueryParameterName(string queryParam) => queryParam.TrimStart('$').ToFirstCharacterLowerCase();
- private const string RequestBodyVarName = "requestBody";
- private static (string, string) GetRequestPayloadAndVariableName(SnippetModel snippetModel, IndentManager indentManager)
+
+ private static void WriteParameters(SnippetCodeGraph codeGraph, StringBuilder builder)
{
- if (string.IsNullOrWhiteSpace(snippetModel?.RequestBody))
- return (default, default);
- if (indentManager == null) throw new ArgumentNullException(nameof(indentManager));
+ if (!codeGraph.HasParameters()) return;
- var payloadSB = new StringBuilder();
- switch (snippetModel.ContentType?.Split(';').First().ToLowerInvariant())
- {
- case "application/json":
- TryParseBody(snippetModel, payloadSB, indentManager);
- break;
- case "application/octet-stream":
- payloadSB.AppendLine($"using var {RequestBodyVarName} = new WebStream();");
- break;
- default:
- if(TryParseBody(snippetModel, payloadSB, indentManager)) //in case the content type header is missing but we still have a json payload
- break;
- else
- throw new InvalidOperationException($"Unsupported content type: {snippetModel.ContentType}");
- }
- var result = payloadSB.ToString();
- return (result, string.IsNullOrEmpty(result) ? string.Empty : RequestBodyVarName);
- }
- private static bool TryParseBody(SnippetModel snippetModel, StringBuilder payloadSB, IndentManager indentManager) {
- if(snippetModel.IsRequestBodyValid)
- try {
- using var parsedBody = JsonDocument.Parse(snippetModel.RequestBody, new JsonDocumentOptions { AllowTrailingCommas = true });
- var schema = snippetModel.RequestSchema;
- var className = schema.GetSchemaTitle().ToFirstCharacterUpperCase() ?? $"{snippetModel.Path.Split("/").Last().ToFirstCharacterUpperCase()}RequestBody";
- payloadSB.AppendLine($"const {RequestBodyVarName} = new {className}();");
- WriteJsonObjectValue(RequestBodyVarName, payloadSB, parsedBody.RootElement, schema, indentManager);
- return true;
- } catch (Exception ex) when (ex is JsonException || ex is ArgumentException) {
- // the payload wasn't json or poorly formatted
- }
- return false;
+ var indentManager = new IndentManager();
+ builder.AppendLine($"const {RequestParametersVarName} = {{");
+ indentManager.Indent();
+ foreach (var param in codeGraph.Parameters)
+ builder.AppendLine($"{indentManager.GetIndent()}{param.Name} : {param.Value},");
+ indentManager.Unindent();
+ builder.AppendLine($"{indentManager.GetIndent()}}};");
}
- private static void WriteAnonymousObjectValues(StringBuilder payloadSB, JsonElement value, OpenApiSchema schema, IndentManager indentManager, bool includePropertyAssignment = true)
+
+ private static void WriteExecutionStatement(SnippetCodeGraph codeGraph, StringBuilder builder, params string[] parameters)
{
- if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
- indentManager.Indent();
+ var methodName = codeGraph.HttpMethod.ToString().ToLower();
+ var responseAssignment = codeGraph.ResponseSchema == null ? string.Empty : "const result = ";
- var propertiesAndSchema = value.EnumerateObject()
- .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
- foreach (var propertyAndSchema in propertiesAndSchema)
- {
- var propertyName = propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase();
- var propertyAssignment = includePropertyAssignment ? $"{indentManager.GetIndent()} [\"{propertyName}\" , " : string.Empty;
- WriteProperty(string.Empty, payloadSB, propertyAndSchema.Item1.Value, propertyAndSchema.Item2, indentManager, propertyAssignment, "]", ",");
- }
+ var parametersList = GetActionParametersList(parameters);
+ var indentManager = new IndentManager();
+ builder.AppendLine($"{responseAssignment}async () => {{");
+ indentManager.Indent();
+ builder.AppendLine($"{indentManager.GetIndent()}await {ClientVarName}.{GetFluentApiPath(codeGraph.Nodes)}.{methodName}({parametersList});");
indentManager.Unindent();
+ builder.AppendLine($"}}");
}
- private static void WriteJsonObjectValue(String objectName, StringBuilder payloadSB, JsonElement value, OpenApiSchema schema, IndentManager indentManager, bool includePropertyAssignment = true)
+
+ private static void WriteBody(SnippetCodeGraph codeGraph, StringBuilder builder)
{
- if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
- indentManager.Indent();
- var propertiesAndSchema = value.EnumerateObject()
- .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
- foreach (var propertyAndSchema in propertiesAndSchema.Where(x => x.Item2 != null))
+ if (codeGraph.Body.PropertyType == PropertyType.Default) return;
+
+ var indentManager = new IndentManager();
+
+ if (codeGraph.Body.PropertyType == PropertyType.Binary)
{
- var propertyName = propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase();
- var propertyAssignment = includePropertyAssignment ? $"{objectName}.{propertyName} = " : string.Empty;
- WriteProperty($"{objectName}.{propertyName}", payloadSB, propertyAndSchema.Item1.Value, propertyAndSchema.Item2, indentManager, propertyAssignment);
+ builder.AppendLine($"{indentManager.GetIndent()}const {RequestBodyVarName} = new WebStream();");
}
- var propertiesWithoutSchema = propertiesAndSchema.Where(x => x.Item2 == null).Select(x => x.Item1);
- if (propertiesWithoutSchema.Any())
+ else
{
- payloadSB.AppendLine($"{objectName}.additionalData = {{");
+ builder.AppendLine($"{indentManager.GetIndent()}const {RequestBodyVarName} : {codeGraph.Body.Name} = {{");
indentManager.Indent();
-
- int elementIndex = 0;
- var lastIndex = propertiesWithoutSchema.Count() - 1;
- foreach (var property in propertiesWithoutSchema)
- {
- var propertyAssignment = $"{indentManager.GetIndent()} \"{property.Name}\" : ";
- WriteProperty(objectName, payloadSB, property.Value, null, indentManager, propertyAssignment, "", (lastIndex == elementIndex) ? default : ",");
- elementIndex++;
- }
+ WriteCodePropertyObject(builder, codeGraph.Body, indentManager);
indentManager.Unindent();
- payloadSB.AppendLine($"{indentManager.GetIndent()} }}");
+ builder.AppendLine($"}};");
}
- indentManager.Unindent();
}
- private static void WriteProperty(String objectName, StringBuilder payloadSB, JsonElement value, OpenApiSchema propSchema, IndentManager indentManager, string propertyAssignment, string propertySuffix = default, string terminateLine = ";", bool objectPropertyAssign = true)
+
+ private static void WriteCodePropertyObject(StringBuilder builder, CodeProperty codeProperty, IndentManager indentManager)
{
- switch (value.ValueKind)
+ foreach (var child in codeProperty.Children)
{
- case JsonValueKind.String:
- if (propSchema?.Format?.Equals("base64url", StringComparison.OrdinalIgnoreCase) ?? false)
- payloadSB.AppendLine($"{propertyAssignment}btoa(\"{value.GetString()}\"){propertySuffix}{terminateLine}");
- else if (propSchema?.Format?.Equals("date-time", StringComparison.OrdinalIgnoreCase) ?? false)
- payloadSB.AppendLine($"{propertyAssignment} new Date(\"{value.GetString()}\"){propertySuffix}{terminateLine}");
- else
- {
- var enumSchema = propSchema?.AnyOf.FirstOrDefault(x => x.Enum.Count > 0);
- if(enumSchema == null)
+ switch (child.PropertyType)
+ {
+ case PropertyType.Object:
+ case PropertyType.Map:
+ if (codeProperty.PropertyType == PropertyType.Array)
{
- payloadSB.AppendLine($"{propertyAssignment}\"{value.GetString()}\"{propertySuffix}{terminateLine}");
+ builder.AppendLine($"{indentManager.GetIndent()}{{");
}
else
{
- payloadSB.AppendLine($"{propertyAssignment}{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}{propertySuffix}{terminateLine}");
+ builder.AppendLine($"{indentManager.GetIndent()}{child.Name.ToFirstCharacterLowerCase()} : {{");
}
- }
- break;
- case JsonValueKind.Number:
- payloadSB.AppendLine($"{propertyAssignment}{value}{propertySuffix}{terminateLine}");
- break;
- case JsonValueKind.False:
- case JsonValueKind.True:
- payloadSB.AppendLine($"{propertyAssignment}{value.GetBoolean().ToString().ToLowerInvariant()}{propertySuffix}{terminateLine}");
- break;
- case JsonValueKind.Null:
- payloadSB.AppendLine($"{propertyAssignment}null{propertySuffix},");
- break;
- case JsonValueKind.Object:
- if (propSchema != null)
- {
- if(objectPropertyAssign)
- payloadSB.AppendLine($"{propertyAssignment}new {propSchema.GetSchemaTitle().ToFirstCharacterUpperCase()}(){terminateLine}");
-
- WriteJsonObjectValue(objectName, payloadSB, value, propSchema, indentManager);
- }
- else
- {
- WriteAnonymousObjectValues(payloadSB, value, propSchema, indentManager);
- }
- break;
- case JsonValueKind.Array:
- WriteJsonArrayValue(objectName, payloadSB, value, propSchema, indentManager, propertyAssignment, "],");
- break;
- default:
- throw new NotImplementedException($"Unsupported JsonValueKind: {value.ValueKind}");
- }
- }
- private static void WriteJsonArrayValue(String objectName, StringBuilder payloadSB, JsonElement value, OpenApiSchema schema, IndentManager indentManager, string propertyAssignment, string terminateLine)
- {
- indentManager.Indent(2);
+ indentManager.Indent();
+ WriteCodePropertyObject(builder, child, indentManager);
+ indentManager.Unindent();
+ builder.AppendLine($"{indentManager.GetIndent()}}},");
- // for an array of objects the properties should be written after all the objects have be initiated and not before
- var itemsBuilder = new StringBuilder();
- var arrayListBuilder = new StringBuilder();
-
- int elementIndex = 0;
- var elements = value.EnumerateArray();
- var lastIndex = elements.Count() - 1;
- foreach (var item in elements)
- {
- if (item.ValueKind == JsonValueKind.Object)
- {
- var termination = (lastIndex == elementIndex) ? default : ",";
+ break;
+ case PropertyType.Array:
- var elementName = $"{schema.GetSchemaTitle().ToLowerInvariant()}{(elementIndex == 0 ? default : elementIndex.ToString())}";
- itemsBuilder.AppendLine($"const {elementName} = new {schema.GetSchemaTitle().ToFirstCharacterUpperCase()}();");
+ builder.AppendLine($"{indentManager.GetIndent()}{child.Name} : [");
+ indentManager.Indent();
+ WriteCodePropertyObject(builder, child, indentManager);
+ indentManager.Unindent();
+ builder.AppendLine($"{indentManager.GetIndent()}],");
- // create a new object
- WriteProperty(elementName, itemsBuilder, item, schema, indentManager, default, default, termination, false);
- arrayListBuilder.AppendLine($"{indentManager.GetIndent()}{elementName}{termination}");
- }
- else
- {
- WriteProperty(objectName, arrayListBuilder, item, schema, indentManager, indentManager.GetIndent(), default, (lastIndex == elementIndex) ? default : ",");
+ break;
+ case PropertyType.String:
+ var propName = codeProperty.PropertyType == PropertyType.Map ? $"\"{child.Name.ToFirstCharacterLowerCase()}\"" : child.Name.ToFirstCharacterLowerCase();
+ builder.AppendLine($"{indentManager.GetIndent()}{propName} : \"{child.Value}\",");
+ break;
+ case PropertyType.Enum:
+ builder.AppendLine($"{indentManager.GetIndent()}{child.Name.ToFirstCharacterLowerCase()} : {child.Value},");
+ break;
+ case PropertyType.Date:
+ builder.AppendLine($"{indentManager.GetIndent()}{child.Name} : new Date(\"{child.Value}\"),");
+ break;
+ default:
+ builder.AppendLine($"{indentManager.GetIndent()}{child.Name.ToFirstCharacterLowerCase()} : {child.Value.ToFirstCharacterLowerCase()},");
+ break;
}
- elementIndex++;
}
+ }
-
- payloadSB.Append(itemsBuilder.ToString());
-
- payloadSB.AppendLine($"{propertyAssignment}[");
- payloadSB.Append(arrayListBuilder.ToString());
- indentManager.Unindent();
- payloadSB.AppendLine($"{indentManager.GetIndent()}]");
- indentManager.Unindent();
-
-
-
+ private static string GetActionParametersList(params string[] parameters)
+ {
+ var nonEmptyParameters = parameters.Where(p => !string.IsNullOrEmpty(p));
+ if (nonEmptyParameters.Any())
+ return string.Join(", ", nonEmptyParameters.Aggregate((a, b) => $"{a}, {b}"));
+ else return string.Empty;
}
+
private static string GetFluentApiPath(IEnumerable nodes)
{
if (!(nodes?.Any() ?? false)) return string.Empty;
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs
new file mode 100644
index 000000000..8d9378049
--- /dev/null
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs
@@ -0,0 +1,10 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeSnippetsReflection.OpenAPI.ModelGraph
+{
+ public record struct CodeProperty(string Name, string Value, List Children, PropertyType PropertyType = PropertyType.String);
+}
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
new file mode 100644
index 000000000..cc2402286
--- /dev/null
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
@@ -0,0 +1,249 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using CodeSnippetsReflection.StringExtensions;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+
+namespace CodeSnippetsReflection.OpenAPI.ModelGraph
+{
+
+ public static class ModelGraphBuilder
+ {
+
+ private static readonly Regex nestedStatementRegex = new(@"(\w+)(\([^)]+\))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
+ private static readonly CodeProperty EMPTY_PROPERTY = new() { Name = null , Value = null , Children = null, PropertyType = PropertyType.Default };
+
+ private static readonly CodeProperty BINARY_PROPERTY = new() { Name = null, Value = null, Children = null, PropertyType = PropertyType.Binary };
+
+ public static SnippetCodeGraph BuildCodeGraph(SnippetModel snippetModel)
+ {
+ return new SnippetCodeGraph {
+ ResponseSchema = snippetModel.ResponseSchema,
+ HttpMethod = snippetModel.Method,
+ Nodes = snippetModel.PathNodes,
+ Headers = parseHeaders(snippetModel),
+ Options = parseOptions(snippetModel),
+ Parameters = buildParameters(snippetModel),
+ Body = buildBody(snippetModel)
+ };
+ }
+
+ /***
+ * Returns headers filtering `Host` out
+ */
+ private static IEnumerable parseHeaders(SnippetModel snippetModel)
+ {
+ return snippetModel.RequestHeaders.Where(h => !h.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
+ .Select(h => new CodeProperty { Name = h.Key , Value = h.Value.FirstOrDefault(), Children = null, PropertyType = PropertyType.String })
+ .ToList();
+ }
+
+ /// TODO Add support for options
+ private static List parseOptions(SnippetModel snippetModel)
+ {
+ return new List();
+ }
+
+
+ private static List buildParameters(SnippetModel snippetModel)
+ {
+ var parameters = new List();
+ if (!string.IsNullOrEmpty(snippetModel.QueryString))
+ {
+ var (queryString, replacements) = ReplaceNestedOdataQueryParameters(snippetModel.QueryString);
+ foreach (var queryParam in queryString.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries))
+ {
+ if (queryParam.Contains("="))
+ {
+ var kvPair = queryParam.Split('=', StringSplitOptions.RemoveEmptyEntries);
+ parameters.Add(new() { Name = NormalizeQueryParameterName(kvPair[0]), Value = GetQueryParameterValue(kvPair[1], replacements) });
+ }
+ else
+ parameters.Add(new() { Name = NormalizeQueryParameterName(queryParam), Value = GetQueryParameterValue("undefined", replacements) });
+ }
+
+ }
+ return parameters;
+ }
+
+ private static string NormalizeQueryParameterName(string queryParam) => queryParam.TrimStart('$').ToFirstCharacterLowerCase();
+
+ private static (string, Dictionary) ReplaceNestedOdataQueryParameters(string queryParams)
+ {
+ var replacements = new Dictionary();
+ var matches = nestedStatementRegex.Matches(queryParams);
+ if (matches.Any())
+ foreach (Match match in matches)
+ {
+ var key = match.Groups[1].Value;
+ var value = match.Groups[2].Value;
+ replacements.Add(key, value);
+ queryParams = queryParams.Replace(value, string.Empty);
+ }
+ return (queryParams, replacements);
+ }
+
+ private static string GetQueryParameterValue(string originalValue, Dictionary replacements)
+ {
+ if (originalValue.Equals("true", StringComparison.OrdinalIgnoreCase) || originalValue.Equals("false", StringComparison.OrdinalIgnoreCase))
+ return originalValue.ToLowerInvariant();
+ else if (int.TryParse(originalValue, out var intValue))
+ return intValue.ToString();
+ else
+ {
+ var valueWithNested = originalValue.Split(',')
+ .Select(v => replacements.ContainsKey(v) ? v + replacements[v] : v)
+ .Aggregate((a, b) => $"{a},{b}");
+ return $"\"{valueWithNested}\"";
+ }
+ }
+
+ private static CodeProperty buildBody(SnippetModel snippetModel)
+ {
+ if (string.IsNullOrWhiteSpace(snippetModel?.RequestBody))
+ return EMPTY_PROPERTY;
+
+ switch (snippetModel.ContentType?.Split(';').First().ToLowerInvariant())
+ {
+ case "application/json":
+ return TryParseBody(snippetModel);
+ case "application/octet-stream":
+ return BINARY_PROPERTY;
+ default:
+ return TryParseBody(snippetModel);//in case the content type header is missing but we still have a json payload
+ }
+ }
+
+ private static CodeProperty TryParseBody(SnippetModel snippetModel)
+ {
+ if (!snippetModel.IsRequestBodyValid)
+ throw new InvalidOperationException($"Unsupported content type: {snippetModel.ContentType}");
+
+ using var parsedBody = JsonDocument.Parse(snippetModel.RequestBody, new JsonDocumentOptions { AllowTrailingCommas = true });
+ var schema = snippetModel.RequestSchema;
+ var className = schema.GetSchemaTitle().ToFirstCharacterUpperCase() ?? $"{snippetModel.Path.Split("/").Last().ToFirstCharacterUpperCase()}RequestBody";
+ return parseJsonObjectValue(className, parsedBody.RootElement, schema, snippetModel);
+ }
+
+ private static CodeProperty parseJsonObjectValue(String rootPropertyName, JsonElement value, OpenApiSchema schema, SnippetModel snippetModel = null)
+ {
+ var children = new List();
+
+ if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
+
+ var propertiesAndSchema = value.EnumerateObject()
+ .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
+ foreach (var propertyAndSchema in propertiesAndSchema.Where(x => x.Item2 != null))
+ {
+ var propertyName = propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase();
+ children.Add(parseProperty(propertyName, propertyAndSchema.Item1.Value, propertyAndSchema.Item2));
+ }
+
+ var propertiesWithoutSchema = propertiesAndSchema.Where(x => x.Item2 == null).Select(x => x.Item1);
+ if (propertiesWithoutSchema.Any())
+ {
+
+ var additionalChildren = new List();
+ foreach (var property in propertiesWithoutSchema)
+ {
+ var propName = property.Name;
+ if (string.IsNullOrEmpty(propName) || (property.Value.ValueKind != JsonValueKind.Object))
+ {
+ additionalChildren.Add(parseProperty(propName, property.Value, null));
+ }
+ else
+ {
+ children.Add(parseProperty(propName, property.Value, null));
+ }
+ }
+
+ if (additionalChildren.Any() == true)
+ {
+ var propertyName = "additionalData";
+ var codeProperty = new CodeProperty { Name = propertyName, PropertyType = PropertyType.Map, Children = additionalChildren };
+ children.Add(codeProperty);
+ }
+ }
+
+ return new CodeProperty { Name = rootPropertyName, PropertyType = PropertyType.Object, Children = children };
+ }
+
+ private static CodeProperty parseProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
+ {
+ switch (value.ValueKind)
+ {
+ case JsonValueKind.String:
+ if (propSchema?.Format?.Equals("base64url", StringComparison.OrdinalIgnoreCase) ?? false)
+ {
+ return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Base64Url, Children = new List() };
+ }
+ else if (propSchema?.Format?.Equals("date-time", StringComparison.OrdinalIgnoreCase) ?? false)
+ {
+ return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Date, Children = new List() };
+ }
+ else
+ {
+ var enumSchema = propSchema?.AnyOf.FirstOrDefault(x => x.Enum.Count > 0);
+ if (enumSchema == null)
+ {
+ return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.String, Children = new List() };
+ }
+ else
+ {
+ var propValue = $"{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}";
+ return new CodeProperty { Name = propertyName, Value = propValue, PropertyType = PropertyType.Enum, Children = new List() };
+ }
+
+ }
+ case JsonValueKind.Number:
+ return new CodeProperty { Name = propertyName, Value = $"{value}", PropertyType = PropertyType.Number, Children = new List() };
+ case JsonValueKind.False:
+ case JsonValueKind.True:
+ return new CodeProperty { Name = propertyName, Value = value.GetBoolean().ToString(), PropertyType = PropertyType.Boolean, Children = new List() };
+ case JsonValueKind.Null:
+ return new CodeProperty { Name = propertyName, Value = "null", PropertyType = PropertyType.Null, Children = new List() };
+ case JsonValueKind.Object:
+ if (propSchema != null)
+ {
+ return parseJsonObjectValue(propertyName, value, propSchema) ;
+ }
+ else
+ {
+ return parseAnonymousObjectValues(propertyName, value, propSchema);
+ }
+ case JsonValueKind.Array:
+ return parseJsonArrayValue(propertyName, value, propSchema);
+ default:
+ throw new NotImplementedException($"Unsupported JsonValueKind: {value.ValueKind}");
+ }
+ }
+
+ private static CodeProperty parseJsonArrayValue(string propertyName, JsonElement value, OpenApiSchema schema)
+ {
+ var children = value.EnumerateArray().Select(item => parseProperty(schema.GetSchemaTitle().ToFirstCharacterUpperCase(), item, schema)).ToList();
+ return new CodeProperty { Name = propertyName, Value = null, PropertyType = PropertyType.Array, Children = children };
+ }
+
+ private static CodeProperty parseAnonymousObjectValues(string propertyName, JsonElement value, OpenApiSchema schema)
+ {
+ if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
+
+ var children = new List();
+ var propertiesAndSchema = value.EnumerateObject()
+ .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
+ foreach (var propertyAndSchema in propertiesAndSchema)
+ {
+ children.Add(parseProperty(propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase(), propertyAndSchema.Item1.Value, propertyAndSchema.Item2));
+ }
+
+ return new CodeProperty { Name = propertyName, Value = null, PropertyType = PropertyType.Object, Children = children };
+ }
+ }
+}
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/PropertyType.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/PropertyType.cs
new file mode 100644
index 000000000..b411c255c
--- /dev/null
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/PropertyType.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeSnippetsReflection.OpenAPI.ModelGraph
+{
+ public enum PropertyType
+ {
+ // Empty object
+ Default,
+ String ,
+ Number,
+ Date ,
+ Boolean ,
+ Null,
+ Enum ,
+ Object,
+ Base64Url,
+ Binary,
+ Array,
+ Map
+ }
+}
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
new file mode 100644
index 000000000..3b1551644
--- /dev/null
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Services;
+
+namespace CodeSnippetsReflection.OpenAPI.ModelGraph
+{
+ public class SnippetCodeGraph
+ {
+ public OpenApiSchema ResponseSchema
+ {
+ get; set;
+ }
+ public HttpMethod HttpMethod
+ {
+ get; set;
+ }
+
+ public IEnumerable Headers
+ {
+ get; set;
+ }
+ public IEnumerable Options
+ {
+ get; set;
+ }
+
+ public IEnumerable Parameters
+ {
+ get; set;
+ }
+
+ public CodeProperty Body
+ {
+ get; set;
+ }
+
+ public IEnumerable Nodes
+ {
+ get; set;
+ }
+
+
+ public Boolean HasHeaders()
+ {
+ return Headers.Any() == true;
+ }
+
+ public Boolean HasOptions()
+ {
+ return Options.Any() == true;
+ }
+
+ public Boolean HasParameters()
+ {
+ return Parameters.Any() == true;
+ }
+
+ public Boolean HasBody()
+ {
+ return Body.PropertyType != PropertyType.Default;
+ }
+
+ }
+}
diff --git a/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs b/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs
index 81994ff1b..8f6805304 100644
--- a/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs
+++ b/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs
@@ -13,7 +13,8 @@ public static IEnumerable> GetAllProperties(
return schema.Properties
.Union(schema.AllOf.FlattenEmptyEntries(x => x.AllOf, 2).SelectMany(x => x.Properties))
.Union(schema.AnyOf.SelectMany(x => x.Properties))
- .Union(schema.OneOf.SelectMany(x => x.Properties));
+ .Union(schema.OneOf.SelectMany(x => x.Properties))
+ .Union(schema.Items != null ? schema.Items.AllOf.SelectMany(x => x.Properties) : Enumerable.Empty>());
}
return schema.AllOf.Union(schema.AnyOf).Union(schema.OneOf).SelectMany(x => x.GetAllProperties());
}
From f7b22fad98678c5b276f148ca8bcf8d8bc0c833d Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Thu, 26 May 2022 11:45:17 +0300
Subject: [PATCH 02/14] Adds unit tests to ModelGraphBuilder
---
.../ModelGraph/ModelGraphBuilderTests.cs | 283 ++++++++++++++++++
.../TypeScriptGeneratorTest.cs | 27 +-
.../ModelGraph/CodeProperty.cs | 1 +
.../ModelGraph/ModelGraphBuilder.cs | 29 +-
4 files changed, 314 insertions(+), 26 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs b/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs
index 213bd51ae..75c873c34 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs
@@ -1,12 +1,295 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
+using CodeSnippetsReflection.OpenAPI.ModelGraph;
+using Microsoft.OpenApi.Services;
+using Xunit;
namespace CodeSnippetsReflection.OpenAPI.Test.ModelGraph
{
public class ModelGraphBuilderTests
{
+ private const string ServiceRootUrl = "https://graph.microsoft.com/v1.0";
+ private static OpenApiUrlTreeNode _v1TreeNode;
+
+ private static string TypesSample = @"
+ {
+ ""attendees"": [
+ {
+ ""type"": ""null"",
+ ""emailAddress"": {
+ ""name"": ""Alex Wilbur"",
+ ""address"": ""alexw@contoso.onmicrosoft.com""
+ }
+ }
+ ],
+ ""locationConstraint"": {
+ ""isRequired"": false,
+ ""suggestLocation"": false,
+ ""locations"": [
+ {
+ ""resolveAvailability"": false,
+ ""displayName"": ""Conf room Hood""
+ }
+ ]
+ },
+ ""timeConstraint"": {
+ ""activityDomain"":""work"",
+ ""timeSlots"": [
+ {
+ ""start"": {
+ ""dateTime"": ""2019-04-16T09:00:00"",
+ ""timeZone"": ""Pacific Standard Time""
+ },
+ ""end"": {
+ ""dateTime"": ""2019-04-18T17:00:00"",
+ ""timeZone"": ""Pacific Standard Time""
+ }
+ }
+ ]
+ },
+ ""isOrganizerOptional"": ""false"",
+ ""meetingDuration"": ""PT1H"",
+ ""returnSuggestionReasons"": ""true"",
+ ""minimumAttendeePercentage"": 100
+ }
+ ";
+
+ // read the file from disk
+ private static async Task GetV1TreeNode()
+ {
+ if (_v1TreeNode == null)
+ {
+ _v1TreeNode = await SnippetModelTests.GetTreeNode("https://raw.githubusercontent.com/microsoftgraph/msgraph-metadata/master/openapi/v1.0/openapi.yaml");
+ }
+ return _v1TreeNode;
+ }
+
+ [Fact]
+ public async Task ParsesHeaders()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users");
+
+ request.Headers.Add("Host", "graph.microsoft.com");
+ request.Headers.Add("Prefer", "outlook.timezone=\"Pacific Standard Time\"");
+
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var header = result.Headers.First();
+
+ Assert.True(result.HasHeaders());
+ Assert.Single(result.Headers); // host should be ignored in headers
+ Assert.Equal("outlook.timezone=\"Pacific Standard Time\"", header.Value);
+ Assert.Equal("Prefer", header.Name);
+ Assert.Equal(PropertyType.String, header.PropertyType);
+ }
+
+ [Fact]
+ public async Task HasHeadeIsFalseWhenNoneIsInRequest()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users");
+ request.Headers.Add("Host", "graph.microsoft.com");
+
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ Assert.False(result.HasHeaders());
+ }
+
+ [Fact]
+ public async Task ParsesParameters()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users/19:4b6bed8d24574f6a9e436813cb2617d8?$select=displayName,givenName,postalCode,identities");
+
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var parameter = result.Parameters.First();
+
+ Assert.True(result.HasParameters());
+ Assert.Single(result.Parameters);
+
+ var expectedProperty = new CodeProperty { Name = "select", Value = "displayName,givenName,postalCode,identities", PropertyType = PropertyType.String, Children = null };
+ Assert.Equal(expectedProperty, parameter);
+
+ Assert.Equal("displayName,givenName,postalCode,identities", parameter.Value);
+ Assert.Equal("select", parameter.Name);
+ Assert.Equal(PropertyType.String, parameter.PropertyType);
+
+ Assert.True(result.HasParameters());
+ }
+
+ [Fact]
+ public async Task HasParametersIsFalseWhenNoParamterExists()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users/19:4b6bed8d24574f6a9e436813cb2617d8");
+
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ Assert.False(result.HasParameters());
+ }
+
+ [Fact]
+ public async Task ParsesBodyTypeBinary()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Put, $"{ServiceRootUrl}/applications/{{application-id}}/logo")
+ {
+ Content = new ByteArrayContent(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 })
+ };
+ request.Content.Headers.ContentType = new("application/octet-stream");
+
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+
+ Assert.NotNull(result.Body);
+ Assert.Equal(PropertyType.Binary, result.Body.PropertyType);
+
+ }
+
+ [Fact]
+ public async Task ParsesBodyWithoutProperContentType()
+ {
+
+ var sampleBody = @"
+ {
+ ""createdDateTime"": ""2019-02-04T19:58:15.511Z""
+ }
+ ";
+
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/teams/team-id/channels/19:4b6bed8d24574f6a9e436813cb2617d8@thread.tacv2/messages")
+ {
+ Content = new StringContent(sampleBody, Encoding.UTF8) // snippet missing content type
+ };
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ ;
+
+ var expectedObject = new CodeProperty { Name = "MessagesRequestBody", Value = null, PropertyType = PropertyType.Object, Children = new List() };
+
+ Assert.Equal(expectedObject.Name, result.Body.Name);
+ Assert.Equal(expectedObject.Value, result.Body.Value);
+ Assert.Equal(expectedObject.PropertyType, result.Body.PropertyType);
+ }
+
+ private CodeProperty? findProperyInSnipet(CodeProperty codeProperty, string name)
+ {
+ if (codeProperty.Name == name) return codeProperty;
+
+ if (codeProperty.Children.Any())
+ {
+ foreach (var param in codeProperty.Children)
+ {
+ var result = findProperyInSnipet(param, name);
+ if (result != null) return result;
+ }
+ }
+
+ return null;
+ }
+
+ [Fact]
+ public async Task ParsesBodyPropertyTypeString()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/findMeetingTimes")
+ {
+ Content = new StringContent(TypesSample, Encoding.UTF8)
+ };
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ // meetingDuration should be a string
+ var property = findProperyInSnipet(snippetCodeGraph.Body, "meetingDuration");
+
+ Assert.NotNull(property);
+ Assert.Equal(PropertyType.String, property?.PropertyType);
+ Assert.Equal("PT1H", property?.Value);
+ }
+
+ [Fact]
+ public async Task ParsesBodyPropertyTypeNumber()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/findMeetingTimes")
+ {
+ Content = new StringContent(TypesSample, Encoding.UTF8)
+ };
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ var property = findProperyInSnipet(snippetCodeGraph.Body, "minimumAttendeePercentage");
+
+ Assert.NotNull(property);
+ Assert.Equal(PropertyType.Number, property?.PropertyType);
+ Assert.Equal("100" , property?.Value);
+ }
+
+ [Fact]
+ public async Task ParsesBodyPropertyTypeBoolean()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/findMeetingTimes")
+ {
+ Content = new StringContent(TypesSample, Encoding.UTF8)
+ };
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ var property = findProperyInSnipet(snippetCodeGraph.Body, "suggestLocation");
+
+ Assert.NotNull(property);
+ Assert.Equal(PropertyType.Boolean, property?.PropertyType);
+ Assert.Equal("False", property?.Value);
+ }
+
+ [Fact]
+ public async Task ParsesBodyPropertyTypeObject()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/findMeetingTimes")
+ {
+ Content = new StringContent(TypesSample, Encoding.UTF8)
+ };
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ var property = findProperyInSnipet(snippetCodeGraph.Body, "locationConstraint");
+
+ Assert.NotNull(property);
+ Assert.Equal(PropertyType.Object, property?.PropertyType);
+ }
+
+ [Fact]
+ public async Task ParsesBodyPropertyTypeArray()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/findMeetingTimes")
+ {
+ Content = new StringContent(TypesSample, Encoding.UTF8)
+ };
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ var property = findProperyInSnipet(snippetCodeGraph.Body, "attendees");
+
+ Assert.NotNull(property);
+ Assert.Equal(PropertyType.Array, property?.PropertyType);
+ }
+
+ [Fact]
+ public async Task ParsesBodyPropertyTypeMap()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/findMeetingTimes")
+ {
+ Content = new StringContent(TypesSample, Encoding.UTF8)
+ };
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+
+ var property = findProperyInSnipet(snippetCodeGraph.Body, "additionalData");
+
+ Assert.NotNull(property);
+ Assert.Equal(PropertyType.Map, property?.PropertyType);
+ }
}
+
}
diff --git a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
index d6694ae76..f58f4d1a1 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
@@ -111,22 +111,29 @@ public async Task GeneratesTheDeleteMethodCall()
[Fact]
public async Task WritesTheRequestPayload()
{
- const string userJsonObject = "{\r\n \"accountEnabled\": true,\r\n " +
- "\"displayName\": \"displayName-value\",\r\n " +
- "\"mailNickname\": \"mailNickname-value\",\r\n " +
- "\"userPrincipalName\": \"upn-value@tenant-value.onmicrosoft.com\",\r\n " +
- " \"passwordProfile\" : {\r\n \"forceChangePasswordNextSignIn\": true,\r\n \"password\": \"password-value\"\r\n }\r\n}";//nested passwordProfile Object
+ var sampleJson = @"
+ {
+ ""accountEnabled"": true,
+ ""displayName"": ""displayName-value"",
+ ""mailNickname"": ""mailNickname-value"",
+ ""userPrincipalName"": ""upn-value@tenant-value.onmicrosoft.com"",
+ "" passwordProfile"": {
+ ""forceChangePasswordNextSignIn"": true,
+ ""password"": ""password-value""
+ }
+ }
+ ";
using var requestPayload = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/users")
{
- Content = new StringContent(userJsonObject, Encoding.UTF8, "application/json")
+ Content = new StringContent(sampleJson, Encoding.UTF8, "application/json")
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("new User", result);
- Assert.Contains("requestBody.accountEnabled = true;", result);
- Assert.Contains("requestBody.passwordProfile = new PasswordProfile", result);
- Assert.Contains("requestBody.displayName = \"displayName-value\"", result);
+
+ Assert.Contains("const requestBody : User = {", result);
+ Assert.Contains("accountEnabled : true", result);
+ Assert.Contains("passwordProfile : {", result);
}
[Fact]
public async Task WritesALongAndFindsAnAction()
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs
index 8d9378049..12bf9dd3f 100644
--- a/CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/CodeProperty.cs
@@ -7,4 +7,5 @@
namespace CodeSnippetsReflection.OpenAPI.ModelGraph
{
public record struct CodeProperty(string Name, string Value, List Children, PropertyType PropertyType = PropertyType.String);
+
}
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
index cc2402286..66b179f25 100644
--- a/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
@@ -19,8 +19,6 @@ public static class ModelGraphBuilder
private static readonly Regex nestedStatementRegex = new(@"(\w+)(\([^)]+\))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly CodeProperty EMPTY_PROPERTY = new() { Name = null , Value = null , Children = null, PropertyType = PropertyType.Default };
-
- private static readonly CodeProperty BINARY_PROPERTY = new() { Name = null, Value = null, Children = null, PropertyType = PropertyType.Binary };
public static SnippetCodeGraph BuildCodeGraph(SnippetModel snippetModel)
{
@@ -30,14 +28,14 @@ public static SnippetCodeGraph BuildCodeGraph(SnippetModel snippetModel)
Nodes = snippetModel.PathNodes,
Headers = parseHeaders(snippetModel),
Options = parseOptions(snippetModel),
- Parameters = buildParameters(snippetModel),
- Body = buildBody(snippetModel)
+ Parameters = parseParameters(snippetModel),
+ Body = parseBody(snippetModel)
};
}
- /***
- * Returns headers filtering `Host` out
- */
+ ///
+ /// Parses Headers Filtering Out 'Host'
+ ///
private static IEnumerable parseHeaders(SnippetModel snippetModel)
{
return snippetModel.RequestHeaders.Where(h => !h.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
@@ -52,7 +50,7 @@ private static List parseOptions(SnippetModel snippetModel)
}
- private static List buildParameters(SnippetModel snippetModel)
+ private static List parseParameters(SnippetModel snippetModel)
{
var parameters = new List();
if (!string.IsNullOrEmpty(snippetModel.QueryString))
@@ -63,10 +61,10 @@ private static List buildParameters(SnippetModel snippetModel)
if (queryParam.Contains("="))
{
var kvPair = queryParam.Split('=', StringSplitOptions.RemoveEmptyEntries);
- parameters.Add(new() { Name = NormalizeQueryParameterName(kvPair[0]), Value = GetQueryParameterValue(kvPair[1], replacements) });
+ parameters.Add(new() { Name = NormalizeQueryParameterName(kvPair[0]), Value = GetQueryParameterValue(kvPair[1], replacements), PropertyType = PropertyType.String });
}
else
- parameters.Add(new() { Name = NormalizeQueryParameterName(queryParam), Value = GetQueryParameterValue("undefined", replacements) });
+ parameters.Add(new() { Name = NormalizeQueryParameterName(queryParam), Value = GetQueryParameterValue("undefined", replacements), PropertyType = PropertyType.String });
}
}
@@ -98,14 +96,13 @@ private static string GetQueryParameterValue(string originalValue, Dictionary replacements.ContainsKey(v) ? v + replacements[v] : v)
- .Aggregate((a, b) => $"{a},{b}");
- return $"\"{valueWithNested}\"";
+ return originalValue.Split(',')
+ .Select(v => replacements.ContainsKey(v) ? v + replacements[v] : v)
+ .Aggregate((a, b) => $"{a},{b}");
}
}
- private static CodeProperty buildBody(SnippetModel snippetModel)
+ private static CodeProperty parseBody(SnippetModel snippetModel)
{
if (string.IsNullOrWhiteSpace(snippetModel?.RequestBody))
return EMPTY_PROPERTY;
@@ -115,7 +112,7 @@ private static CodeProperty buildBody(SnippetModel snippetModel)
case "application/json":
return TryParseBody(snippetModel);
case "application/octet-stream":
- return BINARY_PROPERTY;
+ return new() { Name = null, Value = null, Children = null, PropertyType = PropertyType.Binary };
default:
return TryParseBody(snippetModel);//in case the content type header is missing but we still have a json payload
}
From f7996132b51f95384ab4cfc616532f951128ae74 Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Thu, 26 May 2022 11:58:17 +0300
Subject: [PATCH 03/14] Move test
---
.../{ModelGraph => }/ModelGraphBuilderTests.cs | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename CodeSnippetsReflection.OpenAPI.Test/{ModelGraph => }/ModelGraphBuilderTests.cs (100%)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs b/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
similarity index 100%
rename from CodeSnippetsReflection.OpenAPI.Test/ModelGraph/ModelGraphBuilderTests.cs
rename to CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
From 024472e918b0a43216cb8fc6d87e4e6c033bebd6 Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Thu, 26 May 2022 14:33:44 +0300
Subject: [PATCH 04/14] Fix array serialization
---
.../LanguageGenerators/TypeScriptGenerator.cs | 22 +++++++++++++++----
1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
index e1e89a35b..750a76ef7 100644
--- a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
+++ b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
@@ -147,6 +147,13 @@ private static void WriteBody(SnippetCodeGraph codeGraph, StringBuilder builder)
builder.AppendLine($"}};");
}
}
+
+ private static string NormalizeJsonName(string Name)
+ {
+ if (Name.Contains(".")) return $"\"{Name}\"";
+
+ return Name;
+ }
private static void WriteCodePropertyObject(StringBuilder builder, CodeProperty codeProperty, IndentManager indentManager)
{
@@ -162,7 +169,7 @@ private static void WriteCodePropertyObject(StringBuilder builder, CodeProperty
}
else
{
- builder.AppendLine($"{indentManager.GetIndent()}{child.Name.ToFirstCharacterLowerCase()} : {{");
+ builder.AppendLine($"{indentManager.GetIndent()}{NormalizeJsonName(child.Name.ToFirstCharacterLowerCase())} : {{");
}
indentManager.Indent();
@@ -173,7 +180,7 @@ private static void WriteCodePropertyObject(StringBuilder builder, CodeProperty
break;
case PropertyType.Array:
- builder.AppendLine($"{indentManager.GetIndent()}{child.Name} : [");
+ builder.AppendLine($"{indentManager.GetIndent()}{NormalizeJsonName(child.Name)} : [");
indentManager.Indent();
WriteCodePropertyObject(builder, child, indentManager);
indentManager.Unindent();
@@ -181,8 +188,15 @@ private static void WriteCodePropertyObject(StringBuilder builder, CodeProperty
break;
case PropertyType.String:
- var propName = codeProperty.PropertyType == PropertyType.Map ? $"\"{child.Name.ToFirstCharacterLowerCase()}\"" : child.Name.ToFirstCharacterLowerCase();
- builder.AppendLine($"{indentManager.GetIndent()}{propName} : \"{child.Value}\",");
+ var propName = codeProperty.PropertyType == PropertyType.Map ? $"\"{NormalizeJsonName(child.Name.ToFirstCharacterLowerCase())}\"" : child.Name.ToFirstCharacterLowerCase();
+ if (String.IsNullOrWhiteSpace(propName))
+ {
+ builder.AppendLine($"{indentManager.GetIndent()}\"{child.Value}\",");
+ }
+ else
+ {
+ builder.AppendLine($"{indentManager.GetIndent()}{propName} : \"{child.Value}\",");
+ }
break;
case PropertyType.Enum:
builder.AppendLine($"{indentManager.GetIndent()}{child.Name.ToFirstCharacterLowerCase()} : {child.Value},");
From 9cc25550c69da6a6b399bee29702bfea813b9023 Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Mon, 30 May 2022 17:38:03 +0300
Subject: [PATCH 05/14] Fix - Get schema inforation for propeties generated
under additional data
---
.../LanguageGenerators/TypeScriptGenerator.cs | 89 +++++++++----------
.../ModelGraph/ModelGraphBuilder.cs | 75 +++++++++-------
.../OpenApiSchemaExtensions.cs | 3 +-
3 files changed, 87 insertions(+), 80 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
index 750a76ef7..8cc207f02 100644
--- a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
+++ b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
@@ -26,7 +26,7 @@ public class TypeScriptGenerator : ILanguageGenerator parseHeaders(SnippetModel snippetModel)
{
return snippetModel.RequestHeaders.Where(h => !h.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
- .Select(h => new CodeProperty { Name = h.Key , Value = h.Value.FirstOrDefault(), Children = null, PropertyType = PropertyType.String })
+ .Select(h => new CodeProperty { Name = h.Key , Value = h.Value?.FirstOrDefault(), Children = null, PropertyType = PropertyType.String })
.ToList();
}
@@ -71,7 +71,7 @@ private static List parseParameters(SnippetModel snippetModel)
return parameters;
}
- private static string NormalizeQueryParameterName(string queryParam) => queryParam.TrimStart('$').ToFirstCharacterLowerCase();
+ private static string NormalizeQueryParameterName(string queryParam) => System.Web.HttpUtility.UrlDecode(queryParam.TrimStart('$').ToFirstCharacterLowerCase());
private static (string, Dictionary) ReplaceNestedOdataQueryParameters(string queryParams)
{
@@ -90,13 +90,14 @@ private static (string, Dictionary) ReplaceNestedOdataQueryParam
private static string GetQueryParameterValue(string originalValue, Dictionary replacements)
{
- if (originalValue.Equals("true", StringComparison.OrdinalIgnoreCase) || originalValue.Equals("false", StringComparison.OrdinalIgnoreCase))
- return originalValue.ToLowerInvariant();
- else if (int.TryParse(originalValue, out var intValue))
+ var escapedParam = System.Web.HttpUtility.UrlDecode(originalValue);
+ if (escapedParam.Equals("true", StringComparison.OrdinalIgnoreCase) || escapedParam.Equals("false", StringComparison.OrdinalIgnoreCase))
+ return escapedParam.ToLowerInvariant();
+ else if (int.TryParse(escapedParam, out var intValue))
return intValue.ToString();
else
{
- return originalValue.Split(',')
+ return escapedParam.Split(',')
.Select(v => replacements.ContainsKey(v) ? v + replacements[v] : v)
.Aggregate((a, b) => $"{a},{b}");
}
@@ -118,6 +119,31 @@ private static CodeProperty parseBody(SnippetModel snippetModel)
}
}
+ private static string ComputeRequestBody(SnippetModel snippetModel)
+ {
+ var nodes = snippetModel.PathNodes;
+ if (!(nodes?.Any() ?? false)) return string.Empty;
+
+ var nodeName = nodes.Where(x => !x.Segment.IsCollectionIndex())
+ .Select(x =>
+ {
+ if (x.Segment.IsFunction())
+ return x.Segment.Split('.').Last();
+ else
+ return x.Segment;
+ })
+ .Last()
+ .ToFirstCharacterUpperCase();
+
+ var singularNodeName = nodeName[nodeName.Length - 1] == 's' ? nodeName.Substring(0, nodeName.Length - 1) : nodeName;
+
+ if (nodes.Last()?.Segment?.IsCollectionIndex() == true)
+ return singularNodeName;
+ else
+ return $"{nodeName}PostRequestBody";
+
+ }
+
private static CodeProperty TryParseBody(SnippetModel snippetModel)
{
if (!snippetModel.IsRequestBodyValid)
@@ -125,7 +151,7 @@ private static CodeProperty TryParseBody(SnippetModel snippetModel)
using var parsedBody = JsonDocument.Parse(snippetModel.RequestBody, new JsonDocumentOptions { AllowTrailingCommas = true });
var schema = snippetModel.RequestSchema;
- var className = schema.GetSchemaTitle().ToFirstCharacterUpperCase() ?? $"{snippetModel.Path.Split("/").Last().ToFirstCharacterUpperCase()}RequestBody";
+ var className = schema.GetSchemaTitle().ToFirstCharacterUpperCase() ?? ComputeRequestBody(snippetModel);
return parseJsonObjectValue(className, parsedBody.RootElement, schema, snippetModel);
}
@@ -149,52 +175,37 @@ private static CodeProperty parseJsonObjectValue(String rootPropertyName, JsonEl
var additionalChildren = new List();
foreach (var property in propertiesWithoutSchema)
- {
- var propName = property.Name;
- if (string.IsNullOrEmpty(propName) || (property.Value.ValueKind != JsonValueKind.Object))
- {
- additionalChildren.Add(parseProperty(propName, property.Value, null));
- }
- else
- {
- children.Add(parseProperty(propName, property.Value, null));
- }
- }
+ additionalChildren.Add(parseProperty(property.Name, property.Value, null));
- if (additionalChildren.Any() == true)
- {
- var propertyName = "additionalData";
- var codeProperty = new CodeProperty { Name = propertyName, PropertyType = PropertyType.Map, Children = additionalChildren };
- children.Add(codeProperty);
- }
+ if (additionalChildren.Any())
+ children.Add(new CodeProperty { Name = "additionalData", PropertyType = PropertyType.Map, Children = additionalChildren });
}
return new CodeProperty { Name = rootPropertyName, PropertyType = PropertyType.Object, Children = children };
}
+ private static String escapeSpecialCharacters(string value)
+ {
+ return value?.Replace("\"", "\\\"")?.Replace("\n", "\\n")?.Replace("\r", "\\r");
+ }
+
private static CodeProperty parseProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
{
switch (value.ValueKind)
{
case JsonValueKind.String:
if (propSchema?.Format?.Equals("base64url", StringComparison.OrdinalIgnoreCase) ?? false)
- {
return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Base64Url, Children = new List() };
- }
else if (propSchema?.Format?.Equals("date-time", StringComparison.OrdinalIgnoreCase) ?? false)
- {
return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Date, Children = new List() };
- }
else
{
var enumSchema = propSchema?.AnyOf.FirstOrDefault(x => x.Enum.Count > 0);
if (enumSchema == null)
- {
- return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.String, Children = new List() };
- }
+ return new CodeProperty { Name = propertyName, Value = escapeSpecialCharacters(value.GetString()), PropertyType = PropertyType.String, Children = new List() };
else
{
- var propValue = $"{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}";
+ var propValue = String.IsNullOrWhiteSpace(value.GetString()) ? null : $"{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}";
return new CodeProperty { Name = propertyName, Value = propValue, PropertyType = PropertyType.Enum, Children = new List() };
}
diff --git a/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs b/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs
index 8f6805304..21f248a84 100644
--- a/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs
+++ b/CodeSnippetsReflection.OpenAPI/OpenApiSchemaExtensions.cs
@@ -14,7 +14,8 @@ public static IEnumerable> GetAllProperties(
.Union(schema.AllOf.FlattenEmptyEntries(x => x.AllOf, 2).SelectMany(x => x.Properties))
.Union(schema.AnyOf.SelectMany(x => x.Properties))
.Union(schema.OneOf.SelectMany(x => x.Properties))
- .Union(schema.Items != null ? schema.Items.AllOf.SelectMany(x => x.Properties) : Enumerable.Empty>());
+ .Union(schema.Items != null ? schema.Items.AllOf.SelectMany(x => x.Properties) : Enumerable.Empty>())
+ .Union(schema.Items != null ? schema.Items.AnyOf.SelectMany(x => x.Properties) : Enumerable.Empty>());
}
return schema.AllOf.Union(schema.AnyOf).Union(schema.OneOf).SelectMany(x => x.GetAllProperties());
}
From dd64e9f05b379889f4a56d687ade864d6e3dfe72 Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Mon, 30 May 2022 19:30:06 +0300
Subject: [PATCH 06/14] Adds TS test cases
---
.../ModelGraphBuilderTests.cs | 4 +-
.../TypeScriptGeneratorTest.cs | 514 +++++++++++++++---
.../LanguageGenerators/TypeScriptGenerator.cs | 2 +-
.../ModelGraph/ModelGraphBuilder.cs | 61 +--
4 files changed, 481 insertions(+), 100 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs b/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
index 75c873c34..60c4cdd64 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
@@ -8,7 +8,7 @@
using Microsoft.OpenApi.Services;
using Xunit;
-namespace CodeSnippetsReflection.OpenAPI.Test.ModelGraph
+namespace CodeSnippetsReflection.OpenAPI.Test
{
public class ModelGraphBuilderTests
{
@@ -168,7 +168,7 @@ public async Task ParsesBodyWithoutProperContentType()
var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
;
- var expectedObject = new CodeProperty { Name = "MessagesRequestBody", Value = null, PropertyType = PropertyType.Object, Children = new List() };
+ var expectedObject = new CodeProperty { Name = "MessagesPostRequestBody", Value = null, PropertyType = PropertyType.Object, Children = new List() };
Assert.Equal(expectedObject.Name, result.Body.Name);
Assert.Equal(expectedObject.Value, result.Body.Value);
diff --git a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
index f58f4d1a1..9c30dba8a 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
@@ -34,7 +34,20 @@ public async Task GeneratesTheCorrectFluentAPIPath()
using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/me/messages");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains(".me.messages", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+
+ const result = async () => {
+ await graphServiceClient.me.messages.get();
+ }
+ ";
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
[Fact]
@@ -47,16 +60,51 @@ public async Task GeneratesClassWithDefaultBodyWhenSchemaNotPresent()
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("const requestBody = new BatchRecordDecisionsRequestBody()", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : BatchRecordDecisionsPostRequestBody = {
+ decision : ""Approve"",
+ justification : ""All principals with access need continued access to the resource (Marketing Group) as all the principals are on the marketing team"",
+ resourceId : ""a5c51e59-3fcd-4a37-87a1-835c0c21488a"",
+ };
+
+ async () => {
+ await graphServiceClient.identityGovernance.accessReviews.definitionsById(""accessReviewScheduleDefinition-id"").instancesById(""accessReviewInstance-id"").batchRecordDecisions.post(requestBody);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesTheCorrectFluentAPIPathForIndexedCollections()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/me/messages/{{message-id}}");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains(".me.messagesById(\"message-id\")", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+
+ const result = async () => {
+ await graphServiceClient.me.messagesById(""message-id"").get();
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesTheSnippetInitializationStatement()
{
@@ -65,31 +113,67 @@ public async Task GeneratesTheSnippetInitializationStatement()
var result = _generator.GenerateCodeSnippet(snippetModel);
Assert.Contains("const graphServiceClient = GraphServiceClient.init({authProvider});", result);
}
+
[Fact]
public async Task GeneratesTheGetMethodCall()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/me/messages");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("get", result);
- Assert.Contains("await", result);
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+
+ const result = async () => {
+ await graphServiceClient.me.messages.get();
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesThePostMethodCall()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/messages");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("post", result);
+ var expected = @"
+ const result = async () => {
+ await graphServiceClient.me.messages.post();
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesThePatchMethodCall()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Patch, $"{ServiceRootUrl}/me/messages/{{message-id}}");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("patch", result);
+ var expected = @"
+ const result = async () => {
+ await graphServiceClient.me.messagesById(""message-id"").patch();
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesThePutMethodCall()
{
@@ -98,16 +182,29 @@ public async Task GeneratesThePutMethodCall()
var result = _generator.GenerateCodeSnippet(snippetModel);
Assert.Contains("put", result);
}
+
[Fact]
public async Task GeneratesTheDeleteMethodCall()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Delete, $"{ServiceRootUrl}/me/messages/{{message-id}}");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("delete", result);
- Assert.DoesNotContain("let result =", result);
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+
+ const result = async () => {
+ await graphServiceClient.me.messagesById(""message-id"").delete();
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
- // code
+
[Fact]
public async Task WritesTheRequestPayload()
{
@@ -131,10 +228,34 @@ public async Task WritesTheRequestPayload()
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("const requestBody : User = {", result);
- Assert.Contains("accountEnabled : true", result);
- Assert.Contains("passwordProfile : {", result);
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : User = {
+ accountEnabled : true,
+ displayName : ""displayName-value"",
+ mailNickname : ""mailNickname-value"",
+ userPrincipalName : ""upn-value@tenant-value.onmicrosoft.com"",
+ additionalData : {
+ passwordProfile : {
+ forceChangePasswordNextSignIn : true,
+ password : ""password-value"",
+ },
+ },
+ };
+
+ const result = async () => {
+ await graphServiceClient.users.post(requestBody);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task WritesALongAndFindsAnAction()
{
@@ -147,21 +268,44 @@ public async Task WritesALongAndFindsAnAction()
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
Assert.Contains("10", result);
- Assert.DoesNotContain("microsoft.graph", result);
+ Assert.DoesNotContain("microsoft.graph1", result);
}
+
[Fact]
public async Task WritesADouble()
{
- const string userJsonObject = "{\r\n \"minimumAttendeePercentage\": 10\r\n\r\n}";
+ var sampleJson = @"
+ {
+ ""minimumAttendeePercentage"": 10
+ }
+ ";
using var requestPayload = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/me/findMeetingTimes")
{
- Content = new StringContent(userJsonObject, Encoding.UTF8, "application/json")
+ Content = new StringContent(sampleJson, Encoding.UTF8, "application/json")
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("10", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : FindMeetingTimesPostRequestBody = {
+ minimumAttendeePercentage : 10,
+ };
+
+ const result = async () => {
+ await graphServiceClient.me.findMeetingTimes.post(requestBody);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesABinaryPayload()
{
@@ -172,8 +316,23 @@ public async Task GeneratesABinaryPayload()
requestPayload.Content.Headers.ContentType = new("application/octet-stream");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("new WebStream", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody = new ArrayBuffer(16);
+
+ async () => {
+ await graphServiceClient.applicationsById(""application-id"").logo.put(requestBody);
+ }
+ ";
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesABase64UrlPayload()
{
@@ -184,8 +343,26 @@ public async Task GeneratesABase64UrlPayload()
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("btoa", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : ChatMessageHostedContent = {
+ contentBytes : ""wiubviuwbegviwubiu"",
+ };
+
+ const result = async () => {
+ await graphServiceClient.chatsById(""chat-id"").messagesById(""chatMessage-id"").hostedContents.post(requestBody);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesADatePayload()
{
@@ -196,22 +373,67 @@ public async Task GeneratesADatePayload()
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("new Date(", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : Message = {
+ receivedDateTime : new Date(""2021-08-30T20:00:00:00Z""),
+ };
+
+ const result = async () => {
+ await graphServiceClient.me.messages.post(requestBody);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesAnArrayPayloadInAdditionalData()
{
- const string userJsonObject = "{\r\n \"members@odata.bind\": [\r\n \"https://graph.microsoft.com/v1.0/directoryObjects/{id}\",\r\n \"https://graph.microsoft.com/v1.0/directoryObjects/{id}\",\r\n \"https://graph.microsoft.com/v1.0/directoryObjects/{id}\"\r\n ]\r\n}";
+ var samplePayload = @"
+ {
+ ""members@odata.bind"": [
+ ""https://graph.microsoft.com/v1.0/directoryObjects/{id}"",
+ ""https://graph.microsoft.com/v1.0/directoryObjects/{id}"",
+ ""https://graph.microsoft.com/v1.0/directoryObjects/{id}""
+ ]
+ }
+ ";
+
using var requestPayload = new HttpRequestMessage(HttpMethod.Patch, $"{ServiceRootUrl}/groups/{{group-id}}")
{
- Content = new StringContent(userJsonObject, Encoding.UTF8, "application/json")
+ Content = new StringContent(samplePayload, Encoding.UTF8, "application/json")
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("\"members@odata.bind\" : [", result);
- Assert.Contains("requestBody.additionalData", result);
- Assert.Contains("members", result); // property name hasn't been changed
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : Group = {
+ additionalData : {
+ ""members@odata.bind"" : [
+ ""https://graph.microsoft.com/v1.0/directoryObjects/{id}"",
+ ""https://graph.microsoft.com/v1.0/directoryObjects/{id}"",
+ ""https://graph.microsoft.com/v1.0/directoryObjects/{id}"",
+ ],
+ },
+ };
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesAnArrayOfObjectsPayloadData()
{
@@ -222,38 +444,121 @@ public async Task GeneratesAnArrayOfObjectsPayloadData()
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("const extension = new Extension();", result); // property is initialized on its own
- Assert.Contains("extension.additionalData = {", result); // additional data is initialized as a Record not mpa
- Assert.Contains("requestBody.extensions = [", result); // property is added to list
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : Group = {
+ extensions : [
+ {
+ additionalData : {
+ dealValue : 10000,
+ },
+ },
+ ],
+ additionalData : {
+ body : {
+ contentType : ""HTML"",
+ },
+ },
+ };
+
+ const result = async () => {
+ await graphServiceClient.groupsById(""group-id"").patch(requestBody);
+ }
+ ";
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesSelectQueryParameters()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/me?$select=displayName,id");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("displayName", result);
- Assert.Contains("let requestParameters = {", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const configuration = {
+ queryParameters : {
+ select: ""displayName,id"",
+ }
+ };
+
+ const result = async () => {
+ await graphServiceClient.me.get(configuration);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesCountBooleanQueryParameters()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users?$count=true&$select=displayName,id");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("displayName", result);
- Assert.DoesNotContain("\"true\"", result);
- Assert.Contains("true", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const configuration = {
+ queryParameters : {
+ count: ""true"",
+ select: ""displayName,id"",
+ }
+ };
+
+ const result = async () => {
+ await graphServiceClient.users.get(configuration);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesSkipQueryParameters()
{
using var requestPayload = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users?$skip=10");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.DoesNotContain("\"10\"", result);
- Assert.Contains("10", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const configuration = {
+ queryParameters : {
+ skip: ""10"",
+ }
+ };
+
+ const result = async () => {
+ await graphServiceClient.users.get(configuration);
+ }
+ ";
+
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GeneratesSelectExpandQueryParameters()
{
@@ -264,6 +569,7 @@ public async Task GeneratesSelectExpandQueryParameters()
Assert.Contains("members($select=id,displayName)", result);
Assert.DoesNotContain("select :", result);
}
+
[Fact]
public async Task GeneratesRequestHeaders()
{
@@ -271,52 +577,138 @@ public async Task GeneratesRequestHeaders()
requestPayload.Headers.Add("ConsistencyLevel", "eventual");
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("\"ConsistencyLevel\": \"eventual\",", result);
- Assert.Contains("const headers = {", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const configuration = {
+ headers : {
+ ""ConsistencyLevel"": ""eventual"",
+ }
+ };
+
+ const result = async () => {
+ await graphServiceClient.groups.get(configuration);
+ }
+ ";
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
+
[Fact]
public async Task GenerateAdditionalData()
{
- const string chatObject = "{ \"createdDateTime\":\"2019-02-04T19:58:15.511Z\", " +
- "\"from\":{ \"user\":{ \"id\":\"id-value\", \"displayName\":\"Joh Doe\", " +
- " \"userIdentityType\":\"aadUser\" } }, \"body\":{ \"contentType\":\"html\", " +
- " \"content\":\"Hello World\" }}";
+ var samplePayload = @"
+ {
+ ""createdDateTime"": ""2019-02-04T19:58:15.511Z"",
+ ""from"": {
+ ""user"": {
+ ""id"": ""id-value"",
+ ""displayName"": ""Joh Doe"",
+ ""userIdentityType"": ""aadUser""
+ }
+ },
+ ""body"": {
+ ""contentType"": ""html"",
+ ""content"": ""Hello World""
+ }
+ }
+ ";
using var requestPayload = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/teams/team-id/channels/19:4b6bed8d24574f6a9e436813cb2617d8@thread.tacv2/messages")
{
- Content = new StringContent(chatObject, Encoding.UTF8, "application/json")
+ Content = new StringContent(samplePayload, Encoding.UTF8, "application/json")
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("new ChatMessage", result);
- Assert.Contains("requestBody.from.user.additionalData", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : ChatMessage = {
+ createdDateTime : new Date(""2019-02-04T19:58:15.511Z""),
+ from : {
+ user : {
+ id : ""id-value"",
+ displayName : ""Joh Doe"",
+ additionalData : {
+ ""userIdentityType"" : ""aadUser"",
+ },
+ },
+ },
+ body : {
+ contentType : BodyType.Html,
+ content : ""Hello World"",
+ },
+ };
+
+ const result = async () => {
+ await graphServiceClient.teamsById(""team-id"").channelsById(""channel-id"").messages.post(requestBody);
+ }
+ ";
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
[Fact]
public async Task GeneratesEnumsWhenVariableIsEnum()
{
- const string userJsonObject = @"{
- ""displayName"": ""Test create"",
- ""settings"": {
- ""recurrence"": {
- ""pattern"": {
- ""type"": ""weekly"",
- ""interval"": 1
- },
- ""range"": {
- ""type"": ""noEnd"",
- ""startDate"": ""2020-09-08T12:02:30.667Z""
- }
- }
- }
-}";
+ const string payloadJson = @"{
+ ""displayName"": ""Test create"",
+ ""settings"": {
+ ""recurrence"": {
+ ""pattern"": {
+ ""type"": ""weekly"",
+ ""interval"": 1
+ },
+ ""range"": {
+ ""type"": ""noEnd"",
+ ""startDate"": ""2020-09-08T12:02:30.667Z""
+ }
+ }
+ }
+ }";
using var requestPayload = new HttpRequestMessage(HttpMethod.Post, $"{ServiceRootUrl}/identityGovernance/accessReviews/definitions")
{
- Content = new StringContent(userJsonObject, Encoding.UTF8, "application/json")
+ Content = new StringContent(payloadJson, Encoding.UTF8, "application/json")
};
var snippetModel = new SnippetModel(requestPayload, ServiceRootUrl, await GetV1TreeNode());
var result = _generator.GenerateCodeSnippet(snippetModel);
- Assert.Contains("requestBody.settings.recurrence.pattern.type = RecurrencePatternType.Weekly", result);
+
+ var expected = @"
+ const graphServiceClient = GraphServiceClient.init({authProvider});
+
+ const requestBody : AccessReviewScheduleDefinition = {
+ displayName : ""Test create"",
+ settings : {
+ recurrence : {
+ pattern : {
+ type : RecurrencePatternType.Weekly,
+ interval : 1,
+ },
+ range : {
+ type : RecurrenceRangeType.NoEnd,
+ startDate : ""2020-09-08T12:02:30.667Z"",
+ },
+ },
+ },
+ };
+
+ const result = async () => {
+ await graphServiceClient.identityGovernance.accessReviews.definitions.post(requestBody);
+ }
+ ";
+
+ Assert.Contains(
+ expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
+ result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
+ );
}
}
}
diff --git a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
index 8cc207f02..241a9b13b 100644
--- a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
+++ b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
@@ -135,7 +135,7 @@ private static void WriteBody(SnippetCodeGraph codeGraph, StringBuilder builder)
if (codeGraph.Body.PropertyType == PropertyType.Binary)
{
- builder.AppendLine($"{indentManager.GetIndent()}const {RequestBodyVarName} = new WebStream();");
+ builder.AppendLine($"{indentManager.GetIndent()}const {RequestBodyVarName} = new ArrayBuffer({codeGraph.Body.Value.Length});");
}
else
{
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
index 12346ff33..8757e93bf 100644
--- a/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
@@ -1,13 +1,9 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
-using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
-using System.Threading.Tasks;
using CodeSnippetsReflection.StringExtensions;
-using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
namespace CodeSnippetsReflection.OpenAPI.ModelGraph
@@ -27,7 +23,7 @@ public static SnippetCodeGraph BuildCodeGraph(SnippetModel snippetModel)
HttpMethod = snippetModel.Method,
Nodes = snippetModel.PathNodes,
Headers = parseHeaders(snippetModel),
- Options = parseOptions(snippetModel),
+ Options = Enumerable.Empty(),
Parameters = parseParameters(snippetModel),
Body = parseBody(snippetModel)
};
@@ -43,12 +39,6 @@ private static IEnumerable parseHeaders(SnippetModel snippetModel)
.ToList();
}
- /// TODO Add support for options
- private static List parseOptions(SnippetModel snippetModel)
- {
- return new List();
- }
-
private static List parseParameters(SnippetModel snippetModel)
{
@@ -58,7 +48,7 @@ private static List parseParameters(SnippetModel snippetModel)
var (queryString, replacements) = ReplaceNestedOdataQueryParameters(snippetModel.QueryString);
foreach (var queryParam in queryString.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries))
{
- if (queryParam.Contains("="))
+ if (queryParam.Contains('='))
{
var kvPair = queryParam.Split('=', StringSplitOptions.RemoveEmptyEntries);
parameters.Add(new() { Name = NormalizeQueryParameterName(kvPair[0]), Value = GetQueryParameterValue(kvPair[1], replacements), PropertyType = PropertyType.String });
@@ -113,7 +103,7 @@ private static CodeProperty parseBody(SnippetModel snippetModel)
case "application/json":
return TryParseBody(snippetModel);
case "application/octet-stream":
- return new() { Name = null, Value = null, Children = null, PropertyType = PropertyType.Binary };
+ return new() { Name = null, Value = snippetModel.RequestBody?.ToString(), Children = null, PropertyType = PropertyType.Binary };
default:
return TryParseBody(snippetModel);//in case the content type header is missing but we still have a json payload
}
@@ -152,10 +142,10 @@ private static CodeProperty TryParseBody(SnippetModel snippetModel)
using var parsedBody = JsonDocument.Parse(snippetModel.RequestBody, new JsonDocumentOptions { AllowTrailingCommas = true });
var schema = snippetModel.RequestSchema;
var className = schema.GetSchemaTitle().ToFirstCharacterUpperCase() ?? ComputeRequestBody(snippetModel);
- return parseJsonObjectValue(className, parsedBody.RootElement, schema, snippetModel);
+ return parseJsonObjectValue(className, parsedBody.RootElement, schema);
}
- private static CodeProperty parseJsonObjectValue(String rootPropertyName, JsonElement value, OpenApiSchema schema, SnippetModel snippetModel = null)
+ private static CodeProperty parseJsonObjectValue(String rootPropertyName, JsonElement value, OpenApiSchema schema)
{
var children = new List();
@@ -189,27 +179,30 @@ private static String escapeSpecialCharacters(string value)
return value?.Replace("\"", "\\\"")?.Replace("\n", "\\n")?.Replace("\r", "\\r");
}
+ private static CodeProperty evaluateStringProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
+ {
+ if (propSchema?.Format?.Equals("base64url", StringComparison.OrdinalIgnoreCase) ?? false)
+ return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Base64Url, Children = new List() };
+
+ if (propSchema?.Format?.Equals("date-time", StringComparison.OrdinalIgnoreCase) ?? false)
+ return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Date, Children = new List() };
+
+
+ var enumSchema = propSchema?.AnyOf.FirstOrDefault(x => x.Enum.Count > 0);
+ if (enumSchema == null)
+ return new CodeProperty { Name = propertyName, Value = escapeSpecialCharacters(value.GetString()), PropertyType = PropertyType.String, Children = new List() };
+
+
+ var propValue = String.IsNullOrWhiteSpace(value.GetString()) ? null : $"{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}";
+ return new CodeProperty { Name = propertyName, Value = propValue, PropertyType = PropertyType.Enum, Children = new List() };
+ }
+
private static CodeProperty parseProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
{
switch (value.ValueKind)
{
case JsonValueKind.String:
- if (propSchema?.Format?.Equals("base64url", StringComparison.OrdinalIgnoreCase) ?? false)
- return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Base64Url, Children = new List() };
- else if (propSchema?.Format?.Equals("date-time", StringComparison.OrdinalIgnoreCase) ?? false)
- return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Date, Children = new List() };
- else
- {
- var enumSchema = propSchema?.AnyOf.FirstOrDefault(x => x.Enum.Count > 0);
- if (enumSchema == null)
- return new CodeProperty { Name = propertyName, Value = escapeSpecialCharacters(value.GetString()), PropertyType = PropertyType.String, Children = new List() };
- else
- {
- var propValue = String.IsNullOrWhiteSpace(value.GetString()) ? null : $"{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}";
- return new CodeProperty { Name = propertyName, Value = propValue, PropertyType = PropertyType.Enum, Children = new List() };
- }
-
- }
+ return evaluateStringProperty(propertyName, value, propSchema);
case JsonValueKind.Number:
return new CodeProperty { Name = propertyName, Value = $"{value}", PropertyType = PropertyType.Number, Children = new List() };
case JsonValueKind.False:
@@ -219,13 +212,9 @@ private static CodeProperty parseProperty(string propertyName, JsonElement value
return new CodeProperty { Name = propertyName, Value = "null", PropertyType = PropertyType.Null, Children = new List() };
case JsonValueKind.Object:
if (propSchema != null)
- {
- return parseJsonObjectValue(propertyName, value, propSchema) ;
- }
+ return parseJsonObjectValue(propertyName, value, propSchema);
else
- {
return parseAnonymousObjectValues(propertyName, value, propSchema);
- }
case JsonValueKind.Array:
return parseJsonArrayValue(propertyName, value, propSchema);
default:
From 9fe6a1f7ee4d6c60b1b454c40e6182bd3c29d618 Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Mon, 30 May 2022 19:46:20 +0300
Subject: [PATCH 07/14] Fix code smells
---
.../ModelGraphBuilderTests.cs | 3 ---
.../LanguageGenerators/TypeScriptGenerator.cs | 6 +++---
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs b/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
index 60c4cdd64..f84d69661 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
@@ -144,10 +144,7 @@ public async Task ParsesBodyTypeBinary()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
-
- Assert.NotNull(result.Body);
Assert.Equal(PropertyType.Binary, result.Body.PropertyType);
-
}
[Fact]
diff --git a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
index 241a9b13b..a773c8fc4 100644
--- a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
+++ b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
@@ -87,7 +87,7 @@ private static void WriteOptions(SnippetCodeGraph codeGraph, StringBuilder build
if (!codeGraph.HasOptions()) return;
if (codeGraph.HasHeaders())
- builder.Append(",");
+ builder.Append(',');
builder.AppendLine($"{indentManager.GetIndent()}{RequestOptionsVarName} : {{");
indentManager.Indent();
@@ -102,7 +102,7 @@ private static void WriteParameters(SnippetCodeGraph codeGraph, StringBuilder bu
if (!codeGraph.HasParameters()) return;
if (codeGraph.HasHeaders() || codeGraph.HasOptions())
- builder.Append(",");
+ builder.Append(',');
builder.AppendLine($"{indentManager.GetIndent()}{RequestParametersVarName} : {{");
indentManager.Indent();
@@ -149,7 +149,7 @@ private static void WriteBody(SnippetCodeGraph codeGraph, StringBuilder builder)
private static string NormalizeJsonName(string Name)
{
- return (!String.IsNullOrWhiteSpace(Name) && Name.Substring(1) != "\"") && (Name.Contains(".") || Name.Contains("-")) ? $"\"{Name}\"" : Name;
+ return (!String.IsNullOrWhiteSpace(Name) && Name.Substring(1) != "\"") && (Name.Contains('.') || Name.Contains('-')) ? $"\"{Name}\"" : Name;
}
private static void WriteCodePropertyObject(StringBuilder builder, CodeProperty codeProperty, IndentManager indentManager)
From 2d7835fd6285f9ad8478ba98c1df2453dd3480a0 Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Tue, 31 May 2022 11:39:46 +0300
Subject: [PATCH 08/14] Refactor SnippetCodeGraph to constructor initialization
---
.../AssertUtil.cs | 20 ++
...ilderTests.cs => SnippetCodeGraphTests.cs} | 47 +++-
.../SnippetModelTests.cs | 2 +-
.../TypeScriptGeneratorTest.cs | 106 ++------
.../CodeSnippetsReflection.OpenAPI.csproj | 1 -
.../LanguageGenerators/TypeScriptGenerator.cs | 8 +-
.../ModelGraph/ModelGraphBuilder.cs | 246 ------------------
.../ModelGraph/SnippetCodeGraph.cs | 240 ++++++++++++++++-
.../StringExtensions/StringExtensions.cs | 5 +
9 files changed, 320 insertions(+), 355 deletions(-)
create mode 100644 CodeSnippetsReflection.OpenAPI.Test/AssertUtil.cs
rename CodeSnippetsReflection.OpenAPI.Test/{ModelGraphBuilderTests.cs => SnippetCodeGraphTests.cs} (84%)
delete mode 100644 CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
diff --git a/CodeSnippetsReflection.OpenAPI.Test/AssertUtil.cs b/CodeSnippetsReflection.OpenAPI.Test/AssertUtil.cs
new file mode 100644
index 000000000..d7cf7f26f
--- /dev/null
+++ b/CodeSnippetsReflection.OpenAPI.Test/AssertUtil.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace CodeSnippetsReflection.OpenAPI.Test
+{
+ public static class AssertUtil
+ {
+ public static void ContainsIgnoreWhiteSpace(string expectedSubstring, string actualString)
+ {
+ Xunit.Assert.Contains(
+ expectedSubstring.Replace(" ", string.Empty).Replace(Environment.NewLine, string.Empty),
+ actualString.Replace(" ", string.Empty).Replace(Environment.NewLine, string.Empty)
+ );
+ }
+ }
+}
diff --git a/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
similarity index 84%
rename from CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
rename to CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
index f84d69661..60566dbef 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/ModelGraphBuilderTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
@@ -10,7 +10,7 @@
namespace CodeSnippetsReflection.OpenAPI.Test
{
- public class ModelGraphBuilderTests
+ public class SnippetCodeGraphTests
{
private const string ServiceRootUrl = "https://graph.microsoft.com/v1.0";
private static OpenApiUrlTreeNode _v1TreeNode;
@@ -77,7 +77,7 @@ public async Task ParsesHeaders()
request.Headers.Add("Prefer", "outlook.timezone=\"Pacific Standard Time\"");
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var result = new SnippetCodeGraph(snippetModel);
var header = result.Headers.First();
Assert.True(result.HasHeaders());
@@ -94,7 +94,7 @@ public async Task HasHeadeIsFalseWhenNoneIsInRequest()
request.Headers.Add("Host", "graph.microsoft.com");
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var result = new SnippetCodeGraph(snippetModel);
Assert.False(result.HasHeaders());
}
@@ -105,7 +105,7 @@ public async Task ParsesParameters()
using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users/19:4b6bed8d24574f6a9e436813cb2617d8?$select=displayName,givenName,postalCode,identities");
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var result = new SnippetCodeGraph(snippetModel);
var parameter = result.Parameters.First();
Assert.True(result.HasParameters());
@@ -121,13 +121,33 @@ public async Task ParsesParameters()
Assert.True(result.HasParameters());
}
+ [Fact]
+ public async Task ParsesQueryParametersWithSpaces()
+ {
+ using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/roleManagement/directory/roleAssignments?$filter=roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'&$expand=principal");
+
+ var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
+ var result = new SnippetCodeGraph(snippetModel);
+ var parameter = result.Parameters.First();
+
+ Assert.True(result.HasParameters());
+ Assert.Equal(2, result.Parameters.Count());
+
+ var expectedProperty1 = new CodeProperty { Name = "filter", Value = "roleDefinitionId eq '62e90394-69f5-4237-9190-012177145e10'", PropertyType = PropertyType.String, Children = null };
+ Assert.Equal(expectedProperty1, result.Parameters.First());
+
+ var expectedProperty2 = new CodeProperty { Name = "expand", Value = "principal", PropertyType = PropertyType.String, Children = null };
+ Assert.Equal(expectedProperty2, result.Parameters.Skip(1).Take(1).First());
+
+ }
+
[Fact]
public async Task HasParametersIsFalseWhenNoParamterExists()
{
using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users/19:4b6bed8d24574f6a9e436813cb2617d8");
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var result = new SnippetCodeGraph(snippetModel);
Assert.False(result.HasParameters());
}
@@ -142,7 +162,7 @@ public async Task ParsesBodyTypeBinary()
request.Content.Headers.ContentType = new("application/octet-stream");
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var result = new SnippetCodeGraph(snippetModel);
Assert.Equal(PropertyType.Binary, result.Body.PropertyType);
}
@@ -162,8 +182,7 @@ public async Task ParsesBodyWithoutProperContentType()
Content = new StringContent(sampleBody, Encoding.UTF8) // snippet missing content type
};
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var result = ModelGraphBuilder.BuildCodeGraph(snippetModel);
- ;
+ var result = new SnippetCodeGraph(snippetModel);
var expectedObject = new CodeProperty { Name = "MessagesPostRequestBody", Value = null, PropertyType = PropertyType.Object, Children = new List() };
@@ -196,7 +215,7 @@ public async Task ParsesBodyPropertyTypeString()
Content = new StringContent(TypesSample, Encoding.UTF8)
};
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
// meetingDuration should be a string
var property = findProperyInSnipet(snippetCodeGraph.Body, "meetingDuration");
@@ -214,7 +233,7 @@ public async Task ParsesBodyPropertyTypeNumber()
Content = new StringContent(TypesSample, Encoding.UTF8)
};
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
var property = findProperyInSnipet(snippetCodeGraph.Body, "minimumAttendeePercentage");
@@ -231,7 +250,7 @@ public async Task ParsesBodyPropertyTypeBoolean()
Content = new StringContent(TypesSample, Encoding.UTF8)
};
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
var property = findProperyInSnipet(snippetCodeGraph.Body, "suggestLocation");
@@ -248,7 +267,7 @@ public async Task ParsesBodyPropertyTypeObject()
Content = new StringContent(TypesSample, Encoding.UTF8)
};
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
var property = findProperyInSnipet(snippetCodeGraph.Body, "locationConstraint");
@@ -264,7 +283,7 @@ public async Task ParsesBodyPropertyTypeArray()
Content = new StringContent(TypesSample, Encoding.UTF8)
};
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
var property = findProperyInSnipet(snippetCodeGraph.Body, "attendees");
@@ -280,7 +299,7 @@ public async Task ParsesBodyPropertyTypeMap()
Content = new StringContent(TypesSample, Encoding.UTF8)
};
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
- var snippetCodeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
var property = findProperyInSnipet(snippetCodeGraph.Body, "additionalData");
diff --git a/CodeSnippetsReflection.OpenAPI.Test/SnippetModelTests.cs b/CodeSnippetsReflection.OpenAPI.Test/SnippetModelTests.cs
index 70eb9c947..4c7b90603 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/SnippetModelTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/SnippetModelTests.cs
@@ -96,4 +96,4 @@ public async Task GetsTheResponseSchema()
Assert.NotEmpty(snippetModel.ResponseSchema.Properties);
}
}
-}
\ No newline at end of file
+}
diff --git a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
index 9c30dba8a..ab9208acf 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
@@ -44,10 +44,7 @@ public async Task GeneratesTheCorrectFluentAPIPath()
}
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -76,10 +73,7 @@ public async Task GeneratesClassWithDefaultBodyWhenSchemaNotPresent()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -99,10 +93,7 @@ public async Task GeneratesTheCorrectFluentAPIPathForIndexedCollections()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -130,10 +121,7 @@ public async Task GeneratesTheGetMethodCall()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -149,10 +137,7 @@ public async Task GeneratesThePostMethodCall()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -168,10 +153,7 @@ public async Task GeneratesThePatchMethodCall()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -199,10 +181,7 @@ public async Task GeneratesTheDeleteMethodCall()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -250,10 +229,7 @@ public async Task WritesTheRequestPayload()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -299,11 +275,7 @@ public async Task WritesADouble()
}
";
-
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -327,10 +299,7 @@ public async Task GeneratesABinaryPayload()
}
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -357,10 +326,7 @@ public async Task GeneratesABase64UrlPayload()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -387,10 +353,7 @@ public async Task GeneratesADatePayload()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -427,11 +390,7 @@ public async Task GeneratesAnArrayPayloadInAdditionalData()
};
";
-
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -468,10 +427,7 @@ public async Task GeneratesAnArrayOfObjectsPayloadData()
}
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -495,11 +451,7 @@ public async Task GeneratesSelectQueryParameters()
}
";
-
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -525,10 +477,7 @@ public async Task GeneratesCountBooleanQueryParameters()
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -551,12 +500,8 @@ public async Task GeneratesSkipQueryParameters()
await graphServiceClient.users.get(configuration);
}
";
-
-
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -592,10 +537,7 @@ public async Task GeneratesRequestHeaders()
}
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -650,10 +592,7 @@ public async Task GenerateAdditionalData()
}
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -705,10 +644,7 @@ public async Task GeneratesEnumsWhenVariableIsEnum()
}
";
- Assert.Contains(
- expected.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty),
- result.Replace(" ", String.Empty).Replace(Environment.NewLine, String.Empty)
- );
+ AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
}
}
}
diff --git a/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj b/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj
index 3fa375f73..d3b8310d4 100644
--- a/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj
+++ b/CodeSnippetsReflection.OpenAPI/CodeSnippetsReflection.OpenAPI.csproj
@@ -8,7 +8,6 @@
-
diff --git a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
index a773c8fc4..a7d14ede9 100644
--- a/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
+++ b/CodeSnippetsReflection.OpenAPI/LanguageGenerators/TypeScriptGenerator.cs
@@ -33,7 +33,7 @@ public string GenerateCodeSnippet(SnippetModel snippetModel)
{
if (snippetModel == null) throw new ArgumentNullException("Argument snippetModel cannot be null");
- var codeGraph = ModelGraphBuilder.BuildCodeGraph(snippetModel);
+ var codeGraph = new SnippetCodeGraph(snippetModel);
var snippetBuilder = new StringBuilder(
"//THIS SNIPPET IS A PREVIEW FOR THE KIOTA BASED SDK. NON-PRODUCTION USE ONLY" + Environment.NewLine +
$"const {ClientVarName} = {ClientVarType}.init({{authProvider}});{Environment.NewLine}{Environment.NewLine}");
@@ -78,7 +78,7 @@ private static void WriteHeader(SnippetCodeGraph codeGraph, StringBuilder builde
builder.AppendLine($"{indentManager.GetIndent()}{RequestHeadersVarName} : {{");
indentManager.Indent();
foreach (var param in codeGraph.Headers)
- builder.AppendLine($"{indentManager.GetIndent()}\"{param.Name}\": \"{param.Value.Replace("\"", "\\\"")}\",");
+ builder.AppendLine($"{indentManager.GetIndent()}\"{param.Name}\": \"{param.Value.EscapeQuotes()}\",");
indentManager.Unindent();
builder.AppendLine($"{indentManager.GetIndent()}}}");
}
@@ -92,7 +92,7 @@ private static void WriteOptions(SnippetCodeGraph codeGraph, StringBuilder build
builder.AppendLine($"{indentManager.GetIndent()}{RequestOptionsVarName} : {{");
indentManager.Indent();
foreach (var param in codeGraph.Options)
- builder.AppendLine($"{indentManager.GetIndent()}\"{param.Name}\": \"{param.Value.Replace("\"", "\\\"")}\",");
+ builder.AppendLine($"{indentManager.GetIndent()}\"{param.Name}\": \"{param.Value.EscapeQuotes()}\",");
indentManager.Unindent();
builder.AppendLine($"{indentManager.GetIndent()}}}");
}
@@ -107,7 +107,7 @@ private static void WriteParameters(SnippetCodeGraph codeGraph, StringBuilder bu
builder.AppendLine($"{indentManager.GetIndent()}{RequestParametersVarName} : {{");
indentManager.Indent();
foreach (var param in codeGraph.Parameters)
- builder.AppendLine($"{indentManager.GetIndent()}{NormalizeJsonName(param.Name)}: \"{param.Value.Replace("\"", "\\\"")}\",");
+ builder.AppendLine($"{indentManager.GetIndent()}{NormalizeJsonName(param.Name)}: \"{param.Value.EscapeQuotes()}\",");
indentManager.Unindent();
builder.AppendLine($"{indentManager.GetIndent()}}}");
}
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
deleted file mode 100644
index 8757e93bf..000000000
--- a/CodeSnippetsReflection.OpenAPI/ModelGraph/ModelGraphBuilder.cs
+++ /dev/null
@@ -1,246 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text.Json;
-using System.Text.RegularExpressions;
-using CodeSnippetsReflection.StringExtensions;
-using Microsoft.OpenApi.Models;
-
-namespace CodeSnippetsReflection.OpenAPI.ModelGraph
-{
-
- public static class ModelGraphBuilder
- {
-
- private static readonly Regex nestedStatementRegex = new(@"(\w+)(\([^)]+\))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
-
- private static readonly CodeProperty EMPTY_PROPERTY = new() { Name = null , Value = null , Children = null, PropertyType = PropertyType.Default };
-
- public static SnippetCodeGraph BuildCodeGraph(SnippetModel snippetModel)
- {
- return new SnippetCodeGraph {
- ResponseSchema = snippetModel.ResponseSchema,
- HttpMethod = snippetModel.Method,
- Nodes = snippetModel.PathNodes,
- Headers = parseHeaders(snippetModel),
- Options = Enumerable.Empty(),
- Parameters = parseParameters(snippetModel),
- Body = parseBody(snippetModel)
- };
- }
-
- ///
- /// Parses Headers Filtering Out 'Host'
- ///
- private static IEnumerable parseHeaders(SnippetModel snippetModel)
- {
- return snippetModel.RequestHeaders.Where(h => !h.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
- .Select(h => new CodeProperty { Name = h.Key , Value = h.Value?.FirstOrDefault(), Children = null, PropertyType = PropertyType.String })
- .ToList();
- }
-
-
- private static List parseParameters(SnippetModel snippetModel)
- {
- var parameters = new List();
- if (!string.IsNullOrEmpty(snippetModel.QueryString))
- {
- var (queryString, replacements) = ReplaceNestedOdataQueryParameters(snippetModel.QueryString);
- foreach (var queryParam in queryString.TrimStart('?').Split('&', StringSplitOptions.RemoveEmptyEntries))
- {
- if (queryParam.Contains('='))
- {
- var kvPair = queryParam.Split('=', StringSplitOptions.RemoveEmptyEntries);
- parameters.Add(new() { Name = NormalizeQueryParameterName(kvPair[0]), Value = GetQueryParameterValue(kvPair[1], replacements), PropertyType = PropertyType.String });
- }
- else
- parameters.Add(new() { Name = NormalizeQueryParameterName(queryParam), Value = GetQueryParameterValue("undefined", replacements), PropertyType = PropertyType.String });
- }
-
- }
- return parameters;
- }
-
- private static string NormalizeQueryParameterName(string queryParam) => System.Web.HttpUtility.UrlDecode(queryParam.TrimStart('$').ToFirstCharacterLowerCase());
-
- private static (string, Dictionary) ReplaceNestedOdataQueryParameters(string queryParams)
- {
- var replacements = new Dictionary();
- var matches = nestedStatementRegex.Matches(queryParams);
- if (matches.Any())
- foreach (Match match in matches)
- {
- var key = match.Groups[1].Value;
- var value = match.Groups[2].Value;
- replacements.Add(key, value);
- queryParams = queryParams.Replace(value, string.Empty);
- }
- return (queryParams, replacements);
- }
-
- private static string GetQueryParameterValue(string originalValue, Dictionary replacements)
- {
- var escapedParam = System.Web.HttpUtility.UrlDecode(originalValue);
- if (escapedParam.Equals("true", StringComparison.OrdinalIgnoreCase) || escapedParam.Equals("false", StringComparison.OrdinalIgnoreCase))
- return escapedParam.ToLowerInvariant();
- else if (int.TryParse(escapedParam, out var intValue))
- return intValue.ToString();
- else
- {
- return escapedParam.Split(',')
- .Select(v => replacements.ContainsKey(v) ? v + replacements[v] : v)
- .Aggregate((a, b) => $"{a},{b}");
- }
- }
-
- private static CodeProperty parseBody(SnippetModel snippetModel)
- {
- if (string.IsNullOrWhiteSpace(snippetModel?.RequestBody))
- return EMPTY_PROPERTY;
-
- switch (snippetModel.ContentType?.Split(';').First().ToLowerInvariant())
- {
- case "application/json":
- return TryParseBody(snippetModel);
- case "application/octet-stream":
- return new() { Name = null, Value = snippetModel.RequestBody?.ToString(), Children = null, PropertyType = PropertyType.Binary };
- default:
- return TryParseBody(snippetModel);//in case the content type header is missing but we still have a json payload
- }
- }
-
- private static string ComputeRequestBody(SnippetModel snippetModel)
- {
- var nodes = snippetModel.PathNodes;
- if (!(nodes?.Any() ?? false)) return string.Empty;
-
- var nodeName = nodes.Where(x => !x.Segment.IsCollectionIndex())
- .Select(x =>
- {
- if (x.Segment.IsFunction())
- return x.Segment.Split('.').Last();
- else
- return x.Segment;
- })
- .Last()
- .ToFirstCharacterUpperCase();
-
- var singularNodeName = nodeName[nodeName.Length - 1] == 's' ? nodeName.Substring(0, nodeName.Length - 1) : nodeName;
-
- if (nodes.Last()?.Segment?.IsCollectionIndex() == true)
- return singularNodeName;
- else
- return $"{nodeName}PostRequestBody";
-
- }
-
- private static CodeProperty TryParseBody(SnippetModel snippetModel)
- {
- if (!snippetModel.IsRequestBodyValid)
- throw new InvalidOperationException($"Unsupported content type: {snippetModel.ContentType}");
-
- using var parsedBody = JsonDocument.Parse(snippetModel.RequestBody, new JsonDocumentOptions { AllowTrailingCommas = true });
- var schema = snippetModel.RequestSchema;
- var className = schema.GetSchemaTitle().ToFirstCharacterUpperCase() ?? ComputeRequestBody(snippetModel);
- return parseJsonObjectValue(className, parsedBody.RootElement, schema);
- }
-
- private static CodeProperty parseJsonObjectValue(String rootPropertyName, JsonElement value, OpenApiSchema schema)
- {
- var children = new List();
-
- if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
-
- var propertiesAndSchema = value.EnumerateObject()
- .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
- foreach (var propertyAndSchema in propertiesAndSchema.Where(x => x.Item2 != null))
- {
- var propertyName = propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase();
- children.Add(parseProperty(propertyName, propertyAndSchema.Item1.Value, propertyAndSchema.Item2));
- }
-
- var propertiesWithoutSchema = propertiesAndSchema.Where(x => x.Item2 == null).Select(x => x.Item1);
- if (propertiesWithoutSchema.Any())
- {
-
- var additionalChildren = new List();
- foreach (var property in propertiesWithoutSchema)
- additionalChildren.Add(parseProperty(property.Name, property.Value, null));
-
- if (additionalChildren.Any())
- children.Add(new CodeProperty { Name = "additionalData", PropertyType = PropertyType.Map, Children = additionalChildren });
- }
-
- return new CodeProperty { Name = rootPropertyName, PropertyType = PropertyType.Object, Children = children };
- }
-
- private static String escapeSpecialCharacters(string value)
- {
- return value?.Replace("\"", "\\\"")?.Replace("\n", "\\n")?.Replace("\r", "\\r");
- }
-
- private static CodeProperty evaluateStringProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
- {
- if (propSchema?.Format?.Equals("base64url", StringComparison.OrdinalIgnoreCase) ?? false)
- return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Base64Url, Children = new List() };
-
- if (propSchema?.Format?.Equals("date-time", StringComparison.OrdinalIgnoreCase) ?? false)
- return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Date, Children = new List() };
-
-
- var enumSchema = propSchema?.AnyOf.FirstOrDefault(x => x.Enum.Count > 0);
- if (enumSchema == null)
- return new CodeProperty { Name = propertyName, Value = escapeSpecialCharacters(value.GetString()), PropertyType = PropertyType.String, Children = new List() };
-
-
- var propValue = String.IsNullOrWhiteSpace(value.GetString()) ? null : $"{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}";
- return new CodeProperty { Name = propertyName, Value = propValue, PropertyType = PropertyType.Enum, Children = new List() };
- }
-
- private static CodeProperty parseProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
- {
- switch (value.ValueKind)
- {
- case JsonValueKind.String:
- return evaluateStringProperty(propertyName, value, propSchema);
- case JsonValueKind.Number:
- return new CodeProperty { Name = propertyName, Value = $"{value}", PropertyType = PropertyType.Number, Children = new List() };
- case JsonValueKind.False:
- case JsonValueKind.True:
- return new CodeProperty { Name = propertyName, Value = value.GetBoolean().ToString(), PropertyType = PropertyType.Boolean, Children = new List() };
- case JsonValueKind.Null:
- return new CodeProperty { Name = propertyName, Value = "null", PropertyType = PropertyType.Null, Children = new List() };
- case JsonValueKind.Object:
- if (propSchema != null)
- return parseJsonObjectValue(propertyName, value, propSchema);
- else
- return parseAnonymousObjectValues(propertyName, value, propSchema);
- case JsonValueKind.Array:
- return parseJsonArrayValue(propertyName, value, propSchema);
- default:
- throw new NotImplementedException($"Unsupported JsonValueKind: {value.ValueKind}");
- }
- }
-
- private static CodeProperty parseJsonArrayValue(string propertyName, JsonElement value, OpenApiSchema schema)
- {
- var children = value.EnumerateArray().Select(item => parseProperty(schema.GetSchemaTitle().ToFirstCharacterUpperCase(), item, schema)).ToList();
- return new CodeProperty { Name = propertyName, Value = null, PropertyType = PropertyType.Array, Children = children };
- }
-
- private static CodeProperty parseAnonymousObjectValues(string propertyName, JsonElement value, OpenApiSchema schema)
- {
- if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
-
- var children = new List();
- var propertiesAndSchema = value.EnumerateObject()
- .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
- foreach (var propertyAndSchema in propertiesAndSchema)
- {
- children.Add(parseProperty(propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase(), propertyAndSchema.Item1.Value, propertyAndSchema.Item2));
- }
-
- return new CodeProperty { Name = propertyName, Value = null, PropertyType = PropertyType.Object, Children = children };
- }
- }
-}
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
index 3b1551644..b5c65283d 100644
--- a/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
@@ -1,14 +1,35 @@
using System;
using System.Collections.Generic;
+using System.Collections.Specialized;
using System.Linq;
using System.Net.Http;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Web;
+using CodeSnippetsReflection.StringExtensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;
namespace CodeSnippetsReflection.OpenAPI.ModelGraph
{
- public class SnippetCodeGraph
+ public record SnippetCodeGraph
{
+
+ private static readonly Regex nestedStatementRegex = new(@"(\w+)(\([^)]+\))", RegexOptions.IgnoreCase | RegexOptions.Compiled);
+
+ private static readonly CodeProperty EMPTY_PROPERTY = new() { Name = null, Value = null, Children = null, PropertyType = PropertyType.Default };
+
+ public SnippetCodeGraph(SnippetModel snippetModel)
+ {
+ ResponseSchema = snippetModel.ResponseSchema;
+ HttpMethod = snippetModel.Method;
+ Nodes = snippetModel.PathNodes;
+ Headers = parseHeaders(snippetModel);
+ Options = Enumerable.Empty();
+ Parameters = parseParameters(snippetModel);
+ Body = parseBody(snippetModel);
+ }
+
public OpenApiSchema ResponseSchema
{
get; set;
@@ -45,17 +66,17 @@ public IEnumerable Nodes
public Boolean HasHeaders()
{
- return Headers.Any() == true;
+ return Headers.Any();
}
public Boolean HasOptions()
{
- return Options.Any() == true;
+ return Options.Any();
}
public Boolean HasParameters()
{
- return Parameters.Any() == true;
+ return Parameters.Any();
}
public Boolean HasBody()
@@ -63,5 +84,216 @@ public Boolean HasBody()
return Body.PropertyType != PropertyType.Default;
}
+
+ ///
+ /// Parses Headers Filtering Out 'Host'
+ ///
+ private static IEnumerable parseHeaders(SnippetModel snippetModel)
+ {
+ return snippetModel.RequestHeaders.Where(h => !h.Key.Equals("Host", StringComparison.OrdinalIgnoreCase))
+ .Select(h => new CodeProperty { Name = h.Key, Value = h.Value?.FirstOrDefault(), Children = null, PropertyType = PropertyType.String })
+ .ToList();
+ }
+
+
+ private static List parseParameters(SnippetModel snippetModel)
+ {
+ var parameters = new List();
+ if (!string.IsNullOrEmpty(snippetModel.QueryString))
+ {
+ var (queryString, replacements) = ReplaceNestedOdataQueryParameters(snippetModel.QueryString);
+
+ NameValueCollection queryCollection = HttpUtility.ParseQueryString(queryString);
+ foreach (String key in queryCollection.AllKeys)
+ {
+ parameters.Add(new() { Name = NormalizeQueryParameterName(key), Value = GetQueryParameterValue(queryCollection[key], replacements), PropertyType = PropertyType.String });
+ }
+
+ }
+ return parameters;
+ }
+
+ private static string NormalizeQueryParameterName(string queryParam) => System.Web.HttpUtility.UrlDecode(queryParam.TrimStart('$').ToFirstCharacterLowerCase());
+
+ private static (string, Dictionary) ReplaceNestedOdataQueryParameters(string queryParams)
+ {
+ var replacements = new Dictionary();
+ var matches = nestedStatementRegex.Matches(queryParams);
+ if (matches.Any())
+ foreach (Match match in matches)
+ {
+ var key = match.Groups[1].Value;
+ var value = match.Groups[2].Value;
+ replacements.Add(key, value);
+ queryParams = queryParams.Replace(value, string.Empty);
+ }
+ return (queryParams, replacements);
+ }
+
+ private static string GetQueryParameterValue(string originalValue, Dictionary replacements)
+ {
+ var escapedParam = System.Web.HttpUtility.UrlDecode(originalValue);
+ if (escapedParam.Equals("true", StringComparison.OrdinalIgnoreCase) || escapedParam.Equals("false", StringComparison.OrdinalIgnoreCase))
+ return escapedParam.ToLowerInvariant();
+ else if (int.TryParse(escapedParam, out var intValue))
+ return intValue.ToString();
+ else
+ {
+ return escapedParam.Split(',')
+ .Select(v => replacements.ContainsKey(v) ? v + replacements[v] : v)
+ .Aggregate((a, b) => $"{a},{b}");
+ }
+ }
+
+ private static CodeProperty parseBody(SnippetModel snippetModel)
+ {
+ if (string.IsNullOrWhiteSpace(snippetModel?.RequestBody))
+ return EMPTY_PROPERTY;
+
+ switch (snippetModel.ContentType?.Split(';').First().ToLowerInvariant())
+ {
+ case "application/json":
+ return TryParseBody(snippetModel);
+ case "application/octet-stream":
+ return new() { Name = null, Value = snippetModel.RequestBody?.ToString(), Children = null, PropertyType = PropertyType.Binary };
+ default:
+ return TryParseBody(snippetModel);//in case the content type header is missing but we still have a json payload
+ }
+ }
+
+ private static string ComputeRequestBody(SnippetModel snippetModel)
+ {
+ var nodes = snippetModel.PathNodes;
+ if (!(nodes?.Any() ?? false)) return string.Empty;
+
+ var nodeName = nodes.Where(x => !x.Segment.IsCollectionIndex())
+ .Select(x =>
+ {
+ if (x.Segment.IsFunction())
+ return x.Segment.Split('.').Last();
+ else
+ return x.Segment;
+ })
+ .Last()
+ .ToFirstCharacterUpperCase();
+
+ var singularNodeName = nodeName[nodeName.Length - 1] == 's' ? nodeName.Substring(0, nodeName.Length - 1) : nodeName;
+
+ if (nodes.Last()?.Segment?.IsCollectionIndex() == true)
+ return singularNodeName;
+ else
+ return $"{nodeName}PostRequestBody";
+
+ }
+
+ private static CodeProperty TryParseBody(SnippetModel snippetModel)
+ {
+ if (!snippetModel.IsRequestBodyValid)
+ throw new InvalidOperationException($"Unsupported content type: {snippetModel.ContentType}");
+
+ using var parsedBody = JsonDocument.Parse(snippetModel.RequestBody, new JsonDocumentOptions { AllowTrailingCommas = true });
+ var schema = snippetModel.RequestSchema;
+ var className = schema.GetSchemaTitle().ToFirstCharacterUpperCase() ?? ComputeRequestBody(snippetModel);
+ return parseJsonObjectValue(className, parsedBody.RootElement, schema);
+ }
+
+ private static CodeProperty parseJsonObjectValue(String rootPropertyName, JsonElement value, OpenApiSchema schema)
+ {
+ var children = new List();
+
+ if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
+
+ var propertiesAndSchema = value.EnumerateObject()
+ .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
+ foreach (var propertyAndSchema in propertiesAndSchema.Where(x => x.Item2 != null))
+ {
+ var propertyName = propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase();
+ children.Add(parseProperty(propertyName, propertyAndSchema.Item1.Value, propertyAndSchema.Item2));
+ }
+
+ var propertiesWithoutSchema = propertiesAndSchema.Where(x => x.Item2 == null).Select(x => x.Item1);
+ if (propertiesWithoutSchema.Any())
+ {
+
+ var additionalChildren = new List();
+ foreach (var property in propertiesWithoutSchema)
+ additionalChildren.Add(parseProperty(property.Name, property.Value, null));
+
+ if (additionalChildren.Any())
+ children.Add(new CodeProperty { Name = "additionalData", PropertyType = PropertyType.Map, Children = additionalChildren });
+ }
+
+ return new CodeProperty { Name = rootPropertyName, PropertyType = PropertyType.Object, Children = children };
+ }
+
+ private static String escapeSpecialCharacters(string value)
+ {
+ return value?.Replace("\"", "\\\"")?.Replace("\n", "\\n")?.Replace("\r", "\\r");
+ }
+
+ private static CodeProperty evaluateStringProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
+ {
+ if (propSchema?.Format?.Equals("base64url", StringComparison.OrdinalIgnoreCase) ?? false)
+ return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Base64Url, Children = new List() };
+
+ if (propSchema?.Format?.Equals("date-time", StringComparison.OrdinalIgnoreCase) ?? false)
+ return new CodeProperty { Name = propertyName, Value = value.GetString(), PropertyType = PropertyType.Date, Children = new List() };
+
+
+ var enumSchema = propSchema?.AnyOf.FirstOrDefault(x => x.Enum.Count > 0);
+ if (enumSchema == null)
+ return new CodeProperty { Name = propertyName, Value = escapeSpecialCharacters(value.GetString()), PropertyType = PropertyType.String, Children = new List() };
+
+
+ var propValue = String.IsNullOrWhiteSpace(value.GetString()) ? null : $"{enumSchema.Title.ToFirstCharacterUpperCase()}.{value.GetString().ToFirstCharacterUpperCase()}";
+ return new CodeProperty { Name = propertyName, Value = propValue, PropertyType = PropertyType.Enum, Children = new List() };
+ }
+
+ private static CodeProperty parseProperty(string propertyName, JsonElement value, OpenApiSchema propSchema)
+ {
+ switch (value.ValueKind)
+ {
+ case JsonValueKind.String:
+ return evaluateStringProperty(propertyName, value, propSchema);
+ case JsonValueKind.Number:
+ return new CodeProperty { Name = propertyName, Value = $"{value}", PropertyType = PropertyType.Number, Children = new List() };
+ case JsonValueKind.False:
+ case JsonValueKind.True:
+ return new CodeProperty { Name = propertyName, Value = value.GetBoolean().ToString(), PropertyType = PropertyType.Boolean, Children = new List() };
+ case JsonValueKind.Null:
+ return new CodeProperty { Name = propertyName, Value = "null", PropertyType = PropertyType.Null, Children = new List() };
+ case JsonValueKind.Object:
+ if (propSchema != null)
+ return parseJsonObjectValue(propertyName, value, propSchema);
+ else
+ return parseAnonymousObjectValues(propertyName, value, propSchema);
+ case JsonValueKind.Array:
+ return parseJsonArrayValue(propertyName, value, propSchema);
+ default:
+ throw new NotImplementedException($"Unsupported JsonValueKind: {value.ValueKind}");
+ }
+ }
+
+ private static CodeProperty parseJsonArrayValue(string propertyName, JsonElement value, OpenApiSchema schema)
+ {
+ var children = value.EnumerateArray().Select(item => parseProperty(schema.GetSchemaTitle().ToFirstCharacterUpperCase(), item, schema)).ToList();
+ return new CodeProperty { Name = propertyName, Value = null, PropertyType = PropertyType.Array, Children = children };
+ }
+
+ private static CodeProperty parseAnonymousObjectValues(string propertyName, JsonElement value, OpenApiSchema schema)
+ {
+ if (value.ValueKind != JsonValueKind.Object) throw new InvalidOperationException($"Expected JSON object and got {value.ValueKind}");
+
+ var children = new List();
+ var propertiesAndSchema = value.EnumerateObject()
+ .Select(x => new Tuple(x, schema.GetPropertySchema(x.Name)));
+ foreach (var propertyAndSchema in propertiesAndSchema)
+ {
+ children.Add(parseProperty(propertyAndSchema.Item1.Name.ToFirstCharacterLowerCase(), propertyAndSchema.Item1.Value, propertyAndSchema.Item2));
+ }
+
+ return new CodeProperty { Name = propertyName, Value = null, PropertyType = PropertyType.Object, Children = children };
+ }
}
+
}
diff --git a/CodeSnippetsReflection/StringExtensions/StringExtensions.cs b/CodeSnippetsReflection/StringExtensions/StringExtensions.cs
index dbec33690..3f8e1a9dd 100644
--- a/CodeSnippetsReflection/StringExtensions/StringExtensions.cs
+++ b/CodeSnippetsReflection/StringExtensions/StringExtensions.cs
@@ -34,5 +34,10 @@ public static string ToFirstCharacterUpperCaseAfterCharacter(this string stringV
if (charIndex < 0) return stringValue;
return stringValue[0..charIndex] + char.ToUpper(stringValue[charIndex + 1]) + stringValue[(charIndex + 2)..].ToFirstCharacterUpperCaseAfterCharacter(character);
}
+
+ public static string EscapeQuotes(this string stringValue)
+ {
+ return stringValue.Replace("\"", "\\\"");
+ }
}
}
From 46f91f48884d2a465764cceefd49e52bff06cddd Mon Sep 17 00:00:00 2001
From: Ronald K <43806892+rkodev@users.noreply.github.com>
Date: Tue, 31 May 2022 16:06:45 +0300
Subject: [PATCH 09/14] Update
CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
Co-authored-by: Vincent Biret
---
.../ModelGraph/SnippetCodeGraph.cs | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
index b5c65283d..03627ff25 100644
--- a/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
@@ -120,10 +120,10 @@ private static (string, Dictionary) ReplaceNestedOdataQueryParam
var replacements = new Dictionary();
var matches = nestedStatementRegex.Matches(queryParams);
if (matches.Any())
- foreach (Match match in matches)
+ foreach (var groups in matches.Select(m => m.Groups)))
{
- var key = match.Groups[1].Value;
- var value = match.Groups[2].Value;
+ var key = groups[1].Value;
+ var value = groups[2].Value;
replacements.Add(key, value);
queryParams = queryParams.Replace(value, string.Empty);
}
From 9289f85429445fe9e77a2296a06e3cc1b9d7f4b4 Mon Sep 17 00:00:00 2001
From: Vincent Biret
Date: Tue, 31 May 2022 09:13:11 -0400
Subject: [PATCH 10/14] - additional parenthesis fix
---
CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
index 03627ff25..0233a00b4 100644
--- a/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
+++ b/CodeSnippetsReflection.OpenAPI/ModelGraph/SnippetCodeGraph.cs
@@ -120,7 +120,7 @@ private static (string, Dictionary) ReplaceNestedOdataQueryParam
var replacements = new Dictionary();
var matches = nestedStatementRegex.Matches(queryParams);
if (matches.Any())
- foreach (var groups in matches.Select(m => m.Groups)))
+ foreach (var groups in matches.Select(m => m.Groups))
{
var key = groups[1].Value;
var value = groups[2].Value;
From 3483edd3bfd90008f86f388a792a3a61248e675d Mon Sep 17 00:00:00 2001
From: Ronald K <43806892+rkodev@users.noreply.github.com>
Date: Tue, 31 May 2022 16:23:39 +0300
Subject: [PATCH 11/14] Update
CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
Co-authored-by: Vincent Biret
---
CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
index 60566dbef..55d36fed1 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
@@ -199,8 +199,7 @@ public async Task ParsesBodyWithoutProperContentType()
{
foreach (var param in codeProperty.Children)
{
- var result = findProperyInSnipet(param, name);
- if (result != null) return result;
+ if(findProperyInSnipet(param, name) is CodeProperty result) return result;
}
}
From f5483d4fc4084b34b194254db20d1a7fd7e964b7 Mon Sep 17 00:00:00 2001
From: Ronald K <43806892+rkodev@users.noreply.github.com>
Date: Thu, 2 Jun 2022 12:50:20 +0300
Subject: [PATCH 12/14] Update
CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
Co-authored-by: Mustafa Zengin
---
CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
index 55d36fed1..59916fbaa 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
@@ -88,7 +88,7 @@ public async Task ParsesHeaders()
}
[Fact]
- public async Task HasHeadeIsFalseWhenNoneIsInRequest()
+ public async Task HasHeadersIsFalseWhenNoneIsInRequest()
{
using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users");
request.Headers.Add("Host", "graph.microsoft.com");
From 035fea4da1e1e3e3db1e7ea46b11ec4a7c746746 Mon Sep 17 00:00:00 2001
From: Ronald K <43806892+rkodev@users.noreply.github.com>
Date: Thu, 2 Jun 2022 12:50:31 +0300
Subject: [PATCH 13/14] Rename AssertUtil to AssertExtenstions
---
.../{AssertUtil.cs => AssertExtensions.cs} | 2 +-
.../SnippetCodeGraphTests.cs | 46 ++++++++-----------
.../TypeScriptGeneratorTest.cs | 40 ++++++++--------
3 files changed, 41 insertions(+), 47 deletions(-)
rename CodeSnippetsReflection.OpenAPI.Test/{AssertUtil.cs => AssertExtensions.cs} (93%)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/AssertUtil.cs b/CodeSnippetsReflection.OpenAPI.Test/AssertExtensions.cs
similarity index 93%
rename from CodeSnippetsReflection.OpenAPI.Test/AssertUtil.cs
rename to CodeSnippetsReflection.OpenAPI.Test/AssertExtensions.cs
index d7cf7f26f..d45d4fae1 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/AssertUtil.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/AssertExtensions.cs
@@ -7,7 +7,7 @@
namespace CodeSnippetsReflection.OpenAPI.Test
{
- public static class AssertUtil
+ public static class AssertExtensions
{
public static void ContainsIgnoreWhiteSpace(string expectedSubstring, string actualString)
{
diff --git a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
index 59916fbaa..94c3fc62f 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
@@ -137,12 +137,12 @@ public async Task ParsesQueryParametersWithSpaces()
Assert.Equal(expectedProperty1, result.Parameters.First());
var expectedProperty2 = new CodeProperty { Name = "expand", Value = "principal", PropertyType = PropertyType.String, Children = null };
- Assert.Equal(expectedProperty2, result.Parameters.Skip(1).Take(1).First());
+ Assert.Equal(expectedProperty2, result.Parameters.Skip(1).First());
}
[Fact]
- public async Task HasParametersIsFalseWhenNoParamterExists()
+ public async Task HasParametersIsFalseWhenNoParameterExists()
{
using var request = new HttpRequestMessage(HttpMethod.Get, $"{ServiceRootUrl}/users/19:4b6bed8d24574f6a9e436813cb2617d8");
@@ -191,7 +191,7 @@ public async Task ParsesBodyWithoutProperContentType()
Assert.Equal(expectedObject.PropertyType, result.Body.PropertyType);
}
- private CodeProperty? findProperyInSnipet(CodeProperty codeProperty, string name)
+ private CodeProperty FindPropertyInSnippet(CodeProperty codeProperty, string name)
{
if (codeProperty.Name == name) return codeProperty;
@@ -199,11 +199,11 @@ public async Task ParsesBodyWithoutProperContentType()
{
foreach (var param in codeProperty.Children)
{
- if(findProperyInSnipet(param, name) is CodeProperty result) return result;
+ if(FindPropertyInSnippet(param, name) is CodeProperty result) return result;
}
}
- return null;
+ throw new ArgumentException("Property does not containt child " + name);
}
[Fact]
@@ -217,11 +217,10 @@ public async Task ParsesBodyPropertyTypeString()
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
// meetingDuration should be a string
- var property = findProperyInSnipet(snippetCodeGraph.Body, "meetingDuration");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "meetingDuration");
- Assert.NotNull(property);
- Assert.Equal(PropertyType.String, property?.PropertyType);
- Assert.Equal("PT1H", property?.Value);
+ Assert.Equal(PropertyType.String, property.PropertyType);
+ Assert.Equal("PT1H", property.Value);
}
[Fact]
@@ -234,11 +233,10 @@ public async Task ParsesBodyPropertyTypeNumber()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = findProperyInSnipet(snippetCodeGraph.Body, "minimumAttendeePercentage");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "minimumAttendeePercentage");
- Assert.NotNull(property);
- Assert.Equal(PropertyType.Number, property?.PropertyType);
- Assert.Equal("100" , property?.Value);
+ Assert.Equal(PropertyType.Number, property.PropertyType);
+ Assert.Equal("100" , property.Value);
}
[Fact]
@@ -251,11 +249,10 @@ public async Task ParsesBodyPropertyTypeBoolean()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = findProperyInSnipet(snippetCodeGraph.Body, "suggestLocation");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "suggestLocation");
- Assert.NotNull(property);
- Assert.Equal(PropertyType.Boolean, property?.PropertyType);
- Assert.Equal("False", property?.Value);
+ Assert.Equal(PropertyType.Boolean, property.PropertyType);
+ Assert.Equal("False", property.Value);
}
[Fact]
@@ -268,10 +265,9 @@ public async Task ParsesBodyPropertyTypeObject()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = findProperyInSnipet(snippetCodeGraph.Body, "locationConstraint");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "locationConstraint");
- Assert.NotNull(property);
- Assert.Equal(PropertyType.Object, property?.PropertyType);
+ Assert.Equal(PropertyType.Object, property.PropertyType);
}
[Fact]
@@ -284,10 +280,9 @@ public async Task ParsesBodyPropertyTypeArray()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = findProperyInSnipet(snippetCodeGraph.Body, "attendees");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "attendees");
- Assert.NotNull(property);
- Assert.Equal(PropertyType.Array, property?.PropertyType);
+ Assert.Equal(PropertyType.Array, property.PropertyType);
}
[Fact]
@@ -300,10 +295,9 @@ public async Task ParsesBodyPropertyTypeMap()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = findProperyInSnipet(snippetCodeGraph.Body, "additionalData");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "additionalData");
- Assert.NotNull(property);
- Assert.Equal(PropertyType.Map, property?.PropertyType);
+ Assert.Equal(PropertyType.Map, property.PropertyType);
}
}
diff --git a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
index ab9208acf..363752e49 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/TypeScriptGeneratorTest.cs
@@ -44,7 +44,7 @@ public async Task GeneratesTheCorrectFluentAPIPath()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -73,7 +73,7 @@ public async Task GeneratesClassWithDefaultBodyWhenSchemaNotPresent()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -93,7 +93,7 @@ public async Task GeneratesTheCorrectFluentAPIPathForIndexedCollections()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -121,7 +121,7 @@ public async Task GeneratesTheGetMethodCall()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -137,7 +137,7 @@ public async Task GeneratesThePostMethodCall()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -153,7 +153,7 @@ public async Task GeneratesThePatchMethodCall()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -181,7 +181,7 @@ public async Task GeneratesTheDeleteMethodCall()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -229,7 +229,7 @@ public async Task WritesTheRequestPayload()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -275,7 +275,7 @@ public async Task WritesADouble()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -299,7 +299,7 @@ public async Task GeneratesABinaryPayload()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -326,7 +326,7 @@ public async Task GeneratesABase64UrlPayload()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -353,7 +353,7 @@ public async Task GeneratesADatePayload()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -390,7 +390,7 @@ public async Task GeneratesAnArrayPayloadInAdditionalData()
};
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -427,7 +427,7 @@ public async Task GeneratesAnArrayOfObjectsPayloadData()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -451,7 +451,7 @@ public async Task GeneratesSelectQueryParameters()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -477,7 +477,7 @@ public async Task GeneratesCountBooleanQueryParameters()
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -501,7 +501,7 @@ public async Task GeneratesSkipQueryParameters()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -537,7 +537,7 @@ public async Task GeneratesRequestHeaders()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -592,7 +592,7 @@ public async Task GenerateAdditionalData()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
[Fact]
@@ -644,7 +644,7 @@ public async Task GeneratesEnumsWhenVariableIsEnum()
}
";
- AssertUtil.ContainsIgnoreWhiteSpace(expected, result);
+ AssertExtensions.ContainsIgnoreWhiteSpace(expected, result);
}
}
}
From f5548a02a883ce889646eba33d94c3cfb3083c6e Mon Sep 17 00:00:00 2001
From: rkodev <43806892+rkodev@users.noreply.github.com>
Date: Thu, 2 Jun 2022 15:07:20 +0300
Subject: [PATCH 14/14] Fix broken tests
---
.../SnippetCodeGraphTests.cs | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
index 94c3fc62f..1adf45e2f 100644
--- a/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
+++ b/CodeSnippetsReflection.OpenAPI.Test/SnippetCodeGraphTests.cs
@@ -191,7 +191,7 @@ public async Task ParsesBodyWithoutProperContentType()
Assert.Equal(expectedObject.PropertyType, result.Body.PropertyType);
}
- private CodeProperty FindPropertyInSnippet(CodeProperty codeProperty, string name)
+ private CodeProperty? FindPropertyInSnippet(CodeProperty codeProperty, string name)
{
if (codeProperty.Name == name) return codeProperty;
@@ -203,7 +203,7 @@ private CodeProperty FindPropertyInSnippet(CodeProperty codeProperty, string nam
}
}
- throw new ArgumentException("Property does not containt child " + name);
+ return null;
}
[Fact]
@@ -217,7 +217,7 @@ public async Task ParsesBodyPropertyTypeString()
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
// meetingDuration should be a string
- var property = FindPropertyInSnippet(snippetCodeGraph.Body, "meetingDuration");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "meetingDuration").Value;
Assert.Equal(PropertyType.String, property.PropertyType);
Assert.Equal("PT1H", property.Value);
@@ -233,7 +233,7 @@ public async Task ParsesBodyPropertyTypeNumber()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = FindPropertyInSnippet(snippetCodeGraph.Body, "minimumAttendeePercentage");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "minimumAttendeePercentage").Value;
Assert.Equal(PropertyType.Number, property.PropertyType);
Assert.Equal("100" , property.Value);
@@ -249,7 +249,7 @@ public async Task ParsesBodyPropertyTypeBoolean()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = FindPropertyInSnippet(snippetCodeGraph.Body, "suggestLocation");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "suggestLocation").Value;
Assert.Equal(PropertyType.Boolean, property.PropertyType);
Assert.Equal("False", property.Value);
@@ -265,7 +265,7 @@ public async Task ParsesBodyPropertyTypeObject()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = FindPropertyInSnippet(snippetCodeGraph.Body, "locationConstraint");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "locationConstraint").Value;
Assert.Equal(PropertyType.Object, property.PropertyType);
}
@@ -280,7 +280,7 @@ public async Task ParsesBodyPropertyTypeArray()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = FindPropertyInSnippet(snippetCodeGraph.Body, "attendees");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "attendees").Value;
Assert.Equal(PropertyType.Array, property.PropertyType);
}
@@ -295,7 +295,7 @@ public async Task ParsesBodyPropertyTypeMap()
var snippetModel = new SnippetModel(request, ServiceRootUrl, await GetV1TreeNode());
var snippetCodeGraph = new SnippetCodeGraph(snippetModel);
- var property = FindPropertyInSnippet(snippetCodeGraph.Body, "additionalData");
+ var property = FindPropertyInSnippet(snippetCodeGraph.Body, "additionalData").Value;
Assert.Equal(PropertyType.Map, property.PropertyType);
}