-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1) Add feature: remove Paths and Definitions from OpenApi documentati…
…on for specific controller action without accepted roles; 2) Add code refactoring.
- Loading branch information
Chebotov Nikolay
committed
Feb 28, 2020
1 parent
6a4fa35
commit 7d89e80
Showing
16 changed files
with
451 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
version: 2.1.{build} | ||
version: 2.2.{build} | ||
pull_requests: | ||
do_not_increment_build_number: true | ||
skip_tags: true | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
src/Unchase.Swashbuckle.AspNetCore.Extensions/Extensions/OpenApiDocumentExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Reflection; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.OpenApi.Models; | ||
using Unchase.Swashbuckle.AspNetCore.Extensions.Factories; | ||
using Unchase.Swashbuckle.AspNetCore.Extensions.Filters; | ||
|
||
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Extensions | ||
{ | ||
/// <summary> | ||
/// Extension methods for <see cref=" OpenApiDocument"/>. | ||
/// </summary> | ||
public static class OpenApiDocumentExtensions | ||
{ | ||
/// <summary> | ||
/// Remove Paths and Components from OpenApi documentation without accepted roles. | ||
/// </summary> | ||
/// <param name="openApiDoc"><see cref="OpenApiDocument"/>.</param> | ||
/// <param name="actionNameSelector">Action name selector.</param> | ||
/// <param name="acceptedRoles">Collection of accepted roles.</param> | ||
/// <returns> | ||
/// Returns <see cref="OpenApiDocument"/>. | ||
/// </returns> | ||
public static OpenApiDocument RemovePathsAndComponentsWithoutAcceptedRolesFor<TController>(this OpenApiDocument openApiDoc, Func<TController, string> actionNameSelector, | ||
IReadOnlyList<string> acceptedRoles) where TController : class, new() | ||
{ | ||
var actionDescriptor = ApiDescriptionFactory.Create(actionNameSelector, typeof(TController).GetCustomAttribute<RouteAttribute>().Template)?.ActionDescriptor; | ||
if (actionDescriptor != null) | ||
{ | ||
var paths = new Dictionary<MethodInfo, string> | ||
{ | ||
{ ((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)actionDescriptor).MethodInfo, actionDescriptor.AttributeRouteInfo.Template } | ||
}; | ||
|
||
HidePathsAndDefinitionsByRolesDocumentFilter.RemovePathsAndComponents(openApiDoc, paths, openApiDoc.Components.Schemas, acceptedRoles); | ||
} | ||
|
||
return openApiDoc; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
193 changes: 193 additions & 0 deletions
193
src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ApiDescriptionFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Microsoft.AspNetCore.Mvc.Abstractions; | ||
using Microsoft.AspNetCore.Mvc.ApiExplorer; | ||
using Microsoft.AspNetCore.Mvc.Controllers; | ||
using Microsoft.AspNetCore.Mvc.Routing; | ||
|
||
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Factories | ||
{ | ||
/// <summary> | ||
/// <see cref="ApiDescription"/> factory. | ||
/// </summary> | ||
internal static class ApiDescriptionFactory | ||
{ | ||
/// <summary> | ||
/// Create <see cref="ApiDescription"/>. | ||
/// </summary> | ||
/// <param name="controllerType">Type of Controller.</param> | ||
/// <param name="actionName">Controller action name.</param> | ||
/// <param name="groupName">Group name.</param> | ||
/// <param name="httpMethod">Http method.</param> | ||
/// <param name="relativePath">Relative path.</param> | ||
/// <param name="parameterDescriptions">Collection of <see cref="ApiParameterDescription"/>.</param> | ||
/// <param name="supportedRequestFormats">Collection of <see cref="ApiRequestFormat"/>.</param> | ||
/// <param name="supportedResponseTypes">Collection of <see cref="ApiResponseType"/>.</param> | ||
/// <returns> | ||
/// Returns <see cref="ApiDescription"/>. | ||
/// </returns> | ||
internal static ApiDescription Create( | ||
Type controllerType, | ||
string actionName, | ||
string groupName = null, | ||
string httpMethod = null, | ||
string relativePath = null, | ||
IEnumerable<ApiParameterDescription> parameterDescriptions = null, | ||
IEnumerable<ApiRequestFormat> supportedRequestFormats = null, | ||
IEnumerable<ApiResponseType> supportedResponseTypes = null) | ||
{ | ||
var methodInfo = controllerType.GetMethod(actionName); | ||
|
||
if (methodInfo == null) | ||
return null; | ||
|
||
var actionDescriptor = CreateActionDescriptor(methodInfo); | ||
|
||
var routAttr = controllerType.GetCustomAttributes().OfType<RouteAttribute>().LastOrDefault(); | ||
|
||
if (string.IsNullOrWhiteSpace(actionDescriptor.AttributeRouteInfo.Template)) | ||
return null; | ||
//throw new InvalidOperationException($"HttpMethod attribute of \"{methodInfo.Name}\" action in \"{controllerType.Name}\" controller must have a template specified."); | ||
|
||
if (routAttr != null && !string.IsNullOrWhiteSpace(routAttr.Template)) | ||
{ | ||
var template = routAttr.Template; | ||
actionDescriptor.AttributeRouteInfo.Template = template + "/" + actionDescriptor.AttributeRouteInfo.Template; | ||
} | ||
|
||
foreach (var routeValue in actionDescriptor.RouteValues) | ||
{ | ||
actionDescriptor.AttributeRouteInfo.Template = | ||
actionDescriptor.AttributeRouteInfo.Template.Replace($"[{routeValue.Key}]", routeValue.Value); | ||
} | ||
|
||
httpMethod = httpMethod ?? methodInfo?.GetCustomAttributes().OfType<HttpMethodAttribute>().FirstOrDefault()?.HttpMethods?.FirstOrDefault(); | ||
|
||
var apiDescription = new ApiDescription | ||
{ | ||
ActionDescriptor = actionDescriptor, | ||
GroupName = groupName, | ||
HttpMethod = httpMethod, | ||
RelativePath = relativePath, | ||
}; | ||
|
||
if (parameterDescriptions != null) | ||
{ | ||
foreach (var parameter in parameterDescriptions) | ||
{ | ||
// If the provided action has a matching parameter - use it to assign ParameterDescriptor & ModelMetadata | ||
var controllerParameterDescriptor = actionDescriptor.Parameters | ||
.OfType<ControllerParameterDescriptor>() | ||
.FirstOrDefault(parameterDescriptor => parameterDescriptor.Name == parameter.Name); | ||
|
||
if (controllerParameterDescriptor != null) | ||
{ | ||
parameter.ParameterDescriptor = controllerParameterDescriptor; | ||
parameter.ModelMetadata = ModelMetadataFactory.CreateForParameter(controllerParameterDescriptor.ParameterInfo); | ||
} | ||
|
||
apiDescription.ParameterDescriptions.Add(parameter); | ||
} | ||
} | ||
|
||
if (supportedRequestFormats != null) | ||
{ | ||
foreach (var requestFormat in supportedRequestFormats) | ||
{ | ||
apiDescription.SupportedRequestFormats.Add(requestFormat); | ||
} | ||
} | ||
|
||
if (supportedResponseTypes != null) | ||
{ | ||
foreach (var responseType in supportedResponseTypes) | ||
{ | ||
// If the provided action has a return value AND the response status is 2XX - use it to assign ModelMetadata | ||
if (methodInfo?.ReturnType != null && responseType.StatusCode / 100 == 2) | ||
{ | ||
responseType.ModelMetadata = ModelMetadataFactory.CreateForType(methodInfo.ReturnType); | ||
} | ||
|
||
apiDescription.SupportedResponseTypes.Add(responseType); | ||
} | ||
} | ||
|
||
return apiDescription; | ||
} | ||
|
||
/// <summary> | ||
/// Create <see cref="ApiDescription"/>. | ||
/// </summary> | ||
/// <typeparam name="TController">Type of Controller.</typeparam> | ||
/// <param name="actionNameSelector">Action name selector.</param> | ||
/// <param name="groupName">Group name.</param> | ||
/// <param name="httpMethod">Http method.</param> | ||
/// <param name="relativePath">Relative path.</param> | ||
/// <param name="parameterDescriptions">Collection of <see cref="ApiParameterDescription"/>.</param> | ||
/// <param name="supportedRequestFormats">Collection of <see cref="ApiRequestFormat"/>.</param> | ||
/// <param name="supportedResponseTypes">Collection of <see cref="ApiResponseType"/>.</param> | ||
/// <returns> | ||
/// Returns <see cref="ApiDescription"/>. | ||
/// </returns> | ||
internal static ApiDescription Create<TController>( | ||
Func<TController, string> actionNameSelector, | ||
string groupName = null, | ||
string httpMethod = null, | ||
string relativePath = null, | ||
IEnumerable<ApiParameterDescription> parameterDescriptions = null, | ||
IEnumerable<ApiRequestFormat> supportedRequestFormats = null, | ||
IEnumerable<ApiResponseType> supportedResponseTypes = null) | ||
where TController : new() | ||
{ | ||
return Create( | ||
typeof(TController), | ||
actionNameSelector(new TController()), | ||
groupName, | ||
httpMethod, | ||
relativePath, | ||
parameterDescriptions, | ||
supportedRequestFormats, | ||
supportedResponseTypes | ||
); | ||
} | ||
|
||
private static ActionDescriptor CreateActionDescriptor(MethodInfo methodInfo) | ||
{ | ||
var httpMethodAttribute = methodInfo.GetCustomAttribute<HttpMethodAttribute>(); | ||
var attributeRouteInfo = (httpMethodAttribute != null) | ||
? new AttributeRouteInfo { Template = httpMethodAttribute.Template, Name = httpMethodAttribute.Name } | ||
: null; | ||
|
||
var parameterDescriptors = methodInfo.GetParameters() | ||
.Select(CreateParameterDescriptor) | ||
.ToList(); | ||
|
||
var routeValues = new Dictionary<string, string> | ||
{ | ||
["controller"] = methodInfo.DeclaringType?.Name.Replace("Controller", string.Empty) | ||
}; | ||
|
||
return new ControllerActionDescriptor | ||
{ | ||
AttributeRouteInfo = attributeRouteInfo, | ||
ControllerTypeInfo = methodInfo.DeclaringType.GetTypeInfo(), | ||
MethodInfo = methodInfo, | ||
Parameters = parameterDescriptors, | ||
RouteValues = routeValues | ||
}; | ||
} | ||
|
||
private static ParameterDescriptor CreateParameterDescriptor(ParameterInfo parameterInfo) | ||
{ | ||
return new ControllerParameterDescriptor | ||
{ | ||
Name = parameterInfo.Name, | ||
ParameterInfo = parameterInfo, | ||
ParameterType = parameterInfo.ParameterType, | ||
}; | ||
} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
src/Unchase.Swashbuckle.AspNetCore.Extensions/Factories/ModelMetadataFactory.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
using System; | ||
using System.Reflection; | ||
using Microsoft.AspNetCore.Mvc.ModelBinding; | ||
|
||
namespace Unchase.Swashbuckle.AspNetCore.Extensions.Factories | ||
{ | ||
public static class ModelMetadataFactory | ||
{ | ||
public static ModelMetadata CreateForType(Type type) | ||
{ | ||
return new EmptyModelMetadataProvider().GetMetadataForType(type); | ||
} | ||
|
||
public static ModelMetadata CreateForProperty(Type containingType, string propertyName) | ||
{ | ||
return new EmptyModelMetadataProvider().GetMetadataForProperty(containingType, propertyName); | ||
} | ||
|
||
public static ModelMetadata CreateForParameter(ParameterInfo parameter) | ||
{ | ||
return new EmptyModelMetadataProvider().GetMetadataForParameter(parameter); | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.