From a4ac22dfb7abc91cad3013fe2a39596b7325271f Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Mon, 7 Feb 2022 18:21:09 +0000 Subject: [PATCH 1/4] Support OpenAPI summaries and descriptions on minimal endpoints --- .../src/Metadata/IDescriptionMetadata.cs | 15 +++++ .../src/Metadata/ISummaryMetadata.cs | 15 +++++ .../src/PublicAPI.Unshipped.txt | 4 ++ .../src/DescriptionAttribute.cs | 30 +++++++++ .../src/PublicAPI.Unshipped.txt | 6 ++ .../Http.Extensions/src/SummaryAttribute.cs | 25 +++++++ .../OpenApiRouteHandlerBuilderExtensions.cs | 26 +++++++ src/Http/Routing/src/PublicAPI.Unshipped.txt | 2 + ...pointMetadataApiDescriptionProviderTest.cs | 67 ++++++++++++++++++- 9 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs create mode 100644 src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs create mode 100644 src/Http/Http.Extensions/src/DescriptionAttribute.cs create mode 100644 src/Http/Http.Extensions/src/SummaryAttribute.cs diff --git a/src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs new file mode 100644 index 000000000000..0d8fa7b6648e --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Defines a contract used to specify a description in . +/// +public interface IDescriptionMetadata +{ + /// + /// Gets the description associated with the endpoint. + /// + string Description { get; } +} diff --git a/src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs new file mode 100644 index 000000000000..e7bdb78a202e --- /dev/null +++ b/src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.AspNetCore.Http.Metadata; + +/// +/// Defines a contract used to specify a summary in . +/// +public interface ISummaryMetadata +{ + /// + /// Gets the summary associated with the endpoint. + /// + string Summary { get; } +} diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 92c400c0bac6..7552735693bc 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -4,3 +4,7 @@ Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string? abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string? Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata +Microsoft.AspNetCore.Http.Metadata.IDescriptionMetadata +Microsoft.AspNetCore.Http.Metadata.IDescriptionMetadata.Description.get -> string! +Microsoft.AspNetCore.Http.Metadata.ISummaryMetadata +Microsoft.AspNetCore.Http.Metadata.ISummaryMetadata.Summary.get -> string! diff --git a/src/Http/Http.Extensions/src/DescriptionAttribute.cs b/src/Http/Http.Extensions/src/DescriptionAttribute.cs new file mode 100644 index 000000000000..c6864cd988a6 --- /dev/null +++ b/src/Http/Http.Extensions/src/DescriptionAttribute.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Http.Metadata; + +namespace Microsoft.AspNetCore.Http; + +/// +/// Specifies a description for the endpoint in . +/// +/// +/// The OpenAPI specification supports a description attribute on operations and parameters that +/// can be used to annotate endpoints with detailed, multiline descriptors of their behavior. +/// behavior. +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] +public sealed class DescriptionAttribute : Attribute, IDescriptionMetadata +{ + /// + /// Initializes an instance of the . + /// + /// The description associated with the endpoint or parameter. + public DescriptionAttribute(string description) + { + Description = description; + } + + /// + public string Description { get; } +} diff --git a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt index 1030d0f0793e..4fa03eb4f007 100644 --- a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt @@ -1,3 +1,9 @@ #nullable enable Microsoft.Extensions.DependencyInjection.RouteHandlerJsonServiceExtensions static Microsoft.Extensions.DependencyInjection.RouteHandlerJsonServiceExtensions.ConfigureRouteHandlerJsonOptions(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! +Microsoft.AspNetCore.Http.DescriptionAttribute +Microsoft.AspNetCore.Http.DescriptionAttribute.DescriptionAttribute(string! description) -> void +Microsoft.AspNetCore.Http.DescriptionAttribute.Description.get -> string! +Microsoft.AspNetCore.Http.SummaryAttribute +Microsoft.AspNetCore.Http.SummaryAttribute.SummaryAttribute(string! summary) -> void +Microsoft.AspNetCore.Http.SummaryAttribute.Summary.get -> string! diff --git a/src/Http/Http.Extensions/src/SummaryAttribute.cs b/src/Http/Http.Extensions/src/SummaryAttribute.cs new file mode 100644 index 000000000000..f32ea74128f5 --- /dev/null +++ b/src/Http/Http.Extensions/src/SummaryAttribute.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Http.Metadata; + +namespace Microsoft.AspNetCore.Http; + +/// +/// Specifies a summary in . +/// +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] +public sealed class SummaryAttribute : Attribute, ISummaryMetadata +{ + /// + /// Initializes an instance of the . + /// + /// The summary associated with the endpoint or parameter. + public SummaryAttribute(string summary) + { + Summary = summary; + } + + /// + public string Summary { get; } +} diff --git a/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs index 31696a3da7be..8057c21f1252 100644 --- a/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs @@ -209,6 +209,32 @@ public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, return builder; } + /// + /// Adds to for all builders + /// produced by . + /// + /// The . + /// A string representing a detailed description of the endpoint. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder WithDescription(this RouteHandlerBuilder builder, string description) + { + builder.WithMetadata(new DescriptionAttribute(description)); + return builder; + } + + /// + /// Adds to for all builders + /// produced by . + /// + /// The . + /// A string representation a brief description of the endpoint. + /// A that can be used to further customize the endpoint. + public static RouteHandlerBuilder WithSummary(this RouteHandlerBuilder builder, string summary) + { + builder.WithMetadata(new SummaryAttribute(summary)); + return builder; + } + private static string[] GetAllContentTypes(string contentType, string[] additionalContentTypes) { var allContentTypes = new string[additionalContentTypes.Length + 1]; diff --git a/src/Http/Routing/src/PublicAPI.Unshipped.txt b/src/Http/Routing/src/PublicAPI.Unshipped.txt index 341f2446a1cf..17d11a018a8e 100644 --- a/src/Http/Routing/src/PublicAPI.Unshipped.txt +++ b/src/Http/Routing/src/PublicAPI.Unshipped.txt @@ -6,3 +6,5 @@ static Microsoft.AspNetCore.Builder.EndpointRouteBuilderExtensions.MapPatch(this override Microsoft.AspNetCore.Routing.RouteValuesAddress.ToString() -> string? *REMOVED*~Microsoft.AspNetCore.Routing.DefaultInlineConstraintResolver.DefaultInlineConstraintResolver(Microsoft.Extensions.Options.IOptions! routeOptions, System.IServiceProvider! serviceProvider) -> void Microsoft.AspNetCore.Routing.DefaultInlineConstraintResolver.DefaultInlineConstraintResolver(Microsoft.Extensions.Options.IOptions! routeOptions, System.IServiceProvider! serviceProvider) -> void +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithDescription(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! description) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! +static Microsoft.AspNetCore.Http.OpenApiRouteHandlerBuilderExtensions.WithSummary(this Microsoft.AspNetCore.Builder.RouteHandlerBuilder! builder, string! summary) -> Microsoft.AspNetCore.Builder.RouteHandlerBuilder! diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index f933be54ce6d..d982d59ea39b 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -6,6 +6,7 @@ using System.Security.Claims; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Metadata; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ModelBinding; @@ -512,7 +513,7 @@ public void TestParameterIsRequiredForObliviousNullabilityContext() [Fact] public void TestParameterAttributesCanBeInspected() { - var apiDescription = GetApiDescription(([Description("The name.")] string name) => { }); + var apiDescription = GetApiDescription(([System.ComponentModel.Description("The name.")] string name) => { }); Assert.Equal(1, apiDescription.ParameterDescriptions.Count); var nameParam = apiDescription.ParameterDescriptions[0]; @@ -529,7 +530,7 @@ public void TestParameterAttributesCanBeInspected() Assert.NotNull(descriptor.ParameterInfo); - var description = Assert.Single(descriptor.ParameterInfo.GetCustomAttributes()); + var description = Assert.Single(descriptor.ParameterInfo.GetCustomAttributes()); Assert.NotNull(description); Assert.Equal("The name.", description.Description); @@ -1145,6 +1146,68 @@ public void HandlesEndpointWithRouteConstraints() constraint => Assert.IsType(constraint)); } + [Fact] + public void HandlesEndpointWithDescriptionAndSummary_WithExtensionMethods() + { + var builder = CreateBuilder(); + builder.MapGet("/api/todos/{id}", (int id) => "").WithDescription("A description").WithSummary("A summary"); + + var context = new ApiDescriptionProviderContext(Array.Empty()); + + var endpointDataSource = builder.DataSources.OfType().Single(); + var hostEnvironment = new HostEnvironment + { + ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) + }; + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); + + // Act + provider.OnProvidersExecuting(context); + + // Assert + var apiDescription = Assert.Single(context.Results); + Assert.NotEmpty(apiDescription.ActionDescriptor.EndpointMetadata); + + var descriptionMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + Assert.NotNull(descriptionMetadata); + Assert.Equal("A description", descriptionMetadata.Description); + + var summaryMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + Assert.NotNull(summaryMetadata); + Assert.Equal("A summary", summaryMetadata.Summary); + } + + [Fact] + public void HandlesEndpointWithDescriptionAndSummary_WithAttributes() + { + var builder = CreateBuilder(); + builder.MapGet("/api/todos/{id}", [Summary("A summary")][Http.Description("A description")] (int id) => ""); + + var context = new ApiDescriptionProviderContext(Array.Empty()); + + var endpointDataSource = builder.DataSources.OfType().Single(); + var hostEnvironment = new HostEnvironment + { + ApplicationName = nameof(EndpointMetadataApiDescriptionProviderTest) + }; + var provider = CreateEndpointMetadataApiDescriptionProvider(endpointDataSource); + + // Act + provider.OnProvidersExecuting(context); + + // Assert + var apiDescription = Assert.Single(context.Results); + Assert.NotEmpty(apiDescription.ActionDescriptor.EndpointMetadata); + + var descriptionMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + Assert.NotNull(descriptionMetadata); + Assert.Equal("A description", descriptionMetadata.Description); + + var summaryMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + Assert.NotNull(summaryMetadata); + Assert.Equal("A summary", summaryMetadata.Summary); + } + private static IEnumerable GetSortedMediaTypes(ApiResponseType apiResponseType) { return apiResponseType.ApiResponseFormats From ae7b22fdcd58d686db17d4d52d311a7777cbb7d6 Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 10 Feb 2022 16:14:29 +0000 Subject: [PATCH 2/4] Prefix new types with 'Endpoint' --- .../src/Metadata/IDescriptionMetadata.cs | 2 +- .../src/Metadata/ISummaryMetadata.cs | 2 +- .../Http.Abstractions/src/PublicAPI.Unshipped.txt | 8 ++++---- ...ttribute.cs => EndpointDescriptionAttribute.cs} | 6 +++--- ...aryAttribute.cs => EndpointSummaryAttribute.cs} | 6 +++--- .../Http.Extensions/src/PublicAPI.Unshipped.txt | 12 ++++++------ .../OpenApiRouteHandlerBuilderExtensions.cs | 8 ++++---- .../EndpointMetadataApiDescriptionProviderTest.cs | 14 +++++++------- 8 files changed, 29 insertions(+), 29 deletions(-) rename src/Http/Http.Extensions/src/{DescriptionAttribute.cs => EndpointDescriptionAttribute.cs} (79%) rename src/Http/Http.Extensions/src/{SummaryAttribute.cs => EndpointSummaryAttribute.cs} (75%) diff --git a/src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs index 0d8fa7b6648e..067f48f70411 100644 --- a/src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs +++ b/src/Http/Http.Abstractions/src/Metadata/IDescriptionMetadata.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Http.Metadata; /// /// Defines a contract used to specify a description in . /// -public interface IDescriptionMetadata +public interface IEndpointDescriptionMetadata { /// /// Gets the description associated with the endpoint. diff --git a/src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs b/src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs index e7bdb78a202e..fa1083d52183 100644 --- a/src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs +++ b/src/Http/Http.Abstractions/src/Metadata/ISummaryMetadata.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Http.Metadata; /// /// Defines a contract used to specify a summary in . /// -public interface ISummaryMetadata +public interface IEndpointSummaryMetadata { /// /// Gets the summary associated with the endpoint. diff --git a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt index 7552735693bc..7e611715814c 100644 --- a/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Abstractions/src/PublicAPI.Unshipped.txt @@ -4,7 +4,7 @@ Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata Microsoft.AspNetCore.Http.Metadata.IFromFormMetadata.Name.get -> string? abstract Microsoft.AspNetCore.Http.HttpResponse.ContentType.get -> string? Microsoft.AspNetCore.Http.Metadata.ISkipStatusCodePagesMetadata -Microsoft.AspNetCore.Http.Metadata.IDescriptionMetadata -Microsoft.AspNetCore.Http.Metadata.IDescriptionMetadata.Description.get -> string! -Microsoft.AspNetCore.Http.Metadata.ISummaryMetadata -Microsoft.AspNetCore.Http.Metadata.ISummaryMetadata.Summary.get -> string! +Microsoft.AspNetCore.Http.Metadata.IEndpointDescriptionMetadata +Microsoft.AspNetCore.Http.Metadata.IEndpointDescriptionMetadata.Description.get -> string! +Microsoft.AspNetCore.Http.Metadata.IEndpointSummaryMetadata +Microsoft.AspNetCore.Http.Metadata.IEndpointSummaryMetadata.Summary.get -> string! diff --git a/src/Http/Http.Extensions/src/DescriptionAttribute.cs b/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs similarity index 79% rename from src/Http/Http.Extensions/src/DescriptionAttribute.cs rename to src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs index c6864cd988a6..e2acb2990690 100644 --- a/src/Http/Http.Extensions/src/DescriptionAttribute.cs +++ b/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs @@ -14,13 +14,13 @@ namespace Microsoft.AspNetCore.Http; /// behavior. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] -public sealed class DescriptionAttribute : Attribute, IDescriptionMetadata +public sealed class EndpointDescriptionAttribute : Attribute, IEndpointDescriptionMetadata { /// - /// Initializes an instance of the . + /// Initializes an instance of the . /// /// The description associated with the endpoint or parameter. - public DescriptionAttribute(string description) + public EndpointDescriptionAttribute(string description) { Description = description; } diff --git a/src/Http/Http.Extensions/src/SummaryAttribute.cs b/src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs similarity index 75% rename from src/Http/Http.Extensions/src/SummaryAttribute.cs rename to src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs index f32ea74128f5..7ce57268beef 100644 --- a/src/Http/Http.Extensions/src/SummaryAttribute.cs +++ b/src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs @@ -9,13 +9,13 @@ namespace Microsoft.AspNetCore.Http; /// Specifies a summary in . /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] -public sealed class SummaryAttribute : Attribute, ISummaryMetadata +public sealed class EndpointSummaryAttribute : Attribute, IEndpointSummaryMetadata { /// - /// Initializes an instance of the . + /// Initializes an instance of the . /// /// The summary associated with the endpoint or parameter. - public SummaryAttribute(string summary) + public EndpointSummaryAttribute(string summary) { Summary = summary; } diff --git a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt index 4fa03eb4f007..28a2fa1553be 100644 --- a/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt +++ b/src/Http/Http.Extensions/src/PublicAPI.Unshipped.txt @@ -1,9 +1,9 @@ #nullable enable Microsoft.Extensions.DependencyInjection.RouteHandlerJsonServiceExtensions static Microsoft.Extensions.DependencyInjection.RouteHandlerJsonServiceExtensions.ConfigureRouteHandlerJsonOptions(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection! -Microsoft.AspNetCore.Http.DescriptionAttribute -Microsoft.AspNetCore.Http.DescriptionAttribute.DescriptionAttribute(string! description) -> void -Microsoft.AspNetCore.Http.DescriptionAttribute.Description.get -> string! -Microsoft.AspNetCore.Http.SummaryAttribute -Microsoft.AspNetCore.Http.SummaryAttribute.SummaryAttribute(string! summary) -> void -Microsoft.AspNetCore.Http.SummaryAttribute.Summary.get -> string! +Microsoft.AspNetCore.Http.EndpointDescriptionAttribute +Microsoft.AspNetCore.Http.EndpointDescriptionAttribute.EndpointDescriptionAttribute(string! description) -> void +Microsoft.AspNetCore.Http.EndpointDescriptionAttribute.Description.get -> string! +Microsoft.AspNetCore.Http.EndpointSummaryAttribute +Microsoft.AspNetCore.Http.EndpointSummaryAttribute.EndpointSummaryAttribute(string! summary) -> void +Microsoft.AspNetCore.Http.EndpointSummaryAttribute.Summary.get -> string! diff --git a/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs index 8057c21f1252..61c9e9f807ee 100644 --- a/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs @@ -210,7 +210,7 @@ public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, } /// - /// Adds to for all builders + /// Adds to for all builders /// produced by . /// /// The . @@ -218,12 +218,12 @@ public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, /// A that can be used to further customize the endpoint. public static RouteHandlerBuilder WithDescription(this RouteHandlerBuilder builder, string description) { - builder.WithMetadata(new DescriptionAttribute(description)); + builder.WithMetadata(new EndpointDescriptionAttribute(description)); return builder; } /// - /// Adds to for all builders + /// Adds to for all builders /// produced by . /// /// The . @@ -231,7 +231,7 @@ public static RouteHandlerBuilder WithDescription(this RouteHandlerBuilder build /// A that can be used to further customize the endpoint. public static RouteHandlerBuilder WithSummary(this RouteHandlerBuilder builder, string summary) { - builder.WithMetadata(new SummaryAttribute(summary)); + builder.WithMetadata(new EndpointSummaryAttribute(summary)); return builder; } diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index d982d59ea39b..5a77ef5f9b55 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -513,7 +513,7 @@ public void TestParameterIsRequiredForObliviousNullabilityContext() [Fact] public void TestParameterAttributesCanBeInspected() { - var apiDescription = GetApiDescription(([System.ComponentModel.Description("The name.")] string name) => { }); + var apiDescription = GetApiDescription(([Description("The name.")] string name) => { }); Assert.Equal(1, apiDescription.ParameterDescriptions.Count); var nameParam = apiDescription.ParameterDescriptions[0]; @@ -530,7 +530,7 @@ public void TestParameterAttributesCanBeInspected() Assert.NotNull(descriptor.ParameterInfo); - var description = Assert.Single(descriptor.ParameterInfo.GetCustomAttributes()); + var description = Assert.Single(descriptor.ParameterInfo.GetCustomAttributes()); Assert.NotNull(description); Assert.Equal("The name.", description.Description); @@ -1168,11 +1168,11 @@ public void HandlesEndpointWithDescriptionAndSummary_WithExtensionMethods() var apiDescription = Assert.Single(context.Results); Assert.NotEmpty(apiDescription.ActionDescriptor.EndpointMetadata); - var descriptionMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + var descriptionMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); Assert.NotNull(descriptionMetadata); Assert.Equal("A description", descriptionMetadata.Description); - var summaryMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + var summaryMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); Assert.NotNull(summaryMetadata); Assert.Equal("A summary", summaryMetadata.Summary); } @@ -1181,7 +1181,7 @@ public void HandlesEndpointWithDescriptionAndSummary_WithExtensionMethods() public void HandlesEndpointWithDescriptionAndSummary_WithAttributes() { var builder = CreateBuilder(); - builder.MapGet("/api/todos/{id}", [Summary("A summary")][Http.Description("A description")] (int id) => ""); + builder.MapGet("/api/todos/{id}", [EndpointSummary("A summary")][EndpointDescription("A description")] (int id) => ""); var context = new ApiDescriptionProviderContext(Array.Empty()); @@ -1199,11 +1199,11 @@ public void HandlesEndpointWithDescriptionAndSummary_WithAttributes() var apiDescription = Assert.Single(context.Results); Assert.NotEmpty(apiDescription.ActionDescriptor.EndpointMetadata); - var descriptionMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + var descriptionMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); Assert.NotNull(descriptionMetadata); Assert.Equal("A description", descriptionMetadata.Description); - var summaryMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); + var summaryMetadata = apiDescription.ActionDescriptor.EndpointMetadata.OfType().SingleOrDefault(); Assert.NotNull(summaryMetadata); Assert.Equal("A summary", summaryMetadata.Summary); } From c986ad6dcbe45d58252ab088187a384f3043b53f Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Thu, 10 Feb 2022 18:50:38 +0000 Subject: [PATCH 3/4] Bring back docstring fixes --- .../src/EndpointDescriptionAttribute.cs | 1 - .../OpenApiRouteHandlerBuilderExtensions.cs | 26 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs b/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs index e2acb2990690..b5ea17b23285 100644 --- a/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs +++ b/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs @@ -11,7 +11,6 @@ namespace Microsoft.AspNetCore.Http; /// /// The OpenAPI specification supports a description attribute on operations and parameters that /// can be used to annotate endpoints with detailed, multiline descriptors of their behavior. -/// behavior. /// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] public sealed class EndpointDescriptionAttribute : Attribute, IEndpointDescriptionMetadata diff --git a/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs index 61c9e9f807ee..2456ed622c2d 100644 --- a/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs +++ b/src/Http/Routing/src/Builder/OpenApiRouteHandlerBuilderExtensions.cs @@ -17,7 +17,7 @@ public static class OpenApiRouteHandlerBuilderExtensions private static readonly ExcludeFromDescriptionAttribute _excludeFromDescriptionMetadataAttribute = new(); /// - /// Adds the to for all builders + /// Adds the to for all endpoints /// produced by . /// /// The . @@ -30,7 +30,7 @@ public static RouteHandlerBuilder ExcludeFromDescription(this RouteHandlerBuilde } /// - /// Adds an to for all builders + /// Adds an to for all endpoints /// produced by . /// /// The type of the response. @@ -50,7 +50,7 @@ public static RouteHandlerBuilder Produces(this RouteHandlerBuilder b } /// - /// Adds an to for all builders + /// Adds an to for all endpoints /// produced by . /// /// The . @@ -85,7 +85,7 @@ public static RouteHandlerBuilder Produces(this RouteHandlerBuilder builder, /// /// Adds an with a type - /// to for all builders produced by . + /// to for all endpoints produced by . /// /// The . /// The response status code. @@ -105,7 +105,7 @@ public static RouteHandlerBuilder ProducesProblem(this RouteHandlerBuilder build /// /// Adds an with a type - /// to for all builders produced by . + /// to for all endpoints produced by . /// /// The . /// The response status code. Defaults to . @@ -124,7 +124,7 @@ public static RouteHandlerBuilder ProducesValidationProblem(this RouteHandlerBui } /// - /// Adds the to for all builders + /// Adds the to for all endpoints /// produced by . /// /// @@ -142,7 +142,7 @@ public static RouteHandlerBuilder WithTags(this RouteHandlerBuilder builder, par } /// - /// Adds to for all builders + /// Adds to for all endpoints /// produced by . /// /// The type of the request body. @@ -159,7 +159,7 @@ public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder bui } /// - /// Adds to for all builders + /// Adds to for all endpoints /// produced by . /// /// The type of the request body. @@ -177,7 +177,7 @@ public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder bui } /// - /// Adds to for all builders + /// Adds to for all endpoints /// produced by . /// /// The . @@ -193,7 +193,7 @@ public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, } /// - /// Adds to for all builders + /// Adds to for all endpoints /// produced by . /// /// The . @@ -210,7 +210,7 @@ public static RouteHandlerBuilder Accepts(this RouteHandlerBuilder builder, } /// - /// Adds to for all builders + /// Adds to for all endpoints /// produced by . /// /// The . @@ -223,11 +223,11 @@ public static RouteHandlerBuilder WithDescription(this RouteHandlerBuilder build } /// - /// Adds to for all builders + /// Adds to for all endpoints /// produced by . /// /// The . - /// A string representation a brief description of the endpoint. + /// A string representing a brief description of the endpoint. /// A that can be used to further customize the endpoint. public static RouteHandlerBuilder WithSummary(this RouteHandlerBuilder builder, string summary) { From 95ff9ed4d4544027e0c7095f01fedadf6503108f Mon Sep 17 00:00:00 2001 From: Safia Abdalla Date: Fri, 11 Feb 2022 01:44:54 +0000 Subject: [PATCH 4/4] Remove AttributeTargets.Delegate from attributes --- src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs | 2 +- src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs b/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs index b5ea17b23285..c8e7b51a2614 100644 --- a/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs +++ b/src/Http/Http.Extensions/src/EndpointDescriptionAttribute.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Http; /// The OpenAPI specification supports a description attribute on operations and parameters that /// can be used to annotate endpoints with detailed, multiline descriptors of their behavior. /// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] public sealed class EndpointDescriptionAttribute : Attribute, IEndpointDescriptionMetadata { /// diff --git a/src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs b/src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs index 7ce57268beef..d5435365dc4e 100644 --- a/src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs +++ b/src/Http/Http.Extensions/src/EndpointSummaryAttribute.cs @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Http; /// /// Specifies a summary in . /// -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)] +[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] public sealed class EndpointSummaryAttribute : Attribute, IEndpointSummaryMetadata { ///