Skip to content

Commit 7f18865

Browse files
authored
Fixed crash where the OpenAPI atomic:operations discriminator key isn't unique per schema ID (#1776)
Use case: ```c# [Resource] public sealed class JigsawPuzzlePicture : Identifiable<long>; [Resource] public sealed class JigsawPuzzle : Identifiable<long> { [HasOne] public required JigsawPuzzlePicture Picture { get; set; } } ```
1 parent 9277256 commit 7f18865

File tree

26 files changed

+395
-432
lines changed

26 files changed

+395
-432
lines changed

src/Examples/JsonApiDotNetCoreExample/GeneratedSwagger/JsonApiDotNetCoreExample.json

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5391,29 +5391,29 @@
53915391
"discriminator": {
53925392
"propertyName": "openapi:discriminator",
53935393
"mapping": {
5394-
"addPerson": "#/components/schemas/createPersonOperation",
5395-
"addTag": "#/components/schemas/createTagOperation",
5396-
"addToPersonAssignedTodoItems": "#/components/schemas/addToPersonAssignedTodoItemsRelationshipOperation",
5397-
"addToPersonOwnedTodoItems": "#/components/schemas/addToPersonOwnedTodoItemsRelationshipOperation",
5398-
"addToTagTodoItems": "#/components/schemas/addToTagTodoItemsRelationshipOperation",
5399-
"addToTodoItemTags": "#/components/schemas/addToTodoItemTagsRelationshipOperation",
5400-
"addTodoItem": "#/components/schemas/createTodoItemOperation",
5401-
"removeFromPersonAssignedTodoItems": "#/components/schemas/removeFromPersonAssignedTodoItemsRelationshipOperation",
5402-
"removeFromPersonOwnedTodoItems": "#/components/schemas/removeFromPersonOwnedTodoItemsRelationshipOperation",
5403-
"removeFromTagTodoItems": "#/components/schemas/removeFromTagTodoItemsRelationshipOperation",
5404-
"removeFromTodoItemTags": "#/components/schemas/removeFromTodoItemTagsRelationshipOperation",
5405-
"removePerson": "#/components/schemas/deletePersonOperation",
5406-
"removeTag": "#/components/schemas/deleteTagOperation",
5407-
"removeTodoItem": "#/components/schemas/deleteTodoItemOperation",
5408-
"updatePerson": "#/components/schemas/updatePersonOperation",
5409-
"updatePersonAssignedTodoItems": "#/components/schemas/updatePersonAssignedTodoItemsRelationshipOperation",
5410-
"updatePersonOwnedTodoItems": "#/components/schemas/updatePersonOwnedTodoItemsRelationshipOperation",
5411-
"updateTag": "#/components/schemas/updateTagOperation",
5412-
"updateTagTodoItems": "#/components/schemas/updateTagTodoItemsRelationshipOperation",
5413-
"updateTodoItem": "#/components/schemas/updateTodoItemOperation",
5414-
"updateTodoItemAssignee": "#/components/schemas/updateTodoItemAssigneeRelationshipOperation",
5415-
"updateTodoItemOwner": "#/components/schemas/updateTodoItemOwnerRelationshipOperation",
5416-
"updateTodoItemTags": "#/components/schemas/updateTodoItemTagsRelationshipOperation"
5394+
"addToPersonAssignedTodoItemsRelationshipOperation": "#/components/schemas/addToPersonAssignedTodoItemsRelationshipOperation",
5395+
"addToPersonOwnedTodoItemsRelationshipOperation": "#/components/schemas/addToPersonOwnedTodoItemsRelationshipOperation",
5396+
"addToTagTodoItemsRelationshipOperation": "#/components/schemas/addToTagTodoItemsRelationshipOperation",
5397+
"addToTodoItemTagsRelationshipOperation": "#/components/schemas/addToTodoItemTagsRelationshipOperation",
5398+
"createPersonOperation": "#/components/schemas/createPersonOperation",
5399+
"createTagOperation": "#/components/schemas/createTagOperation",
5400+
"createTodoItemOperation": "#/components/schemas/createTodoItemOperation",
5401+
"deletePersonOperation": "#/components/schemas/deletePersonOperation",
5402+
"deleteTagOperation": "#/components/schemas/deleteTagOperation",
5403+
"deleteTodoItemOperation": "#/components/schemas/deleteTodoItemOperation",
5404+
"removeFromPersonAssignedTodoItemsRelationshipOperation": "#/components/schemas/removeFromPersonAssignedTodoItemsRelationshipOperation",
5405+
"removeFromPersonOwnedTodoItemsRelationshipOperation": "#/components/schemas/removeFromPersonOwnedTodoItemsRelationshipOperation",
5406+
"removeFromTagTodoItemsRelationshipOperation": "#/components/schemas/removeFromTagTodoItemsRelationshipOperation",
5407+
"removeFromTodoItemTagsRelationshipOperation": "#/components/schemas/removeFromTodoItemTagsRelationshipOperation",
5408+
"updatePersonAssignedTodoItemsRelationshipOperation": "#/components/schemas/updatePersonAssignedTodoItemsRelationshipOperation",
5409+
"updatePersonOperation": "#/components/schemas/updatePersonOperation",
5410+
"updatePersonOwnedTodoItemsRelationshipOperation": "#/components/schemas/updatePersonOwnedTodoItemsRelationshipOperation",
5411+
"updateTagOperation": "#/components/schemas/updateTagOperation",
5412+
"updateTagTodoItemsRelationshipOperation": "#/components/schemas/updateTagTodoItemsRelationshipOperation",
5413+
"updateTodoItemAssigneeRelationshipOperation": "#/components/schemas/updateTodoItemAssigneeRelationshipOperation",
5414+
"updateTodoItemOperation": "#/components/schemas/updateTodoItemOperation",
5415+
"updateTodoItemOwnerRelationshipOperation": "#/components/schemas/updateTodoItemOwnerRelationshipOperation",
5416+
"updateTodoItemTagsRelationshipOperation": "#/components/schemas/updateTodoItemTagsRelationshipOperation"
54175417
}
54185418
},
54195419
"x-abstract": true

src/Examples/OpenApiKiotaClientExample/GeneratedCode/Models/AtomicOperation.cs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,29 +51,29 @@ public AtomicOperation()
5151
var mappingValue = parseNode.GetChildNode("openapi:discriminator")?.GetStringValue();
5252
return mappingValue switch
5353
{
54-
"addPerson" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.CreatePersonOperation(),
55-
"addTag" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.CreateTagOperation(),
56-
"addTodoItem" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.CreateTodoItemOperation(),
57-
"addToPersonAssignedTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToPersonAssignedTodoItemsRelationshipOperation(),
58-
"addToPersonOwnedTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToPersonOwnedTodoItemsRelationshipOperation(),
59-
"addToTagTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToTagTodoItemsRelationshipOperation(),
60-
"addToTodoItemTags" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToTodoItemTagsRelationshipOperation(),
61-
"removeFromPersonAssignedTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromPersonAssignedTodoItemsRelationshipOperation(),
62-
"removeFromPersonOwnedTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromPersonOwnedTodoItemsRelationshipOperation(),
63-
"removeFromTagTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromTagTodoItemsRelationshipOperation(),
64-
"removeFromTodoItemTags" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromTodoItemTagsRelationshipOperation(),
65-
"removePerson" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.DeletePersonOperation(),
66-
"removeTag" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.DeleteTagOperation(),
67-
"removeTodoItem" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.DeleteTodoItemOperation(),
68-
"updatePerson" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonOperation(),
69-
"updatePersonAssignedTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonAssignedTodoItemsRelationshipOperation(),
70-
"updatePersonOwnedTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonOwnedTodoItemsRelationshipOperation(),
71-
"updateTag" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTagOperation(),
72-
"updateTagTodoItems" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTagTodoItemsRelationshipOperation(),
73-
"updateTodoItem" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemOperation(),
74-
"updateTodoItemAssignee" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemAssigneeRelationshipOperation(),
75-
"updateTodoItemOwner" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemOwnerRelationshipOperation(),
76-
"updateTodoItemTags" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemTagsRelationshipOperation(),
54+
"addToPersonAssignedTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToPersonAssignedTodoItemsRelationshipOperation(),
55+
"addToPersonOwnedTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToPersonOwnedTodoItemsRelationshipOperation(),
56+
"addToTagTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToTagTodoItemsRelationshipOperation(),
57+
"addToTodoItemTagsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AddToTodoItemTagsRelationshipOperation(),
58+
"createPersonOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.CreatePersonOperation(),
59+
"createTagOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.CreateTagOperation(),
60+
"createTodoItemOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.CreateTodoItemOperation(),
61+
"deletePersonOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.DeletePersonOperation(),
62+
"deleteTagOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.DeleteTagOperation(),
63+
"deleteTodoItemOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.DeleteTodoItemOperation(),
64+
"removeFromPersonAssignedTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromPersonAssignedTodoItemsRelationshipOperation(),
65+
"removeFromPersonOwnedTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromPersonOwnedTodoItemsRelationshipOperation(),
66+
"removeFromTagTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromTagTodoItemsRelationshipOperation(),
67+
"removeFromTodoItemTagsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.RemoveFromTodoItemTagsRelationshipOperation(),
68+
"updatePersonAssignedTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonAssignedTodoItemsRelationshipOperation(),
69+
"updatePersonOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonOperation(),
70+
"updatePersonOwnedTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdatePersonOwnedTodoItemsRelationshipOperation(),
71+
"updateTagOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTagOperation(),
72+
"updateTagTodoItemsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTagTodoItemsRelationshipOperation(),
73+
"updateTodoItemAssigneeRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemAssigneeRelationshipOperation(),
74+
"updateTodoItemOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemOperation(),
75+
"updateTodoItemOwnerRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemOwnerRelationshipOperation(),
76+
"updateTodoItemTagsRelationshipOperation" => new global::OpenApiKiotaClientExample.GeneratedCode.Models.UpdateTodoItemTagsRelationshipOperation(),
7777
_ => new global::OpenApiKiotaClientExample.GeneratedCode.Models.AtomicOperation(),
7878
};
7979
}

src/JsonApiDotNetCore.OpenApi.Swashbuckle/JsonApiSchemaIdSelector.cs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ internal sealed class JsonApiSchemaIdSelector
1515
private const string ResourceTypeSchemaIdTemplate = "[ResourceName] Resource Type";
1616
private const string MetaSchemaIdTemplate = "Meta";
1717

18-
private const string ResourceAtomicOperationDiscriminatorValueTemplate = "[OperationCode] [ResourceName]";
19-
private const string UpdateRelationshipAtomicOperationDiscriminatorValueTemplate = "Update [ResourceName] [RelationshipName]";
20-
private const string AddToRelationshipAtomicOperationDiscriminatorValueTemplate = "Add To [ResourceName] [RelationshipName]";
21-
private const string RemoveFromRelationshipAtomicOperationDiscriminatorValueTemplate = "Remove From [ResourceName] [RelationshipName]";
22-
2318
private const string UpdateRelationshipAtomicOperationSchemaIdTemplate = "Update [ResourceName] [RelationshipName] Relationship Operation";
2419
private const string AddToRelationshipAtomicOperationSchemaIdTemplate = "Add To [ResourceName] [RelationshipName] Relationship Operation";
2520
private const string RemoveFromRelationshipAtomicOperationSchemaIdTemplate = "Remove From [ResourceName] [RelationshipName] Relationship Operation";
@@ -157,27 +152,6 @@ public string GetAtomicOperationCodeSchemaId(AtomicOperationCode operationCode)
157152
return ApplySchemaTemplate("[OperationCode] Operation Code", null, null, operationCode);
158153
}
159154

160-
public string GetAtomicOperationDiscriminatorValue(AtomicOperationCode operationCode, ResourceType resourceType)
161-
{
162-
ArgumentNullException.ThrowIfNull(resourceType);
163-
164-
return ApplySchemaTemplate(ResourceAtomicOperationDiscriminatorValueTemplate, resourceType, null, operationCode);
165-
}
166-
167-
public string GetAtomicOperationDiscriminatorValue(AtomicOperationCode operationCode, RelationshipAttribute relationship)
168-
{
169-
ArgumentNullException.ThrowIfNull(relationship);
170-
171-
string schemaIdTemplate = operationCode switch
172-
{
173-
AtomicOperationCode.Add => AddToRelationshipAtomicOperationDiscriminatorValueTemplate,
174-
AtomicOperationCode.Remove => RemoveFromRelationshipAtomicOperationDiscriminatorValueTemplate,
175-
_ => UpdateRelationshipAtomicOperationDiscriminatorValueTemplate
176-
};
177-
178-
return ApplySchemaTemplate(schemaIdTemplate, relationship.LeftType, relationship.PublicName, null);
179-
}
180-
181155
public string GetRelationshipAtomicOperationSchemaId(RelationshipAttribute relationship, AtomicOperationCode operationCode)
182156
{
183157
ArgumentNullException.ThrowIfNull(relationship);

src/JsonApiDotNetCore.OpenApi.Swashbuckle/SchemaGenerators/Documents/AtomicOperationsDocumentSchemaGenerator.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,7 @@ private void GenerateSchemaForResourceOperation(Type operationOpenType, Resource
206206
SetOperationCode(inlineSchemaForOperation, operationCode, schemaRepository);
207207
}
208208

209-
string discriminatorValue = _schemaIdSelector.GetAtomicOperationDiscriminatorValue(operationCode, resourceType);
210-
MapInDiscriminator(referenceSchemaForOperation, discriminatorValue, schemaRepository);
209+
MapInDiscriminator(referenceSchemaForOperation, schemaRepository);
211210

212211
traceScope.TraceSucceeded(referenceSchemaForOperation.Reference.Id);
213212
}
@@ -264,11 +263,11 @@ private void SetOperationCode(OpenApiSchema fullSchema, AtomicOperationCode oper
264263
fullSchema.Properties[JsonApiPropertyName.Op] = referenceSchema.WrapInExtendedSchema();
265264
}
266265

267-
private static void MapInDiscriminator(OpenApiSchema referenceSchemaForOperation, string discriminatorValue, SchemaRepository schemaRepository)
266+
private static void MapInDiscriminator(OpenApiSchema referenceSchemaForOperation, SchemaRepository schemaRepository)
268267
{
269268
OpenApiSchema referenceSchemaForAbstractOperation = schemaRepository.LookupByType(AtomicOperationAbstractType);
270269
OpenApiSchema fullSchemaForAbstractOperation = schemaRepository.Schemas[referenceSchemaForAbstractOperation.Reference.Id];
271-
fullSchemaForAbstractOperation.Discriminator.Mapping.Add(discriminatorValue, referenceSchemaForOperation.Reference.ReferenceV3);
270+
fullSchemaForAbstractOperation.Discriminator.Mapping.Add(referenceSchemaForOperation.Reference.Id, referenceSchemaForOperation.Reference.ReferenceV3);
272271
}
273272

274273
private static HashSet<RelationshipAttribute> GetRelationshipsInTypeHierarchy(ResourceType baseType)
@@ -371,8 +370,7 @@ private void GenerateSchemaForRelationshipOperation(Type operationOpenType, Rela
371370
};
372371
}
373372

374-
string discriminatorValue = _schemaIdSelector.GetAtomicOperationDiscriminatorValue(operationCode, relationship);
375-
MapInDiscriminator(referenceSchemaForOperation, discriminatorValue, schemaRepository);
373+
MapInDiscriminator(referenceSchemaForOperation, schemaRepository);
376374

377375
traceScope.TraceSucceeded(schemaId);
378376
}

0 commit comments

Comments
 (0)