diff --git a/.editorconfig b/.editorconfig
index 9968717e38..63a79b9250 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -3,31 +3,19 @@ root=true
[*]
charset = utf-8
end_of_line = lf
-indent_style = tab
-indent_size = 4
-insert_final_newline=true
-
-[*.cs]
-trim_trailing_whitespace=true
-
-[*.cshtml]
-indent_style = tab
-indent_size = 4
-
-[*.{fs,fsx}]
indent_style = space
indent_size = 4
+insert_final_newline=true
[*.yml]
-indent_style = space
indent_size = 2
-[*.{md,markdown,json,js,csproj,fsproj,targets,targets,props}]
-indent_style = space
+[*.{md,markdown,json,js,csproj,fsproj,targets,props}]
indent_size = 2
# Dotnet code style settings:
[*.{cs,vb}]
+trim_trailing_whitespace = true
# ---
# naming conventions https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-naming-conventions
@@ -171,7 +159,7 @@ csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
-# just a suggestion do to our JSON tests that use anonymous types to
+# just a suggestion do to our JSON tests that use anonymous types to
# represent json quite a bit (makes copy paste easier).
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
diff --git a/src/ApiGenerator/ApiGenerator.csproj b/src/ApiGenerator/ApiGenerator.csproj
index 25b9546a0e..64ed0d5220 100644
--- a/src/ApiGenerator/ApiGenerator.csproj
+++ b/src/ApiGenerator/ApiGenerator.csproj
@@ -4,7 +4,7 @@
Exe
net6.0
false
-
+
CS1591;NU1701
true
@@ -12,7 +12,8 @@
-
+
+
diff --git a/src/ApiGenerator/Configuration/GeneratorLocations.cs b/src/ApiGenerator/Configuration/GeneratorLocations.cs
index f97b7d80df..8032c3df93 100644
--- a/src/ApiGenerator/Configuration/GeneratorLocations.cs
+++ b/src/ApiGenerator/Configuration/GeneratorLocations.cs
@@ -31,44 +31,42 @@
namespace ApiGenerator.Configuration
{
- public static class GeneratorLocations
- {
- // @formatter:off — disable formatter after this line
- public static string OpenApiSpecFile { get; } = $@"{Root}OpenSearch.openapi.json";
+ public static class GeneratorLocations
+ {
+ public static string OpenApiSpecFile { get; } = $"{Root}opensearch-openapi.yaml";
- public static string OpenSearchNetFolder { get; } = $@"{Root}../../src/OpenSearch.Net/";
+ public static string OpenSearchNetFolder { get; } = $"{Root}../../src/OpenSearch.Net/";
- public static string OpenSearchClientFolder { get; } = $@"{Root}../../src/OpenSearch.Client/";
+ public static string OpenSearchClientFolder { get; } = $"{Root}../../src/OpenSearch.Client/";
public static string LowLevelGeneratedFolder { get; } = $"{OpenSearchNetFolder}_Generated/";
public static string HighLevelGeneratedFolder { get; } = $"{OpenSearchClientFolder}_Generated/";
- // @formatter:on — enable formatter after this line
+ public static string HighLevel(params string[] paths) => HighLevelGeneratedFolder + string.Join("/", paths);
- public static string HighLevel(params string[] paths) => HighLevelGeneratedFolder + string.Join("/", paths);
- public static string LowLevel(params string[] paths) => LowLevelGeneratedFolder + string.Join("/", paths);
+ public static string LowLevel(params string[] paths) => LowLevelGeneratedFolder + string.Join("/", paths);
- public static readonly Assembly Assembly = typeof(Generator.ApiGenerator).Assembly;
+ public static readonly Assembly Assembly = typeof(Generator.ApiGenerator).Assembly;
- private static string _root;
- public static string Root
- {
- get
- {
- if (_root != null) return _root;
+ private static string _root;
- var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
+ public static string Root
+ {
+ get
+ {
+ if (_root != null) return _root;
- var dotnetRun =
- directoryInfo.Name == "ApiGenerator" &&
- directoryInfo.Parent != null &&
- directoryInfo.Parent.Name == "src";
+ var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory());
- _root = dotnetRun ? "" : @"../../../";
- return _root;
- }
- }
+ var dotnetRun =
+ directoryInfo.Name == "ApiGenerator" &&
+ directoryInfo.Parent != null &&
+ directoryInfo.Parent.Name == "src";
- }
+ _root = dotnetRun ? "" : "../../../";
+ return _root;
+ }
+ }
+ }
}
diff --git a/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs b/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs
index 22fa44eb5e..f543df3231 100644
--- a/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs
+++ b/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs
@@ -36,10 +36,10 @@ public class GlobalOverrides : EndpointOverridesBase
private GlobalOverrides() { }
- public IDictionary> ObsoleteEnumMembers { get; set; } = new Dictionary>()
- {
- { "VersionType", new Dictionary() { { "force", "Force is no longer accepted by the server as of 7.5.0 and will result in an error when used" } } }
- };
+ public IDictionary RenameEnums { get; } = new Dictionary
+ {
+ { "ExpandWildcard", "ExpandWildcards" }
+ };
public override IDictionary ObsoleteQueryStringParams { get; set; } = new Dictionary
{
diff --git a/src/ApiGenerator/Domain/ApiRequestParametersPatcher.cs b/src/ApiGenerator/Domain/ApiRequestParametersPatcher.cs
index e4be8819ac..94eba43012 100644
--- a/src/ApiGenerator/Domain/ApiRequestParametersPatcher.cs
+++ b/src/ApiGenerator/Domain/ApiRequestParametersPatcher.cs
@@ -97,18 +97,8 @@ private static string CreateCSharpName(string queryStringKey, string endpointNam
if (queryStringKey == "format" && endpointName == "text_structure.find_structure")
return "TextStructureFindStructureFormat";
- var cased = queryStringKey.ToPascalCase();
- switch (cased)
- {
- case "Type":
- case "Index":
- case "Source":
- case "Script":
- return cased + "QueryString";
- default:
- return cased;
- }
- }
+ return queryStringKey.ToPascalCase();
+ }
private static IList CreateSkipList(IEndpointOverrides local, ICollection declaredKeys) =>
CreateList(local, "skip", e => e.SkipQueryStringParams, declaredKeys);
diff --git a/src/ApiGenerator/Domain/Code/CsharpNames.cs b/src/ApiGenerator/Domain/Code/CsharpNames.cs
index 46a77cbabe..ea9b23d2ea 100644
--- a/src/ApiGenerator/Domain/Code/CsharpNames.cs
+++ b/src/ApiGenerator/Domain/Code/CsharpNames.cs
@@ -30,6 +30,7 @@
using System.Collections.Generic;
using System.Linq;
using ApiGenerator.Configuration;
+using ApiGenerator.Configuration.Overrides;
using ApiGenerator.Generator;
namespace ApiGenerator.Domain.Code
@@ -103,6 +104,7 @@ public string ResponseName
public const string LowLevelClientNamespacePrefix = "LowLevel";
public const string HighLevelClientNamespacePrefix = "";
public const string ClientNamespaceSuffix = "Namespace";
+
private static string CreateCSharpNamespace(string endpointNamespace)
{
switch (endpointNamespace)
@@ -203,5 +205,12 @@ public string GenericOrNonGenericResponseName
: RequestName;
public bool CustomResponseBuilderPerRequestOverride(out string call) => CodeConfiguration.ResponseBuilderInClientCalls.TryGetValue(RequestName, out call);
+
+ public static string GetEnumName(string schemaKey)
+ {
+ var enumName = schemaKey.Replace("_common", "").SplitPascalCase().ToPascalCase();
+ if (GlobalOverrides.Instance.RenameEnums.TryGetValue(enumName, out var renamed)) enumName = renamed;
+ return enumName;
+ }
}
}
diff --git a/src/ApiGenerator/Domain/RestApiSpec.cs b/src/ApiGenerator/Domain/RestApiSpec.cs
index 042910cefb..47568fa6a2 100644
--- a/src/ApiGenerator/Domain/RestApiSpec.cs
+++ b/src/ApiGenerator/Domain/RestApiSpec.cs
@@ -38,6 +38,7 @@ namespace ApiGenerator.Domain
public class EnumDescription
{
public string Name { get; set; }
+ public bool IsFlag { get; set; }
public IEnumerable Options { get; set; }
}
@@ -55,48 +56,6 @@ public class RestApiSpec
.GroupBy(e => e.CsharpNames.Namespace)
.ToImmutableSortedDictionary(kv => kv.Key, kv => kv.ToList().AsReadOnly());
-
- private IEnumerable _enumDescriptions;
- public IEnumerable EnumsInTheSpec
- {
- get
- {
- if (_enumDescriptions != null) return _enumDescriptions;
-
- var urlParameterEnums = Endpoints
- .Values
- .SelectMany(e => e.Url.Params.Values)
- .Where(p => !p.Skip && p.Options != null && p.Options.Any())
- .Select(p => new EnumDescription
- {
- Name = p.ClsName,
- Options = p.Options
- })
- .ToList();
-
- var urlPartEnums = Endpoints
- .Values
- .SelectMany(e => e.Url.Parts, (e, part) => new { e, part })
- .Where(p => p.part.Options != null && p.part.Options.Any())
- .Select(p =>
- {
- var ns = p.e.CsharpNames.Namespace;
- var m = p.e.CsharpNames.MethodName;
- return new EnumDescription
- {
- Name = (!m.StartsWith(ns) ? ns : string.Empty) + m + p.part.Name.ToPascalCase(),
- Options = p.part.Options
- };
- }).
- ToList();
-
- _enumDescriptions = urlPartEnums
- .Concat(urlParameterEnums)
- .DistinctBy(e => e.Name)
- .ToList();
-
- return _enumDescriptions;
- }
- }
+ public ImmutableList EnumsInTheSpec { get; set; }
}
}
diff --git a/src/ApiGenerator/Domain/Specification/QueryParameters.cs b/src/ApiGenerator/Domain/Specification/QueryParameters.cs
index 71fae53ebd..e0a11b35ea 100644
--- a/src/ApiGenerator/Domain/Specification/QueryParameters.cs
+++ b/src/ApiGenerator/Domain/Specification/QueryParameters.cs
@@ -106,7 +106,6 @@ public string Obsolete
public Deprecation Deprecated { get; set; }
- public IEnumerable Options { get; set; }
public string QueryStringKey { get; set; }
public bool RenderPartial { get; set; }
diff --git a/src/ApiGenerator/Domain/Specification/UrlPart.cs b/src/ApiGenerator/Domain/Specification/UrlPart.cs
index 08a3d02089..4f3450af85 100644
--- a/src/ApiGenerator/Domain/Specification/UrlPart.cs
+++ b/src/ApiGenerator/Domain/Specification/UrlPart.cs
@@ -145,7 +145,6 @@ public string Description
public string Name { get; set; }
public string NameAsArgument => Name.ToCamelCase();
- public IEnumerable Options { get; set; }
public bool Required { get; set; }
public bool Deprecated { get; set; }
public string Type { get; set; }
diff --git a/src/ApiGenerator/Extensions.cs b/src/ApiGenerator/Extensions.cs
index 9484b5b428..9062eeb15a 100644
--- a/src/ApiGenerator/Extensions.cs
+++ b/src/ApiGenerator/Extensions.cs
@@ -39,7 +39,7 @@ public static class Extensions
///
/// Removes _ . but not an underscore at the start of the string, unless the string is _all or removeLeadingUnderscore == true.
///
- private static readonly Regex RemovePunctuationExceptFirstUnderScore = new Regex(@"(?!^_(?!All$))[_\.]");
+ private static readonly Regex RemovePunctuationExceptFirstUnderScore = new(@"(?!^_(?!All$))[_\-\.: ]");
public static IEnumerable DistinctBy(this IEnumerable items, Func property) =>
items.GroupBy(property).Select(x => x.First());
diff --git a/src/ApiGenerator/Generator/ApiEndpointFactory.cs b/src/ApiGenerator/Generator/ApiEndpointFactory.cs
index bb6915f39f..76f7e1eed4 100644
--- a/src/ApiGenerator/Generator/ApiEndpointFactory.cs
+++ b/src/ApiGenerator/Generator/ApiEndpointFactory.cs
@@ -30,6 +30,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Mime;
+using System.Text.RegularExpressions;
using ApiGenerator.Configuration;
using ApiGenerator.Configuration.Overrides;
using ApiGenerator.Domain;
@@ -42,49 +43,50 @@
namespace ApiGenerator.Generator
{
- public static class ApiEndpointFactory
- {
- public static ApiEndpoint From(string name, List<(string HttpPath, OpenApiPathItem Path, string HttpMethod, OpenApiOperation Operation)> variants)
- {
- var tokens = name.Split(".");
- var methodName = tokens[^1];
- var ns = tokens.Length > 1 ? tokens[0] : null;
+ public static class ApiEndpointFactory
+ {
+ public static ApiEndpoint From(
+ string name,
+ List<(string HttpPath, OpenApiPathItem Path, string HttpMethod, OpenApiOperation Operation)> variants,
+ Action trackEnumToGenerate
+ )
+ {
+ var tokens = name.Split(".");
+ var methodName = tokens[^1];
+ var ns = tokens.Length > 1 ? tokens[0] : null;
var names = new CsharpNames(name, methodName, ns);
var overrides = LoadOverrides(name, names.MethodName);
- HashSet requiredPathParts = null;
- var allParts = new Dictionary();
+ HashSet requiredPathParts = null;
+ var allParts = new Dictionary();
var canonicalPaths = new Dictionary, UrlPath>(HashSet.CreateSetComparer());
var deprecatedPaths = new Dictionary, UrlPath>(HashSet.CreateSetComparer());
- var overloads = new List<(UrlPath Path, List<(string From, string To)> Renames)>();
- foreach (var (httpPath, path, _, operation) in variants.DistinctBy(v => v.HttpPath))
+ foreach (var (httpPath, path, _, operation) in variants.DistinctBy(v => v.HttpPath))
{
var parts = new List();
var partNames = new HashSet();
- var overloadedParts = new List<(string From, string To)>();
foreach (var param in path.Parameters
.Concat(operation.Parameters)
+ .Select(p => p.ActualParameter)
.Where(p => p.Kind == OpenApiParameterKind.Path))
{
var partName = param.Name;
if (!allParts.TryGetValue(partName, out var part))
{
+ var type = GetOpenSearchType(param.Schema, trackEnumToGenerate);
part = allParts[partName] = new UrlPart
{
ClrTypeNameOverride = null,
Deprecated = param.IsDeprecated,
- Description = param.Description,
+ Description = param.Description?.SanitizeDescription(),
Name = partName,
- Type = GetOpenSearchType(param.Schema),
- Options = GetEnumOptions(param.Schema)
+ Type = type
};
}
partNames.Add(partName);
parts.Add(part);
-
- if (param.Schema.XOverloadedParam() is {} overloadedParam) overloadedParts.Add((partName, overloadedParam));
}
parts.SortBy(p => httpPath.IndexOf($"{{{p.Name}}}", StringComparison.Ordinal));
@@ -92,33 +94,18 @@ public static ApiEndpoint From(string name, List<(string HttpPath, OpenApiPathIt
var urlPath = new UrlPath(httpPath, parts, GetDeprecation(operation), operation.XVersionAdded());
(urlPath.Deprecation == null ? canonicalPaths : deprecatedPaths).TryAdd(partNames, urlPath);
- if (overloadedParts.Count > 0)
- overloads.Add((urlPath, overloadedParts));
-
if (requiredPathParts != null)
- requiredPathParts.IntersectWith(partNames);
- else
- requiredPathParts = partNames;
- }
-
- foreach (var (path, renames) in overloads)
- {
- foreach (var (from, to) in renames)
- {
- var newPath = path.Path.Replace($"{{{from}}}", $"{{{to}}}");
- var newParts = path.Parts.Select(p => p.Name == from ? allParts[to] : p).ToList();
- var newPartNames = newParts.Select(p => p.Name).ToHashSet();
- var newUrlPath = new UrlPath(newPath, newParts, path.Deprecation, path.VersionAdded);
- (newUrlPath.Deprecation == null ? canonicalPaths : deprecatedPaths).TryAdd(newPartNames, newUrlPath);
- }
+ requiredPathParts.IntersectWith(partNames);
+ else
+ requiredPathParts = partNames;
}
//some deprecated paths describe aliases to the canonical using the same path e.g
- // PUT /{index}/_mapping/{type}
- // PUT /{index}/{type}/_mappings
- //
- //The following routine dedups these occasions and prefers either the canonical path
- //or the first duplicate deprecated path
+ // PUT /{index}/_mapping/{type}
+ // PUT /{index}/{type}/_mappings
+ //
+ //The following routine dedups these occasions and prefers either the canonical path
+ //or the first duplicate deprecated path
var paths = canonicalPaths.Values
.Concat(deprecatedPaths
@@ -129,36 +116,30 @@ public static ApiEndpoint From(string name, List<(string HttpPath, OpenApiPathIt
ApiRequestParametersPatcher.PatchUrlPaths(name, paths, overrides);
paths.Sort((p1, p2) => p1.Parts
- .Zip(p2.Parts)
- .Select(t => string.Compare(t.First.Name, t.Second.Name, StringComparison.Ordinal))
- .SkipWhile(c => c == 0)
- .FirstOrDefault());
-
- // // now, check for and prefer deprecated URLs
- //
- // var finalPathsWithDeprecations = new List(_pathsWithDeprecation.Count);
- //
- // foreach (var path in _pathsWithDeprecation)
- // {
- // if (path.Deprecation is null &&
- // DeprecatedPaths.SingleOrDefault(p => p.Path.Equals(path.Path, StringComparison.OrdinalIgnoreCase)) is { } match)
- // {
- // finalPathsWithDeprecations.Add(new UrlPath(match, OriginalParts, Paths));
- // }
- // else
- // {
- // finalPathsWithDeprecations.Add(path);
- // }
- // }
+ .Zip(p2.Parts)
+ .Select(t => string.Compare(t.First.Name, t.Second.Name, StringComparison.Ordinal))
+ .SkipWhile(c => c == 0)
+ .FirstOrDefault());
foreach (var partName in requiredPathParts ?? Enumerable.Empty()) allParts[partName].Required = true;
IDictionary queryParams = variants.SelectMany(v => v.Path.Parameters.Concat(v.Operation.Parameters))
+ .Select(p => p.ActualParameter)
.Where(p => p.Kind == OpenApiParameterKind.Query)
.DistinctBy(p => p.Name)
- .ToDictionary(p => p.Name, BuildQueryParam);
+ .ToDictionary(p => p.Name, p => BuildQueryParam(p, trackEnumToGenerate));
queryParams = ApiRequestParametersPatcher.PatchQueryParameters(name, queryParams, overrides);
+ Body body = null;
+ if (variants.Select(v => v.Operation.RequestBody).FirstOrDefault() is {} requestBody)
+ {
+ body = new Body
+ {
+ Description = GetDescription(requestBody)?.SanitizeDescription(),
+ Required = requestBody.IsRequired
+ };
+ }
+
return new ApiEndpoint
{
Name = name,
@@ -169,78 +150,89 @@ public static ApiEndpoint From(string name, List<(string HttpPath, OpenApiPathIt
Stability = Stability.Stable, // TODO: for realsies
OfficialDocumentationLink = new Documentation
{
- Description = variants[0].Operation.Description,
+ Description = variants[0].Operation.Description?.SanitizeDescription(),
Url = variants[0].Operation.ExternalDocumentation?.Url
},
- Url = new UrlInformation
- {
- AllPaths = paths,
- Params = queryParams
- },
- Body = variants
- .Select(v => v.Operation.RequestBody)
- .FirstOrDefault(b => b != null) is { } reqBody
- ? new Body { Description = GetDescription(reqBody), Required = reqBody.IsRequired }
- : null,
+ Url = new UrlInformation { AllPaths = paths, Params = queryParams },
+ Body = body,
HttpMethods = variants.Select(v => v.HttpMethod.ToString().ToUpper()).Distinct().ToList(),
};
- }
+ }
- private static IEndpointOverrides LoadOverrides(string endpointName, string methodName)
- {
- if (CodeConfiguration.ApiNameMapping.TryGetValue(endpointName, out var mapsApiMethodName))
- methodName = mapsApiMethodName;
+ private static IEndpointOverrides LoadOverrides(string endpointName, string methodName)
+ {
+ if (CodeConfiguration.ApiNameMapping.TryGetValue(endpointName, out var mapsApiMethodName))
+ methodName = mapsApiMethodName;
- var namespacePrefix = $"{typeof(GlobalOverrides).Namespace}.Endpoints.";
- var typeName = $"{namespacePrefix}{methodName}Overrides";
- var type = GeneratorLocations.Assembly.GetType(typeName);
+ var namespacePrefix = $"{typeof(GlobalOverrides).Namespace}.Endpoints.";
+ var typeName = $"{namespacePrefix}{methodName}Overrides";
+ var type = GeneratorLocations.Assembly.GetType(typeName);
return type != null && Activator.CreateInstance(type) is IEndpointOverrides overrides ? overrides : null;
}
- private static QueryParameters BuildQueryParam(OpenApiParameter p)
- {
- var param = new QueryParameters
- {
- Type = GetOpenSearchType(p.Schema),
- Description = p.Description,
- Options = GetEnumOptions(p.Schema),
- Deprecated = GetDeprecation(p.Schema),
- VersionAdded = p.Schema.XVersionAdded(),
- };
-
- if (param.Type == "enum" && p.Schema.HasReference)
- param.ClsName = ((IJsonReference)p.Schema).ReferencePath.Split('/').Last();
-
- return param;
- }
+ private static QueryParameters BuildQueryParam(OpenApiParameter p, Action trackEnumToGenerate) =>
+ new()
+ {
+ Type = GetOpenSearchType(p.Schema, trackEnumToGenerate),
+ Description = p.Description?.SanitizeDescription(),
+ Deprecated = GetDeprecation(p) ?? GetDeprecation(p.ActualSchema),
+ VersionAdded = p.XVersionAdded(),
+ };
- private static string GetOpenSearchType(JsonSchema schema)
+ private static string GetOpenSearchType(JsonSchema schema, Action trackEnumToGenerate, bool isListContext = false)
{
+ var schemaKey = ((IJsonReference)schema).ReferencePath?.Split('/').Last();
schema = schema.ActualSchema;
- if (schema.XDataType() is {} dataType)
+ if (schema.OneOf is { Count: > 0 })
+ {
+ var oneOf = schema.OneOf.ToArray();
+
+ if (oneOf.Length == 2)
+ {
+ var first = GetOpenSearchType(oneOf[0], trackEnumToGenerate);
+ var second = GetOpenSearchType(oneOf[1], trackEnumToGenerate);
+ if (first.EndsWith("?")) return first;
+ if (first == second) return first;
+
+ switch (first, second)
+ {
+ case ("string", "list"): return second;
+ case ("boolean", "string"): return first;
+ case ("string", "number"): return first;
+ case ("number", _): return "string";
+ }
+ }
+ }
+
+ if (schema.XDataType() is { } dataType)
return dataType == "array" ? "list" : dataType;
- return schema.Type switch
- {
- JsonObjectType.String when schema.Enumeration is { Count: > 0 } => "enum",
- JsonObjectType.Integer => "number",
- JsonObjectType.Array => "list",
- var t => t.ToString().ToLowerInvariant()
- };
- }
+ var enumOptions = schema.Enumeration.Where(e => e != null).Select(e => e.ToString()).ToList();
+
+ if (schemaKey != null && schema.Type == JsonObjectType.String && enumOptions.Count > 0)
+ {
+ trackEnumToGenerate(schemaKey, isListContext);
+ return CsharpNames.GetEnumName(schemaKey) + "?";
+ }
+
+ if (schema.Type == JsonObjectType.Array && (schema.Item?.HasReference ?? false))
+ _ = GetOpenSearchType(schema.Item, trackEnumToGenerate, true);
- private static IEnumerable GetEnumOptions(JsonSchema schema) =>
- schema.ActualSchema.XEnumOptions()
- ?? schema.ActualSchema.Enumeration?.Select(e => e.ToString())
- ?? Enumerable.Empty();
+ return schema.Type switch
+ {
+ JsonObjectType.Integer => "number",
+ JsonObjectType.Array => "list",
+ var t => t.ToString().ToLowerInvariant()
+ };
+ }
private static Deprecation GetDeprecation(IJsonExtensionObject schema) =>
(schema.XDeprecationMessage(), schema.XVersionDeprecated()) switch
{
(null, null) => null,
- var (m, v) => new Deprecation { Description = m, Version = v }
+ var (m, v) => new Deprecation { Description = m?.SanitizeDescription(), Version = v }
};
private static string GetDescription(OpenApiRequestBody requestBody)
@@ -253,6 +245,17 @@ private static string GetDescription(OpenApiRequestBody requestBody)
: null;
}
+ private static string SanitizeDescription(this string description)
+ {
+ if (string.IsNullOrWhiteSpace(description)) return null;
+
+ description = Regex.Replace(description, @"\s+", " ");
+
+ if (!description.EndsWith('.')) description += '.';
+
+ return description;
+ }
+
private static string XDeprecationMessage(this IJsonExtensionObject schema) =>
schema.GetExtension("x-deprecation-message") as string;
@@ -261,24 +264,18 @@ private static string XVersionDeprecated(this IJsonExtensionObject schema) =>
private static Version XVersionAdded(this IJsonExtensionObject schema) =>
schema.GetExtension("x-version-added") is string s
- ? s.Split('.').Length switch
- {
- 1 => new Version($"{s}.0.0"),
- 2 => new Version($"{s}.0"),
- _ => new Version(s),
- }
- : null;
+ ? s.Split('.').Length switch
+ {
+ 1 => new Version($"{s}.0.0"),
+ 2 => new Version($"{s}.0"),
+ _ => new Version(s),
+ }
+ : null;
private static string XDataType(this IJsonExtensionObject schema) =>
schema.GetExtension("x-data-type") as string;
- private static string XOverloadedParam(this IJsonExtensionObject schema) =>
- schema.GetExtension("x-overloaded-param") as string;
-
- private static IEnumerable XEnumOptions(this IJsonExtensionObject schema) =>
- schema.GetExtension("x-enum-options") is object[] opts ? opts.Cast() : null;
-
- private static object GetExtension(this IJsonExtensionObject schema, string key) =>
- schema.ExtensionData?.TryGetValue(key, out var value) ?? false ? value : null;
+ private static object GetExtension(this IJsonExtensionObject schema, string key) =>
+ schema.ExtensionData?.TryGetValue(key, out var value) ?? false ? value : null;
}
}
diff --git a/src/ApiGenerator/Generator/ApiGenerator.cs b/src/ApiGenerator/Generator/ApiGenerator.cs
index 9b0d7d4f13..dfa87f38ed 100644
--- a/src/ApiGenerator/Generator/ApiGenerator.cs
+++ b/src/ApiGenerator/Generator/ApiGenerator.cs
@@ -29,16 +29,20 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
+using System.Dynamic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ApiGenerator.Configuration;
using ApiGenerator.Domain;
+using ApiGenerator.Domain.Code;
+using ApiGenerator.Domain.Specification;
using ApiGenerator.Generator.Razor;
using NJsonSchema;
using NSwag;
using ShellProgressBar;
+using YamlDotNet.Serialization;
namespace ApiGenerator.Generator
{
@@ -92,18 +96,64 @@ await DoGenerate(
public static async Task CreateRestApiSpecModel(CancellationToken token = default)
{
- var document = await OpenApiYamlDocument.FromFileAsync(GeneratorLocations.OpenApiSpecFile, token);
+ var json = PreprocessRawOpenApiSpec(await File.ReadAllTextAsync(GeneratorLocations.OpenApiSpecFile, token));
+ var document = await OpenApiDocument.FromJsonAsync(json, token);
JsonSchemaReferenceUtilities.UpdateSchemaReferencePaths(document);
+ var enumsToGenerate = new Dictionary();
+
var endpoints = document.Paths
.Select(kv => new { HttpPath = kv.Key, PathItem = kv.Value })
- .SelectMany(p => p.PathItem.Select(kv => new { p.HttpPath, p.PathItem, HttpMethod = kv.Key, Operation = kv.Value }))
- .GroupBy(o => o.Operation.ExtensionData["x-operation-group"].ToString())
+ .SelectMany(p => p.PathItem.Select(kv => new
+ {
+ p.HttpPath, p.PathItem, HttpMethod = kv.Key, Operation = kv.Value
+ }))
+ .GroupBy(o => o.Operation.ExtensionData!["x-operation-group"]!.ToString())
.Where(o => CodeConfiguration.IncludeOperation(o.Key))
- .Select(o => ApiEndpointFactory.From(o.Key, o.Select(i => (i.HttpPath, i.PathItem, i.HttpMethod, i.Operation)).ToList()))
+ .Select(o => ApiEndpointFactory.From(
+ o.Key,
+ o.Select(i => (i.HttpPath, i.PathItem, i.HttpMethod, i.Operation)).ToList(),
+ (e, isFlag) =>
+ {
+ if (enumsToGenerate.TryGetValue(e, out var f)) isFlag |= f;
+ enumsToGenerate[e] = isFlag;
+ }))
.ToImmutableSortedDictionary(e => e.Name, e => e);
- return new RestApiSpec { Endpoints = endpoints };
+ var enumsInSpec = enumsToGenerate.Select(kvp => new EnumDescription
+ {
+ Name = CsharpNames.GetEnumName(kvp.Key),
+ IsFlag = kvp.Value,
+ Options = document.Components.Schemas[kvp.Key].Enumeration.Where(e => e != null).Select(e => e.ToString()).ToImmutableList()
+ })
+ .OrderBy(e => e.Name)
+ .ToImmutableList();
+
+ return new RestApiSpec { Endpoints = endpoints, EnumsInTheSpec = enumsInSpec };
+ }
+
+ private static string PreprocessRawOpenApiSpec(string yaml)
+ {
+ // FIXME: work-around until NSwag adds support for requestBody references: https://github.com/RicoSuter/NSwag/pull/4747
+ dynamic doc = new DeserializerBuilder().Build().Deserialize(yaml)!;
+ var requestBodies = doc["components"]["requestBodies"];
+ foreach (KeyValuePair