From 481e230194dec3af1ece519273af51ea6c88ac79 Mon Sep 17 00:00:00 2001 From: knoepfda Date: Mon, 30 Dec 2024 00:31:08 +0100 Subject: [PATCH 1/2] Allow endpoints to be excluded by path and/or by deprecated --- .../CSharpClientSettingsTests.cs | 52 +++++++++++++++++++ .../ClientGeneratorBase.cs | 13 ++++- .../ClientGeneratorBaseSettings.cs | 11 ++++ .../OpenApiToCSharpClientCommand.cs | 14 +++++ .../OpenApiToTypeScriptClientCommand.cs | 14 +++++ src/NSwag.Sample.NET80Minimal/nswag.json | 4 ++ src/NSwag.Sample.NET90Minimal/nswag.json | 4 ++ .../SwaggerToCSharpClientGeneratorView.xaml | 23 ++++++-- ...waggerToTypeScriptClientGeneratorView.xaml | 12 +++++ 9 files changed, 141 insertions(+), 6 deletions(-) diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs index 20cb289665..1cb9810ebb 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs @@ -9,6 +9,9 @@ public class CSharpClientSettingsTests { public class FooController : Controller { +#pragma warning disable S1133 // Deprecated code should be removed + [Obsolete("Testing generation of obsolete endpoints")] +#pragma warning restore S1133 // Deprecated code should be removed public object GetPerson(bool @override = false) { return null; @@ -104,6 +107,7 @@ public async Task When_parameter_name_is_reserved_keyword_then_it_is_appended_wi // Assert Assert.Contains("Task GetPersonAsync(bool? @override, ", code); + Assert.Contains("Obsolete", code); } [Fact] @@ -247,5 +251,53 @@ public async Task When_client_interface_generation_is_enabled_and_suppressed_the Assert.DoesNotContain("public partial interface IFooClient", code); Assert.Contains("public partial class FooClient : IFooClient", code); } + + [Fact] + public async Task When_regex_is_set_to_excluded_endpoints_the_client_will_not_generate_these_endpoint() + { + // Arrange + var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings + { + SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + }); + + var document = await swaggerGenerator.GenerateForControllerAsync(); + string firstPath = document.Paths.Keys.First(); + var generator = new CSharpClientGenerator(document, new CSharpClientGeneratorSettings + { + GenerateClientClasses = true, + ExcludeByPathRegex = firstPath.Replace("/", "\\/").TrimStart('/') // path: "/api/Foo" so corresponding regex is "api\/Foo" + }); + + // Act + var code = generator.GenerateFile(); + + // Assert + Assert.DoesNotContain("GetPerson", code); + } + + [Fact] + public async Task When_depreacted_endpoints_are_excluded_the_client_will_not_generate_these_endpoint() + { + // Arrange + var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings + { + SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + }); + + var document = await swaggerGenerator.GenerateForControllerAsync(); + var generator = new CSharpClientGenerator(document, new CSharpClientGeneratorSettings + { + GenerateClientClasses = true, + ExcludeDeprecated = true + }); + + // Act + var code = generator.GenerateFile(); + + // Assert + Assert.DoesNotContain("GetPerson", code); + Assert.DoesNotContain("Obsolete", code); + } } } \ No newline at end of file diff --git a/src/NSwag.CodeGeneration/ClientGeneratorBase.cs b/src/NSwag.CodeGeneration/ClientGeneratorBase.cs index eccc67c2dd..83da754b6c 100644 --- a/src/NSwag.CodeGeneration/ClientGeneratorBase.cs +++ b/src/NSwag.CodeGeneration/ClientGeneratorBase.cs @@ -9,6 +9,7 @@ using NJsonSchema; using NJsonSchema.CodeGeneration; using NSwag.CodeGeneration.Models; +using System.Text.RegularExpressions; namespace NSwag.CodeGeneration { @@ -162,6 +163,17 @@ private List GetOperations(OpenApiDocument document) var httpMethod = p.Key; var operation = p.Value; + if (this.BaseSettings.ExcludeDeprecated && operation.IsDeprecated) + { + continue; + } + + if (!string.IsNullOrWhiteSpace(this.BaseSettings.ExcludeByPathRegex) && Regex.IsMatch(pair.Key, this.BaseSettings.ExcludeByPathRegex)) + { + continue; + } + + var operationName = BaseSettings.OperationNameGenerator.GetOperationName(document, path, httpMethod, operation); @@ -181,7 +193,6 @@ private List GetOperations(OpenApiDocument document) operationModel.Path = path; operationModel.HttpMethod = httpMethod; operationModel.OperationName = operationName; - result.Add(operationModel); } } diff --git a/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs b/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs index 88d40e2589..ecdb6e10a2 100644 --- a/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs +++ b/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs @@ -85,5 +85,16 @@ public virtual string GenerateControllerName(string controllerName) /// Gets or sets the name of the response class (supports the '{controller}' placeholder). public string ResponseClass { get; set; } + + //------- + /// Gets or sets the value indicating if deprecated endpoints shall be rendered + public bool ExcludeDeprecated { get; set; } + + /// Gets or sets the regular expression to indicate for which path's client code should be generated (null/empty means all) + public string ExcludeByPathRegex { get; set; } + + + + } } \ No newline at end of file diff --git a/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs b/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs index 4b6a248011..5b0084288e 100644 --- a/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs +++ b/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToCSharpClientCommand.cs @@ -261,6 +261,20 @@ public string QueryNullValue set => Settings.QueryNullValue = value; } + [Argument(Name = "ExcludeDeprecated", IsRequired = false, Description = "Specifies if deprecated endpoints should be generated")] + public bool ExcludeDeprecated + { + get { return Settings.ExcludeDeprecated; } + set { Settings.ExcludeDeprecated = value; } + } + + [Argument(Name = "ExcludeByPathRegex", IsRequired = false, Description = "The regex which defines endpoints should not be genereated (regex is applied to path)")] + public string ExcludeByPathRegex + { + get { return Settings.ExcludeByPathRegex; } + set { Settings.ExcludeByPathRegex = value; } + } + public override async Task RunAsync(CommandLineProcessor processor, IConsoleHost host) { var result = await RunAsync(); diff --git a/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToTypeScriptClientCommand.cs b/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToTypeScriptClientCommand.cs index 01d96e0132..f32c28196e 100644 --- a/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToTypeScriptClientCommand.cs +++ b/src/NSwag.Commands/Commands/CodeGeneration/OpenApiToTypeScriptClientCommand.cs @@ -392,6 +392,20 @@ public bool IncludeHttpContext set => Settings.IncludeHttpContext = value; } + [Argument(Name = "ExcludeDeprecated", IsRequired = false, Description = "Specifies if deprecated endpoints should be generated")] + public bool ExcludeDeprecated + { + get { return Settings.ExcludeDeprecated; } + set { Settings.ExcludeDeprecated = value; } + } + + [Argument(Name = "ExcludeByPathRegex", IsRequired = false, Description = "The regex which defines endpoints should not be genereated (regex is applied to path)")] + public string ExcludeByPathRegex + { + get { return Settings.ExcludeByPathRegex; } + set { Settings.ExcludeByPathRegex = value; } + } + public override async Task RunAsync(CommandLineProcessor processor, IConsoleHost host) { var code = await RunAsync(); diff --git a/src/NSwag.Sample.NET80Minimal/nswag.json b/src/NSwag.Sample.NET80Minimal/nswag.json index 3c1f7acba1..1d6ff9ff29 100644 --- a/src/NSwag.Sample.NET80Minimal/nswag.json +++ b/src/NSwag.Sample.NET80Minimal/nswag.json @@ -73,6 +73,8 @@ "inlineNamedDictionaries": false, "inlineNamedAny": false, "includeHttpContext": false, + "excludeDeprecated": false, + "excludeByPathRegex": null, "templateDirectory": null, "serviceHost": null, "serviceSchemes": null, @@ -114,6 +116,8 @@ "useRequestAndResponseSerializationSettings": false, "serializeTypeInformation": false, "queryNullValue": "", + "excludeDeprecated": false, + "excludeByPathRegex": null, "className": "{controller}Client", "operationGenerationMode": "MultipleClientsFromOperationId", "additionalNamespaceUsages": [], diff --git a/src/NSwag.Sample.NET90Minimal/nswag.json b/src/NSwag.Sample.NET90Minimal/nswag.json index 720f576a37..de153f33f7 100644 --- a/src/NSwag.Sample.NET90Minimal/nswag.json +++ b/src/NSwag.Sample.NET90Minimal/nswag.json @@ -73,6 +73,8 @@ "inlineNamedDictionaries": false, "inlineNamedAny": false, "includeHttpContext": false, + "excludeDeprecated": false, + "excludeByPathRegex": null, "templateDirectory": null, "serviceHost": null, "serviceSchemes": null, @@ -114,6 +116,8 @@ "useRequestAndResponseSerializationSettings": false, "serializeTypeInformation": false, "queryNullValue": "", + "excludeDeprecated": false, + "excludeByPathRegex": null, "className": "{controller}Client", "operationGenerationMode": "MultipleClientsFromOperationId", "additionalNamespaceUsages": [], diff --git a/src/NSwagStudio/Views/CodeGenerators/SwaggerToCSharpClientGeneratorView.xaml b/src/NSwagStudio/Views/CodeGenerators/SwaggerToCSharpClientGeneratorView.xaml index 4de29841ac..2b33880325 100644 --- a/src/NSwagStudio/Views/CodeGenerators/SwaggerToCSharpClientGeneratorView.xaml +++ b/src/NSwagStudio/Views/CodeGenerators/SwaggerToCSharpClientGeneratorView.xaml @@ -44,15 +44,15 @@ ToolTip="AdditionalNamespaceUsages" Margin="0,0,0,12" /> - - + + + + + + + + + + + + + From 9862560b4bb48d0a63d7ed5262fa0ec1ba0a9747 Mon Sep 17 00:00:00 2001 From: knoepfda Date: Mon, 30 Dec 2024 17:08:16 +0100 Subject: [PATCH 2/2] Settings: ExcludeDeprecated + ExcludeByPathRegex: Tests for typescript added --- .../CSharpClientSettingsTests.cs | 2 - .../TypeScriptClientSettingTests.cs | 80 +++++++++++++++++++ .../ClientGeneratorBaseSettings.cs | 5 -- 3 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptClientSettingTests.cs diff --git a/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs b/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs index 1cb9810ebb..d2083b6e6b 100644 --- a/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs +++ b/src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs @@ -9,9 +9,7 @@ public class CSharpClientSettingsTests { public class FooController : Controller { -#pragma warning disable S1133 // Deprecated code should be removed [Obsolete("Testing generation of obsolete endpoints")] -#pragma warning restore S1133 // Deprecated code should be removed public object GetPerson(bool @override = false) { return null; diff --git a/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptClientSettingTests.cs b/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptClientSettingTests.cs new file mode 100644 index 0000000000..812ace8a23 --- /dev/null +++ b/src/NSwag.CodeGeneration.TypeScript.Tests/TypeScriptClientSettingTests.cs @@ -0,0 +1,80 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using NJsonSchema.CodeGeneration.TypeScript; +using NSwag.Generation.WebApi; +using System.Runtime.Serialization; +using Xunit; +using NJsonSchema.NewtonsoftJson.Converters; +using NJsonSchema; +using NJsonSchema.NewtonsoftJson.Generation; +using static NSwag.CodeGeneration.TypeScript.Tests.OperationParameterTests; + +namespace NSwag.CodeGeneration.TypeScript.Tests +{ + public class TypeScriptClientSettingTests + { + public class FooController + { + [Route("test")] + public string Test(int a, int? b) + { + return null; + } + + [Obsolete("Obsolete endpoint for testing")] + [Route("obsoleteEndpoint")] + public string ObsoleteEndpoint(int a, int? b) + { + return null; + } + } + + [Fact] + public async Task When_depreacted_endpoints_are_excluded_the_client_will_not_generate_these_endpoint() + { + // Arrange + var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings + { + SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + }); + + var document = await swaggerGenerator.GenerateForControllerAsync(); + var generator = new TypeScriptClientGenerator(document, new TypeScriptClientGeneratorSettings + { + ExcludeDeprecated = true + }); + + // Act + var code = generator.GenerateFile(); + + // Assert + Assert.DoesNotContain("obsoleteEndpoint", code); + Assert.DoesNotContain("deprecated", code); + Assert.Contains("test", code); // contains other endpoint + } + + [Fact] + public async Task When_regex_is_set_to_excluded_endpoints_the_client_will_not_generate_these_endpoint() + { + // Arrange + var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings + { + SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings() + }); + + var document = await swaggerGenerator.GenerateForControllerAsync(); + var generator = new TypeScriptClientGenerator(document, new TypeScriptClientGeneratorSettings + { + ExcludeByPathRegex = "test" + }); + + // Act + var code = generator.GenerateFile(); + + // Assert + Assert.DoesNotContain("foo", code); + Assert.Contains("obsoleteEndpoint", code); // contains other endpoint + Assert.Contains("deprecated", code); + } + } +} diff --git a/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs b/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs index ecdb6e10a2..2e2c26a923 100644 --- a/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs +++ b/src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs @@ -86,15 +86,10 @@ public virtual string GenerateControllerName(string controllerName) /// Gets or sets the name of the response class (supports the '{controller}' placeholder). public string ResponseClass { get; set; } - //------- /// Gets or sets the value indicating if deprecated endpoints shall be rendered public bool ExcludeDeprecated { get; set; } /// Gets or sets the regular expression to indicate for which path's client code should be generated (null/empty means all) public string ExcludeByPathRegex { get; set; } - - - - } } \ No newline at end of file