Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow endpoints to be excluded by path and/or by deprecated #5075

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions src/NSwag.CodeGeneration.CSharp.Tests/CSharpClientSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class CSharpClientSettingsTests
{
public class FooController : Controller
{
[Obsolete("Testing generation of obsolete endpoints")]
public object GetPerson(bool @override = false)
{
return null;
Expand Down Expand Up @@ -104,6 +105,7 @@ public async Task When_parameter_name_is_reserved_keyword_then_it_is_appended_wi

// Assert
Assert.Contains("Task<object> GetPersonAsync(bool? @override, ", code);
Assert.Contains("Obsolete", code);
}

[Fact]
Expand Down Expand Up @@ -247,5 +249,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<FooController>();
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<FooController>();
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<FooController>();
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<FooController>();
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);
}
}
}
13 changes: 12 additions & 1 deletion src/NSwag.CodeGeneration/ClientGeneratorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NJsonSchema;
using NJsonSchema.CodeGeneration;
using NSwag.CodeGeneration.Models;
using System.Text.RegularExpressions;

namespace NSwag.CodeGeneration
{
Expand Down Expand Up @@ -162,6 +163,17 @@ private List<TOperationModel> 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);

Expand All @@ -181,7 +193,6 @@ private List<TOperationModel> GetOperations(OpenApiDocument document)
operationModel.Path = path;
operationModel.HttpMethod = httpMethod;
operationModel.OperationName = operationName;

result.Add(operationModel);
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/NSwag.CodeGeneration/ClientGeneratorBaseSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,11 @@ public virtual string GenerateControllerName(string controllerName)

/// <summary>Gets or sets the name of the response class (supports the '{controller}' placeholder).</summary>
public string ResponseClass { get; set; }

/// <summary>Gets or sets the value indicating if deprecated endpoints shall be rendered</summary>
public bool ExcludeDeprecated { get; set; }

/// <summary>Gets or sets the regular expression to indicate for which path's client code should be generated (null/empty means all)</summary>
public string ExcludeByPathRegex { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<object> RunAsync(CommandLineProcessor processor, IConsoleHost host)
{
var result = await RunAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<object> RunAsync(CommandLineProcessor processor, IConsoleHost host)
{
var code = await RunAsync();
Expand Down
4 changes: 4 additions & 0 deletions src/NSwag.Sample.NET80Minimal/nswag.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"inlineNamedDictionaries": false,
"inlineNamedAny": false,
"includeHttpContext": false,
"excludeDeprecated": false,
"excludeByPathRegex": null,
"templateDirectory": null,
"serviceHost": null,
"serviceSchemes": null,
Expand Down Expand Up @@ -114,6 +116,8 @@
"useRequestAndResponseSerializationSettings": false,
"serializeTypeInformation": false,
"queryNullValue": "",
"excludeDeprecated": false,
"excludeByPathRegex": null,
"className": "{controller}Client",
"operationGenerationMode": "MultipleClientsFromOperationId",
"additionalNamespaceUsages": [],
Expand Down
4 changes: 4 additions & 0 deletions src/NSwag.Sample.NET90Minimal/nswag.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"inlineNamedDictionaries": false,
"inlineNamedAny": false,
"includeHttpContext": false,
"excludeDeprecated": false,
"excludeByPathRegex": null,
"templateDirectory": null,
"serviceHost": null,
"serviceSchemes": null,
Expand Down Expand Up @@ -114,6 +116,8 @@
"useRequestAndResponseSerializationSettings": false,
"serializeTypeInformation": false,
"queryNullValue": "",
"excludeDeprecated": false,
"excludeByPathRegex": null,
"className": "{controller}Client",
"operationGenerationMode": "MultipleClientsFromOperationId",
"additionalNamespaceUsages": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@
ToolTip="AdditionalNamespaceUsages"
Margin="0,0,0,12" />

<CheckBox IsChecked="{Binding Command.GenerateContractsOutput, Mode=TwoWay}"
ToolTip="GenerateContractsOutput"
Content="Generate contracts output"
Margin="0,0,0,12" />

<CheckBox IsChecked="{Binding Command.GenerateNativeRecords, Mode=TwoWay}"
ToolTip="GenerateNativeRecords"
Content="Generate record types"
Margin="0,0,0,12" />

<CheckBox IsChecked="{Binding Command.GenerateContractsOutput, Mode=TwoWay}"
ToolTip="GenerateContractsOutput"
Content="Generate contracts output"
Margin="0,0,0,12" />

<StackPanel Visibility="{Binding Command.GenerateContractsOutput, Converter={StaticResource VisibilityConverter}}">
<TextBlock Text="Contracts Namespace"
Expand Down Expand Up @@ -162,6 +162,19 @@
ToolTip="SuppressClientInterfacesOutput"
Content="Suppress output of generated interfaces for Client classes" Margin="0,0,0,12" />

<CheckBox IsChecked="{Binding Command.ExcludeDeprecated, Mode=TwoWay}"
Content="Exclude deprecated endpoints"
ToolTip="ExcludeDeprecated"
Margin="0,0,0,12" />

<TextBlock Text="Exclude the following endpoints by their path (via Regex)"
FontWeight="Bold"
Margin="0,0,0,6" />

<TextBox Text="{Binding Command.ExcludeByPathRegex, Mode=TwoWay}"
ToolTip="ExcludeByPathRegex"
Margin="0,0,0,12" />

<TextBlock Text="Base interface for Client Interfaces (optional)" FontWeight="Bold" Margin="0,0,0,6"
Visibility="{Binding Command.GenerateClientInterfaces, Converter={StaticResource VisibilityConverter}}" />
<TextBox Text="{Binding Command.ClientBaseInterface, Mode=TwoWay}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,18 @@
<TextBox ToolTip="ProtectedMethods"
Text="{Binding Command.ProtectedMethods, Mode=TwoWay, Converter={StaticResource StringArrayConverter}, ConverterParameter=','}"
Margin="0,0,0,12" />

<CheckBox IsChecked="{Binding Command.ExcludeDeprecated, Mode=TwoWay}"
Content="Exclude deprecated endpoints"
ToolTip="ExcludeDeprecated"
Margin="0,0,0,12" />

<TextBlock Text="Exclude the following endpoints by their path (via Regex)"
FontWeight="Bold"
Margin="0,0,0,6" />
<TextBox Text="{Binding Command.ExcludeByPathRegex, Mode=TwoWay}"
ToolTip="ExcludeByPathRegex"
Margin="0,0,0,12" />
</StackPanel>
</StackPanel>
</GroupBox>
Expand Down
Loading