Skip to content

Commit fb92b0e

Browse files
committed
Introduced types for attributes and relationships in resource objects
1 parent 727d068 commit fb92b0e

16 files changed

+117
-129
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using JsonApiDotNetCore.Resources;
2+
3+
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
4+
{
5+
// ReSharper disable once UnusedTypeParameter
6+
internal sealed class AttributesInPatchRequest<TResource>
7+
where TResource : IIdentifiable
8+
{
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using JsonApiDotNetCore.Resources;
2+
3+
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
4+
{
5+
// ReSharper disable once UnusedTypeParameter
6+
internal sealed class AttributesInPostRequest<TResource>
7+
where TResource : IIdentifiable
8+
{
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using JsonApiDotNetCore.Resources;
2+
3+
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
4+
{
5+
// ReSharper disable once UnusedTypeParameter
6+
internal sealed class AttributesInResponse<TResource>
7+
where TResource : IIdentifiable
8+
{
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using JsonApiDotNetCore.Resources;
2+
3+
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
4+
{
5+
// ReSharper disable once UnusedTypeParameter
6+
internal sealed class RelationshipsInPatchRequest<TResource>
7+
where TResource : IIdentifiable
8+
{
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using JsonApiDotNetCore.Resources;
2+
3+
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
4+
{
5+
// ReSharper disable once UnusedTypeParameter
6+
internal sealed class RelationshipsInPostRequest<TResource>
7+
where TResource : IIdentifiable
8+
{
9+
}
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using JsonApiDotNetCore.Resources;
2+
3+
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
4+
{
5+
// ReSharper disable once UnusedTypeParameter
6+
internal sealed class RelationshipsInResponse<TResource>
7+
where TResource : IIdentifiable
8+
{
9+
}
10+
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceIdentifierObject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
66
{
77
// ReSharper disable once UnusedTypeParameter
8-
internal class ResourceIdentifierObject<TResource> : ResourceIdentifierObject
8+
internal sealed class ResourceIdentifierObject<TResource> : ResourceIdentifierObject
99
where TResource : IIdentifiable
1010
{
1111
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObject.cs

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
using JetBrains.Annotations;
12
using JsonApiDotNetCore.Resources;
23

34
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
45
{
5-
internal sealed class ResourceObjectInPatchRequest<TResource> : ResourceObject<TResource>
6+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
7+
internal sealed class ResourceObjectInPatchRequest<TResource> : ResourceIdentifierObject
68
where TResource : IIdentifiable
79
{
10+
public AttributesInPatchRequest<TResource> Attributes { get; set; } = null!;
11+
12+
public RelationshipsInPatchRequest<TResource> Relationships { get; set; } = null!;
813
}
914
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
using JetBrains.Annotations;
12
using JsonApiDotNetCore.Resources;
23

34
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
45
{
5-
internal sealed class ResourceObjectInPostRequest<TResource> : ResourceObject<TResource>
6+
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
7+
internal sealed class ResourceObjectInPostRequest<TResource> : ResourceIdentifierObject
68
where TResource : IIdentifiable
79
{
10+
public AttributesInPostRequest<TResource> Attributes { get; set; } = null!;
11+
12+
public RelationshipsInPostRequest<TResource> Relationships { get; set; } = null!;
813
}
914
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInResponse.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
88
{
99
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10-
internal sealed class ResourceObjectInResponse<TResource> : ResourceObject<TResource>
10+
internal sealed class ResourceObjectInResponse<TResource> : ResourceIdentifierObject
1111
where TResource : IIdentifiable
1212
{
13+
public AttributesInResponse<TResource> Attributes { get; set; } = null!;
14+
15+
public RelationshipsInResponse<TResource> Relationships { get; set; } = null!;
16+
1317
[Required]
1418
public LinksInResourceObject Links { get; set; } = null!;
1519

src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ internal sealed class JsonApiSchemaIdSelector
1616
[typeof(ResourcePostRequestDocument<>)] = "###-post-request-document",
1717
[typeof(ResourcePatchRequestDocument<>)] = "###-patch-request-document",
1818
[typeof(ResourceObjectInPostRequest<>)] = "###-in-post-request",
19+
[typeof(AttributesInPostRequest<>)] = "###-attributes-in-post-request",
20+
[typeof(RelationshipsInPostRequest<>)] = "###-relationships-in-post-request",
1921
[typeof(ResourceObjectInPatchRequest<>)] = "###-in-patch-request",
22+
[typeof(AttributesInPatchRequest<>)] = "###-attributes-in-patch-request",
23+
[typeof(RelationshipsInPatchRequest<>)] = "###-relationships-in-patch-request",
2024
[typeof(ToOneRelationshipInRequest<>)] = "to-one-###-in-request",
2125
[typeof(NullableToOneRelationshipInRequest<>)] = "nullable-to-one-###-in-request",
2226
[typeof(ToManyRelationshipInRequest<>)] = "to-many-###-in-request",
@@ -31,16 +35,11 @@ internal sealed class JsonApiSchemaIdSelector
3135
[typeof(NullableToOneRelationshipInResponse<>)] = "nullable-to-one-###-in-response",
3236
[typeof(ToManyRelationshipInResponse<>)] = "to-many-###-in-response",
3337
[typeof(ResourceObjectInResponse<>)] = "###-in-response",
38+
[typeof(AttributesInResponse<>)] = "###-attributes-in-response",
39+
[typeof(RelationshipsInResponse<>)] = "###-relationships-in-response",
3440
[typeof(ResourceIdentifierObject<>)] = "###-identifier"
3541
};
3642

37-
private readonly Type[] _resourceObjectOpenTypes =
38-
{
39-
typeof(ResourceObjectInPostRequest<>),
40-
typeof(ResourceObjectInPatchRequest<>),
41-
typeof(ResourceObjectInResponse<>)
42-
};
43-
4443
private readonly ResourceNameFormatter _formatter;
4544
private readonly IResourceGraph _resourceGraph;
4645

@@ -76,25 +75,5 @@ public string GetSchemaId(Type type)
7675
// Used for a fixed set of types, such as jsonapi-object, links-in-many-resource-document etc.
7776
return _formatter.FormatResourceName(type).Singularize();
7877
}
79-
80-
public string GetSchemaId(Type resourceObjectType, ResourceObjectFieldType fieldType)
81-
{
82-
ArgumentGuard.NotNull(resourceObjectType, nameof(resourceObjectType));
83-
84-
if (!resourceObjectType.IsConstructedGenericType || !_resourceObjectOpenTypes.Contains(resourceObjectType.GetGenericTypeDefinition()))
85-
{
86-
throw new InvalidOperationException($"Type '{resourceObjectType.Name}' must be a resource object.");
87-
}
88-
89-
Type resourceClrType = resourceObjectType.GetGenericArguments().First();
90-
string resourceName = _formatter.FormatResourceName(resourceClrType).Singularize();
91-
string template = OpenTypeToSchemaTemplateMap[resourceObjectType.GetGenericTypeDefinition()];
92-
93-
string fieldObjectName = fieldType == ResourceObjectFieldType.Attributes
94-
? JsonApiObjectPropertyName.AttributesObject
95-
: JsonApiObjectPropertyName.RelationshipsObject;
96-
97-
return template.Replace("###", $"{resourceName}-{fieldObjectName}");
98-
}
9978
}
10079
}

src/JsonApiDotNetCore.OpenApi/ResourceObjectFieldType.cs

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,21 @@ internal sealed class ResourceFieldObjectSchemaBuilder
2626
private readonly ResourceTypeInfo _resourceTypeInfo;
2727
private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor;
2828
private readonly SchemaGenerator _defaultSchemaGenerator;
29-
private readonly JsonApiSchemaIdSelector _jsonApiSchemaIdSelector;
3029
private readonly ResourceTypeSchemaGenerator _resourceTypeSchemaGenerator;
3130
private readonly NullableReferenceSchemaGenerator _nullableReferenceSchemaGenerator;
3231
private readonly IDictionary<string, OpenApiSchema> _schemasForResourceFields;
3332

3433
public ResourceFieldObjectSchemaBuilder(ResourceTypeInfo resourceTypeInfo, ISchemaRepositoryAccessor schemaRepositoryAccessor,
35-
SchemaGenerator defaultSchemaGenerator, JsonApiSchemaIdSelector jsonApiSchemaIdSelector, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator)
34+
SchemaGenerator defaultSchemaGenerator, ResourceTypeSchemaGenerator resourceTypeSchemaGenerator)
3635
{
3736
ArgumentGuard.NotNull(resourceTypeInfo, nameof(resourceTypeInfo));
3837
ArgumentGuard.NotNull(schemaRepositoryAccessor, nameof(schemaRepositoryAccessor));
3938
ArgumentGuard.NotNull(defaultSchemaGenerator, nameof(defaultSchemaGenerator));
40-
ArgumentGuard.NotNull(jsonApiSchemaIdSelector, nameof(jsonApiSchemaIdSelector));
4139
ArgumentGuard.NotNull(resourceTypeSchemaGenerator, nameof(resourceTypeSchemaGenerator));
4240

4341
_resourceTypeInfo = resourceTypeInfo;
4442
_schemaRepositoryAccessor = schemaRepositoryAccessor;
4543
_defaultSchemaGenerator = defaultSchemaGenerator;
46-
_jsonApiSchemaIdSelector = jsonApiSchemaIdSelector;
4744
_resourceTypeSchemaGenerator = resourceTypeSchemaGenerator;
4845

4946
_nullableReferenceSchemaGenerator = new NullableReferenceSchemaGenerator(schemaRepositoryAccessor);
@@ -61,26 +58,7 @@ private IDictionary<string, OpenApiSchema> GetFieldSchemas()
6158
return fullSchemaForResource.Properties;
6259
}
6360

64-
public OpenApiSchema? BuildAttributesObject(OpenApiSchema fullSchemaForResourceObject)
65-
{
66-
ArgumentGuard.NotNull(fullSchemaForResourceObject, nameof(fullSchemaForResourceObject));
67-
68-
OpenApiSchema fullSchemaForAttributesObject = fullSchemaForResourceObject.Properties[JsonApiObjectPropertyName.AttributesObject];
69-
70-
SetMembersOfAttributesObject(fullSchemaForAttributesObject);
71-
72-
if (!fullSchemaForAttributesObject.Properties.Any())
73-
{
74-
return null;
75-
}
76-
77-
fullSchemaForAttributesObject.AdditionalPropertiesAllowed = false;
78-
79-
string fieldObjectSchemaId = _jsonApiSchemaIdSelector.GetSchemaId(_resourceTypeInfo.ResourceObjectType, ResourceObjectFieldType.Attributes);
80-
return _schemaRepositoryAccessor.Current.AddDefinition(fieldObjectSchemaId, fullSchemaForAttributesObject);
81-
}
82-
83-
private void SetMembersOfAttributesObject(OpenApiSchema fullSchemaForAttributesObject)
61+
public void SetMembersOfAttributesObject(OpenApiSchema fullSchemaForAttributesObject)
8462
{
8563
AttrCapabilities requiredCapability = GetRequiredCapabilityForAttributes(_resourceTypeInfo.ResourceObjectOpenType);
8664

@@ -145,26 +123,7 @@ private bool IsFieldRequired(ResourceFieldAttribute field)
145123
};
146124
}
147125

148-
public OpenApiSchema? BuildRelationshipsObject(OpenApiSchema fullSchemaForResourceObject)
149-
{
150-
ArgumentGuard.NotNull(fullSchemaForResourceObject, nameof(fullSchemaForResourceObject));
151-
152-
OpenApiSchema fullSchemaForRelationshipsObject = fullSchemaForResourceObject.Properties[JsonApiObjectPropertyName.RelationshipsObject];
153-
154-
SetMembersOfRelationshipsObject(fullSchemaForRelationshipsObject);
155-
156-
if (!fullSchemaForRelationshipsObject.Properties.Any())
157-
{
158-
return null;
159-
}
160-
161-
fullSchemaForRelationshipsObject.AdditionalPropertiesAllowed = false;
162-
163-
string fieldObjectSchemaId = _jsonApiSchemaIdSelector.GetSchemaId(_resourceTypeInfo.ResourceObjectType, ResourceObjectFieldType.Relationships);
164-
return _schemaRepositoryAccessor.Current.AddDefinition(fieldObjectSchemaId, fullSchemaForRelationshipsObject);
165-
}
166-
167-
private void SetMembersOfRelationshipsObject(OpenApiSchema fullSchemaForRelationshipsObject)
126+
public void SetMembersOfRelationshipsObject(OpenApiSchema fullSchemaForRelationshipsObject)
168127
{
169128
foreach (string fieldName in _schemasForResourceFields.Keys)
170129
{
@@ -176,6 +135,11 @@ private void SetMembersOfRelationshipsObject(OpenApiSchema fullSchemaForRelation
176135
AddRelationshipSchemaToResourceObject(matchingRelationship, fullSchemaForRelationshipsObject);
177136
}
178137
}
138+
139+
if (fullSchemaForRelationshipsObject.Properties.Any())
140+
{
141+
fullSchemaForRelationshipsObject.AdditionalPropertiesAllowed = false;
142+
}
179143
}
180144

181145
private void EnsureResourceIdentifierObjectSchemaExists(RelationshipAttribute relationship)

src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Text.Json;
3+
using System.Linq;
44
using JsonApiDotNetCore.Configuration;
55
using JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects;
66
using Microsoft.OpenApi.Models;
@@ -15,7 +15,7 @@ internal sealed class ResourceObjectSchemaGenerator
1515
private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor;
1616
private readonly ResourceTypeSchemaGenerator _resourceTypeSchemaGenerator;
1717
private readonly bool _allowClientGeneratedIds;
18-
private readonly Func<ResourceTypeInfo, ResourceFieldObjectSchemaBuilder> _createFieldObjectBuilderFactory;
18+
private readonly Func<ResourceTypeInfo, ResourceFieldObjectSchemaBuilder> _fieldObjectBuilderFactory;
1919

2020
public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IResourceGraph resourceGraph, IJsonApiOptions options,
2121
ISchemaRepositoryAccessor schemaRepositoryAccessor)
@@ -32,20 +32,8 @@ public ResourceObjectSchemaGenerator(SchemaGenerator defaultSchemaGenerator, IRe
3232
_resourceTypeSchemaGenerator = new ResourceTypeSchemaGenerator(schemaRepositoryAccessor, resourceGraph);
3333
_allowClientGeneratedIds = options.AllowClientGeneratedIds;
3434

35-
_createFieldObjectBuilderFactory = CreateFieldObjectBuilderFactory(defaultSchemaGenerator, resourceGraph, options, schemaRepositoryAccessor,
36-
_resourceTypeSchemaGenerator);
37-
}
38-
39-
private static Func<ResourceTypeInfo, ResourceFieldObjectSchemaBuilder> CreateFieldObjectBuilderFactory(SchemaGenerator defaultSchemaGenerator,
40-
IResourceGraph resourceGraph, IJsonApiOptions options, ISchemaRepositoryAccessor schemaRepositoryAccessor,
41-
ResourceTypeSchemaGenerator resourceTypeSchemaGenerator)
42-
{
43-
JsonNamingPolicy? namingPolicy = options.SerializerOptions.PropertyNamingPolicy;
44-
ResourceNameFormatter resourceNameFormatter = new(namingPolicy);
45-
var jsonApiSchemaIdSelector = new JsonApiSchemaIdSelector(resourceNameFormatter, resourceGraph);
46-
47-
return resourceTypeInfo => new ResourceFieldObjectSchemaBuilder(resourceTypeInfo, schemaRepositoryAccessor, defaultSchemaGenerator,
48-
jsonApiSchemaIdSelector, resourceTypeSchemaGenerator);
35+
_fieldObjectBuilderFactory = resourceTypeInfo => new ResourceFieldObjectSchemaBuilder(resourceTypeInfo, schemaRepositoryAccessor,
36+
defaultSchemaGenerator, _resourceTypeSchemaGenerator);
4937
}
5038

5139
public OpenApiSchema GenerateSchema(Type resourceObjectType)
@@ -55,7 +43,7 @@ public OpenApiSchema GenerateSchema(Type resourceObjectType)
5543
(OpenApiSchema fullSchemaForResourceObject, OpenApiSchema referenceSchemaForResourceObject) = EnsureSchemasExist(resourceObjectType);
5644

5745
var resourceTypeInfo = ResourceTypeInfo.Create(resourceObjectType, _resourceGraph);
58-
ResourceFieldObjectSchemaBuilder fieldObjectBuilder = _createFieldObjectBuilderFactory(resourceTypeInfo);
46+
ResourceFieldObjectSchemaBuilder fieldObjectBuilder = _fieldObjectBuilderFactory(resourceTypeInfo);
5947

6048
RemoveResourceIdIfPostResourceObject(resourceTypeInfo.ResourceObjectOpenType, fullSchemaForResourceObject);
6149

@@ -104,31 +92,39 @@ private void SetResourceType(OpenApiSchema fullSchemaForResourceObject, Type res
10492
fullSchemaForResourceObject.Properties[JsonApiObjectPropertyName.Type] = _resourceTypeSchemaGenerator.Get(resourceType);
10593
}
10694

107-
private static void SetResourceAttributes(OpenApiSchema fullSchemaForResourceObject, ResourceFieldObjectSchemaBuilder builder)
95+
private void SetResourceAttributes(OpenApiSchema fullSchemaForResourceObject, ResourceFieldObjectSchemaBuilder builder)
10896
{
109-
OpenApiSchema? fullSchemaForAttributesObject = builder.BuildAttributesObject(fullSchemaForResourceObject);
97+
OpenApiSchema referenceSchemaForAttributesObject = fullSchemaForResourceObject.Properties[JsonApiObjectPropertyName.AttributesObject];
98+
OpenApiSchema fullSchemaForAttributesObject = _schemaRepositoryAccessor.Current.Schemas[referenceSchemaForAttributesObject.Reference.Id];
99+
100+
builder.SetMembersOfAttributesObject(fullSchemaForAttributesObject);
110101

111-
if (fullSchemaForAttributesObject != null)
102+
if (!fullSchemaForAttributesObject.Properties.Any())
112103
{
113-
fullSchemaForResourceObject.Properties[JsonApiObjectPropertyName.AttributesObject] = fullSchemaForAttributesObject;
104+
fullSchemaForResourceObject.Properties.Remove(JsonApiObjectPropertyName.AttributesObject);
105+
_schemaRepositoryAccessor.Current.Schemas.Remove(referenceSchemaForAttributesObject.Reference.Id);
114106
}
115107
else
116108
{
117-
fullSchemaForResourceObject.Properties.Remove(JsonApiObjectPropertyName.AttributesObject);
109+
fullSchemaForAttributesObject.AdditionalPropertiesAllowed = false;
118110
}
119111
}
120112

121-
private static void SetResourceRelationships(OpenApiSchema fullSchemaForResourceObject, ResourceFieldObjectSchemaBuilder builder)
113+
private void SetResourceRelationships(OpenApiSchema fullSchemaForResourceObject, ResourceFieldObjectSchemaBuilder builder)
122114
{
123-
OpenApiSchema? fullSchemaForRelationshipsObject = builder.BuildRelationshipsObject(fullSchemaForResourceObject);
115+
OpenApiSchema referenceSchemaForRelationshipsObject = fullSchemaForResourceObject.Properties[JsonApiObjectPropertyName.RelationshipsObject];
116+
OpenApiSchema fullSchemaForRelationshipsObject = _schemaRepositoryAccessor.Current.Schemas[referenceSchemaForRelationshipsObject.Reference.Id];
117+
118+
builder.SetMembersOfRelationshipsObject(fullSchemaForRelationshipsObject);
124119

125-
if (fullSchemaForRelationshipsObject != null)
120+
if (!fullSchemaForRelationshipsObject.Properties.Any())
126121
{
127-
fullSchemaForResourceObject.Properties[JsonApiObjectPropertyName.RelationshipsObject] = fullSchemaForRelationshipsObject;
122+
fullSchemaForResourceObject.Properties.Remove(JsonApiObjectPropertyName.RelationshipsObject);
123+
_schemaRepositoryAccessor.Current.Schemas.Remove(referenceSchemaForRelationshipsObject.Reference.Id);
128124
}
129125
else
130126
{
131-
fullSchemaForResourceObject.Properties.Remove(JsonApiObjectPropertyName.RelationshipsObject);
127+
fullSchemaForRelationshipsObject.AdditionalPropertiesAllowed = false;
132128
}
133129
}
134130

0 commit comments

Comments
 (0)