From 1349182c56e4d2e71ccd4a40a97490ccd73e1395 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 21 Aug 2023 18:33:37 +0300 Subject: [PATCH 01/18] Create a media type object if produces is empty but a response schema exists --- .../V2/OpenApiOperationDeserializer.cs | 2 +- .../V2/OpenApiResponseDeserializer.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs index 1cf5b7ae8..2630c14b5 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs @@ -67,7 +67,7 @@ internal static partial class OpenApiV2Deserializer { "produces", (o, n) => { var produces = n.CreateSimpleList(s => s.GetScalarValue()); - if (produces.Count > 0) { + if (produces != null) { n.Context.SetTempStorage(TempStorageKeys.OperationProduces, produces); } } diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 343dcd2ce..f58af4fd2 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -74,11 +74,23 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P var produces = context.GetFromTempStorage>(TempStorageKeys.OperationProduces) ?? context.GetFromTempStorage>(TempStorageKeys.GlobalProduces); + if (produces != null) { + var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); + + if (produces.Count == 0 && schema != null) + { + var mediaType = new OpenApiMediaType + { + Schema = schema + }; + + response.Content.Add(string.Empty, mediaType); + } + foreach (var produce in produces) { - var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); if (response.Content.ContainsKey(produce) && response.Content[produce] != null) { From a53defa25dac6bd23ba82c51912381948aad03ed Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 21 Aug 2023 18:37:04 +0300 Subject: [PATCH 02/18] Add a unit test for validation --- .../Microsoft.OpenApi.Readers.Tests.csproj | 3 +- .../V2Tests/OpenApiOperationTests.cs | 44 +++++++++++++++++++ ...ationWithEmptyProducesArrayInResponse.json | 14 ++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithEmptyProducesArrayInResponse.json diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index 1245dd629..e3e13fb4c 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -1,4 +1,4 @@ - + net7.0 false @@ -55,6 +55,7 @@ Never + Never diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index 0deb72a5c..8bda55326 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -375,5 +375,49 @@ public void ParseOperationWithResponseExamplesShouldSucceed() } ); } + + [Fact] + public void ParseOperationWithEmptyProducesArraySetsResponseSchemaIfExists() + { + // Arrange + MapNode node; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "operationWithEmptyProducesArrayInResponse.json"))) + { + node = TestHelper.CreateYamlMapNode(stream); + } + + // Act + var operation = OpenApiV2Deserializer.LoadOperation(node); + + // Assert + operation.Should().BeEquivalentTo( + new OpenApiOperation() + { + Responses = new OpenApiResponses() + { + { "200", new OpenApiResponse() + { + Description = "OK", + Content = + { + [""] = new OpenApiMediaType() + { + Schema = new OpenApiSchema() + { + Format = "binary", + Description = "The content of the file.", + Type = "string", + Extensions = + { + ["x-ms-summary"] = new OpenApiString("File Content") + } + } + } + } + }} + } + } + ); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithEmptyProducesArrayInResponse.json b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithEmptyProducesArrayInResponse.json new file mode 100644 index 000000000..d1495fe44 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithEmptyProducesArrayInResponse.json @@ -0,0 +1,14 @@ +{ + "produces": [], + "responses": { + "200": { + "description": "OK", + "schema": { + "format": "binary", + "description": "The content of the file.", + "type": "string", + "x-ms-summary": "File Content" + } + } + } +} \ No newline at end of file From e9d3660d6b6b62ea90eb745b732cd63c745ec540 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 17:06:48 +0300 Subject: [PATCH 03/18] Default to application/octet-stream as the MIME type if produces is an empty array --- src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index f58af4fd2..55d2730d1 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -86,7 +86,7 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P Schema = schema }; - response.Content.Add(string.Empty, mediaType); + response.Content.Add("application/octet-stream", mediaType); } foreach (var produce in produces) From 2ba6f70fcafae09d44bcf1d00be63cfc23563ba7 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 19:00:08 +0300 Subject: [PATCH 04/18] Revert change; add a default value for produces when its null; remove unnecessary check --- .../V2/OpenApiOperationDeserializer.cs | 2 +- .../V2/OpenApiResponseDeserializer.cs | 47 +++++++------------ 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs index 2630c14b5..1cf5b7ae8 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs @@ -67,7 +67,7 @@ internal static partial class OpenApiV2Deserializer { "produces", (o, n) => { var produces = n.CreateSimpleList(s => s.GetScalarValue()); - if (produces != null) { + if (produces.Count > 0) { n.Context.SetTempStorage(TempStorageKeys.OperationProduces, produces); } } diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 55d2730d1..8aeb40c6d 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -73,47 +73,34 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P } var produces = context.GetFromTempStorage>(TempStorageKeys.OperationProduces) - ?? context.GetFromTempStorage>(TempStorageKeys.GlobalProduces); + ?? new List { "application/octet-stream" }; - if (produces != null) + var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); + + foreach (var produce in produces) { - var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); - if (produces.Count == 0 && schema != null) + if (response.Content.ContainsKey(produce) && response.Content[produce] != null) { - var mediaType = new OpenApiMediaType + if (schema != null) { - Schema = schema - }; - - response.Content.Add("application/octet-stream", mediaType); + response.Content[produce].Schema = schema; + ProcessAnyFields(mapNode, response.Content[produce], _mediaTypeAnyFields); + } } - - foreach (var produce in produces) + else { - - if (response.Content.ContainsKey(produce) && response.Content[produce] != null) - { - if (schema != null) - { - response.Content[produce].Schema = schema; - ProcessAnyFields(mapNode, response.Content[produce], _mediaTypeAnyFields); - } - } - else + var mediaType = new OpenApiMediaType { - var mediaType = new OpenApiMediaType - { - Schema = schema - }; + Schema = schema + }; - response.Content.Add(produce, mediaType); - } + response.Content.Add(produce, mediaType); } - - context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response); - context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); } + + context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response); + context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); } private static void LoadExamples(OpenApiResponse response, ParseNode node) From 3636351c94f8bf6ead3ca766b594cc54f0962877 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 19:00:19 +0300 Subject: [PATCH 05/18] Update test --- .../V2Tests/OpenApiOperationTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index 8bda55326..b9681e25a 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -400,7 +400,7 @@ public void ParseOperationWithEmptyProducesArraySetsResponseSchemaIfExists() Description = "OK", Content = { - [""] = new OpenApiMediaType() + ["application/octet-stream"] = new OpenApiMediaType() { Schema = new OpenApiSchema() { From 6a7993e8033efc05158d535e00de2e6910628390 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 19:09:05 +0300 Subject: [PATCH 06/18] Clean up test --- .../V2Tests/OpenApiOperationTests.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index b9681e25a..f3f251a9e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -182,8 +182,8 @@ public class OpenApiOperationTests } } }, - Extensions = { - [OpenApiConstants.BodyName] = new OpenApiString("petObject") + Extensions = { + [OpenApiConstants.BodyName] = new OpenApiString("petObject") } }, Responses = new OpenApiResponses @@ -381,10 +381,8 @@ public void ParseOperationWithEmptyProducesArraySetsResponseSchemaIfExists() { // Arrange MapNode node; - using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "operationWithEmptyProducesArrayInResponse.json"))) - { - node = TestHelper.CreateYamlMapNode(stream); - } + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "operationWithEmptyProducesArrayInResponse.json")); + node = TestHelper.CreateYamlMapNode(stream); // Act var operation = OpenApiV2Deserializer.LoadOperation(node); From 66c3e5ca253571070659cc121c398cbaa4c40e1c Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 19:11:04 +0300 Subject: [PATCH 07/18] Add test to verify request body schema is set when consumes is an empty array --- .../Microsoft.OpenApi.Readers.Tests.csproj | 3 +++ .../V2Tests/OpenApiOperationTests.cs | 17 ++++++++++++- .../operationWithBodyAndEmptyConsumes.yaml | 25 +++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithBodyAndEmptyConsumes.yaml diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index e3e13fb4c..93aaebbcd 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -51,6 +51,9 @@ Never + + Never + Never diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index f3f251a9e..3b0f32871 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -417,5 +417,20 @@ public void ParseOperationWithEmptyProducesArraySetsResponseSchemaIfExists() } ); } + + [Fact] + public void ParseOperationWithBodyAndEmptyConsumesSetsRequestBodySchemaIfExists() + { + // Arrange + MapNode node; + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "operationWithBodyAndEmptyConsumes.yaml")); + node = TestHelper.CreateYamlMapNode(stream); + + // Act + var operation = OpenApiV2Deserializer.LoadOperation(node); + + // Assert + operation.Should().BeEquivalentTo(_operationWithBody); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithBodyAndEmptyConsumes.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithBodyAndEmptyConsumes.yaml new file mode 100644 index 000000000..802ec9cf7 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/operationWithBodyAndEmptyConsumes.yaml @@ -0,0 +1,25 @@ +# Modified from https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#operation-object-example +summary: Updates a pet in the store with request body +description: "" +operationId: updatePetWithBody +consumes: [] +produces: +- application/json +- application/xml +parameters: +- name: petId + in: path + description: ID of pet that needs to be updated + required: true + type: string +- name: petObject + in: body + description: Pet to update with + required: true + schema: + type: object +responses: + '200': + description: Pet updated. + '405': + description: Invalid input From 11d9f6800fffdde6677dee09e8d45756b2a03f32 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 19:32:32 +0300 Subject: [PATCH 08/18] Revert previous fix --- .../V2/OpenApiOperationDeserializer.cs | 2 +- .../V2/OpenApiResponseDeserializer.cs | 47 ++++++++++++------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs index 1cf5b7ae8..2630c14b5 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs @@ -67,7 +67,7 @@ internal static partial class OpenApiV2Deserializer { "produces", (o, n) => { var produces = n.CreateSimpleList(s => s.GetScalarValue()); - if (produces.Count > 0) { + if (produces != null) { n.Context.SetTempStorage(TempStorageKeys.OperationProduces, produces); } } diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 8aeb40c6d..f58af4fd2 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -73,34 +73,47 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P } var produces = context.GetFromTempStorage>(TempStorageKeys.OperationProduces) - ?? new List { "application/octet-stream" }; + ?? context.GetFromTempStorage>(TempStorageKeys.GlobalProduces); - var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); - - foreach (var produce in produces) + if (produces != null) { + var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); - if (response.Content.ContainsKey(produce) && response.Content[produce] != null) - { - if (schema != null) - { - response.Content[produce].Schema = schema; - ProcessAnyFields(mapNode, response.Content[produce], _mediaTypeAnyFields); - } - } - else + if (produces.Count == 0 && schema != null) { var mediaType = new OpenApiMediaType { Schema = schema }; - response.Content.Add(produce, mediaType); + response.Content.Add(string.Empty, mediaType); } - } - context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response); - context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); + foreach (var produce in produces) + { + + if (response.Content.ContainsKey(produce) && response.Content[produce] != null) + { + if (schema != null) + { + response.Content[produce].Schema = schema; + ProcessAnyFields(mapNode, response.Content[produce], _mediaTypeAnyFields); + } + } + else + { + var mediaType = new OpenApiMediaType + { + Schema = schema + }; + + response.Content.Add(produce, mediaType); + } + } + + context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response); + context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); + } } private static void LoadExamples(OpenApiResponse response, ParseNode node) From 5f90def1e4f82e0d6462f17f689f161a822079c6 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 19:34:20 +0300 Subject: [PATCH 09/18] Use "application/octet-stream" as default MIME type if produces is an empty array --- src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index f58af4fd2..55d2730d1 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -86,7 +86,7 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P Schema = schema }; - response.Content.Add(string.Empty, mediaType); + response.Content.Add("application/octet-stream", mediaType); } foreach (var produce in produces) From 9b9113028a378234587cd2d92cd45ff9feef7e34 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 22 Aug 2023 19:37:53 +0300 Subject: [PATCH 10/18] Combine two checks using TryGetValue() for efficiency --- .../V2/OpenApiResponseDeserializer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 55d2730d1..8357aa3d2 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -92,12 +92,12 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P foreach (var produce in produces) { - if (response.Content.ContainsKey(produce) && response.Content[produce] != null) + if (response.Content.TryGetValue(produce, out var produceValue)) { if (schema != null) { - response.Content[produce].Schema = schema; - ProcessAnyFields(mapNode, response.Content[produce], _mediaTypeAnyFields); + produceValue.Schema = schema; + ProcessAnyFields(mapNode, produceValue, _mediaTypeAnyFields); } } else From dc319ec435d05ccfe09c4c6cec0d9219c22732ec Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 30 Aug 2023 14:50:28 +0300 Subject: [PATCH 11/18] Only set produces if it contains a value --- .../V2/OpenApiOperationDeserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs index 2630c14b5..1cf5b7ae8 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs @@ -67,7 +67,7 @@ internal static partial class OpenApiV2Deserializer { "produces", (o, n) => { var produces = n.CreateSimpleList(s => s.GetScalarValue()); - if (produces != null) { + if (produces.Count > 0) { n.Context.SetTempStorage(TempStorageKeys.OperationProduces, produces); } } From 845f3145ced3bcf13f8118898dd3267f71b8badc Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 30 Aug 2023 14:51:22 +0300 Subject: [PATCH 12/18] Refactor and clean up code --- .../V2/OpenApiResponseDeserializer.cs | 48 +++++-------- .../Services/OpenApiReferenceResolver.cs | 2 +- .../V2Tests/OpenApiDocumentTests.cs | 72 +++++++------------ 3 files changed, 45 insertions(+), 77 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 8357aa3d2..5734ff19d 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -73,47 +73,35 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P } var produces = context.GetFromTempStorage>(TempStorageKeys.OperationProduces) - ?? context.GetFromTempStorage>(TempStorageKeys.GlobalProduces); + ?? context.GetFromTempStorage>(TempStorageKeys.GlobalProduces) + ?? new List { "application/octet-stream" }; - if (produces != null) + var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); + + foreach (var produce in produces) { - var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); - if (produces.Count == 0 && schema != null) + if (response.Content.TryGetValue(produce, out var produceValue)) { - var mediaType = new OpenApiMediaType + if (schema != null) { - Schema = schema - }; - - response.Content.Add("application/octet-stream", mediaType); + produceValue.Schema = schema; + ProcessAnyFields(mapNode, produceValue, _mediaTypeAnyFields); + } } - - foreach (var produce in produces) + else { - - if (response.Content.TryGetValue(produce, out var produceValue)) - { - if (schema != null) - { - produceValue.Schema = schema; - ProcessAnyFields(mapNode, produceValue, _mediaTypeAnyFields); - } - } - else + var mediaType = new OpenApiMediaType { - var mediaType = new OpenApiMediaType - { - Schema = schema - }; + Schema = schema + }; - response.Content.Add(produce, mediaType); - } + response.Content.Add(produce, mediaType); } - - context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response); - context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); } + + context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response); + context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); } private static void LoadExamples(OpenApiResponse response, ParseNode node) diff --git a/src/Microsoft.OpenApi/Services/OpenApiReferenceResolver.cs b/src/Microsoft.OpenApi/Services/OpenApiReferenceResolver.cs index feeceb9af..3b431d4b5 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiReferenceResolver.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiReferenceResolver.cs @@ -256,7 +256,7 @@ private void ResolveTags(IList tags) private T ResolveReference(OpenApiReference reference) where T : class, IOpenApiReferenceable, new() { - if (string.IsNullOrEmpty(reference.ExternalResource)) + if (string.IsNullOrEmpty(reference?.ExternalResource)) { try { diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs index fcf0471ea..2779c598d 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -163,45 +163,25 @@ public void ShouldParseProducesInAnyOrder() var reader = new OpenApiStreamReader(); var doc = reader.Read(stream, out var diagnostic); - var successSchema = new OpenApiSchema() + var okSchema = new OpenApiSchema() { - Type = "array", Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "Item", HostDocument = doc }, - Items = new OpenApiSchema() + Properties = new Dictionary() { - Reference = new OpenApiReference() - { - Type = ReferenceType.Schema, - Id = "Item", - HostDocument = doc + { "id", new OpenApiSchema() + { + Type = "string", + Description = "Item identifier." + } } } }; - var okSchema = new OpenApiSchema() - { - Reference = new OpenApiReference - { - Type = ReferenceType.Schema, - Id = "Item", - HostDocument = doc - }, - Properties = new Dictionary() - { - { "id", new OpenApiSchema() - { - Type = "string", - Description = "Item identifier." - } - } - } - }; - var errorSchema = new OpenApiSchema() { Reference = new OpenApiReference @@ -211,24 +191,24 @@ public void ShouldParseProducesInAnyOrder() HostDocument = doc }, Properties = new Dictionary() - { - { "code", new OpenApiSchema() - { - Type = "integer", - Format = "int32" - } - }, - { "message", new OpenApiSchema() - { - Type = "string" - } - }, - { "fields", new OpenApiSchema() - { - Type = "string" - } - } - } + { + { "code", new OpenApiSchema() + { + Type = "integer", + Format = "int32" + } + }, + { "message", new OpenApiSchema() + { + Type = "string" + } + }, + { "fields", new OpenApiSchema() + { + Type = "string" + } + } + } }; var okMediaType = new OpenApiMediaType From 4e81bf938300b62f275a7806328c97525fda597b Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 30 Aug 2023 18:33:41 +0300 Subject: [PATCH 13/18] Maintain ordering of properties as specified in fixedFieldMap by doing a union of fixed field map keys and property names in the map node --- src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs index 4bb15a8d9..907af8e37 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs @@ -29,10 +29,11 @@ private static void ParseMap( return; } - foreach (var propertyNode in mapNode) + var allFields = fixedFieldMap.Keys.Union(mapNode.Select(x => x.Name)); + foreach (var propertyNode in allFields) { - propertyNode.ParseField(domainObject, fixedFieldMap, patternFieldMap); - requiredFields?.Remove(propertyNode.Name); + mapNode[propertyNode]?.ParseField(domainObject, fixedFieldMap, patternFieldMap); + requiredFields?.Remove(propertyNode); } } From ab9648cb69dea337d35516762261cfb793db59d6 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 30 Aug 2023 18:52:07 +0300 Subject: [PATCH 14/18] Remove unused private method --- .../V2/OpenApiV2Deserializer.cs | 41 ------------------- 1 file changed, 41 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs index 907af8e37..7bba51dd5 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs @@ -105,47 +105,6 @@ private static void ProcessAnyListFields( } } - private static void ProcessAnyMapFields( - MapNode mapNode, - T domainObject, - AnyMapFieldMap anyMapFieldMap) - { - foreach (var anyMapFieldName in anyMapFieldMap.Keys.ToList()) - { - try - { - var newProperty = new List(); - - mapNode.Context.StartObject(anyMapFieldName); - - foreach (var propertyMapElement in anyMapFieldMap[anyMapFieldName].PropertyMapGetter(domainObject)) - { - if (propertyMapElement.Value != null) - { - mapNode.Context.StartObject(propertyMapElement.Key); - - var any = anyMapFieldMap[anyMapFieldName].PropertyGetter(propertyMapElement.Value); - - var newAny = OpenApiAnyConverter.GetSpecificOpenApiAny( - any, - anyMapFieldMap[anyMapFieldName].SchemaGetter(domainObject)); - - anyMapFieldMap[anyMapFieldName].PropertySetter(propertyMapElement.Value, newAny); - } - } - } - catch (OpenApiException exception) - { - exception.Pointer = mapNode.Context.GetLocation(); - mapNode.Context.Diagnostic.Errors.Add(new OpenApiError(exception)); - } - finally - { - mapNode.Context.EndObject(); - } - } - } - public static IOpenApiAny LoadAny(ParseNode node) { return OpenApiAnyConverter.GetSpecificOpenApiAny(node.CreateAny()); From e666d3dc1fdee42fded39687d06d78ec949cae8d Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 31 Aug 2023 20:55:04 +0300 Subject: [PATCH 15/18] Add null conditional operator --- src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs index 7bba51dd5..bc67760e7 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs @@ -79,7 +79,7 @@ private static void ProcessAnyListFields( mapNode.Context.StartObject(anyListFieldName); - var list = anyListFieldMap[anyListFieldName].PropertyGetter(domainObject); + var list = anyListFieldMap[anyListFieldName]?.PropertyGetter(domainObject); if (list != null) { foreach (var propertyElement in list) From 966b4598f8064bf1d3bba70c005b986bab8a1fe7 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 31 Aug 2023 21:05:26 +0300 Subject: [PATCH 16/18] Use Assert.Fail() to fail the test --- .../V2Tests/OpenApiDocumentTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs index 2779c598d..0b35d43e8 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -419,7 +419,7 @@ public void ShouldAllowComponentsThatJustContainAReference() if (schema2.UnresolvedReference && schema1.Reference.Id == schema2.Reference.Id) { // detected a cycle - this code gets triggered - Assert.True(false, "A cycle should not be detected"); + Assert.Fail("A cycle should not be detected"); } } } From a83f01a3a412c375a9aff4546dcf3e1e7db2fe2d Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 31 Aug 2023 22:34:31 +0300 Subject: [PATCH 17/18] Use static keyword; fix sonarcloud bug --- .../Microsoft.OpenApi.Readers.csproj | 1 + .../V2/OpenApiV2Deserializer.cs | 22 ++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index ba2cf568d..5bdf557f2 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -1,6 +1,7 @@  netstandard2.0 + 9.0 true http://go.microsoft.com/fwlink/?LinkID=288890 https://github.com/Microsoft/OpenAPI.NET diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs index bc67760e7..c3f26b896 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2Deserializer.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -29,7 +29,7 @@ private static void ParseMap( return; } - var allFields = fixedFieldMap.Keys.Union(mapNode.Select(x => x.Name)); + var allFields = fixedFieldMap.Keys.Union(mapNode.Select(static x => x.Name)); foreach (var propertyNode in allFields) { mapNode[propertyNode]?.ParseField(domainObject, fixedFieldMap, patternFieldMap); @@ -78,16 +78,18 @@ private static void ProcessAnyListFields( var newProperty = new List(); mapNode.Context.StartObject(anyListFieldName); - - var list = anyListFieldMap[anyListFieldName]?.PropertyGetter(domainObject); - if (list != null) + if (anyListFieldMap.TryGetValue(anyListFieldName, out var fieldName)) { - foreach (var propertyElement in list) + var list = fieldName.PropertyGetter(domainObject); + if (list != null) { - newProperty.Add( - OpenApiAnyConverter.GetSpecificOpenApiAny( - propertyElement, - anyListFieldMap[anyListFieldName].SchemaGetter(domainObject))); + foreach (var propertyElement in list) + { + newProperty.Add( + OpenApiAnyConverter.GetSpecificOpenApiAny( + propertyElement, + anyListFieldMap[anyListFieldName].SchemaGetter(domainObject))); + } } } From 201e90e9ce887f8547fb3e60a3e2aa5258e52d68 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 31 Aug 2023 23:16:03 +0300 Subject: [PATCH 18/18] Update langVersion to latest --- src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index 5bdf557f2..b7bdc3ac3 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -1,7 +1,7 @@  netstandard2.0 - 9.0 + latest true http://go.microsoft.com/fwlink/?LinkID=288890 https://github.com/Microsoft/OpenAPI.NET