Skip to content

Commit

Permalink
Add new settings Sync/AsyncMethodFormat
Browse files Browse the repository at this point in the history
  • Loading branch information
david-brink-talogy committed Mar 13, 2024
1 parent 0c9d87f commit d07f1d3
Show file tree
Hide file tree
Showing 13 changed files with 120 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Also, since you've come to this repo, we'll assume you want to use NSwag as part
- [OpenAPI Swagger Editor VS Code Extension](https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi) *(optional)* - This Visual Studio Code (VS Code) extension adds rich support for the OpenAPI Specification (OAS) (formerly known as Swagger Specification) in JSON or YAML format. The features include, for example, SwaggerUI and ReDoc preview, IntelliSense, linting, schema enforcement, code navigation, definition links, snippets, static security analysis, and more!
- If in later steps you choose to download the 3rd-Party Service's Open API Spec, this plugin makes it easy visualize

**Notes**:
**Notes**:

- You may need to specify runtime version `nswag version /runtime:Net50` to run nswag on your local machine, since the sample [nswag config](https://github.com/RicoSuter/NSwag/wiki/NSwag-Configuration-Document) we'll use specifies `runtime` as `Net50`.
- If you chose to download NSwag as a ZIP Archive, you may see dotnet version errors when trying to execute commands. If you are not able to resolve the issues, you may opt to install via Chocolatey or the MSI from the install instructions page provided above.
Expand Down Expand Up @@ -73,7 +73,7 @@ nswag run sample.nswag /runtime:Net50
"output": null,
"newLineBehavior": "Auto"
}
},
},
"codeGenerators": {
"openApiToCSharpClient": {
"generateClientClasses": true,
Expand Down Expand Up @@ -121,7 +121,7 @@ nswag run sample.nswag /runtime:Net50
"timeType": "System.TimeSpan",
"timeSpanType": "System.TimeSpan",
"arrayType": "System.Collections.ObjectModel.ObservableCollection",
"arrayInstanceType": "System.Collections.ObjectModel.ObservableCollection",
"arrayInstanceType": "System.Collections.ObjectModel.ObservableCollection",
"dictionaryType": "System.Collections.Generic.Dictionary",
"arrayBaseType": "System.Collections.ObjectModel.ObservableCollection",
"dictionaryBaseType": "System.Collections.Generic.Dictionary",
Expand All @@ -132,6 +132,8 @@ nswag run sample.nswag /runtime:Net50
"handleReferences": false,
"generateImmutableArrayProperties": false,
"generateImmutableDictionaryProperties": false,
"asyncMethodFormat": "{0}Async",
"syncMethodFormat": "{0}",
"output": "GENERATEDCODE.cs"
}
}
Expand Down
Binary file modified docs/tutorials/GenerateProxyClientWithCLI/sample.nswag
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
<NSwagServiceSchemes>$(NSwagServiceSchemes)</NSwagServiceSchemes>
<NSwagOutput>$(NSwagOutput)</NSwagOutput>
<NSwagNewLineBehavior>$(NSwagNewLineBehavior)</NSwagNewLineBehavior>
<NSwagAsyncMethodFormat>$(NSwagAsyncMethodFormat)</NSwagAsyncMethodFormat>
<NSwagSyncMethodFormat>$(NSwagSyncMethodFormat)</NSwagSyncMethodFormat>
</OpenApiReference>
<OpenApiProjectReference>
<CodeGenerator>NSwagCSharp</CodeGenerator>
Expand Down Expand Up @@ -177,6 +179,8 @@
<NSwagServiceSchemes>$(NSwagServiceSchemes)</NSwagServiceSchemes>
<NSwagOutput>$(NSwagOutput)</NSwagOutput>
<NSwagNewLineBehavior>$(NSwagNewLineBehavior)</NSwagNewLineBehavior>
<NSwagAsyncMethodFormat>$(NSwagAsyncMethodFormat)</NSwagAsyncMethodFormat>
<NSwagSyncMethodFormat>$(NSwagSyncMethodFormat)</NSwagSyncMethodFormat>
</OpenApiProjectReference>
</ItemDefinitionGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,13 @@
<CurrentOpenApiReference>
<Command Condition="'%(NSwagNewLineBehavior)' != ''">%(Command) /newLineBehavior:%(NSwagNewLineBehavior)</Command>
</CurrentOpenApiReference>

<CurrentOpenApiReference>
<Command Condition="'%(NSwagAsyncMethodFormat)' != ''">%(Command) /asyncMethodFormat:%(NSwagAsyncMethodFormat)</Command>
</CurrentOpenApiReference>
<CurrentOpenApiReference>
<Command Condition="'%(NSwagSyncMethodFormat)' != ''">%(Command) /syncMethodFormat:%(NSwagSyncMethodFormat)</Command>
</CurrentOpenApiReference>

</ItemGroup>

<Message Importance="high" Text="%0AGenerateNSwagCSharp:" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using NJsonSchema.Generation;
using NJsonSchema.NewtonsoftJson.Generation;
using NSwag.Generation.WebApi;
using Xunit;
Expand Down Expand Up @@ -249,5 +249,52 @@ 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_async_method_format_specified_then_methods_use_it()
{
// Arrange
var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings
{
SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings()
});

var document = await swaggerGenerator.GenerateForControllerAsync<FooController>();
var generator = new CSharpClientGenerator(document, new CSharpClientGeneratorSettings
{
GenerateClientInterfaces = true,
AsyncMethodFormat = "{0}Asynchronous"
});

// Act
var code = generator.GenerateFile();

// Assert
Assert.Equal(4, Regex.Matches(code, @"Task<object> GetPersonAsynchronous\(").Count);
}

[Fact]
public async Task When_sync_method_format_specified_then_methods_use_it()
{
// Arrange
var swaggerGenerator = new WebApiOpenApiDocumentGenerator(new WebApiOpenApiDocumentGeneratorSettings
{
SchemaSettings = new NewtonsoftJsonSchemaGeneratorSettings()
});

var document = await swaggerGenerator.GenerateForControllerAsync<FooController>();
var generator = new CSharpClientGenerator(document, new CSharpClientGeneratorSettings
{
GenerateClientInterfaces = true,
GenerateSyncMethods = true,
SyncMethodFormat = "{0}Synchronous"
});

// Act
var code = generator.GenerateFile();

// Assert
Assert.Equal(2, Regex.Matches(code, @"object GetPersonSynchronous\(").Count);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,11 @@ public CSharpClientGeneratorSettings()

/// <summary>Gets or sets a value indicating whether to expose the JsonSerializerSettings property (default: false).</summary>
public bool ExposeJsonSerializerSettings { get; set; }

/// <summary>Gets or sets the format for asynchronous methods (default: "{0}Async").</summary>
public string AsyncMethodFormat { get; set; } = "{0}Async";

/// <summary>Gets or sets the format for synchronous methods (default: "{0}").</summary>
public string SyncMethodFormat { get; set; } = "{0}";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ public CSharpClientTemplateModel(
/// </summary>
public bool GeneratePrepareRequestAndProcessResponseAsAsyncMethods => _settings.GeneratePrepareRequestAndProcessResponseAsAsyncMethods;

/// <summary>Gets the format for asynchronous methods.</summary>
public string AsyncMethodFormat => _settings.AsyncMethodFormat;

/// <summary>Gets the format for synchronous methods.</summary>
public string SyncMethodFormat => _settings.SyncMethodFormat;

/// <summary>Gets the JSON serializer parameter code.</summary>
public string JsonSerializerParameterCode
{
Expand Down
10 changes: 5 additions & 5 deletions src/NSwag.CodeGeneration.CSharp/Templates/Client.Class.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -125,25 +125,25 @@
{% if GenerateOptionalParameters == false -%}
{% template Client.Method.Documentation %}
{% template Client.Method.Annotations %}
{{ operation.MethodAccessModifier }} virtual {{ operation.ResultType }} {{ operation.ActualOperationName }}Async({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %})
{{ operation.MethodAccessModifier }} virtual {{ operation.ResultType }} {{ AsyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %})
{
return {{ operation.ActualOperationName }}Async({% for parameter in operation.Parameters %}{{ parameter.VariableName }}, {% endfor %}System.Threading.CancellationToken.None);
return {{ AsyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.VariableName }}, {% endfor %}System.Threading.CancellationToken.None);
}

{% endif -%}
{% if GenerateSyncMethods -%}
{% template Client.Method.Documentation %}
{% template Client.Method.Annotations %}
{{ operation.MethodAccessModifier }} virtual {{ operation.SyncResultType }} {{ operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %})
{{ operation.MethodAccessModifier }} virtual {{ operation.SyncResultType }} {{ SyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %})
{
{% if operation.HasResult or operation.WrapResponse %}return {% endif %}System.Threading.Tasks.Task.Run(async () => await {{ operation.ActualOperationName }}Async({% for parameter in operation.Parameters %}{{ parameter.VariableName }}, {% endfor %}System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
{% if operation.HasResult or operation.WrapResponse %}return {% endif %}System.Threading.Tasks.Task.Run(async () => await {{ AsyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.VariableName }}, {% endfor %}System.Threading.CancellationToken.None)).GetAwaiter().GetResult();
}

{% endif -%}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
{% template Client.Method.Documentation %}
{% template Client.Method.Annotations %}
{{ operation.MethodAccessModifier }} virtual async {{ operation.ResultType }} {{ operation.ActualOperationName }}Async({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}, {% endfor %}System.Threading.CancellationToken cancellationToken{% if GenerateOptionalParameters %} = default(System.Threading.CancellationToken){% endif %})
{{ operation.MethodAccessModifier }} virtual async {{ operation.ResultType }} {{ AsyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}, {% endfor %}System.Threading.CancellationToken cancellationToken{% if GenerateOptionalParameters %} = default(System.Threading.CancellationToken){% endif %})
{
{% for parameter in operation.PathParameters -%}
{% if parameter.IsNullable == false and parameter.IsRequired -%}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ public partial interface I{{ Class }}{% if HasClientBaseInterface %} : {{ Client
{% if GenerateOptionalParameters == false -%}
{% template Client.Method.Documentation %}
{% template Client.Method.Annotations %}
{{ operation.ResultType }} {{ operation.ActualOperationName }}Async({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %});
{{ operation.ResultType }} {{ AsyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %});

{% endif -%}
{% if GenerateSyncMethods -%}
{% template Client.Method.Documentation %}
{% template Client.Method.Annotations %}
{{ operation.SyncResultType }} {{ operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %});
{{ operation.SyncResultType }} {{ SyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}{% if parameter.IsLast == false %}, {% endif %}{% endfor %});

{%- endif %}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
{% template Client.Method.Documentation %}
{% template Client.Method.Annotations %}
{{ operation.ResultType }} {{ operation.ActualOperationName }}Async({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}, {% endfor %}System.Threading.CancellationToken cancellationToken{% if GenerateOptionalParameters %} = default(System.Threading.CancellationToken){% endif %});
{{ operation.ResultType }} {{ AsyncMethodFormat | format_string: operation.ActualOperationName }}({% for parameter in operation.Parameters %}{{ parameter.Type }} {{ parameter.VariableName }}{% if GenerateOptionalParameters and parameter.IsOptional %} = null{% endif %}, {% endfor %}System.Threading.CancellationToken cancellationToken{% if GenerateOptionalParameters %} = default(System.Threading.CancellationToken){% endif %});

{% endfor -%}
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,22 @@ public string QueryNullValue
set { Settings.QueryNullValue = value; }
}

[Argument(Name = "AsyncMethodFormat", IsRequired = false,
Description = "Specifies the format for asynchronous methods (default: '{0}Async').")]
public string AsyncMethodFormat
{
get { return Settings.AsyncMethodFormat; }
set { Settings.AsyncMethodFormat = value; }
}

[Argument(Name = "SyncMethodFormat", IsRequired = false,
Description = "Specifies the format for synchronous methods (default: '{0}').")]
public string SyncMethodFormat
{
get { return Settings.SyncMethodFormat; }
set { Settings.SyncMethodFormat = value; }
}

public override async Task<object> RunAsync(CommandLineProcessor processor, IConsoleHost host)
{
var result = await RunAsync();
Expand Down
10 changes: 7 additions & 3 deletions src/NSwag.Sample.NET70Minimal/nswag.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@
"serviceHost": null,
"serviceSchemes": null,
"output": "GeneratedClientsCs.gen",
"newLineBehavior": "Auto"
"newLineBehavior": "Auto",
"asyncMethodFormat": "{0}Async",
"syncMethodFormat": "{0}"
},
"openApiToCSharpController": {
"controllerBaseClass": null,
Expand Down Expand Up @@ -225,7 +227,9 @@
"serviceHost": null,
"serviceSchemes": null,
"output": "GeneratedControllersCs.gen",
"newLineBehavior": "Auto"
"newLineBehavior": "Auto",
"asyncMethodFormat": "{0}Async",
"syncMethodFormat": "{0}"
}
}
}
}
10 changes: 7 additions & 3 deletions src/NSwag.Sample.NET80Minimal/nswag.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@
"serviceHost": null,
"serviceSchemes": null,
"output": "GeneratedClientsCs.gen",
"newLineBehavior": "Auto"
"newLineBehavior": "Auto",
"asyncMethodFormat": "{0}Async",
"syncMethodFormat": "{0}"
},
"openApiToCSharpController": {
"controllerBaseClass": null,
Expand Down Expand Up @@ -225,7 +227,9 @@
"serviceHost": null,
"serviceSchemes": null,
"output": "GeneratedControllersCs.gen",
"newLineBehavior": "Auto"
"newLineBehavior": "Auto",
"asyncMethodFormat": "{0}Async",
"syncMethodFormat": "{0}"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@
<TextBox Text="{Binding Command.QueryNullValue, Mode=TwoWay}"
ToolTip="QueryNullValue" Margin="0,0,0,12" />

<TextBlock Text="Format for asynchronous methods" FontWeight="Bold" Margin="0,0,0,6" />
<TextBox Text="{Binding Command.AsyncMethodFormat, Mode=TwoWay}" ToolTip="AsyncMethodFormat" Margin="0,0,0,12" />

<TextBlock Text="Format for synchronous methods" FontWeight="Bold" Margin="0,0,0,6" />
<TextBox Text="{Binding Command.SyncMethodFormat, Mode=TwoWay}" ToolTip="SyncMethodFormat" Margin="0,0,0,12" />

<GroupBox Header="Base Class and Configuration Class" Margin="0,0,0,12">
<StackPanel Margin="4,8,4,-8">
<TextBlock Text="Base Class Name (optional)" FontWeight="Bold" Margin="0,0,0,6" />
Expand Down

0 comments on commit d07f1d3

Please sign in to comment.