Skip to content

Commit 0709ea4

Browse files
authored
Fix order of JSON:API properties in OAS document (#1362)
1 parent a420db9 commit 0709ea4

24 files changed

+779
-743
lines changed

src/JsonApiDotNetCore.OpenApi/JsonApiObjectPropertyName.cs

-14
This file was deleted.

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableResourceIdentifierResponseDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
1313
internal sealed class NullableResourceIdentifierResponseDocument<TResource> : NullableSingleData<ResourceIdentifierObject<TResource>>
1414
where TResource : IIdentifiable
1515
{
16-
[JsonPropertyName("meta")]
17-
public IDictionary<string, object> Meta { get; set; } = null!;
18-
1916
[JsonPropertyName("jsonapi")]
2017
public JsonapiObject Jsonapi { get; set; } = null!;
2118

2219
[Required]
2320
[JsonPropertyName("links")]
2421
public LinksInResourceIdentifierDocument Links { get; set; } = null!;
22+
23+
[JsonPropertyName("meta")]
24+
public IDictionary<string, object> Meta { get; set; } = null!;
2525
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/NullableSecondaryResourceResponseDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
1111
internal sealed class NullableSecondaryResourceResponseDocument<TResource> : NullableSingleData<ResourceObjectInResponse<TResource>>
1212
where TResource : IIdentifiable
1313
{
14-
[JsonPropertyName("meta")]
15-
public IDictionary<string, object> Meta { get; set; } = null!;
16-
1714
[JsonPropertyName("jsonapi")]
1815
public JsonapiObject Jsonapi { get; set; } = null!;
1916

2017
[Required]
2118
[JsonPropertyName("links")]
2219
public LinksInResourceDocument Links { get; set; } = null!;
20+
21+
[JsonPropertyName("meta")]
22+
public IDictionary<string, object> Meta { get; set; } = null!;
2323
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/PrimaryResourceResponseDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
1111
internal sealed class PrimaryResourceResponseDocument<TResource> : SingleData<ResourceObjectInResponse<TResource>>
1212
where TResource : IIdentifiable
1313
{
14-
[JsonPropertyName("meta")]
15-
public IDictionary<string, object> Meta { get; set; } = null!;
16-
1714
[JsonPropertyName("jsonapi")]
1815
public JsonapiObject Jsonapi { get; set; } = null!;
1916

2017
[Required]
2118
[JsonPropertyName("links")]
2219
public LinksInResourceDocument Links { get; set; } = null!;
20+
21+
[JsonPropertyName("meta")]
22+
public IDictionary<string, object> Meta { get; set; } = null!;
2323
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceCollectionResponseDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
1111
internal sealed class ResourceCollectionResponseDocument<TResource> : ManyData<ResourceObjectInResponse<TResource>>
1212
where TResource : IIdentifiable
1313
{
14-
[JsonPropertyName("meta")]
15-
public IDictionary<string, object> Meta { get; set; } = null!;
16-
1714
[JsonPropertyName("jsonapi")]
1815
public JsonapiObject Jsonapi { get; set; } = null!;
1916

2017
[Required]
2118
[JsonPropertyName("links")]
2219
public LinksInResourceCollectionDocument Links { get; set; } = null!;
20+
21+
[JsonPropertyName("meta")]
22+
public IDictionary<string, object> Meta { get; set; } = null!;
2323
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierCollectionResponseDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
1111
internal sealed class ResourceIdentifierCollectionResponseDocument<TResource> : ManyData<ResourceIdentifierObject<TResource>>
1212
where TResource : IIdentifiable
1313
{
14-
[JsonPropertyName("meta")]
15-
public IDictionary<string, object> Meta { get; set; } = null!;
16-
1714
[JsonPropertyName("jsonapi")]
1815
public JsonapiObject Jsonapi { get; set; } = null!;
1916

2017
[Required]
2118
[JsonPropertyName("links")]
2219
public LinksInResourceIdentifierCollectionDocument Links { get; set; } = null!;
20+
21+
[JsonPropertyName("meta")]
22+
public IDictionary<string, object> Meta { get; set; } = null!;
2323
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/ResourceIdentifierResponseDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
1111
internal sealed class ResourceIdentifierResponseDocument<TResource> : SingleData<ResourceIdentifierObject<TResource>>
1212
where TResource : IIdentifiable
1313
{
14-
[JsonPropertyName("meta")]
15-
public IDictionary<string, object> Meta { get; set; } = null!;
16-
1714
[JsonPropertyName("jsonapi")]
1815
public JsonapiObject Jsonapi { get; set; } = null!;
1916

2017
[Required]
2118
[JsonPropertyName("links")]
2219
public LinksInResourceIdentifierDocument Links { get; set; } = null!;
20+
21+
[JsonPropertyName("meta")]
22+
public IDictionary<string, object> Meta { get; set; } = null!;
2323
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Documents/SecondaryResourceResponseDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents;
1111
internal sealed class SecondaryResourceResponseDocument<TResource> : SingleData<ResourceObjectInResponse<TResource>>
1212
where TResource : IIdentifiable
1313
{
14-
[JsonPropertyName("meta")]
15-
public IDictionary<string, object> Meta { get; set; } = null!;
16-
1714
[JsonPropertyName("jsonapi")]
1815
public JsonapiObject Jsonapi { get; set; } = null!;
1916

2017
[Required]
2118
[JsonPropertyName("links")]
2219
public LinksInResourceDocument Links { get; set; } = null!;
20+
21+
[JsonPropertyName("meta")]
22+
public IDictionary<string, object> Meta { get; set; } = null!;
2323
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierCollectionDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ internal sealed class LinksInResourceIdentifierCollectionDocument
1111
[JsonPropertyName("self")]
1212
public string Self { get; set; } = null!;
1313

14-
[JsonPropertyName("describedby")]
15-
public string Describedby { get; set; } = null!;
16-
1714
[Required]
1815
[JsonPropertyName("related")]
1916
public string Related { get; set; } = null!;
2017

18+
[JsonPropertyName("describedby")]
19+
public string Describedby { get; set; } = null!;
20+
2121
[Required]
2222
[JsonPropertyName("first")]
2323
public string First { get; set; } = null!;

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/Links/LinksInResourceIdentifierDocument.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ internal sealed class LinksInResourceIdentifierDocument
1111
[JsonPropertyName("self")]
1212
public string Self { get; set; } = null!;
1313

14-
[JsonPropertyName("describedby")]
15-
public string Describedby { get; set; } = null!;
16-
1714
[Required]
1815
[JsonPropertyName("related")]
1916
public string Related { get; set; } = null!;
17+
18+
[JsonPropertyName("describedby")]
19+
public string Describedby { get; set; } = null!;
2020
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#pragma warning disable AV1008 // Class should not be static
2+
3+
namespace JsonApiDotNetCore.OpenApi;
4+
5+
internal static class JsonApiPropertyName
6+
{
7+
public const string Jsonapi = "jsonapi";
8+
public const string Links = "links";
9+
public const string Data = "data";
10+
public const string Type = "type";
11+
public const string Id = "id";
12+
public const string Attributes = "attributes";
13+
public const string Relationships = "relationships";
14+
public const string Errors = "errors";
15+
public const string Included = "included";
16+
public const string Meta = "meta";
17+
}

src/JsonApiDotNetCore.OpenApi/SwaggerComponents/JsonApiSchemaGenerator.cs

+16-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ internal sealed class JsonApiSchemaGenerator : ISchemaGenerator
3434
typeof(NullableToOneRelationshipInRequest<>)
3535
};
3636

37+
private static readonly string[] DocumentPropertyNamesInOrder =
38+
{
39+
JsonApiPropertyName.Jsonapi,
40+
JsonApiPropertyName.Links,
41+
JsonApiPropertyName.Data,
42+
JsonApiPropertyName.Errors,
43+
JsonApiPropertyName.Included,
44+
JsonApiPropertyName.Meta
45+
};
46+
3747
private readonly ISchemaGenerator _defaultSchemaGenerator;
3848
private readonly ResourceObjectSchemaGenerator _resourceObjectSchemaGenerator;
3949
private readonly NullableReferenceSchemaGenerator _nullableReferenceSchemaGenerator;
@@ -76,7 +86,7 @@ public OpenApiSchema GenerateSchema(Type modelType, SchemaRepository schemaRepos
7686
}
7787
}
7888

79-
return _defaultSchemaGenerator.GenerateSchema(modelType, schemaRepository, memberInfo, parameterInfo);
89+
return _defaultSchemaGenerator.GenerateSchema(modelType, schemaRepository, memberInfo, parameterInfo, routeInfo);
8090
}
8191

8292
private static bool IsJsonApiDocument(Type type)
@@ -100,7 +110,9 @@ private OpenApiSchema GenerateJsonApiDocumentSchema(Type documentType)
100110
? CreateArrayTypeDataSchema(referenceSchemaForResourceObject)
101111
: referenceSchemaForResourceObject;
102112

103-
fullSchemaForDocument.Properties[JsonApiObjectPropertyName.Data] = referenceSchemaForDataObject;
113+
fullSchemaForDocument.Properties[JsonApiPropertyName.Data] = referenceSchemaForDataObject;
114+
115+
fullSchemaForDocument.ReorderProperties(DocumentPropertyNamesInOrder);
104116

105117
return referenceSchemaForDocument;
106118
}
@@ -120,8 +132,8 @@ private static bool IsDataPropertyNullableInDocument(Type documentType)
120132
private void SetDataObjectSchemaToNullable(OpenApiSchema referenceSchemaForDocument)
121133
{
122134
OpenApiSchema fullSchemaForDocument = _schemaRepositoryAccessor.Current.Schemas[referenceSchemaForDocument.Reference.Id];
123-
OpenApiSchema referenceSchemaForData = fullSchemaForDocument.Properties[JsonApiObjectPropertyName.Data];
124-
fullSchemaForDocument.Properties[JsonApiObjectPropertyName.Data] = _nullableReferenceSchemaGenerator.GenerateSchema(referenceSchemaForData);
135+
OpenApiSchema referenceSchemaForData = fullSchemaForDocument.Properties[JsonApiPropertyName.Data];
136+
fullSchemaForDocument.Properties[JsonApiPropertyName.Data] = _nullableReferenceSchemaGenerator.GenerateSchema(referenceSchemaForData);
125137
}
126138

127139
private static OpenApiSchema CreateArrayTypeDataSchema(OpenApiSchema referenceSchemaForResourceObject)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Microsoft.OpenApi.Models;
2+
3+
namespace JsonApiDotNetCore.OpenApi.SwaggerComponents;
4+
5+
internal static class OpenApiSchemaExtensions
6+
{
7+
public static void ReorderProperties(this OpenApiSchema fullSchemaForResourceObject, IEnumerable<string> propertyNamesInOrder)
8+
{
9+
var propertiesInOrder = new Dictionary<string, OpenApiSchema>();
10+
11+
foreach (string propertyName in propertyNamesInOrder)
12+
{
13+
if (fullSchemaForResourceObject.Properties.TryGetValue(propertyName, out OpenApiSchema? schema))
14+
{
15+
propertiesInOrder.Add(propertyName, schema);
16+
}
17+
}
18+
19+
if (fullSchemaForResourceObject.Properties.Count != propertiesInOrder.Count)
20+
{
21+
throw new UnreachableCodeException();
22+
}
23+
24+
fullSchemaForResourceObject.Properties = propertiesInOrder;
25+
}
26+
}

src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs

+12-4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ internal sealed class ResourceFieldObjectSchemaBuilder
2323
typeof(NullableToOneRelationshipInResponse<>)
2424
};
2525

26+
private static readonly string[] RelationshipObjectPropertyNamesInOrder =
27+
{
28+
JsonApiPropertyName.Links,
29+
JsonApiPropertyName.Data,
30+
JsonApiPropertyName.Meta
31+
};
32+
2633
private readonly ResourceTypeInfo _resourceTypeInfo;
2734
private readonly ISchemaRepositoryAccessor _schemaRepositoryAccessor;
2835
private readonly SchemaGenerator _defaultSchemaGenerator;
@@ -156,7 +163,7 @@ private void GenerateResourceIdentifierObjectSchema(Type resourceIdentifierObjec
156163
_schemaRepositoryAccessor.Current.Schemas[referenceSchemaForResourceIdentifierObject.Reference.Id];
157164

158165
Type resourceType = resourceIdentifierObjectType.GetGenericArguments()[0];
159-
fullSchemaForResourceIdentifierObject.Properties[JsonApiObjectPropertyName.Type] = _resourceTypeSchemaGenerator.Get(resourceType);
166+
fullSchemaForResourceIdentifierObject.Properties[JsonApiPropertyName.Type] = _resourceTypeSchemaGenerator.Get(resourceType);
160167
}
161168

162169
private void AddRelationshipSchemaToResourceObject(RelationshipAttribute relationship, OpenApiSchema fullSchemaForRelationshipsObject)
@@ -194,13 +201,14 @@ private OpenApiSchema CreateRelationshipSchema(Type relationshipSchemaType)
194201

195202
if (IsDataPropertyNullableInRelationshipSchemaType(relationshipSchemaType))
196203
{
197-
fullSchema.Properties[JsonApiObjectPropertyName.Data] =
198-
_nullableReferenceSchemaGenerator.GenerateSchema(fullSchema.Properties[JsonApiObjectPropertyName.Data]);
204+
fullSchema.Properties[JsonApiPropertyName.Data] = _nullableReferenceSchemaGenerator.GenerateSchema(fullSchema.Properties[JsonApiPropertyName.Data]);
199205
}
200206

201207
if (IsRelationshipInResponseType(relationshipSchemaType))
202208
{
203-
fullSchema.Required.Remove(JsonApiObjectPropertyName.Data);
209+
fullSchema.Required.Remove(JsonApiPropertyName.Data);
210+
211+
fullSchema.ReorderProperties(RelationshipObjectPropertyNamesInOrder);
204212
}
205213

206214
return referenceSchema;

0 commit comments

Comments
 (0)