Skip to content

Commit

Permalink
Process review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
maurei committed Nov 19, 2021
1 parent e07903c commit a1b0a7b
Show file tree
Hide file tree
Showing 32 changed files with 831 additions and 324 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.OpenApi.JsonApiMetadata;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -26,15 +25,14 @@ internal sealed class JsonApiActionDescriptorCollectionProvider : IActionDescrip

public ActionDescriptorCollection ActionDescriptors => GetActionDescriptors();

public JsonApiActionDescriptorCollectionProvider(IResourceGraph resourceGraph, IControllerResourceMapping controllerResourceMapping,
public JsonApiActionDescriptorCollectionProvider(IControllerResourceMapping controllerResourceMapping,
IActionDescriptorCollectionProvider defaultProvider)
{
ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph));
ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping));
ArgumentGuard.NotNull(defaultProvider, nameof(defaultProvider));

_defaultProvider = defaultProvider;
_jsonApiEndpointMetadataProvider = new JsonApiEndpointMetadataProvider(resourceGraph, controllerResourceMapping);
_jsonApiEndpointMetadataProvider = new JsonApiEndpointMetadataProvider(controllerResourceMapping);
}

private ActionDescriptorCollection GetActionDescriptors()
Expand Down Expand Up @@ -67,27 +65,26 @@ private static bool IsVisibleJsonApiEndpoint(ActionDescriptor descriptor)
return descriptor is ControllerActionDescriptor controllerAction && controllerAction.Properties.ContainsKey(typeof(ApiDescriptionActionData));
}

private static IList<ActionDescriptor> AddJsonApiMetadataToAction(ActionDescriptor endpoint, IJsonApiEndpointMetadata? jsonApiEndpointMetadata)
private static IEnumerable<ActionDescriptor> AddJsonApiMetadataToAction(ActionDescriptor endpoint, IJsonApiEndpointMetadata? jsonApiEndpointMetadata)
{
switch (jsonApiEndpointMetadata)
{
case PrimaryResponseMetadata primaryMetadata:
{
UpdateProducesResponseTypeAttribute(endpoint, primaryMetadata.Type);
UpdateProducesResponseTypeAttribute(endpoint, primaryMetadata.DocumentType);
return Array.Empty<ActionDescriptor>();
}
case PrimaryRequestMetadata primaryMetadata:
{
UpdateBodyParameterDescriptor(endpoint, primaryMetadata.Type);
UpdateBodyParameterDescriptor(endpoint, primaryMetadata.DocumentType);
return Array.Empty<ActionDescriptor>();
}
case ExpansibleEndpointMetadata expansibleMetadata
when expansibleMetadata is RelationshipResponseMetadata || expansibleMetadata is SecondaryResponseMetadata:
case ExpansibleEndpointMetadata expansibleMetadata and (RelationshipResponseMetadata or SecondaryResponseMetadata):
{
return Expand(endpoint, expansibleMetadata,
(expandedEndpoint, relationshipType, _) => UpdateProducesResponseTypeAttribute(expandedEndpoint, relationshipType));
(expandedEndpoint, documentType, _) => UpdateProducesResponseTypeAttribute(expandedEndpoint, documentType));
}
case ExpansibleEndpointMetadata expansibleMetadata when expansibleMetadata is RelationshipRequestMetadata:
case ExpansibleEndpointMetadata expansibleMetadata and RelationshipRequestMetadata:
{
return Expand(endpoint, expansibleMetadata, UpdateBodyParameterDescriptor);
}
Expand All @@ -98,56 +95,56 @@ private static IList<ActionDescriptor> AddJsonApiMetadataToAction(ActionDescript
}
}

private static void UpdateProducesResponseTypeAttribute(ActionDescriptor endpoint, Type responseTypeToSet)
private static void UpdateProducesResponseTypeAttribute(ActionDescriptor endpoint, Type responseDocumentType)
{
if (ProducesJsonApiResponseBody(endpoint))
if (ProducesJsonApiResponseDocument(endpoint))
{
var producesResponse = endpoint.GetFilterMetadata<ProducesResponseTypeAttribute>();

if (producesResponse != null)
{
producesResponse.Type = responseTypeToSet;

producesResponse.Type = responseDocumentType;
return;
}
}

throw new UnreachableCodeException();
}

private static bool ProducesJsonApiResponseBody(ActionDescriptor endpoint)
private static bool ProducesJsonApiResponseDocument(ActionDescriptor endpoint)
{
var produces = endpoint.GetFilterMetadata<ProducesAttribute>();

return produces != null && produces.ContentTypes.Any(contentType => contentType == HeaderConstants.MediaType);
}

private static IList<ActionDescriptor> Expand(ActionDescriptor genericEndpoint, ExpansibleEndpointMetadata metadata,
private static IEnumerable<ActionDescriptor> Expand(ActionDescriptor genericEndpoint, ExpansibleEndpointMetadata metadata,
Action<ActionDescriptor, Type, string> expansionCallback)
{
var expansion = new List<ActionDescriptor>();

foreach ((string relationshipName, Type relationshipType) in metadata.ExpansionElements)
foreach ((string relationshipName, Type documentType) in metadata.DocumentTypesByRelationshipName)
{
ActionDescriptor expandedEndpoint = Clone(genericEndpoint);
RemovePathParameter(expandedEndpoint.Parameters, JsonApiPathParameter.RelationshipName);

if (expandedEndpoint.AttributeRouteInfo == null)
if (genericEndpoint.AttributeRouteInfo == null)
{
throw new NotSupportedException("Only attribute based routing is supported for JsonApiDotNetCore endpoints");
throw new NotSupportedException("Only attribute routing is supported for JsonApiDotNetCore endpoints.");
}

ExpandTemplate(expandedEndpoint.AttributeRouteInfo, relationshipName);
ActionDescriptor expandedEndpoint = Clone(genericEndpoint);

RemovePathParameter(expandedEndpoint.Parameters, JsonApiPathParameter.RelationshipName);

ExpandTemplate(expandedEndpoint.AttributeRouteInfo!, relationshipName);

expansionCallback(expandedEndpoint, relationshipType, relationshipName);
expansionCallback(expandedEndpoint, documentType, relationshipName);

expansion.Add(expandedEndpoint);
}

return expansion;
}

private static void UpdateBodyParameterDescriptor(ActionDescriptor endpoint, Type bodyType, string? parameterName = null)
private static void UpdateBodyParameterDescriptor(ActionDescriptor endpoint, Type documentType, string? parameterName = null)
{
ControllerParameterDescriptor? requestBodyDescriptor = endpoint.GetBodyParameterDescriptor();

Expand All @@ -157,8 +154,8 @@ private static void UpdateBodyParameterDescriptor(ActionDescriptor endpoint, Typ
throw new UnreachableCodeException();
}

requestBodyDescriptor.ParameterType = bodyType;
ParameterInfo replacementParameterInfo = requestBodyDescriptor.ParameterInfo.WithParameterType(bodyType);
requestBodyDescriptor.ParameterType = documentType;
ParameterInfo replacementParameterInfo = requestBodyDescriptor.ParameterInfo.WithParameterType(documentType);

if (parameterName != null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiMetadata
{
internal abstract class ExpansibleEndpointMetadata
{
public abstract IDictionary<string, Type> ExpansionElements { get; }
public IDictionary<string, Type> DocumentTypesByRelationshipName { get; }

protected ExpansibleEndpointMetadata(IDictionary<string, Type> documentTypesByRelationshipName)
{
ArgumentGuard.NotNull(documentTypesByRelationshipName, nameof(documentTypesByRelationshipName));

DocumentTypesByRelationshipName = documentTypesByRelationshipName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
using System.Reflection;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.OpenApi.JsonApiObjects;
using JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
using JsonApiDotNetCore.OpenApi.JsonApiObjects.RelationshipData;
using JsonApiDotNetCore.Resources.Annotations;

namespace JsonApiDotNetCore.OpenApi.JsonApiMetadata
Expand All @@ -16,16 +16,14 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiMetadata
/// </summary>
internal sealed class JsonApiEndpointMetadataProvider
{
private readonly IResourceGraph _resourceGraph;
private readonly IControllerResourceMapping _controllerResourceMapping;
private readonly EndpointResolver _endpointResolver = new();
private readonly NonPrimaryDocumentTypeFactory _nonPrimaryDocumentTypeFactory = new();

public JsonApiEndpointMetadataProvider(IResourceGraph resourceGraph, IControllerResourceMapping controllerResourceMapping)
public JsonApiEndpointMetadataProvider(IControllerResourceMapping controllerResourceMapping)
{
ArgumentGuard.NotNull(resourceGraph, nameof(resourceGraph));
ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping));

_resourceGraph = resourceGraph;
_controllerResourceMapping = controllerResourceMapping;
}

Expand All @@ -47,28 +45,28 @@ public JsonApiEndpointMetadataContainer Get(MethodInfo controllerAction)
throw new UnreachableCodeException();
}

IJsonApiRequestMetadata? requestMetadata = GetRequestMetadata(endpoint.Value, primaryResourceType.ClrType);
IJsonApiResponseMetadata? responseMetadata = GetResponseMetadata(endpoint.Value, primaryResourceType.ClrType);
IJsonApiRequestMetadata? requestMetadata = GetRequestMetadata(endpoint.Value, primaryResourceType);
IJsonApiResponseMetadata? responseMetadata = GetResponseMetadata(endpoint.Value, primaryResourceType);
return new JsonApiEndpointMetadataContainer(requestMetadata, responseMetadata);
}

private IJsonApiRequestMetadata? GetRequestMetadata(JsonApiEndpoint endpoint, Type primaryResourceType)
private IJsonApiRequestMetadata? GetRequestMetadata(JsonApiEndpoint endpoint, ResourceType primaryResourceType)
{
switch (endpoint)
{
case JsonApiEndpoint.Post:
{
return GetPostRequestMetadata(primaryResourceType);
return GetPostRequestMetadata(primaryResourceType.ClrType);
}
case JsonApiEndpoint.Patch:
{
return GetPatchRequestMetadata(primaryResourceType);
return GetPatchRequestMetadata(primaryResourceType.ClrType);
}
case JsonApiEndpoint.PostRelationship:
case JsonApiEndpoint.PatchRelationship:
case JsonApiEndpoint.DeleteRelationship:
{
return GetRelationshipRequestMetadata(primaryResourceType, endpoint != JsonApiEndpoint.PatchRelationship);
return GetRelationshipRequestMetadata(primaryResourceType.Relationships, endpoint != JsonApiEndpoint.PatchRelationship);
}
default:
{
Expand All @@ -77,38 +75,31 @@ public JsonApiEndpointMetadataContainer Get(MethodInfo controllerAction)
}
}

private static PrimaryRequestMetadata GetPostRequestMetadata(Type primaryResourceType)
private static PrimaryRequestMetadata GetPostRequestMetadata(Type resourceClrType)
{
Type documentType = typeof(ResourcePostRequestDocument<>).MakeGenericType(primaryResourceType);
Type documentType = typeof(ResourcePostRequestDocument<>).MakeGenericType(resourceClrType);

return new PrimaryRequestMetadata(documentType);
}

private static PrimaryRequestMetadata GetPatchRequestMetadata(Type primaryResourceType)
private static PrimaryRequestMetadata GetPatchRequestMetadata(Type resourceClrType)
{
Type documentType = typeof(ResourcePatchRequestDocument<>).MakeGenericType(primaryResourceType);
Type documentType = typeof(ResourcePatchRequestDocument<>).MakeGenericType(resourceClrType);

return new PrimaryRequestMetadata(documentType);
}

private RelationshipRequestMetadata GetRelationshipRequestMetadata(Type primaryResourceType, bool ignoreHasOneRelationships)
private RelationshipRequestMetadata GetRelationshipRequestMetadata(IEnumerable<RelationshipAttribute> relationships, bool ignoreHasOneRelationships)
{
IEnumerable<RelationshipAttribute> relationships = _resourceGraph.GetResourceType(primaryResourceType).Relationships;
IEnumerable<RelationshipAttribute> relationshipsOfEndpoint = ignoreHasOneRelationships ? relationships.OfType<HasManyAttribute>() : relationships;

if (ignoreHasOneRelationships)
{
relationships = relationships.OfType<HasManyAttribute>();
}

IDictionary<string, Type> resourceTypesByRelationshipName = relationships.ToDictionary(relationship => relationship.PublicName,
relationship => relationship is HasManyAttribute
? typeof(ToManyRelationshipRequestData<>).MakeGenericType(relationship.RightType.ClrType)
: typeof(ToOneRelationshipRequestData<>).MakeGenericType(relationship.RightType.ClrType));
IDictionary<string, Type> requestDocumentTypesByRelationshipName = relationshipsOfEndpoint.ToDictionary(relationship => relationship.PublicName,
_nonPrimaryDocumentTypeFactory.GetForRelationshipRequest);

return new RelationshipRequestMetadata(resourceTypesByRelationshipName);
return new RelationshipRequestMetadata(requestDocumentTypesByRelationshipName);
}

private IJsonApiResponseMetadata? GetResponseMetadata(JsonApiEndpoint endpoint, Type primaryResourceType)
private IJsonApiResponseMetadata? GetResponseMetadata(JsonApiEndpoint endpoint, ResourceType primaryResourceType)
{
switch (endpoint)
{
Expand All @@ -117,15 +108,15 @@ private RelationshipRequestMetadata GetRelationshipRequestMetadata(Type primaryR
case JsonApiEndpoint.Post:
case JsonApiEndpoint.Patch:
{
return GetPrimaryResponseMetadata(primaryResourceType, endpoint == JsonApiEndpoint.GetCollection);
return GetPrimaryResponseMetadata(primaryResourceType.ClrType, endpoint == JsonApiEndpoint.GetCollection);
}
case JsonApiEndpoint.GetSecondary:
{
return GetSecondaryResponseMetadata(primaryResourceType);
return GetSecondaryResponseMetadata(primaryResourceType.Relationships);
}
case JsonApiEndpoint.GetRelationship:
{
return GetRelationshipResponseMetadata(primaryResourceType);
return GetRelationshipResponseMetadata(primaryResourceType.Relationships);
}
default:
{
Expand All @@ -134,44 +125,28 @@ private RelationshipRequestMetadata GetRelationshipRequestMetadata(Type primaryR
}
}

private static PrimaryResponseMetadata GetPrimaryResponseMetadata(Type primaryResourceType, bool endpointReturnsCollection)
private static PrimaryResponseMetadata GetPrimaryResponseMetadata(Type resourceClrType, bool endpointReturnsCollection)
{
Type documentOpenType = endpointReturnsCollection ? typeof(ResourceCollectionResponseDocument<>) : typeof(PrimaryResourceResponseDocument<>);
Type documentType = documentOpenType.MakeGenericType(primaryResourceType);
Type documentType = documentOpenType.MakeGenericType(resourceClrType);

return new PrimaryResponseMetadata(documentType);
}

private SecondaryResponseMetadata GetSecondaryResponseMetadata(Type primaryResourceType)
{
IDictionary<string, Type> responseTypesByRelationshipName = GetMetadataByRelationshipName(primaryResourceType, relationship =>
{
Type documentType = relationship is HasManyAttribute
? typeof(ResourceCollectionResponseDocument<>)
: typeof(SecondaryResourceResponseDocument<>);

return documentType.MakeGenericType(relationship.RightType.ClrType);
});

return new SecondaryResponseMetadata(responseTypesByRelationshipName);
}

private IDictionary<string, Type> GetMetadataByRelationshipName(Type primaryResourceType,
Func<RelationshipAttribute, Type> extractRelationshipMetadataCallback)
private SecondaryResponseMetadata GetSecondaryResponseMetadata(IEnumerable<RelationshipAttribute> relationships)
{
IReadOnlyCollection<RelationshipAttribute> relationships = _resourceGraph.GetResourceType(primaryResourceType).Relationships;
IDictionary<string, Type> responseDocumentTypesByRelationshipName = relationships.ToDictionary(relationship => relationship.PublicName,
_nonPrimaryDocumentTypeFactory.GetForSecondaryResponse);

return relationships.ToDictionary(relationship => relationship.PublicName, extractRelationshipMetadataCallback);
return new SecondaryResponseMetadata(responseDocumentTypesByRelationshipName);
}

private RelationshipResponseMetadata GetRelationshipResponseMetadata(Type primaryResourceType)
private RelationshipResponseMetadata GetRelationshipResponseMetadata(IEnumerable<RelationshipAttribute> relationships)
{
IDictionary<string, Type> responseTypesByRelationshipName = GetMetadataByRelationshipName(primaryResourceType,
relationship => relationship is HasManyAttribute
? typeof(ResourceIdentifierCollectionResponseDocument<>).MakeGenericType(relationship.RightType.ClrType)
: typeof(ResourceIdentifierResponseDocument<>).MakeGenericType(relationship.RightType.ClrType));
IDictionary<string, Type> responseDocumentTypesByRelationshipName = relationships.ToDictionary(relationship => relationship.PublicName,
_nonPrimaryDocumentTypeFactory.GetForRelationshipResponse);

return new RelationshipResponseMetadata(responseTypesByRelationshipName);
return new RelationshipResponseMetadata(responseDocumentTypesByRelationshipName);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiMetadata
{
internal sealed class PrimaryRequestMetadata : IJsonApiRequestMetadata
{
public Type Type { get; }
public Type DocumentType { get; }

public PrimaryRequestMetadata(Type type)
public PrimaryRequestMetadata(Type documentType)
{
ArgumentGuard.NotNull(type, nameof(type));
ArgumentGuard.NotNull(documentType, nameof(documentType));

Type = type;
DocumentType = documentType;
}
}
}
Loading

0 comments on commit a1b0a7b

Please sign in to comment.