From e71d5be297638d43bde392402d78b99991343ca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Draga=C5=84czuk?= Date: Tue, 14 May 2024 15:14:09 +0200 Subject: [PATCH 1/2] Allow overriding default RouteValues in CQRS API description --- .../CQRSApiDescriptionConfiguration.cs | 16 ++++++++++++++++ .../Registration/CQRSApiDescriptionProvider.cs | 11 ++++++++--- .../ServiceCollectionCQRSExtensions.cs | 10 ++++++++-- .../CQRSApiDescriptionProviderTests.cs | 2 +- 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionConfiguration.cs diff --git a/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionConfiguration.cs b/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionConfiguration.cs new file mode 100644 index 00000000..0ad60cd6 --- /dev/null +++ b/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionConfiguration.cs @@ -0,0 +1,16 @@ +using System.Collections.Frozen; +using Microsoft.AspNetCore.Routing; + +namespace LeanCode.CQRS.AspNetCore.Registration; + +public class CQRSApiDescriptionConfiguration +{ + // Required by Swagger (Swashbuckle) + public static readonly FrozenDictionary DefaultRouteValues = new Dictionary + { + ["controller"] = "CQRS" + }.ToFrozenDictionary(); + + public Func> RouteValuesMapping { get; init; } = + _ => DefaultRouteValues; +} diff --git a/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionProvider.cs b/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionProvider.cs index 1b535437..8905f7d4 100644 --- a/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionProvider.cs +++ b/src/CQRS/LeanCode.CQRS.AspNetCore/Registration/CQRSApiDescriptionProvider.cs @@ -16,12 +16,17 @@ internal sealed class CQRSApiDescriptionProvider : IApiDescriptionProvider private const string ApplicationJson = "application/json"; private readonly EndpointDataSource endpointDataSource; + private readonly CQRSApiDescriptionConfiguration configuration; public int Order => -1200; - public CQRSApiDescriptionProvider(EndpointDataSource endpointDataSource) + public CQRSApiDescriptionProvider( + EndpointDataSource endpointDataSource, + CQRSApiDescriptionConfiguration configuration + ) { this.endpointDataSource = endpointDataSource; + this.configuration = configuration; } public void OnProvidersExecuting(ApiDescriptionProviderContext context) @@ -40,7 +45,7 @@ endpoint is RouteEndpoint routeEndpoint public void OnProvidersExecuted(ApiDescriptionProviderContext context) { } - private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, CQRSObjectMetadata metadata) + private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, CQRSObjectMetadata metadata) { var apiDescription = new ApiDescription { @@ -50,7 +55,7 @@ private static ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, ActionDescriptor = new ActionDescriptor { DisplayName = routeEndpoint.DisplayName, - RouteValues = { ["controller"] = "CQRS", }, // Required by Swagger (Swashbuckle) + RouteValues = configuration.RouteValuesMapping(routeEndpoint), }, }; apiDescription.SupportedRequestFormats.Add(new() { MediaType = ApplicationJson }); diff --git a/src/CQRS/LeanCode.CQRS.AspNetCore/ServiceCollectionCQRSExtensions.cs b/src/CQRS/LeanCode.CQRS.AspNetCore/ServiceCollectionCQRSExtensions.cs index f385392b..16febbfd 100644 --- a/src/CQRS/LeanCode.CQRS.AspNetCore/ServiceCollectionCQRSExtensions.cs +++ b/src/CQRS/LeanCode.CQRS.AspNetCore/ServiceCollectionCQRSExtensions.cs @@ -7,6 +7,7 @@ using LeanCode.CQRS.Security; using LeanCode.CQRS.Validation; using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -36,9 +37,14 @@ TypesCatalog handlersCatalog return new CQRSServicesBuilder(serviceCollection, objectsSource); } - public static IServiceCollection AddCQRSApiExplorer(this IServiceCollection serviceCollection) + public static IServiceCollection AddCQRSApiExplorer( + this IServiceCollection serviceCollection, + CQRSApiDescriptionConfiguration? configuration = null + ) { - serviceCollection.AddTransient(); + serviceCollection.AddTransient(sp => + new(sp.GetRequiredService(), configuration ?? new()) + ); return serviceCollection; } } diff --git a/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Registration/CQRSApiDescriptionProviderTests.cs b/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Registration/CQRSApiDescriptionProviderTests.cs index 44db1675..30e2e47f 100644 --- a/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Registration/CQRSApiDescriptionProviderTests.cs +++ b/test/CQRS/LeanCode.CQRS.AspNetCore.Tests/Registration/CQRSApiDescriptionProviderTests.cs @@ -191,7 +191,7 @@ private static List ListApis(Type forObject) { var dataSource = CreateDataSource(forObject); var context = new ApiDescriptionProviderContext([]); - new CQRSApiDescriptionProvider(dataSource).OnProvidersExecuting(context); + new CQRSApiDescriptionProvider(dataSource, new()).OnProvidersExecuting(context); return context.Results.ToList(); } From 0491e58d4ea48c1027f96480680e35e2f6e593fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emil=20Draga=C5=84czuk?= Date: Tue, 14 May 2024 16:13:15 +0200 Subject: [PATCH 2/2] Adjust API exploration docs section --- docs/external_integrations/api_explorer/index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/external_integrations/api_explorer/index.md b/docs/external_integrations/api_explorer/index.md index faf33106..02020188 100644 --- a/docs/external_integrations/api_explorer/index.md +++ b/docs/external_integrations/api_explorer/index.md @@ -21,6 +21,12 @@ public override void ConfigureServices(IServiceCollection services) } ``` +The `AddCQRSApiExplorer` method has an optional parameter that accepts a configuration override. +For now, it only allows to set a custom mapping of the `RouteValues` parameter, +which is used by OpenAPI generation tools to pass some metadata, by which the endpoints +could be grouped on the OpenAPI UI, for example. Usage of this metadata varies between +different OpenAPI UI implementations, so please check the tools documentation before overriding it. + ## JSON Casing Every integration uses different configuration for the generated payload types. It is highly probable that the default configuration of JSON serializer for these integrations will use wrong property casing. Unfortunately, every tool is configured differently.