From 2cdb8a699a1d8bba469f6142623f6342d230a15a Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Fri, 4 Oct 2024 23:06:31 +0000 Subject: [PATCH 1/2] Fix handling for ambient route parameters --- .../Services/Schemas/OpenApiSchemaService.cs | 9 ++++++++- .../OpenApiSchemaService.ParameterSchemas.cs | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs index d7ea158b919a..1096f2b7c530 100644 --- a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs +++ b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs @@ -126,8 +126,15 @@ internal sealed class OpenApiSchemaService( } }; - internal async Task GetOrCreateSchemaAsync(Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, bool captureSchemaByRef = false, CancellationToken cancellationToken = default) + internal async Task GetOrCreateSchemaAsync(Type? type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, bool captureSchemaByRef = false, CancellationToken cancellationToken = default) { + // If the type is null, then we're dealing with an ambient + // route parameter that does not define a specific parameter type in the + // route handler or in the response. In this case, we default to a string schema. + if (type is null) + { + return new OpenApiSchema { Type = "string" }; + } var key = parameterDescription?.ParameterDescriptor is IParameterInfoParameterDescriptor parameterInfoDescription && parameterDescription.ModelMetadata.PropertyName is null ? new OpenApiSchemaKey(type, parameterInfoDescription.ParameterInfo) : new OpenApiSchemaKey(type, null); diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs index 4842e189ade4..671fc166e87a 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiSchemaService/OpenApiSchemaService.ParameterSchemas.cs @@ -592,4 +592,22 @@ internal enum ItemStatus Approved = 1, Rejected = 2, } + + [Fact] + public async Task SupportsMvcActionWithAmbientRouteParameter() + { + // Arrange + var action = CreateActionDescriptor(nameof(AmbientRouteParameter)); + + // Assert + await VerifyOpenApiDocument(action, document => + { + var operation = document.Paths["/api/with-ambient-route-param/{versionId}"].Operations[OperationType.Get]; + var parameter = Assert.Single(operation.Parameters); + Assert.Equal("string", parameter.Schema.Type); + }); + } + + [Route("/api/with-ambient-route-param/{versionId}")] + private void AmbientRouteParameter() { } } From 53a6a1ea5f6a4eee50aad6adecf77e7200b01791 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Wed, 9 Oct 2024 00:36:39 +0000 Subject: [PATCH 2/2] Set default type earlier --- src/OpenApi/src/Services/OpenApiDocumentService.cs | 4 ++++ src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs | 9 +-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/OpenApi/src/Services/OpenApiDocumentService.cs b/src/OpenApi/src/Services/OpenApiDocumentService.cs index c594ebb7ac08..bc0094faa108 100644 --- a/src/OpenApi/src/Services/OpenApiDocumentService.cs +++ b/src/OpenApi/src/Services/OpenApiDocumentService.cs @@ -409,6 +409,10 @@ private async Task GetResponseAsync( var targetType = parameter.Type == typeof(string) && parameter.ModelMetadata.ModelType != parameter.Type ? parameter.ModelMetadata.ModelType : parameter.Type; + // If the type is null, then we're dealing with an inert + // route parameter that does not define a specific parameter type in the + // route handler or in the response. In this case, we default to a string schema. + targetType ??= typeof(string); var openApiParameter = new OpenApiParameter { Name = parameter.Name, diff --git a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs index 1096f2b7c530..d7ea158b919a 100644 --- a/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs +++ b/src/OpenApi/src/Services/Schemas/OpenApiSchemaService.cs @@ -126,15 +126,8 @@ internal sealed class OpenApiSchemaService( } }; - internal async Task GetOrCreateSchemaAsync(Type? type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, bool captureSchemaByRef = false, CancellationToken cancellationToken = default) + internal async Task GetOrCreateSchemaAsync(Type type, IServiceProvider scopedServiceProvider, IOpenApiSchemaTransformer[] schemaTransformers, ApiParameterDescription? parameterDescription = null, bool captureSchemaByRef = false, CancellationToken cancellationToken = default) { - // If the type is null, then we're dealing with an ambient - // route parameter that does not define a specific parameter type in the - // route handler or in the response. In this case, we default to a string schema. - if (type is null) - { - return new OpenApiSchema { Type = "string" }; - } var key = parameterDescription?.ParameterDescriptor is IParameterInfoParameterDescriptor parameterInfoDescription && parameterDescription.ModelMetadata.PropertyName is null ? new OpenApiSchemaKey(type, parameterInfoDescription.ParameterInfo) : new OpenApiSchemaKey(type, null);