Skip to content

Commit b527567

Browse files
committed
Process review feedback, fixed culture issue in test
1 parent 6160ad0 commit b527567

18 files changed

+300
-263
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents
99
{
1010
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
11-
internal sealed class NullableSecondaryResourceResponseDocument<TResource> : NullableSingleData<ResourceResponseObject<TResource>>
11+
internal sealed class NullableSecondaryResourceResponseDocument<TResource> : NullableSingleData<ResourceObjectInResponse<TResource>>
1212
where TResource : IIdentifiable
1313
{
1414
public IDictionary<string, object> Meta { get; set; } = null!;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents
1010
// Types in the current namespace are never touched by ASP.NET ModelState validation, therefore using a non-nullable reference type for a property does not
1111
// imply this property is required. Instead, we use [Required] explicitly, because this is how Swashbuckle is instructed to mark properties as required.
1212
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
13-
internal sealed class PrimaryResourceResponseDocument<TResource> : SingleData<ResourceResponseObject<TResource>>
13+
internal sealed class PrimaryResourceResponseDocument<TResource> : SingleData<ResourceObjectInResponse<TResource>>
1414
where TResource : IIdentifiable
1515
{
1616
public IDictionary<string, object> Meta { get; set; } = null!;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents
99
{
1010
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
11-
internal sealed class ResourceCollectionResponseDocument<TResource> : ManyData<ResourceResponseObject<TResource>>
11+
internal sealed class ResourceCollectionResponseDocument<TResource> : ManyData<ResourceObjectInResponse<TResource>>
1212
where TResource : IIdentifiable
1313
{
1414
public IDictionary<string, object> Meta { get; set; } = null!;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
internal sealed class ResourcePatchRequestDocument<TResource> : SingleData<ResourcePatchRequestObject<TResource>>
8+
internal sealed class ResourcePatchRequestDocument<TResource> : SingleData<ResourceObjectInPatchRequest<TResource>>
99
where TResource : IIdentifiable
1010
{
1111
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents
66
{
77
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
8-
internal sealed class ResourcePostRequestDocument<TResource> : SingleData<ResourcePostRequestObject<TResource>>
8+
internal sealed class ResourcePostRequestDocument<TResource> : SingleData<ResourceObjectInPostRequest<TResource>>
99
where TResource : IIdentifiable
1010
{
1111
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.Documents
99
{
1010
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
11-
internal sealed class SecondaryResourceResponseDocument<TResource> : SingleData<ResourceResponseObject<TResource>>
11+
internal sealed class SecondaryResourceResponseDocument<TResource> : SingleData<ResourceObjectInResponse<TResource>>
1212
where TResource : IIdentifiable
1313
{
1414
public IDictionary<string, object> Meta { get; set; } = null!;

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourcePostRequestObject.cs renamed to src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPatchRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
44
{
5-
internal sealed class ResourcePostRequestObject<TResource> : ResourceObject<TResource>
5+
internal sealed class ResourceObjectInPatchRequest<TResource> : ResourceObject<TResource>
66
where TResource : IIdentifiable
77
{
88
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourcePatchRequestObject.cs renamed to src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInPostRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
44
{
5-
internal sealed class ResourcePatchRequestObject<TResource> : ResourceObject<TResource>
5+
internal sealed class ResourceObjectInPostRequest<TResource> : ResourceObject<TResource>
66
where TResource : IIdentifiable
77
{
88
}

src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceResponseObject.cs renamed to src/JsonApiDotNetCore.OpenApi/JsonApiObjects/ResourceObjects/ResourceObjectInResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace JsonApiDotNetCore.OpenApi.JsonApiObjects.ResourceObjects
88
{
99
[UsedImplicitly(ImplicitUseTargetFlags.Members)]
10-
internal sealed class ResourceResponseObject<TResource> : ResourceObject<TResource>
10+
internal sealed class ResourceObjectInResponse<TResource> : ResourceObject<TResource>
1111
where TResource : IIdentifiable
1212
{
1313
[Required]

src/JsonApiDotNetCore.OpenApi/JsonApiSchemaIdSelector.cs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,32 @@ internal sealed class JsonApiSchemaIdSelector
1515
{
1616
[typeof(ResourcePostRequestDocument<>)] = "###-post-request-document",
1717
[typeof(ResourcePatchRequestDocument<>)] = "###-patch-request-document",
18-
[typeof(ResourcePostRequestObject<>)] = "###-data-in-post-request",
19-
[typeof(ResourcePatchRequestObject<>)] = "###-data-in-patch-request",
20-
[typeof(ToOneRelationshipInRequest<>)] = "to-one-###-data-in-request",
21-
[typeof(NullableToOneRelationshipInRequest<>)] = "nullable-to-one-###-data-in-request",
22-
[typeof(ToManyRelationshipInRequest<>)] = "to-many-###-data-in-request",
18+
[typeof(ResourceObjectInPostRequest<>)] = "###-in-post-request",
19+
[typeof(ResourceObjectInPatchRequest<>)] = "###-in-patch-request",
20+
[typeof(ToOneRelationshipInRequest<>)] = "to-one-###-in-request",
21+
[typeof(NullableToOneRelationshipInRequest<>)] = "nullable-to-one-###-in-request",
22+
[typeof(ToManyRelationshipInRequest<>)] = "to-many-###-in-request",
2323
[typeof(PrimaryResourceResponseDocument<>)] = "###-primary-response-document",
2424
[typeof(SecondaryResourceResponseDocument<>)] = "###-secondary-response-document",
2525
[typeof(NullableSecondaryResourceResponseDocument<>)] = "nullable-###-secondary-response-document",
2626
[typeof(ResourceCollectionResponseDocument<>)] = "###-collection-response-document",
2727
[typeof(ResourceIdentifierResponseDocument<>)] = "###-identifier-response-document",
2828
[typeof(NullableResourceIdentifierResponseDocument<>)] = "nullable-###-identifier-response-document",
2929
[typeof(ResourceIdentifierCollectionResponseDocument<>)] = "###-identifier-collection-response-document",
30-
[typeof(ToOneRelationshipInResponse<>)] = "to-one-###-data-in-response",
31-
[typeof(NullableToOneRelationshipInResponse<>)] = "nullable-to-one-###-data-in-response",
32-
[typeof(ToManyRelationshipInResponse<>)] = "to-many-###-data-in-response",
33-
[typeof(ResourceResponseObject<>)] = "###-data-in-response",
30+
[typeof(ToOneRelationshipInResponse<>)] = "to-one-###-in-response",
31+
[typeof(NullableToOneRelationshipInResponse<>)] = "nullable-to-one-###-in-response",
32+
[typeof(ToManyRelationshipInResponse<>)] = "to-many-###-in-response",
33+
[typeof(ResourceObjectInResponse<>)] = "###-in-response",
3434
[typeof(ResourceIdentifierObject<>)] = "###-identifier"
3535
};
3636

37+
private readonly Type[] _resourceObjectOpenTypes =
38+
{
39+
typeof(ResourceObjectInPostRequest<>),
40+
typeof(ResourceObjectInPatchRequest<>),
41+
typeof(ResourceObjectInResponse<>)
42+
};
43+
3744
private readonly ResourceNameFormatter _formatter;
3845
private readonly IResourceGraph _resourceGraph;
3946

@@ -69,5 +76,25 @@ public string GetSchemaId(Type type)
6976
// Used for a fixed set of types, such as jsonapi-object, links-in-many-resource-document etc.
7077
return _formatter.FormatResourceName(type).Singularize();
7178
}
79+
80+
public string GetSchemaId(Type resourceObjectOpenType, ResourceObjectFieldType fieldType)
81+
{
82+
ArgumentGuard.NotNull(resourceObjectOpenType, nameof(resourceObjectOpenType));
83+
84+
if (!resourceObjectOpenType.IsConstructedGenericType || !ResourceObjectsOpenTypes.Contains(resourceObjectOpenType.GetGenericTypeDefinition()))
85+
{
86+
throw new InvalidOperationException($"Type '{resourceObjectOpenType.Name}' must be an open type representing a resource object.");
87+
}
88+
89+
Type resourceClrType = resourceObjectOpenType.GetGenericArguments().First();
90+
string resourceName = _formatter.FormatResourceName(resourceClrType).Singularize();
91+
string template = OpenTypeToSchemaTemplateMap[resourceObjectOpenType.GetGenericTypeDefinition()];
92+
93+
string fieldObjectName = fieldType == ResourceObjectFieldType.Attributes
94+
? JsonApiObjectPropertyName.AttributesObject
95+
: JsonApiObjectPropertyName.RelationshipsObject;
96+
97+
return template.Replace("###", $"{resourceName}-{fieldObjectName}");
98+
}
7299
}
73100
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace JsonApiDotNetCore.OpenApi
2+
{
3+
internal enum ResourceObjectFieldType
4+
{
5+
Attributes,
6+
Relationships
7+
}
8+
}

src/JsonApiDotNetCore.OpenApi/SwaggerComponents/CachingSwaggerGenerator.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
using System;
12
using System.Collections.Concurrent;
3+
using System.Collections.Generic;
24
using Microsoft.OpenApi.Models;
35
using Swashbuckle.AspNetCore.Swagger;
46
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -26,7 +28,14 @@ public OpenApiDocument GetSwagger(string documentName, string? host = null, stri
2628

2729
string cacheKey = $"{documentName}#{host}#{basePath}";
2830

29-
return _openApiDocumentCache.GetOrAdd(cacheKey, _ => _defaultSwaggerGenerator.GetSwagger(documentName, host, basePath));
31+
return _openApiDocumentCache.GetOrAdd(cacheKey, _ =>
32+
{
33+
OpenApiDocument document = _defaultSwaggerGenerator.GetSwagger(documentName, host, basePath);
34+
35+
// Remove once https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2283 is addressed.
36+
document.Components.Schemas = new SortedDictionary<string, OpenApiSchema>(document.Components.Schemas, StringComparer.Ordinal);
37+
return document;
38+
});
3039
}
3140
}
3241
}

src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceFieldObjectSchemaBuilder.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ private IDictionary<string, OpenApiSchema> GetFieldSchemas()
7676

7777
fullSchemaForAttributesObject.AdditionalPropertiesAllowed = false;
7878

79-
return GetReferenceSchemaForFieldObject(fullSchemaForAttributesObject, JsonApiObjectPropertyName.AttributesObject);
79+
string fieldObjectSchemaId = _jsonApiSchemaIdSelector.GetSchemaId(_resourceTypeInfo.ResourceObjectType, ResourceObjectFieldType.Attributes);
80+
return _schemaRepositoryAccessor.Current.AddDefinition(fieldObjectSchemaId, fullSchemaForAttributesObject);
8081
}
8182

8283
private void SetMembersOfAttributesObject(OpenApiSchema fullSchemaForAttributesObject)
@@ -103,9 +104,9 @@ private void SetMembersOfAttributesObject(OpenApiSchema fullSchemaForAttributesO
103104

104105
private static AttrCapabilities GetRequiredCapabilityForAttributes(Type resourceObjectOpenType)
105106
{
106-
return resourceObjectOpenType == typeof(ResourceResponseObject<>) ? AttrCapabilities.AllowView :
107-
resourceObjectOpenType == typeof(ResourcePostRequestObject<>) ? AttrCapabilities.AllowCreate :
108-
resourceObjectOpenType == typeof(ResourcePatchRequestObject<>) ? AttrCapabilities.AllowChange : throw new UnreachableCodeException();
107+
return resourceObjectOpenType == typeof(ResourceObjectInResponse<>) ? AttrCapabilities.AllowView :
108+
resourceObjectOpenType == typeof(ResourceObjectInPostRequest<>) ? AttrCapabilities.AllowCreate :
109+
resourceObjectOpenType == typeof(ResourceObjectInPatchRequest<>) ? AttrCapabilities.AllowChange : throw new UnreachableCodeException();
109110
}
110111

111112
private void AddAttributeSchemaToResourceObject(AttrAttribute attribute, OpenApiSchema attributesObjectSchema, OpenApiSchema resourceAttributeSchema)
@@ -127,7 +128,7 @@ private void ExposeSchema(OpenApiReference openApiReference, Type typeRepresente
127128

128129
private bool IsFieldRequired(ResourceFieldAttribute field)
129130
{
130-
if (field is HasManyAttribute || _resourceTypeInfo.ResourceObjectOpenType != typeof(ResourcePostRequestObject<>))
131+
if (field is HasManyAttribute || _resourceTypeInfo.ResourceObjectOpenType != typeof(ResourceObjectInPostRequest<>))
131132
{
132133
return false;
133134
}
@@ -144,15 +145,6 @@ private bool IsFieldRequired(ResourceFieldAttribute field)
144145
};
145146
}
146147

147-
private OpenApiSchema GetReferenceSchemaForFieldObject(OpenApiSchema fullSchema, string fieldObjectName)
148-
{
149-
// NSwag does not have proper support for using an inline schema for the attributes and relationships object in a resource object, see https://github.com/RicoSuter/NSwag/issues/3474. Once this issue has been resolved, we can remove this.
150-
string resourceObjectSchemaId = _jsonApiSchemaIdSelector.GetSchemaId(_resourceTypeInfo.ResourceObjectType);
151-
string fieldObjectSchemaId = resourceObjectSchemaId.Replace(JsonApiObjectPropertyName.Data, fieldObjectName);
152-
153-
return _schemaRepositoryAccessor.Current.AddDefinition(fieldObjectSchemaId, fullSchema);
154-
}
155-
156148
public OpenApiSchema? BuildRelationshipsObject(OpenApiSchema fullSchemaForResourceObject)
157149
{
158150
ArgumentGuard.NotNull(fullSchemaForResourceObject, nameof(fullSchemaForResourceObject));
@@ -168,7 +160,8 @@ private OpenApiSchema GetReferenceSchemaForFieldObject(OpenApiSchema fullSchema,
168160

169161
fullSchemaForRelationshipsObject.AdditionalPropertiesAllowed = false;
170162

171-
return GetReferenceSchemaForFieldObject(fullSchemaForRelationshipsObject, JsonApiObjectPropertyName.RelationshipsObject);
163+
string fieldObjectSchemaId = _jsonApiSchemaIdSelector.GetSchemaId(_resourceTypeInfo.ResourceObjectType, ResourceObjectFieldType.Relationships);
164+
return _schemaRepositoryAccessor.Current.AddDefinition(fieldObjectSchemaId, fullSchemaForRelationshipsObject);
172165
}
173166

174167
private void SetMembersOfRelationshipsObject(OpenApiSchema fullSchemaForRelationshipsObject)
@@ -228,7 +221,7 @@ private void AddRelationshipSchemaToResourceObject(RelationshipAttribute relatio
228221

229222
private static Type GetRelationshipSchemaType(RelationshipAttribute relationship, Type resourceObjectType)
230223
{
231-
return resourceObjectType.GetGenericTypeDefinition().IsAssignableTo(typeof(ResourceResponseObject<>))
224+
return resourceObjectType.GetGenericTypeDefinition().IsAssignableTo(typeof(ResourceObjectInResponse<>))
232225
? RelationshipTypeFactory.Instance.GetForResponse(relationship)
233226
: RelationshipTypeFactory.Instance.GetForRequest(relationship);
234227
}

src/JsonApiDotNetCore.OpenApi/SwaggerComponents/ResourceObjectSchemaGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public OpenApiSchema GenerateSchema(Type resourceObjectType)
9292

9393
private void RemoveResourceIdIfPostResourceObject(Type resourceObjectOpenType, OpenApiSchema fullSchemaForResourceObject)
9494
{
95-
if (resourceObjectOpenType == typeof(ResourcePostRequestObject<>) && !_allowClientGeneratedIds)
95+
if (resourceObjectOpenType == typeof(ResourceObjectInPostRequest<>) && !_allowClientGeneratedIds)
9696
{
9797
fullSchemaForResourceObject.Required.Remove(JsonApiObjectPropertyName.Id);
9898
fullSchemaForResourceObject.Properties.Remove(JsonApiObjectPropertyName.Id);

0 commit comments

Comments
 (0)