Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,31 @@ public static TBuilder WithMetadata<TBuilder>(this TBuilder builder, params obje

return builder;
}

/// <summary>
/// Sets the <see cref="EndpointNameMetadata"/> for all endpoints produced
/// on the target <see cref="IEndpointConventionBuilder"/>.
/// </summary>
/// <param name="builder">The <see cref="IEndpointConventionBuilder"/>.</param>
/// <param name="endpointName">The endpoint name.</param>
/// <returns>The <see cref="IEndpointConventionBuilder"/>.</returns>
public static TBuilder WithName<TBuilder>(this TBuilder builder, string endpointName) where TBuilder : IEndpointConventionBuilder
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excited about this

{
builder.WithMetadata(new EndpointNameMetadata(endpointName));
return builder;
}

/// <summary>
/// Sets the <see cref="EndpointGroupNameMetadata"/> for all endpoints produced
/// on the target <see cref="IEndpointConventionBuilder"/>.
/// </summary>
/// <param name="builder">The <see cref="IEndpointConventionBuilder"/>.</param>
/// <param name="endpointGroupName">The endpoint group name.</param>
/// <returns>The <see cref="IEndpointConventionBuilder"/>.</returns>
public static TBuilder WithGroupName<TBuilder>(this TBuilder builder, string endpointGroupName) where TBuilder : IEndpointConventionBuilder
{
builder.WithMetadata(new EndpointGroupNameMetadata(endpointGroupName));
return builder;
}
}
}
34 changes: 34 additions & 0 deletions src/Http/Routing/src/EndpointGroupNameAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Specifies the endpoint group name in <see cref="Microsoft.AspNetCore.Http.Endpoint.Metadata"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)]
public sealed class EndpointGroupNameAttribute : Attribute, IEndpointGroupNameMetadata
{
/// <summary>
/// Initializes an instance of the EndpointGroupNameAttribute.
/// </summary>
/// <param name="endpointGroupName">The endpoint name.</param>
public EndpointGroupNameAttribute(string endpointGroupName)
{
if (endpointGroupName == null)
{
throw new ArgumentNullException(nameof(endpointGroupName));
}

EndpointGroupName = endpointGroupName;
}

/// <summary>
/// The endpoint group name.
/// </summary>
public string EndpointGroupName { get; }
}
}
33 changes: 33 additions & 0 deletions src/Http/Routing/src/EndpointGroupNameMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Specifies an endpoint group name in <see cref="Microsoft.AspNetCore.Http.Endpoint.Metadata"/>.
/// </summary>
public class EndpointGroupNameMetadata : IEndpointGroupNameMetadata
{
/// <summary>
/// Creates a new instance of <see cref="EndpointGroupNameMetadata"/> with the provided endpoint group name.
/// </summary>
/// <param name="endpointGroupName">The endpoint group name.</param>
public EndpointGroupNameMetadata(string endpointGroupName)
{
if (endpointGroupName == null)
{
throw new ArgumentNullException(nameof(endpointGroupName));
}

EndpointGroupName = endpointGroupName;
}

/// <summary>
/// Gets the endpoint group name.
/// </summary>
public string EndpointGroupName { get; }
}
}
38 changes: 38 additions & 0 deletions src/Http/Routing/src/EndpointNameAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Specifies the endpoint name in <see cref="Endpoint.Metadata"/>.
/// </summary>
/// <remarks>
/// Endpoint names must be unique within an application, and can be used to unambiguously
/// identify a desired endpoint for URI generation using <see cref="Microsoft.AspNetCore.Routing.LinkGenerator"/>
/// </remarks>
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Delegate, Inherited = false, AllowMultiple = false)]
public sealed class EndpointNameAttribute : Attribute, IEndpointNameMetadata
{
/// <summary>
/// Initializes an instance of the EndpointNameAttribute.
/// </summary>
/// <param name="endpointName">The endpoint name.</param>
public EndpointNameAttribute(string endpointName)
{
if (endpointName == null)
{
throw new ArgumentNullException(nameof(endpointName));
}

EndpointName = endpointName;
}

/// <summary>
/// The endpoint name.
/// </summary>
public string EndpointName { get; }
}
}
18 changes: 18 additions & 0 deletions src/Http/Routing/src/IEndpointGroupNameMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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;

namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Defines a contract used to specify an endpoint group name in <see cref="Endpoint.Metadata"/>.
/// </summary>
public interface IEndpointGroupNameMetadata
{
/// <summary>
/// Gets the endpoint group name.
/// </summary>
string EndpointGroupName { get; }
}
}
20 changes: 20 additions & 0 deletions src/Http/Routing/src/ISuppressApiMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Indicates that API explorer data should not be emitted for this endpoint.
/// </summary>
public interface ISuppressApiMetadata
{
/// <summary>
/// Gets a value indicating whether API explorer
/// data should be emitted for this endpoint.
/// </summary>
bool SuppressApi { get; }
}
}
18 changes: 18 additions & 0 deletions src/Http/Routing/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,21 @@ static Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.
static Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapMethods(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Collections.Generic.IEnumerable<string!>! httpMethods, System.Delegate! action) -> Microsoft.AspNetCore.Builder.MinimalActionEndpointConventionBuilder!
static Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapPost(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! action) -> Microsoft.AspNetCore.Builder.MinimalActionEndpointConventionBuilder!
static Microsoft.AspNetCore.Builder.MinimalActionEndpointRouteBuilderExtensions.MapPut(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder! endpoints, string! pattern, System.Delegate! action) -> Microsoft.AspNetCore.Builder.MinimalActionEndpointConventionBuilder!
Microsoft.AspNetCore.Routing.IEndpointGroupNameMetadata
Microsoft.AspNetCore.Routing.IEndpointGroupNameMetadata.EndpointGroupName.get -> string!
Microsoft.AspNetCore.Routing.EndpointGroupNameMetadata
Microsoft.AspNetCore.Routing.EndpointGroupNameMetadata.EndpointGroupNameMetadata(string! endpointGroupName) -> void
Microsoft.AspNetCore.Routing.EndpointGroupNameMetadata.EndpointGroupName.get -> string!
Microsoft.AspNetCore.Routing.EndpointGroupNameAttribute
Microsoft.AspNetCore.Routing.EndpointGroupNameAttribute.EndpointGroupNameAttribute(string! endpointGroupName) -> void
Microsoft.AspNetCore.Routing.EndpointGroupNameAttribute.EndpointGroupName.get -> string!
Microsoft.AspNetCore.Routing.EndpointNameAttribute
Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointNameAttribute(string! endpointName) -> void
Microsoft.AspNetCore.Routing.EndpointNameAttribute.EndpointName.get -> string!
static Microsoft.AspNetCore.Builder.RoutingEndpointConventionBuilderExtensions.WithName<TBuilder>(this TBuilder builder, string! endpointName) -> TBuilder
static Microsoft.AspNetCore.Builder.RoutingEndpointConventionBuilderExtensions.WithGroupName<TBuilder>(this TBuilder builder, string! endpointGroupName) -> TBuilder
Microsoft.AspNetCore.Routing.ISuppressApiMetadata
Microsoft.AspNetCore.Routing.ISuppressApiMetadata.SuppressApi.get -> bool
Microsoft.AspNetCore.Routing.SuppressApiMetadata
Microsoft.AspNetCore.Routing.SuppressApiMetadata.SuppressApiMetadata() -> void
Microsoft.AspNetCore.Routing.SuppressApiMetadata.SuppressApi.get -> bool
20 changes: 20 additions & 0 deletions src/Http/Routing/src/SuppressApiMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using Microsoft.AspNetCore.Http;

namespace Microsoft.AspNetCore.Routing
{
/// <summary>
/// Indicates that API explorer data should not be emitted for this endpoint.
/// </summary>
public sealed class SuppressApiMetadata : ISuppressApiMetadata
{
/// <summary>
/// Gets a value indicating whether API explorer
/// data should be emitted for this endpoint.
/// </summary>
public bool SuppressApi => true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,38 @@ public void WithMetadata_ChainedCall_ReturnedBuilderIsDerivedType()
Assert.True(chainedBuilder.TestProperty);
}

[Fact]
public void WithName_SetsEndpointName()
{
// Arrange
var builder = CreateBuilder();

// Act
builder.WithName("SomeEndpointName");

// Assert
var endpoint = builder.Build();

var endpointName = endpoint.Metadata.GetMetadata<EndpointNameMetadata>();
Assert.Equal("SomeEndpointName", endpointName.EndpointName);
}

[Fact]
public void WithGroupName_SetsEndpointGroupName()
{
// Arrange
var builder = CreateBuilder();

// Act
builder.WithGroupName("SomeEndpointGroupName");

// Assert
var endpoint = builder.Build();

var endpointGroupName = endpoint.Metadata.GetMetadata<EndpointGroupNameMetadata>();
Assert.Equal("SomeEndpointGroupName", endpointGroupName.EndpointGroupName);
}

private TestEndpointConventionBuilder CreateBuilder()
{
var conventionBuilder = new DefaultEndpointConventionBuilder(new RouteEndpointBuilder(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ public void OnProvidersExecuting(ApiDescriptionProviderContext context)
{
if (endpoint is RouteEndpoint routeEndpoint &&
routeEndpoint.Metadata.GetMetadata<MethodInfo>() is { } methodInfo &&
routeEndpoint.Metadata.GetMetadata<IHttpMethodMetadata>() is { } httpMethodMetadata)
routeEndpoint.Metadata.GetMetadata<IHttpMethodMetadata>() is { } httpMethodMetadata &&
(routeEndpoint.Metadata.GetMetadata<ISuppressApiMetadata>() == null ||
routeEndpoint.Metadata.GetMetadata<ISuppressApiMetadata>() is { SuppressApi: false} ))
{
// REVIEW: Should we add an ApiDescription for endpoints without IHttpMethodMetadata? Swagger doesn't handle
// a null HttpMethod even though it's nullable on ApiDescription, so we'd need to define "default" HTTP methods.
Expand Down Expand Up @@ -89,6 +91,7 @@ private ApiDescription CreateApiDescription(RouteEndpoint routeEndpoint, string
var apiDescription = new ApiDescription
{
HttpMethod = httpMethod,
GroupName = routeEndpoint.Metadata.GetMetadata<EndpointGroupNameMetadata>()?.EndpointGroupName,
RelativePath = routeEndpoint.RoutePattern.RawText?.TrimStart('/'),
ActionDescriptor = new ActionDescriptor
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ public void GetApiDescription_ReturnsActionResultWithProduces_And_ProducesConten
// Arrange
var action = CreateActionDescriptor(methodName, controllerType);
action.FilterDescriptors = filterDescriptors;
var expectedMediaTypes = new[] { "application/json", "text/json" };
var expectedMediaTypes = new[] { "application/json", "application/xml", "text/json", "text/xml" };

// Act
var descriptions = GetApiDescriptions(action);
Expand Down Expand Up @@ -677,7 +677,7 @@ public void GetApiDescription_ReturnsVoidWithProducesContentType(
// Arrange
var action = CreateActionDescriptor(methodName, controllerType);
action.FilterDescriptors = filterDescriptors;
var expectedMediaTypes = new[] { "application/json", "text/json" };
var expectedMediaTypes = new[] { "application/json", "application/xml", "text/json", "text/xml" };

// Act
var descriptions = GetApiDescriptions(action);
Expand Down Expand Up @@ -740,7 +740,7 @@ public void GetApiDescription_ReturnsActionResultOfTWithProducesContentType(
new ProducesResponseTypeAttribute(typeof(ErrorDetails), 500),
FilterScope.Action)
};
var expectedMediaTypes = new[] { "application/json", "text/json" };
var expectedMediaTypes = new[] { "application/json", "application/xml", "text/json", "text/xml" };

// Act
var descriptions = GetApiDescriptions(action);
Expand Down Expand Up @@ -810,7 +810,7 @@ public void GetApiDescription_ReturnsActionResultOfTWithProducesContentType_ForS
new ProducesResponseTypeAttribute(typeof(ErrorDetails), 500),
FilterScope.Action)
};
var expectedMediaTypes = new[] { "application/json", "text/json" };
var expectedMediaTypes = new[] { "application/json", "application/xml", "text/json", "text/xml" };

// Act
var descriptions = GetApiDescriptions(action);
Expand Down Expand Up @@ -880,7 +880,7 @@ public void GetApiDescription_ReturnsActionResultOfSequenceOfTWithProducesConten
new ProducesResponseTypeAttribute(typeof(ErrorDetails), 500),
FilterScope.Action)
};
var expectedMediaTypes = new[] { "application/json", "text/json" };
var expectedMediaTypes = new[] { "application/json", "application/xml", "text/json", "text/xml" };

// Act
var descriptions = GetApiDescriptions(action);
Expand Down
Loading