From 233f0257ad8700b2020547ea9d130c928cbaac17 Mon Sep 17 00:00:00 2001 From: Artemiy Izakov Date: Fri, 24 Nov 2023 13:26:06 +0500 Subject: [PATCH] fix(autodoc): don't override GroupName if it's already specified --- .../JsonRpcDescriptionProvider.cs | 5 ++++- src/Tochka.JsonRpc.Client/Extensions.cs | 8 +++++--- .../DependencyInjectionExtensions.cs | 5 +++++ .../JsonRpcDescriptionProviderTests.cs | 19 +++++++++++++++++++ .../IntegrationTests.cs | 12 ++++++++++++ .../DependencyInjectionExtensionsTests.cs | 3 ++- .../IntegrationTests.cs | 1 + .../CustomGroupNameJsonRpcController.cs | 10 ++++++++++ .../Program.cs | 9 ++++++++- 9 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/CustomGroupNameJsonRpcController.cs diff --git a/src/Tochka.JsonRpc.ApiExplorer/JsonRpcDescriptionProvider.cs b/src/Tochka.JsonRpc.ApiExplorer/JsonRpcDescriptionProvider.cs index 65108b1f..3dafe067 100644 --- a/src/Tochka.JsonRpc.ApiExplorer/JsonRpcDescriptionProvider.cs +++ b/src/Tochka.JsonRpc.ApiExplorer/JsonRpcDescriptionProvider.cs @@ -62,7 +62,10 @@ public void OnProvidersExecuting(ApiDescriptionProviderContext context) var serializerMetadata = actionDescriptor.EndpointMetadata.Get(); var serializerOptionsProviderType = serializerMetadata?.ProviderType; - description.GroupName = ApiExplorerUtils.GetDocumentName(ApiExplorerConstants.DefaultDocumentName, serializerOptionsProviderType); + if (string.IsNullOrWhiteSpace(description.GroupName)) + { + description.GroupName = ApiExplorerUtils.GetDocumentName(ApiExplorerConstants.DefaultDocumentName, serializerOptionsProviderType); + } description.HttpMethod = HttpMethods.Post; description.RelativePath += $"#{methodMetadata.Method}"; diff --git a/src/Tochka.JsonRpc.Client/Extensions.cs b/src/Tochka.JsonRpc.Client/Extensions.cs index c57e13b6..034633e6 100644 --- a/src/Tochka.JsonRpc.Client/Extensions.cs +++ b/src/Tochka.JsonRpc.Client/Extensions.cs @@ -14,7 +14,7 @@ namespace Tochka.JsonRpc.Client; public static class Extensions { /// - /// Register JSON-RPC client as + /// Register JSON-RPC client as /// /// The to add the service to /// Type of JSON-RPC client @@ -26,7 +26,7 @@ public static IHttpClientBuilder AddJsonRpcClient(this AddJsonRpcClient(services, static (_, _) => { }); /// - /// Register JSON-RPC client as and configure internal + /// Register JSON-RPC client as and configure internal /// /// The to add the service to /// Delegate used to configure internal @@ -71,5 +71,7 @@ public static IHttpClientBuilder AddJsonRpcClient(this IServiceCollecti /// /// Register handler to log outgoing requests /// - public static IHttpClientBuilder WithJsonRpcRequestLogging(this IHttpClientBuilder httpClientBuilder) => httpClientBuilder.AddHttpMessageHandler(); + [ExcludeFromCodeCoverage(Justification = "it's almost impossible to test AddHttpMessageHandler")] + public static IHttpClientBuilder WithJsonRpcRequestLogging(this IHttpClientBuilder httpClientBuilder) => + httpClientBuilder.AddHttpMessageHandler(); } diff --git a/src/Tochka.JsonRpc.Server/Extensions/DependencyInjectionExtensions.cs b/src/Tochka.JsonRpc.Server/Extensions/DependencyInjectionExtensions.cs index 5c3f06db..89440069 100644 --- a/src/Tochka.JsonRpc.Server/Extensions/DependencyInjectionExtensions.cs +++ b/src/Tochka.JsonRpc.Server/Extensions/DependencyInjectionExtensions.cs @@ -1,5 +1,6 @@ using System.Diagnostics.CodeAnalysis; using Asp.Versioning; +using Asp.Versioning.ApiExplorer; using Asp.Versioning.ApplicationModels; using JetBrains.Annotations; using Microsoft.AspNetCore.Builder; @@ -60,6 +61,10 @@ public static IServiceCollection AddJsonRpcServer(this IServiceCollection servic options.FormatGroupName = static (name, version) => $"{name}_{version}"; }); services.TryAddEnumerable(ServiceDescriptor.Singleton()); + + // required for correct autodocs document names if ApiExplorerSettingsAttribute with custom GroupName is used + services.Replace(ServiceDescriptor.Singleton()); + services.AddSingleton(); return services; } diff --git a/src/tests/Tochka.JsonRpc.ApiExplorer.Tests/JsonRpcDescriptionProviderTests.cs b/src/tests/Tochka.JsonRpc.ApiExplorer.Tests/JsonRpcDescriptionProviderTests.cs index 740941c6..0d0a792d 100644 --- a/src/tests/Tochka.JsonRpc.ApiExplorer.Tests/JsonRpcDescriptionProviderTests.cs +++ b/src/tests/Tochka.JsonRpc.ApiExplorer.Tests/JsonRpcDescriptionProviderTests.cs @@ -117,6 +117,25 @@ public void OnProvidersExecuting_ValidDescriptor_UpdateDescriptionProperties() description.Properties[ApiExplorerConstants.MethodNameProperty] = Method; } + [Test] + public void OnProvidersExecuting_GroupNameAlreadySpecified_DontOverrideGroupName() + { + var groupName = "group-name"; + var context = GetContext(); + context.Results.First().ActionDescriptor.EndpointMetadata.Add(new JsonRpcMethodAttribute(Method)); + context.Results.First().GroupName = groupName; + typeEmitterMock.Setup(static e => e.CreateRequestType(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(Mock.Of); + typeEmitterMock.Setup(static e => e.CreateResponseType(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Mock.Of); + + descriptionProvider.OnProvidersExecuting(context); + + context.Results.Should().HaveCount(1); + var description = context.Results.Single(); + description.GroupName.Should().Be(groupName); + } + [Test] public void OnProvidersExecuting_HasCustomSerializer_UseSerializer() { diff --git a/src/tests/Tochka.JsonRpc.OpenRpc.Tests.Integration/IntegrationTests.cs b/src/tests/Tochka.JsonRpc.OpenRpc.Tests.Integration/IntegrationTests.cs index c11a76d6..3148fcb8 100644 --- a/src/tests/Tochka.JsonRpc.OpenRpc.Tests.Integration/IntegrationTests.cs +++ b/src/tests/Tochka.JsonRpc.OpenRpc.Tests.Integration/IntegrationTests.cs @@ -44,4 +44,16 @@ public async Task GetDocument_ReturnsAllMethods(string method) var methods = responseJson.RootElement.GetProperty("methods").EnumerateArray().ToArray(); methods.Should().Contain(m => m.GetProperty("name").GetString() == method); } + + [Test] + public async Task GetDocument_AttributeWithCustomGroup_ReturnAllCustomGroupMethods() + { + var response = await ApiClient.GetAsync("/openrpc/custom_v1.json"); + + var responseContent = await response.Content.ReadAsStringAsync(); + var responseJson = JsonDocument.Parse(responseContent); + response.StatusCode.Should().Be(HttpStatusCode.OK); + var methods = responseJson.RootElement.GetProperty("methods").EnumerateArray().ToArray(); + methods.Should().OnlyContain(static m => m.GetProperty("name").GetString() == "custom_group"); + } } diff --git a/src/tests/Tochka.JsonRpc.Server.Tests/Extensions/DependencyInjectionExtensionsTests.cs b/src/tests/Tochka.JsonRpc.Server.Tests/Extensions/DependencyInjectionExtensionsTests.cs index 071dabdd..7aa84bdf 100644 --- a/src/tests/Tochka.JsonRpc.Server.Tests/Extensions/DependencyInjectionExtensionsTests.cs +++ b/src/tests/Tochka.JsonRpc.Server.Tests/Extensions/DependencyInjectionExtensionsTests.cs @@ -48,13 +48,14 @@ public void AddJsonRpcServer_RegisterServices() result.Remove((typeof(IJsonRpcRequestValidator), typeof(JsonRpcRequestValidator), ServiceLifetime.Singleton)).Should().BeTrue(); result.Remove((typeof(IJsonRpcErrorFactory), typeof(JsonRpcErrorFactory), ServiceLifetime.Singleton)).Should().BeTrue(); result.Remove((typeof(IApiControllerSpecification), typeof(JsonRpcControllerSpecification), ServiceLifetime.Singleton)).Should().BeTrue(); + result.Remove((typeof(IApiVersionDescriptionProvider), typeof(DefaultApiVersionDescriptionProvider), ServiceLifetime.Singleton)).Should().BeTrue(); result.Remove((typeof(JsonRpcMarkerService), typeof(JsonRpcMarkerService), ServiceLifetime.Singleton)).Should().BeTrue(); // one of services registered by calling AddApiVersioning result.Remove((typeof(IApiVersionSetBuilderFactory), typeof(DefaultApiVersionSetBuilderFactory), ServiceLifetime.Singleton)).Should().BeTrue(); // one of services registered by calling AddApiVersioning.AddMvc result.Remove((typeof(IControllerNameConvention), typeof(DefaultControllerNameConvention), ServiceLifetime.Singleton)).Should().BeTrue(); // one of services registered by calling AddApiVersioning.AddApiExplorer - result.Remove((typeof(IApiVersionDescriptionProvider), null, ServiceLifetime.Singleton)).Should().BeTrue(); + result.Remove((typeof(IApiVersionDescriptionProviderFactory), null, ServiceLifetime.Transient)).Should().BeTrue(); } [Test] diff --git a/src/tests/Tochka.JsonRpc.Swagger.Tests.Integration/IntegrationTests.cs b/src/tests/Tochka.JsonRpc.Swagger.Tests.Integration/IntegrationTests.cs index e6c85caf..5ad63b3c 100644 --- a/src/tests/Tochka.JsonRpc.Swagger.Tests.Integration/IntegrationTests.cs +++ b/src/tests/Tochka.JsonRpc.Swagger.Tests.Integration/IntegrationTests.cs @@ -36,6 +36,7 @@ internal class IntegrationTests : IntegrationTestsBase [TestCase("jsonrpc_v1", "/api/jsonrpc#return_error_from_factory")] [TestCase("jsonrpc_v1", "/api/jsonrpc#return_mvc_error")] [TestCase("jsonrpc_v1", "/api/jsonrpc#error_throw_as_response_exception")] + [TestCase("custom_v1", "/api/jsonrpc#custom_group")] public async Task GetDocument_ReturnsAllMethods(string document, string path) { var response = await ApiClient.GetAsync($"/swagger/{document}/swagger.json"); diff --git a/src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/CustomGroupNameJsonRpcController.cs b/src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/CustomGroupNameJsonRpcController.cs new file mode 100644 index 00000000..09387bfc --- /dev/null +++ b/src/tests/Tochka.JsonRpc.Tests.WebApplication/Controllers/CustomGroupNameJsonRpcController.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Mvc; +using Tochka.JsonRpc.Server; + +namespace Tochka.JsonRpc.Tests.WebApplication.Controllers; + +[ApiExplorerSettings(GroupName = "custom")] +public class CustomGroupNameJsonRpcController : JsonRpcControllerBase +{ + public bool CustomGroup() => true; +} diff --git a/src/tests/Tochka.JsonRpc.Tests.WebApplication/Program.cs b/src/tests/Tochka.JsonRpc.Tests.WebApplication/Program.cs index 422966ce..e2d96baa 100644 --- a/src/tests/Tochka.JsonRpc.Tests.WebApplication/Program.cs +++ b/src/tests/Tochka.JsonRpc.Tests.WebApplication/Program.cs @@ -1,6 +1,8 @@ using System.Reflection; +using Asp.Versioning.ApiExplorer; using Microsoft.OpenApi.Models; using Tochka.JsonRpc.OpenRpc; +using Tochka.JsonRpc.OpenRpc.Models; using Tochka.JsonRpc.Server.Extensions; using Tochka.JsonRpc.Server.Serialization; using Tochka.JsonRpc.Server.Settings; @@ -23,18 +25,22 @@ builder.Services.AddSingleton(); builder.Services.AddSwaggerWithJsonRpc(Assembly.GetExecutingAssembly()); // swagger for json-rpc -builder.Services.AddSwaggerGen(static c => // swagger for REST +builder.Services.ConfigureSwaggerGen(static c => // swagger for REST { c.SwaggerDoc("rest", new OpenApiInfo { Title = "RESTful API", Version = "v1" }); + c.SwaggerDoc("custom_v1", new OpenApiInfo { Title = "Custom group", Version = "v1" }); }); builder.Services.AddOpenRpc(Assembly.GetExecutingAssembly()); // OpenRpc +builder.Services.Configure(static o => o.OpenRpcDoc("custom_v1", new OpenRpcInfo("Custom group", "v1"))); // auth builder.Services.AddAuthentication(AuthConstants.SchemeName) .AddScheme(AuthConstants.SchemeName, null); builder.Services.AddAuthorization(); +builder.Services.AddSingleton(); + var app = builder.Build(); app.UseAuthentication(); @@ -42,6 +48,7 @@ { c.JsonRpcSwaggerEndpoints(app.Services); // register json-rpc in swagger UI c.SwaggerEndpoint("/swagger/rest/swagger.json", "RESTful"); // register REST in swagger UI + c.SwaggerEndpoint("/swagger/custom_v1/swagger.json", "Custom group"); }); app.UseJsonRpc(); app.UseRouting();