From 41fd508074f1b70415026df3aa878cd5f5e7b1ee Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 19 May 2025 11:37:25 +0300 Subject: [PATCH 1/3] fix: refactor to avoid adding duplicate entries --- .../Models/OpenApiDocument.cs | 45 ++++++++++--------- .../V3Tests/OpenApiDocumentTests.cs | 1 + 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index c5ac5f759..4bcaf761d 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -665,47 +665,52 @@ public bool AddComponent(string id, T componentToRegister) Utils.CheckArgumentNull(componentToRegister); Utils.CheckArgumentNullOrEmpty(id); Components ??= new(); + + static Dictionary AddToDictionary(Dictionary? dict, string key, TValue value) + { + dict ??= new Dictionary(); +#if NET5_0_OR_GREATER + dict.TryAdd(key, value); +#else + if (!dict.ContainsKey(key)) + { + dict.Add(key, value); + } +#endif + return dict; + } + switch (componentToRegister) { case IOpenApiSchema openApiSchema: - Components.Schemas ??= []; - Components.Schemas.Add(id, openApiSchema); + Components.Schemas = AddToDictionary(Components.Schemas, id, openApiSchema); break; case IOpenApiParameter openApiParameter: - Components.Parameters ??= []; - Components.Parameters.Add(id, openApiParameter); + Components.Parameters = AddToDictionary(Components.Parameters, id, openApiParameter); break; case IOpenApiResponse openApiResponse: - Components.Responses ??= []; - Components.Responses.Add(id, openApiResponse); + Components.Responses = AddToDictionary(Components.Responses, id, openApiResponse); break; case IOpenApiRequestBody openApiRequestBody: - Components.RequestBodies ??= []; - Components.RequestBodies.Add(id, openApiRequestBody); + Components.RequestBodies = AddToDictionary(Components.RequestBodies, id, openApiRequestBody); break; case IOpenApiLink openApiLink: - Components.Links ??= []; - Components.Links.Add(id, openApiLink); + Components.Links = AddToDictionary(Components.Links, id, openApiLink); break; case IOpenApiCallback openApiCallback: - Components.Callbacks ??= []; - Components.Callbacks.Add(id, openApiCallback); + Components.Callbacks = AddToDictionary(Components.Callbacks, id, openApiCallback); break; case IOpenApiPathItem openApiPathItem: - Components.PathItems ??= []; - Components.PathItems.Add(id, openApiPathItem); + Components.PathItems = AddToDictionary(Components.PathItems, id, openApiPathItem); break; case IOpenApiExample openApiExample: - Components.Examples ??= []; - Components.Examples.Add(id, openApiExample); + Components.Examples = AddToDictionary(Components.Examples, id, openApiExample); break; case IOpenApiHeader openApiHeader: - Components.Headers ??= []; - Components.Headers.Add(id, openApiHeader); + Components.Headers = AddToDictionary(Components.Headers, id, openApiHeader); break; case IOpenApiSecurityScheme openApiSecurityScheme: - Components.SecuritySchemes ??= []; - Components.SecuritySchemes.Add(id, openApiSecurityScheme); + Components.SecuritySchemes = AddToDictionary(Components.SecuritySchemes, id, openApiSecurityScheme); break; default: throw new ArgumentException($"Component type {componentToRegister!.GetType().Name} is not supported."); diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index d6b393404..fd6256203 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -1270,6 +1270,7 @@ public async Task SerializesDoubleHopeReferences() Description = "A reference to a pet" }; document.AddComponent("PetReference", petSchemaReference); + document.AddComponent("Pet", petSchema); document.Paths.Add("/pets", new OpenApiPathItem { Operations = new() From ad859ea0230ad7a7c71bae506c0b630c86ed1b2c Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 19 May 2025 11:38:32 +0300 Subject: [PATCH 2/3] chore: add comment --- .../V3Tests/OpenApiDocumentTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index fd6256203..3a65911db 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -1270,7 +1270,7 @@ public async Task SerializesDoubleHopeReferences() Description = "A reference to a pet" }; document.AddComponent("PetReference", petSchemaReference); - document.AddComponent("Pet", petSchema); + document.AddComponent("Pet", petSchema); // does not add duplicate keys document.Paths.Add("/pets", new OpenApiPathItem { Operations = new() From 634e8c50db44175c8f2a572e46912c8f24ec123e Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 19 May 2025 16:45:09 +0300 Subject: [PATCH 3/3] chore: refactor to keep track and register only when a component is added to a collection --- .../Models/OpenApiDocument.cs | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 4bcaf761d..51569f08c 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -666,56 +666,69 @@ public bool AddComponent(string id, T componentToRegister) Utils.CheckArgumentNullOrEmpty(id); Components ??= new(); - static Dictionary AddToDictionary(Dictionary? dict, string key, TValue value) + static bool AddToDictionary(Dictionary dict, string key, TValue value) { - dict ??= new Dictionary(); #if NET5_0_OR_GREATER - dict.TryAdd(key, value); + return dict.TryAdd(key, value); #else if (!dict.ContainsKey(key)) { dict.Add(key, value); + return true; } + return false; #endif - return dict; } + bool added = false; switch (componentToRegister) { case IOpenApiSchema openApiSchema: - Components.Schemas = AddToDictionary(Components.Schemas, id, openApiSchema); + Components.Schemas ??= []; + added = AddToDictionary(Components.Schemas, id, openApiSchema); break; case IOpenApiParameter openApiParameter: - Components.Parameters = AddToDictionary(Components.Parameters, id, openApiParameter); + Components.Parameters ??= []; + added = AddToDictionary(Components.Parameters, id, openApiParameter); break; case IOpenApiResponse openApiResponse: - Components.Responses = AddToDictionary(Components.Responses, id, openApiResponse); + Components.Responses ??= []; + added = AddToDictionary(Components.Responses, id, openApiResponse); break; case IOpenApiRequestBody openApiRequestBody: - Components.RequestBodies = AddToDictionary(Components.RequestBodies, id, openApiRequestBody); + Components.RequestBodies ??= []; + added = AddToDictionary(Components.RequestBodies, id, openApiRequestBody); break; case IOpenApiLink openApiLink: - Components.Links = AddToDictionary(Components.Links, id, openApiLink); + Components.Links ??= []; + added = AddToDictionary(Components.Links, id, openApiLink); break; case IOpenApiCallback openApiCallback: - Components.Callbacks = AddToDictionary(Components.Callbacks, id, openApiCallback); + Components.Callbacks ??= []; + added = AddToDictionary(Components.Callbacks, id, openApiCallback); break; case IOpenApiPathItem openApiPathItem: - Components.PathItems = AddToDictionary(Components.PathItems, id, openApiPathItem); + Components.PathItems ??= []; + added = AddToDictionary(Components.PathItems, id, openApiPathItem); break; case IOpenApiExample openApiExample: - Components.Examples = AddToDictionary(Components.Examples, id, openApiExample); + Components.Examples ??= []; + added = AddToDictionary(Components.Examples, id, openApiExample); break; case IOpenApiHeader openApiHeader: - Components.Headers = AddToDictionary(Components.Headers, id, openApiHeader); + Components.Headers ??= []; + added = AddToDictionary(Components.Headers, id, openApiHeader); break; case IOpenApiSecurityScheme openApiSecurityScheme: - Components.SecuritySchemes = AddToDictionary(Components.SecuritySchemes, id, openApiSecurityScheme); + Components.SecuritySchemes ??= []; + added = AddToDictionary(Components.SecuritySchemes, id, openApiSecurityScheme); break; default: throw new ArgumentException($"Component type {componentToRegister!.GetType().Name} is not supported."); } - return Workspace?.RegisterComponentForDocument(this, componentToRegister, id) ?? false; + + // Register only if it was actually added to the collection + return added && (Workspace?.RegisterComponentForDocument(this, componentToRegister, id) ?? false); } }