-
-
Notifications
You must be signed in to change notification settings - Fork 158
/
Copy pathJsonApiEndpointMetadataProvider.cs
152 lines (128 loc) · 6.66 KB
/
JsonApiEndpointMetadataProvider.cs
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
148
149
150
151
152
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Middleware;
using JsonApiDotNetCore.OpenApi.JsonApiObjects;
using JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
using JsonApiDotNetCore.Resources.Annotations;
namespace JsonApiDotNetCore.OpenApi.JsonApiMetadata
{
/// <summary>
/// Provides JsonApiDotNetCore related metadata for an ASP.NET controller action that can only be computed from the <see cref="ResourceGraph" /> at
/// runtime.
/// </summary>
internal sealed class JsonApiEndpointMetadataProvider
{
private readonly IControllerResourceMapping _controllerResourceMapping;
private readonly EndpointResolver _endpointResolver = new();
private readonly NonPrimaryDocumentTypeFactory _nonPrimaryDocumentTypeFactory = new();
public JsonApiEndpointMetadataProvider(IControllerResourceMapping controllerResourceMapping)
{
ArgumentGuard.NotNull(controllerResourceMapping, nameof(controllerResourceMapping));
_controllerResourceMapping = controllerResourceMapping;
}
public JsonApiEndpointMetadataContainer Get(MethodInfo controllerAction)
{
ArgumentGuard.NotNull(controllerAction, nameof(controllerAction));
JsonApiEndpoint? endpoint = _endpointResolver.Get(controllerAction);
if (endpoint == null)
{
throw new NotSupportedException($"Unable to provide metadata for non-JsonApiDotNetCore endpoint '{controllerAction.ReflectedType!.FullName}'.");
}
ResourceType? primaryResourceType = _controllerResourceMapping.GetResourceTypeForController(controllerAction.ReflectedType);
if (primaryResourceType == null)
{
throw new UnreachableCodeException();
}
IJsonApiRequestMetadata? requestMetadata = GetRequestMetadata(endpoint.Value, primaryResourceType);
IJsonApiResponseMetadata? responseMetadata = GetResponseMetadata(endpoint.Value, primaryResourceType);
return new JsonApiEndpointMetadataContainer(requestMetadata, responseMetadata);
}
private IJsonApiRequestMetadata? GetRequestMetadata(JsonApiEndpoint endpoint, ResourceType primaryResourceType)
{
switch (endpoint)
{
case JsonApiEndpoint.Post:
{
return GetPostRequestMetadata(primaryResourceType.ClrType);
}
case JsonApiEndpoint.Patch:
{
return GetPatchRequestMetadata(primaryResourceType.ClrType);
}
case JsonApiEndpoint.PostRelationship:
case JsonApiEndpoint.PatchRelationship:
case JsonApiEndpoint.DeleteRelationship:
{
return GetRelationshipRequestMetadata(primaryResourceType.Relationships, endpoint != JsonApiEndpoint.PatchRelationship);
}
default:
{
return null;
}
}
}
private static PrimaryRequestMetadata GetPostRequestMetadata(Type resourceClrType)
{
Type documentType = typeof(ResourcePostRequestDocument<>).MakeGenericType(resourceClrType);
return new PrimaryRequestMetadata(documentType);
}
private static PrimaryRequestMetadata GetPatchRequestMetadata(Type resourceClrType)
{
Type documentType = typeof(ResourcePatchRequestDocument<>).MakeGenericType(resourceClrType);
return new PrimaryRequestMetadata(documentType);
}
private RelationshipRequestMetadata GetRelationshipRequestMetadata(IEnumerable<RelationshipAttribute> relationships, bool ignoreHasOneRelationships)
{
IEnumerable<RelationshipAttribute> relationshipsOfEndpoint = ignoreHasOneRelationships ? relationships.OfType<HasManyAttribute>() : relationships;
IDictionary<string, Type> requestDocumentTypesByRelationshipName = relationshipsOfEndpoint.ToDictionary(relationship => relationship.PublicName,
_nonPrimaryDocumentTypeFactory.GetForRelationshipRequest);
return new RelationshipRequestMetadata(requestDocumentTypesByRelationshipName);
}
private IJsonApiResponseMetadata? GetResponseMetadata(JsonApiEndpoint endpoint, ResourceType primaryResourceType)
{
switch (endpoint)
{
case JsonApiEndpoint.GetCollection:
case JsonApiEndpoint.GetSingle:
case JsonApiEndpoint.Post:
case JsonApiEndpoint.Patch:
{
return GetPrimaryResponseMetadata(primaryResourceType.ClrType, endpoint == JsonApiEndpoint.GetCollection);
}
case JsonApiEndpoint.GetSecondary:
{
return GetSecondaryResponseMetadata(primaryResourceType.Relationships);
}
case JsonApiEndpoint.GetRelationship:
{
return GetRelationshipResponseMetadata(primaryResourceType.Relationships);
}
default:
{
return null;
}
}
}
private static PrimaryResponseMetadata GetPrimaryResponseMetadata(Type resourceClrType, bool endpointReturnsCollection)
{
Type documentOpenType = endpointReturnsCollection ? typeof(ResourceCollectionResponseDocument<>) : typeof(PrimaryResourceResponseDocument<>);
Type documentType = documentOpenType.MakeGenericType(resourceClrType);
return new PrimaryResponseMetadata(documentType);
}
private SecondaryResponseMetadata GetSecondaryResponseMetadata(IEnumerable<RelationshipAttribute> relationships)
{
IDictionary<string, Type> responseDocumentTypesByRelationshipName = relationships.ToDictionary(relationship => relationship.PublicName,
_nonPrimaryDocumentTypeFactory.GetForSecondaryResponse);
return new SecondaryResponseMetadata(responseDocumentTypesByRelationshipName);
}
private RelationshipResponseMetadata GetRelationshipResponseMetadata(IEnumerable<RelationshipAttribute> relationships)
{
IDictionary<string, Type> responseDocumentTypesByRelationshipName = relationships.ToDictionary(relationship => relationship.PublicName,
_nonPrimaryDocumentTypeFactory.GetForRelationshipResponse);
return new RelationshipResponseMetadata(responseDocumentTypesByRelationshipName);
}
}
}