diff --git a/src/ApiGenerator/CodeTemplatePage.cs b/src/ApiGenerator/CodeTemplatePage.cs index 9c5585a318..83a983e706 100644 --- a/src/ApiGenerator/CodeTemplatePage.cs +++ b/src/ApiGenerator/CodeTemplatePage.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -35,5 +35,5 @@ namespace ApiGenerator; /// This only exists to make the IDE tooling happy, not actually used to render the templates public class CodeTemplatePage : TemplatePage { - public override Task ExecuteAsync() => throw new NotImplementedException(); + public override Task ExecuteAsync() => throw new NotImplementedException(); } diff --git a/src/ApiGenerator/Configuration/CodeConfiguration.cs b/src/ApiGenerator/Configuration/CodeConfiguration.cs index 9ed23df740..ebeb8da027 100644 --- a/src/ApiGenerator/Configuration/CodeConfiguration.cs +++ b/src/ApiGenerator/Configuration/CodeConfiguration.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,167 +36,167 @@ namespace ApiGenerator.Configuration; public static class CodeConfiguration { - /// - /// Map API default names for API's we are only supporting on the low level client first - /// - private static readonly Dictionary LowLevelApiNameMapping = new() - { - { "indices.delete_index_template", "DeleteIndexTemplateV2" }, - { "indices.get_index_template", "GetIndexTemplateV2" }, - { "indices.put_index_template", "PutIndexTemplateV2" } - }; - - private static readonly FileInfo[] ClientCsharpFiles = new DirectoryInfo(GeneratorLocations.OpenSearchClientFolder) - .GetFiles("*.cs", SearchOption.AllDirectories); - private static readonly FileInfo[] ClientRequestCsharpFiles = ClientCsharpFiles.Where(f => f.Name.EndsWith("Request.cs")).ToArray(); - private static readonly FileInfo[] ClientResponseCsharpFiles = ClientCsharpFiles.Where(f => f.Name.EndsWith("Response.cs")).ToArray(); - - private static readonly Regex ResponseBuilderAttributeRegex = new(@"^.+\[ResponseBuilderWithGeneric\(""([^ \r\n]+)""\)\].*$", RegexOptions.Singleline); - private static readonly Regex GenericsRegex = new(@"^.*?(?:(\<.+>).*?)?$"); - private static readonly Regex GenericsRemovalRegex = new(@"<.*$"); - private static readonly Regex RequestInterfaceRegex = new(@"^.+interface ([^ \r\n]+Request(?:<[^>\r\n]+>)?[^ \r\n]*).*$", RegexOptions.Singleline); - private static readonly Regex ResponseClassRegex = new(@"^.+public class ([^ \r\n]+Response(?:<[^>\r\n]+>)?[^ \r\n]*).*$", RegexOptions.Singleline); - - /// - /// Scan all OSC source code files for Requests and look for the [MapsApi(filename)] attribute. - /// The class name minus Request is used as the canonical .NET name for the API. - /// - public static readonly Dictionary HighLevelApiNameMapping = - ClientCsharpFiles.Select(f => new - { - File = f, - MappedApi = Regex.Replace( - File.ReadAllText(f.FullName), - @"^.+\[MapsApi\(""([^ \r\n]+)""\)\].*$", - "$1", - RegexOptions.Singleline - ) - }) - .Where(f => !f.MappedApi.Contains(' ')) - .Select(f => new - { - MappedApi = f.MappedApi.Replace(".json", ""), - ApiName = f.File.Name.Replace("Request", "").Replace(".cs", "") - }) - .DistinctBy(v => v.MappedApi) - .ToDictionary(k => k.MappedApi, v => v.ApiName); - - public static readonly Dictionary ApiNameMapping = - new Dictionary(HighLevelApiNameMapping).OverrideWith(LowLevelApiNameMapping); - - /// - /// Scan all OSC source code files for Requests and look for the [MapsApi(filename)] attribute. - /// The class name minus Request is used as the canonical .NET name for the API. - /// - public static readonly Dictionary ResponseBuilderInClientCalls = - ClientCsharpFiles - .SelectMany(f => File.ReadLines(f.FullName) - .Where(l => ResponseBuilderAttributeRegex.IsMatch(l)) - .Select(l => new - { - Key = f.Name.Replace(".cs", ""), - Value = ResponseBuilderAttributeRegex.Replace(l, "$1") - })) - .DistinctBy(v => v.Key) - .ToDictionary(v => v.Key, v => v.Value); - - - public static readonly IDictionary DescriptorGenericsLookup = - ClientRequestCsharpFiles - .Select(f => - { - var descriptor = Path.GetFileNameWithoutExtension(f.Name).Replace("Request", "Descriptor"); - var c = Regex.Replace( - File.ReadAllText(f.FullName), - $@"^.+class ({descriptor}(?:<[^>\r\n]+>)?[^ \r\n]*).*$", - "$1", - RegexOptions.Singleline - ); - return new { Key = descriptor, Value = GenericsRegex.Replace(c, "$1") }; - }) - .DistinctBy(v => v.Key) - .ToImmutableSortedDictionary(v => v.Key, v => v.Value); - - - private static readonly List RequestInterfaceDeclarations = - ClientRequestCsharpFiles - .SelectMany(f => File.ReadLines(f.FullName)) - .Where(l => RequestInterfaceRegex.IsMatch(l)) - .ToList(); - - /// Scan all OSC files for request interfaces and note any generics declared on them - private static readonly List<(string Request, string Generics)> AllKnownRequestInterfaces = - RequestInterfaceDeclarations - .Select(l => RequestInterfaceRegex.Replace(l, "$1")) - .Where(c => c.StartsWith("I") && c.Contains("Request")) - .Select(c => ( - Request: GenericsRemovalRegex.Replace(c, ""), - Generics: GenericsRegex.Replace(c, "$1") - )) - .OrderBy(v => v.Request) - .ToList(); - - public static readonly HashSet GenericOnlyInterfaces = AllKnownRequestInterfaces - .GroupBy(v => v.Item1) - .Where(g => g.All(v => !string.IsNullOrEmpty(v.Item2))) - .Select(g => g.Key) - .ToHashSet(); - - public static readonly HashSet DocumentRequests = - RequestInterfaceDeclarations - .Where(l => l.Contains("IDocumentRequest")) - .Select(l => RequestInterfaceRegex.Replace(l, "$1")) - .Select(c => GenericsRemovalRegex.Replace(c, "")) - .ToHashSet(); - - public static readonly Dictionary DescriptorConstructors = - ClientRequestCsharpFiles - .SelectMany(f => - { - var descriptor = Path.GetFileNameWithoutExtension(f.Name).Replace("Request", "Descriptor"); - var re = new Regex($@"^.+public {descriptor}\(([^\r\n\)]+?)\).*$", RegexOptions.Singleline); - return File.ReadLines(f.FullName) - .Where(l => re.IsMatch(l)) - .Select(l => re.Replace(l, "$1")) - .Where(args => !string.IsNullOrWhiteSpace(args) && !args.Contains(": base")) - .Select(args => new - { - Descriptor = descriptor, - Args = args - }); - }) - .ToDictionary(r => r.Descriptor, r => r.Args); - - public static readonly Dictionary RequestInterfaceGenericsLookup = - AllKnownRequestInterfaces - .GroupBy(v => v.Request) - .Select(g => g.Last()) - .ToDictionary(k => k.Request, v => v.Generics); - - /// - /// Some API's reuse response this is a hardcoded map of these cases - /// - private static readonly Dictionary ResponseReroute = new() - { - { "UpdateByQueryRethrottleResponse", ("ListTasksResponse", "") }, - { "DeleteByQueryRethrottleResponse", ("ListTasksResponse", "") }, - { "MultiSearchTemplateResponse", ("MultiSearchResponse", "") }, - { "ScrollResponse", ("SearchResponse", "") }, - { "SearchTemplateResponse", ("SearchResponse", "") } - }; - - /// Create a dictionary lookup of all responses and their generics - public static readonly IDictionary ResponseLookup = - ClientResponseCsharpFiles - .SelectMany(f => File.ReadLines(f.FullName)) - .Where(l => ResponseClassRegex.IsMatch(l)) - .Select(l => - { - var c = ResponseClassRegex.Replace(l, "$1"); - var response = GenericsRemovalRegex.Replace(c, ""); - var generics = GenericsRegex.Replace(c, "$1"); - return (response, (response, generics)); - }) - .Concat(ResponseReroute.Select(kv => (kv.Key, (kv.Value.Item1, kv.Value.Item2)))) - .ToImmutableSortedDictionary(t => t.Item1, t => t.Item2); + /// + /// Map API default names for API's we are only supporting on the low level client first + /// + private static readonly Dictionary LowLevelApiNameMapping = new() + { + { "indices.delete_index_template", "DeleteIndexTemplateV2" }, + { "indices.get_index_template", "GetIndexTemplateV2" }, + { "indices.put_index_template", "PutIndexTemplateV2" } + }; + + private static readonly FileInfo[] ClientCsharpFiles = new DirectoryInfo(GeneratorLocations.OpenSearchClientFolder) + .GetFiles("*.cs", SearchOption.AllDirectories); + private static readonly FileInfo[] ClientRequestCsharpFiles = ClientCsharpFiles.Where(f => f.Name.EndsWith("Request.cs")).ToArray(); + private static readonly FileInfo[] ClientResponseCsharpFiles = ClientCsharpFiles.Where(f => f.Name.EndsWith("Response.cs")).ToArray(); + + private static readonly Regex ResponseBuilderAttributeRegex = new(@"^.+\[ResponseBuilderWithGeneric\(""([^ \r\n]+)""\)\].*$", RegexOptions.Singleline); + private static readonly Regex GenericsRegex = new(@"^.*?(?:(\<.+>).*?)?$"); + private static readonly Regex GenericsRemovalRegex = new(@"<.*$"); + private static readonly Regex RequestInterfaceRegex = new(@"^.+interface ([^ \r\n]+Request(?:<[^>\r\n]+>)?[^ \r\n]*).*$", RegexOptions.Singleline); + private static readonly Regex ResponseClassRegex = new(@"^.+public class ([^ \r\n]+Response(?:<[^>\r\n]+>)?[^ \r\n]*).*$", RegexOptions.Singleline); + + /// + /// Scan all OSC source code files for Requests and look for the [MapsApi(filename)] attribute. + /// The class name minus Request is used as the canonical .NET name for the API. + /// + public static readonly Dictionary HighLevelApiNameMapping = + ClientCsharpFiles.Select(f => new + { + File = f, + MappedApi = Regex.Replace( + File.ReadAllText(f.FullName), + @"^.+\[MapsApi\(""([^ \r\n]+)""\)\].*$", + "$1", + RegexOptions.Singleline + ) + }) + .Where(f => !f.MappedApi.Contains(' ')) + .Select(f => new + { + MappedApi = f.MappedApi.Replace(".json", ""), + ApiName = f.File.Name.Replace("Request", "").Replace(".cs", "") + }) + .DistinctBy(v => v.MappedApi) + .ToDictionary(k => k.MappedApi, v => v.ApiName); + + public static readonly Dictionary ApiNameMapping = + new Dictionary(HighLevelApiNameMapping).OverrideWith(LowLevelApiNameMapping); + + /// + /// Scan all OSC source code files for Requests and look for the [MapsApi(filename)] attribute. + /// The class name minus Request is used as the canonical .NET name for the API. + /// + public static readonly Dictionary ResponseBuilderInClientCalls = + ClientCsharpFiles + .SelectMany(f => File.ReadLines(f.FullName) + .Where(l => ResponseBuilderAttributeRegex.IsMatch(l)) + .Select(l => new + { + Key = f.Name.Replace(".cs", ""), + Value = ResponseBuilderAttributeRegex.Replace(l, "$1") + })) + .DistinctBy(v => v.Key) + .ToDictionary(v => v.Key, v => v.Value); + + + public static readonly IDictionary DescriptorGenericsLookup = + ClientRequestCsharpFiles + .Select(f => + { + var descriptor = Path.GetFileNameWithoutExtension(f.Name).Replace("Request", "Descriptor"); + var c = Regex.Replace( + File.ReadAllText(f.FullName), + $@"^.+class ({descriptor}(?:<[^>\r\n]+>)?[^ \r\n]*).*$", + "$1", + RegexOptions.Singleline + ); + return new { Key = descriptor, Value = GenericsRegex.Replace(c, "$1") }; + }) + .DistinctBy(v => v.Key) + .ToImmutableSortedDictionary(v => v.Key, v => v.Value); + + + private static readonly List RequestInterfaceDeclarations = + ClientRequestCsharpFiles + .SelectMany(f => File.ReadLines(f.FullName)) + .Where(l => RequestInterfaceRegex.IsMatch(l)) + .ToList(); + + /// Scan all OSC files for request interfaces and note any generics declared on them + private static readonly List<(string Request, string Generics)> AllKnownRequestInterfaces = + RequestInterfaceDeclarations + .Select(l => RequestInterfaceRegex.Replace(l, "$1")) + .Where(c => c.StartsWith("I") && c.Contains("Request")) + .Select(c => ( + Request: GenericsRemovalRegex.Replace(c, ""), + Generics: GenericsRegex.Replace(c, "$1") + )) + .OrderBy(v => v.Request) + .ToList(); + + public static readonly HashSet GenericOnlyInterfaces = AllKnownRequestInterfaces + .GroupBy(v => v.Item1) + .Where(g => g.All(v => !string.IsNullOrEmpty(v.Item2))) + .Select(g => g.Key) + .ToHashSet(); + + public static readonly HashSet DocumentRequests = + RequestInterfaceDeclarations + .Where(l => l.Contains("IDocumentRequest")) + .Select(l => RequestInterfaceRegex.Replace(l, "$1")) + .Select(c => GenericsRemovalRegex.Replace(c, "")) + .ToHashSet(); + + public static readonly Dictionary DescriptorConstructors = + ClientRequestCsharpFiles + .SelectMany(f => + { + var descriptor = Path.GetFileNameWithoutExtension(f.Name).Replace("Request", "Descriptor"); + var re = new Regex($@"^.+public {descriptor}\(([^\r\n\)]+?)\).*$", RegexOptions.Singleline); + return File.ReadLines(f.FullName) + .Where(l => re.IsMatch(l)) + .Select(l => re.Replace(l, "$1")) + .Where(args => !string.IsNullOrWhiteSpace(args) && !args.Contains(": base")) + .Select(args => new + { + Descriptor = descriptor, + Args = args + }); + }) + .ToDictionary(r => r.Descriptor, r => r.Args); + + public static readonly Dictionary RequestInterfaceGenericsLookup = + AllKnownRequestInterfaces + .GroupBy(v => v.Request) + .Select(g => g.Last()) + .ToDictionary(k => k.Request, v => v.Generics); + + /// + /// Some API's reuse response this is a hardcoded map of these cases + /// + private static readonly Dictionary ResponseReroute = new() + { + { "UpdateByQueryRethrottleResponse", ("ListTasksResponse", "") }, + { "DeleteByQueryRethrottleResponse", ("ListTasksResponse", "") }, + { "MultiSearchTemplateResponse", ("MultiSearchResponse", "") }, + { "ScrollResponse", ("SearchResponse", "") }, + { "SearchTemplateResponse", ("SearchResponse", "") } + }; + + /// Create a dictionary lookup of all responses and their generics + public static readonly IDictionary ResponseLookup = + ClientResponseCsharpFiles + .SelectMany(f => File.ReadLines(f.FullName)) + .Where(l => ResponseClassRegex.IsMatch(l)) + .Select(l => + { + var c = ResponseClassRegex.Replace(l, "$1"); + var response = GenericsRemovalRegex.Replace(c, ""); + var generics = GenericsRegex.Replace(c, "$1"); + return (response, (response, generics)); + }) + .Concat(ResponseReroute.Select(kv => (kv.Key, (kv.Value.Item1, kv.Value.Item2)))) + .ToImmutableSortedDictionary(t => t.Item1, t => t.Item2); } diff --git a/src/ApiGenerator/Configuration/GeneratorLocations.cs b/src/ApiGenerator/Configuration/GeneratorLocations.cs index 47ac335616..71b18280f7 100644 --- a/src/ApiGenerator/Configuration/GeneratorLocations.cs +++ b/src/ApiGenerator/Configuration/GeneratorLocations.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,30 +33,30 @@ namespace ApiGenerator.Configuration; public static class GeneratorLocations { - public static string OpenSearchNetFolder { get; } = $@"{Root}../../src/OpenSearch.Net/"; - public static string OpenSearchClientFolder { get; } = $@"{Root}../../src/OpenSearch.Client/"; - public static string RestSpecificationFolder { get; } = $@"{Root}RestSpecification/"; + public static string OpenSearchNetFolder { get; } = $@"{Root}../../src/OpenSearch.Net/"; + public static string OpenSearchClientFolder { get; } = $@"{Root}../../src/OpenSearch.Client/"; + public static string RestSpecificationFolder { get; } = $@"{Root}RestSpecification/"; - public static string HighLevel(params string[] paths) => OpenSearchClientFolder + string.Join("/", paths); + public static string HighLevel(params string[] paths) => OpenSearchClientFolder + string.Join("/", paths); - public static string LowLevel(params string[] paths) => OpenSearchNetFolder + string.Join("/", paths); + public static string LowLevel(params string[] paths) => OpenSearchNetFolder + 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; + private static string _root; - public static string Root - { - get - { - if (_root != null) return _root; + public static string Root + { + get + { + if (_root != null) return _root; - var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory()); + var directoryInfo = new DirectoryInfo(Directory.GetCurrentDirectory()); - _root = directoryInfo.Name == "ApiGenerator" && directoryInfo.Parent is { Name: "src" } - ? "" - : @"../../../"; - return _root; - } - } + _root = directoryInfo.Name == "ApiGenerator" && directoryInfo.Parent is { Name: "src" } + ? "" + : @"../../../"; + return _root; + } + } } diff --git a/src/ApiGenerator/Configuration/Overrides/EndpointOverridesBase.cs b/src/ApiGenerator/Configuration/Overrides/EndpointOverridesBase.cs index 0caca9f170..e4ded949fc 100644 --- a/src/ApiGenerator/Configuration/Overrides/EndpointOverridesBase.cs +++ b/src/ApiGenerator/Configuration/Overrides/EndpointOverridesBase.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,11 +33,11 @@ namespace ApiGenerator.Configuration.Overrides; public abstract class EndpointOverridesBase : IEndpointOverrides { - public virtual IDictionary ObsoleteQueryStringParams { get; set; } = new SortedDictionary(); + public virtual IDictionary ObsoleteQueryStringParams { get; set; } = new SortedDictionary(); - public virtual IDictionary RenameQueryStringParams { get; } = new SortedDictionary(); + public virtual IDictionary RenameQueryStringParams { get; } = new SortedDictionary(); - public virtual IEnumerable RenderPartial { get; } = Enumerable.Empty(); + public virtual IEnumerable RenderPartial { get; } = Enumerable.Empty(); - public virtual IEnumerable SkipQueryStringParams { get; } = Enumerable.Empty(); + public virtual IEnumerable SkipQueryStringParams { get; } = Enumerable.Empty(); } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/DeleteByQueryOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/DeleteByQueryOverrides.cs index c0b27a21e8..4cb3fb8e90 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/DeleteByQueryOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/DeleteByQueryOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class DeleteByQueryOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "max_docs" }; + public override IEnumerable SkipQueryStringParams => new[] { "max_docs" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/FlushJobOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/FlushJobOverrides.cs index cd8a0e7e66..0fddab7aa3 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/FlushJobOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/FlushJobOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class FlushJobOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "advance_time", "end", "start", "calc_interim" }; + public override IEnumerable SkipQueryStringParams => new[] { "advance_time", "end", "start", "calc_interim" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/ForecastJobOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/ForecastJobOverrides.cs index e87507e9da..e832291aa3 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/ForecastJobOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/ForecastJobOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class ForecastJobOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "duration", "expires_in" }; + public override IEnumerable SkipQueryStringParams => new[] { "duration", "expires_in" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetAnomalyRecordsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetAnomalyRecordsOverrides.cs index 9bca01fb2c..dc726510c5 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetAnomalyRecordsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetAnomalyRecordsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,8 +32,8 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetAnomalyRecordsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] - { - "exclude_interim", "from", "size", "start", "end", "record_score", "sort", "desc" - }; + public override IEnumerable SkipQueryStringParams => new[] + { + "exclude_interim", "from", "size", "start", "end", "record_score", "sort", "desc" + }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetBucketsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetBucketsOverrides.cs index 51f9e99af0..61a5046563 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetBucketsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetBucketsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,8 +32,8 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetBucketsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] - { - "expand", "exclude_interim", "from", "size", "start", "timestamp", "end", "anomaly_score", "sort", "desc" - }; + public override IEnumerable SkipQueryStringParams => new[] + { + "expand", "exclude_interim", "from", "size", "start", "timestamp", "end", "anomaly_score", "sort", "desc" + }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarEventsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarEventsOverrides.cs index 8f6fb79135..c2db3aa7aa 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarEventsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarEventsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetCalendarEventsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "from", "size" }; + public override IEnumerable SkipQueryStringParams => new[] { "from", "size" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarsOverrides.cs index 3940a481df..45f87da499 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCalendarsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetCalendarsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "from", "size" }; + public override IEnumerable SkipQueryStringParams => new[] { "from", "size" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCategoriesOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCategoriesOverrides.cs index 2d26b319c8..d3b22b9051 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCategoriesOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetCategoriesOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetCategoriesOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "from", "size" }; + public override IEnumerable SkipQueryStringParams => new[] { "from", "size" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetInfluencersOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetInfluencersOverrides.cs index 88eebd4d3c..5cf7aa151b 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetInfluencersOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetInfluencersOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,8 +32,8 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetInfluencersOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] - { - "exclude_interim", "from", "size", "start", "end", "influencer_score", "sort", "desc" - }; + public override IEnumerable SkipQueryStringParams => new[] + { + "exclude_interim", "from", "size", "start", "end", "influencer_score", "sort", "desc" + }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetModelSnapshotsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetModelSnapshotsOverrides.cs index 6ced0e4bf9..466957f1cb 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetModelSnapshotsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetModelSnapshotsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetModelSnapshotsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "from", "size", "start", "end", "sort", "desc" }; + public override IEnumerable SkipQueryStringParams => new[] { "from", "size", "start", "end", "sort", "desc" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetOverallBucketsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetOverallBucketsOverrides.cs index 1451418793..7acf383949 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/GetOverallBucketsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/GetOverallBucketsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,8 +32,8 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class GetOverallBucketsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] - { - "allow_no_jobs", "bucket_span", "end", "exclude_interim", "overall_score", "start", "top_n" - }; + public override IEnumerable SkipQueryStringParams => new[] + { + "allow_no_jobs", "bucket_span", "end", "exclude_interim", "overall_score", "start", "top_n" + }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/IndicesStatsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/IndicesStatsOverrides.cs index 841261bfa9..5f2c2fce95 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/IndicesStatsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/IndicesStatsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,5 +33,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; // ReSharper disable once UnusedMember.Global public class IndicesStatsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "types" }; + public override IEnumerable SkipQueryStringParams => new[] { "types" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/MultiTermVectorsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/MultiTermVectorsOverrides.cs index 792a94cf72..8941b5f9e2 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/MultiTermVectorsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/MultiTermVectorsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,5 +33,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; // ReSharper disable once UnusedMember.Global public class MultiTermVectorsOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "ids" }; + public override IEnumerable SkipQueryStringParams => new[] { "ids" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/NodesHotThreadsOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/NodesHotThreadsOverrides.cs index d88803994e..525ecf74f2 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/NodesHotThreadsOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/NodesHotThreadsOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,5 +33,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; // ReSharper disable once UnusedMember.Global public class NodesHotThreadsOverrides : EndpointOverridesBase { - public override IDictionary RenameQueryStringParams => new Dictionary { { "type", "thread_type" } }; + public override IDictionary RenameQueryStringParams => new Dictionary { { "type", "thread_type" } }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/PutIndexTemplateOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/PutIndexTemplateOverrides.cs index 0fec678cd7..7f26ee10db 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/PutIndexTemplateOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/PutIndexTemplateOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,5 +33,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; // ReSharper disable once UnusedMember.Global public class PutIndexTemplateOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "order" }; + public override IEnumerable SkipQueryStringParams => new[] { "order" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/ReindexOnServerOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/ReindexOnServerOverrides.cs index 68977cd9c9..4b24d4f665 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/ReindexOnServerOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/ReindexOnServerOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class ReindexOnServerOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "max_docs" }; + public override IEnumerable SkipQueryStringParams => new[] { "max_docs" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/RevertModelSnapshotOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/RevertModelSnapshotOverrides.cs index 40e63ca8fa..0d0cd03f7c 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/RevertModelSnapshotOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/RevertModelSnapshotOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class RevertModelSnapshotOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "delete_intervening_results" }; + public override IEnumerable SkipQueryStringParams => new[] { "delete_intervening_results" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/ScrollOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/ScrollOverrides.cs index 1841dc64a7..637be5021c 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/ScrollOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/ScrollOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,5 +33,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; // ReSharper disable once UnusedMember.Global public class ScrollOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "scroll_id", "scroll" }; + public override IEnumerable SkipQueryStringParams => new[] { "scroll_id", "scroll" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/SearchOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/SearchOverrides.cs index 8570c4d4d2..6bf2a45b17 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/SearchOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/SearchOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,10 +33,10 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; // ReSharper disable once UnusedMember.Global public class SearchOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] - { - "size", "from", "timeout", "explain", "version", "sort", "_source", "_source_includes", "_source_excludes", "track_scores", - "terminate_after", "track_total_hits" - }; + public override IEnumerable SkipQueryStringParams => new[] + { + "size", "from", "timeout", "explain", "version", "sort", "_source", "_source_includes", "_source_excludes", "track_scores", + "terminate_after", "track_total_hits" + }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateByQueryOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateByQueryOverrides.cs index e47a3526cd..2d8d63b5fa 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateByQueryOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateByQueryOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,5 +32,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; public class UpdateByQueryOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "max_docs" }; + public override IEnumerable SkipQueryStringParams => new[] { "max_docs" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateOverrides.cs b/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateOverrides.cs index 0768476ccc..9c37cb516e 100644 --- a/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/Endpoints/UpdateOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,5 +33,5 @@ namespace ApiGenerator.Configuration.Overrides.Endpoints; // ReSharper disable once UnusedMember.Global public class UpdateOverrides : EndpointOverridesBase { - public override IEnumerable SkipQueryStringParams => new[] { "fields", "_source_includes", "_source_excludes" }; + public override IEnumerable SkipQueryStringParams => new[] { "fields", "_source_includes", "_source_excludes" }; } diff --git a/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs b/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs index 3a1f9c48af..d68ae247e6 100644 --- a/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/GlobalOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,42 +32,42 @@ namespace ApiGenerator.Configuration.Overrides; public class GlobalOverrides : EndpointOverridesBase { - 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> 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 override IDictionary ObsoleteQueryStringParams { get; set; } = new Dictionary { { "copy_settings", "" } }; + public override IDictionary ObsoleteQueryStringParams { get; set; } = new Dictionary { { "copy_settings", "" } }; - public override IDictionary RenameQueryStringParams { get; } = new Dictionary - { - { "_source", "source_enabled" }, - { "_source_includes", "source_includes" }, - { "_source_excludes", "source_excludes" }, - { "rest_total_hits_as_int", "total_hits_as_integer" }, - { "docvalue_fields", "doc_value_fields" }, - { "q", "query_on_query_string" }, - //make cat parameters more descriptive - { "h", "Headers" }, - { "s", "sort_by_columns" }, - { "v", "verbose" }, - { "ts", "include_timestamp" }, - { "if_seq_no", "if_sequence_number" }, - { "seq_no_primary_term", "sequence_number_primary_term" } - }; + public override IDictionary RenameQueryStringParams { get; } = new Dictionary + { + { "_source", "source_enabled" }, + { "_source_includes", "source_includes" }, + { "_source_excludes", "source_excludes" }, + { "rest_total_hits_as_int", "total_hits_as_integer" }, + { "docvalue_fields", "doc_value_fields" }, + { "q", "query_on_query_string" }, + //make cat parameters more descriptive + { "h", "Headers" }, + { "s", "sort_by_columns" }, + { "v", "verbose" }, + { "ts", "include_timestamp" }, + { "if_seq_no", "if_sequence_number" }, + { "seq_no_primary_term", "sequence_number_primary_term" } + }; - public override IEnumerable RenderPartial => new[] { "stored_fields", "docvalue_fields" }; + public override IEnumerable RenderPartial => new[] { "stored_fields", "docvalue_fields" }; - public override IEnumerable SkipQueryStringParams { get; } = new[] - { - "copy_settings", //this still needs a PR? - "source", // allows the body to be specified as a request param, we do not want to advertise this with a strongly typed method - "timestamp", "time" - }; + public override IEnumerable SkipQueryStringParams { get; } = new[] + { + "copy_settings", //this still needs a PR? + "source", // allows the body to be specified as a request param, we do not want to advertise this with a strongly typed method + "timestamp", "time" + }; } diff --git a/src/ApiGenerator/Configuration/Overrides/IEndpointOverrides.cs b/src/ApiGenerator/Configuration/Overrides/IEndpointOverrides.cs index 3464ecf267..d9a8e162f9 100644 --- a/src/ApiGenerator/Configuration/Overrides/IEndpointOverrides.cs +++ b/src/ApiGenerator/Configuration/Overrides/IEndpointOverrides.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -35,25 +35,25 @@ namespace ApiGenerator.Configuration.Overrides; /// public interface IEndpointOverrides { - /// - /// A map of key -> obsolete message for properties in the spec that should not be used any longer - /// - IDictionary ObsoleteQueryStringParams { get; set; } + /// + /// A map of key -> obsolete message for properties in the spec that should not be used any longer + /// + IDictionary ObsoleteQueryStringParams { get; set; } - /// - /// Override how the query param name is exposed to the client. - /// - IDictionary RenameQueryStringParams { get; } + /// + /// Override how the query param name is exposed to the client. + /// + IDictionary RenameQueryStringParams { get; } - /// - /// Force these be rendered as interface properties only, so that they'd have to be implemented manually - /// and become part of the body. This only takes affect on requests that take a body (e.g not GET or HEAD). - /// - IEnumerable RenderPartial { get; } + /// + /// Force these be rendered as interface properties only, so that they'd have to be implemented manually + /// and become part of the body. This only takes affect on requests that take a body (e.g not GET or HEAD). + /// + IEnumerable RenderPartial { get; } - /// - /// Sometimes params can be defined on the body as well as on the querystring - /// We favor specifying params on the body so here we can specify params we don't want on the querystring. - /// - IEnumerable SkipQueryStringParams { get; } + /// + /// Sometimes params can be defined on the body as well as on the querystring + /// We favor specifying params on the body so here we can specify params we don't want on the querystring. + /// + IEnumerable SkipQueryStringParams { get; } } diff --git a/src/ApiGenerator/Configuration/ViewLocations.cs b/src/ApiGenerator/Configuration/ViewLocations.cs index 545ef43052..0026fb211a 100644 --- a/src/ApiGenerator/Configuration/ViewLocations.cs +++ b/src/ApiGenerator/Configuration/ViewLocations.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,7 +30,7 @@ namespace ApiGenerator.Configuration; public static class ViewLocations { - public static string HighLevel(params string[] paths) => "HighLevel." + string.Join(".", paths); + public static string HighLevel(params string[] paths) => "HighLevel." + string.Join(".", paths); - public static string LowLevel(params string[] paths) => "LowLevel." + string.Join(".", paths); + public static string LowLevel(params string[] paths) => "LowLevel." + string.Join(".", paths); } diff --git a/src/ApiGenerator/Domain/ApiQueryParametersPatcher.cs b/src/ApiGenerator/Domain/ApiQueryParametersPatcher.cs index d7af79f9f4..e88a76d3c8 100644 --- a/src/ApiGenerator/Domain/ApiQueryParametersPatcher.cs +++ b/src/ApiGenerator/Domain/ApiQueryParametersPatcher.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,114 +36,114 @@ namespace ApiGenerator.Domain; public static class ApiQueryParametersPatcher { - public static SortedDictionary Patch( - string endpointName, - IDictionary source, - IEndpointOverrides overrides - ) - { - if (source == null) return null; - - var globalOverrides = new GlobalOverrides(); - var declaredKeys = source.Keys; - var skipList = CreateSkipList(globalOverrides, overrides, declaredKeys); - var partialList = CreatePartialList(globalOverrides, overrides, declaredKeys); - - var renameLookup = CreateRenameLookup(globalOverrides, overrides, declaredKeys); - var obsoleteLookup = CreateObsoleteLookup(globalOverrides, overrides, declaredKeys); - - var patchedParams = new SortedDictionary(); - foreach (var (key, value) in source) - { - value.QueryStringKey = key; - - if (!renameLookup.TryGetValue(key, out var preferredName)) preferredName = key; - value.ClsName = CreateCSharpName(preferredName, endpointName); - - if (skipList.Contains(key)) value.Skip = true; - - if (partialList.Contains(key)) value.RenderPartial = true; - - if (obsoleteLookup.TryGetValue(key, out var obsolete)) value.Obsolete = obsolete; - - //make sure source_enabled takes a boolean only - if (preferredName == "source_enabled") value.Type = "boolean"; - - patchedParams[preferredName] = value; - } - - return patchedParams; - } - - private static string CreateCSharpName(string queryStringKey, string endpointName) - { - if (string.IsNullOrWhiteSpace(queryStringKey)) return "UNKNOWN"; - - 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; - } - } - - private static IList CreateSkipList(IEndpointOverrides global, IEndpointOverrides local, ICollection declaredKeys) => - CreateList(global, local, "skip", e => e.SkipQueryStringParams, declaredKeys); - - private static IList CreatePartialList(IEndpointOverrides global, IEndpointOverrides local, ICollection declaredKeys) => - CreateList(global, local, "partial", e => e.RenderPartial, declaredKeys); - - private static IDictionary CreateLookup(IEndpointOverrides global, IEndpointOverrides local, string type, - Func> from, ICollection declaredKeys - ) - { - var d = new SortedDictionary(); - foreach (var kv in from(global)) d[kv.Key] = kv.Value; - - if (local == null) return d; - - var localDictionary = from(local); - foreach (var kv in localDictionary) d[kv.Key] = kv.Value; - - var name = local.GetType().Name; - foreach (var p in localDictionary.Keys.Except(declaredKeys)) - Generator.ApiGenerator.Warnings.Add($"On {name} {type} key '{p}' is not found in spec"); - - return d; - } - - private static IList CreateList(IEndpointOverrides global, IEndpointOverrides local, string type, - Func> from, IEnumerable declaredKeys - ) - { - var list = new List(); - if (global != null) list.AddRange(from(global)); - if (local != null) - { - var localList = from(local).ToList(); - list.AddRange(localList); - var name = local.GetType().Name; - foreach (var p in localList.Except(declaredKeys)) - Generator.ApiGenerator.Warnings.Add($"On {name} {type} key '{p}' is not found in spec"); - } - return list.Distinct().ToList(); - } - - private static IDictionary CreateRenameLookup(IEndpointOverrides global, IEndpointOverrides local, - ICollection declaredKeys - ) => - CreateLookup(global, local, "rename", e => e.RenameQueryStringParams, declaredKeys); - - private static IDictionary CreateObsoleteLookup(IEndpointOverrides global, IEndpointOverrides local, - ICollection declaredKeys - ) => - CreateLookup(global, local, "obsolete", e => e.ObsoleteQueryStringParams, declaredKeys); + public static SortedDictionary Patch( + string endpointName, + IDictionary source, + IEndpointOverrides overrides + ) + { + if (source == null) return null; + + var globalOverrides = new GlobalOverrides(); + var declaredKeys = source.Keys; + var skipList = CreateSkipList(globalOverrides, overrides, declaredKeys); + var partialList = CreatePartialList(globalOverrides, overrides, declaredKeys); + + var renameLookup = CreateRenameLookup(globalOverrides, overrides, declaredKeys); + var obsoleteLookup = CreateObsoleteLookup(globalOverrides, overrides, declaredKeys); + + var patchedParams = new SortedDictionary(); + foreach (var (key, value) in source) + { + value.QueryStringKey = key; + + if (!renameLookup.TryGetValue(key, out var preferredName)) preferredName = key; + value.ClsName = CreateCSharpName(preferredName, endpointName); + + if (skipList.Contains(key)) value.Skip = true; + + if (partialList.Contains(key)) value.RenderPartial = true; + + if (obsoleteLookup.TryGetValue(key, out var obsolete)) value.Obsolete = obsolete; + + //make sure source_enabled takes a boolean only + if (preferredName == "source_enabled") value.Type = "boolean"; + + patchedParams[preferredName] = value; + } + + return patchedParams; + } + + private static string CreateCSharpName(string queryStringKey, string endpointName) + { + if (string.IsNullOrWhiteSpace(queryStringKey)) return "UNKNOWN"; + + 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; + } + } + + private static IList CreateSkipList(IEndpointOverrides global, IEndpointOverrides local, ICollection declaredKeys) => + CreateList(global, local, "skip", e => e.SkipQueryStringParams, declaredKeys); + + private static IList CreatePartialList(IEndpointOverrides global, IEndpointOverrides local, ICollection declaredKeys) => + CreateList(global, local, "partial", e => e.RenderPartial, declaredKeys); + + private static IDictionary CreateLookup(IEndpointOverrides global, IEndpointOverrides local, string type, + Func> from, ICollection declaredKeys + ) + { + var d = new SortedDictionary(); + foreach (var kv in from(global)) d[kv.Key] = kv.Value; + + if (local == null) return d; + + var localDictionary = from(local); + foreach (var kv in localDictionary) d[kv.Key] = kv.Value; + + var name = local.GetType().Name; + foreach (var p in localDictionary.Keys.Except(declaredKeys)) + Generator.ApiGenerator.Warnings.Add($"On {name} {type} key '{p}' is not found in spec"); + + return d; + } + + private static IList CreateList(IEndpointOverrides global, IEndpointOverrides local, string type, + Func> from, IEnumerable declaredKeys + ) + { + var list = new List(); + if (global != null) list.AddRange(from(global)); + if (local != null) + { + var localList = from(local).ToList(); + list.AddRange(localList); + var name = local.GetType().Name; + foreach (var p in localList.Except(declaredKeys)) + Generator.ApiGenerator.Warnings.Add($"On {name} {type} key '{p}' is not found in spec"); + } + return list.Distinct().ToList(); + } + + private static IDictionary CreateRenameLookup(IEndpointOverrides global, IEndpointOverrides local, + ICollection declaredKeys + ) => + CreateLookup(global, local, "rename", e => e.RenameQueryStringParams, declaredKeys); + + private static IDictionary CreateObsoleteLookup(IEndpointOverrides global, IEndpointOverrides local, + ICollection declaredKeys + ) => + CreateLookup(global, local, "obsolete", e => e.ObsoleteQueryStringParams, declaredKeys); } diff --git a/src/ApiGenerator/Domain/Code/CsharpNames.cs b/src/ApiGenerator/Domain/Code/CsharpNames.cs index 922726504f..e98cfe7143 100644 --- a/src/ApiGenerator/Domain/Code/CsharpNames.cs +++ b/src/ApiGenerator/Domain/Code/CsharpNames.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,170 +36,170 @@ namespace ApiGenerator.Domain.Code; public class CsharpNames { - public CsharpNames(string name, string endpointMethodName, string endpointNamespace) - { - RestSpecName = string.IsNullOrWhiteSpace(endpointNamespace) ? endpointMethodName : $"{endpointNamespace}.{endpointMethodName}"; - Namespace = CreateCSharpNamespace(endpointNamespace); - ApiName = CodeConfiguration.ApiNameMapping.TryGetValue(name, out var mapsApiMethodName) - ? mapsApiMethodName - : endpointMethodName.ToPascalCase(); - - //if the api name starts with the namespace do not repeat it in the method name - string Replace(string original, string ns, string find, string replace, string[] exceptions) - { - if (ns != null && Namespace != ns) return original; - if (exceptions.Contains(original)) return original; - - var replaced = original.Replace(find, replace); - return string.IsNullOrEmpty(replaced) ? original : replaced; - } - - MethodName = Replace(ApiName, null, Namespace, "", Array.Empty()); - - var namespaceRenames = new Dictionary - { - { "Indices", (find: "Index", replace: "", exceptions: new[] { "SimulateIndexTemplate" }) } - }; - foreach (var (ns, (find, replace, exceptions)) in namespaceRenames) - MethodName = Replace(MethodName, ns, find, replace, exceptions); - } - - /// Pascal cased version of the namespace from the specification - public string Namespace { get; } - - public string RestSpecName { get; } - - /// - /// The pascal cased method name as loaded by - ///
Uses  mapping of request implementations in the OSC code base
- ///
- public string MethodName { get; } - - public string ApiName { get; } - - public string RequestName => $"{ApiName}Request"; - - public string ResponseName - { - get - { - if (Namespace == "Cat") return $"CatResponse<{ApiName}Record>"; - if (ApiName.EndsWith("Exists")) return "ExistsResponse"; - - var generatedName = $"{ApiName}Response"; - return CodeConfiguration.ResponseLookup.TryGetValue(generatedName, out var lookup) ? lookup.Item1 : generatedName; - } - } - - public string RequestInterfaceName => $"I{RequestName}"; - public string ParametersName => $"{RequestName}Parameters"; - public string DescriptorName => $"{ApiName}Descriptor"; - - public const string ApiNamespace = "Specification"; - public const string ApiNamespaceSuffix = "Api"; - public const string RootNamespace = "NoNamespace"; - public const string LowLevelClientNamespacePrefix = "LowLevel"; - public const string HighLevelClientNamespacePrefix = ""; - public const string ClientNamespaceSuffix = "Namespace"; - - private static string CreateCSharpNamespace(string endpointNamespace) - { - switch (endpointNamespace) - { - case null: - case "": return RootNamespace; - default: return endpointNamespace.ToPascalCase(); - } - } - - public string PerPathMethodName(string path) - { - var method = MethodName; - // This is temporary for transition period - // TODO: remove in branch once it in opensearch is scrubbed - if (path.Contains("{type}") && !method.Contains("Type")) method += "UsingType"; - - if (Namespace == null) return method; - - if (Namespace.StartsWith("Indices") && !path.Contains("{index}")) - return (method + "ForAll").Replace("AsyncForAll", "ForAllAsync"); - - if (Namespace.StartsWith("Nodes") && !path.Contains("{node_id}")) - return (method + "ForAll").Replace("AsyncForAll", "ForAllAsync"); - - return method; - } - - - public string GenericsDeclaredOnRequest => - CodeConfiguration.RequestInterfaceGenericsLookup.TryGetValue(RequestInterfaceName, out var requestGeneric) ? requestGeneric : null; - - public string GenericsDeclaredOnResponse => - CodeConfiguration.ResponseLookup.TryGetValue(ResponseName, out var requestGeneric) ? requestGeneric.Item2 : null; - - public string GenericsDeclaredOnDescriptor => - CodeConfiguration.DescriptorGenericsLookup.TryGetValue(DescriptorName, out var generic) ? generic : null; - - public IEnumerable ResponseGenerics => - !CodeConfiguration.ResponseLookup.TryGetValue(ResponseName, out var responseGeneric) - || string.IsNullOrEmpty(responseGeneric.Item2) - ? Enumerable.Empty() - : SplitGeneric(responseGeneric.Item2); - - public IEnumerable DescriptorGenerics => - CodeConfiguration.DescriptorGenericsLookup.TryGetValue(DescriptorName, out var generic) ? SplitGeneric(generic) : Enumerable.Empty(); - - public bool DescriptorBindsOverMultipleDocuments => - HighLevelDescriptorMethodGenerics.Count == 2 && HighLevelDescriptorMethodGenerics.All(g => g.Contains("Document")); - //&& ResponseGenerics.FirstOrDefault() == DescriptorBoundDocumentGeneric ; - - public string DescriptorBoundDocumentGeneric => - HighLevelDescriptorMethodGenerics.FirstOrDefault(g => g == "TDocument") ?? HighLevelDescriptorMethodGenerics.Last(); - - public List HighLevelDescriptorMethodGenerics => DescriptorGenerics - .Concat(ResponseGenerics) - .Distinct() - .ToList(); - - public static IEnumerable SplitGeneric(string generic) => (generic ?? string.Empty) - .Replace("<", "") - .Replace(">", "") - .Split(",") - .Where(g => !string.IsNullOrWhiteSpace(g)) - .Distinct(); - - - public bool DescriptorNotFoundInCodebase => !CodeConfiguration.DescriptorGenericsLookup.TryGetValue(DescriptorName, out _); - - public string GenericDescriptorName => string.IsNullOrEmpty(GenericsDeclaredOnDescriptor) ? null : $"{DescriptorName}{GenericsDeclaredOnDescriptor}"; - public string GenericRequestName => string.IsNullOrEmpty(GenericsDeclaredOnRequest) ? null : $"{RequestName}{GenericsDeclaredOnRequest}"; - public string GenericInterfaceName => string.IsNullOrEmpty(GenericsDeclaredOnRequest) ? null : $"I{GenericRequestName}"; - public string GenericResponseName => string.IsNullOrEmpty(GenericsDeclaredOnResponse) ? null : $"{ResponseName}{GenericsDeclaredOnResponse}"; - - public string GenericOrNonGenericDescriptorName => GenericDescriptorName ?? DescriptorName; - public string GenericOrNonGenericInterfaceName => GenericInterfaceName ?? RequestInterfaceName; - - public string GenericOrNonGenericResponseName - { - get - { - var full = GenericResponseName ?? ResponseName; - if (full.StartsWith("SearchResponse<")) - full = $"I{full}"; - return full; - } - } - - /// If matching Request.cs only defined generic interface make the client method only accept said interface - public string GenericOrNonGenericInterfacePreference => CodeConfiguration.GenericOnlyInterfaces.Contains(RequestInterfaceName) - ? GenericInterfaceName - : RequestInterfaceName; - - /// If matching Request.cs only defined generic interface make the client method only accept said interface - public string GenericOrNonGenericRequestPreference => CodeConfiguration.GenericOnlyInterfaces.Contains(RequestInterfaceName) - ? GenericRequestName - : RequestName; - - public bool CustomResponseBuilderPerRequestOverride(out string call) => - CodeConfiguration.ResponseBuilderInClientCalls.TryGetValue(RequestName, out call); + public CsharpNames(string name, string endpointMethodName, string endpointNamespace) + { + RestSpecName = string.IsNullOrWhiteSpace(endpointNamespace) ? endpointMethodName : $"{endpointNamespace}.{endpointMethodName}"; + Namespace = CreateCSharpNamespace(endpointNamespace); + ApiName = CodeConfiguration.ApiNameMapping.TryGetValue(name, out var mapsApiMethodName) + ? mapsApiMethodName + : endpointMethodName.ToPascalCase(); + + //if the api name starts with the namespace do not repeat it in the method name + string Replace(string original, string ns, string find, string replace, string[] exceptions) + { + if (ns != null && Namespace != ns) return original; + if (exceptions.Contains(original)) return original; + + var replaced = original.Replace(find, replace); + return string.IsNullOrEmpty(replaced) ? original : replaced; + } + + MethodName = Replace(ApiName, null, Namespace, "", Array.Empty()); + + var namespaceRenames = new Dictionary + { + { "Indices", (find: "Index", replace: "", exceptions: new[] { "SimulateIndexTemplate" }) } + }; + foreach (var (ns, (find, replace, exceptions)) in namespaceRenames) + MethodName = Replace(MethodName, ns, find, replace, exceptions); + } + + /// Pascal cased version of the namespace from the specification + public string Namespace { get; } + + public string RestSpecName { get; } + + /// + /// The pascal cased method name as loaded by + ///
Uses  mapping of request implementations in the OSC code base
+ ///
+ public string MethodName { get; } + + public string ApiName { get; } + + public string RequestName => $"{ApiName}Request"; + + public string ResponseName + { + get + { + if (Namespace == "Cat") return $"CatResponse<{ApiName}Record>"; + if (ApiName.EndsWith("Exists")) return "ExistsResponse"; + + var generatedName = $"{ApiName}Response"; + return CodeConfiguration.ResponseLookup.TryGetValue(generatedName, out var lookup) ? lookup.Item1 : generatedName; + } + } + + public string RequestInterfaceName => $"I{RequestName}"; + public string ParametersName => $"{RequestName}Parameters"; + public string DescriptorName => $"{ApiName}Descriptor"; + + public const string ApiNamespace = "Specification"; + public const string ApiNamespaceSuffix = "Api"; + public const string RootNamespace = "NoNamespace"; + public const string LowLevelClientNamespacePrefix = "LowLevel"; + public const string HighLevelClientNamespacePrefix = ""; + public const string ClientNamespaceSuffix = "Namespace"; + + private static string CreateCSharpNamespace(string endpointNamespace) + { + switch (endpointNamespace) + { + case null: + case "": return RootNamespace; + default: return endpointNamespace.ToPascalCase(); + } + } + + public string PerPathMethodName(string path) + { + var method = MethodName; + // This is temporary for transition period + // TODO: remove in branch once it in opensearch is scrubbed + if (path.Contains("{type}") && !method.Contains("Type")) method += "UsingType"; + + if (Namespace == null) return method; + + if (Namespace.StartsWith("Indices") && !path.Contains("{index}")) + return (method + "ForAll").Replace("AsyncForAll", "ForAllAsync"); + + if (Namespace.StartsWith("Nodes") && !path.Contains("{node_id}")) + return (method + "ForAll").Replace("AsyncForAll", "ForAllAsync"); + + return method; + } + + + public string GenericsDeclaredOnRequest => + CodeConfiguration.RequestInterfaceGenericsLookup.TryGetValue(RequestInterfaceName, out var requestGeneric) ? requestGeneric : null; + + public string GenericsDeclaredOnResponse => + CodeConfiguration.ResponseLookup.TryGetValue(ResponseName, out var requestGeneric) ? requestGeneric.Item2 : null; + + public string GenericsDeclaredOnDescriptor => + CodeConfiguration.DescriptorGenericsLookup.TryGetValue(DescriptorName, out var generic) ? generic : null; + + public IEnumerable ResponseGenerics => + !CodeConfiguration.ResponseLookup.TryGetValue(ResponseName, out var responseGeneric) + || string.IsNullOrEmpty(responseGeneric.Item2) + ? Enumerable.Empty() + : SplitGeneric(responseGeneric.Item2); + + public IEnumerable DescriptorGenerics => + CodeConfiguration.DescriptorGenericsLookup.TryGetValue(DescriptorName, out var generic) ? SplitGeneric(generic) : Enumerable.Empty(); + + public bool DescriptorBindsOverMultipleDocuments => + HighLevelDescriptorMethodGenerics.Count == 2 && HighLevelDescriptorMethodGenerics.All(g => g.Contains("Document")); + //&& ResponseGenerics.FirstOrDefault() == DescriptorBoundDocumentGeneric ; + + public string DescriptorBoundDocumentGeneric => + HighLevelDescriptorMethodGenerics.FirstOrDefault(g => g == "TDocument") ?? HighLevelDescriptorMethodGenerics.Last(); + + public List HighLevelDescriptorMethodGenerics => DescriptorGenerics + .Concat(ResponseGenerics) + .Distinct() + .ToList(); + + public static IEnumerable SplitGeneric(string generic) => (generic ?? string.Empty) + .Replace("<", "") + .Replace(">", "") + .Split(",") + .Where(g => !string.IsNullOrWhiteSpace(g)) + .Distinct(); + + + public bool DescriptorNotFoundInCodebase => !CodeConfiguration.DescriptorGenericsLookup.TryGetValue(DescriptorName, out _); + + public string GenericDescriptorName => string.IsNullOrEmpty(GenericsDeclaredOnDescriptor) ? null : $"{DescriptorName}{GenericsDeclaredOnDescriptor}"; + public string GenericRequestName => string.IsNullOrEmpty(GenericsDeclaredOnRequest) ? null : $"{RequestName}{GenericsDeclaredOnRequest}"; + public string GenericInterfaceName => string.IsNullOrEmpty(GenericsDeclaredOnRequest) ? null : $"I{GenericRequestName}"; + public string GenericResponseName => string.IsNullOrEmpty(GenericsDeclaredOnResponse) ? null : $"{ResponseName}{GenericsDeclaredOnResponse}"; + + public string GenericOrNonGenericDescriptorName => GenericDescriptorName ?? DescriptorName; + public string GenericOrNonGenericInterfaceName => GenericInterfaceName ?? RequestInterfaceName; + + public string GenericOrNonGenericResponseName + { + get + { + var full = GenericResponseName ?? ResponseName; + if (full.StartsWith("SearchResponse<")) + full = $"I{full}"; + return full; + } + } + + /// If matching Request.cs only defined generic interface make the client method only accept said interface + public string GenericOrNonGenericInterfacePreference => CodeConfiguration.GenericOnlyInterfaces.Contains(RequestInterfaceName) + ? GenericInterfaceName + : RequestInterfaceName; + + /// If matching Request.cs only defined generic interface make the client method only accept said interface + public string GenericOrNonGenericRequestPreference => CodeConfiguration.GenericOnlyInterfaces.Contains(RequestInterfaceName) + ? GenericRequestName + : RequestName; + + public bool CustomResponseBuilderPerRequestOverride(out string call) => + CodeConfiguration.ResponseBuilderInClientCalls.TryGetValue(RequestName, out call); } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/BoundFluentMethod.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/BoundFluentMethod.cs index deaa300307..6d2eb33eee 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/BoundFluentMethod.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/BoundFluentMethod.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -35,27 +35,27 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public class BoundFluentMethod : FluentSyntaxBase { - public BoundFluentMethod(CsharpNames names, IReadOnlyCollection parts, bool selectorIsOptional, string link, string summary) - : base(names, parts, selectorIsOptional, link, summary) { } + public BoundFluentMethod(CsharpNames names, IReadOnlyCollection parts, bool selectorIsOptional, string link, string summary) + : base(names, parts, selectorIsOptional, link, summary) { } - private string DescriptorTypeParams => string.Join(", ", CsharpNames.DescriptorGenerics - .Select(e => CsharpNames.DescriptorBoundDocumentGeneric)); + private string DescriptorTypeParams => string.Join(", ", CsharpNames.DescriptorGenerics + .Select(e => CsharpNames.DescriptorBoundDocumentGeneric)); - private string RequestTypeParams => string.Join(", ", CsharpNames.SplitGeneric(CsharpNames.GenericsDeclaredOnRequest) - .Select(e => CsharpNames.DescriptorBoundDocumentGeneric)); + private string RequestTypeParams => string.Join(", ", CsharpNames.SplitGeneric(CsharpNames.GenericsDeclaredOnRequest) + .Select(e => CsharpNames.DescriptorBoundDocumentGeneric)); - private string SelectorReturn => string.IsNullOrWhiteSpace(CsharpNames.GenericsDeclaredOnRequest) - || !CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) - ? CsharpNames.RequestInterfaceName - : $"{CsharpNames.RequestInterfaceName}<{RequestTypeParams}>"; + private string SelectorReturn => string.IsNullOrWhiteSpace(CsharpNames.GenericsDeclaredOnRequest) + || !CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) + ? CsharpNames.RequestInterfaceName + : $"{CsharpNames.RequestInterfaceName}<{RequestTypeParams}>"; - public override string DescriptorName => $"{CsharpNames.DescriptorName}<{DescriptorTypeParams}>"; - public override string GenericWhereClause => $"where {CsharpNames.DescriptorBoundDocumentGeneric} : class"; - public override string MethodGenerics => $"<{CsharpNames.DescriptorBoundDocumentGeneric}>"; + public override string DescriptorName => $"{CsharpNames.DescriptorName}<{DescriptorTypeParams}>"; + public override string GenericWhereClause => $"where {CsharpNames.DescriptorBoundDocumentGeneric} : class"; + public override string MethodGenerics => $"<{CsharpNames.DescriptorBoundDocumentGeneric}>"; - public override string RequestMethodGenerics => !string.IsNullOrWhiteSpace(RequestTypeParams) - ? $"<{RequestTypeParams}>" - : base.RequestMethodGenerics; + public override string RequestMethodGenerics => !string.IsNullOrWhiteSpace(RequestTypeParams) + ? $"<{RequestTypeParams}>" + : base.RequestMethodGenerics; - public override string Selector => $"Func<{DescriptorName}, {SelectorReturn}>"; + public override string Selector => $"Func<{DescriptorName}, {SelectorReturn}>"; } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentMethod.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentMethod.cs index e547075914..0ec63c1220 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentMethod.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentMethod.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -34,12 +34,12 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public class FluentMethod : FluentSyntaxBase { - public FluentMethod(CsharpNames names, IReadOnlyCollection parts, bool selectorIsOptional, string link, string summary) - : base(names, parts, selectorIsOptional, link, summary) { } + public FluentMethod(CsharpNames names, IReadOnlyCollection parts, bool selectorIsOptional, string link, string summary) + : base(names, parts, selectorIsOptional, link, summary) { } - public override string GenericWhereClause => - string.Join(" ", CsharpNames.HighLevelDescriptorMethodGenerics - .Where(g => g.Contains("Document")) - .Select(g => $"where {g} : class") - ); + public override string GenericWhereClause => + string.Join(" ", CsharpNames.HighLevelDescriptorMethodGenerics + .Where(g => g.Contains("Document")) + .Select(g => $"where {g} : class") + ); } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxBase.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxBase.cs index 5e131b15fb..b631bd936e 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxBase.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxBase.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -35,123 +35,123 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public abstract class FluentSyntaxBase : MethodSyntaxBase { - private readonly bool _selectorIsOptional; - - protected FluentSyntaxBase(CsharpNames names, IReadOnlyCollection parts, bool selectorIsOptional, string link, string summary) - : base(names, link, summary) => - (UrlParts, _selectorIsOptional) = (CreateDescriptorArgs(parts), selectorIsOptional); - - private IReadOnlyCollection UrlParts { get; } - - /// - /// The selector is optional if so set by (has no or optional body) - /// Or if there is a custom constructor on the descriptor in which case we assume that constructor holds all the required - /// values - /// - private bool SelectorIsOptional => _selectorIsOptional || CodeConfiguration.DescriptorConstructors.ContainsKey(CsharpNames.DescriptorName); - - public string MethodName => CsharpNames.MethodName; - - public string OptionalSelectorSuffix => SelectorIsOptional ? " = null" : string.Empty; - - public virtual string DescriptorName => CsharpNames.GenericOrNonGenericDescriptorName; - public virtual string Selector => $"Func<{DescriptorName}, {CsharpNames.GenericOrNonGenericInterfacePreference}>"; - - public override string MethodGenerics => - CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) - ? CsharpNames.GenericsDeclaredOnRequest - : DescriptorGenerics; - - public virtual string RequestMethodGenerics => - CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) - ? CsharpNames.GenericsDeclaredOnRequest - : CsharpNames.GenericsDeclaredOnResponse; - - private string DescriptorGenerics => CsharpNames.HighLevelDescriptorMethodGenerics.Any() - ? $"<{string.Join(", ", CsharpNames.HighLevelDescriptorMethodGenerics)}>" - : null; - - private List CreateDescriptorArgs(IReadOnlyCollection parts) - { - var requiredParts = parts.Where(p => p.Required).ToList(); - - //Many api's return ALOT of information by default e.g get_alias or get_mapping - //the client methods that take a descriptor default to forcing a choice on the user. - //except for cat api's where the amount of information returned is manageable - - var willInferFromDocument = CsharpNames.GenericsDeclaredOnDescriptor?.Contains("Document") ?? false; - if (!requiredParts.Any() && CsharpNames.Namespace != "Cat") - { - var candidates = new[] - { - //only make index part the first argument if the descriptor is not generic on T.*?Document - parts.FirstOrDefault(p => p.Type == "list" && p.Name is "index" or "indices" && !willInferFromDocument), - parts.FirstOrDefault(p => p.Name == "name") - }; - requiredParts = candidates.Where(p => p != null).Take(1).ToList(); - } - if (!willInferFromDocument) return requiredParts; - - //if index, indices is required but the descriptor is generic these will be inferred so no need to pass explicitly - requiredParts = requiredParts.Where(p => p.Name != "index" && p.Name != "indices").ToList(); - var idPart = requiredParts.FirstOrDefault(i => i.Name == "id"); - if ((idPart != null && UrlInformation.IsADocumentRoute(parts)) || IsDocumentRequest) - { - if (requiredParts.Contains(idPart)) requiredParts.Remove(idPart); - var generic = GenericFirstArgument; - var typeName = IsDocumentRequest ? generic : $"DocumentPath<{generic}>"; - var arg = IsDocumentRequest ? "document" : idPart.Name; - requiredParts.Add(new UrlPart { Name = arg, Required = true, ClrTypeNameOverride = typeName }); - } - - return requiredParts; - } - - private bool IsDocumentRequest => CodeConfiguration.DocumentRequests.Contains(CsharpNames.RequestInterfaceName); - - private string GenericFirstArgument => - CsharpNames.GenericsDeclaredOnDescriptor.Replace("<", "").Replace(">", "").Split(",").First().Trim(); - - public string DescriptorArguments() - { - if (CodeConfiguration.DescriptorConstructors.TryGetValue(CsharpNames.DescriptorName, out var codeArgs)) - codeArgs += ","; - - if (!UrlParts.Any()) return codeArgs; - - string Optional(UrlPart p) - { - return !p.Required && SelectorIsOptional ? " = null" : string.Empty; - } - - return $"{codeArgs}{string.Join(", ", UrlParts.Select(p => $"{p.HighLevelTypeName} {p.Name.ToCamelCase()}{Optional(p)}"))}, "; - } - - public string SelectorArguments() - { - if (CodeConfiguration.DescriptorConstructors.TryGetValue(CsharpNames.DescriptorName, out var codeArgs)) - return string.Join(", ", codeArgs.Split(',').Select(a => a.Split(' ').Last())); - - var parts = UrlParts.Where(p => p.Required).ToList(); - if (!parts.Any()) return null; - - string ToArg(UrlPart p) - { - if (IsDocumentRequest) return "documentWithId: document"; - - return p.HighLevelTypeName.StartsWith("DocumentPath") - ? "documentWithId: id?.Document, index: id?.Self?.Index, id: id?.Self?.Id" - : $"{p.Name.ToCamelCase()}: {p.Name.ToCamelCase()}"; - } - - return string.Join(", ", parts.Select(ToArg)); - } - - public string SelectorChainedDefaults() - { - var parts = UrlParts.Where(p => !p.Required).ToList(); - if (!parts.Any()) return null; - - return $".{string.Join(".", parts.Select(p => $"{p.Name.ToPascalCase()}({p.Name.ToCamelCase()}: {p.Name.ToCamelCase()})"))}"; - } + private readonly bool _selectorIsOptional; + + protected FluentSyntaxBase(CsharpNames names, IReadOnlyCollection parts, bool selectorIsOptional, string link, string summary) + : base(names, link, summary) => + (UrlParts, _selectorIsOptional) = (CreateDescriptorArgs(parts), selectorIsOptional); + + private IReadOnlyCollection UrlParts { get; } + + /// + /// The selector is optional if so set by (has no or optional body) + /// Or if there is a custom constructor on the descriptor in which case we assume that constructor holds all the required + /// values + /// + private bool SelectorIsOptional => _selectorIsOptional || CodeConfiguration.DescriptorConstructors.ContainsKey(CsharpNames.DescriptorName); + + public string MethodName => CsharpNames.MethodName; + + public string OptionalSelectorSuffix => SelectorIsOptional ? " = null" : string.Empty; + + public virtual string DescriptorName => CsharpNames.GenericOrNonGenericDescriptorName; + public virtual string Selector => $"Func<{DescriptorName}, {CsharpNames.GenericOrNonGenericInterfacePreference}>"; + + public override string MethodGenerics => + CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) + ? CsharpNames.GenericsDeclaredOnRequest + : DescriptorGenerics; + + public virtual string RequestMethodGenerics => + CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) + ? CsharpNames.GenericsDeclaredOnRequest + : CsharpNames.GenericsDeclaredOnResponse; + + private string DescriptorGenerics => CsharpNames.HighLevelDescriptorMethodGenerics.Any() + ? $"<{string.Join(", ", CsharpNames.HighLevelDescriptorMethodGenerics)}>" + : null; + + private List CreateDescriptorArgs(IReadOnlyCollection parts) + { + var requiredParts = parts.Where(p => p.Required).ToList(); + + //Many api's return ALOT of information by default e.g get_alias or get_mapping + //the client methods that take a descriptor default to forcing a choice on the user. + //except for cat api's where the amount of information returned is manageable + + var willInferFromDocument = CsharpNames.GenericsDeclaredOnDescriptor?.Contains("Document") ?? false; + if (!requiredParts.Any() && CsharpNames.Namespace != "Cat") + { + var candidates = new[] + { + //only make index part the first argument if the descriptor is not generic on T.*?Document + parts.FirstOrDefault(p => p.Type == "list" && p.Name is "index" or "indices" && !willInferFromDocument), + parts.FirstOrDefault(p => p.Name == "name") + }; + requiredParts = candidates.Where(p => p != null).Take(1).ToList(); + } + if (!willInferFromDocument) return requiredParts; + + //if index, indices is required but the descriptor is generic these will be inferred so no need to pass explicitly + requiredParts = requiredParts.Where(p => p.Name != "index" && p.Name != "indices").ToList(); + var idPart = requiredParts.FirstOrDefault(i => i.Name == "id"); + if ((idPart != null && UrlInformation.IsADocumentRoute(parts)) || IsDocumentRequest) + { + if (requiredParts.Contains(idPart)) requiredParts.Remove(idPart); + var generic = GenericFirstArgument; + var typeName = IsDocumentRequest ? generic : $"DocumentPath<{generic}>"; + var arg = IsDocumentRequest ? "document" : idPart.Name; + requiredParts.Add(new UrlPart { Name = arg, Required = true, ClrTypeNameOverride = typeName }); + } + + return requiredParts; + } + + private bool IsDocumentRequest => CodeConfiguration.DocumentRequests.Contains(CsharpNames.RequestInterfaceName); + + private string GenericFirstArgument => + CsharpNames.GenericsDeclaredOnDescriptor.Replace("<", "").Replace(">", "").Split(",").First().Trim(); + + public string DescriptorArguments() + { + if (CodeConfiguration.DescriptorConstructors.TryGetValue(CsharpNames.DescriptorName, out var codeArgs)) + codeArgs += ","; + + if (!UrlParts.Any()) return codeArgs; + + string Optional(UrlPart p) + { + return !p.Required && SelectorIsOptional ? " = null" : string.Empty; + } + + return $"{codeArgs}{string.Join(", ", UrlParts.Select(p => $"{p.HighLevelTypeName} {p.Name.ToCamelCase()}{Optional(p)}"))}, "; + } + + public string SelectorArguments() + { + if (CodeConfiguration.DescriptorConstructors.TryGetValue(CsharpNames.DescriptorName, out var codeArgs)) + return string.Join(", ", codeArgs.Split(',').Select(a => a.Split(' ').Last())); + + var parts = UrlParts.Where(p => p.Required).ToList(); + if (!parts.Any()) return null; + + string ToArg(UrlPart p) + { + if (IsDocumentRequest) return "documentWithId: document"; + + return p.HighLevelTypeName.StartsWith("DocumentPath") + ? "documentWithId: id?.Document, index: id?.Self?.Index, id: id?.Self?.Id" + : $"{p.Name.ToCamelCase()}: {p.Name.ToCamelCase()}"; + } + + return string.Join(", ", parts.Select(ToArg)); + } + + public string SelectorChainedDefaults() + { + var parts = UrlParts.Where(p => !p.Required).ToList(); + if (!parts.Any()) return null; + + return $".{string.Join(".", parts.Select(p => $"{p.Name.ToPascalCase()}({p.Name.ToCamelCase()}: {p.Name.ToCamelCase()})"))}"; + } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxView.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxView.cs index 080ba2fd72..2b142220fa 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxView.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/FluentSyntaxView.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,9 +30,9 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public class FluentSyntaxView { - public FluentSyntaxView(FluentSyntaxBase syntax, bool async) => (Syntax, Async) = (syntax, async); + public FluentSyntaxView(FluentSyntaxBase syntax, bool async) => (Syntax, Async) = (syntax, async); - public FluentSyntaxBase Syntax { get; } + public FluentSyntaxBase Syntax { get; } - public bool Async { get; } + public bool Async { get; } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/HighLevelModel.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/HighLevelModel.cs index 8397139d6a..566dd76f6f 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/HighLevelModel.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/HighLevelModel.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,8 +30,8 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public class HighLevelModel { - public CsharpNames CsharpNames { get; set; } - public FluentMethod Fluent { get; set; } - public BoundFluentMethod FluentBound { get; set; } - public InitializerMethod Initializer { get; set; } + public CsharpNames CsharpNames { get; set; } + public FluentMethod Fluent { get; set; } + public BoundFluentMethod FluentBound { get; set; } + public InitializerMethod Initializer { get; set; } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerMethod.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerMethod.cs index 8d0157e834..82d696ce70 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerMethod.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerMethod.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,40 +33,40 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public class InitializerMethod : MethodSyntaxBase { - public InitializerMethod(CsharpNames names, string link, string summary) : base(names, link, summary) { } + public InitializerMethod(CsharpNames names, string link, string summary) : base(names, link, summary) { } - public string MethodName => CsharpNames.MethodName; + public string MethodName => CsharpNames.MethodName; - public string ArgumentType => CsharpNames.GenericOrNonGenericInterfacePreference; + public string ArgumentType => CsharpNames.GenericOrNonGenericInterfacePreference; - public override string MethodGenerics => - CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) - ? CsharpNames.GenericsDeclaredOnRequest - : CsharpNames.GenericsDeclaredOnResponse; + public override string MethodGenerics => + CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName) + ? CsharpNames.GenericsDeclaredOnRequest + : CsharpNames.GenericsDeclaredOnResponse; - public override string GenericWhereClause => - string.Join(" ", CsharpNames.SplitGeneric(MethodGenerics) - .Where(g => g.Contains("Document")) - .Select(g => $"where {g} : class") - ); + public override string GenericWhereClause => + string.Join(" ", CsharpNames.SplitGeneric(MethodGenerics) + .Where(g => g.Contains("Document")) + .Select(g => $"where {g} : class") + ); - private bool IsCatMethod => CsharpNames.Namespace == "Cat"; + private bool IsCatMethod => CsharpNames.Namespace == "Cat"; - public string DispatchMethod => IsCatMethod ? "DoCat" : "DoRequest"; + public string DispatchMethod => IsCatMethod ? "DoCat" : "DoRequest"; - /// - /// Dispatch needs a class instance so if the response is an interface transform it to the concrete implementation - /// when calling into DoRequest - /// - private string DispatchResponseName => InterfaceResponse ? ResponseName.Substring(1, ResponseName.Length - 1) : ResponseName; + /// + /// Dispatch needs a class instance so if the response is an interface transform it to the concrete implementation + /// when calling into DoRequest + /// + private string DispatchResponseName => InterfaceResponse ? ResponseName.Substring(1, ResponseName.Length - 1) : ResponseName; - public string DispatchGenerics => IsCatMethod - ? $"<{ArgumentType},{CsharpNames.ParametersName},{CsharpNames.RequestName.Replace("Request", "Record")}>" - : $"<{ArgumentType},{DispatchResponseName}>"; + public string DispatchGenerics => IsCatMethod + ? $"<{ArgumentType},{CsharpNames.ParametersName},{CsharpNames.RequestName.Replace("Request", "Record")}>" + : $"<{ArgumentType},{DispatchResponseName}>"; - public string DispatchParameters => IsCatMethod - ? "request" - : CsharpNames.CustomResponseBuilderPerRequestOverride(out var builder) - ? $"request, ResponseBuilder(request.RequestParameters, {builder})" - : "request, request.RequestParameters"; + public string DispatchParameters => IsCatMethod + ? "request" + : CsharpNames.CustomResponseBuilderPerRequestOverride(out var builder) + ? $"request, ResponseBuilder(request.RequestParameters, {builder})" + : "request, request.RequestParameters"; } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerSyntaxView.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerSyntaxView.cs index 8eb9478e8e..a6c60b692c 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerSyntaxView.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/InitializerSyntaxView.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,9 +30,9 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public class InitializerSyntaxView { - public InitializerSyntaxView(InitializerMethod syntax, bool async) => (Syntax, Async) = (syntax, async); + public InitializerSyntaxView(InitializerMethod syntax, bool async) => (Syntax, Async) = (syntax, async); - public InitializerMethod Syntax { get; } + public InitializerMethod Syntax { get; } - public bool Async { get; } + public bool Async { get; } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Methods/MethodSyntaxBase.cs b/src/ApiGenerator/Domain/Code/HighLevel/Methods/MethodSyntaxBase.cs index a7d775e1ed..aa1e0c5958 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Methods/MethodSyntaxBase.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Methods/MethodSyntaxBase.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,22 +30,22 @@ namespace ApiGenerator.Domain.Code.HighLevel.Methods; public abstract class MethodSyntaxBase { - protected MethodSyntaxBase(CsharpNames names, string link, string summary) => - (CsharpNames, DocumentationLink, XmlDocSummary) = (names, link, summary); + protected MethodSyntaxBase(CsharpNames names, string link, string summary) => + (CsharpNames, DocumentationLink, XmlDocSummary) = (names, link, summary); - public string DocumentationLink { get; } + public string DocumentationLink { get; } - public string XmlDocSummary { get; } + public string XmlDocSummary { get; } - protected CsharpNames CsharpNames { get; } + protected CsharpNames CsharpNames { get; } - public bool InterfaceResponse => ResponseName.StartsWith("ISearchResponse<"); + public bool InterfaceResponse => ResponseName.StartsWith("ISearchResponse<"); - public string ResponseName => CsharpNames.GenericOrNonGenericResponseName; + public string ResponseName => CsharpNames.GenericOrNonGenericResponseName; - public string DocumentationCref => CsharpNames.GenericOrNonGenericInterfacePreference; + public string DocumentationCref => CsharpNames.GenericOrNonGenericInterfacePreference; - public abstract string MethodGenerics { get; } + public abstract string MethodGenerics { get; } - public abstract string GenericWhereClause { get; } + public abstract string GenericWhereClause { get; } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Requests/Constructor.cs b/src/ApiGenerator/Domain/Code/HighLevel/Requests/Constructor.cs index 794ec1c0a7..14f04f0f4b 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Requests/Constructor.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Requests/Constructor.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,100 +36,100 @@ namespace ApiGenerator.Domain.Code.HighLevel.Requests; public class Constructor { - private static readonly string Indent = $"{Environment.NewLine}\t\t"; - public string AdditionalCode { get; set; } = string.Empty; - public bool Parameterless { get; set; } - public string Body { get; set; } - public string Description { get; set; } - public string Generated { get; set; } + private static readonly string Indent = $"{Environment.NewLine}\t\t"; + public string AdditionalCode { get; set; } = string.Empty; + public bool Parameterless { get; set; } + public string Body { get; set; } + public string Description { get; set; } + public string Generated { get; set; } - public static IEnumerable DescriptorConstructors(CsharpNames names, UrlInformation url) - { - var m = names.DescriptorName; - var generic = FirstGeneric(names.GenericsDeclaredOnDescriptor); - var generateGeneric = !string.IsNullOrEmpty(generic); - return GenerateConstructors(url, true, generateGeneric, m, generic); - } + public static IEnumerable DescriptorConstructors(CsharpNames names, UrlInformation url) + { + var m = names.DescriptorName; + var generic = FirstGeneric(names.GenericsDeclaredOnDescriptor); + var generateGeneric = !string.IsNullOrEmpty(generic); + return GenerateConstructors(url, true, generateGeneric, m, generic); + } - public static IEnumerable RequestConstructors(CsharpNames names, UrlInformation url, bool inheritsFromPlainRequestBase) - { - var generic = FirstGeneric(names.GenericsDeclaredOnRequest); - var generateGeneric = CodeConfiguration.GenericOnlyInterfaces.Contains(names.RequestInterfaceName) || !inheritsFromPlainRequestBase; - return GenerateConstructors(url, inheritsFromPlainRequestBase, generateGeneric, names.RequestName, generic); - } + public static IEnumerable RequestConstructors(CsharpNames names, UrlInformation url, bool inheritsFromPlainRequestBase) + { + var generic = FirstGeneric(names.GenericsDeclaredOnRequest); + var generateGeneric = CodeConfiguration.GenericOnlyInterfaces.Contains(names.RequestInterfaceName) || !inheritsFromPlainRequestBase; + return GenerateConstructors(url, inheritsFromPlainRequestBase, generateGeneric, names.RequestName, generic); + } - private static string FirstGeneric(string fullGenericString) => - fullGenericString?.Replace("<", "").Replace(">", "").Split(",").First().Trim(); + private static string FirstGeneric(string fullGenericString) => + fullGenericString?.Replace("<", "").Replace(">", "").Split(",").First().Trim(); - private static IEnumerable GenerateConstructors( - UrlInformation url, - bool inheritsFromPlainRequestBase, - bool generateGeneric, - string typeName, - string generic - ) - { - var ctors = new List(); + private static IEnumerable GenerateConstructors( + UrlInformation url, + bool inheritsFromPlainRequestBase, + bool generateGeneric, + string typeName, + string generic + ) + { + var ctors = new List(); - var paths = url.Paths.ToList(); + var paths = url.Paths.ToList(); - if (url.IsPartless) return ctors; + if (url.IsPartless) return ctors; - ctors.AddRange(from path in paths - let baseArgs = inheritsFromPlainRequestBase ? path.RequestBaseArguments : path.TypedSubClassBaseArguments - let constParams = path.ConstructorArguments - let generated = $"public {typeName}({constParams}) : base({baseArgs})" - select new Constructor - { - Parameterless = string.IsNullOrEmpty(constParams), - Generated = generated, - Description = path.GetXmlDocs(Indent), - //Body = isDocumentApi ? $" => Q(\"routing\", new Routing(() => AutoRouteDocument()));" : string.Empty - Body = string.Empty - }); + ctors.AddRange(from path in paths + let baseArgs = inheritsFromPlainRequestBase ? path.RequestBaseArguments : path.TypedSubClassBaseArguments + let constParams = path.ConstructorArguments + let generated = $"public {typeName}({constParams}) : base({baseArgs})" + select new Constructor + { + Parameterless = string.IsNullOrEmpty(constParams), + Generated = generated, + Description = path.GetXmlDocs(Indent), + //Body = isDocumentApi ? $" => Q(\"routing\", new Routing(() => AutoRouteDocument()));" : string.Empty + Body = string.Empty + }); - if (generateGeneric && !string.IsNullOrWhiteSpace(generic)) - { - ctors.AddRange(from path in paths.Where(path => path.HasResolvableArguments) - let baseArgs = path.AutoResolveBaseArguments(generic) - let constructorArgs = path.AutoResolveConstructorArguments - let baseOrThis = inheritsFromPlainRequestBase - ? "this" - : "base" - let generated = $"public {typeName}({constructorArgs}) : {baseOrThis}({baseArgs})" - select new Constructor - { - Parameterless = string.IsNullOrEmpty(constructorArgs), - Generated = generated, - Description = path.GetXmlDocs(Indent, true), - Body = string.Empty - }); + if (generateGeneric && !string.IsNullOrWhiteSpace(generic)) + { + ctors.AddRange(from path in paths.Where(path => path.HasResolvableArguments) + let baseArgs = path.AutoResolveBaseArguments(generic) + let constructorArgs = path.AutoResolveConstructorArguments + let baseOrThis = inheritsFromPlainRequestBase + ? "this" + : "base" + let generated = $"public {typeName}({constructorArgs}) : {baseOrThis}({baseArgs})" + select new Constructor + { + Parameterless = string.IsNullOrEmpty(constructorArgs), + Generated = generated, + Description = path.GetXmlDocs(Indent, true), + Body = string.Empty + }); - if (url.TryGetDocumentApiPath(out var docPath)) - { - var docPathBaseArgs = docPath.DocumentPathBaseArgument(generic); - var docPathConstArgs = docPath.DocumentPathConstructorArgument(generic); - ctors.Add(new Constructor - { - Parameterless = string.IsNullOrEmpty(docPathConstArgs), - Generated = $"public {typeName}({docPathConstArgs}) : this({docPathBaseArgs})", - AdditionalCode = $"partial void DocumentFromPath({generic} document);", - Description = docPath.GetXmlDocs(Indent, true, true), - Body = "=> DocumentFromPath(documentWithId);" - }); - } - } - var constructors = ctors.GroupBy(c => c.Generated.Split(new[] { ':' }, 2)[0]).Select(g => g.Last()).ToList(); - if (!constructors.Any(c => c.Parameterless)) - { - constructors.Add(new Constructor - { - Parameterless = true, - Generated = $"protected {typeName}() : base()", - Description = - $"///Used for serialization purposes, making sure we have a parameterless constructor{Indent}[SerializationConstructor]" - }); - } - return constructors; - } + if (url.TryGetDocumentApiPath(out var docPath)) + { + var docPathBaseArgs = docPath.DocumentPathBaseArgument(generic); + var docPathConstArgs = docPath.DocumentPathConstructorArgument(generic); + ctors.Add(new Constructor + { + Parameterless = string.IsNullOrEmpty(docPathConstArgs), + Generated = $"public {typeName}({docPathConstArgs}) : this({docPathBaseArgs})", + AdditionalCode = $"partial void DocumentFromPath({generic} document);", + Description = docPath.GetXmlDocs(Indent, true, true), + Body = "=> DocumentFromPath(documentWithId);" + }); + } + } + var constructors = ctors.GroupBy(c => c.Generated.Split(new[] { ':' }, 2)[0]).Select(g => g.Last()).ToList(); + if (!constructors.Any(c => c.Parameterless)) + { + constructors.Add(new Constructor + { + Parameterless = true, + Generated = $"protected {typeName}() : base()", + Description = + $"///Used for serialization purposes, making sure we have a parameterless constructor{Indent}[SerializationConstructor]" + }); + } + return constructors; + } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Requests/DescriptorPartialImplementation.cs b/src/ApiGenerator/Domain/Code/HighLevel/Requests/DescriptorPartialImplementation.cs index 65b8b43af9..9f98d14463 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Requests/DescriptorPartialImplementation.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Requests/DescriptorPartialImplementation.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -34,63 +34,63 @@ namespace ApiGenerator.Domain.Code.HighLevel.Requests; public class DescriptorPartialImplementation { - public CsharpNames CsharpNames { get; set; } - public string OfficialDocumentationLink { get; set; } - public IReadOnlyCollection Constructors { get; set; } - public IReadOnlyCollection Parts { get; set; } - public IReadOnlyCollection Paths { get; set; } - public IReadOnlyCollection Params { get; set; } - public bool HasBody { get; set; } + public CsharpNames CsharpNames { get; set; } + public string OfficialDocumentationLink { get; set; } + public IReadOnlyCollection Constructors { get; set; } + public IReadOnlyCollection Parts { get; set; } + public IReadOnlyCollection Paths { get; set; } + public IReadOnlyCollection Params { get; set; } + public bool HasBody { get; set; } - public IEnumerable GetFluentRouteSetters() - { - var setters = new List(); - if (!Parts.Any()) return setters; + public IEnumerable GetFluentRouteSetters() + { + var setters = new List(); + if (!Parts.Any()) return setters; - var alwaysGenerate = new[] { "index" }; - var parts = Parts - .Where(p => !p.Required || alwaysGenerate.Contains(p.Name)) - .Where(p => !string.IsNullOrEmpty(p.Name)) - .ToList(); - var returnType = CsharpNames.GenericOrNonGenericDescriptorName; - foreach (var part in parts) - { - var paramName = part.Name.ToPascalCase(); - paramName = paramName.Length > 1 - ? paramName[..1].ToLowerInvariant() + paramName[1..] - : paramName.ToLowerInvariant(); + var alwaysGenerate = new[] { "index" }; + var parts = Parts + .Where(p => !p.Required || alwaysGenerate.Contains(p.Name)) + .Where(p => !string.IsNullOrEmpty(p.Name)) + .ToList(); + var returnType = CsharpNames.GenericOrNonGenericDescriptorName; + foreach (var part in parts) + { + var paramName = part.Name.ToPascalCase(); + paramName = paramName.Length > 1 + ? paramName[..1].ToLowerInvariant() + paramName[1..] + : paramName.ToLowerInvariant(); - var routeSetter = part.Required ? "Required" : "Optional"; + var routeSetter = part.Required ? "Required" : "Optional"; - var code = - $"public {returnType} {part.InterfaceName}({part.HighLevelTypeName} {paramName}) => Assign({paramName}, (a,v)=>a.RouteValues.{routeSetter}(\"{part.Name}\", v));"; - var xmlDoc = $"///{part.Description}"; - setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); - switch (paramName) - { - case "index": - { - code = $"public {returnType} {part.InterfaceName}() where TOther : class "; - code += $"=> Assign(typeof(TOther), (a,v)=>a.RouteValues.{routeSetter}(\"{part.Name}\", ({part.HighLevelTypeName})v));"; - xmlDoc = $"///a shortcut into calling {part.InterfaceName}(typeof(TOther))"; - setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); + var code = + $"public {returnType} {part.InterfaceName}({part.HighLevelTypeName} {paramName}) => Assign({paramName}, (a,v)=>a.RouteValues.{routeSetter}(\"{part.Name}\", v));"; + var xmlDoc = $"///{part.Description}"; + setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); + switch (paramName) + { + case "index": + { + code = $"public {returnType} {part.InterfaceName}() where TOther : class "; + code += $"=> Assign(typeof(TOther), (a,v)=>a.RouteValues.{routeSetter}(\"{part.Name}\", ({part.HighLevelTypeName})v));"; + xmlDoc = $"///a shortcut into calling {part.InterfaceName}(typeof(TOther))"; + setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); - if (part.Type == "list") - { - code = $"public {returnType} AllIndices() => Index(Indices.All);"; - xmlDoc = "///A shortcut into calling Index(Indices.All)"; - setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); - } - break; - } - case "fields" when part.Type == "list": - code = $"public {returnType} Fields(params Expression>[] fields) "; - code += $"=> Assign(fields, (a,v)=>a.RouteValues.{routeSetter}(\"fields\", (Fields)v));"; - xmlDoc = $"///{part.Description}"; - setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); - break; - } - } - return setters; - } + if (part.Type == "list") + { + code = $"public {returnType} AllIndices() => Index(Indices.All);"; + xmlDoc = "///A shortcut into calling Index(Indices.All)"; + setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); + } + break; + } + case "fields" when part.Type == "list": + code = $"public {returnType} Fields(params Expression>[] fields) "; + code += $"=> Assign(fields, (a,v)=>a.RouteValues.{routeSetter}(\"fields\", (Fields)v));"; + xmlDoc = $"///{part.Description}"; + setters.Add(new FluentRouteSetter { Code = code, XmlDoc = xmlDoc }); + break; + } + } + return setters; + } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Requests/FluentRouteSetter.cs b/src/ApiGenerator/Domain/Code/HighLevel/Requests/FluentRouteSetter.cs index a6780d0c51..be033bee4b 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Requests/FluentRouteSetter.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Requests/FluentRouteSetter.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,6 +30,6 @@ namespace ApiGenerator.Domain.Code.HighLevel.Requests; public class FluentRouteSetter { - public string Code { get; set; } - public string XmlDoc { get; set; } + public string Code { get; set; } + public string XmlDoc { get; set; } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestInterface.cs b/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestInterface.cs index 3b6cabd268..584b77f713 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestInterface.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestInterface.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -34,22 +34,22 @@ namespace ApiGenerator.Domain.Code.HighLevel.Requests; public class RequestInterface { - public IReadOnlyCollection UrlParts { get; set; } + public IReadOnlyCollection UrlParts { get; set; } - /// - /// Partial parameters are query string parameters we prefer to send over the body of a request. - /// We declare these on the generated interfaces so that we don't forget to implement them in our request - /// implementations - /// - public IReadOnlyCollection PartialParameters { get; set; } + /// + /// Partial parameters are query string parameters we prefer to send over the body of a request. + /// We declare these on the generated interfaces so that we don't forget to implement them in our request + /// implementations + /// + public IReadOnlyCollection PartialParameters { get; set; } - public string OfficialDocumentationLink { get; set; } + public string OfficialDocumentationLink { get; set; } - public CsharpNames CsharpNames { get; set; } + public CsharpNames CsharpNames { get; set; } - private bool GenerateOnlyGenericInterface => CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName); + private bool GenerateOnlyGenericInterface => CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName); - public bool NeedsGenericInterface => !GenerateOnlyGenericInterface && !string.IsNullOrWhiteSpace(CsharpNames.GenericsDeclaredOnRequest); + public bool NeedsGenericInterface => !GenerateOnlyGenericInterface && !string.IsNullOrWhiteSpace(CsharpNames.GenericsDeclaredOnRequest); - public string Name => CsharpNames.GenericOrNonGenericInterfacePreference; + public string Name => CsharpNames.GenericOrNonGenericInterfacePreference; } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestParameterImplementation.cs b/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestParameterImplementation.cs index 9fc3281f25..a7113a12e7 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestParameterImplementation.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestParameterImplementation.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,8 +33,8 @@ namespace ApiGenerator.Domain.Code.HighLevel.Requests; public class RequestParameterImplementation { - public CsharpNames CsharpNames { get; set; } - public string OfficialDocumentationLink { get; set; } - public IReadOnlyCollection Params { get; set; } - public string HttpMethod { get; set; } + public CsharpNames CsharpNames { get; set; } + public string OfficialDocumentationLink { get; set; } + public IReadOnlyCollection Params { get; set; } + public string HttpMethod { get; set; } } diff --git a/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestPartialImplementation.cs b/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestPartialImplementation.cs index 7793febf5c..9430c94699 100644 --- a/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestPartialImplementation.cs +++ b/src/ApiGenerator/Domain/Code/HighLevel/Requests/RequestPartialImplementation.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -34,21 +34,21 @@ namespace ApiGenerator.Domain.Code.HighLevel.Requests; public class RequestPartialImplementation { - public CsharpNames CsharpNames { get; set; } - public string OfficialDocumentationLink { get; set; } - public Stability Stability { get; set; } - public IReadOnlyCollection Parts { get; set; } - public IReadOnlyCollection Paths { get; set; } - public IReadOnlyCollection Params { get; set; } - public IReadOnlyCollection Constructors { get; set; } - public IReadOnlyCollection GenericConstructors { get; set; } - public bool HasBody { get; set; } + public CsharpNames CsharpNames { get; set; } + public string OfficialDocumentationLink { get; set; } + public Stability Stability { get; set; } + public IReadOnlyCollection Parts { get; set; } + public IReadOnlyCollection Paths { get; set; } + public IReadOnlyCollection Params { get; set; } + public IReadOnlyCollection Constructors { get; set; } + public IReadOnlyCollection GenericConstructors { get; set; } + public bool HasBody { get; set; } - private bool GenerateOnlyGenericInterface => CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName); + private bool GenerateOnlyGenericInterface => CodeConfiguration.GenericOnlyInterfaces.Contains(CsharpNames.RequestInterfaceName); - public bool NeedsGenericImplementation => !GenerateOnlyGenericInterface && !string.IsNullOrWhiteSpace(CsharpNames.GenericsDeclaredOnRequest); + public bool NeedsGenericImplementation => !GenerateOnlyGenericInterface && !string.IsNullOrWhiteSpace(CsharpNames.GenericsDeclaredOnRequest); - public string Name => CsharpNames.GenericOrNonGenericRequestPreference; + public string Name => CsharpNames.GenericOrNonGenericRequestPreference; - public string InterfaceName => CsharpNames.GenericOrNonGenericInterfacePreference; + public string InterfaceName => CsharpNames.GenericOrNonGenericInterfacePreference; } diff --git a/src/ApiGenerator/Domain/Code/LowLevel/LowLevelClientMethod.cs b/src/ApiGenerator/Domain/Code/LowLevel/LowLevelClientMethod.cs index 4ddb1f540c..44250a732f 100644 --- a/src/ApiGenerator/Domain/Code/LowLevel/LowLevelClientMethod.cs +++ b/src/ApiGenerator/Domain/Code/LowLevel/LowLevelClientMethod.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -35,42 +35,42 @@ namespace ApiGenerator.Domain.Code.LowLevel; public class LowLevelClientMethod { - public CsharpNames CsharpNames { get; set; } + public CsharpNames CsharpNames { get; set; } - public string Arguments { get; set; } - public string OfficialDocumentationLink { get; set; } + public string Arguments { get; set; } + public string OfficialDocumentationLink { get; set; } - public Stability Stability { get; set; } - public string PerPathMethodName { get; set; } - public string HttpMethod { get; set; } + public Stability Stability { get; set; } + public string PerPathMethodName { get; set; } + public string HttpMethod { get; set; } - public DeprecatedPath DeprecatedPath { get; set; } - public UrlInformation Url { get; set; } - public bool HasBody { get; set; } - public IEnumerable Parts { get; set; } - public string Path { get; set; } + public DeprecatedPath DeprecatedPath { get; set; } + public UrlInformation Url { get; set; } + public bool HasBody { get; set; } + public IEnumerable Parts { get; set; } + public string Path { get; set; } - public string UrlInCode - { - get - { - string Evaluator(Match m) - { - var arg = m.Groups[^1].Value.ToCamelCase(); - return $"{{{arg}:{arg}}}"; - } + public string UrlInCode + { + get + { + string Evaluator(Match m) + { + var arg = m.Groups[^1].Value.ToCamelCase(); + return $"{{{arg}:{arg}}}"; + } - var url = Path.TrimStart('/'); - var options = Url.OriginalParts?.Select(p => p.Key) ?? Enumerable.Empty(); + var url = Path.TrimStart('/'); + var options = Url.OriginalParts?.Select(p => p.Key) ?? Enumerable.Empty(); - if (!Path.Contains('{')) return $"\"{url}\""; + if (!Path.Contains('{')) return $"\"{url}\""; - var patchedUrl = Regex.Replace(url, $"{{({string.Join("|", options)})}}", Evaluator); + var patchedUrl = Regex.Replace(url, $"{{({string.Join("|", options)})}}", Evaluator); - return $"Url($\"{patchedUrl}\")"; - } - } + return $"Url($\"{patchedUrl}\")"; + } + } - public string MapsApiArguments { get; set; } + public string MapsApiArguments { get; set; } } diff --git a/src/ApiGenerator/Domain/RestApiSpec.cs b/src/ApiGenerator/Domain/RestApiSpec.cs index 6893ccbea9..2bb50b686e 100644 --- a/src/ApiGenerator/Domain/RestApiSpec.cs +++ b/src/ApiGenerator/Domain/RestApiSpec.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,63 +36,63 @@ namespace ApiGenerator.Domain; public class EnumDescription { - public string Name { get; set; } - public IEnumerable Options { get; set; } + public string Name { get; set; } + public IEnumerable Options { get; set; } } public class RestApiSpec { - public IDictionary Endpoints { get; set; } + public IDictionary Endpoints { get; set; } - public ImmutableSortedDictionary> EndpointsPerNamespaceLowLevel => - Endpoints.Values.GroupBy(e => e.CsharpNames.Namespace) - .ToImmutableSortedDictionary(kv => kv.Key, kv => kv.ToList().AsReadOnly()); + public ImmutableSortedDictionary> EndpointsPerNamespaceLowLevel => + Endpoints.Values.GroupBy(e => e.CsharpNames.Namespace) + .ToImmutableSortedDictionary(kv => kv.Key, kv => kv.ToList().AsReadOnly()); - public ImmutableSortedDictionary> EndpointsPerNamespaceHighLevel => - Endpoints.Values - .GroupBy(e => e.CsharpNames.Namespace) - .ToImmutableSortedDictionary(kv => kv.Key, kv => kv.ToList().AsReadOnly()); + public ImmutableSortedDictionary> EndpointsPerNamespaceHighLevel => + Endpoints.Values + .GroupBy(e => e.CsharpNames.Namespace) + .ToImmutableSortedDictionary(kv => kv.Key, kv => kv.ToList().AsReadOnly()); - private IEnumerable _enumDescriptions; + private IEnumerable _enumDescriptions; - public IEnumerable EnumsInTheSpec - { - get - { - if (_enumDescriptions != null) return _enumDescriptions; + public IEnumerable EnumsInTheSpec + { + get + { + if (_enumDescriptions != null) return _enumDescriptions; - string CreateName(string name, string methodName, string @namespace) - { - if (!name.ToLowerInvariant().Contains("metric") - && name.ToLowerInvariant() != "status" - && name.ToLowerInvariant() != "format") return name; + string CreateName(string name, string methodName, string @namespace) + { + if (!name.ToLowerInvariant().Contains("metric") + && name.ToLowerInvariant() != "status" + && name.ToLowerInvariant() != "format") return name; - return methodName.StartsWith(@namespace) ? methodName + name : @namespace + methodName + name; - } + return methodName.StartsWith(@namespace) ? methodName + name : @namespace + methodName + name; + } - var urlParameterEnums = Endpoints.Values - .SelectMany(e => e.Url.Params.Values, (e, param) => new { e, param }) - .Where(t => t.param.Options?.Any() ?? false) - .Select(t => new { t.param, name = CreateName(t.param.ClsName, t.e.CsharpNames.MethodName, t.e.CsharpNames.Namespace) }) - .Where(t => t.name != "Time") - .Select(t => new EnumDescription { Name = t.name, Options = t.param.Options }).ToList(); + var urlParameterEnums = Endpoints.Values + .SelectMany(e => e.Url.Params.Values, (e, param) => new { e, param }) + .Where(t => t.param.Options?.Any() ?? false) + .Select(t => new { t.param, name = CreateName(t.param.ClsName, t.e.CsharpNames.MethodName, t.e.CsharpNames.Namespace) }) + .Where(t => t.name != "Time") + .Select(t => new EnumDescription { Name = t.name, Options = t.param.Options }).ToList(); - var urlPartEnums = Endpoints.Values - .SelectMany(e => e.Url.Parts, (e, part) => new { e, part }) - .Where(t => t.part.Options?.Any() ?? false) - .Select(t => new EnumDescription - { - Name = CreateName(t.part.Name.ToPascalCase(), t.e.CsharpNames.MethodName, t.e.CsharpNames.Namespace), - Options = t.part.Options - }).ToList(); + var urlPartEnums = Endpoints.Values + .SelectMany(e => e.Url.Parts, (e, part) => new { e, part }) + .Where(t => t.part.Options?.Any() ?? false) + .Select(t => new EnumDescription + { + Name = CreateName(t.part.Name.ToPascalCase(), t.e.CsharpNames.MethodName, t.e.CsharpNames.Namespace), + Options = t.part.Options + }).ToList(); - _enumDescriptions = urlPartEnums - .Concat(urlParameterEnums) - .DistinctBy(e => e.Name) - .ToList(); + _enumDescriptions = urlPartEnums + .Concat(urlParameterEnums) + .DistinctBy(e => e.Name) + .ToList(); - return _enumDescriptions; - } - } + return _enumDescriptions; + } + } } diff --git a/src/ApiGenerator/Domain/Specification/ApiEndpoint.cs b/src/ApiGenerator/Domain/Specification/ApiEndpoint.cs index 3c0da04112..75d616fdf3 100644 --- a/src/ApiGenerator/Domain/Specification/ApiEndpoint.cs +++ b/src/ApiGenerator/Domain/Specification/ApiEndpoint.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -38,164 +38,164 @@ namespace ApiGenerator.Domain.Specification; public class ApiEndpoint { - /// The original name as declared in the spec - public string Name { get; set; } - - /// The original namespace as declared in the spec - public string Namespace { get; set; } - - /// The original method name as declared in the spec - public string MethodName { get; set; } - - /// Computed Csharp identifier names - public CsharpNames CsharpNames { get; set; } - - public Stability Stability { get; set; } - - public Documentation OfficialDocumentationLink { get; set; } - - public UrlInformation Url { get; set; } - - public Body Body { get; set; } - - public IReadOnlyCollection HttpMethods { get; set; } - - public IEndpointOverrides Overrides { get; internal set; } - - public RequestInterface RequestInterface => new() - { - CsharpNames = CsharpNames, - UrlParts = Url.Parts, - PartialParameters = - Body == null ? Enumerable.Empty().ToList() : Url.Params.Values.Where(p => p.RenderPartial && !p.Skip).ToList(), - OfficialDocumentationLink = OfficialDocumentationLink?.Url - }; - - public RequestPartialImplementation RequestPartialImplementation => new() - { - CsharpNames = CsharpNames, - OfficialDocumentationLink = OfficialDocumentationLink?.Url, - Stability = Stability, - Paths = Url.Paths, - Parts = Url.Parts, - Params = Url.Params.Values.Where(p => !p.Skip).ToList(), - Constructors = Constructor.RequestConstructors(CsharpNames, Url, true).ToList(), - GenericConstructors = Constructor.RequestConstructors(CsharpNames, Url, false).ToList(), - HasBody = Body != null - }; - - public DescriptorPartialImplementation DescriptorPartialImplementation => new() - { - CsharpNames = CsharpNames, - OfficialDocumentationLink = OfficialDocumentationLink?.Url, - Constructors = Constructor.DescriptorConstructors(CsharpNames, Url).ToList(), - Paths = Url.Paths, - Parts = Url.Parts, - Params = Url.Params.Values.Where(p => !p.Skip).ToList(), - HasBody = Body != null - }; - - public RequestParameterImplementation RequestParameterImplementation => new() - { - CsharpNames = CsharpNames, - OfficialDocumentationLink = OfficialDocumentationLink?.Url, - Params = Url.Params.Values.Where(p => !p.Skip).ToList(), - HttpMethod = PreferredHttpMethod - }; - - public string PreferredHttpMethod - { - get - { - var first = HttpMethods.First(); - if (HttpMethods.Count > 1 && first.ToUpperInvariant() == "GET") - return HttpMethods.Last(); - - return first; - } - } - - public string HighLevelMethodXmlDocDescription => - $"{PreferredHttpMethod} request to the {Name} API, read more about this API online:"; - - public HighLevelModel HighLevelModel => new() - { - CsharpNames = CsharpNames, - Fluent = new FluentMethod(CsharpNames, Url.Parts, - Body is not { Required: true } || HttpMethods.Contains("GET"), - OfficialDocumentationLink?.Url, - HighLevelMethodXmlDocDescription - ), - FluentBound = !CsharpNames.DescriptorBindsOverMultipleDocuments - ? null - : new BoundFluentMethod(CsharpNames, Url.Parts, - Body is not { Required: true } || HttpMethods.Contains("GET"), - OfficialDocumentationLink?.Url, - HighLevelMethodXmlDocDescription - ), - Initializer = new InitializerMethod(CsharpNames, - OfficialDocumentationLink?.Url, - HighLevelMethodXmlDocDescription - ) - }; - - private List _lowLevelClientMethods; - - public IReadOnlyCollection LowLevelClientMethods - { - get - { - if (_lowLevelClientMethods is { Count: > 0 }) return _lowLevelClientMethods; - - // enumerate once and cache - _lowLevelClientMethods = new List(); - - if (OfficialDocumentationLink == null) - Generator.ApiGenerator.Warnings.Add($"API '{Name}' has no documentation"); - - var httpMethod = PreferredHttpMethod; - foreach (var path in Url.PathsWithDeprecations) - { - var methodName = CsharpNames.PerPathMethodName(path.Path); - var parts = new List(path.Parts); - var mapsApiArgumentHints = parts.Select(p => p.Name).ToList(); - httpMethod = Name switch - { - // TODO This is hack until we stop transforming the new spec format into the old - "index" when !mapsApiArgumentHints.Contains("id") => "POST", - "index" => PreferredHttpMethod, - _ => httpMethod - }; - - if (Body != null) - { - parts.Add(new UrlPart { Name = "body", Type = "PostData", Description = Body.Description }); - mapsApiArgumentHints.Add("body"); - } - - var args = parts - .Select(p => p.Argument) - .Concat(new[] { $"{CsharpNames.ParametersName} requestParameters = null" }) - .ToList(); - - var apiMethod = new LowLevelClientMethod - { - Arguments = string.Join(", ", args), - MapsApiArguments = string.Join(", ", mapsApiArgumentHints), - CsharpNames = CsharpNames, - PerPathMethodName = methodName, - HttpMethod = httpMethod, - OfficialDocumentationLink = OfficialDocumentationLink?.Url, - Stability = Stability, - DeprecatedPath = path.Deprecation, - Path = path.Path, - Parts = parts, - Url = Url, - HasBody = Body != null - }; - _lowLevelClientMethods.Add(apiMethod); - } - return _lowLevelClientMethods; - } - } + /// The original name as declared in the spec + public string Name { get; set; } + + /// The original namespace as declared in the spec + public string Namespace { get; set; } + + /// The original method name as declared in the spec + public string MethodName { get; set; } + + /// Computed Csharp identifier names + public CsharpNames CsharpNames { get; set; } + + public Stability Stability { get; set; } + + public Documentation OfficialDocumentationLink { get; set; } + + public UrlInformation Url { get; set; } + + public Body Body { get; set; } + + public IReadOnlyCollection HttpMethods { get; set; } + + public IEndpointOverrides Overrides { get; internal set; } + + public RequestInterface RequestInterface => new() + { + CsharpNames = CsharpNames, + UrlParts = Url.Parts, + PartialParameters = + Body == null ? Enumerable.Empty().ToList() : Url.Params.Values.Where(p => p.RenderPartial && !p.Skip).ToList(), + OfficialDocumentationLink = OfficialDocumentationLink?.Url + }; + + public RequestPartialImplementation RequestPartialImplementation => new() + { + CsharpNames = CsharpNames, + OfficialDocumentationLink = OfficialDocumentationLink?.Url, + Stability = Stability, + Paths = Url.Paths, + Parts = Url.Parts, + Params = Url.Params.Values.Where(p => !p.Skip).ToList(), + Constructors = Constructor.RequestConstructors(CsharpNames, Url, true).ToList(), + GenericConstructors = Constructor.RequestConstructors(CsharpNames, Url, false).ToList(), + HasBody = Body != null + }; + + public DescriptorPartialImplementation DescriptorPartialImplementation => new() + { + CsharpNames = CsharpNames, + OfficialDocumentationLink = OfficialDocumentationLink?.Url, + Constructors = Constructor.DescriptorConstructors(CsharpNames, Url).ToList(), + Paths = Url.Paths, + Parts = Url.Parts, + Params = Url.Params.Values.Where(p => !p.Skip).ToList(), + HasBody = Body != null + }; + + public RequestParameterImplementation RequestParameterImplementation => new() + { + CsharpNames = CsharpNames, + OfficialDocumentationLink = OfficialDocumentationLink?.Url, + Params = Url.Params.Values.Where(p => !p.Skip).ToList(), + HttpMethod = PreferredHttpMethod + }; + + public string PreferredHttpMethod + { + get + { + var first = HttpMethods.First(); + if (HttpMethods.Count > 1 && first.ToUpperInvariant() == "GET") + return HttpMethods.Last(); + + return first; + } + } + + public string HighLevelMethodXmlDocDescription => + $"{PreferredHttpMethod} request to the {Name} API, read more about this API online:"; + + public HighLevelModel HighLevelModel => new() + { + CsharpNames = CsharpNames, + Fluent = new FluentMethod(CsharpNames, Url.Parts, + Body is not { Required: true } || HttpMethods.Contains("GET"), + OfficialDocumentationLink?.Url, + HighLevelMethodXmlDocDescription + ), + FluentBound = !CsharpNames.DescriptorBindsOverMultipleDocuments + ? null + : new BoundFluentMethod(CsharpNames, Url.Parts, + Body is not { Required: true } || HttpMethods.Contains("GET"), + OfficialDocumentationLink?.Url, + HighLevelMethodXmlDocDescription + ), + Initializer = new InitializerMethod(CsharpNames, + OfficialDocumentationLink?.Url, + HighLevelMethodXmlDocDescription + ) + }; + + private List _lowLevelClientMethods; + + public IReadOnlyCollection LowLevelClientMethods + { + get + { + if (_lowLevelClientMethods is { Count: > 0 }) return _lowLevelClientMethods; + + // enumerate once and cache + _lowLevelClientMethods = new List(); + + if (OfficialDocumentationLink == null) + Generator.ApiGenerator.Warnings.Add($"API '{Name}' has no documentation"); + + var httpMethod = PreferredHttpMethod; + foreach (var path in Url.PathsWithDeprecations) + { + var methodName = CsharpNames.PerPathMethodName(path.Path); + var parts = new List(path.Parts); + var mapsApiArgumentHints = parts.Select(p => p.Name).ToList(); + httpMethod = Name switch + { + // TODO This is hack until we stop transforming the new spec format into the old + "index" when !mapsApiArgumentHints.Contains("id") => "POST", + "index" => PreferredHttpMethod, + _ => httpMethod + }; + + if (Body != null) + { + parts.Add(new UrlPart { Name = "body", Type = "PostData", Description = Body.Description }); + mapsApiArgumentHints.Add("body"); + } + + var args = parts + .Select(p => p.Argument) + .Concat(new[] { $"{CsharpNames.ParametersName} requestParameters = null" }) + .ToList(); + + var apiMethod = new LowLevelClientMethod + { + Arguments = string.Join(", ", args), + MapsApiArguments = string.Join(", ", mapsApiArgumentHints), + CsharpNames = CsharpNames, + PerPathMethodName = methodName, + HttpMethod = httpMethod, + OfficialDocumentationLink = OfficialDocumentationLink?.Url, + Stability = Stability, + DeprecatedPath = path.Deprecation, + Path = path.Path, + Parts = parts, + Url = Url, + HasBody = Body != null + }; + _lowLevelClientMethods.Add(apiMethod); + } + return _lowLevelClientMethods; + } + } } diff --git a/src/ApiGenerator/Domain/Specification/Body.cs b/src/ApiGenerator/Domain/Specification/Body.cs index 175e071e54..a6c8700e9e 100644 --- a/src/ApiGenerator/Domain/Specification/Body.cs +++ b/src/ApiGenerator/Domain/Specification/Body.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,6 +30,6 @@ namespace ApiGenerator.Domain.Specification; public class Body { - public string Description { get; set; } - public bool Required { get; set; } + public string Description { get; set; } + public bool Required { get; set; } } diff --git a/src/ApiGenerator/Domain/Specification/Documentation.cs b/src/ApiGenerator/Domain/Specification/Documentation.cs index c778c15cb3..0aaf63aad7 100644 --- a/src/ApiGenerator/Domain/Specification/Documentation.cs +++ b/src/ApiGenerator/Domain/Specification/Documentation.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -30,13 +30,13 @@ namespace ApiGenerator.Domain.Specification; public class Documentation { - public string Description { get; set; } + public string Description { get; set; } - private string _url; + private string _url; - public string Url - { - get => _url; - set => _url = value?.Replace("http://", "https://"); - } + public string Url + { + get => _url; + set => _url = value?.Replace("http://", "https://"); + } } diff --git a/src/ApiGenerator/Domain/Specification/QueryParameters.cs b/src/ApiGenerator/Domain/Specification/QueryParameters.cs index 352c59fd0f..11e63b68d5 100644 --- a/src/ApiGenerator/Domain/Specification/QueryParameters.cs +++ b/src/ApiGenerator/Domain/Specification/QueryParameters.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -35,164 +35,164 @@ namespace ApiGenerator.Domain.Specification; public class QueryParameters { - private static readonly string[] FieldsParams = { "fields", "_source_includes", "_source_excludes" }; - - public bool Skip { get; set; } - - public string ClsArgumentName => ClsName.ToCamelCase(); - - public string ClsName { get; set; } - - public string Description { get; set; } - - public IEnumerable DescriptionHighLevel - { - get - { - switch (QueryStringKey) - { - case "routing": - yield return "A document is routed to a particular shard in an index using the following formula"; - yield return " shard_num = hash(_routing) % num_primary_shards"; - yield return "OpenSearch will use the document id if not provided. "; - yield return - "For requests that are constructed from/for a document OpenSearch.Client will automatically infer the routing key"; - yield return - "if that document has a or a routing mapping on for its type exists on "; - - yield break; - case "_source": - yield return "Whether the _source should be included in the response."; - - yield break; - case "filter_path": - yield return Description; - yield return "Use of response filtering can result in a response from OpenSearch "; - yield return "that cannot be correctly deserialized to the respective response type for the request. "; - yield return "In such situations, use the low level client to issue the request and handle response deserialization"; - - yield break; - default: - yield return Description ?? "TODO"; - - yield break; - } - } - } - - public bool IsArray => Type == "list" && TypeHighLevel.EndsWith("[]"); - - public string DescriptorArgumentType => IsArray ? $"params {TypeHighLevel}" : TypeHighLevel; - - public string DescriptorEnumerableArgumentType => - IsArray - ? $"IEnumerable<{TypeHighLevel.TrimEnd('[', ']')}>" - : throw new InvalidOperationException("Only array arguments have IEnumerable overload"); - - public Func FluentGenerator { get; set; } - public bool IsFieldParam => TypeHighLevel == "Field"; - - public bool IsFieldsParam => TypeHighLevel == "Fields"; - - public string Obsolete - { - get - { - if (!string.IsNullOrEmpty(_obsolete)) return _obsolete; - if (Deprecated == null) return null; - if (!string.IsNullOrEmpty(Deprecated.Version) && !string.IsNullOrEmpty(Deprecated.Description)) - return $"Deprecated as of: {Deprecated.Version}, reason: {Deprecated.Description}"; - if (!string.IsNullOrEmpty(Deprecated.Version)) - return $"Deprecated as of: {Deprecated.Version}"; - - return !string.IsNullOrEmpty(Deprecated.Description) ? $"reason: {Deprecated.Description}" : "deprecated"; - } - set => _obsolete = value; - } - - public QueryParameterDeprecation Deprecated { get; set; } - - public IEnumerable Options { get; set; } - public string QueryStringKey { get; set; } - - public bool RenderPartial { get; set; } - public string SetterHighLevel => "value"; - - public string SetterLowLevel => "value"; - - private string _type; - private string _obsolete; - - public string Type - { - // TODO support unions - get => !_type.Contains('|') - ? _type - : _type.Split('|', StringSplitOptions.RemoveEmptyEntries).First().Trim(); - set => _type = value; - } - - public string TypeHighLevel - { - get - { - if (QueryStringKey == "routing") return "Routing"; - - var isFields = FieldsParams.Contains(QueryStringKey) || QueryStringKey.EndsWith("_fields"); - - var csharpType = TypeLowLevel; - switch (csharpType) - { - case "TimeSpan": return "Time"; - } - - switch (Type) - { - case "list" when isFields: - case "string" when isFields: return "Fields"; - case "string" when QueryStringKey.Contains("field"): return "Field"; - default: - return csharpType; - } - } - } - - public string TypeLowLevel - { - get - { - switch (Type) - { - case "boolean": return "bool?"; - case "list": return "string[]"; - case "int": return "int?"; - case "date": return "DateTimeOffset?"; - case "enum": return $"{ClsName}?"; - case "number": - return new[] { "boost", "percen", "score" }.Any(s => QueryStringKey.ToLowerInvariant().Contains(s)) - ? "double?" - : "long?"; - case "duration": - case "time": - return "TimeSpan"; - case "text": - case "": - case null: - return "string"; - default: - return Type; - } - } - } - - - public string InitializerGenerator(string @namespace, string type, string name, string key, string setter, params string[] doc) => - CodeGenerator.Property(@namespace, type, name, key, setter, Obsolete, doc); + private static readonly string[] FieldsParams = { "fields", "_source_includes", "_source_excludes" }; + + public bool Skip { get; set; } + + public string ClsArgumentName => ClsName.ToCamelCase(); + + public string ClsName { get; set; } + + public string Description { get; set; } + + public IEnumerable DescriptionHighLevel + { + get + { + switch (QueryStringKey) + { + case "routing": + yield return "A document is routed to a particular shard in an index using the following formula"; + yield return " shard_num = hash(_routing) % num_primary_shards"; + yield return "OpenSearch will use the document id if not provided. "; + yield return + "For requests that are constructed from/for a document OpenSearch.Client will automatically infer the routing key"; + yield return + "if that document has a or a routing mapping on for its type exists on "; + + yield break; + case "_source": + yield return "Whether the _source should be included in the response."; + + yield break; + case "filter_path": + yield return Description; + yield return "Use of response filtering can result in a response from OpenSearch "; + yield return "that cannot be correctly deserialized to the respective response type for the request. "; + yield return "In such situations, use the low level client to issue the request and handle response deserialization"; + + yield break; + default: + yield return Description ?? "TODO"; + + yield break; + } + } + } + + public bool IsArray => Type == "list" && TypeHighLevel.EndsWith("[]"); + + public string DescriptorArgumentType => IsArray ? $"params {TypeHighLevel}" : TypeHighLevel; + + public string DescriptorEnumerableArgumentType => + IsArray + ? $"IEnumerable<{TypeHighLevel.TrimEnd('[', ']')}>" + : throw new InvalidOperationException("Only array arguments have IEnumerable overload"); + + public Func FluentGenerator { get; set; } + public bool IsFieldParam => TypeHighLevel == "Field"; + + public bool IsFieldsParam => TypeHighLevel == "Fields"; + + public string Obsolete + { + get + { + if (!string.IsNullOrEmpty(_obsolete)) return _obsolete; + if (Deprecated == null) return null; + if (!string.IsNullOrEmpty(Deprecated.Version) && !string.IsNullOrEmpty(Deprecated.Description)) + return $"Deprecated as of: {Deprecated.Version}, reason: {Deprecated.Description}"; + if (!string.IsNullOrEmpty(Deprecated.Version)) + return $"Deprecated as of: {Deprecated.Version}"; + + return !string.IsNullOrEmpty(Deprecated.Description) ? $"reason: {Deprecated.Description}" : "deprecated"; + } + set => _obsolete = value; + } + + public QueryParameterDeprecation Deprecated { get; set; } + + public IEnumerable Options { get; set; } + public string QueryStringKey { get; set; } + + public bool RenderPartial { get; set; } + public string SetterHighLevel => "value"; + + public string SetterLowLevel => "value"; + + private string _type; + private string _obsolete; + + public string Type + { + // TODO support unions + get => !_type.Contains('|') + ? _type + : _type.Split('|', StringSplitOptions.RemoveEmptyEntries).First().Trim(); + set => _type = value; + } + + public string TypeHighLevel + { + get + { + if (QueryStringKey == "routing") return "Routing"; + + var isFields = FieldsParams.Contains(QueryStringKey) || QueryStringKey.EndsWith("_fields"); + + var csharpType = TypeLowLevel; + switch (csharpType) + { + case "TimeSpan": return "Time"; + } + + switch (Type) + { + case "list" when isFields: + case "string" when isFields: return "Fields"; + case "string" when QueryStringKey.Contains("field"): return "Field"; + default: + return csharpType; + } + } + } + + public string TypeLowLevel + { + get + { + switch (Type) + { + case "boolean": return "bool?"; + case "list": return "string[]"; + case "int": return "int?"; + case "date": return "DateTimeOffset?"; + case "enum": return $"{ClsName}?"; + case "number": + return new[] { "boost", "percen", "score" }.Any(s => QueryStringKey.ToLowerInvariant().Contains(s)) + ? "double?" + : "long?"; + case "duration": + case "time": + return "TimeSpan"; + case "text": + case "": + case null: + return "string"; + default: + return Type; + } + } + } + + + public string InitializerGenerator(string @namespace, string type, string name, string key, string setter, params string[] doc) => + CodeGenerator.Property(@namespace, type, name, key, setter, Obsolete, doc); } public class QueryParameterDeprecation { - public string Version { get; set; } + public string Version { get; set; } - public string Description { get; set; } + public string Description { get; set; } } diff --git a/src/ApiGenerator/Domain/Specification/Stability.cs b/src/ApiGenerator/Domain/Specification/Stability.cs index 9ed83eb788..828ed2493c 100644 --- a/src/ApiGenerator/Domain/Specification/Stability.cs +++ b/src/ApiGenerator/Domain/Specification/Stability.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -32,21 +32,21 @@ namespace ApiGenerator.Domain.Specification; public enum Stability { - /// - /// Highly likely to break in the near future (minor/path), no BWC guarantees. Possibly removed in the future. - /// - [EnumMember(Value = "experimental")] - Experimental, + /// + /// Highly likely to break in the near future (minor/path), no BWC guarantees. Possibly removed in the future. + /// + [EnumMember(Value = "experimental")] + Experimental, - /// - /// Less likely to break or be removed but still reserve the right to do so. - /// - [EnumMember(Value = "beta")] - Beta, + /// + /// Less likely to break or be removed but still reserve the right to do so. + /// + [EnumMember(Value = "beta")] + Beta, - /// - /// No backwards breaking changes in a minor. - /// - [EnumMember(Value = "stable")] - Stable + /// + /// No backwards breaking changes in a minor. + /// + [EnumMember(Value = "stable")] + Stable } diff --git a/src/ApiGenerator/Domain/Specification/UrlInformation.cs b/src/ApiGenerator/Domain/Specification/UrlInformation.cs index 3a618e3a4c..30339c5729 100644 --- a/src/ApiGenerator/Domain/Specification/UrlInformation.cs +++ b/src/ApiGenerator/Domain/Specification/UrlInformation.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -35,99 +35,99 @@ namespace ApiGenerator.Domain.Specification; // ReSharper disable once ClassNeverInstantiated.Global public class UrlInformation { - public IDictionary Params { get; set; } = new SortedDictionary(); + public IDictionary Params { get; set; } = new SortedDictionary(); - public IList OriginalPaths { get; set; } = new List(); + public IList OriginalPaths { get; set; } = new List(); - public IDictionary OriginalParts { get; set; } = new SortedDictionary(); + public IDictionary OriginalParts { get; set; } = new SortedDictionary(); - public IList DeprecatedPaths { get; set; } = new List(); + public IList DeprecatedPaths { get; set; } = new List(); - private List _paths; + private List _paths; - public IReadOnlyCollection Paths - { - get - { - if (_paths is { Count: > 0 }) return _paths; + public IReadOnlyCollection Paths + { + get + { + if (_paths is { Count: > 0 }) return _paths; - _paths = OriginalPaths.Select(p => new UrlPath(p, OriginalParts)).ToList(); - return _paths; - } - } + _paths = OriginalPaths.Select(p => new UrlPath(p, OriginalParts)).ToList(); + return _paths; + } + } - private List _pathsWithDeprecation; + private List _pathsWithDeprecation; - public IEnumerable PathsWithDeprecations - { - get - { - if (_pathsWithDeprecation is { Count: > 0 }) return _pathsWithDeprecation; + public IEnumerable PathsWithDeprecations + { + get + { + if (_pathsWithDeprecation is { Count: > 0 }) return _pathsWithDeprecation; - var paths = Paths ?? Array.Empty(); - if (DeprecatedPaths == null || DeprecatedPaths.Count == 0) return Paths; - if (OriginalParts == null) return Paths; + var paths = Paths ?? Array.Empty(); + if (DeprecatedPaths == null || DeprecatedPaths.Count == 0) return Paths; + if (OriginalParts == null) return Paths; - //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 + //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 - var canonicalPartNameLookup = paths.Select(path => new HashSet(path.Parts.Select(p => p.Name))).ToList(); - var withoutDeprecatedAliases = DeprecatedPaths - .Select(deprecatedPath => new - { - deprecatedPath, parts = new HashSet(OriginalParts.Keys.Where(k => deprecatedPath.Path.Contains($"{{{k}}}"))) - }) - .GroupBy(t => t.parts, HashSet.CreateSetComparer()) - .Where(grouped => !canonicalPartNameLookup.Any(set => set.SetEquals(grouped.Key))) - .Select(grouped => grouped.First().deprecatedPath); + var canonicalPartNameLookup = paths.Select(path => new HashSet(path.Parts.Select(p => p.Name))).ToList(); + var withoutDeprecatedAliases = DeprecatedPaths + .Select(deprecatedPath => new + { + deprecatedPath, parts = new HashSet(OriginalParts.Keys.Where(k => deprecatedPath.Path.Contains($"{{{k}}}"))) + }) + .GroupBy(t => t.parts, HashSet.CreateSetComparer()) + .Where(grouped => !canonicalPartNameLookup.Any(set => set.SetEquals(grouped.Key))) + .Select(grouped => grouped.First().deprecatedPath); - _pathsWithDeprecation = paths - .Concat(withoutDeprecatedAliases.Select(p => new UrlPath(p, OriginalParts, Paths))) - .ToList(); + _pathsWithDeprecation = paths + .Concat(withoutDeprecatedAliases.Select(p => new UrlPath(p, OriginalParts, Paths))) + .ToList(); - // now, check for and prefer deprecated URLs + // now, check for and prefer deprecated URLs - var finalPathsWithDeprecations = new List(_pathsWithDeprecation.Count); + 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); - } + 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); + } - _pathsWithDeprecation = finalPathsWithDeprecations; + _pathsWithDeprecation = finalPathsWithDeprecations; - return _pathsWithDeprecation; - } - } + return _pathsWithDeprecation; + } + } - public IReadOnlyCollection Parts => Paths.SelectMany(p => p.Parts).DistinctBy(p => p.Name).ToList(); + public IReadOnlyCollection Parts => Paths.SelectMany(p => p.Parts).DistinctBy(p => p.Name).ToList(); - public bool IsPartless => !Parts.Any(); + public bool IsPartless => !Parts.Any(); - private static readonly string[] DocumentApiParts = { "index", "id" }; + private static readonly string[] DocumentApiParts = { "index", "id" }; - public bool IsDocumentApi => IsADocumentRoute(Parts); + public bool IsDocumentApi => IsADocumentRoute(Parts); - public static bool IsADocumentRoute(IReadOnlyCollection parts) => - parts.Count == DocumentApiParts.Length - && parts.All(p => DocumentApiParts.Contains(p.Name)); + public static bool IsADocumentRoute(IReadOnlyCollection parts) => + parts.Count == DocumentApiParts.Length + && parts.All(p => DocumentApiParts.Contains(p.Name)); - public bool TryGetDocumentApiPath(out UrlPath path) - { - path = null; - if (!IsDocumentApi) return false; + public bool TryGetDocumentApiPath(out UrlPath path) + { + path = null; + if (!IsDocumentApi) return false; - var mostVerbosePath = _paths.OrderByDescending(p => p.Parts.Count).First(); - path = new UrlPath(mostVerbosePath.Path, OriginalParts, mostVerbosePath.Parts); - return true; - } + var mostVerbosePath = _paths.OrderByDescending(p => p.Parts.Count).First(); + path = new UrlPath(mostVerbosePath.Path, OriginalParts, mostVerbosePath.Parts); + return true; + } } diff --git a/src/ApiGenerator/Domain/Specification/UrlPart.cs b/src/ApiGenerator/Domain/Specification/UrlPart.cs index 994d18a46d..78f92e79d3 100644 --- a/src/ApiGenerator/Domain/Specification/UrlPart.cs +++ b/src/ApiGenerator/Domain/Specification/UrlPart.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -33,126 +33,126 @@ namespace ApiGenerator.Domain.Specification; // Rename this type to Deprecation and remove Path duplication public class DeprecatedPath { - public string Version { get; set; } - public string Path { get; set; } - public string Description { get; set; } + public string Version { get; set; } + public string Path { get; set; } + public string Description { get; set; } } public class UrlPart { - private string _description; - - public string Argument => $"{LowLevelTypeName} {NameAsArgument}"; - - public string LowLevelTypeName => - //TODO treat list with fixed options as Flags Enum - Type switch - { - "int" => //does not occur on part - Type, - "number" => //does not occur on part - Type, - "string" => Type, - "list" => "string", - "enum" => Name.ToPascalCase(), - _ => Type - }; - - public string HighLevelTypeName - { - get - { - if (ClrTypeNameOverride != null) return ClrTypeNameOverride; - - switch (Name) - { - case "category_id": return "LongId"; - case "timestamp": return "Timestamp"; - case "index_metric": return "IndexMetrics"; - case "metric": return "Metrics"; - - case "node_id" when Type == "list": - return "NodeIds"; - - case "fields" when Type == "list": - return "Fields"; - - case "parent_task_id": - case "task_id": - return "TaskId"; - - case "forecast_id": - case "action_id": - case "ids" when Type == "list": - return "Ids"; - - case "index": - case "new_index": - case "target": - return Type == "string" ? "IndexName" : "Indices"; - - case "job_id": - case "calendar_id": - case "event_id": - case "snapshot_id": - case "filter_id": - case "model_id": - case "id": - return "Id"; - - case "policy_id": - return Type == "string" ? "Id" : "Ids"; - - case "application": - case "repository": - case "snapshot": - case "target_snapshot": - case "user": - case "username": - case "realms": - case "alias": - case "context": - case "name": - case "thread_pool_patterns": - case "type": - return Type == "string" ? "Name" : "Names"; - - case "block": - return "IndexBlock"; - - case "index_uuid": - return "IndexUuid"; - - //This forces a compilation error post code generation as intended - default: return $"{Type}_"; - } - } - } - - public string ClrTypeNameOverride { get; set; } - - public string Description - { - get => _description; - set => _description = CleanUpDescription(value); - } - - public string InterfaceName => - Name switch - { - "repository" => "RepositoryName", - _ => Name.ToPascalCase() - }; - - 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; } - - private static string CleanUpDescription(string value) => - string.IsNullOrWhiteSpace(value) - ? value - : value.Replace("use `_all` or empty string", "use the special string `_all` or Indices.All"); + private string _description; + + public string Argument => $"{LowLevelTypeName} {NameAsArgument}"; + + public string LowLevelTypeName => + //TODO treat list with fixed options as Flags Enum + Type switch + { + "int" => //does not occur on part + Type, + "number" => //does not occur on part + Type, + "string" => Type, + "list" => "string", + "enum" => Name.ToPascalCase(), + _ => Type + }; + + public string HighLevelTypeName + { + get + { + if (ClrTypeNameOverride != null) return ClrTypeNameOverride; + + switch (Name) + { + case "category_id": return "LongId"; + case "timestamp": return "Timestamp"; + case "index_metric": return "IndexMetrics"; + case "metric": return "Metrics"; + + case "node_id" when Type == "list": + return "NodeIds"; + + case "fields" when Type == "list": + return "Fields"; + + case "parent_task_id": + case "task_id": + return "TaskId"; + + case "forecast_id": + case "action_id": + case "ids" when Type == "list": + return "Ids"; + + case "index": + case "new_index": + case "target": + return Type == "string" ? "IndexName" : "Indices"; + + case "job_id": + case "calendar_id": + case "event_id": + case "snapshot_id": + case "filter_id": + case "model_id": + case "id": + return "Id"; + + case "policy_id": + return Type == "string" ? "Id" : "Ids"; + + case "application": + case "repository": + case "snapshot": + case "target_snapshot": + case "user": + case "username": + case "realms": + case "alias": + case "context": + case "name": + case "thread_pool_patterns": + case "type": + return Type == "string" ? "Name" : "Names"; + + case "block": + return "IndexBlock"; + + case "index_uuid": + return "IndexUuid"; + + //This forces a compilation error post code generation as intended + default: return $"{Type}_"; + } + } + } + + public string ClrTypeNameOverride { get; set; } + + public string Description + { + get => _description; + set => _description = CleanUpDescription(value); + } + + public string InterfaceName => + Name switch + { + "repository" => "RepositoryName", + _ => Name.ToPascalCase() + }; + + 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; } + + private static string CleanUpDescription(string value) => + string.IsNullOrWhiteSpace(value) + ? value + : value.Replace("use `_all` or empty string", "use the special string `_all` or Indices.All"); } diff --git a/src/ApiGenerator/Domain/Specification/UrlPath.cs b/src/ApiGenerator/Domain/Specification/UrlPath.cs index b7c22d26be..84b16999dc 100644 --- a/src/ApiGenerator/Domain/Specification/UrlPath.cs +++ b/src/ApiGenerator/Domain/Specification/UrlPath.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -34,82 +34,82 @@ namespace ApiGenerator.Domain.Specification; public class UrlPath { - private readonly List _additionalPartsForConstructor; - public string Path { get; } - public DeprecatedPath Deprecation { get; } - - - public List Parts { get; } - - //TODO mark the parts that are deprecated - public UrlPath(DeprecatedPath path, IDictionary originalParts, IReadOnlyCollection allNonDeprecatedPaths) - : this(path.Path, originalParts) - { - Deprecation = path; - foreach (var part in Parts) - { - if (!part.Deprecated && !allNonDeprecatedPaths.Any(p => p.Path.Contains($"{{{part.Name}}}"))) - part.Deprecated = true; - } - } - - public UrlPath(string path, IDictionary allParts, List additionalPartsForConstructor = null) - { - _additionalPartsForConstructor = additionalPartsForConstructor ?? new List(); - Path = LeadingBackslash(path); - if (allParts == null) - { - Parts = new List(); - return; - } - var parts = allParts.Select(p => new { p, name = p.Value.Name = p.Key }) - .Where(t => path.Contains($"{{{t.name}}}")) - .OrderBy(t => path.IndexOf($"{{{t.name}}}", StringComparison.Ordinal)) - .Select(t => t.p.Value); - Parts = parts.ToList(); - } - - public string ConstructorArguments => string.Join(", ", Parts.Select(p => $"{p.HighLevelTypeName} {p.NameAsArgument}")); - - public string RequestBaseArguments => - !Parts.Any() - ? string.Empty - : $"r => r.{string.Join(".", Parts.Select(p => $"{(p.Required ? "Required" : "Optional")}(\"{p.Name}\", {p.NameAsArgument})"))}"; - - public string TypedSubClassBaseArguments => string.Join(", ", Parts.Select(p => p.NameAsArgument)); - - private static readonly string[] ResolvableFromT = { "index" }; - - public bool HasResolvableArguments => Parts.Any(p => ResolvableFromT.Contains(p.Name)); - - public string AutoResolveConstructorArguments => string.Join(", ", - Parts.Where(p => !ResolvableFromT.Contains(p.Name)).Select(p => $"{p.HighLevelTypeName} {p.NameAsArgument}")); - - public string AutoResolveBaseArguments(string generic) => - string.Join(", ", Parts.Select(p => !ResolvableFromT.Contains(p.Name) ? p.Name : $"typeof({generic})")); - - public string DocumentPathBaseArgument(string generic) => string.Join(", ", - _additionalPartsForConstructor.Select(p => p.Name == "id" ? "id ?? OpenSearch.Client.Id.From(documentWithId)" - : ResolvableFromT.Contains(p.Name) ? $"{p.Name} ?? typeof({generic})" : p.Name)); - - public string DocumentPathConstructorArgument(string generic) => string.Join(", ", - new[] { $"{generic} documentWithId" }.Concat(_additionalPartsForConstructor.Select(p => $"{p.HighLevelTypeName} {p.NameAsArgument} = null"))); - - public string GetXmlDocs(string indent, bool skipResolvable = false, bool documentConstructor = false) - { - var doc = $@"///{Path}"; - var parts = Parts.Where(p => !skipResolvable || !ResolvableFromT.Contains(p.Name)).ToList(); - if (!parts.Any()) return doc; - - doc += indent; - doc += string.Join(indent, parts.Select(p => P(p.NameAsArgument, documentConstructor - ? "The document used to resolve the path from" - : p.Required - ? "this parameter is required" : "Optional, accepts null"))); - return doc; - } - - private static string P(string name, string description) => $"///{description}"; - - private static string LeadingBackslash(string p) => p.StartsWith("/") ? p : $"/{p}"; + private readonly List _additionalPartsForConstructor; + public string Path { get; } + public DeprecatedPath Deprecation { get; } + + + public List Parts { get; } + + //TODO mark the parts that are deprecated + public UrlPath(DeprecatedPath path, IDictionary originalParts, IReadOnlyCollection allNonDeprecatedPaths) + : this(path.Path, originalParts) + { + Deprecation = path; + foreach (var part in Parts) + { + if (!part.Deprecated && !allNonDeprecatedPaths.Any(p => p.Path.Contains($"{{{part.Name}}}"))) + part.Deprecated = true; + } + } + + public UrlPath(string path, IDictionary allParts, List additionalPartsForConstructor = null) + { + _additionalPartsForConstructor = additionalPartsForConstructor ?? new List(); + Path = LeadingBackslash(path); + if (allParts == null) + { + Parts = new List(); + return; + } + var parts = allParts.Select(p => new { p, name = p.Value.Name = p.Key }) + .Where(t => path.Contains($"{{{t.name}}}")) + .OrderBy(t => path.IndexOf($"{{{t.name}}}", StringComparison.Ordinal)) + .Select(t => t.p.Value); + Parts = parts.ToList(); + } + + public string ConstructorArguments => string.Join(", ", Parts.Select(p => $"{p.HighLevelTypeName} {p.NameAsArgument}")); + + public string RequestBaseArguments => + !Parts.Any() + ? string.Empty + : $"r => r.{string.Join(".", Parts.Select(p => $"{(p.Required ? "Required" : "Optional")}(\"{p.Name}\", {p.NameAsArgument})"))}"; + + public string TypedSubClassBaseArguments => string.Join(", ", Parts.Select(p => p.NameAsArgument)); + + private static readonly string[] ResolvableFromT = { "index" }; + + public bool HasResolvableArguments => Parts.Any(p => ResolvableFromT.Contains(p.Name)); + + public string AutoResolveConstructorArguments => string.Join(", ", + Parts.Where(p => !ResolvableFromT.Contains(p.Name)).Select(p => $"{p.HighLevelTypeName} {p.NameAsArgument}")); + + public string AutoResolveBaseArguments(string generic) => + string.Join(", ", Parts.Select(p => !ResolvableFromT.Contains(p.Name) ? p.Name : $"typeof({generic})")); + + public string DocumentPathBaseArgument(string generic) => string.Join(", ", + _additionalPartsForConstructor.Select(p => p.Name == "id" ? "id ?? OpenSearch.Client.Id.From(documentWithId)" + : ResolvableFromT.Contains(p.Name) ? $"{p.Name} ?? typeof({generic})" : p.Name)); + + public string DocumentPathConstructorArgument(string generic) => string.Join(", ", + new[] { $"{generic} documentWithId" }.Concat(_additionalPartsForConstructor.Select(p => $"{p.HighLevelTypeName} {p.NameAsArgument} = null"))); + + public string GetXmlDocs(string indent, bool skipResolvable = false, bool documentConstructor = false) + { + var doc = $@"///{Path}"; + var parts = Parts.Where(p => !skipResolvable || !ResolvableFromT.Contains(p.Name)).ToList(); + if (!parts.Any()) return doc; + + doc += indent; + doc += string.Join(indent, parts.Select(p => P(p.NameAsArgument, documentConstructor + ? "The document used to resolve the path from" + : p.Required + ? "this parameter is required" : "Optional, accepts null"))); + return doc; + } + + private static string P(string name, string description) => $"///{description}"; + + private static string LeadingBackslash(string p) => p.StartsWith("/") ? p : $"/{p}"; } diff --git a/src/ApiGenerator/Extensions.cs b/src/ApiGenerator/Extensions.cs index c636d5cf72..708039834f 100644 --- a/src/ApiGenerator/Extensions.cs +++ b/src/ApiGenerator/Extensions.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -34,41 +34,41 @@ namespace ApiGenerator; 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(@"(?!^_(?!All$))[_\.]"); + /// + /// 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(@"(?!^_(?!All$))[_\.]"); - public static string ToPascalCase(this string s, bool removeLeadingUnderscore = false) - { - if (string.IsNullOrEmpty(s)) return s; + public static string ToPascalCase(this string s, bool removeLeadingUnderscore = false) + { + if (string.IsNullOrEmpty(s)) return s; - var textInfo = new CultureInfo("en-US").TextInfo; - var titleCased = textInfo.ToTitleCase(s.ToLowerInvariant()); - var result = RemovePunctuationExceptFirstUnderScore.Replace(titleCased, ""); - if (removeLeadingUnderscore) - result = result.TrimStart('_'); - return result; - } + var textInfo = new CultureInfo("en-US").TextInfo; + var titleCased = textInfo.ToTitleCase(s.ToLowerInvariant()); + var result = RemovePunctuationExceptFirstUnderScore.Replace(titleCased, ""); + if (removeLeadingUnderscore) + result = result.TrimStart('_'); + return result; + } - public static string ToCamelCase(this string s) - { - if (string.IsNullOrEmpty(s)) return s; + public static string ToCamelCase(this string s) + { + if (string.IsNullOrEmpty(s)) return s; - var pascal = s.ToPascalCase(true); - if (pascal.Length <= 1) return pascal; + var pascal = s.ToPascalCase(true); + if (pascal.Length <= 1) return pascal; - return char.ToLower(pascal[0]) + pascal[1..]; - } + return char.ToLower(pascal[0]) + pascal[1..]; + } - public static string SplitPascalCase(this string s) => - Regex.Replace(s, "([A-Z]+[a-z]*)", " $1").Trim(); + public static string SplitPascalCase(this string s) => + Regex.Replace(s, "([A-Z]+[a-z]*)", " $1").Trim(); - public static TDictionary OverrideWith(this TDictionary original, IDictionary overrides) - where TDictionary: IDictionary - { - foreach (var (key, value) in overrides) - original[key] = value; - return original; - } + public static TDictionary OverrideWith(this TDictionary original, IDictionary overrides) + where TDictionary: IDictionary + { + foreach (var (key, value) in overrides) + original[key] = value; + return original; + } } diff --git a/src/ApiGenerator/Generator/ApiEndpointFactory.cs b/src/ApiGenerator/Generator/ApiEndpointFactory.cs index 168f6737a3..1b2abab45c 100644 --- a/src/ApiGenerator/Generator/ApiEndpointFactory.cs +++ b/src/ApiGenerator/Generator/ApiEndpointFactory.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -42,122 +42,122 @@ 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; - - var urlInfo = new UrlInformation(); - HashSet requiredPathParams = null; - var allPathParams = new List(); - - foreach (var (httpPath, path, _, operation) in variants.DistinctBy(v => v.HttpPath)) - { - urlInfo.OriginalPaths.Add(httpPath); - var pathParams = path.Parameters - .Concat(operation.Parameters) - .Where(p => p.Kind == OpenApiParameterKind.Path) - .ToList(); - var paramNames = pathParams.Select(p => p.Name); - if (requiredPathParams != null) - requiredPathParams.IntersectWith(paramNames); - else - requiredPathParams = new HashSet(paramNames); - allPathParams.AddRange(pathParams); - } - - urlInfo.OriginalParts = allPathParams.DistinctBy(p => p.Name) - .Select(p => new UrlPart - { - ClrTypeNameOverride = null, - Deprecated = p.IsDeprecated, - Description = p.Description, - Name = p.Name, - Required = requiredPathParams?.Contains(p.Name) ?? false, - Type = GetOpenSearchType(p.Schema), - Options = GetEnumOptions(p.Schema), - }) - .ToImmutableSortedDictionary(p => p.Name, p => p); - - urlInfo.Params = variants.SelectMany(v => v.Path.Parameters.Concat(v.Operation.Parameters)) - .Where(p => p.Kind == OpenApiParameterKind.Query) - .DistinctBy(p => p.Name) - .ToImmutableSortedDictionary(p => p.Name, - p => new QueryParameters - { - Type = GetOpenSearchType(p.Schema), - Description = p.Description, - Options = GetEnumOptions(p.Schema), - Deprecated = p.IsDeprecated ? new QueryParameterDeprecation { Description = p.DeprecatedMessage } : null - }); - - var endpoint = new ApiEndpoint - { - Name = name, - Namespace = ns, - MethodName = methodName, - CsharpNames = new CsharpNames(name, methodName, ns), - Stability = Stability.Stable, // TODO: for realsies - OfficialDocumentationLink = new Documentation - { - Description = variants[0].Operation.Description, - Url = variants[0].Operation.ExternalDocumentation?.Url, - }, - Url = urlInfo, - Body = variants.Select(v => v.Operation.RequestBody).FirstOrDefault(b => b != null) is {} reqBody ? new Body - { - Description = reqBody.Description, - Required = reqBody.IsRequired - } : null, - HttpMethods = variants.Select(v => v.HttpMethod.ToString().ToUpper()).Distinct().ToList(), - }; - - LoadOverridesOnEndpoint(endpoint); - PatchRequestParameters(endpoint); - - return endpoint; - } - - private static void LoadOverridesOnEndpoint(ApiEndpoint endpoint) - { - var method = endpoint.CsharpNames.MethodName; - if (CodeConfiguration.ApiNameMapping.TryGetValue(endpoint.Name, out var mapsApiMethodName)) - method = mapsApiMethodName; - - var namespacePrefix = $"{typeof(GlobalOverrides).Namespace}.Endpoints."; - var typeName = $"{namespacePrefix}{method}Overrides"; - var type = GeneratorLocations.Assembly.GetType(typeName); - if (type != null && Activator.CreateInstance(type) is IEndpointOverrides overrides) - endpoint.Overrides = overrides; - } - - private static void PatchRequestParameters(ApiEndpoint endpoint) => - endpoint.Url.Params = ApiQueryParametersPatcher.Patch(endpoint.Name, endpoint.Url.Params, endpoint.Overrides) - ?? throw new ArgumentNullException("ApiQueryParametersPatcher.Patch(endpoint.Name, endpoint.Url.Params, endpoint.Overrides)"); - - private static string GetOpenSearchType(JsonSchema schema) - { - while (schema.HasReference) schema = schema.Reference; - - return schema.Type switch - { - JsonObjectType.String when schema.Enumeration is { Count: > 0 } => "enum", - JsonObjectType.String when schema.Pattern is "^([0-9]+)(?:d|h|m|s|ms|micros|nanos)$" => "time", - JsonObjectType.String when schema.GetExtension("x-comma-separated-list") is "true" => "list", - JsonObjectType.Integer => "number", - JsonObjectType.Array => "list", - var t => t.ToString().ToLowerInvariant() - }; - } - - private static IEnumerable GetEnumOptions(JsonSchema schema) - { - while (schema.HasReference) schema = schema.Reference; - - return schema.Enumeration?.Select(e => e.ToString()) ?? Enumerable.Empty(); - } - - private static object GetExtension(this IJsonExtensionObject schema, string key) => - schema.ExtensionData?.TryGetValue(key, out var value) ?? false ? value : null; + 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; + + var urlInfo = new UrlInformation(); + HashSet requiredPathParams = null; + var allPathParams = new List(); + + foreach (var (httpPath, path, _, operation) in variants.DistinctBy(v => v.HttpPath)) + { + urlInfo.OriginalPaths.Add(httpPath); + var pathParams = path.Parameters + .Concat(operation.Parameters) + .Where(p => p.Kind == OpenApiParameterKind.Path) + .ToList(); + var paramNames = pathParams.Select(p => p.Name); + if (requiredPathParams != null) + requiredPathParams.IntersectWith(paramNames); + else + requiredPathParams = new HashSet(paramNames); + allPathParams.AddRange(pathParams); + } + + urlInfo.OriginalParts = allPathParams.DistinctBy(p => p.Name) + .Select(p => new UrlPart + { + ClrTypeNameOverride = null, + Deprecated = p.IsDeprecated, + Description = p.Description, + Name = p.Name, + Required = requiredPathParams?.Contains(p.Name) ?? false, + Type = GetOpenSearchType(p.Schema), + Options = GetEnumOptions(p.Schema), + }) + .ToImmutableSortedDictionary(p => p.Name, p => p); + + urlInfo.Params = variants.SelectMany(v => v.Path.Parameters.Concat(v.Operation.Parameters)) + .Where(p => p.Kind == OpenApiParameterKind.Query) + .DistinctBy(p => p.Name) + .ToImmutableSortedDictionary(p => p.Name, + p => new QueryParameters + { + Type = GetOpenSearchType(p.Schema), + Description = p.Description, + Options = GetEnumOptions(p.Schema), + Deprecated = p.IsDeprecated ? new QueryParameterDeprecation { Description = p.DeprecatedMessage } : null + }); + + var endpoint = new ApiEndpoint + { + Name = name, + Namespace = ns, + MethodName = methodName, + CsharpNames = new CsharpNames(name, methodName, ns), + Stability = Stability.Stable, // TODO: for realsies + OfficialDocumentationLink = new Documentation + { + Description = variants[0].Operation.Description, + Url = variants[0].Operation.ExternalDocumentation?.Url, + }, + Url = urlInfo, + Body = variants.Select(v => v.Operation.RequestBody).FirstOrDefault(b => b != null) is {} reqBody ? new Body + { + Description = reqBody.Description, + Required = reqBody.IsRequired + } : null, + HttpMethods = variants.Select(v => v.HttpMethod.ToString().ToUpper()).Distinct().ToList(), + }; + + LoadOverridesOnEndpoint(endpoint); + PatchRequestParameters(endpoint); + + return endpoint; + } + + private static void LoadOverridesOnEndpoint(ApiEndpoint endpoint) + { + var method = endpoint.CsharpNames.MethodName; + if (CodeConfiguration.ApiNameMapping.TryGetValue(endpoint.Name, out var mapsApiMethodName)) + method = mapsApiMethodName; + + var namespacePrefix = $"{typeof(GlobalOverrides).Namespace}.Endpoints."; + var typeName = $"{namespacePrefix}{method}Overrides"; + var type = GeneratorLocations.Assembly.GetType(typeName); + if (type != null && Activator.CreateInstance(type) is IEndpointOverrides overrides) + endpoint.Overrides = overrides; + } + + private static void PatchRequestParameters(ApiEndpoint endpoint) => + endpoint.Url.Params = ApiQueryParametersPatcher.Patch(endpoint.Name, endpoint.Url.Params, endpoint.Overrides) + ?? throw new ArgumentNullException("ApiQueryParametersPatcher.Patch(endpoint.Name, endpoint.Url.Params, endpoint.Overrides)"); + + private static string GetOpenSearchType(JsonSchema schema) + { + while (schema.HasReference) schema = schema.Reference; + + return schema.Type switch + { + JsonObjectType.String when schema.Enumeration is { Count: > 0 } => "enum", + JsonObjectType.String when schema.Pattern is "^([0-9]+)(?:d|h|m|s|ms|micros|nanos)$" => "time", + JsonObjectType.String when schema.GetExtension("x-comma-separated-list") is "true" => "list", + JsonObjectType.Integer => "number", + JsonObjectType.Array => "list", + var t => t.ToString().ToLowerInvariant() + }; + } + + private static IEnumerable GetEnumOptions(JsonSchema schema) + { + while (schema.HasReference) schema = schema.Reference; + + return schema.Enumeration?.Select(e => e.ToString()) ?? Enumerable.Empty(); + } + + 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 5e79c1cfd5..e23832b7af 100644 --- a/src/ApiGenerator/Generator/ApiGenerator.cs +++ b/src/ApiGenerator/Generator/ApiGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -41,58 +41,58 @@ namespace ApiGenerator.Generator; public class ApiGenerator { - public static List Warnings { get; } = new(); + public static List Warnings { get; } = new(); - public static async Task Generate(bool lowLevelOnly, RestApiSpec spec, CancellationToken token) - { - async Task DoGenerate(bool highLevel, params RazorGeneratorBase[] generators) - { - using var pbar = new ProgressBar( - generators.Length, - $"Generating {(highLevel ? "high" : "low")} level code", - new ProgressBarOptions { ProgressCharacter = '─', BackgroundColor = ConsoleColor.Yellow } - ); - foreach (var generator in generators) - { - pbar.Message = $"Generating {generator.Title}"; - await generator.Generate(spec, pbar, token); - pbar.Tick($"Generated {generator.Title}"); - } - } + public static async Task Generate(bool lowLevelOnly, RestApiSpec spec, CancellationToken token) + { + async Task DoGenerate(bool highLevel, params RazorGeneratorBase[] generators) + { + using var pbar = new ProgressBar( + generators.Length, + $"Generating {(highLevel ? "high" : "low")} level code", + new ProgressBarOptions { ProgressCharacter = '─', BackgroundColor = ConsoleColor.Yellow } + ); + foreach (var generator in generators) + { + pbar.Message = $"Generating {generator.Title}"; + await generator.Generate(spec, pbar, token); + pbar.Tick($"Generated {generator.Title}"); + } + } - // low level client - await DoGenerate( - false, - new LowLevelClientInterfaceGenerator(), - new LowLevelClientImplementationGenerator(), - new RequestParametersGenerator(), - new EnumsGenerator(), - new ApiUrlsLookupsGenerator()); + // low level client + await DoGenerate( + false, + new LowLevelClientInterfaceGenerator(), + new LowLevelClientImplementationGenerator(), + new RequestParametersGenerator(), + new EnumsGenerator(), + new ApiUrlsLookupsGenerator()); - if (lowLevelOnly) return; + if (lowLevelOnly) return; - // high level client - await DoGenerate( - true, - new HighLevelClientInterfaceGenerator(), - new HighLevelClientImplementationGenerator(), - new DescriptorsGenerator(), - new RequestsGenerator()); - } + // high level client + await DoGenerate( + true, + new HighLevelClientInterfaceGenerator(), + new HighLevelClientImplementationGenerator(), + new DescriptorsGenerator(), + new RequestsGenerator()); + } - public static async Task CreateRestApiSpecModel(string file, ISet namespaces) - { - var document = await OpenApiYamlDocument.FromFileAsync(file); + public static async Task CreateRestApiSpecModel(string file, ISet namespaces) + { + var document = await OpenApiYamlDocument.FromFileAsync(file); - 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()) - .Select(o => ApiEndpointFactory.From(o.Key, o.Select(i => (i.HttpPath, i.PathItem, i.HttpMethod, i.Operation)).ToList())) - .Where(e => namespaces.Contains(e.Namespace)) - .ToImmutableSortedDictionary(e => e.Name, e => e); + 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()) + .Select(o => ApiEndpointFactory.From(o.Key, o.Select(i => (i.HttpPath, i.PathItem, i.HttpMethod, i.Operation)).ToList())) + .Where(e => namespaces.Contains(e.Namespace)) + .ToImmutableSortedDictionary(e => e.Name, e => e); - return new RestApiSpec { Endpoints = endpoints }; - } + return new RestApiSpec { Endpoints = endpoints }; + } } diff --git a/src/ApiGenerator/Generator/CodeGenerator.cs b/src/ApiGenerator/Generator/CodeGenerator.cs index be3a1a23bd..a379699b89 100644 --- a/src/ApiGenerator/Generator/CodeGenerator.cs +++ b/src/ApiGenerator/Generator/CodeGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,66 +36,66 @@ namespace ApiGenerator.Generator; //TODO this should be in views and models public static class CodeGenerator { - public static string Property(string @namespace, string type, string name, string key, string setter, string obsolete, params string[] doc) - { - var components = RenderDocumentation(doc).ToList(); - if (!string.IsNullOrWhiteSpace(obsolete)) components.Add($"[Obsolete(\"Scheduled to be removed in 8.0, {obsolete}\")]"); + public static string Property(string @namespace, string type, string name, string key, string setter, string obsolete, params string[] doc) + { + var components = RenderDocumentation(doc).ToList(); + if (!string.IsNullOrWhiteSpace(obsolete)) components.Add($"[Obsolete(\"Scheduled to be removed in 8.0, {obsolete}\")]"); - var generated = @namespace is "Cat" && name == "Format" - ? $"public {type} {name} {{ get => Q<{type}>(\"{key}\"); set {{ Q(\"{key}\", {setter}); SetAcceptHeader({setter}); }}}}" - : $"public {type} {name} {{ get => Q<{type}>(\"{key}\"); set => Q(\"{key}\", {setter}); }}"; + var generated = @namespace is "Cat" && name == "Format" + ? $"public {type} {name} {{ get => Q<{type}>(\"{key}\"); set {{ Q(\"{key}\", {setter}); SetAcceptHeader({setter}); }}}}" + : $"public {type} {name} {{ get => Q<{type}>(\"{key}\"); set => Q(\"{key}\", {setter}); }}"; - components.Add(generated); - return string.Join($"{Environment.NewLine}\t\t", components); - } + components.Add(generated); + return string.Join($"{Environment.NewLine}\t\t", components); + } - public static string Constructor(Constructor c) - { - var components = new List(); - if (!string.IsNullOrEmpty(c.Description)) components.Add(c.Description); - var generated = c.Generated; - if (string.IsNullOrEmpty(c.Body)) generated += "{}"; - components.Add(generated); - if (!string.IsNullOrEmpty(c.Body)) components.Add(c.Body); - if (!string.IsNullOrEmpty(c.AdditionalCode)) components.Add(c.AdditionalCode); - return string.Join($"{Environment.NewLine}\t\t", components); - } + public static string Constructor(Constructor c) + { + var components = new List(); + if (!string.IsNullOrEmpty(c.Description)) components.Add(c.Description); + var generated = c.Generated; + if (string.IsNullOrEmpty(c.Body)) generated += "{}"; + components.Add(generated); + if (!string.IsNullOrEmpty(c.Body)) components.Add(c.Body); + if (!string.IsNullOrEmpty(c.AdditionalCode)) components.Add(c.AdditionalCode); + return string.Join($"{Environment.NewLine}\t\t", components); + } - private static IEnumerable RenderDocumentation(params string[] doc) - { - doc = (doc?.SelectMany(WrapDocumentation) ?? Enumerable.Empty()).ToArray(); - switch (doc.Length) - { - case 0: yield break; - case 1: - yield return $"///{doc[0]}"; + private static IEnumerable RenderDocumentation(params string[] doc) + { + doc = (doc?.SelectMany(WrapDocumentation) ?? Enumerable.Empty()).ToArray(); + switch (doc.Length) + { + case 0: yield break; + case 1: + yield return $"///{doc[0]}"; - yield break; - default: - yield return "///"; + yield break; + default: + yield return "///"; - foreach (var d in doc) yield return $"/// {d}"; + foreach (var d in doc) yield return $"/// {d}"; - yield return "///"; + yield return "///"; - yield break; - } - } + yield break; + } + } - private static string[] WrapDocumentation(string documentation) - { - if (string.IsNullOrWhiteSpace(documentation)) return Array.Empty(); + private static string[] WrapDocumentation(string documentation) + { + if (string.IsNullOrWhiteSpace(documentation)) return Array.Empty(); - const int max = 140; - var charCount = 0; - return documentation - .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) - .GroupBy(w => - { - var increase = charCount % max + w.Length + 1 >= max ? max - charCount % max : 0; - return (charCount += increase + w.Length + 1) / max; - }) - .Select(l => string.Join(" ", l)) - .ToArray(); - } + const int max = 140; + var charCount = 0; + return documentation + .Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) + .GroupBy(w => + { + var increase = charCount % max + w.Length + 1 >= max ? max - charCount % max : 0; + return (charCount += increase + w.Length + 1) / max; + }) + .Select(l => string.Join(" ", l)) + .ToArray(); + } } diff --git a/src/ApiGenerator/Generator/Razor/ApiUrlsLookupsGenerator.cs b/src/ApiGenerator/Generator/Razor/ApiUrlsLookupsGenerator.cs index 5b245172d7..cb749f20eb 100644 --- a/src/ApiGenerator/Generator/Razor/ApiUrlsLookupsGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/ApiUrlsLookupsGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,12 +36,12 @@ namespace ApiGenerator.Generator.Razor; public class ApiUrlsLookupsGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Client static url lookups"; + public override string Title => "OpenSearch.Client static url lookups"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => - await DoRazor( - spec, - ViewLocations.HighLevel("Requests", "ApiUrlsLookup"), - GeneratorLocations.HighLevel("ApiUrlsLookup.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => + await DoRazor( + spec, + ViewLocations.HighLevel("Requests", "ApiUrlsLookup"), + GeneratorLocations.HighLevel("ApiUrlsLookup.Generated.cs"), + token); } diff --git a/src/ApiGenerator/Generator/Razor/DescriptorsGenerator.cs b/src/ApiGenerator/Generator/Razor/DescriptorsGenerator.cs index 0a4346bf77..ab4b63355e 100644 --- a/src/ApiGenerator/Generator/Razor/DescriptorsGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/DescriptorsGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -37,14 +37,14 @@ namespace ApiGenerator.Generator.Razor; public class DescriptorsGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Client descriptors"; + public override string Title => "OpenSearch.Client descriptors"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => - await DoRazorDependantFiles( - progressBar, - spec.EndpointsPerNamespaceHighLevel.ToList(), - ViewLocations.HighLevel("Descriptors", "Descriptors"), - kv => kv.Key, - id => GeneratorLocations.HighLevel($"Descriptors.{id}.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => + await DoRazorDependantFiles( + progressBar, + spec.EndpointsPerNamespaceHighLevel.ToList(), + ViewLocations.HighLevel("Descriptors", "Descriptors"), + kv => kv.Key, + id => GeneratorLocations.HighLevel($"Descriptors.{id}.Generated.cs"), + token); } diff --git a/src/ApiGenerator/Generator/Razor/EnumsGenerator.cs b/src/ApiGenerator/Generator/Razor/EnumsGenerator.cs index 741f886872..21949b4ec8 100644 --- a/src/ApiGenerator/Generator/Razor/EnumsGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/EnumsGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,12 +36,12 @@ namespace ApiGenerator.Generator.Razor; public class EnumsGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Net enums"; + public override string Title => "OpenSearch.Net enums"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => - await DoRazor( - spec, - ViewLocations.LowLevel("Enums.Generated"), - GeneratorLocations.LowLevel("Api", "Enums.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => + await DoRazor( + spec, + ViewLocations.LowLevel("Enums.Generated"), + GeneratorLocations.LowLevel("Api", "Enums.Generated.cs"), + token); } diff --git a/src/ApiGenerator/Generator/Razor/HighLevelClientImplementationGenerator.cs b/src/ApiGenerator/Generator/Razor/HighLevelClientImplementationGenerator.cs index 93caaeafcb..24b64fc5a4 100644 --- a/src/ApiGenerator/Generator/Razor/HighLevelClientImplementationGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/HighLevelClientImplementationGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -38,22 +38,22 @@ namespace ApiGenerator.Generator.Razor; public class HighLevelClientImplementationGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Client client implementation"; + public override string Title => "OpenSearch.Client client implementation"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) - { - await DoRazor( - spec, - ViewLocations.HighLevel("Client", "Implementation", "OpenSearchClient"), - GeneratorLocations.HighLevel($"OpenSearchClient.{CsharpNames.RootNamespace}.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) + { + await DoRazor( + spec, + ViewLocations.HighLevel("Client", "Implementation", "OpenSearchClient"), + GeneratorLocations.HighLevel($"OpenSearchClient.{CsharpNames.RootNamespace}.Generated.cs"), + token); - await DoRazorDependantFiles( - progressBar, - spec.EndpointsPerNamespaceHighLevel.Where(kv => kv.Key != CsharpNames.RootNamespace).ToList(), - ViewLocations.HighLevel("Client", "Implementation", "OpenSearchClient.Namespace"), - kv => kv.Key, - id => GeneratorLocations.HighLevel($"OpenSearchClient.{id}.Generated.cs"), - token); - } + await DoRazorDependantFiles( + progressBar, + spec.EndpointsPerNamespaceHighLevel.Where(kv => kv.Key != CsharpNames.RootNamespace).ToList(), + ViewLocations.HighLevel("Client", "Implementation", "OpenSearchClient.Namespace"), + kv => kv.Key, + id => GeneratorLocations.HighLevel($"OpenSearchClient.{id}.Generated.cs"), + token); + } } diff --git a/src/ApiGenerator/Generator/Razor/HighLevelClientInterfaceGenerator.cs b/src/ApiGenerator/Generator/Razor/HighLevelClientInterfaceGenerator.cs index 0583e68bc1..fe8a37fcc5 100644 --- a/src/ApiGenerator/Generator/Razor/HighLevelClientInterfaceGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/HighLevelClientInterfaceGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,12 +36,12 @@ namespace ApiGenerator.Generator.Razor; public class HighLevelClientInterfaceGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Client client interface"; + public override string Title => "OpenSearch.Client client interface"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => - await DoRazor( - spec, - ViewLocations.HighLevel("Client", "Interface", "IOpenSearchClient"), - GeneratorLocations.HighLevel("IOpenSearchClient.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => + await DoRazor( + spec, + ViewLocations.HighLevel("Client", "Interface", "IOpenSearchClient"), + GeneratorLocations.HighLevel("IOpenSearchClient.Generated.cs"), + token); } diff --git a/src/ApiGenerator/Generator/Razor/LowLevelClientImplementationGenerator.cs b/src/ApiGenerator/Generator/Razor/LowLevelClientImplementationGenerator.cs index 9812663bbb..44c253e07a 100644 --- a/src/ApiGenerator/Generator/Razor/LowLevelClientImplementationGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/LowLevelClientImplementationGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -38,22 +38,22 @@ namespace ApiGenerator.Generator.Razor; public class LowLevelClientImplementationGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Net client implementation"; + public override string Title => "OpenSearch.Net client implementation"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) - { - await DoRazor( - spec, - ViewLocations.LowLevel("Client", "Implementation", "OpenSearchLowLevelClient"), - GeneratorLocations.LowLevel($"OpenSearchLowLevelClient.{CsharpNames.RootNamespace}.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) + { + await DoRazor( + spec, + ViewLocations.LowLevel("Client", "Implementation", "OpenSearchLowLevelClient"), + GeneratorLocations.LowLevel($"OpenSearchLowLevelClient.{CsharpNames.RootNamespace}.Generated.cs"), + token); - await DoRazorDependantFiles( - progressBar, - spec.EndpointsPerNamespaceLowLevel.Where(kv => kv.Key != CsharpNames.RootNamespace).ToList(), - ViewLocations.LowLevel("Client", "Implementation", "OpenSearchLowLevelClient.Namespace"), - kv => kv.Key, - id => GeneratorLocations.LowLevel($"OpenSearchLowLevelClient.{id}.Generated.cs"), - token); - } + await DoRazorDependantFiles( + progressBar, + spec.EndpointsPerNamespaceLowLevel.Where(kv => kv.Key != CsharpNames.RootNamespace).ToList(), + ViewLocations.LowLevel("Client", "Implementation", "OpenSearchLowLevelClient.Namespace"), + kv => kv.Key, + id => GeneratorLocations.LowLevel($"OpenSearchLowLevelClient.{id}.Generated.cs"), + token); + } } diff --git a/src/ApiGenerator/Generator/Razor/LowLevelClientInterfaceGenerator.cs b/src/ApiGenerator/Generator/Razor/LowLevelClientInterfaceGenerator.cs index 2d66691edc..9166d8a141 100644 --- a/src/ApiGenerator/Generator/Razor/LowLevelClientInterfaceGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/LowLevelClientInterfaceGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -36,12 +36,12 @@ namespace ApiGenerator.Generator.Razor; public class LowLevelClientInterfaceGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Net client interface"; + public override string Title => "OpenSearch.Net client interface"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => - await DoRazor( - spec, - ViewLocations.LowLevel("Client", "Interface", "IOpenSearchLowLevelClient"), - GeneratorLocations.LowLevel("IOpenSearchLowLevelClient.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => + await DoRazor( + spec, + ViewLocations.LowLevel("Client", "Interface", "IOpenSearchLowLevelClient"), + GeneratorLocations.LowLevel("IOpenSearchLowLevelClient.Generated.cs"), + token); } diff --git a/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs b/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs index f381831645..f04aa13a78 100644 --- a/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs +++ b/src/ApiGenerator/Generator/Razor/RazorGeneratorBase.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -42,55 +42,55 @@ namespace ApiGenerator.Generator.Razor; public abstract class RazorGeneratorBase { - private static readonly RazorLightEngine Engine = new RazorLightEngineBuilder() - .UseEmbeddedResourcesProject(typeof(RazorGeneratorBase).Assembly, "ApiGenerator.Views") - .SetOperatingAssembly(typeof(RazorGeneratorBase).Assembly) - .UseMemoryCachingProvider() - .EnableDebugMode() - .Build(); + private static readonly RazorLightEngine Engine = new RazorLightEngineBuilder() + .UseEmbeddedResourcesProject(typeof(RazorGeneratorBase).Assembly, "ApiGenerator.Views") + .SetOperatingAssembly(typeof(RazorGeneratorBase).Assembly) + .UseMemoryCachingProvider() + .EnableDebugMode() + .Build(); - protected static async Task DoRazor(TModel model, string viewLocation, string targetLocation, CancellationToken token) - { - try - { - token.ThrowIfCancellationRequested(); - var generated = await Engine.CompileRenderAsync(viewLocation, model); - await WriteFormattedCsharpFile(targetLocation, generated, token); - } - catch (TemplateGenerationException e) - { - foreach (var d in e.Diagnostics) Console.WriteLine(d.GetMessage()); - throw; - } - } + protected static async Task DoRazor(TModel model, string viewLocation, string targetLocation, CancellationToken token) + { + try + { + token.ThrowIfCancellationRequested(); + var generated = await Engine.CompileRenderAsync(viewLocation, model); + await WriteFormattedCsharpFile(targetLocation, generated, token); + } + catch (TemplateGenerationException e) + { + foreach (var d in e.Diagnostics) Console.WriteLine(d.GetMessage()); + throw; + } + } - protected async Task DoRazorDependantFiles( - ProgressBar pbar, IReadOnlyCollection items, string viewLocation, - Func identifier, Func target, - CancellationToken token - ) - { - using var c = pbar.Spawn( - items.Count, - "Generating namespaces", - new ProgressBarOptions { ProgressCharacter = '─', ForegroundColor = ConsoleColor.Yellow } - ); + protected async Task DoRazorDependantFiles( + ProgressBar pbar, IReadOnlyCollection items, string viewLocation, + Func identifier, Func target, + CancellationToken token + ) + { + using var c = pbar.Spawn( + items.Count, + "Generating namespaces", + new ProgressBarOptions { ProgressCharacter = '─', ForegroundColor = ConsoleColor.Yellow } + ); - foreach (var item in items) - { - var id = identifier(item); - await DoRazor(item, viewLocation, target(id), token); - c.Tick($"{Title}: {id}"); - } - } + foreach (var item in items) + { + var id = identifier(item); + await DoRazor(item, viewLocation, target(id), token); + c.Tick($"{Title}: {id}"); + } + } - private static async Task WriteFormattedCsharpFile(string path, string contents, CancellationToken token) - { - var root = await CSharpSyntaxTree.ParseText(contents, cancellationToken: token).GetRootAsync(token); - await File.WriteAllTextAsync(path, root.NormalizeWhitespace("\t", "\n").ToFullString(), token); - } + private static async Task WriteFormattedCsharpFile(string path, string contents, CancellationToken token) + { + var root = await CSharpSyntaxTree.ParseText(contents, cancellationToken: token).GetRootAsync(token); + await File.WriteAllTextAsync(path, root.NormalizeWhitespace("\t", "\n").ToFullString(), token); + } - public abstract string Title { get; } + public abstract string Title { get; } - public abstract Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token); + public abstract Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token); } diff --git a/src/ApiGenerator/Generator/Razor/RequestParametersGenerator.cs b/src/ApiGenerator/Generator/Razor/RequestParametersGenerator.cs index a1dc0f3e5c..01da3d3ee2 100644 --- a/src/ApiGenerator/Generator/Razor/RequestParametersGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/RequestParametersGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -37,14 +37,14 @@ namespace ApiGenerator.Generator.Razor; public class RequestParametersGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Net request parameters"; + public override string Title => "OpenSearch.Net request parameters"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => - await DoRazorDependantFiles( - progressBar, - spec.EndpointsPerNamespaceLowLevel.ToList(), - ViewLocations.LowLevel("RequestParameters", "RequestParameters"), - kv => kv.Key, - id => GeneratorLocations.LowLevel("Api", "RequestParameters", $"RequestParameters.{id}.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => + await DoRazorDependantFiles( + progressBar, + spec.EndpointsPerNamespaceLowLevel.ToList(), + ViewLocations.LowLevel("RequestParameters", "RequestParameters"), + kv => kv.Key, + id => GeneratorLocations.LowLevel("Api", "RequestParameters", $"RequestParameters.{id}.Generated.cs"), + token); } diff --git a/src/ApiGenerator/Generator/Razor/RequestsGenerator.cs b/src/ApiGenerator/Generator/Razor/RequestsGenerator.cs index 5e4409d6d2..6580768d21 100644 --- a/src/ApiGenerator/Generator/Razor/RequestsGenerator.cs +++ b/src/ApiGenerator/Generator/Razor/RequestsGenerator.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -37,14 +37,14 @@ namespace ApiGenerator.Generator.Razor; public class RequestsGenerator : RazorGeneratorBase { - public override string Title => "OpenSearch.Client requests"; + public override string Title => "OpenSearch.Client requests"; - public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => - await DoRazorDependantFiles( - progressBar, - spec.EndpointsPerNamespaceHighLevel.ToList(), - ViewLocations.HighLevel("Requests", "Requests"), - kv => kv.Key, - id => GeneratorLocations.HighLevel($"Requests.{id}.Generated.cs"), - token); + public override async Task Generate(RestApiSpec spec, ProgressBar progressBar, CancellationToken token) => + await DoRazorDependantFiles( + progressBar, + spec.EndpointsPerNamespaceHighLevel.ToList(), + ViewLocations.HighLevel("Requests", "Requests"), + kv => kv.Key, + id => GeneratorLocations.HighLevel($"Requests.{id}.Generated.cs"), + token); } diff --git a/src/ApiGenerator/Program.cs b/src/ApiGenerator/Program.cs index 8e71194f53..6471f222d0 100644 --- a/src/ApiGenerator/Program.cs +++ b/src/ApiGenerator/Program.cs @@ -16,7 +16,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -40,110 +40,110 @@ namespace ApiGenerator; public static class Program { - private static bool Interactive { get; set; } - - /// - /// A main function can also take which is hooked up to support termination (e.g CTRL+C) - /// - /// Run the generation interactively, this will ignore all flags - /// Whether to download the specs or use an already downloaded copy - /// Also generate the high level client (OpenSearch.Client) - /// Only download the specs, skip all code generation - /// Namespaces to generate - /// - /// - private static async Task Main( - bool interactive = false, bool download = false, bool includeHighLevel = false, bool skipGenerate = false, - string[] namespaces = null, CancellationToken token = default - ) - { - Interactive = interactive; - try - { - await Generate(download, includeHighLevel, skipGenerate, namespaces ?? Array.Empty(), token); - } - catch (OperationCanceledException) - { - AnsiConsole.WriteLine(); - AnsiConsole.Write(new Rule("[b white on orange4_1] Cancelled [/]").LeftJustified()); - AnsiConsole.WriteLine(); - return 1; - } - catch (Exception ex) - { - AnsiConsole.WriteLine(); - AnsiConsole.Write(new Rule("[b white on darkred] Exception [/]").LeftJustified()); - AnsiConsole.WriteLine(); - AnsiConsole.WriteException(ex); - return 1; - } - return 0; - } - - private static async Task Generate(bool download, bool includeHighLevel, bool skipGenerate, string[] namespaces, CancellationToken token = default) - { - var generateCode = Ask("Generate code from the specification files on disk?", !skipGenerate); - var lowLevelOnly = generateCode && Ask("Generate low level client only?", !includeHighLevel); - - static string YesNo(bool value) - { - return value ? "[bold green]Yes[/]" : "[grey]No[/]"; - } - - var grid = new Grid() - .AddColumn(new GridColumn().PadRight(4)) - .AddColumn() - .AddRow("[b]Download specification[/]", $"{YesNo(download)}") - .AddRow("[b]Generate code from specification[/]", $"{YesNo(generateCode)}") - .AddRow("[b]Include high level client[/]", $"{YesNo(!lowLevelOnly)}"); - - Console.WriteLine(); - AnsiConsole.Write( - new Panel(grid) - .Header(new PanelHeader("[b white on chartreuse4] OpenSearch .NET client API generator [/]", Justify.Left)) - ); - Console.WriteLine(); - - if (!generateCode) return; - - Console.WriteLine(); - AnsiConsole.Write(new Rule("[b white on chartreuse4] Loading specification [/]").LeftJustified()); - Console.WriteLine(); - - var spec = await Generator.ApiGenerator.CreateRestApiSpecModel("/Users/tsfarr/Development/opensearch-api-specification/build/smithyprojections/opensearch-api-specification/full/openapi/OpenSearch.openapi.json", namespaces.ToImmutableHashSet()); - - Console.WriteLine(); - AnsiConsole.Write(new Rule("[b white on chartreuse4] Generating code [/]").LeftJustified()); - Console.WriteLine(); - - await Generator.ApiGenerator.Generate(lowLevelOnly, spec, token); - - var warnings = Generator.ApiGenerator.Warnings; - if (warnings.Count > 0) - { - Console.WriteLine(); - AnsiConsole.Write(new Rule("[b black on yellow] Specification warnings [/]").LeftJustified()); - Console.WriteLine(); - - foreach (var warning in warnings.Distinct().OrderBy(w => w)) - AnsiConsole.MarkupLine(" {0} [yellow] {1} [/] ", Emoji.Known.Warning, warning); - } - } - - private static bool Ask(string question, bool defaultAnswer = true) - { - if (!Interactive) return defaultAnswer; - - var answer = "invalid"; - var defaultResponse = defaultAnswer ? "y" : "n"; - - while (answer != "y" && answer != "n" && answer != "") - { - Console.Write($"{question}[y/N] (default {defaultResponse}): "); - answer = Console.ReadLine()?.Trim().ToLowerInvariant(); - if (string.IsNullOrWhiteSpace(answer)) answer = defaultResponse; - defaultAnswer = answer == "y"; - } - return defaultAnswer; - } + private static bool Interactive { get; set; } + + /// + /// A main function can also take which is hooked up to support termination (e.g CTRL+C) + /// + /// Run the generation interactively, this will ignore all flags + /// Whether to download the specs or use an already downloaded copy + /// Also generate the high level client (OpenSearch.Client) + /// Only download the specs, skip all code generation + /// Namespaces to generate + /// + /// + private static async Task Main( + bool interactive = false, bool download = false, bool includeHighLevel = false, bool skipGenerate = false, + string[] namespaces = null, CancellationToken token = default + ) + { + Interactive = interactive; + try + { + await Generate(download, includeHighLevel, skipGenerate, namespaces ?? Array.Empty(), token); + } + catch (OperationCanceledException) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("[b white on orange4_1] Cancelled [/]").LeftJustified()); + AnsiConsole.WriteLine(); + return 1; + } + catch (Exception ex) + { + AnsiConsole.WriteLine(); + AnsiConsole.Write(new Rule("[b white on darkred] Exception [/]").LeftJustified()); + AnsiConsole.WriteLine(); + AnsiConsole.WriteException(ex); + return 1; + } + return 0; + } + + private static async Task Generate(bool download, bool includeHighLevel, bool skipGenerate, string[] namespaces, CancellationToken token = default) + { + var generateCode = Ask("Generate code from the specification files on disk?", !skipGenerate); + var lowLevelOnly = generateCode && Ask("Generate low level client only?", !includeHighLevel); + + static string YesNo(bool value) + { + return value ? "[bold green]Yes[/]" : "[grey]No[/]"; + } + + var grid = new Grid() + .AddColumn(new GridColumn().PadRight(4)) + .AddColumn() + .AddRow("[b]Download specification[/]", $"{YesNo(download)}") + .AddRow("[b]Generate code from specification[/]", $"{YesNo(generateCode)}") + .AddRow("[b]Include high level client[/]", $"{YesNo(!lowLevelOnly)}"); + + Console.WriteLine(); + AnsiConsole.Write( + new Panel(grid) + .Header(new PanelHeader("[b white on chartreuse4] OpenSearch .NET client API generator [/]", Justify.Left)) + ); + Console.WriteLine(); + + if (!generateCode) return; + + Console.WriteLine(); + AnsiConsole.Write(new Rule("[b white on chartreuse4] Loading specification [/]").LeftJustified()); + Console.WriteLine(); + + var spec = await Generator.ApiGenerator.CreateRestApiSpecModel("/Users/tsfarr/Development/opensearch-api-specification/build/smithyprojections/opensearch-api-specification/full/openapi/OpenSearch.openapi.json", namespaces.ToImmutableHashSet()); + + Console.WriteLine(); + AnsiConsole.Write(new Rule("[b white on chartreuse4] Generating code [/]").LeftJustified()); + Console.WriteLine(); + + await Generator.ApiGenerator.Generate(lowLevelOnly, spec, token); + + var warnings = Generator.ApiGenerator.Warnings; + if (warnings.Count > 0) + { + Console.WriteLine(); + AnsiConsole.Write(new Rule("[b black on yellow] Specification warnings [/]").LeftJustified()); + Console.WriteLine(); + + foreach (var warning in warnings.Distinct().OrderBy(w => w)) + AnsiConsole.MarkupLine(" {0} [yellow] {1} [/] ", Emoji.Known.Warning, warning); + } + } + + private static bool Ask(string question, bool defaultAnswer = true) + { + if (!Interactive) return defaultAnswer; + + var answer = "invalid"; + var defaultResponse = defaultAnswer ? "y" : "n"; + + while (answer != "y" && answer != "n" && answer != "") + { + Console.Write($"{question}[y/N] (default {defaultResponse}): "); + answer = Console.ReadLine()?.Trim().ToLowerInvariant(); + if (string.IsNullOrWhiteSpace(answer)) answer = defaultResponse; + defaultAnswer = answer == "y"; + } + return defaultAnswer; + } } diff --git a/src/ApiGenerator/Views/GeneratorNotice.cshtml b/src/ApiGenerator/Views/GeneratorNotice.cshtml index 51aee15377..fc624a8e9e 100644 --- a/src/ApiGenerator/Views/GeneratorNotice.cshtml +++ b/src/ApiGenerator/Views/GeneratorNotice.cshtml @@ -18,7 +18,7 @@ * not use this file except in compliance with the License. * You may obtain a copy of the License at * -* http://www.apache.org/licenses/LICENSE-2.0 +* http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an @@ -39,7 +39,7 @@ // Please do not edit these files manually // Run the following in the root of the repos: // -// *NIX : ./build.sh codegen -// Windows : build.bat codegen +// *NIX : ./build.sh codegen +// Windows : build.bat codegen // // ----------------------------------------------- diff --git a/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethod.cshtml b/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethod.cshtml index c0a7278dd3..b96c553850 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethod.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethod.cshtml @@ -1,14 +1,14 @@ @using ApiGenerator.Domain.Code.HighLevel.Methods @inherits ApiGenerator.CodeTemplatePage @{ - var syntax = Model.Syntax; - - var method = !Model.Async ? syntax.MethodName : $"{syntax.MethodName}Async"; - var asyncKeyword = Model.Syntax.InterfaceResponse && Model.Async ? "async " : string.Empty; - var awaitKeyWord = Model.Syntax.InterfaceResponse && Model.Async ? "await ": string.Empty; - var configureAwait = Model.Syntax.InterfaceResponse && Model.Async ? ".ConfigureAwait(false)" : string.Empty; - var cancellationToken = !Model.Async ? string.Empty : ", ct"; + var syntax = Model.Syntax; + + var method = !Model.Async ? syntax.MethodName : $"{syntax.MethodName}Async"; + var asyncKeyword = Model.Syntax.InterfaceResponse && Model.Async ? "async " : string.Empty; + var awaitKeyWord = Model.Syntax.InterfaceResponse && Model.Async ? "await ": string.Empty; + var configureAwait = Model.Syntax.InterfaceResponse && Model.Async ? ".ConfigureAwait(false)" : string.Empty; + var cancellationToken = !Model.Async ? string.Empty : ", ct"; } @{ await IncludeAsync("HighLevel.Client.MethodXmlDocs", syntax); } public @(asyncKeyword)@{ await IncludeAsync("HighLevel.Client.FluentSyntax.FluentMethodHeader", Model);} @Raw("=>") - @(awaitKeyWord)@(method)@(Raw(syntax.RequestMethodGenerics))(selector.InvokeOrDefault(new @(Raw(syntax.DescriptorName))(@Raw(syntax.SelectorArguments()))@syntax.SelectorChainedDefaults())@cancellationToken)@Raw(configureAwait); + @(awaitKeyWord)@(method)@(Raw(syntax.RequestMethodGenerics))(selector.InvokeOrDefault(new @(Raw(syntax.DescriptorName))(@Raw(syntax.SelectorArguments()))@syntax.SelectorChainedDefaults())@cancellationToken)@Raw(configureAwait); diff --git a/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethodHeader.cshtml b/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethodHeader.cshtml index c9d09f4b52..d5d4769c09 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethodHeader.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/FluentSyntax/FluentMethodHeader.cshtml @@ -1,10 +1,10 @@ @using ApiGenerator.Domain.Code.HighLevel.Methods @inherits ApiGenerator.CodeTemplatePage @{ - var syntax = Model.Syntax; + var syntax = Model.Syntax; - var response = !Model.Async ? syntax.ResponseName : $"Task<{syntax.ResponseName}>"; - var method = !Model.Async ? syntax.MethodName : $"{syntax.MethodName}Async"; - var cancellationToken = !Model.Async ? string.Empty : ", CancellationToken ct = default"; + var response = !Model.Async ? syntax.ResponseName : $"Task<{syntax.ResponseName}>"; + var method = !Model.Async ? syntax.MethodName : $"{syntax.MethodName}Async"; + var cancellationToken = !Model.Async ? string.Empty : ", CancellationToken ct = default"; } @Raw(response) @(method)@(Raw(syntax.MethodGenerics))(@Raw(syntax.DescriptorArguments())@Raw(syntax.Selector) selector@(syntax.OptionalSelectorSuffix)@(cancellationToken))@syntax.GenericWhereClause diff --git a/src/ApiGenerator/Views/HighLevel/Client/Implementation/MethodImplementation.cshtml b/src/ApiGenerator/Views/HighLevel/Client/Implementation/MethodImplementation.cshtml index ff9b976911..a7930131cf 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/Implementation/MethodImplementation.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/Implementation/MethodImplementation.cshtml @@ -3,16 +3,16 @@ @using HighLevelModel = ApiGenerator.Domain.Code.HighLevel.Methods.HighLevelModel @inherits CodeTemplatePage @{ - const string fluentPath = "HighLevel.Client.FluentSyntax.FluentMethod"; - const string initializerPath = "HighLevel.Client.InitializerSyntax.InitializerMethod"; + const string fluentPath = "HighLevel.Client.FluentSyntax.FluentMethod"; + const string initializerPath = "HighLevel.Client.InitializerSyntax.InitializerMethod"; } @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.Fluent, async: false)); } @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.Fluent, async: true)); } @if (Model.FluentBound != null) { - @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: false)); } - @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: true)); } + @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: false)); } + @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: true)); } } @{ await IncludeAsync(initializerPath, new InitializerSyntaxView(Model.Initializer, async: false)); } diff --git a/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.Namespace.cshtml b/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.Namespace.cshtml index d76bfd6e98..9e860cd5f9 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.Namespace.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.Namespace.cshtml @@ -4,7 +4,7 @@ @using ApiGenerator.Domain.Specification @inherits ApiGenerator.CodeTemplatePage>> @{ - var (ns, endpoints) = Model; + var (ns, endpoints) = Model; } @{ await IncludeAsync("GeneratorNotice", Model); } // ReSharper disable RedundantUsingDirective @@ -25,9 +25,9 @@ namespace OpenSearch.Client.@(CsharpNames.ApiNamespace).@(ns)@(CsharpNames.ApiNa /// public partial class @(CsharpNames.HighLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix) : NamespacedClientProxy { - internal @(CsharpNames.HighLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix)(OpenSearchClient client) : base(client) {} - @foreach(var e in endpoints) - { - await IncludeAsync("HighLevel.Client.Implementation.MethodImplementation", e.HighLevelModel); - } + internal @(CsharpNames.HighLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix)(OpenSearchClient client) : base(client) {} + @foreach(var e in endpoints) + { + await IncludeAsync("HighLevel.Client.Implementation.MethodImplementation", e.HighLevelModel); + } } diff --git a/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.cshtml b/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.cshtml index 737555be1c..de809546b0 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/Implementation/OpenSearchClient.cshtml @@ -12,7 +12,7 @@ using OpenSearch.Client; @{ await IncludeAsync("HighLevel.Client.Usings", Model);} @{ - var namespaces = Model.EndpointsPerNamespaceHighLevel.Keys.Where(k => k != CsharpNames.RootNamespace).ToList(); + var namespaces = Model.EndpointsPerNamespaceHighLevel.Keys.Where(k => k != CsharpNames.RootNamespace).ToList(); // ReSharper disable RedundantTypeArgumentsOfMethod namespace OpenSearch.Client; @@ -25,29 +25,29 @@ public partial class OpenSearchClient : IOpenSearchClient foreach (var ns in namespaces) { - ///@(ns.SplitPascalCase()) APIs - public @CsharpNames.HighLevelClientNamespacePrefix@(ns)@CsharpNames.ClientNamespaceSuffix @ns { get; private set; } + ///@(ns.SplitPascalCase()) APIs + public @CsharpNames.HighLevelClientNamespacePrefix@(ns)@CsharpNames.ClientNamespaceSuffix @ns { get; private set; } } - partial void SetupNamespacesGenerated() - { + partial void SetupNamespacesGenerated() + { foreach (var ns in namespaces) { - @ns = new @(CsharpNames.HighLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix)(this); + @ns = new @(CsharpNames.HighLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix)(this); } - } + } - + foreach (var (_, endpoints) in Model.EndpointsPerNamespaceHighLevel.Where(kv => kv.Key == CsharpNames.RootNamespace)) { - foreach (var e in endpoints) - { - await IncludeAsync("HighLevel.Client.Implementation.MethodImplementation", e.HighLevelModel); - } + foreach (var e in endpoints) + { + await IncludeAsync("HighLevel.Client.Implementation.MethodImplementation", e.HighLevelModel); + } } } diff --git a/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethod.cshtml b/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethod.cshtml index 9be5611d2c..78241c123a 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethod.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethod.cshtml @@ -1,20 +1,20 @@ @using ApiGenerator.Domain.Code.HighLevel.Methods @inherits ApiGenerator.CodeTemplatePage @{ - var syntax = Model.Syntax; - - var dispatchMethod = !Model.Async ? syntax.DispatchMethod : $"{syntax.DispatchMethod}Async"; - var asyncKeyword = Model.Syntax.InterfaceResponse && Model.Async ? "async " : string.Empty; - var awaitKeyWord = Model.Syntax.InterfaceResponse && Model.Async ? "await ": string.Empty; - var configureAwait = Model.Syntax.InterfaceResponse && Model.Async ? ".ConfigureAwait(false)" : string.Empty; - var dispatchParameters = syntax.DispatchParameters; - if (Model.Async) - { - dispatchParameters += ", ct"; - } + var syntax = Model.Syntax; + + var dispatchMethod = !Model.Async ? syntax.DispatchMethod : $"{syntax.DispatchMethod}Async"; + var asyncKeyword = Model.Syntax.InterfaceResponse && Model.Async ? "async " : string.Empty; + var awaitKeyWord = Model.Syntax.InterfaceResponse && Model.Async ? "await ": string.Empty; + var configureAwait = Model.Syntax.InterfaceResponse && Model.Async ? ".ConfigureAwait(false)" : string.Empty; + var dispatchParameters = syntax.DispatchParameters; + if (Model.Async) + { + dispatchParameters += ", ct"; + } } @{ await IncludeAsync("HighLevel.Client.MethodXmlDocs", syntax); } public @Raw(asyncKeyword)@{ await IncludeAsync("HighLevel.Client.InitializerSyntax.InitializerMethodHeader", Model); } @Raw("=>") - @(awaitKeyWord)@(dispatchMethod)@(Raw(syntax.DispatchGenerics))(@Raw(dispatchParameters))@Raw(configureAwait); + @(awaitKeyWord)@(dispatchMethod)@(Raw(syntax.DispatchGenerics))(@Raw(dispatchParameters))@Raw(configureAwait); diff --git a/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethodHeader.cshtml b/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethodHeader.cshtml index 7c802567ad..db3b2ea76f 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethodHeader.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/InitializerSyntax/InitializerMethodHeader.cshtml @@ -1,15 +1,15 @@ @using ApiGenerator.Domain.Code.HighLevel.Methods @inherits ApiGenerator.CodeTemplatePage @{ - var syntax = Model.Syntax; - - var response = !Model.Async ? syntax.ResponseName : $"Task<{syntax.ResponseName}>"; - var method = !Model.Async ? syntax.MethodName : $"{syntax.MethodName}Async"; + var syntax = Model.Syntax; + + var response = !Model.Async ? syntax.ResponseName : $"Task<{syntax.ResponseName}>"; + var method = !Model.Async ? syntax.MethodName : $"{syntax.MethodName}Async"; - var methodsArgs = $"{syntax.ArgumentType} request"; - if (Model.Async) - { - methodsArgs += ", CancellationToken ct = default"; - } + var methodsArgs = $"{syntax.ArgumentType} request"; + if (Model.Async) + { + methodsArgs += ", CancellationToken ct = default"; + } } @Raw(response) @(method)@(Raw(syntax.MethodGenerics))(@Raw(methodsArgs))@syntax.GenericWhereClause diff --git a/src/ApiGenerator/Views/HighLevel/Client/Interface/IOpenSearchClient.cshtml b/src/ApiGenerator/Views/HighLevel/Client/Interface/IOpenSearchClient.cshtml index 76afc92763..9c1d09c11f 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/Interface/IOpenSearchClient.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/Interface/IOpenSearchClient.cshtml @@ -19,18 +19,18 @@ namespace OpenSearch.Client; /// public partial interface IOpenSearchClient { - @foreach(var (ns, endpoints) in Model.EndpointsPerNamespaceHighLevel) - { - if (ns != CsharpNames.RootNamespace) - { - ///@ns.SplitPascalCase() APIs - @CsharpNames.HighLevelClientNamespacePrefix@(ns)@CsharpNames.ClientNamespaceSuffix @ns { get; } + @foreach(var (ns, endpoints) in Model.EndpointsPerNamespaceHighLevel) + { + if (ns != CsharpNames.RootNamespace) + { + ///@ns.SplitPascalCase() APIs + @CsharpNames.HighLevelClientNamespacePrefix@(ns)@CsharpNames.ClientNamespaceSuffix @ns { get; } - continue; - } - foreach(var e in endpoints) - { - await IncludeAsync("HighLevel.Client.Interface.MethodInterface", e.HighLevelModel); - } - } + continue; + } + foreach(var e in endpoints) + { + await IncludeAsync("HighLevel.Client.Interface.MethodInterface", e.HighLevelModel); + } + } } diff --git a/src/ApiGenerator/Views/HighLevel/Client/Interface/MethodInterface.cshtml b/src/ApiGenerator/Views/HighLevel/Client/Interface/MethodInterface.cshtml index 89d9c1c8e4..f34bfa1c38 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/Interface/MethodInterface.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/Interface/MethodInterface.cshtml @@ -3,9 +3,9 @@ @using HighLevelModel = ApiGenerator.Domain.Code.HighLevel.Methods.HighLevelModel @inherits CodeTemplatePage @{ - const string xmlDocsPath = "HighLevel.Client.MethodXmlDocs"; - const string fluentPath = "HighLevel.Client.FluentSyntax.FluentMethodHeader"; - const string initializerPath = "HighLevel.Client.InitializerSyntax.InitializerMethodHeader"; + const string xmlDocsPath = "HighLevel.Client.MethodXmlDocs"; + const string fluentPath = "HighLevel.Client.FluentSyntax.FluentMethodHeader"; + const string initializerPath = "HighLevel.Client.InitializerSyntax.InitializerMethodHeader"; } @{ await IncludeAsync(xmlDocsPath, Model.Fluent); } @@ -16,10 +16,10 @@ @if (Model.FluentBound != null) { - @{ await IncludeAsync(xmlDocsPath, Model.FluentBound); } - @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: false)); }; - @{ await IncludeAsync(xmlDocsPath, Model.FluentBound); } - @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: true)); }; + @{ await IncludeAsync(xmlDocsPath, Model.FluentBound); } + @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: false)); }; + @{ await IncludeAsync(xmlDocsPath, Model.FluentBound); } + @{ await IncludeAsync(fluentPath, new FluentSyntaxView(Model.FluentBound, async: true)); }; } @{ await IncludeAsync(xmlDocsPath, Model.Initializer); } diff --git a/src/ApiGenerator/Views/HighLevel/Client/Usings.cshtml b/src/ApiGenerator/Views/HighLevel/Client/Usings.cshtml index 86e523865a..0c0fa5cac7 100644 --- a/src/ApiGenerator/Views/HighLevel/Client/Usings.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Client/Usings.cshtml @@ -3,6 +3,6 @@ @inherits ApiGenerator.CodeTemplatePage @foreach (var kv in Model.EndpointsPerNamespaceHighLevel.Where(kv => kv.Key != CsharpNames.RootNamespace)) { - @:using OpenSearch.Client.@(CsharpNames.ApiNamespace).@kv.Key@(CsharpNames.ApiNamespaceSuffix); + @:using OpenSearch.Client.@(CsharpNames.ApiNamespace).@kv.Key@(CsharpNames.ApiNamespaceSuffix); } diff --git a/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptor.cshtml b/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptor.cshtml index 6dfbcb182f..1c3a9299b4 100644 --- a/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptor.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptor.cshtml @@ -3,83 +3,83 @@ @using ApiGenerator.Generator @inherits global::ApiGenerator.CodeTemplatePage @{ - var type = Model.CsharpNames.GenericOrNonGenericDescriptorName; - var concreteInterface = Model.CsharpNames.GenericOrNonGenericInterfaceName; - var baseInterface = Model.CsharpNames.GenericOrNonGenericInterfacePreference; - var apiLookup = $"ApiUrlsLookups.{Model.CsharpNames.Namespace}{Model.CsharpNames.MethodName}"; + var type = Model.CsharpNames.GenericOrNonGenericDescriptorName; + var concreteInterface = Model.CsharpNames.GenericOrNonGenericInterfaceName; + var baseInterface = Model.CsharpNames.GenericOrNonGenericInterfacePreference; + var apiLookup = $"ApiUrlsLookups.{Model.CsharpNames.Namespace}{Model.CsharpNames.MethodName}"; } ///Descriptor for @Model.CsharpNames.MethodName@(Raw(string.IsNullOrEmpty(Model.OfficialDocumentationLink) ? "" : $" {Model.OfficialDocumentationLink}")) public partial class @Raw(type) @Raw(string.Format(" : RequestDescriptorBase<{0},{1}, {2}>, {2}", type,Model.CsharpNames.ParametersName, concreteInterface)) { - internal override ApiUrls ApiUrls => @apiLookup; + internal override ApiUrls ApiUrls => @apiLookup; @foreach (var c in Model.Constructors) { - @(Raw(CodeGenerator.Constructor(c))) + @(Raw(CodeGenerator.Constructor(c))) } - // values part of the url path + // values part of the url path @foreach (var part in Model.Parts) { - @(Raw(part.HighLevelTypeName)) @(Raw(baseInterface)).@(part.InterfaceName) => Self.RouteValues.Get@(Raw($"<{part.HighLevelTypeName}>"))("@(part.Name)"); + @(Raw(part.HighLevelTypeName)) @(Raw(baseInterface)).@(part.InterfaceName) => Self.RouteValues.Get@(Raw($"<{part.HighLevelTypeName}>"))("@(part.Name)"); } @foreach (var c in Model.GetFluentRouteSetters()) { - @(Raw(c.XmlDoc)) - @(Raw(c.Code)) + @(Raw(c.XmlDoc)) + @(Raw(c.Code)) } - // Request parameters + // Request parameters @foreach (var param in Model.Params) { - var original = param.QueryStringKey; - //skip parameters already part of the path portion of the url - if (Model.Parts.Any(p=>p.Name == original)) - { - continue; - } - //we prefer this parameter to be explictly implemented on the request body - if (param.RenderPartial && Model.HasBody) - { - continue; - } - var tSuffix = param.TypeHighLevel is "bool" or "bool?" ? " = true" : ""; - var typed = !string.IsNullOrEmpty(Model.CsharpNames.GenericsDeclaredOnDescriptor); - var g = typed ? Model.CsharpNames.GenericsDeclaredOnDescriptor.Replace("<", "").Replace(">", "") : "T"; - var desc = param.DescriptionHighLevel.ToList(); - - await IncludeAsync("HighLevel.Descriptors.XmlDocs", desc); - if(!string.IsNullOrWhiteSpace(param.Obsolete)) - { - - [Obsolete("Scheduled to be removed in 8.0, @param.Obsolete")] - - } + var original = param.QueryStringKey; + //skip parameters already part of the path portion of the url + if (Model.Parts.Any(p=>p.Name == original)) + { + continue; + } + //we prefer this parameter to be explictly implemented on the request body + if (param.RenderPartial && Model.HasBody) + { + continue; + } + var tSuffix = param.TypeHighLevel is "bool" or "bool?" ? " = true" : ""; + var typed = !string.IsNullOrEmpty(Model.CsharpNames.GenericsDeclaredOnDescriptor); + var g = typed ? Model.CsharpNames.GenericsDeclaredOnDescriptor.Replace("<", "").Replace(">", "") : "T"; + var desc = param.DescriptionHighLevel.ToList(); + + await IncludeAsync("HighLevel.Descriptors.XmlDocs", desc); + if(!string.IsNullOrWhiteSpace(param.Obsolete)) + { + + [Obsolete("Scheduled to be removed in 8.0, @param.Obsolete")] + + } - public @Raw(type) @(param.ClsName)(@param.DescriptorArgumentType @param.ClsArgumentName@tSuffix) => Qs("@original", @(param.ClsArgumentName)); + public @Raw(type) @(param.ClsName)(@param.DescriptorArgumentType @param.ClsArgumentName@tSuffix) => Qs("@original", @(param.ClsArgumentName)); - if (param.IsFieldsParam) - { + if (param.IsFieldsParam) + { - ///@param.Description - public @Raw(type) @param.ClsName@(Raw(typed ? "" : ""))(params @Raw($"Expression>[]") fields) @Raw(typed ? "" : $"where {g} : class") => Qs("@original", fields?@Raw(".Select(e=>(Field)e)")); + ///@param.Description + public @Raw(type) @param.ClsName@(Raw(typed ? "" : ""))(params @Raw($"Expression>[]") fields) @Raw(typed ? "" : $"where {g} : class") => Qs("@original", fields?@Raw(".Select(e=>(Field)e)")); - } - else if (param.IsFieldParam) - { + } + else if (param.IsFieldParam) + { - ///@param.Description - public @Raw(type) @param.ClsName@(Raw(typed ? "" : ""))(@Raw($"Expression>") field) @Raw(typed ? "" : $"where {g} : class") => Qs("@original", (Field)field); + ///@param.Description + public @Raw(type) @param.ClsName@(Raw(typed ? "" : ""))(@Raw($"Expression>") field) @Raw(typed ? "" : $"where {g} : class") => Qs("@original", (Field)field); - } + } } @if (Model.CsharpNames.DescriptorNotFoundInCodebase) { - [Obsolete("Unmapped, blacklist this API in CodeConfiguration.cs or implement @Model.CsharpNames.DescriptorName and @Model.CsharpNames.RequestName in a file called @(Model.CsharpNames.RequestName).cs in OSC's codebase", true)] - public bool IsUnmapped => true; - public bool UseIsUnmapped => IsUnmapped; + [Obsolete("Unmapped, blacklist this API in CodeConfiguration.cs or implement @Model.CsharpNames.DescriptorName and @Model.CsharpNames.RequestName in a file called @(Model.CsharpNames.RequestName).cs in OSC's codebase", true)] + public bool IsUnmapped => true; + public bool UseIsUnmapped => IsUnmapped; -} +} } diff --git a/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptors.cshtml b/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptors.cshtml index 1b98685c4f..a5dc2411df 100644 --- a/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptors.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Descriptors/Descriptors.cshtml @@ -4,8 +4,8 @@ @using ApiGenerator.Domain.Code @inherits CodeTemplatePage>> @{ - var (ns, endpoints) = Model; - ns = ns != CsharpNames.RootNamespace ? $".{CsharpNames.ApiNamespace}.{ns}{CsharpNames.ApiNamespaceSuffix}" : null; + var (ns, endpoints) = Model; + ns = ns != CsharpNames.RootNamespace ? $".{CsharpNames.ApiNamespace}.{ns}{CsharpNames.ApiNamespaceSuffix}" : null; } @{ await IncludeAsync("GeneratorNotice", Model); } // ReSharper disable RedundantUsingDirective @@ -19,7 +19,7 @@ using OpenSearch.Net; using OpenSearch.Net.Utf8Json; @if (ns != null) { - using OpenSearch.Net@(ns); + using OpenSearch.Net@(ns); } @@ -31,5 +31,5 @@ namespace OpenSearch.Client@(ns); @foreach (var endpoint in endpoints) { - await IncludeAsync("HighLevel.Descriptors.Descriptor", endpoint.DescriptorPartialImplementation); + await IncludeAsync("HighLevel.Descriptors.Descriptor", endpoint.DescriptorPartialImplementation); } diff --git a/src/ApiGenerator/Views/HighLevel/Descriptors/XmlDocs.cshtml b/src/ApiGenerator/Views/HighLevel/Descriptors/XmlDocs.cshtml index f2503fff4a..53254f5fb8 100644 --- a/src/ApiGenerator/Views/HighLevel/Descriptors/XmlDocs.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Descriptors/XmlDocs.cshtml @@ -1,16 +1,16 @@ @using System.Linq @inherits ApiGenerator.CodeTemplatePage> @{ - var description = Model.Count == 1 ? Model.First() : string.Join($"{Environment.NewLine}\t\t", Model.Select(d => $"/// {d}")); - if (Model.Count == 1) - { - ///@Raw(description) - } - else - { - /// - @Raw(description) - /// + var description = Model.Count == 1 ? Model.First() : string.Join($"{Environment.NewLine}\t\t", Model.Select(d => $"/// {d}")); + if (Model.Count == 1) + { + ///@Raw(description) + } + else + { + /// + @Raw(description) + /// - } + } } diff --git a/src/ApiGenerator/Views/HighLevel/Requests/ApiUrlsLookup.cshtml b/src/ApiGenerator/Views/HighLevel/Requests/ApiUrlsLookup.cshtml index ebdfb38bd9..cd71ae113b 100644 --- a/src/ApiGenerator/Views/HighLevel/Requests/ApiUrlsLookup.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Requests/ApiUrlsLookup.cshtml @@ -10,11 +10,11 @@ internal static partial class ApiUrlsLookups { @foreach (var endpoint in Model.Endpoints.Values) { - var propertyName = $"{endpoint.CsharpNames.Namespace}{endpoint.CsharpNames.MethodName}"; - var paths = endpoint.Url.Paths.Count == 0 ? endpoint.Url.PathsWithDeprecations : endpoint.Url.Paths; - - internal static ApiUrls @(Raw(propertyName)) = new(new [] {@Raw(string.Join(", ", paths.Select(p=>$"\"{p.Path.TrimStart('/')}\"")))}); - + var propertyName = $"{endpoint.CsharpNames.Namespace}{endpoint.CsharpNames.MethodName}"; + var paths = endpoint.Url.Paths.Count == 0 ? endpoint.Url.PathsWithDeprecations : endpoint.Url.Paths; + + internal static ApiUrls @(Raw(propertyName)) = new(new [] {@Raw(string.Join(", ", paths.Select(p=>$"\"{p.Path.TrimStart('/')}\"")))}); + } } diff --git a/src/ApiGenerator/Views/HighLevel/Requests/RequestImplementations.cshtml b/src/ApiGenerator/Views/HighLevel/Requests/RequestImplementations.cshtml index 38fdb70457..c9036e220a 100644 --- a/src/ApiGenerator/Views/HighLevel/Requests/RequestImplementations.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Requests/RequestImplementations.cshtml @@ -4,74 +4,74 @@ @using ApiGenerator.Generator @inherits global::ApiGenerator.CodeTemplatePage @{ - var apiLookup = $"ApiUrlsLookups.{Model.CsharpNames.Namespace}{Model.CsharpNames.MethodName}"; + var apiLookup = $"ApiUrlsLookups.{Model.CsharpNames.Namespace}{Model.CsharpNames.MethodName}"; } ///Request for @Model.CsharpNames.MethodName@(Raw(string.IsNullOrEmpty(Model.OfficialDocumentationLink) ? "" : $" {Model.OfficialDocumentationLink}")) @if (Model.Stability != Stability.Stable) { - var warningMessage = Model.Stability switch - { - Stability.Experimental => "this functionality is experimental and may be changed or removed completely in a future release. OpenSearch will take a best effort approach to fix any issues, but experimental features are not subject to the support SLA of official GA features.", - Stability.Beta => "this functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features.", - _ => "" - }; + var warningMessage = Model.Stability switch + { + Stability.Experimental => "this functionality is experimental and may be changed or removed completely in a future release. OpenSearch will take a best effort approach to fix any issues, but experimental features are not subject to the support SLA of official GA features.", + Stability.Beta => "this functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features.", + _ => "" + }; - ///@Raw($"Note: {Model.Stability} within the OpenSearch server, {warningMessage}") + ///@Raw($"Note: {Model.Stability} within the OpenSearch server, {warningMessage}") - } - public partial class @Raw(Model.Name) @Raw($": PlainRequestBase<{Model.CsharpNames.ParametersName}>, {Model.InterfaceName}") + } + public partial class @Raw(Model.Name) @Raw($": PlainRequestBase<{Model.CsharpNames.ParametersName}>, {Model.InterfaceName}") { - protected @Raw(Model.InterfaceName) Self => this; - internal override ApiUrls ApiUrls => @apiLookup; + protected @Raw(Model.InterfaceName) Self => this; + internal override ApiUrls ApiUrls => @apiLookup; @foreach (var c in Model.Constructors) { - @(Raw(CodeGenerator.Constructor(c))) + @(Raw(CodeGenerator.Constructor(c))) } - // values part of the url path + // values part of the url path @foreach (var part in Model.Parts) { - [IgnoreDataMember] - @(Raw(part.HighLevelTypeName)) @(Raw(Model.InterfaceName)).@(part.InterfaceName) => Self.RouteValues.Get@(Raw($"<{part.HighLevelTypeName}>"))("@(part.Name)"); + [IgnoreDataMember] + @(Raw(part.HighLevelTypeName)) @(Raw(Model.InterfaceName)).@(part.InterfaceName) => Self.RouteValues.Get@(Raw($"<{part.HighLevelTypeName}>"))("@(part.Name)"); } - // Request parameters + // Request parameters @foreach (var param in Model.Params) { - var original = param.QueryStringKey; - //skip parameters already part of the path portion of the url - if (Model.Parts.Any(p=>p.Name == original)) - { - continue; - } - // We prefer to map these explicitly in our own hand written classes. - // The interface generation will generate properties for these so code won't compile until we do - if (param.RenderPartial && Model.HasBody) - { - continue; - } - var doc = param.DescriptionHighLevel.ToArray(); - @Raw(param.InitializerGenerator(Model.CsharpNames.Namespace, param.TypeHighLevel, param.ClsName, original, param.SetterHighLevel, doc)) + var original = param.QueryStringKey; + //skip parameters already part of the path portion of the url + if (Model.Parts.Any(p=>p.Name == original)) + { + continue; + } + // We prefer to map these explicitly in our own hand written classes. + // The interface generation will generate properties for these so code won't compile until we do + if (param.RenderPartial && Model.HasBody) + { + continue; + } + var doc = param.DescriptionHighLevel.ToArray(); + @Raw(param.InitializerGenerator(Model.CsharpNames.Namespace, param.TypeHighLevel, param.ClsName, original, param.SetterHighLevel, doc)) } @if (Model.CsharpNames.DescriptorNotFoundInCodebase) { - [Obsolete("Unmapped, blacklist this API in CodeConfiguration.cs or implement @Model.CsharpNames.DescriptorName and @Model.CsharpNames.RequestName in a file called @(Model.CsharpNames.RequestName).cs in OSC's codebase", true)] - public bool IsUnmapped => true; - public bool UseIsUnmapped => IsUnmapped; + [Obsolete("Unmapped, blacklist this API in CodeConfiguration.cs or implement @Model.CsharpNames.DescriptorName and @Model.CsharpNames.RequestName in a file called @(Model.CsharpNames.RequestName).cs in OSC's codebase", true)] + public bool IsUnmapped => true; + public bool UseIsUnmapped => IsUnmapped; } - } + } @if (Model.NeedsGenericImplementation) - { - public partial class @Raw(Model.CsharpNames.GenericRequestName) @Raw($": {Model.CsharpNames.RequestName}, {Model.CsharpNames.GenericInterfaceName}") - { - protected @Raw(Model.CsharpNames.GenericInterfaceName) TypedSelf => this; + { + public partial class @Raw(Model.CsharpNames.GenericRequestName) @Raw($": {Model.CsharpNames.RequestName}, {Model.CsharpNames.GenericInterfaceName}") + { + protected @Raw(Model.CsharpNames.GenericInterfaceName) TypedSelf => this; @foreach (var c in Model.GenericConstructors) { - @(Raw(CodeGenerator.Constructor(c))) + @(Raw(CodeGenerator.Constructor(c))) } - } - + } + } diff --git a/src/ApiGenerator/Views/HighLevel/Requests/RequestInterface.cshtml b/src/ApiGenerator/Views/HighLevel/Requests/RequestInterface.cshtml index d7200d4b86..d064b2ac59 100644 --- a/src/ApiGenerator/Views/HighLevel/Requests/RequestInterface.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Requests/RequestInterface.cshtml @@ -1,24 +1,24 @@ @inherits ApiGenerator.CodeTemplatePage @{ - var name = Model.CsharpNames.RequestInterfaceName; + var name = Model.CsharpNames.RequestInterfaceName; } - [InterfaceDataContract] - public partial interface @(Raw(Model.Name)) : IRequest@(Raw($"<{Model.CsharpNames.ParametersName}>")) + [InterfaceDataContract] + public partial interface @(Raw(Model.Name)) : IRequest@(Raw($"<{Model.CsharpNames.ParametersName}>")) { @foreach (var part in Model.UrlParts) { - [IgnoreDataMember] - @(Raw(part.HighLevelTypeName)) @(part.InterfaceName) { get; } + [IgnoreDataMember] + @(Raw(part.HighLevelTypeName)) @(part.InterfaceName) { get; } } @foreach (var partialParam in Model.PartialParameters) { - [DataMember(Name = "@(partialParam.QueryStringKey)")] @(Raw(partialParam.TypeHighLevel)) @(partialParam.ClsName) { get; set; } + [DataMember(Name = "@(partialParam.QueryStringKey)")] @(Raw(partialParam.TypeHighLevel)) @(partialParam.ClsName) { get; set; } } - } + } @if (Model.NeedsGenericInterface) { - public partial interface @(name)@(Raw(Model.CsharpNames.GenericsDeclaredOnRequest)) : @(Raw(name)) { } + public partial interface @(name)@(Raw(Model.CsharpNames.GenericsDeclaredOnRequest)) : @(Raw(name)) { } } diff --git a/src/ApiGenerator/Views/HighLevel/Requests/Requests.cshtml b/src/ApiGenerator/Views/HighLevel/Requests/Requests.cshtml index ed50f415f2..dec44d2f16 100644 --- a/src/ApiGenerator/Views/HighLevel/Requests/Requests.cshtml +++ b/src/ApiGenerator/Views/HighLevel/Requests/Requests.cshtml @@ -4,8 +4,8 @@ @using ApiGenerator.Domain.Code @inherits CodeTemplatePage>> @{ - var (ns, endpoints) = Model; - ns = ns != CsharpNames.RootNamespace ? $".{CsharpNames.ApiNamespace}.{ns}{CsharpNames.ApiNamespaceSuffix}" : null; + var (ns, endpoints) = Model; + ns = ns != CsharpNames.RootNamespace ? $".{CsharpNames.ApiNamespace}.{ns}{CsharpNames.ApiNamespaceSuffix}" : null; } @{ await IncludeAsync("GeneratorNotice", Model); } // ReSharper disable RedundantUsingDirective @@ -19,7 +19,7 @@ using OpenSearch.Net; using OpenSearch.Net.Utf8Json; @if (ns != null) { - using OpenSearch.Net@(ns); + using OpenSearch.Net@(ns); } @@ -32,7 +32,7 @@ namespace OpenSearch.Client@(ns); @foreach (var endpoint in endpoints) { - await IncludeAsync("HighLevel.Requests.RequestInterface", endpoint.RequestInterface); - await IncludeAsync("HighLevel.Requests.RequestImplementations", endpoint.RequestPartialImplementation); + await IncludeAsync("HighLevel.Requests.RequestInterface", endpoint.RequestInterface); + await IncludeAsync("HighLevel.Requests.RequestImplementations", endpoint.RequestPartialImplementation); } diff --git a/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.Namespace.cshtml b/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.Namespace.cshtml index 3e8450f3d3..e6a61e9ee3 100644 --- a/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.Namespace.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.Namespace.cshtml @@ -6,7 +6,7 @@ @inherits CodeTemplatePage>> @{ await IncludeAsync("GeneratorNotice", Model); } @{ - var (ns, endpoints) = Model; + var (ns, endpoints) = Model; } // ReSharper disable RedundantUsingDirective using System; @@ -33,17 +33,17 @@ namespace OpenSearch.Net.@(CsharpNames.ApiNamespace).@(ns)@(CsharpNames.ApiNames /// public partial class @(CsharpNames.LowLevelClientNamespacePrefix)@Model.Key@(CsharpNames.ClientNamespaceSuffix) : NamespacedClientProxy { - internal @(CsharpNames.LowLevelClientNamespacePrefix)@Model.Key@(CsharpNames.ClientNamespaceSuffix)(OpenSearchLowLevelClient client) : base(client) {} + internal @(CsharpNames.LowLevelClientNamespacePrefix)@Model.Key@(CsharpNames.ClientNamespaceSuffix)(OpenSearchLowLevelClient client) : base(client) {} @if (ns == "Cat") { - protected override string ContentType { get; } = "text/plain"; + protected override string ContentType { get; } = "text/plain"; } -@{ - var methods = endpoints.SelectMany(e=>e.LowLevelClientMethods).ToList(); - foreach (var method in methods) - { - await IncludeAsync("LowLevel.Client.Methods.MethodImplementation", method); - } +@{ + var methods = endpoints.SelectMany(e=>e.LowLevelClientMethods).ToList(); + foreach (var method in methods) + { + await IncludeAsync("LowLevel.Client.Methods.MethodImplementation", method); + } } } diff --git a/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.cshtml b/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.cshtml index 9d3438538f..5e948414a8 100644 --- a/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Client/Implementation/OpenSearchLowLevelClient.cshtml @@ -17,8 +17,8 @@ using OpenSearch.Net; using static OpenSearch.Net.HttpMethod; @{ - var model = Model; - var namespaces = model.EndpointsPerNamespaceLowLevel.Keys.Where(k => k != CsharpNames.RootNamespace).ToList(); + var model = Model; + var namespaces = model.EndpointsPerNamespaceLowLevel.Keys.Where(k => k != CsharpNames.RootNamespace).ToList(); // ReSharper disable InterpolatedStringExpressionIsNotIFormattable // ReSharper disable RedundantExtendsListEntry @@ -32,37 +32,37 @@ public partial class OpenSearchLowLevelClient : IOpenSearchLowLevelClient foreach (var ns in namespaces) { - public @(CsharpNames.LowLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix) @ns { get; private set; } + public @(CsharpNames.LowLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix) @ns { get; private set; } } - partial void SetupNamespacesGenerated() - { + partial void SetupNamespacesGenerated() + { foreach (var ns in namespaces) { - @ns = new @(CsharpNames.LowLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix)(this); + @ns = new @(CsharpNames.LowLevelClientNamespacePrefix)@(ns)@(CsharpNames.ClientNamespaceSuffix)(this); } } - + - foreach (var kv in model.EndpointsPerNamespaceLowLevel) - { - if (kv.Key != CsharpNames.RootNamespace) - { - continue; - } - var endpoints = kv.Value; - var methods = endpoints.SelectMany(e=>e.LowLevelClientMethods).ToList(); - foreach (var method in methods) - { - await IncludeAsync("LowLevel.Client.Methods.MethodImplementation", method); - } - } - - } - + foreach (var kv in model.EndpointsPerNamespaceLowLevel) + { + if (kv.Key != CsharpNames.RootNamespace) + { + continue; + } + var endpoints = kv.Value; + var methods = endpoints.SelectMany(e=>e.LowLevelClientMethods).ToList(); + foreach (var method in methods) + { + await IncludeAsync("LowLevel.Client.Methods.MethodImplementation", method); + } + } + + } + } diff --git a/src/ApiGenerator/Views/LowLevel/Client/Interface/IOpenSearchLowLevelClient.cshtml b/src/ApiGenerator/Views/LowLevel/Client/Interface/IOpenSearchLowLevelClient.cshtml index d296f832c7..a6efcd8d75 100644 --- a/src/ApiGenerator/Views/LowLevel/Client/Interface/IOpenSearchLowLevelClient.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Client/Interface/IOpenSearchLowLevelClient.cshtml @@ -22,20 +22,20 @@ namespace OpenSearch.Net; /// public partial interface IOpenSearchLowLevelClient { - @foreach(var (ns, endpoints) in Model.EndpointsPerNamespaceLowLevel) - { - if (ns != CsharpNames.RootNamespace) - { + @foreach(var (ns, endpoints) in Model.EndpointsPerNamespaceLowLevel) + { + if (ns != CsharpNames.RootNamespace) + { - ///@ns.SplitPascalCase() APIs - @CsharpNames.LowLevelClientNamespacePrefix@(ns)@CsharpNames.ClientNamespaceSuffix @ns { get; } + ///@ns.SplitPascalCase() APIs + @CsharpNames.LowLevelClientNamespacePrefix@(ns)@CsharpNames.ClientNamespaceSuffix @ns { get; } - continue; - } - var methods = endpoints.SelectMany(e=>e.LowLevelClientMethods).ToList(); - foreach(var method in methods) - { - await IncludeAsync("LowLevel.Client.Methods.MethodInterface", method); - } - } + continue; + } + var methods = endpoints.SelectMany(e=>e.LowLevelClientMethods).ToList(); + foreach(var method in methods) + { + await IncludeAsync("LowLevel.Client.Methods.MethodInterface", method); + } + } } diff --git a/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodDocs.cshtml b/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodDocs.cshtml index 8db7d7bdf2..4ad740a3a3 100644 --- a/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodDocs.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodDocs.cshtml @@ -4,29 +4,29 @@ ///@Model.HttpMethod on @Model.Path@(Raw(string.IsNullOrEmpty(Model.OfficialDocumentationLink) ? "" : $" {Model.OfficialDocumentationLink}")) @foreach (var part in Model.Parts) { - ///@Raw($"")@part.Description@Raw("") + ///@Raw($"")@part.Description@Raw("") } - ///@Raw(@"Request specific configuration such as querystring parameters & request specific connection settings.") - @if (Model.Stability != Stability.Stable) - { - var warningMessage = ""; - switch (Model.Stability) - { - case Stability.Experimental: - warningMessage = "this functionality is Experimental and may be changed or removed completely in a future release. OpenSearch will take a best effort approach to fix any issues, but experimental features are not subject to the support SLA of official GA features."; - break; - case Stability.Beta: - warningMessage = "this functionality is in Beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features."; - break; - } + ///@Raw(@"Request specific configuration such as querystring parameters & request specific connection settings.") + @if (Model.Stability != Stability.Stable) + { + var warningMessage = ""; + switch (Model.Stability) + { + case Stability.Experimental: + warningMessage = "this functionality is Experimental and may be changed or removed completely in a future release. OpenSearch will take a best effort approach to fix any issues, but experimental features are not subject to the support SLA of official GA features."; + break; + case Stability.Beta: + warningMessage = "this functionality is in Beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features."; + break; + } - warningMessage += " This functionality is subject to potential breaking changes within a minor version, meaning that your referencing code may break when this library is upgraded."; - - ///@Raw($"Note: {Model.Stability} within the OpenSearch server, {warningMessage}") + warningMessage += " This functionality is subject to potential breaking changes within a minor version, meaning that your referencing code may break when this library is upgraded."; + + ///@Raw($"Note: {Model.Stability} within the OpenSearch server, {warningMessage}") } - @if (Model.DeprecatedPath != null) - { - [Obsolete("Deprecated in version @Model.DeprecatedPath.Version: @Raw(Model.DeprecatedPath.Description)")] + @if (Model.DeprecatedPath != null) + { + [Obsolete("Deprecated in version @Model.DeprecatedPath.Version: @Raw(Model.DeprecatedPath.Description)")] } diff --git a/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodImplementation.cshtml b/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodImplementation.cshtml index 98e5270cb0..127ca6a948 100644 --- a/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodImplementation.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodImplementation.cshtml @@ -1,13 +1,13 @@ @using ApiGenerator.Domain.Code.LowLevel @inherits ApiGenerator.CodeTemplatePage @{ - var contentType = Model.CsharpNames.RestSpecName.Contains(".cat_") ? ", contentType: \"text/plain\"" : string.Empty; + var contentType = Model.CsharpNames.RestSpecName.Contains(".cat_") ? ", contentType: \"text/plain\"" : string.Empty; } @{await IncludeAsync("LowLevel.Client.Methods.MethodDocs", Model); } - public TResponse @Model.PerPathMethodName@(Raw(""))(@Raw(Model.Arguments)) - where TResponse : class, IOpenSearchResponse, new() => DoRequest@(Raw(""))(@Model.HttpMethod, @Raw(Model.UrlInCode), @(Model.HasBody ? "body" : "null"), RequestParams(requestParameters@(Raw(contentType)))); + public TResponse @Model.PerPathMethodName@(Raw(""))(@Raw(Model.Arguments)) + where TResponse : class, IOpenSearchResponse, new() => DoRequest@(Raw(""))(@Model.HttpMethod, @Raw(Model.UrlInCode), @(Model.HasBody ? "body" : "null"), RequestParams(requestParameters@(Raw(contentType)))); @{await IncludeAsync("LowLevel.Client.Methods.MethodDocs", Model); } - [MapsApi("@Model.CsharpNames.RestSpecName", "@Model.MapsApiArguments")] - public Task@(Raw("")) @(Model.PerPathMethodName)Async@(Raw(""))(@Raw(Model.Arguments), CancellationToken ctx = default) - where TResponse : class, IOpenSearchResponse, new() => DoRequestAsync@(Raw(""))(@Model.HttpMethod, @Raw(Model.UrlInCode), ctx, @(Model.HasBody ? "body" : "null"), RequestParams(requestParameters@(Raw(contentType)))); + [MapsApi("@Model.CsharpNames.RestSpecName", "@Model.MapsApiArguments")] + public Task@(Raw("")) @(Model.PerPathMethodName)Async@(Raw(""))(@Raw(Model.Arguments), CancellationToken ctx = default) + where TResponse : class, IOpenSearchResponse, new() => DoRequestAsync@(Raw(""))(@Model.HttpMethod, @Raw(Model.UrlInCode), ctx, @(Model.HasBody ? "body" : "null"), RequestParams(requestParameters@(Raw(contentType)))); diff --git a/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodInterface.cshtml b/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodInterface.cshtml index ba14764228..dad64eabcf 100644 --- a/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodInterface.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Client/Methods/MethodInterface.cshtml @@ -1,7 +1,7 @@ @using ApiGenerator.Domain.Code.LowLevel @inherits ApiGenerator.CodeTemplatePage @{await IncludeAsync("LowLevel.Client.Methods.MethodDocs", Model); } - TResponse @Model.PerPathMethodName@(Raw(""))(@Raw(Model.Arguments)) where TResponse : class, IOpenSearchResponse, new(); + TResponse @Model.PerPathMethodName@(Raw(""))(@Raw(Model.Arguments)) where TResponse : class, IOpenSearchResponse, new(); @{await IncludeAsync("LowLevel.Client.Methods.MethodDocs", Model); } - Task@(Raw("")) @(Model.PerPathMethodName)Async@(Raw(""))(@Raw(Model.Arguments), CancellationToken ctx = default) where TResponse : class, IOpenSearchResponse, new(); + Task@(Raw("")) @(Model.PerPathMethodName)Async@(Raw(""))(@Raw(Model.Arguments), CancellationToken ctx = default) where TResponse : class, IOpenSearchResponse, new(); diff --git a/src/ApiGenerator/Views/LowLevel/Client/Usings.cshtml b/src/ApiGenerator/Views/LowLevel/Client/Usings.cshtml index f6bbf8a6bf..3a58ac221e 100644 --- a/src/ApiGenerator/Views/LowLevel/Client/Usings.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Client/Usings.cshtml @@ -3,5 +3,5 @@ @inherits ApiGenerator.CodeTemplatePage @foreach (var kv in Model.EndpointsPerNamespaceLowLevel.Where(kv => kv.Key != CsharpNames.RootNamespace)) { - @:using OpenSearch.Net.@(CsharpNames.ApiNamespace).@kv.Key@(CsharpNames.ApiNamespaceSuffix); + @:using OpenSearch.Net.@(CsharpNames.ApiNamespace).@kv.Key@(CsharpNames.ApiNamespaceSuffix); } diff --git a/src/ApiGenerator/Views/LowLevel/Enums.Generated.cshtml b/src/ApiGenerator/Views/LowLevel/Enums.Generated.cshtml index b12b3b6685..eb2e4bae51 100644 --- a/src/ApiGenerator/Views/LowLevel/Enums.Generated.cshtml +++ b/src/ApiGenerator/Views/LowLevel/Enums.Generated.cshtml @@ -6,38 +6,38 @@ @inherits CodeTemplatePage @{ await IncludeAsync("GeneratorNotice", Model); } @functions { - private const string RawSize = "Raw"; - private const string SizeEnum = "Size"; - private static readonly GlobalOverrides GlobalOverrides = new(); + private const string RawSize = "Raw"; + private const string SizeEnum = "Size"; + private static readonly GlobalOverrides GlobalOverrides = new(); - private static string CreateEnum(string enumName, string value, int? i) - { - var enumValue = enumName == SizeEnum && value == string.Empty ? RawSize : value.ToPascalCase(true); - var enumCsharp = $"[EnumMember(Value = \"{value}\")] {enumValue}{(i.HasValue ? $" = 1 << {i.Value}" : null)}"; - if (GlobalOverrides.ObsoleteEnumMembers.TryGetValue(enumName, out var d) && d.TryGetValue(value, out var obsolete)) - { - return $"[Obsolete(\"{obsolete}\")]{Environment.NewLine}\t\t{enumCsharp}"; - } - return enumCsharp; - } - - private string CreateCase(string e, string o) - { - var enumValue = GetEnumValue(e, o); - var isObsolete = GlobalOverrides.ObsoleteEnumMembers.TryGetValue(e, out var d) && d.TryGetValue(o, out _); - var sb = new StringBuilder(); - if (isObsolete) sb.AppendLine("#pragma warning disable 618"); - sb.Append($"case {e}.{enumValue}: return \"{o}\";"); - if (isObsolete) sb.AppendLine($"{Environment.NewLine}#pragma warning disable 618"); - return sb.ToString(); - } - - private static bool IsFlag(string name) => name.EndsWith("Metric") || name.EndsWith("Feature"); + private static string CreateEnum(string enumName, string value, int? i) + { + var enumValue = enumName == SizeEnum && value == string.Empty ? RawSize : value.ToPascalCase(true); + var enumCsharp = $"[EnumMember(Value = \"{value}\")] {enumValue}{(i.HasValue ? $" = 1 << {i.Value}" : null)}"; + if (GlobalOverrides.ObsoleteEnumMembers.TryGetValue(enumName, out var d) && d.TryGetValue(value, out var obsolete)) + { + return $"[Obsolete(\"{obsolete}\")]{Environment.NewLine}\t\t{enumCsharp}"; + } + return enumCsharp; + } + + private string CreateCase(string e, string o) + { + var enumValue = GetEnumValue(e, o); + var isObsolete = GlobalOverrides.ObsoleteEnumMembers.TryGetValue(e, out var d) && d.TryGetValue(o, out _); + var sb = new StringBuilder(); + if (isObsolete) sb.AppendLine("#pragma warning disable 618"); + sb.Append($"case {e}.{enumValue}: return \"{o}\";"); + if (isObsolete) sb.AppendLine($"{Environment.NewLine}#pragma warning disable 618"); + return sb.ToString(); + } + + private static bool IsFlag(string name) => name.EndsWith("Metric") || name.EndsWith("Feature"); - private static string GetEnumValue(string enumName, string value) => - enumName == SizeEnum && value == string.Empty - ? RawSize - : value.ToPascalCase(true); + private static string GetEnumValue(string enumName, string value) => + enumName == SizeEnum && value == string.Empty + ? RawSize + : value.ToPascalCase(true); } // ReSharper disable RedundantUsingDirective using System; @@ -52,118 +52,118 @@ namespace OpenSearch.Net; @foreach (var e in Model.EnumsInTheSpec.OrderBy(e => e.Name)) { - var isFlag = IsFlag(e.Name); - - @(isFlag ? "[Flags, StringEnum]" : "[StringEnum]")public enum @e.Name - { - @Raw(string.Join($",{Environment.NewLine}\t\t", e.Options.OrderBy(s => s == "_all" ? 1 : 0).Select((s, i) => CreateEnum(e.Name, s, isFlag ? i : null)))) - } + var isFlag = IsFlag(e.Name); + + @(isFlag ? "[Flags, StringEnum]" : "[StringEnum]")public enum @e.Name + { + @Raw(string.Join($",{Environment.NewLine}\t\t", e.Options.OrderBy(s => s == "_all" ? 1 : 0).Select((s, i) => CreateEnum(e.Name, s, isFlag ? i : null)))) + } } public static partial class KnownEnums { - private static readonly @(Raw("ConcurrentDictionary>")) EnumStringResolvers = new @(Raw("ConcurrentDictionary>"))(); + private static readonly @(Raw("ConcurrentDictionary>")) EnumStringResolvers = new @(Raw("ConcurrentDictionary>"))(); - static partial void InitializeStringResolvers() - { - @foreach (var e in Model.EnumsInTheSpec) - { - EnumStringResolvers.TryAdd(typeof(@(e.Name)), (e) => GetStringValue((@(e.Name))e)); + static partial void InitializeStringResolvers() + { + @foreach (var e in Model.EnumsInTheSpec) + { + EnumStringResolvers.TryAdd(typeof(@(e.Name)), (e) => GetStringValue((@(e.Name))e)); - } - } + } + } - private class EnumDictionary : @(Raw("Dictionary")) - { - public EnumDictionary(int capacity) : base(capacity) {} - public @(Raw("Func")) Resolver { get; set; } - } + private class EnumDictionary : @(Raw("Dictionary")) + { + public EnumDictionary(int capacity) : base(capacity) {} + public @(Raw("Func")) Resolver { get; set; } + } @foreach (var e in Model.EnumsInTheSpec) { - var isFlag = IsFlag(e.Name); - - public static string GetStringValue(this @(e.Name) enumValue) - { - - if (isFlag) - { - var allOption = e.Options.FirstOrDefault(o => o == "_all"); - if (allOption != null) - { - if ((enumValue & @(e.Name).All) != 0) return "_all"; + var isFlag = IsFlag(e.Name); + + public static string GetStringValue(this @(e.Name) enumValue) + { + + if (isFlag) + { + var allOption = e.Options.FirstOrDefault(o => o == "_all"); + if (allOption != null) + { + if ((enumValue & @(e.Name).All) != 0) return "_all"; - } - var list = new @(Raw("List()")); + } + var list = new @(Raw("List()")); - var g = GlobalOverrides.ObsoleteEnumMembers.TryGetValue(e.Name, out var d); - foreach (var option in e.Options.Where(o => o != "_all")) - { - var value = GetEnumValue(e.Name, option); - if (g && d.TryGetValue(option, out _)) { + var g = GlobalOverrides.ObsoleteEnumMembers.TryGetValue(e.Name, out var d); + foreach (var option in e.Options.Where(o => o != "_all")) + { + var value = GetEnumValue(e.Name, option); + if (g && d.TryGetValue(option, out _)) { #pragma warning disable 618 - if ((enumValue & @(e.Name).@(value)) != 0) list.Add("@(option)"); + if ((enumValue & @(e.Name).@(value)) != 0) list.Add("@(option)"); #pragma warning restore 618 - - } - else { - if ((enumValue & @(e.Name).@(value)) != 0) list.Add("@(option)"); + + } + else { + if ((enumValue & @(e.Name).@(value)) != 0) list.Add("@(option)"); - } - } - return string.Join(",", list); - } - } - else - { - switch (enumValue) - { - @Raw(string.Join($"{Environment.NewLine}\t\t\t\t", e.Options.Select(o => CreateCase(e.Name, o)))) - } - throw new ArgumentException($"'{enumValue.ToString()}' is not a valid value for enum '@(e.Name)'"); - } - } + } + } + return string.Join(",", list); + } + } + else + { + switch (enumValue) + { + @Raw(string.Join($"{Environment.NewLine}\t\t\t\t", e.Options.Select(o => CreateCase(e.Name, o)))) + } + throw new ArgumentException($"'{enumValue.ToString()}' is not a valid value for enum '@(e.Name)'"); + } + } } - public static string GetStringValue(this Enum e) - { - var type = e.GetType(); - var resolver = EnumStringResolvers.GetOrAdd(type, GetEnumStringResolver); - return resolver(e); - } + public static string GetStringValue(this Enum e) + { + var type = e.GetType(); + var resolver = EnumStringResolvers.GetOrAdd(type, GetEnumStringResolver); + return resolver(e); + } - private static @Raw("Func") GetEnumStringResolver(Type type) - { - var values = Enum.GetValues(type); - var dictionary = new EnumDictionary(values.Length); + private static @Raw("Func") GetEnumStringResolver(Type type) + { + var values = Enum.GetValues(type); + var dictionary = new EnumDictionary(values.Length); - for (int index = 0; index < values.Length; index++) - { - var value = values.GetValue(index); - var info = type.GetField(value.ToString()); - var da = (EnumMemberAttribute[])info.GetCustomAttributes(typeof(EnumMemberAttribute), false); - var stringValue = da.Length > 0 ? da[0].Value : Enum.GetName(type, value); - dictionary.Add((Enum)value, stringValue); - } + for (int index = 0; index < values.Length; index++) + { + var value = values.GetValue(index); + var info = type.GetField(value.ToString()); + var da = (EnumMemberAttribute[])info.GetCustomAttributes(typeof(EnumMemberAttribute), false); + var stringValue = da.Length > 0 ? da[0].Value : Enum.GetName(type, value); + dictionary.Add((Enum)value, stringValue); + } - var isFlag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0; + var isFlag = type.GetCustomAttributes(typeof(FlagsAttribute), false).Length > 0; - return (e) => - { - if (isFlag) - { - var list = new @(Raw("List()")); - foreach(var kv in dictionary) - { - if (e.HasFlag(kv.Key)) list.Add(kv.Value); - } - return string.Join(",", list); - } - else - { - return dictionary[e]; - } - }; - } + return (e) => + { + if (isFlag) + { + var list = new @(Raw("List()")); + foreach(var kv in dictionary) + { + if (e.HasFlag(kv.Key)) list.Add(kv.Value); + } + return string.Join(",", list); + } + else + { + return dictionary[e]; + } + }; + } } diff --git a/src/ApiGenerator/Views/LowLevel/RequestParameters/RequestParameters.cshtml b/src/ApiGenerator/Views/LowLevel/RequestParameters/RequestParameters.cshtml index 929625f6f2..11f96cd4e5 100644 --- a/src/ApiGenerator/Views/LowLevel/RequestParameters/RequestParameters.cshtml +++ b/src/ApiGenerator/Views/LowLevel/RequestParameters/RequestParameters.cshtml @@ -5,8 +5,8 @@ @inherits CodeTemplatePage>> @{ await IncludeAsync("GeneratorNotice", Model); } @{ - var (ns, endpoints) = Model; - ns = ns != CsharpNames.RootNamespace ? $".{CsharpNames.ApiNamespace}.{ns}{CsharpNames.ApiNamespaceSuffix}" : null; + var (ns, endpoints) = Model; + ns = ns != CsharpNames.RootNamespace ? $".{CsharpNames.ApiNamespace}.{ns}{CsharpNames.ApiNamespaceSuffix}" : null; } // ReSharper disable RedundantUsingDirective @@ -21,19 +21,19 @@ namespace OpenSearch.Net@(ns); @foreach (var endpoint in endpoints) { - var r = endpoint.RequestParameterImplementation; - var supportsBody = endpoint.Body != null; - var names = r.CsharpNames; + var r = endpoint.RequestParameterImplementation; + var supportsBody = endpoint.Body != null; + var names = r.CsharpNames; ///Request options for @names.MethodName@(Raw(string.IsNullOrEmpty(r.OfficialDocumentationLink) ? "" : $" {r.OfficialDocumentationLink}")) public partial class @names.ParametersName : RequestParameters<@names.ParametersName> { - public override HttpMethod DefaultHttpMethod => HttpMethod.@r.HttpMethod; - public override bool SupportsBody => @(supportsBody ? "true" : "false"); - @foreach (var param in r.Params) - { - @Raw(param.InitializerGenerator(r.CsharpNames.Namespace, param.TypeLowLevel, param.ClsName, param.QueryStringKey, param.SetterLowLevel, param.Description)) + public override HttpMethod DefaultHttpMethod => HttpMethod.@r.HttpMethod; + public override bool SupportsBody => @(supportsBody ? "true" : "false"); + @foreach (var param in r.Params) + { + @Raw(param.InitializerGenerator(r.CsharpNames.Namespace, param.TypeLowLevel, param.ClsName, param.QueryStringKey, param.SetterLowLevel, param.Description)) - } + } } }