Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support setting operation IDs from EndpointName metadata #2173

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Routing;

namespace Swashbuckle.AspNetCore.SwaggerGen
{
Expand Down Expand Up @@ -61,7 +62,19 @@ private bool DefaultDocInclusionPredicate(string documentName, ApiDescription ap

private string DefaultOperationIdSelector(ApiDescription apiDescription)
{
return apiDescription.ActionDescriptor.AttributeRouteInfo?.Name;
var actionDescriptor = apiDescription.ActionDescriptor;

// Resolve the operation ID from the route name and fallback to the
// endpoint name if no route name is available. This allows us to
// generate operation IDs for endpoints that are defined using
// minimal APIs.
#if (!NETSTANDARD2_0)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is ifdef'd out since the M.A.Routing 2.1.0 package does not contain the EndpointNameMetadata types that we need here. We could update the repo to rely on M.A.* 2.2.0 packages for everything if we wanted to avoid if-defing here but that seemed more appropriate for a separate PR.

return
actionDescriptor.AttributeRouteInfo?.Name
?? (actionDescriptor.EndpointMetadata.FirstOrDefault(m => m is IEndpointNameMetadata) as IEndpointNameMetadata)?.EndpointName;
#else
return actionDescriptor.AttributeRouteInfo?.Name;
#endif
}

private IList<string> DefaultTagsSelector(ApiDescription apiDescription)
Expand Down
@@ -1,9 +1,12 @@
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.OpenApi.Models;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Microsoft.OpenApi.Any;
using Xunit;
using Swashbuckle.AspNetCore.Swagger;
Expand Down Expand Up @@ -77,6 +80,31 @@ public void GetSwagger_SetsOperationIdToRouteName_IfActionHasRouteNameMetadata()
Assert.Equal("SomeRouteName", document.Paths["/resource"].Operations[OperationType.Post].OperationId);
}

[Fact]
public void GetSwagger_SetsOperationIdToEndpointName_IfActionHasEndpointNameMetadata()
{
var methodInfo = typeof(FakeController).GetMethod(nameof(FakeController.ActionWithParameter));
var actionDescriptor = new ActionDescriptor
{
EndpointMetadata = new List<object>() { new EndpointNameMetadata("SomeEndpointName") },
RouteValues = new Dictionary<string, string>
{
["controller"] = methodInfo.DeclaringType.Name.Replace("Controller", string.Empty)
}
};
var subject = Subject(
apiDescriptions: new[]
{
ApiDescriptionFactory.Create(actionDescriptor, methodInfo, groupName: "v1", httpMethod: "POST", relativePath: "resource"),
}
);

var document = subject.GetSwagger("v1");

Assert.Equal("SomeEndpointName", document.Paths["/resource"].Operations[OperationType.Post].OperationId);
}


[Fact]
public void GetSwagger_SetsDeprecated_IfActionHasObsoleteAttribute()
{
Expand Down
Expand Up @@ -12,6 +12,7 @@ namespace Swashbuckle.AspNetCore.TestSupport
public static class ApiDescriptionFactory
{
public static ApiDescription Create(
ActionDescriptor actionDescriptor,
MethodInfo methodInfo,
string groupName = "v1",
string httpMethod = "POST",
Expand All @@ -20,8 +21,6 @@ public static class ApiDescriptionFactory
IEnumerable<ApiRequestFormat> supportedRequestFormats = null,
IEnumerable<ApiResponseType> supportedResponseTypes = null)
{
var actionDescriptor = CreateActionDescriptor(methodInfo);

var apiDescription = new ApiDescription
{
ActionDescriptor = actionDescriptor,
Expand Down Expand Up @@ -74,6 +73,30 @@ public static class ApiDescriptionFactory
return apiDescription;
}

public static ApiDescription Create(
MethodInfo methodInfo,
string groupName = "v1",
string httpMethod = "POST",
string relativePath = "resoure",
IEnumerable<ApiParameterDescription> parameterDescriptions = null,
IEnumerable<ApiRequestFormat> supportedRequestFormats = null,
IEnumerable<ApiResponseType> supportedResponseTypes = null)
{

var actionDescriptor = CreateActionDescriptor(methodInfo);

return Create(
actionDescriptor,
methodInfo,
groupName,
httpMethod,
relativePath,
parameterDescriptions,
supportedRequestFormats,
supportedResponseTypes
);
}

public static ApiDescription Create<TController>(
Func<TController, string> actionNameSelector,
string groupName = "v1",
Expand Down