-
-
Notifications
You must be signed in to change notification settings - Fork 163
Expand file tree
/
Copy pathJsonApiEndpointMetadataProvider.cs
More file actions
147 lines (120 loc) · 7.19 KB
/
JsonApiEndpointMetadataProvider.cs
File metadata and controls
147 lines (120 loc) · 7.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata.ActionMethods;
using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata.Documents;
using JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiObjects.Documents;
using JsonApiDotNetCore.Resources.Annotations;
using Microsoft.AspNetCore.Mvc.Abstractions;
namespace JsonApiDotNetCore.OpenApi.Swashbuckle.JsonApiMetadata;
/// <summary>
/// Provides JsonApiDotNetCore related metadata for an ASP.NET action method that can only be computed from the <see cref="ResourceGraph" /> at runtime.
/// </summary>
internal sealed class JsonApiEndpointMetadataProvider
{
private readonly IControllerResourceMapping _controllerResourceMapping;
private readonly NonPrimaryDocumentTypeFactory _nonPrimaryDocumentTypeFactory;
public JsonApiEndpointMetadataProvider(IControllerResourceMapping controllerResourceMapping, NonPrimaryDocumentTypeFactory nonPrimaryDocumentTypeFactory)
{
ArgumentNullException.ThrowIfNull(controllerResourceMapping);
ArgumentNullException.ThrowIfNull(nonPrimaryDocumentTypeFactory);
_controllerResourceMapping = controllerResourceMapping;
_nonPrimaryDocumentTypeFactory = nonPrimaryDocumentTypeFactory;
}
public JsonApiEndpointMetadata Get(ActionDescriptor descriptor)
{
ArgumentNullException.ThrowIfNull(descriptor);
var actionMethod = OpenApiActionMethod.Create(descriptor);
JsonApiEndpointMetadata? metadata = null;
switch (actionMethod)
{
case AtomicOperationsActionMethod:
{
metadata = new JsonApiEndpointMetadata(AtomicOperationsRequestMetadata.Instance, AtomicOperationsResponseMetadata.Instance);
break;
}
case JsonApiActionMethod jsonApiActionMethod:
{
ResourceType? primaryResourceType = _controllerResourceMapping.GetResourceTypeForController(jsonApiActionMethod.ControllerType);
ConsistencyGuard.ThrowIf(primaryResourceType == null);
IJsonApiRequestMetadata? requestMetadata = GetRequestMetadata(jsonApiActionMethod.Endpoint, primaryResourceType);
IJsonApiResponseMetadata? responseMetadata = GetResponseMetadata(jsonApiActionMethod.Endpoint, primaryResourceType);
metadata = new JsonApiEndpointMetadata(requestMetadata, responseMetadata);
break;
}
}
ConsistencyGuard.ThrowIf(metadata == null);
return metadata;
}
private IJsonApiRequestMetadata? GetRequestMetadata(JsonApiEndpoints endpoint, ResourceType primaryResourceType)
{
return endpoint switch
{
JsonApiEndpoints.Post => GetPostResourceRequestMetadata(primaryResourceType.ClrType),
JsonApiEndpoints.Patch => GetPatchResourceRequestMetadata(primaryResourceType.ClrType),
JsonApiEndpoints.PostRelationship or JsonApiEndpoints.PatchRelationship or JsonApiEndpoints.DeleteRelationship => GetRelationshipRequestMetadata(
primaryResourceType.Relationships, endpoint != JsonApiEndpoints.PatchRelationship),
_ => null
};
}
private static PrimaryRequestMetadata GetPostResourceRequestMetadata(Type resourceClrType)
{
Type documentType = typeof(CreateRequestDocument<>).MakeGenericType(resourceClrType);
return new PrimaryRequestMetadata(documentType);
}
private static PrimaryRequestMetadata GetPatchResourceRequestMetadata(Type resourceClrType)
{
Type documentType = typeof(UpdateRequestDocument<>).MakeGenericType(resourceClrType);
return new PrimaryRequestMetadata(documentType);
}
private RelationshipRequestMetadata GetRelationshipRequestMetadata(IReadOnlyCollection<RelationshipAttribute> relationships, bool ignoreHasOneRelationships)
{
IEnumerable<RelationshipAttribute> relationshipsOfEndpoint = ignoreHasOneRelationships ? relationships.OfType<HasManyAttribute>() : relationships;
Dictionary<RelationshipAttribute, Type> documentTypesByRelationship = relationshipsOfEndpoint.ToDictionary(relationship => relationship,
_nonPrimaryDocumentTypeFactory.GetForRelationshipRequest);
return new RelationshipRequestMetadata(documentTypesByRelationship.AsReadOnly());
}
private IJsonApiResponseMetadata? GetResponseMetadata(JsonApiEndpoints endpoint, ResourceType primaryResourceType)
{
return endpoint switch
{
JsonApiEndpoints.GetCollection or JsonApiEndpoints.GetSingle or JsonApiEndpoints.Post or JsonApiEndpoints.Patch => GetPrimaryResponseMetadata(
primaryResourceType.ClrType, endpoint == JsonApiEndpoints.GetCollection),
JsonApiEndpoints.Delete => GetEmptyPrimaryResponseMetadata(),
JsonApiEndpoints.GetSecondary => GetSecondaryResponseMetadata(primaryResourceType.Relationships),
JsonApiEndpoints.GetRelationship => GetRelationshipResponseMetadata(primaryResourceType.Relationships),
JsonApiEndpoints.PostRelationship or JsonApiEndpoints.PatchRelationship or JsonApiEndpoints.DeleteRelationship =>
GetEmptyRelationshipResponseMetadata(primaryResourceType.Relationships, endpoint != JsonApiEndpoints.PatchRelationship),
_ => null
};
}
private static PrimaryResponseMetadata GetEmptyPrimaryResponseMetadata()
{
return new PrimaryResponseMetadata(null);
}
private static PrimaryResponseMetadata GetPrimaryResponseMetadata(Type resourceClrType, bool endpointReturnsCollection)
{
Type documentOpenType = endpointReturnsCollection ? typeof(CollectionResponseDocument<>) : typeof(PrimaryResponseDocument<>);
Type documentType = documentOpenType.MakeGenericType(resourceClrType);
return new PrimaryResponseMetadata(documentType);
}
private SecondaryResponseMetadata GetSecondaryResponseMetadata(IEnumerable<RelationshipAttribute> relationships)
{
Dictionary<RelationshipAttribute, Type> documentTypesByRelationship = relationships.ToDictionary(relationship => relationship,
_nonPrimaryDocumentTypeFactory.GetForSecondaryResponse);
return new SecondaryResponseMetadata(documentTypesByRelationship.AsReadOnly());
}
private RelationshipResponseMetadata GetRelationshipResponseMetadata(IReadOnlyCollection<RelationshipAttribute> relationships)
{
Dictionary<RelationshipAttribute, Type> documentTypesByRelationship = relationships.ToDictionary(relationship => relationship,
_nonPrimaryDocumentTypeFactory.GetForRelationshipResponse);
return new RelationshipResponseMetadata(documentTypesByRelationship.AsReadOnly());
}
private static EmptyRelationshipResponseMetadata GetEmptyRelationshipResponseMetadata(IReadOnlyCollection<RelationshipAttribute> relationships,
bool ignoreHasOneRelationships)
{
IReadOnlyCollection<RelationshipAttribute> relationshipsOfEndpoint =
ignoreHasOneRelationships ? relationships.OfType<HasManyAttribute>().ToList().AsReadOnly() : relationships;
return new EmptyRelationshipResponseMetadata(relationshipsOfEndpoint);
}
}