Skip to content

Commit

Permalink
API - code improvements and use common practice (#420)
Browse files Browse the repository at this point in the history
  • Loading branch information
KrzysztofPajak authored Jul 22, 2023
1 parent 5d7a449 commit 80073fb
Show file tree
Hide file tree
Showing 88 changed files with 376 additions and 400 deletions.
22 changes: 5 additions & 17 deletions src/API/Grand.Api/ApiExplorer/ApiResponseTypeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,6 @@ public ICollection<ApiResponseType> GetApiResponseTypes(ControllerActionDescript

private static List<IApiResponseMetadataProvider> GetResponseMetadataAttributes(ControllerActionDescriptor action)
{
if (action.FilterDescriptors == null)
{
return new List<IApiResponseMetadataProvider>();
}

// This technique for enumerating filters will intentionally ignore any filter that is an IFilterFactory
// while searching for a filter that implements IApiResponseMetadataProvider.
//
Expand Down Expand Up @@ -114,11 +109,7 @@ private ICollection<ApiResponseType> GetApiResponseTypes(

return responseTypes;
}

private static ApiResponseFormat CreateDefaultApiResponseFormat()
{
return new ApiResponseFormat { MediaType = "application/json" };
}

// Shared with EndpointMetadataApiDescriptionProvider
internal static List<ApiResponseType> ReadResponseMetadata(
IReadOnlyList<IApiResponseMetadataProvider> responseMetadataAttributes,
Expand All @@ -133,9 +124,7 @@ internal static List<ApiResponseType> ReadResponseMetadata(
// Get the content type that the action explicitly set to support.
// Walk through all 'filter' attributes in order, and allow each one to see or override
// the results of the previous ones. This is similar to the execution path for content-negotiation.
if (responseMetadataAttributes != null)
{
foreach (var metadataAttribute in responseMetadataAttributes)
foreach (var metadataAttribute in responseMetadataAttributes)
{
// All ProducesXAttributes, except for ProducesResponseTypeAttribute do
// not allow multiple instances on the same method/class/etc. For those
Expand Down Expand Up @@ -201,8 +190,7 @@ internal static List<ApiResponseType> ReadResponseMetadata(
results[apiResponseType.StatusCode] = apiResponseType;
}
}
}


return results.Values.ToList();
}

Expand Down Expand Up @@ -266,7 +254,7 @@ internal static void CalculateResponseFormatForType(ApiResponseType apiResponse,
}


if (!isSupportedContentType && contentType != null)
if (!isSupportedContentType)
{
// No output formatter was found that supports this content type. Add the user specified content type as-is to the result.
apiResponse.ApiResponseFormats.Add(new ApiResponseFormat {
Expand Down Expand Up @@ -325,7 +313,7 @@ private static bool HasSignificantMetadataProvider(IReadOnlyList<IApiResponseMet
{
var provider = providers[i];

if (provider is ProducesAttribute producesAttribute && producesAttribute.Type is null)
if (provider is ProducesAttribute { Type: null })
{
// ProducesAttribute that does not specify type is considered not significant.
continue;
Expand Down
140 changes: 62 additions & 78 deletions src/API/Grand.Api/ApiExplorer/MetadataApiDescriptionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ public void OnProvidersExecuting(ApiDescriptionProviderContext context)

foreach (var action in context.Actions.OfType<ControllerActionDescriptor>())
{
if (!action.ControllerTypeInfo.GetCustomAttributes<Grand.SharedKernel.Attributes.ApiControllerAttribute>(true).Any())
if (!action.ControllerTypeInfo.GetCustomAttributes<SharedKernel.Attributes.ApiControllerAttribute>(true)
.Any())
continue;

if (action.MethodInfo.GetCustomAttributes<IgnoreApiAttribute>(true).Any())
Expand Down Expand Up @@ -97,20 +98,20 @@ private ApiDescription CreateApiDescription(
var parsedTemplate = ParseTemplate(action);
var path = GetRelativePath(action, parsedTemplate);

var apiDescription = new ApiDescription() {
var apiDescription = new ApiDescription {
ActionDescriptor = action,
GroupName = groupName,
HttpMethod = httpMethod,
RelativePath = path,
};

var templateParameters = parsedTemplate?.Parameters?.ToList() ?? new List<TemplatePart>();
var templateParameters = parsedTemplate?.Parameters.ToList() ?? new List<TemplatePart>();

var parameterContext = new ApiParameterContext(_modelMetadataProvider, action, templateParameters);
foreach (var parameter in GetParameters(parameterContext,
httpMethod.Equals("GET", StringComparison.CurrentCultureIgnoreCase)))
{
apiDescription.ParameterDescriptions.Add(parameter);
apiDescription.ParameterDescriptions.Add(parameter);
}

var requestMetadataAttributes = GetRequestMetadataAttributes(action);
Expand Down Expand Up @@ -158,70 +159,64 @@ private ApiDescription CreateApiDescription(
private IList<ApiParameterDescription> GetParameters(ApiParameterContext context, bool httpGet)
{
// First, get parameters from the model-binding/parameter-binding side of the world.
if (context.ActionDescriptor.Parameters != null)
foreach (var actionParameter in context.ActionDescriptor.Parameters)
{
foreach (var actionParameter in context.ActionDescriptor.Parameters)
{
var visitor = new PseudoModelBindingVisitor(context, actionParameter);

ModelMetadata metadata;
if (actionParameter is ControllerParameterDescriptor controllerParameterDescriptor &&
_modelMetadataProvider is ModelMetadataProvider provider)
{
// The default model metadata provider derives from ModelMetadataProvider
// and can therefore supply information about attributes applied to parameters.
metadata = provider.GetMetadataForParameter(controllerParameterDescriptor.ParameterInfo);
}
else
{
// For backward compatibility, if there's a custom model metadata provider that
// only implements the older IModelMetadataProvider interface, access the more
// limited metadata information it supplies. In this scenario, validation attributes
// are not supported on parameters.
metadata = _modelMetadataProvider.GetMetadataForType(actionParameter.ParameterType);
}
var visitor = new PseudoModelBindingVisitor(context, actionParameter);

if (!httpGet &&
actionParameter.BindingInfo == null &&
!CommonHelper.IsSimpleType(actionParameter.ParameterType))
{
var bindingContext = new ApiParameterDescriptionContext(
metadata,
new BindingInfo {
BindingSource = new BindingSource("Body", "Body", true, true)
},
propertyName: actionParameter.Name);
visitor.WalkParameter(bindingContext);
}
else
{
var bindingContext = new ApiParameterDescriptionContext(
metadata,
actionParameter.BindingInfo,
propertyName: actionParameter.Name);
visitor.WalkParameter(bindingContext);
}
ModelMetadata metadata;
if (actionParameter is ControllerParameterDescriptor controllerParameterDescriptor &&
_modelMetadataProvider is ModelMetadataProvider provider)
{
// The default model metadata provider derives from ModelMetadataProvider
// and can therefore supply information about attributes applied to parameters.
metadata = provider.GetMetadataForParameter(controllerParameterDescriptor.ParameterInfo);
}
else
{
// For backward compatibility, if there's a custom model metadata provider that
// only implements the older IModelMetadataProvider interface, access the more
// limited metadata information it supplies. In this scenario, validation attributes
// are not supported on parameters.
metadata = _modelMetadataProvider.GetMetadataForType(actionParameter.ParameterType);
}
}

if (context.ActionDescriptor.BoundProperties != null)
{
foreach (var actionParameter in context.ActionDescriptor.BoundProperties)
if (!httpGet &&
actionParameter.BindingInfo == null &&
!CommonHelper.IsSimpleType(actionParameter.ParameterType))
{
var visitor = new PseudoModelBindingVisitor(context, actionParameter);
var modelMetadata = context.MetadataProvider.GetMetadataForProperty(
containerType: context.ActionDescriptor.ControllerTypeInfo.AsType(),
var bindingContext = new ApiParameterDescriptionContext(
metadata,
new BindingInfo {
BindingSource = new BindingSource("Body", "Body", true, true)
},
propertyName: actionParameter.Name);

visitor.WalkParameter(bindingContext);
}
else
{
var bindingContext = new ApiParameterDescriptionContext(
modelMetadata,
metadata,
actionParameter.BindingInfo,
propertyName: actionParameter.Name);

visitor.WalkParameter(bindingContext);
}
}

foreach (var actionParameter in context.ActionDescriptor.BoundProperties)
{
var visitor = new PseudoModelBindingVisitor(context, actionParameter);
var modelMetadata = context.MetadataProvider.GetMetadataForProperty(
containerType: context.ActionDescriptor.ControllerTypeInfo.AsType(),
propertyName: actionParameter.Name);

var bindingContext = new ApiParameterDescriptionContext(
modelMetadata,
actionParameter.BindingInfo,
propertyName: actionParameter.Name);

visitor.WalkParameter(bindingContext);
}

for (var i = context.Results.Count - 1; i >= 0; i--)
{
// Remove any 'hidden' parameters. These are things that can't come from user input,
Expand Down Expand Up @@ -286,7 +281,7 @@ private void ProcessRouteParameters(ApiParameterContext context)
// a partner.
foreach (var routeParameter in routeParameters)
{
context.Results.Add(new ApiParameterDescription() {
context.Results.Add(new ApiParameterDescription {
Name = routeParameter.Key,
RouteInfo = routeParameter.Value,
Source = BindingSource.Path,
Expand Down Expand Up @@ -346,15 +341,12 @@ internal static void ProcessParameterDefaultValue(ApiParameterContext context)
private ApiParameterRouteInfo CreateRouteInfo(TemplatePart routeParameter)
{
var constraints = new List<IRouteConstraint>();
if (routeParameter.InlineConstraints != null)
foreach (var constraint in routeParameter.InlineConstraints)
{
foreach (var constraint in routeParameter.InlineConstraints)
{
constraints.Add(_constraintResolver.ResolveConstraint(constraint.Constraint)!);
}
constraints.Add(_constraintResolver.ResolveConstraint(constraint.Constraint)!);
}

return new ApiParameterRouteInfo() {
return new ApiParameterRouteInfo {
Constraints = constraints,
DefaultValue = routeParameter.DefaultValue,
IsOptional = routeParameter.IsOptional || routeParameter.DefaultValue != null,
Expand All @@ -363,19 +355,16 @@ private ApiParameterRouteInfo CreateRouteInfo(TemplatePart routeParameter)

private static IEnumerable<string> GetHttpMethods(ControllerActionDescriptor action)
{
return action.ActionConstraints is { Count: > 0 } ? action.ActionConstraints.OfType<HttpMethodActionConstraint>().SelectMany(c => c.HttpMethods) : new [] { string.Empty };
return action.ActionConstraints is { Count: > 0 }
? action.ActionConstraints.OfType<HttpMethodActionConstraint>().SelectMany(c => c.HttpMethods)
: new[] { string.Empty };
}

private static RouteTemplate? ParseTemplate(ControllerActionDescriptor action)
{
if (action.AttributeRouteInfo?.Template != null)
{
return TemplateParser.Parse(action.AttributeRouteInfo.Template);
}

var x = action.EndpointMetadata.FirstOrDefault();

return null;
return action.AttributeRouteInfo?.Template != null
? TemplateParser.Parse(action.AttributeRouteInfo.Template)
: null;
}

private string? GetRelativePath(ControllerActionDescriptor action, RouteTemplate? parsedTemplate)
Expand Down Expand Up @@ -430,7 +419,7 @@ private IReadOnlyList<ApiRequestFormat> GetSupportedFormats(MediaTypeCollection
{
foreach (var supportedType in supportedTypes.Where(x => x == "application/json"))
{
results.Add(new ApiRequestFormat() {
results.Add(new ApiRequestFormat {
Formatter = formatter,
MediaType = supportedType,
});
Expand Down Expand Up @@ -462,11 +451,6 @@ internal static MediaTypeCollection GetDeclaredContentTypes(

private static IApiRequestMetadataProvider[]? GetRequestMetadataAttributes(ControllerActionDescriptor action)
{
if (action.FilterDescriptors == null)
{
return null;
}

// This technique for enumerating filters will intentionally ignore any filter that is an IFilterFactory
// while searching for a filter that implements IApiRequestMetadataProvider.
//
Expand Down Expand Up @@ -621,7 +605,7 @@ private ApiParameterDescription CreateResult(
BindingSource source,
string containerName)
{
return new ApiParameterDescription() {
return new ApiParameterDescription {
ModelMetadata = bindingContext.ModelMetadata,
Name = GetName(containerName, bindingContext),
Source = source,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using Grand.Api.DTOs.Catalog;
using Grand.Api.Commands.Models.Catalog;
using Grand.Api.DTOs.Catalog;
using Grand.Api.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Brands;
using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Brands;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Common.Logging;
using Grand.Business.Core.Interfaces.Common.Seo;
using Grand.Domain.Seo;
using Grand.Infrastructure;
using MediatR;

namespace Grand.Api.Commands.Models.Catalog
namespace Grand.Api.Commands.Handlers.Catalog
{
public class AddBrandCommandHandler : IRequestHandler<AddBrandCommand, BrandDto>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using Grand.Api.DTOs.Catalog;
using Grand.Api.Commands.Models.Catalog;
using Grand.Api.DTOs.Catalog;
using Grand.Api.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Categories;
using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Categories;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Common.Logging;
using Grand.Business.Core.Interfaces.Common.Seo;
using Grand.Domain.Seo;
using Grand.Infrastructure;
using MediatR;

namespace Grand.Api.Commands.Models.Catalog
namespace Grand.Api.Commands.Handlers.Catalog
{
public class AddCategoryCommandHandler : IRequestHandler<AddCategoryCommand, CategoryDto>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
using Grand.Api.DTOs.Catalog;
using Grand.Api.Commands.Models.Catalog;
using Grand.Api.DTOs.Catalog;
using Grand.Api.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Collections;
using Grand.Business.Core.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Collections;
using Grand.Business.Core.Interfaces.Common.Localization;
using Grand.Business.Core.Interfaces.Common.Logging;
using Grand.Business.Core.Interfaces.Common.Seo;
using Grand.Domain.Seo;
using Grand.Infrastructure;
using MediatR;

namespace Grand.Api.Commands.Models.Catalog
namespace Grand.Api.Commands.Handlers.Catalog
{
public class AddCollectionCommandHandler : IRequestHandler<AddCollectionCommand, CollectionDto>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using Grand.Api.DTOs.Catalog;
using Grand.Api.Commands.Models.Catalog;
using Grand.Api.DTOs.Catalog;
using Grand.Api.Extensions;
using Grand.Business.Core.Interfaces.Catalog.Products;
using MediatR;

namespace Grand.Api.Commands.Models.Catalog
namespace Grand.Api.Commands.Handlers.Catalog
{
public class AddProductAttributeMappingCommandHandler : IRequestHandler<AddProductAttributeMappingCommand, ProductAttributeMappingDto>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Grand.Business.Core.Interfaces.Catalog.Categories;
using Grand.Api.Commands.Models.Catalog;
using Grand.Business.Core.Interfaces.Catalog.Categories;
using Grand.Domain.Catalog;
using MediatR;

namespace Grand.Api.Commands.Models.Catalog
namespace Grand.Api.Commands.Handlers.Catalog
{
public class AddProductCategoryCommandHandler : IRequestHandler<AddProductCategoryCommand, bool>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using Grand.Business.Core.Interfaces.Catalog.Collections;
using Grand.Api.Commands.Models.Catalog;
using Grand.Business.Core.Interfaces.Catalog.Collections;
using Grand.Domain.Catalog;
using MediatR;

namespace Grand.Api.Commands.Models.Catalog
namespace Grand.Api.Commands.Handlers.Catalog
{
public class AddProductCollectionCommandHandler : IRequestHandler<AddProductCollectionCommand, bool>
{
Expand Down
Loading

0 comments on commit 80073fb

Please sign in to comment.