Support EndpointGroupName metadata in MVC ApiExplorer (#37264)

* Support EndpointGroupName metadata in MVC ApiExplorer

* Address feedback from peer review

* Update src/Mvc/Mvc.ApiExplorer/src/DefaultApiDescriptionProvider.cs

Co-authored-by: Pranav K <prkrishn@hotmail.com>

Co-authored-by: Pranav K <prkrishn@hotmail.com>
This commit is contained in:
Safia Abdalla 2021-10-04 21:59:01 -07:00 коммит произвёл GitHub
Родитель 0cff8a75a3
Коммит fa946a82e9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 80 добавлений и 1 удалений

Просмотреть файл

@ -71,13 +71,17 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
continue;
}
// ApiDescriptionActionData is only added to the ControllerActionDescriptor if
// the action is marked as `IsVisible` to the ApiExplorer. This null-check is
// effectively asserting if the endpoint should be generated into the final
// OpenAPI metadata.
var extensionData = action.GetProperty<ApiDescriptionActionData>();
if (extensionData != null)
{
var httpMethods = GetHttpMethods(action);
foreach (var httpMethod in httpMethods)
{
context.Results.Add(CreateApiDescription(action, httpMethod, extensionData.GroupName));
context.Results.Add(CreateApiDescription(action, httpMethod, GetGroupName(action, extensionData)));
}
}
}
@ -463,6 +467,19 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
.ToArray();
}
private static string? GetGroupName(ControllerActionDescriptor action, ApiDescriptionActionData extensionData)
{
// The `GroupName` set in the `ApiDescriptionActionData` is either the
// group name set via [ApiExplorerSettings(GroupName = "foo")] on the
// action or controller. So, this lookup favors the following sequence:
// - EndpointGroupName on the action, if it is set
// - EndpointGroupName on the controller, if it is set
// - ApiExplorerSettings.GroupName on the action, if it is set
// - ApiExplorerSettings.GroupName on the controller, if it is set
var endpointGroupName = action.EndpointMetadata.OfType<IEndpointGroupNameMetadata>().LastOrDefault();
return endpointGroupName?.EndpointGroupName ?? extensionData.GroupName;
}
private class ApiParameterDescriptionContext
{
public ModelMetadata ModelMetadata { get; }

Просмотреть файл

@ -88,6 +88,37 @@ namespace Microsoft.AspNetCore.Mvc.Description
Assert.Equal("Customers", description.GroupName);
}
[Fact]
public void GetApiDescription_PopulatesGroupName_FromMetadata()
{
// Arrange
var action = CreateActionDescriptor();
action.EndpointMetadata = new List<object>() { new EndpointGroupNameAttribute("Customers") };
// Act
var descriptions = GetApiDescriptions(action);
// Assert
var description = Assert.Single(descriptions);
Assert.Equal("Customers", description.GroupName);
}
[Fact]
public void GetApiDescription_PopulatesGroupName_FromMetadataOrExtensionData()
{
// Arrange
var action = CreateActionDescriptor();
action.EndpointMetadata = new List<object>() { new EndpointGroupNameAttribute("Customers") };
action.GetProperty<ApiDescriptionActionData>().GroupName = "NotUsedCustomers";
// Act
var descriptions = GetApiDescriptions(action);
// Assert
var description = Assert.Single(descriptions);
Assert.Equal("Customers", description.GroupName);
}
[Fact]
public void GetApiDescription_HttpMethodIsNullWithoutConstraint()
{

Просмотреть файл

@ -120,6 +120,34 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal("SetOnAction", description.GroupName);
}
[Fact]
public async Task ApiExplorer_GroupName_SetByEndpointMetadataOnController()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/ApiExplorerApiController/ActionWithIdParameter");
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
// Assert
var description = Assert.Single(result);
Assert.Equal("GroupNameOnController", description.GroupName);
}
[Fact]
public async Task ApiExplorer_GroupName_SetByEndpointMetadataOnAction()
{
// Arrange & Act
var response = await Client.GetAsync("http://localhost/ApiExplorerApiController/ActionWithSomeParameters");
var body = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<List<ApiExplorerData>>(body);
// Assert
var description = Assert.Single(result);
Assert.Equal("GroupNameOnAction", description.GroupName);
}
[Fact]
public async Task ApiExplorer_RouteTemplate_DisplaysFixedRoute()
{

Просмотреть файл

@ -4,15 +4,18 @@
using System.IO;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
namespace ApiExplorerWebSite
{
[Route("ApiExplorerApiController/[action]")]
[ApiController]
[EndpointGroupName("GroupNameOnController")]
public class ApiExplorerApiController : Controller
{
public IActionResult ActionWithoutParameters() => Ok();
[EndpointGroupName("GroupNameOnAction")]
public void ActionWithSomeParameters(object input)
{
}