From a7e6c13b3bb3e7851056f59efd151c5f0087cb21 Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 12:36:47 -0400 Subject: [PATCH 01/20] POC of fallbacking on OperationId --- src/Workleap.Extensions.OpenAPI.sln | 11 +++ .../OpenApiBuilder.cs | 27 ++++++++ .../DisplayOperationIdOptions.cs | 13 ++++ ...gerDefaultOperationIdToMethodNameFilter.cs | 17 +++++ ...ggerMethodAsOperationIdConfigureOptions.cs | 67 +++++++++++++++++++ .../ServiceCollectionOpenApiExtensions.cs | 17 +++++ .../Workleap.Extensions.OpenAPI.csproj | 28 +++++--- .../WebApi.OpenAPI.SystemTest/Program.cs | 19 ++++++ .../Properties/OperationIdController.cs | 30 +++++++++ .../Properties/launchSettings.json | 38 +++++++++++ .../SwaggerConfigurationExtensions.cs | 21 ++++++ .../SwaggerHostFactory.cs | 32 +++++++++ .../WebApi.OpenAPI.SystemTest.csproj | 26 +++++++ .../appsettings.Development.json | 8 +++ .../appsettings.json | 9 +++ .../WebApi.OpenAPI.SystemTest/openapi-v1.yaml | 30 +++++++++ 16 files changed, 384 insertions(+), 9 deletions(-) create mode 100644 src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs create mode 100644 src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/DisplayOperationIdOptions.cs create mode 100644 src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerDefaultOperationIdToMethodNameFilter.cs create mode 100644 src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs create mode 100644 src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/Program.cs create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/Properties/OperationIdController.cs create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/Properties/launchSettings.json create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/appsettings.json create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml diff --git a/src/Workleap.Extensions.OpenAPI.sln b/src/Workleap.Extensions.OpenAPI.sln index b1951a8..65edcea 100644 --- a/src/Workleap.Extensions.OpenAPI.sln +++ b/src/Workleap.Extensions.OpenAPI.sln @@ -13,6 +13,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{3B3625CC EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workleap.Extensions.OpenAPI.Tests", "Workleap.Extensions.OpenAPI.Tests\Workleap.Extensions.OpenAPI.Tests.csproj", "{2A5429CC-E179-47E7-85BB-C1E33E2AFD8A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1868781E-E97B-447A-AEB7-507739F5FA88}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.OpenAPI.SystemTest", "tests\WebApi.OpenAPI.SystemTest\WebApi.OpenAPI.SystemTest.csproj", "{F7C54BE5-D538-4D2B-9B07-C77567CEB82A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +34,12 @@ Global {2A5429CC-E179-47E7-85BB-C1E33E2AFD8A}.Debug|Any CPU.Build.0 = Debug|Any CPU {2A5429CC-E179-47E7-85BB-C1E33E2AFD8A}.Release|Any CPU.ActiveCfg = Release|Any CPU {2A5429CC-E179-47E7-85BB-C1E33E2AFD8A}.Release|Any CPU.Build.0 = Release|Any CPU + {F7C54BE5-D538-4D2B-9B07-C77567CEB82A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7C54BE5-D538-4D2B-9B07-C77567CEB82A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7C54BE5-D538-4D2B-9B07-C77567CEB82A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7C54BE5-D538-4D2B-9B07-C77567CEB82A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F7C54BE5-D538-4D2B-9B07-C77567CEB82A} = {1868781E-E97B-447A-AEB7-507739F5FA88} EndGlobalSection EndGlobal diff --git a/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs b/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs new file mode 100644 index 0000000..4e6783c --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs @@ -0,0 +1,27 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Swashbuckle.AspNetCore.SwaggerGen; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace Workleap.Extensions.OpenAPI; + +// TODO: Check what should be sealed or not +// TODO: Check if should have interface +public class OpenApiBuilder +{ + private readonly IServiceCollection _services; + + public OpenApiBuilder(IServiceCollection services) + { + this._services = services; + + // Transiant or singleton? + this._services.AddTransient, DisplayOperationIdOptions>(); + } + + public OpenApiBuilder UseMethodNameAsOperationId() + { + this._services.AddTransient, SwaggerMethodAsOperationIdConfigureOptions>(); + return this; + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/DisplayOperationIdOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/DisplayOperationIdOptions.cs new file mode 100644 index 0000000..af0e364 --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/DisplayOperationIdOptions.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.Extensions.Options; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace Workleap.Extensions.OpenAPI; + +public class DisplayOperationIdOptions: IConfigureOptions +{ + public void Configure(SwaggerUIOptions options) + { + options.DisplayOperationId(); + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerDefaultOperationIdToMethodNameFilter.cs b/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerDefaultOperationIdToMethodNameFilter.cs new file mode 100644 index 0000000..fec6c28 --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerDefaultOperationIdToMethodNameFilter.cs @@ -0,0 +1,17 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Workleap.Extensions.OpenAPI; + +public class SwaggerDefaultOperationIdToMethodNameFilter: IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (!string.IsNullOrEmpty(operation.OperationId)) + { + return; + } + + operation.OperationId = context.MethodInfo.Name; + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs new file mode 100644 index 0000000..d662900 --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs @@ -0,0 +1,67 @@ +using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Swashbuckle.AspNetCore.Annotations; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Workleap.Extensions.OpenAPI; + +// TODO : SHould it be an OperationFilter instead?? +internal class SwaggerMethodAsOperationIdConfigureOptions: IConfigureOptions +{ + public void Configure(SwaggerGenOptions options) + { + // options.CustomOperationIds(apiDesc => + // { + // var operationId = HasExplicitOperationId(apiDesc); + // if (!string.IsNullOrEmpty(operationId)) + // { + // return operationId; + // } + // + // return apiDesc.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null; + // }); + + // First it will run the DefaultOperationIdSelector to get from the method + // We can't really garantee order of OperationFilter if ours will run before or after the Swagger Annotation one, but theirs always overwrite so we are good (AnnotationsOperationFilter) + // Unless we take care of configuring EnableAnnotation() + +// private string DefaultOperationIdSelector(ApiDescription apiDescription) +// { +// var actionDescriptor = apiDescription.ActionDescriptor; +// +// // Resolve the operation ID from the route name and fallback to the +// // endpoint name if no route name is available. This allows us to +// // generate operation IDs for endpoints that are defined using +// // minimal APIs. +// #if (!NETSTANDARD2_0) +// return +// actionDescriptor.AttributeRouteInfo?.Name +// ?? (actionDescriptor.EndpointMetadata?.LastOrDefault(m => m is IEndpointNameMetadata) as IEndpointNameMetadata)?.EndpointName; +// #else +// return actionDescriptor.AttributeRouteInfo?.Name; +// #endif +// } + + + options.OperationFilter(); + } + + private string? HasExplicitOperationId(ApiDescription apiDescription) + { + var attributeName = apiDescription.ActionDescriptor.AttributeRouteInfo.Name; + if (!string.IsNullOrEmpty(attributeName)) + { + return attributeName; + } + + var swaggerOperationAttribute = apiDescription.ActionDescriptor.EndpointMetadata.OfType().FirstOrDefault(); + if (swaggerOperationAttribute != null && !string.IsNullOrEmpty(swaggerOperationAttribute.OperationId)) + { + return swaggerOperationAttribute.OperationId; + } + + return null; + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs b/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs new file mode 100644 index 0000000..8030fdf --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Workleap.Extensions.OpenAPI; + +public static class ServiceCollectionOpenApiExtensions +{ + // TODO: Check the setup name + public static OpenApiBuilder AddOpenApi(this IServiceCollection services) + { + if (services == null) + { + throw new ArgumentNullException(nameof(services)); + } + + return new OpenApiBuilder(services); + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj index 4eeb9eb..60e3665 100644 --- a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj +++ b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj @@ -1,6 +1,6 @@ - netstandard2.0 + net6.0;net8.0 true true snupkg @@ -10,16 +10,26 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + + + + + + + + + + + + + + + + diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Program.cs b/src/tests/WebApi.OpenAPI.SystemTest/Program.cs new file mode 100644 index 0000000..2604df0 --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/Program.cs @@ -0,0 +1,19 @@ +using WebApi.OpenAPI.SystemTest; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllers(); + +builder.Services.AddSwagger(); + +var app = builder.Build(); + +// Test with minimal API +app.MapGet("/", () => "Hello World!"); + +app.UseSwagger(); +app.UseSwaggerUI(); + +app.MapControllers(); + +app.Run(); \ No newline at end of file diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Properties/OperationIdController.cs b/src/tests/WebApi.OpenAPI.SystemTest/Properties/OperationIdController.cs new file mode 100644 index 0000000..87da194 --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/Properties/OperationIdController.cs @@ -0,0 +1,30 @@ +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; + +namespace WebApi.OpenAPI.SystemTest.Properties; + +[ApiController] +[Route("OperationId")] +public class OperationIdController: ControllerBase +{ + [HttpGet("/explicitOperationIdInName", Name = "GetExplicitOperationIdInName")] + public IActionResult GetExplicitOperationIdInName() + { + return this.Ok(); + } + + [HttpGet] + [SwaggerOperation(OperationId = "GetExplicitOperationIdInSwagger")] + [Route("/explicitOperationIdInSwagger")] + public IActionResult GetExplicitOperationIdInSwagger() + { + return this.Ok(); + } + + [HttpGet] + [Route("/noOperationId")] + public IActionResult GetNotOperationId() + { + return this.Ok(); + } +} \ No newline at end of file diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Properties/launchSettings.json b/src/tests/WebApi.OpenAPI.SystemTest/Properties/launchSettings.json new file mode 100644 index 0000000..3b7819e --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:46815", + "sslPort": 44307 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5241", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7219;http://localhost:5241", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs new file mode 100644 index 0000000..cbcbf03 --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs @@ -0,0 +1,21 @@ +using Workleap.Extensions.OpenAPI; + +namespace WebApi.OpenAPI.SystemTest; + +public static class SwaggerConfigurationExtensions +{ + public static IServiceCollection AddSwagger(this IServiceCollection services) + { + services.AddControllers(); + + services.AddSwaggerGen(x => + { + x.EnableAnnotations(); + }); + + services.AddOpenApi() + .UseMethodNameAsOperationId(); + + return services; + } +} \ No newline at end of file diff --git a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs new file mode 100644 index 0000000..9aedb1d --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs @@ -0,0 +1,32 @@ +using Workleap.Extensions.OpenAPI; + +namespace WebApi.OpenAPI.SystemTest; + +public static class SwaggerHostFactory +{ + public static IHost CreateHost() + { + return Host + .CreateDefaultBuilder() + .ConfigureWebHostDefaults(UseSwaggerHostStartup) + .UseEnvironment(Environments.Development) + .Build(); + } + + private static void UseSwaggerHostStartup(IWebHostBuilder builder) + { + builder.UseStartup(); + } + + private sealed class SwaggerHostStartup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddSwagger(); + } + + public void Configure(IApplicationBuilder app) + { + } + } +} \ No newline at end of file diff --git a/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj new file mode 100644 index 0000000..7d29ae3 --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj @@ -0,0 +1,26 @@ + + + + net8.0 + enable + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json b/src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/tests/WebApi.OpenAPI.SystemTest/appsettings.json b/src/tests/WebApi.OpenAPI.SystemTest/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml b/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml new file mode 100644 index 0000000..9ffbac3 --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml @@ -0,0 +1,30 @@ +openapi: 3.0.1 +info: + title: WebApi.OpenAPI.SystemTest + version: '1.0' +paths: + /explicitOperationIdInName: + get: + tags: + - OperationId + operationId: GetExplicitOperationIdInName + responses: + '200': + description: Success + /explicitOperationIdInSwagger: + get: + tags: + - OperationId + operationId: GetExplicitOperationIdInSwagger + responses: + '200': + description: Success + /noOperationId: + get: + tags: + - OperationId + operationId: GetNotOperationId + responses: + '200': + description: Success +components: { } \ No newline at end of file From 9cb299a8ea61e1bde61ab59fabaf761c7ea64dc3 Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 13:32:09 -0400 Subject: [PATCH 02/20] Deleting old way that overwrite operation id selector --- .../OpenApiBuilder.cs | 5 +- .../DisplayOperationIdInSwaggerUIOptions.cs} | 2 +- ...gerDefaultOperationIdToMethodNameFilter.cs | 4 +- ...ggerMethodAsOperationIdConfigureOptions.cs | 14 ++++ ...ggerMethodAsOperationIdConfigureOptions.cs | 67 ------------------- .../ServiceCollectionOpenApiExtensions.cs | 5 +- .../Workleap.Extensions.OpenAPI.csproj | 1 - .../OperationIdController.cs | 7 ++ .../SwaggerConfigurationExtensions.cs | 2 +- 9 files changed, 30 insertions(+), 77 deletions(-) rename src/Workleap.Extensions.OpenAPI/{OperationIdConfiguration/DisplayOperationIdOptions.cs => OperationId/DisplayOperationIdInSwaggerUIOptions.cs} (74%) rename src/Workleap.Extensions.OpenAPI/{OperationIdConfiguration => OperationId}/SwaggerDefaultOperationIdToMethodNameFilter.cs (68%) create mode 100644 src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs delete mode 100644 src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs rename src/tests/WebApi.OpenAPI.SystemTest/{Properties => Controllers}/OperationIdController.cs (77%) diff --git a/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs b/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs index 4e6783c..2cd62ab 100644 --- a/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs +++ b/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs @@ -15,11 +15,12 @@ public OpenApiBuilder(IServiceCollection services) { this._services = services; + // TODO: Maybe not do in the constructor // Transiant or singleton? - this._services.AddTransient, DisplayOperationIdOptions>(); + this._services.AddTransient, DisplayOperationIdInSwaggerUIOptions>(); } - public OpenApiBuilder UseMethodNameAsOperationId() + public OpenApiBuilder FallbackOnMethodNameForOperationId() { this._services.AddTransient, SwaggerMethodAsOperationIdConfigureOptions>(); return this; diff --git a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/DisplayOperationIdOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUIOptions.cs similarity index 74% rename from src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/DisplayOperationIdOptions.cs rename to src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUIOptions.cs index af0e364..9e69b08 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/DisplayOperationIdOptions.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUIOptions.cs @@ -4,7 +4,7 @@ namespace Workleap.Extensions.OpenAPI; -public class DisplayOperationIdOptions: IConfigureOptions +internal class DisplayOperationIdInSwaggerUIOptions: IConfigureOptions { public void Configure(SwaggerUIOptions options) { diff --git a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerDefaultOperationIdToMethodNameFilter.cs b/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerDefaultOperationIdToMethodNameFilter.cs similarity index 68% rename from src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerDefaultOperationIdToMethodNameFilter.cs rename to src/Workleap.Extensions.OpenAPI/OperationId/SwaggerDefaultOperationIdToMethodNameFilter.cs index fec6c28..a070b52 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerDefaultOperationIdToMethodNameFilter.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerDefaultOperationIdToMethodNameFilter.cs @@ -3,7 +3,7 @@ namespace Workleap.Extensions.OpenAPI; -public class SwaggerDefaultOperationIdToMethodNameFilter: IOperationFilter +internal class SwaggerDefaultOperationIdToMethodNameFilter: IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { @@ -12,6 +12,8 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) return; } + // Remove Async + // Should we provide an extension points to customize the name? operation.OperationId = context.MethodInfo.Name; } } \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs new file mode 100644 index 0000000..edc7db9 --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Workleap.Extensions.OpenAPI; + +// TODO : SHould it be an OperationFilter instead?? +internal class SwaggerMethodAsOperationIdConfigureOptions: IConfigureOptions +{ + public void Configure(SwaggerGenOptions options) + { + options.OperationFilter(); + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs deleted file mode 100644 index d662900..0000000 --- a/src/Workleap.Extensions.OpenAPI/OperationIdConfiguration/SwaggerMethodAsOperationIdConfigureOptions.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Reflection; -using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Swashbuckle.AspNetCore.Annotations; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Workleap.Extensions.OpenAPI; - -// TODO : SHould it be an OperationFilter instead?? -internal class SwaggerMethodAsOperationIdConfigureOptions: IConfigureOptions -{ - public void Configure(SwaggerGenOptions options) - { - // options.CustomOperationIds(apiDesc => - // { - // var operationId = HasExplicitOperationId(apiDesc); - // if (!string.IsNullOrEmpty(operationId)) - // { - // return operationId; - // } - // - // return apiDesc.TryGetMethodInfo(out MethodInfo methodInfo) ? methodInfo.Name : null; - // }); - - // First it will run the DefaultOperationIdSelector to get from the method - // We can't really garantee order of OperationFilter if ours will run before or after the Swagger Annotation one, but theirs always overwrite so we are good (AnnotationsOperationFilter) - // Unless we take care of configuring EnableAnnotation() - -// private string DefaultOperationIdSelector(ApiDescription apiDescription) -// { -// var actionDescriptor = apiDescription.ActionDescriptor; -// -// // Resolve the operation ID from the route name and fallback to the -// // endpoint name if no route name is available. This allows us to -// // generate operation IDs for endpoints that are defined using -// // minimal APIs. -// #if (!NETSTANDARD2_0) -// return -// actionDescriptor.AttributeRouteInfo?.Name -// ?? (actionDescriptor.EndpointMetadata?.LastOrDefault(m => m is IEndpointNameMetadata) as IEndpointNameMetadata)?.EndpointName; -// #else -// return actionDescriptor.AttributeRouteInfo?.Name; -// #endif -// } - - - options.OperationFilter(); - } - - private string? HasExplicitOperationId(ApiDescription apiDescription) - { - var attributeName = apiDescription.ActionDescriptor.AttributeRouteInfo.Name; - if (!string.IsNullOrEmpty(attributeName)) - { - return attributeName; - } - - var swaggerOperationAttribute = apiDescription.ActionDescriptor.EndpointMetadata.OfType().FirstOrDefault(); - if (swaggerOperationAttribute != null && !string.IsNullOrEmpty(swaggerOperationAttribute.OperationId)) - { - return swaggerOperationAttribute.OperationId; - } - - return null; - } -} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs b/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs index 8030fdf..9d9bfdb 100644 --- a/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs +++ b/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs @@ -7,10 +7,7 @@ public static class ServiceCollectionOpenApiExtensions // TODO: Check the setup name public static OpenApiBuilder AddOpenApi(this IServiceCollection services) { - if (services == null) - { - throw new ArgumentNullException(nameof(services)); - } + ArgumentNullException.ThrowIfNull(services); return new OpenApiBuilder(services); } diff --git a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj index 60e3665..dce10de 100644 --- a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj +++ b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj @@ -27,7 +27,6 @@ - diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Properties/OperationIdController.cs b/src/tests/WebApi.OpenAPI.SystemTest/Controllers/OperationIdController.cs similarity index 77% rename from src/tests/WebApi.OpenAPI.SystemTest/Properties/OperationIdController.cs rename to src/tests/WebApi.OpenAPI.SystemTest/Controllers/OperationIdController.cs index 87da194..a827882 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/Properties/OperationIdController.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/Controllers/OperationIdController.cs @@ -13,6 +13,13 @@ public IActionResult GetExplicitOperationIdInName() return this.Ok(); } + [HttpGet(Name = "GetExplicitOperationIdInNameWithRoute")] + [Route("/explicitOperationIdInSwagger")] + public IActionResult GetExplicitOperationIdInNameWithRouteAttribute() + { + return this.Ok(); + } + [HttpGet] [SwaggerOperation(OperationId = "GetExplicitOperationIdInSwagger")] [Route("/explicitOperationIdInSwagger")] diff --git a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs index cbcbf03..085e8a7 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs @@ -14,7 +14,7 @@ public static IServiceCollection AddSwagger(this IServiceCollection services) }); services.AddOpenApi() - .UseMethodNameAsOperationId(); + .FallbackOnMethodNameForOperationId(); return services; } From df29991cab04265acd8dde8f56e7df652f613796 Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 16:15:01 -0400 Subject: [PATCH 03/20] Cleanup --- src/Directory.Build.props | 7 ++++ ...lbackOperationIdToMethodNameFilterTests.cs | 17 ++++++++ .../UnitTest1.cs | 10 ----- .../Builder/OpenApiBuilder.cs | 39 +++++++++++++++++++ src/Workleap.Extensions.OpenAPI/Class1.cs | 5 --- .../OpenApiBuilder.cs | 28 ------------- ... => OpenApiServiceCollectionExtensions.cs} | 11 +++++- ...> DisplayOperationIdInSwaggerUiOptions.cs} | 2 +- .../FallbackOperationIdToMethodNameFilter.cs | 36 +++++++++++++++++ ...gerDefaultOperationIdToMethodNameFilter.cs | 19 --------- ...ggerMethodAsOperationIdConfigureOptions.cs | 14 ------- .../PublicAPI.Unshipped.txt | 6 ++- .../Workleap.Extensions.OpenAPI.csproj | 17 +++----- .../OperationIdController.cs | 9 +---- .../OperationId/OperationIdMinimalApis.cs | 21 ++++++++++ .../WebApi.OpenAPI.SystemTest/Program.cs | 6 +-- .../SwaggerConfigurationExtensions.cs | 11 ++++-- .../SwaggerHostFactory.cs | 32 --------------- .../WebApi.OpenAPI.SystemTest.csproj | 5 +-- .../WebApi.OpenAPI.SystemTest/openapi-v1.yaml | 19 ++++++++- 20 files changed, 170 insertions(+), 144 deletions(-) create mode 100644 src/Workleap.Extensions.OpenAPI.Tests/FallbackOperationIdToMethodNameFilterTests.cs delete mode 100644 src/Workleap.Extensions.OpenAPI.Tests/UnitTest1.cs create mode 100644 src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs delete mode 100644 src/Workleap.Extensions.OpenAPI/Class1.cs delete mode 100644 src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs rename src/Workleap.Extensions.OpenAPI/{ServiceCollectionOpenApiExtensions.cs => OpenApiServiceCollectionExtensions.cs} (57%) rename src/Workleap.Extensions.OpenAPI/OperationId/{DisplayOperationIdInSwaggerUIOptions.cs => DisplayOperationIdInSwaggerUiOptions.cs} (74%) create mode 100644 src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs delete mode 100644 src/Workleap.Extensions.OpenAPI/OperationId/SwaggerDefaultOperationIdToMethodNameFilter.cs delete mode 100644 src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs rename src/tests/WebApi.OpenAPI.SystemTest/{Controllers => OperationId}/OperationIdController.cs (72%) create mode 100644 src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs delete mode 100644 src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs diff --git a/src/Directory.Build.props b/src/Directory.Build.props index a7d722b..df423b8 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -17,6 +17,13 @@ true + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all diff --git a/src/Workleap.Extensions.OpenAPI.Tests/FallbackOperationIdToMethodNameFilterTests.cs b/src/Workleap.Extensions.OpenAPI.Tests/FallbackOperationIdToMethodNameFilterTests.cs new file mode 100644 index 0000000..4266861 --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI.Tests/FallbackOperationIdToMethodNameFilterTests.cs @@ -0,0 +1,17 @@ +namespace Workleap.Extensions.OpenAPI.Tests; + +public class FallbackOperationIdToMethodNameFilterTests +{ + [Theory] + [InlineData("GetData", "GetData")] + [InlineData("GetDataAsync", "GetData")] + [InlineData("GetDataasync", "GetData")] + public void Given_Method_Name_When_Cleanup_Then_Clean_Name(string methodName, string expectedOutput) + { + // When + var result = FallbackOperationIdToMethodNameFilter.CleanupName(methodName); + + // Then + Assert.Equal(expectedOutput, result); + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI.Tests/UnitTest1.cs b/src/Workleap.Extensions.OpenAPI.Tests/UnitTest1.cs deleted file mode 100644 index b6e8481..0000000 --- a/src/Workleap.Extensions.OpenAPI.Tests/UnitTest1.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Workleap.Extensions.OpenAPI.Tests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - _ = new Class1(); - } -} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs b/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs new file mode 100644 index 0000000..b87a69b --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Swashbuckle.AspNetCore.SwaggerUI; + +namespace Workleap.Extensions.OpenAPI; + +/// +/// Provides methods to configure Swagger/OpenAPI opinionated settings for the application. +/// +public class OpenApiBuilder +{ + private readonly IServiceCollection _services; + + internal OpenApiBuilder(IServiceCollection services) + { + this._services = services; + + this._services.AddSingleton, DisplayOperationIdInSwaggerUiOptions>(); + } + + /// + /// Configures the Swagger generator to fallback on the method name as the operation ID if no explicit operation ID is specified. + /// + /// + /// This method adds a custom operation filter to the Swagger generator. + /// + /// + /// The same instance so that multiple configuration calls can be chained. + /// + public OpenApiBuilder FallbackOnMethodNameForOperationId() + { + this._services.ConfigureSwaggerGen(options => + { + options.OperationFilter(); + }); + + return this; + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/Class1.cs b/src/Workleap.Extensions.OpenAPI/Class1.cs deleted file mode 100644 index a186d53..0000000 --- a/src/Workleap.Extensions.OpenAPI/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Workleap.Extensions.OpenAPI; - -internal sealed class Class1 -{ -} diff --git a/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs b/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs deleted file mode 100644 index 2cd62ab..0000000 --- a/src/Workleap.Extensions.OpenAPI/OpenApiBuilder.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Swashbuckle.AspNetCore.SwaggerGen; -using Swashbuckle.AspNetCore.SwaggerUI; - -namespace Workleap.Extensions.OpenAPI; - -// TODO: Check what should be sealed or not -// TODO: Check if should have interface -public class OpenApiBuilder -{ - private readonly IServiceCollection _services; - - public OpenApiBuilder(IServiceCollection services) - { - this._services = services; - - // TODO: Maybe not do in the constructor - // Transiant or singleton? - this._services.AddTransient, DisplayOperationIdInSwaggerUIOptions>(); - } - - public OpenApiBuilder FallbackOnMethodNameForOperationId() - { - this._services.AddTransient, SwaggerMethodAsOperationIdConfigureOptions>(); - return this; - } -} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs b/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs similarity index 57% rename from src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs rename to src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs index 9d9bfdb..abd47cb 100644 --- a/src/Workleap.Extensions.OpenAPI/ServiceCollectionOpenApiExtensions.cs +++ b/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs @@ -2,9 +2,16 @@ namespace Workleap.Extensions.OpenAPI; -public static class ServiceCollectionOpenApiExtensions +/// +/// TODO +/// +public static class OpenApiServiceCollectionExtensions { - // TODO: Check the setup name + /// + /// TODO + /// + /// + /// public static OpenApiBuilder AddOpenApi(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUIOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs similarity index 74% rename from src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUIOptions.cs rename to src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs index 9e69b08..7c4c3bb 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUIOptions.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs @@ -4,7 +4,7 @@ namespace Workleap.Extensions.OpenAPI; -internal class DisplayOperationIdInSwaggerUIOptions: IConfigureOptions +internal class DisplayOperationIdInSwaggerUiOptions : IConfigureOptions { public void Configure(SwaggerUIOptions options) { diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs new file mode 100644 index 0000000..3f4a24d --- /dev/null +++ b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Workleap.Extensions.OpenAPI; + +internal class FallbackOperationIdToMethodNameFilter : IOperationFilter +{ + public void Apply(OpenApiOperation operation, OperationFilterContext context) + { + if (!string.IsNullOrEmpty(operation.OperationId)) + { + return; + } + + // Method name for Minimal API is not the best choice for OperationId we want to force explicit OperationId + if (IsMinimalApi(context)) + { + return; + } + + // Remove Async + // Should we provide an extension points to customize the name? + operation.OperationId = CleanupName(context.MethodInfo.Name); + } + + private static bool IsMinimalApi(OperationFilterContext context) + { + return !typeof(ControllerBase).IsAssignableFrom(context.MethodInfo.DeclaringType); + } + + internal static string CleanupName(string methodName) + { + return methodName.Replace("Async", string.Empty, StringComparison.InvariantCultureIgnoreCase); + } +} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerDefaultOperationIdToMethodNameFilter.cs b/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerDefaultOperationIdToMethodNameFilter.cs deleted file mode 100644 index a070b52..0000000 --- a/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerDefaultOperationIdToMethodNameFilter.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.OpenApi.Models; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Workleap.Extensions.OpenAPI; - -internal class SwaggerDefaultOperationIdToMethodNameFilter: IOperationFilter -{ - public void Apply(OpenApiOperation operation, OperationFilterContext context) - { - if (!string.IsNullOrEmpty(operation.OperationId)) - { - return; - } - - // Remove Async - // Should we provide an extension points to customize the name? - operation.OperationId = context.MethodInfo.Name; - } -} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs deleted file mode 100644 index edc7db9..0000000 --- a/src/Workleap.Extensions.OpenAPI/OperationId/SwaggerMethodAsOperationIdConfigureOptions.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Swashbuckle.AspNetCore.SwaggerGen; - -namespace Workleap.Extensions.OpenAPI; - -// TODO : SHould it be an OperationFilter instead?? -internal class SwaggerMethodAsOperationIdConfigureOptions: IConfigureOptions -{ - public void Configure(SwaggerGenOptions options) - { - options.OperationFilter(); - } -} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt b/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt index 815c920..c2b6332 100644 --- a/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt +++ b/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt @@ -1 +1,5 @@ -#nullable enable \ No newline at end of file +#nullable enable +static Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions.AddOpenApi(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Workleap.Extensions.OpenAPI.OpenApiBuilder! +Workleap.Extensions.OpenAPI.OpenApiBuilder +Workleap.Extensions.OpenAPI.OpenApiBuilder.FallbackOnMethodNameForOperationId() -> Workleap.Extensions.OpenAPI.OpenApiBuilder! +Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions diff --git a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj index dce10de..d2f758f 100644 --- a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj +++ b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj @@ -13,17 +13,12 @@ - - - - - - - - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Controllers/OperationIdController.cs b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdController.cs similarity index 72% rename from src/tests/WebApi.OpenAPI.SystemTest/Controllers/OperationIdController.cs rename to src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdController.cs index a827882..155c240 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/Controllers/OperationIdController.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdController.cs @@ -5,7 +5,7 @@ namespace WebApi.OpenAPI.SystemTest.Properties; [ApiController] [Route("OperationId")] -public class OperationIdController: ControllerBase +public class OperationIdController : ControllerBase { [HttpGet("/explicitOperationIdInName", Name = "GetExplicitOperationIdInName")] public IActionResult GetExplicitOperationIdInName() @@ -13,13 +13,6 @@ public IActionResult GetExplicitOperationIdInName() return this.Ok(); } - [HttpGet(Name = "GetExplicitOperationIdInNameWithRoute")] - [Route("/explicitOperationIdInSwagger")] - public IActionResult GetExplicitOperationIdInNameWithRouteAttribute() - { - return this.Ok(); - } - [HttpGet] [SwaggerOperation(OperationId = "GetExplicitOperationIdInSwagger")] [Route("/explicitOperationIdInSwagger")] diff --git a/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs new file mode 100644 index 0000000..808c68f --- /dev/null +++ b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs @@ -0,0 +1,21 @@ +namespace WebApi.OpenAPI.SystemTest.Properties; + +public static class OperationIdMinimalApis +{ + public static void AddEndpointsForOperationId(this WebApplication app) + { + app.MapGet("minimal-endpoint-with-name", (() => Results.Ok())) + .WithName("GetMinimalApiWithName") + .WithTags("OperationId") + .WithOpenApi(); + + app.MapGet("minimal-endpoint-with-no-name", () => Results.Ok()) + .WithTags("OperationId") + .WithOpenApi(); + } +} + +public class Test +{ + public int Count { get; set; } +} \ No newline at end of file diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Program.cs b/src/tests/WebApi.OpenAPI.SystemTest/Program.cs index 2604df0..e3da6df 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/Program.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/Program.cs @@ -1,4 +1,5 @@ using WebApi.OpenAPI.SystemTest; +using WebApi.OpenAPI.SystemTest.Properties; var builder = WebApplication.CreateBuilder(args); @@ -8,12 +9,11 @@ var app = builder.Build(); -// Test with minimal API -app.MapGet("/", () => "Hello World!"); +app.AddEndpointsForOperationId(); app.UseSwagger(); app.UseSwaggerUI(); app.MapControllers(); -app.Run(); \ No newline at end of file +app.Run(); diff --git a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs index 085e8a7..2e5e9c2 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs @@ -1,4 +1,5 @@ -using Workleap.Extensions.OpenAPI; +using Microsoft.OpenApi.Models; +using Workleap.Extensions.OpenAPI; namespace WebApi.OpenAPI.SystemTest; @@ -6,11 +7,13 @@ public static class SwaggerConfigurationExtensions { public static IServiceCollection AddSwagger(this IServiceCollection services) { - services.AddControllers(); + // Required to detect Minimal Api Endpoints + services.AddEndpointsApiExplorer(); - services.AddSwaggerGen(x => + services.AddSwaggerGen(options => { - x.EnableAnnotations(); + options.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" }); + options.EnableAnnotations(); }); services.AddOpenApi() diff --git a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs deleted file mode 100644 index 9aedb1d..0000000 --- a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerHostFactory.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Workleap.Extensions.OpenAPI; - -namespace WebApi.OpenAPI.SystemTest; - -public static class SwaggerHostFactory -{ - public static IHost CreateHost() - { - return Host - .CreateDefaultBuilder() - .ConfigureWebHostDefaults(UseSwaggerHostStartup) - .UseEnvironment(Environments.Development) - .Build(); - } - - private static void UseSwaggerHostStartup(IWebHostBuilder builder) - { - builder.UseStartup(); - } - - private sealed class SwaggerHostStartup - { - public void ConfigureServices(IServiceCollection services) - { - services.AddSwagger(); - } - - public void Configure(IApplicationBuilder app) - { - } - } -} \ No newline at end of file diff --git a/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj index 7d29ae3..4186f1d 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj +++ b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj @@ -7,10 +7,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml b/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml index 9ffbac3..b51c8ca 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml +++ b/src/tests/WebApi.OpenAPI.SystemTest/openapi-v1.yaml @@ -1,8 +1,23 @@ openapi: 3.0.1 info: - title: WebApi.OpenAPI.SystemTest - version: '1.0' + title: Test API + version: v1 paths: + /minimal-endpoint-with-name: + get: + tags: + - OperationId + operationId: GetMinimalApiWithName + responses: + '200': + description: OK + /minimal-endpoint-with-no-name: + get: + tags: + - OperationId + responses: + '200': + description: OK /explicitOperationIdInName: get: tags: From 691610e3d41c6c771d0e66652c9142834c61f91f Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 16:36:20 -0400 Subject: [PATCH 04/20] Cleanup --- .../FallbackOperationIdToMethodNameFilterTests.cs | 4 +++- .../Workleap.Extensions.OpenAPI.Tests.csproj | 4 ---- src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs | 3 ++- .../OpenApiServiceCollectionExtensions.cs | 7 +++---- .../OperationId/DisplayOperationIdInSwaggerUiOptions.cs | 2 +- .../OperationId/FallbackOperationIdToMethodNameFilter.cs | 6 ++---- src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt | 6 +++--- .../OperationId/OperationIdController.cs | 2 +- .../OperationId/OperationIdMinimalApis.cs | 2 +- src/tests/WebApi.OpenAPI.SystemTest/Program.cs | 2 +- 10 files changed, 17 insertions(+), 21 deletions(-) rename src/Workleap.Extensions.OpenAPI.Tests/{ => OperationId}/FallbackOperationIdToMethodNameFilterTests.cs (82%) diff --git a/src/Workleap.Extensions.OpenAPI.Tests/FallbackOperationIdToMethodNameFilterTests.cs b/src/Workleap.Extensions.OpenAPI.Tests/OperationId/FallbackOperationIdToMethodNameFilterTests.cs similarity index 82% rename from src/Workleap.Extensions.OpenAPI.Tests/FallbackOperationIdToMethodNameFilterTests.cs rename to src/Workleap.Extensions.OpenAPI.Tests/OperationId/FallbackOperationIdToMethodNameFilterTests.cs index 4266861..51ce32f 100644 --- a/src/Workleap.Extensions.OpenAPI.Tests/FallbackOperationIdToMethodNameFilterTests.cs +++ b/src/Workleap.Extensions.OpenAPI.Tests/OperationId/FallbackOperationIdToMethodNameFilterTests.cs @@ -1,4 +1,6 @@ -namespace Workleap.Extensions.OpenAPI.Tests; +using Workleap.Extensions.OpenAPI.OperationId; + +namespace Workleap.Extensions.OpenAPI.Tests.OperationId; public class FallbackOperationIdToMethodNameFilterTests { diff --git a/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj b/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj index 713e4d8..cee23f8 100644 --- a/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj +++ b/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj @@ -13,10 +13,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs b/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs index b87a69b..497bbe7 100644 --- a/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs +++ b/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs @@ -1,8 +1,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Swashbuckle.AspNetCore.SwaggerUI; +using Workleap.Extensions.OpenAPI.OperationId; -namespace Workleap.Extensions.OpenAPI; +namespace Workleap.Extensions.OpenAPI.Builder; /// /// Provides methods to configure Swagger/OpenAPI opinionated settings for the application. diff --git a/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs b/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs index abd47cb..f7d4c10 100644 --- a/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs +++ b/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs @@ -1,17 +1,16 @@ using Microsoft.Extensions.DependencyInjection; +using Workleap.Extensions.OpenAPI.Builder; namespace Workleap.Extensions.OpenAPI; /// -/// TODO +/// Provides extension methods to the for configuring OpenAPI/Swagger services. /// public static class OpenApiServiceCollectionExtensions { /// - /// TODO + /// Configures OpenAPI/Swagger document generation and SwaggerUI. /// - /// - /// public static OpenApiBuilder AddOpenApi(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs index 7c4c3bb..2f6b2ec 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Options; using Swashbuckle.AspNetCore.SwaggerUI; -namespace Workleap.Extensions.OpenAPI; +namespace Workleap.Extensions.OpenAPI.OperationId; internal class DisplayOperationIdInSwaggerUiOptions : IConfigureOptions { diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs index 3f4a24d..32ae859 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs @@ -2,7 +2,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Workleap.Extensions.OpenAPI; +namespace Workleap.Extensions.OpenAPI.OperationId; internal class FallbackOperationIdToMethodNameFilter : IOperationFilter { @@ -13,14 +13,12 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) return; } - // Method name for Minimal API is not the best choice for OperationId we want to force explicit OperationId + // Method name for Minimal API is not the best choice for OperationId so we want to force explicit declaration if (IsMinimalApi(context)) { return; } - // Remove Async - // Should we provide an extension points to customize the name? operation.OperationId = CleanupName(context.MethodInfo.Name); } diff --git a/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt b/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt index c2b6332..0d47f74 100644 --- a/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt +++ b/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt @@ -1,5 +1,5 @@ #nullable enable -static Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions.AddOpenApi(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Workleap.Extensions.OpenAPI.OpenApiBuilder! -Workleap.Extensions.OpenAPI.OpenApiBuilder -Workleap.Extensions.OpenAPI.OpenApiBuilder.FallbackOnMethodNameForOperationId() -> Workleap.Extensions.OpenAPI.OpenApiBuilder! +static Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions.AddOpenApi(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder! +Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder +Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder.FallbackOnMethodNameForOperationId() -> Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder! Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions diff --git a/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdController.cs b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdController.cs index 155c240..a5c6791 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdController.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; -namespace WebApi.OpenAPI.SystemTest.Properties; +namespace WebApi.OpenAPI.SystemTest.OperationId; [ApiController] [Route("OperationId")] diff --git a/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs index 808c68f..172103b 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs @@ -1,4 +1,4 @@ -namespace WebApi.OpenAPI.SystemTest.Properties; +namespace WebApi.OpenAPI.SystemTest.OperationId; public static class OperationIdMinimalApis { diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Program.cs b/src/tests/WebApi.OpenAPI.SystemTest/Program.cs index e3da6df..718fa7f 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/Program.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/Program.cs @@ -1,5 +1,5 @@ using WebApi.OpenAPI.SystemTest; -using WebApi.OpenAPI.SystemTest.Properties; +using WebApi.OpenAPI.SystemTest.OperationId; var builder = WebApplication.CreateBuilder(args); From 6676b8fb54ca35fae33f152a022ec6efdd5d8453 Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Wed, 24 Apr 2024 16:41:18 -0400 Subject: [PATCH 05/20] Update readme and update dual framework build blocker --- README.md | 16 +++++++++++++++- .../Workleap.Extensions.OpenAPI.csproj | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6264c3b..9586be1 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,22 @@ ## Getting started -TODO +This library's purpose is to provide helpers and guidelines for annotating API endpoints for OpenAPI spec generation. As such, we provide the following features: +- Fallback to use controller name as OperationId when there is no OperationId explicitly defined for the endpoint. + +### How to use it + +Install the package Workleap.Extensions.OpenAPI in your .NET API project. Then you may use the following method to register the required service. Here is a code snippet on how to register this and to enable the operationId fallback feature in your application. + +``` +public void ConfigureServices(IServiceCollection services) +{ + // [...] + services.AddOpenApi().FallbackOnMethodNameForOperationId(); + // [...] +} +``` ## Building, releasing and versioning diff --git a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj index d2f758f..0d0e832 100644 --- a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj +++ b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj @@ -7,6 +7,7 @@ README.md true ../Workleap.Extensions.OpenAPI.snk + false From b162cb40eb66d2521d0e34cac53618e3e07a8b9e Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 16:52:18 -0400 Subject: [PATCH 06/20] Add system test script --- src/Workleap.Extensions.OpenAPI.sln | 4 +++ src/tests/RunSystemTest.ps1 | 31 ++++++++++++++++ src/tests/expected-openapi-document.yaml | 45 ++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 src/tests/RunSystemTest.ps1 create mode 100644 src/tests/expected-openapi-document.yaml diff --git a/src/Workleap.Extensions.OpenAPI.sln b/src/Workleap.Extensions.OpenAPI.sln index 65edcea..ab42386 100644 --- a/src/Workleap.Extensions.OpenAPI.sln +++ b/src/Workleap.Extensions.OpenAPI.sln @@ -14,6 +14,10 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workleap.Extensions.OpenAPI.Tests", "Workleap.Extensions.OpenAPI.Tests\Workleap.Extensions.OpenAPI.Tests.csproj", "{2A5429CC-E179-47E7-85BB-C1E33E2AFD8A}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1868781E-E97B-447A-AEB7-507739F5FA88}" + ProjectSection(SolutionItems) = preProject + tests\RunSystemTest.ps1 = tests\RunSystemTest.ps1 + tests\expected-openapi-document.yaml = tests\expected-openapi-document.yaml + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApi.OpenAPI.SystemTest", "tests\WebApi.OpenAPI.SystemTest\WebApi.OpenAPI.SystemTest.csproj", "{F7C54BE5-D538-4D2B-9B07-C77567CEB82A}" EndProject diff --git a/src/tests/RunSystemTest.ps1 b/src/tests/RunSystemTest.ps1 new file mode 100644 index 0000000..58f40b5 --- /dev/null +++ b/src/tests/RunSystemTest.ps1 @@ -0,0 +1,31 @@ +# Stop the script when a cmdlet or a native command fails +$ErrorActionPreference = 'Stop' +$PSNativeCommandUseErrorActionPreference = $true + +# Define the paths +$projectPath = Join-Path $PSScriptRoot "WebApi.OpenAPI.SystemTest" +$generatedFilePath = Join-Path $projectPath "openapi-v1.yaml" +$expectedFilePath = Join-Path $PSScriptRoot "expected-openapi-document.yaml" + +# Compile the project +dotnet build $projectPath -c Release + +# Check if the build was successful +if ($LASTEXITCODE -ne 0) { + Write-Host "Build failed." + exit $LASTEXITCODE +} + +# Compare the generated file with the expected file +$generatedFileContent = Get-Content -Path $generatedFilePath +$expectedFileContent = Get-Content -Path $expectedFilePath + +$diff = Compare-Object -ReferenceObject $generatedFileContent -DifferenceObject $expectedFileContent + +if ($diff) { + $diff | Format-Table + Write-Error "The generated file does not match the expected file." + exit 1 +} else { + Write-Host "The generated file matches the expected file." +} \ No newline at end of file diff --git a/src/tests/expected-openapi-document.yaml b/src/tests/expected-openapi-document.yaml new file mode 100644 index 0000000..dfff870 --- /dev/null +++ b/src/tests/expected-openapi-document.yaml @@ -0,0 +1,45 @@ +openapi: 3.0.0 +info: + title: Test API + version: v1 +paths: + /minimal-endpoint-with-name: + get: + tags: + - OperationId + operationId: GetMinimalApiWithName2 + responses: + '200': + description: OK + /minimal-endpoint-with-no-name: + get: + tags: + - OperationId + responses: + '200': + description: OK + /explicitOperationIdInName: + get: + tags: + - OperationId + operationId: GetExplicitOperationIdInName + responses: + '200': + description: Success + /explicitOperationIdInSwagger: + get: + tags: + - OperationId + operationId: GetExplicitOperationIdInSwagger + responses: + '200': + description: Success + /noOperationId: + get: + tags: + - OperationId + operationId: GetNotOperationId + responses: + '200': + description: Success +components: { } \ No newline at end of file From d838ff9e50146af2426bffa3c815d8437c5be542 Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Wed, 24 Apr 2024 17:00:56 -0400 Subject: [PATCH 07/20] changed readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9586be1..7965d46 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Getting started -This library's purpose is to provide helpers and guidelines for annotating API endpoints for OpenAPI spec generation. As such, we provide the following features: +This library's provide helpers and guidelines for API endpoints annotations during OpenAPI spec generation. As such, we provide the following features: - Fallback to use controller name as OperationId when there is no OperationId explicitly defined for the endpoint. From cd8471f651c1b20e5abcd27ced49d39beb50d7d1 Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 17:02:01 -0400 Subject: [PATCH 08/20] Fix pack + add system test in pipeline --- .github/workflows/ci.yml | 5 ++++- .../Workleap.Extensions.OpenAPI.Tests.csproj | 2 +- .../WebApi.OpenAPI.SystemTest.csproj | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75c9e00..7308e1d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,4 +28,7 @@ jobs: shell: pwsh env: NUGET_SOURCE: ${{ secrets.NUGET_GSOFTDEV_FEED_URL }} - NUGET_API_KEY: ${{ secrets.GSOFT_NUGET_API_KEY }} \ No newline at end of file + NUGET_API_KEY: ${{ secrets.GSOFT_NUGET_API_KEY }} + + - run: src/tests/RunSystemTest.ps1 + shell: pwsh diff --git a/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj b/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj index cee23f8..2f347ce 100644 --- a/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj +++ b/src/Workleap.Extensions.OpenAPI.Tests/Workleap.Extensions.OpenAPI.Tests.csproj @@ -1,6 +1,6 @@ - net6.0 + net8.0 false true true diff --git a/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj index 4186f1d..dca8c22 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj +++ b/src/tests/WebApi.OpenAPI.SystemTest/WebApi.OpenAPI.SystemTest.csproj @@ -4,6 +4,8 @@ net8.0 enable enable + false + false From 732c41faea878267aa96f2c468bd0c4ffa8c2037 Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Wed, 24 Apr 2024 17:02:04 -0400 Subject: [PATCH 09/20] changed readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7965d46..c4f2414 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Getting started -This library's provide helpers and guidelines for API endpoints annotations during OpenAPI spec generation. As such, we provide the following features: +This library provides helpers and guidelines for API endpoints annotations during OpenAPI spec generation. As such, we provide the following features: - Fallback to use controller name as OperationId when there is no OperationId explicitly defined for the endpoint. From 19e832b2c473922c9a6be1d4f325aecad126996b Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 17:06:00 -0400 Subject: [PATCH 10/20] Cleanup --- src/Workleap.Extensions.OpenAPI.sln | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workleap.Extensions.OpenAPI.sln b/src/Workleap.Extensions.OpenAPI.sln index ab42386..daee2a1 100644 --- a/src/Workleap.Extensions.OpenAPI.sln +++ b/src/Workleap.Extensions.OpenAPI.sln @@ -8,7 +8,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "files", "files", "{3B3625CC-919B-4216-9B50-BCFE297AA184}" ProjectSection(SolutionItems) = preProject Directory.Build.props = Directory.Build.props - .editorconfig = .editorconfig EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Workleap.Extensions.OpenAPI.Tests", "Workleap.Extensions.OpenAPI.Tests\Workleap.Extensions.OpenAPI.Tests.csproj", "{2A5429CC-E179-47E7-85BB-C1E33E2AFD8A}" @@ -45,5 +44,6 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {F7C54BE5-D538-4D2B-9B07-C77567CEB82A} = {1868781E-E97B-447A-AEB7-507739F5FA88} + {2A5429CC-E179-47E7-85BB-C1E33E2AFD8A} = {1868781E-E97B-447A-AEB7-507739F5FA88} EndGlobalSection EndGlobal From 1b7621698124abcdbe702f45958964ebf155492d Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 17:09:02 -0400 Subject: [PATCH 11/20] Fix expected document --- src/tests/expected-openapi-document.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/expected-openapi-document.yaml b/src/tests/expected-openapi-document.yaml index dfff870..b51c8ca 100644 --- a/src/tests/expected-openapi-document.yaml +++ b/src/tests/expected-openapi-document.yaml @@ -1,4 +1,4 @@ -openapi: 3.0.0 +openapi: 3.0.1 info: title: Test API version: v1 @@ -7,7 +7,7 @@ paths: get: tags: - OperationId - operationId: GetMinimalApiWithName2 + operationId: GetMinimalApiWithName responses: '200': description: OK From 9ec0c6fc7900b8406a9cabc1f5a1c305da7c44f1 Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 17:19:06 -0400 Subject: [PATCH 12/20] Update readme --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c4f2414..70b63d4 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,14 @@ ## Getting started -This library provides helpers and guidelines for API endpoints annotations during OpenAPI spec generation. As such, we provide the following features: +The goal of the library is to help generate better OpenApi document with less effort. -- Fallback to use controller name as OperationId when there is no OperationId explicitly defined for the endpoint. +To achieve this, the library offers an opinionated configuration of OpenAPI document generation and SwaggerUI. + +As such, we provide the following features: + +- Display OperationId in SwaggerUI +- (Optional) Fallback to use controller name as OperationId when there is no OperationId explicitly defined for the endpoint. ### How to use it From 55789a12dd24cfbf6c959954675fde1bd91f4bfa Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 17:25:25 -0400 Subject: [PATCH 13/20] CR fixes --- .../OperationId/OperationIdMinimalApis.cs | 5 ----- .../appsettings.Development.json | 8 -------- 2 files changed, 13 deletions(-) delete mode 100644 src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json diff --git a/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs index 172103b..3fde9ec 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/OperationId/OperationIdMinimalApis.cs @@ -14,8 +14,3 @@ public static void AddEndpointsForOperationId(this WebApplication app) .WithOpenApi(); } } - -public class Test -{ - public int Count { get; set; } -} \ No newline at end of file diff --git a/src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json b/src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json deleted file mode 100644 index 0c208ae..0000000 --- a/src/tests/WebApi.OpenAPI.SystemTest/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} From ca99d7a1ef37a5cfb414d473a97f1b74fb20a174 Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Wed, 24 Apr 2024 17:30:21 -0400 Subject: [PATCH 14/20] Publish preview on gsoftdev --- .github/workflows/publish.yml | 4 ++-- .../OperationId/FallbackOperationIdToMethodNameFilter.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a683b5c..02ea5ac 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -24,5 +24,5 @@ jobs: - run: ./Build.ps1 shell: pwsh env: - NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }} - NUGET_API_KEY: ${{ secrets.WORKLEAP_NUGET_API_KEY }} \ No newline at end of file + NUGET_SOURCE: ${{ secrets.NUGET_GSOFTDEV_FEED_URL }} + NUGET_API_KEY: ${{ secrets.GSOFT_NUGET_API_KEY }} \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs index 32ae859..2cab16a 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs @@ -13,7 +13,7 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) return; } - // Method name for Minimal API is not the best choice for OperationId so we want to force explicit declaration + // Method name for Minimal API is not the best choice for OperationId so we want to enforce explicit declaration if (IsMinimalApi(context)) { return; From 6b341d82413351f2a73ef045e7f32e81a6cf6f14 Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Thu, 25 Apr 2024 10:23:14 -0400 Subject: [PATCH 15/20] Address as per feedback --- README.md | 8 ++++---- src/Directory.Build.props | 4 ---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 70b63d4..24cd786 100644 --- a/README.md +++ b/README.md @@ -3,18 +3,18 @@ [![nuget](https://img.shields.io/nuget/v/Workleap.Extensions.OpenAPI.svg?logo=nuget)](https://www.nuget.org/packages/Workleap.Extensions.OpenAPI/) [![build](https://img.shields.io/github/actions/workflow/status/gsoft-inc/wl-extensions-openapi/publish.yml?logo=github&branch=main)](https://github.com/gsoft-inc/wl-extensions-openapi/actions/workflows/publish.yml) -## Getting started +The Workleap.Extensions.OpenAPI library is designed to help generate better OpenApi document with less effort. -The goal of the library is to help generate better OpenApi document with less effort. +## Value proposition and features overview -To achieve this, the library offers an opinionated configuration of OpenAPI document generation and SwaggerUI. +The library offers an opinionated configuration of OpenAPI document generation and SwaggerUI. As such, we provide the following features: - Display OperationId in SwaggerUI - (Optional) Fallback to use controller name as OperationId when there is no OperationId explicitly defined for the endpoint. -### How to use it +## Getting started Install the package Workleap.Extensions.OpenAPI in your .NET API project. Then you may use the following method to register the required service. Here is a code snippet on how to register this and to enable the operationId fallback feature in your application. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index df423b8..49fb8b3 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -25,10 +25,6 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - all runtime; build; native; contentfiles; analyzers From 3841a6e8640a8e3e4c2da0bb72600e338933cdd9 Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Thu, 25 Apr 2024 10:27:00 -0400 Subject: [PATCH 16/20] combine sys test and build.ps1 --- .github/workflows/ci.yml | 2 -- Build.ps1 | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7308e1d..f21fea8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,5 +30,3 @@ jobs: NUGET_SOURCE: ${{ secrets.NUGET_GSOFTDEV_FEED_URL }} NUGET_API_KEY: ${{ secrets.GSOFT_NUGET_API_KEY }} - - run: src/tests/RunSystemTest.ps1 - shell: pwsh diff --git a/Build.ps1 b/Build.ps1 index 1e237cd..8e23eae 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -23,6 +23,7 @@ Process { Exec { & dotnet clean -c Release } Exec { & dotnet build -c Release } Exec { & dotnet test -c Release --no-build --results-directory "$outputDir" --no-restore -l "trx" -l "console;verbosity=detailed" } + Exec { & ".\tests\RunSystemTest.ps1" } Exec { & dotnet pack -c Release --no-build -o "$outputDir" } if (($null -ne $env:NUGET_SOURCE ) -and ($null -ne $env:NUGET_API_KEY)) { From f8a90db7d4f3a527d9c86ace2fb448f4c2b3f252 Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Thu, 25 Apr 2024 10:39:58 -0400 Subject: [PATCH 17/20] Add system tests to build.ps1 --- Build.ps1 | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index 8e23eae..346a4be 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -4,6 +4,8 @@ Begin { $ErrorActionPreference = "stop" } + + Process { function Exec([scriptblock]$Command) { & $Command @@ -11,19 +13,47 @@ Process { throw ("An error occurred while executing command: {0}" -f $Command) } } - + + function Compare-GeneratedAndExpectedFiles { + param( + [Parameter(Mandatory=$true)] + [string]$generatedFilePath, + + [Parameter(Mandatory=$true)] + [string]$expectedFilePath + ) + + # Compare the generated file with the expected file + $generatedFileContent = Get-Content -Path $generatedFilePath + $expectedFileContent = Get-Content -Path $expectedFilePath + $diff = Compare-Object -ReferenceObject $generatedFileContent -DifferenceObject $expectedFileContent + + if ($diff) { + $diff | Format-Table + Write-Error "The generated file does not match the expected file." + exit 1 + } else { + Write-Host "The generated file matches the expected file." + } + } + $workingDir = Join-Path $PSScriptRoot "src" $outputDir = Join-Path $PSScriptRoot ".output" $nupkgsPath = Join-Path $outputDir "*.nupkg" + $projectPath = Join-Path $workingDir "tests/WebApi.OpenAPI.SystemTest" + $generatedFilePath = Join-Path $projectPath "openapi-v1.yaml" + $expectedFilePath = Join-Path $workingDir "tests/expected-openapi-document.yaml" + + try { Push-Location $workingDir Remove-Item $outputDir -Force -Recurse -ErrorAction SilentlyContinue - + Exec { & dotnet clean -c Release } Exec { & dotnet build -c Release } Exec { & dotnet test -c Release --no-build --results-directory "$outputDir" --no-restore -l "trx" -l "console;verbosity=detailed" } - Exec { & ".\tests\RunSystemTest.ps1" } + Exec { & Compare-GeneratedAndExpectedFiles -generatedFilePath $generatedFilePath -expectedFilePath $expectedFilePath } Exec { & dotnet pack -c Release --no-build -o "$outputDir" } if (($null -ne $env:NUGET_SOURCE ) -and ($null -ne $env:NUGET_API_KEY)) { @@ -33,4 +63,4 @@ Process { finally { Pop-Location } -} \ No newline at end of file +} From fad20a9848a5a498c4c8534f584bb47cb8c4372b Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Fri, 26 Apr 2024 08:09:46 -0400 Subject: [PATCH 18/20] Address PR comments --- README.md | 2 +- .../FallbackOperationIdToMethodNameFilterTests.cs | 4 ++-- .../Builder/OpenApiBuilder.cs | 4 ++-- .../OpenApiServiceCollectionExtensions.cs | 2 +- .../DisplayOperationIdInSwaggerUiOptions.cs | 2 +- .../FallbackOperationIdToMethodNameFilter.cs | 13 +++++++++---- .../Workleap.Extensions.OpenAPI.csproj | 3 +-- .../SwaggerConfigurationExtensions.cs | 4 ++-- 8 files changed, 19 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 24cd786..7b667ad 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![nuget](https://img.shields.io/nuget/v/Workleap.Extensions.OpenAPI.svg?logo=nuget)](https://www.nuget.org/packages/Workleap.Extensions.OpenAPI/) [![build](https://img.shields.io/github/actions/workflow/status/gsoft-inc/wl-extensions-openapi/publish.yml?logo=github&branch=main)](https://github.com/gsoft-inc/wl-extensions-openapi/actions/workflows/publish.yml) -The Workleap.Extensions.OpenAPI library is designed to help generate better OpenApi document with less effort. +The `Workleap.Extensions.OpenAPI` library is designed to help generate better OpenApi document with less effort. ## Value proposition and features overview diff --git a/src/Workleap.Extensions.OpenAPI.Tests/OperationId/FallbackOperationIdToMethodNameFilterTests.cs b/src/Workleap.Extensions.OpenAPI.Tests/OperationId/FallbackOperationIdToMethodNameFilterTests.cs index 51ce32f..7c27155 100644 --- a/src/Workleap.Extensions.OpenAPI.Tests/OperationId/FallbackOperationIdToMethodNameFilterTests.cs +++ b/src/Workleap.Extensions.OpenAPI.Tests/OperationId/FallbackOperationIdToMethodNameFilterTests.cs @@ -6,12 +6,12 @@ public class FallbackOperationIdToMethodNameFilterTests { [Theory] [InlineData("GetData", "GetData")] - [InlineData("GetDataAsync", "GetData")] [InlineData("GetDataasync", "GetData")] + [InlineData("GetAsyncDataasync", "GetAsyncData")] public void Given_Method_Name_When_Cleanup_Then_Clean_Name(string methodName, string expectedOutput) { // When - var result = FallbackOperationIdToMethodNameFilter.CleanupName(methodName); + var result = FallbackOperationIdToMethodNameFilter.GenerateOperationIdFromMethodName(methodName); // Then Assert.Equal(expectedOutput, result); diff --git a/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs b/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs index 497bbe7..74fdf94 100644 --- a/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs +++ b/src/Workleap.Extensions.OpenAPI/Builder/OpenApiBuilder.cs @@ -8,7 +8,7 @@ namespace Workleap.Extensions.OpenAPI.Builder; /// /// Provides methods to configure Swagger/OpenAPI opinionated settings for the application. /// -public class OpenApiBuilder +public sealed class OpenApiBuilder { private readonly IServiceCollection _services; @@ -28,7 +28,7 @@ internal OpenApiBuilder(IServiceCollection services) /// /// The same instance so that multiple configuration calls can be chained. /// - public OpenApiBuilder FallbackOnMethodNameForOperationId() + public OpenApiBuilder GenerateMissingOperationId() { this._services.ConfigureSwaggerGen(options => { diff --git a/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs b/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs index f7d4c10..fe9e2e7 100644 --- a/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs +++ b/src/Workleap.Extensions.OpenAPI/OpenApiServiceCollectionExtensions.cs @@ -11,7 +11,7 @@ public static class OpenApiServiceCollectionExtensions /// /// Configures OpenAPI/Swagger document generation and SwaggerUI. /// - public static OpenApiBuilder AddOpenApi(this IServiceCollection services) + public static OpenApiBuilder ConfigureOpenApiGeneration(this IServiceCollection services) { ArgumentNullException.ThrowIfNull(services); diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs index 2f6b2ec..92c2915 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/DisplayOperationIdInSwaggerUiOptions.cs @@ -4,7 +4,7 @@ namespace Workleap.Extensions.OpenAPI.OperationId; -internal class DisplayOperationIdInSwaggerUiOptions : IConfigureOptions +internal sealed class DisplayOperationIdInSwaggerUiOptions : IConfigureOptions { public void Configure(SwaggerUIOptions options) { diff --git a/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs index 2cab16a..716cfb4 100644 --- a/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs +++ b/src/Workleap.Extensions.OpenAPI/OperationId/FallbackOperationIdToMethodNameFilter.cs @@ -4,7 +4,7 @@ namespace Workleap.Extensions.OpenAPI.OperationId; -internal class FallbackOperationIdToMethodNameFilter : IOperationFilter +internal sealed class FallbackOperationIdToMethodNameFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { @@ -19,7 +19,7 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context) return; } - operation.OperationId = CleanupName(context.MethodInfo.Name); + operation.OperationId = GenerateOperationIdFromMethodName(context.MethodInfo.Name); } private static bool IsMinimalApi(OperationFilterContext context) @@ -27,8 +27,13 @@ private static bool IsMinimalApi(OperationFilterContext context) return !typeof(ControllerBase).IsAssignableFrom(context.MethodInfo.DeclaringType); } - internal static string CleanupName(string methodName) + internal static string GenerateOperationIdFromMethodName(string methodName) { - return methodName.Replace("Async", string.Empty, StringComparison.InvariantCultureIgnoreCase); + if (methodName.EndsWith("Async", StringComparison.OrdinalIgnoreCase)) + { + return methodName[..^"Async".Length]; + } + + return methodName; } } \ No newline at end of file diff --git a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj index 0d0e832..f15ec9d 100644 --- a/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj +++ b/src/Workleap.Extensions.OpenAPI/Workleap.Extensions.OpenAPI.csproj @@ -1,13 +1,12 @@ - net6.0;net8.0 + net8.0 true true snupkg README.md true ../Workleap.Extensions.OpenAPI.snk - false diff --git a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs index 2e5e9c2..ebe0746 100644 --- a/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs +++ b/src/tests/WebApi.OpenAPI.SystemTest/SwaggerConfigurationExtensions.cs @@ -16,8 +16,8 @@ public static IServiceCollection AddSwagger(this IServiceCollection services) options.EnableAnnotations(); }); - services.AddOpenApi() - .FallbackOnMethodNameForOperationId(); + services.ConfigureOpenApiGeneration() + .GenerateMissingOperationId(); return services; } From 6b66a4e8299cf13dba4ac5671d4e62304ec06437 Mon Sep 17 00:00:00 2001 From: He Qian Wang Date: Fri, 26 Apr 2024 08:15:39 -0400 Subject: [PATCH 19/20] changes for ci fixes --- Build.ps1 | 2 -- src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Build.ps1 b/Build.ps1 index 346a4be..a4b8884 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -4,8 +4,6 @@ Begin { $ErrorActionPreference = "stop" } - - Process { function Exec([scriptblock]$Command) { & $Command diff --git a/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt b/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt index 0d47f74..c9e803f 100644 --- a/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt +++ b/src/Workleap.Extensions.OpenAPI/PublicAPI.Unshipped.txt @@ -1,5 +1,5 @@ #nullable enable -static Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions.AddOpenApi(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder! +static Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions.ConfigureOpenApiGeneration(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder! Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder -Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder.FallbackOnMethodNameForOperationId() -> Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder! +Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder.GenerateMissingOperationId() -> Workleap.Extensions.OpenAPI.Builder.OpenApiBuilder! Workleap.Extensions.OpenAPI.OpenApiServiceCollectionExtensions From a6cfa73b88426d950b9b3a1e47a9f2013148dc6d Mon Sep 17 00:00:00 2001 From: Mathieu Gamache Date: Fri, 26 Apr 2024 08:23:53 -0400 Subject: [PATCH 20/20] Cleanup readmne --- README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7b667ad..11cf0f6 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ As such, we provide the following features: Install the package Workleap.Extensions.OpenAPI in your .NET API project. Then you may use the following method to register the required service. Here is a code snippet on how to register this and to enable the operationId fallback feature in your application. -``` +```cs public void ConfigureServices(IServiceCollection services) { // [...] - services.AddOpenApi().FallbackOnMethodNameForOperationId(); - // [...] + services.AddOpenApi() + .FallbackOnMethodNameForOperationId(); } ``` @@ -35,7 +35,6 @@ A new *preview* NuGet package is **automatically published** on any new commit o When you are ready to **officially release** a stable NuGet package by following the [SemVer guidelines](https://semver.org/), simply **manually create a tag** with the format `x.y.z`. This will automatically create and publish a NuGet package for this version. - ## License -Copyright © 2024, Workleap This code is licensed under the Apache License, Version 2.0. You may obtain a copy of this license at https://github.com/gsoft-inc/gsoft-license/blob/master/LICENSE. +Copyright © 2024, Workleap This code is licensed under the Apache License, Version 2.0. You may obtain a copy of this license at [License](https://github.com/gsoft-inc/gsoft-license/blob/master/LICENSE).