From c84871b235f3b2365a8d3b8ebfd2ecc744d07a14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Jan 2024 21:34:37 +0000 Subject: [PATCH 01/31] Bump Verify.Xunit from 22.11.5 to 23.0.0 Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 22.11.5 to 23.0.0. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/22.11.5...23.0.0) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 8fce973d7..02ce8909d 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -15,7 +15,7 @@ - + From a64342b78f22c8f1fcd93b59d61b73a1bb33741f Mon Sep 17 00:00:00 2001 From: "Andrew Omondi (from Dev Box)" Date: Thu, 18 Jan 2024 10:16:32 +0300 Subject: [PATCH 02/31] Fixes errors/warnings on upgrade --- test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs | 1 - .../Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs | 1 - test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs | 1 - test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs | 1 - .../Writers/OpenApiWriterAnyExtensionsTests.cs | 1 - 13 files changed, 13 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs index 6b6a8d734..ad26b8288 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiCallbackTests.cs @@ -13,7 +13,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiCallbackTests { public static OpenApiCallback AdvancedCallback = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs index ea0b98956..7a7f883f6 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs @@ -17,7 +17,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiDocumentTests { public static OpenApiComponents TopLevelReferencingComponents = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs index 14561debd..a847016d0 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiExampleTests.cs @@ -15,7 +15,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiExampleTests { public static OpenApiExample AdvancedExample = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs index b54c2457e..85316cf3b 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiHeaderTests.cs @@ -12,7 +12,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiHeaderTests { public static OpenApiHeader AdvancedHeader = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs index 2f4a532a7..f1a67c330 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiLinkTests.cs @@ -14,7 +14,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiLinkTests { public static readonly OpenApiLink AdvancedLink = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs index a49f415e3..b26287803 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs @@ -16,7 +16,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiParameterTests { public static OpenApiParameter BasicParameter = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs index 08ef42fe7..519c26607 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiRequestBodyTests.cs @@ -12,7 +12,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiRequestBodyTests { public static OpenApiRequestBody AdvancedRequestBody = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs index ea3a6ee29..fdd1207a6 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiResponseTests.cs @@ -17,7 +17,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiResponseTests { public static OpenApiResponse BasicResponse = new(); diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index c4a9ddd53..5d8320c62 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -20,7 +20,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiSchemaTests { public static OpenApiSchema BasicSchema = new(); diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs index 5cc4a19cb..5df97e135 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSecuritySchemeTests.cs @@ -15,7 +15,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiSecuritySchemeTests { public static OpenApiSecurityScheme ApiKeySecurityScheme = new() diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs index fa6690c94..875eb960c 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs @@ -16,7 +16,6 @@ namespace Microsoft.OpenApi.Tests.Models { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiTagTests { public static OpenApiTag BasicTag = new(); diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs index 94164c3cd..511c6c5bd 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiUrlTreeNodeTests.cs @@ -12,7 +12,6 @@ namespace Microsoft.OpenApi.Tests.Services { - [UsesVerify] public class OpenApiUrlTreeNodeTests { private OpenApiDocument OpenApiDocumentSample_1 => new() diff --git a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs index 1e632572e..686e3a08a 100644 --- a/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs +++ b/test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterAnyExtensionsTests.cs @@ -16,7 +16,6 @@ namespace Microsoft.OpenApi.Tests.Writers { [Collection("DefaultSettings")] - [UsesVerify] public class OpenApiWriterAnyExtensionsTests { static bool[] shouldProduceTerseOutputValues = new[] { true, false }; From aa97082ec94bc65f7de2a3793876f4d4c454af75 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Mon, 22 Jan 2024 14:23:45 -0500 Subject: [PATCH 03/31] - adds permissions to workflows --- .github/workflows/ci-cd.yml | 3 +++ .github/workflows/sonarcloud.yml | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 95f001e1f..bf1aa6a33 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -2,6 +2,9 @@ name: CI/CD Pipeline on: [push, pull_request, workflow_dispatch] +permissions: + contents: write + jobs: ci: name: Continuous Integration diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 5f12a604b..021e0577b 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -9,6 +9,11 @@ on: types: [opened, synchronize, reopened] paths-ignore: ['.vscode/**'] + +permissions: + contents: read + pull-requests: read + env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 39e732e7c03bf43e168fb627566b2b1317bbf768 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jan 2024 21:14:08 +0000 Subject: [PATCH 04/31] Bump actions/cache from 3 to 4 Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/sonarcloud.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 5f12a604b..4a409185d 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -46,14 +46,14 @@ jobs: with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache SonarCloud scanner id: cache-sonar-scanner - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ./.sonar/scanner key: ${{ runner.os }}-sonar-scanner From b004ba6a6dfaeaf35df819d513911ca2182cb1fd Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 14:01:52 +0300 Subject: [PATCH 05/31] Preserve examples in v2 files and write them out as extensions --- .../Models/OpenApiParameter.cs | 16 +++++++++++- .../Models/OpenApiRequestBody.cs | 8 +++--- .../Models/OpenApiResponse.cs | 23 ++++++++++++++++- .../Writers/IOpenApiWriter.cs | 12 ++++++++- .../Writers/OpenApiWriterBase.cs | 25 ++++++++++++++++++- 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 9d1651ad8..2ad433d2f 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.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; @@ -394,6 +394,20 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) } } + //examples + if (Examples != null && Examples.Any()) + { + writer.WritePropertyName("x-examples"); + writer.WriteStartObject(); + + foreach (var example in Examples) + { + writer.WritePropertyName(example.Key); + writer.WriteV2Examples(writer, example.Value, OpenApiSpecVersion.OpenApi2_0); + } + writer.WriteEndObject(); + } + // extensions writer.WriteExtensions(extensionsClone, OpenApiSpecVersion.OpenApi2_0); diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs index ff1a70b92..ce0046336 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.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; @@ -152,7 +152,8 @@ internal OpenApiBodyParameter ConvertToBodyParameter() // V2 spec actually allows the body to have custom name. // To allow round-tripping we use an extension to hold the name Name = "body", - Schema = Content.Values.FirstOrDefault()?.Schema ?? new OpenApiSchema(), + Schema = Content.Values.FirstOrDefault()?.Schema ?? new JsonSchemaBuilder().Build(), + Examples = Content.Values.FirstOrDefault()?.Examples, Required = Required, Extensions = Extensions.ToDictionary(static k => k.Key, static v => v.Value) // Clone extensions so we can remove the x-bodyName extensions from the output V2 model. }; @@ -184,7 +185,8 @@ internal IEnumerable ConvertToFormDataParameters() Description = property.Value.Description, Name = property.Key, Schema = property.Value, - Required = Content.First().Value.Schema.Required.Contains(property.Key) + Examples = Content.Values.FirstOrDefault()?.Examples, + Required = Content.First().Value.Schema.GetRequired()?.Contains(property.Key) ?? false }; } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index 320ecc484..f93c323f4 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.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; @@ -201,6 +201,27 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) writer.WriteEndObject(); } + if (Content.Values.Any(m => m.Examples != null && m.Examples.Any())) + { + writer.WritePropertyName("x-examples"); + writer.WriteStartObject(); + + foreach (var mediaTypePair in Content) + { + var examples = mediaTypePair.Value.Examples; + if (examples != null && examples.Any()) + { + foreach (var example in examples) + { + writer.WritePropertyName(example.Key); + writer.WriteV2Examples(writer, example.Value, OpenApiSpecVersion.OpenApi2_0); + } + } + } + + writer.WriteEndObject(); + } + writer.WriteExtensions(mediatype.Value.Extensions, OpenApiSpecVersion.OpenApi2_0); foreach (var key in mediatype.Value.Extensions.Keys) diff --git a/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs b/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs index 9ea04b400..d40643bfc 100644 --- a/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs +++ b/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs @@ -1,6 +1,8 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using Microsoft.OpenApi.Models; + namespace Microsoft.OpenApi.Writers { /// @@ -72,5 +74,13 @@ public interface IOpenApiWriter /// Flush the writer. /// void Flush(); + + /// + /// Writes out existing examples in a mediatype object + /// + /// + /// + /// + void WriteV2Examples(IOpenApiWriter writer, OpenApiExample example, OpenApiSpecVersion version); } } diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs index 5254288f3..06aa469b9 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.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; @@ -405,5 +405,28 @@ protected void VerifyCanWritePropertyName(string name) string.Format(SRResource.ObjectScopeNeededForPropertyNameWriting, name)); } } + + /// + public void WriteV2Examples(IOpenApiWriter writer, OpenApiExample example, OpenApiSpecVersion version) + { + writer.WriteStartObject(); + + // summary + writer.WriteProperty(OpenApiConstants.Summary, example.Summary); + + // description + writer.WriteProperty(OpenApiConstants.Description, example.Description); + + // value + writer.WriteOptionalObject(OpenApiConstants.Value, example.Value, (w, v) => w.WriteAny(v)); + + // externalValue + writer.WriteProperty(OpenApiConstants.ExternalValue, example.ExternalValue); + + // extensions + writer.WriteExtensions(example.Extensions, version); + + writer.WriteEndObject(); + } } } From ef2b99dfcdc2f76acabb138494dbebaf027dae56 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 14:12:00 +0300 Subject: [PATCH 06/31] Update tests and public API interface --- ...V2JsonWorksAsync_produceTerseOutput=False.verified.txt | 8 +++++++- ...sV2JsonWorksAsync_produceTerseOutput=True.verified.txt | 2 +- .../Models/OpenApiParameterTests.cs | 8 +++++++- .../PublicApi/PublicApi.approved.txt | 2 ++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 0542c58ce..744f8451c 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -3,5 +3,11 @@ "name": "name1", "description": "description1", "required": true, - "type": "string" + "type": "string", + "x-examples": { + "test": { + "summary": "summary3", + "description": "description3" + } + } } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index b80b263d3..26b158865 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaTypeObjectAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"in":"header","name":"name1","description":"description1","required":true,"type":"string"} \ No newline at end of file +{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs index b26287803..2f02ff7dd 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.cs @@ -272,7 +272,13 @@ public void SerializeAdvancedParameterAsV2JsonWorks() "name": "name1", "description": "description1", "required": true, - "format": "double" + "format": "double", + "x-examples": { + "test": { + "summary": "summary3", + "description": "description3" + } + } } """; diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index a5309e46d..36a83b1ed 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1486,6 +1486,7 @@ namespace Microsoft.OpenApi.Writers void WriteRaw(string value); void WriteStartArray(); void WriteStartObject(); + void WriteV2Examples(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.Models.OpenApiExample example, Microsoft.OpenApi.OpenApiSpecVersion version); void WriteValue(bool value); void WriteValue(decimal value); void WriteValue(int value); @@ -1547,6 +1548,7 @@ namespace Microsoft.OpenApi.Writers public abstract void WriteRaw(string value); public abstract void WriteStartArray(); public abstract void WriteStartObject(); + public void WriteV2Examples(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.Models.OpenApiExample example, Microsoft.OpenApi.OpenApiSpecVersion version) { } public virtual void WriteValue(bool value) { } public virtual void WriteValue(System.DateTime value) { } public virtual void WriteValue(System.DateTimeOffset value) { } From cfa49e9a24bce679b722a7ef22566d449b393778 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Wed, 10 Jan 2024 11:35:20 +0300 Subject: [PATCH 07/31] Add examples constant to temp storage keys --- src/Microsoft.OpenApi.Readers/V2/TempStorageKeys.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.OpenApi.Readers/V2/TempStorageKeys.cs b/src/Microsoft.OpenApi.Readers/V2/TempStorageKeys.cs index c7b96f6ce..176af8a1e 100644 --- a/src/Microsoft.OpenApi.Readers/V2/TempStorageKeys.cs +++ b/src/Microsoft.OpenApi.Readers/V2/TempStorageKeys.cs @@ -17,5 +17,6 @@ internal static class TempStorageKeys public const string GlobalConsumes = "globalConsumes"; public const string GlobalProduces = "globalProduces"; public const string ParameterIsBodyOrFormData = "parameterIsBodyOrFormData"; + public const string Examples = "examples"; } } From eda97ad2a4bfcbe5b89b5219c67902c5f57e221f Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 14:14:54 +0300 Subject: [PATCH 08/31] Load "x-examples" as Examples; store and retrieve from temp storage and append it to the media type object --- .../V2/OpenApiResponseDeserializer.cs | 55 ++++++++++++++++--- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 2fef353ea..8ec4c699d 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.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; @@ -28,6 +28,10 @@ internal static partial class OpenApiV2Deserializer "examples", LoadExamples }, + { + "x-examples", + LoadExamplesExtension + }, { "schema", (o, n) => n.Context.SetTempStorage(TempStorageKeys.ResponseSchema, LoadSchema(n), o) @@ -37,7 +41,7 @@ internal static partial class OpenApiV2Deserializer private static readonly PatternFieldMap _responsePatternFields = new() { - {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} + {s => s.StartsWith("x-") && !s.Equals("x-examples"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} }; private static readonly AnyFieldMap _mediaTypeAnyFields = @@ -69,6 +73,7 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P ?? context.DefaultContentType ?? new List { "application/octet-stream" }; var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); + var examples = context.GetFromTempStorage>(TempStorageKeys.Examples, response); foreach (var produce in produces) { @@ -84,7 +89,8 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P { var mediaType = new OpenApiMediaType { - Schema = schema + Schema = schema, + Examples = examples }; response.Content.Add(produce, mediaType); @@ -92,12 +98,49 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P } context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response); + context.SetTempStorage(TempStorageKeys.Examples, null, response); context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); } + private static void LoadExamplesExtension(OpenApiResponse response, ParseNode node) + { + var mapNode = node.CheckMapNode("x-examples"); + var examples = new Dictionary(); + + foreach (var examplesNode in mapNode) + { + // Load the media type node as an OpenApiExample object + var example = new OpenApiExample(); + var exampleNode = examplesNode.Value.CheckMapNode(examplesNode.Name); + foreach (var valueNode in exampleNode) + { + switch (valueNode.Name) + { + case "summary": + example.Summary = valueNode.Value.GetScalarValue(); + break; + case "description": + example.Description = valueNode.Value.GetScalarValue(); + break; + case "value": + example.Value = valueNode.Value.CreateAny(); + break; + case "externalValue": + example.ExternalValue = valueNode.Value.GetScalarValue(); + break; + } + } + + examples.Add(examplesNode.Name, example); + } + + node.Context.SetTempStorage(TempStorageKeys.Examples, examples, response); + } + private static void LoadExamples(OpenApiResponse response, ParseNode node) { var mapNode = node.CheckMapNode("examples"); + foreach (var mediaTypeNode in mapNode) { LoadExample(response, mediaTypeNode.Name, mediaTypeNode.Value); @@ -108,10 +151,7 @@ private static void LoadExample(OpenApiResponse response, string mediaType, Pars { var exampleNode = node.CreateAny(); - if (response.Content == null) - { - response.Content = new Dictionary(); - } + response.Content ??= new Dictionary(); OpenApiMediaType mediaTypeObject; if (response.Content.TryGetValue(mediaType, out var value)) @@ -141,6 +181,7 @@ public static OpenApiResponse LoadResponse(ParseNode node) } var response = new OpenApiResponse(); + foreach (var property in mapNode) { property.ParseField(response, _responseFixedFields, _responsePatternFields); From f6001479381ffd69a855eff75e4a1c05e30e3097 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 14:26:57 +0300 Subject: [PATCH 09/31] Default to an empty collection if examples is null --- .../V2/OpenApiResponseDeserializer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 8ec4c699d..7d482aec7 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -73,7 +73,8 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P ?? context.DefaultContentType ?? new List { "application/octet-stream" }; var schema = context.GetFromTempStorage(TempStorageKeys.ResponseSchema, response); - var examples = context.GetFromTempStorage>(TempStorageKeys.Examples, response); + var examples = context.GetFromTempStorage>(TempStorageKeys.Examples, response) + ?? new Dictionary(); foreach (var produce in produces) { From 61d50b5bfdd6323cd4780c64ee55b5d94499094a Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Wed, 10 Jan 2024 13:47:47 +0300 Subject: [PATCH 10/31] Add a reference to the OpenApi.Tests project to access the string extension method --- .../Microsoft.OpenApi.Readers.Tests.csproj | 1 + 1 file changed, 1 insertion(+) 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 7a89c0044..0bf7fcc3e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -28,6 +28,7 @@ + \ No newline at end of file From e41b927377a51062808f1610c97a0d2036c3c510 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Wed, 10 Jan 2024 13:48:56 +0300 Subject: [PATCH 11/31] Add test to validate that a V2 doc with x-examples gets mapped to MediaType Examples object when upcasting to V3 --- .../V2Tests/OpenApiOperationTests.cs | 52 +++++++++++++++++++ .../opWithResponseExamplesExtension.yaml | 28 ++++++++++ 2 files changed, 80 insertions(+) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithResponseExamplesExtension.yaml diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index 326c16969..c2b3c69ff 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -11,6 +11,7 @@ using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers.ParseNodes; using Microsoft.OpenApi.Readers.V2; +using Microsoft.OpenApi.Tests; using Xunit; namespace Microsoft.OpenApi.Readers.Tests.V2Tests @@ -434,5 +435,56 @@ public void ParseOperationWithBodyAndEmptyConsumesSetsRequestBodySchemaIfExists( // Assert operation.Should().BeEquivalentTo(_operationWithBody); } + + [Fact] + public void ParseV2ResponseWithExamplesExtensionWorks() + { + // Arrange + MapNode node; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "opWithResponseExamplesExtension.yaml"))) + { + node = TestHelper.CreateYamlMapNode(stream); + } + + // Act + var operation = OpenApiV2Deserializer.LoadOperation(node); + var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0); + + // Assert + var expected = @"summary: Get all pets +responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + examples: + example1: + summary: Example - List of Pets + value: + - name: Buddy + age: 2 + - name: Whiskers + age: 1 + example2: + summary: Example - Playful Cat + value: + name: Whiskers + age: 1"; + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + //Assert.Equal(expected, actual); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithResponseExamplesExtension.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithResponseExamplesExtension.yaml new file mode 100644 index 000000000..5dcc89d97 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithResponseExamplesExtension.yaml @@ -0,0 +1,28 @@ +summary: Get all pets +produces: +- application/json +responses: + '200': + description: Successful response + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + x-examples: + example1: + summary: Example - List of Pets + value: + - name: "Buddy" + age: 2 + - name: "Whiskers" + age: 1 + example2: + summary: Example - Playful Cat + value: + name: "Whiskers" + age: 1 \ No newline at end of file From eb9ba944c12dcce1752cc59ddead9d5a09510d33 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 11 Jan 2024 12:12:42 +0300 Subject: [PATCH 12/31] Add unit tests --- .../V2Tests/OpenApiOperationTests.cs | 52 ++++++++++++++++++- .../v3OperationWithResponseExamples.yaml | 28 ++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithResponseExamples.yaml diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index c2b3c69ff..fbf157b4c 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -11,6 +11,7 @@ using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers.ParseNodes; using Microsoft.OpenApi.Readers.V2; +using Microsoft.OpenApi.Readers.V3; using Microsoft.OpenApi.Tests; using Xunit; @@ -484,7 +485,56 @@ public void ParseV2ResponseWithExamplesExtensionWorks() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); actual.Should().Be(expected); - //Assert.Equal(expected, actual); + } + + [Fact] + public void LoadV3ExamplesInResponseAsExtensionsWorks() + { + // Arrange + MapNode node; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "v3OperationWithResponseExamples.yaml"))) + { + node = TestHelper.CreateYamlMapNode(stream); + } + + // Act + var operation = OpenApiV3Deserializer.LoadOperation(node); + var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi2_0); + + // Assert + var expected = @"summary: Get all pets +produces: + - application/json +responses: + '200': + description: Successful response + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + x-examples: + example1: + summary: Example - List of Pets + value: + - name: Buddy + age: 2 + - name: Whiskers + age: 1 + example2: + summary: Example - Playful Cat + value: + name: Whiskers + age: 1"; + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithResponseExamples.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithResponseExamples.yaml new file mode 100644 index 000000000..c3b124685 --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithResponseExamples.yaml @@ -0,0 +1,28 @@ +summary: Get all pets +responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + examples: + example1: + summary: Example - List of Pets + value: + - name: "Buddy" + age: 2 + - name: "Whiskers" + age: 1 + example2: + summary: Example - Playful Cat + value: + name: "Whiskers" + age: 1 \ No newline at end of file From af5568ca15b84b60bdc87a1b055279eba7908a45 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 14:31:07 +0300 Subject: [PATCH 13/31] Fix CodeQL warnings --- src/Microsoft.OpenApi/Models/OpenApiDocument.cs | 2 +- src/Microsoft.OpenApi/Models/OpenApiParameter.cs | 9 +-------- src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs | 11 ++--------- src/Microsoft.OpenApi/Models/OpenApiResponse.cs | 9 +-------- 4 files changed, 5 insertions(+), 26 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 578c1e34e..8d4526a20 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.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; diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 2ad433d2f..2185e3aa0 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -204,14 +204,7 @@ public void SerializeAsV3(IOpenApiWriter writer) /// OpenApiParameter public OpenApiParameter GetEffective(OpenApiDocument doc) { - if (this.Reference != null) - { - return doc.ResolveReferenceTo(this.Reference); - } - else - { - return this; - } + return Reference != null ? doc.ResolveReferenceTo(Reference) : this; } /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs index ce0046336..ea3c3dcb1 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs @@ -96,14 +96,7 @@ public void SerializeAsV3(IOpenApiWriter writer) /// OpenApiRequestBody public OpenApiRequestBody GetEffective(OpenApiDocument doc) { - if (this.Reference != null) - { - return doc.ResolveReferenceTo(this.Reference); - } - else - { - return this; - } + return Reference != null ? doc.ResolveReferenceTo(Reference) : this; } /// @@ -157,7 +150,7 @@ internal OpenApiBodyParameter ConvertToBodyParameter() Required = Required, Extensions = Extensions.ToDictionary(static k => k.Key, static v => v.Value) // Clone extensions so we can remove the x-bodyName extensions from the output V2 model. }; - if (bodyParameter.Extensions.ContainsKey(OpenApiConstants.BodyName)) + if (bodyParameter.Extensions.TryGetValue(OpenApiConstants.BodyName, out var bodyParameterName)) { bodyParameter.Name = (Extensions[OpenApiConstants.BodyName] as OpenApiString)?.Value ?? "body"; bodyParameter.Extensions.Remove(OpenApiConstants.BodyName); diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index f93c323f4..ec4058100 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs @@ -101,14 +101,7 @@ public void SerializeAsV3(IOpenApiWriter writer) /// OpenApiResponse public OpenApiResponse GetEffective(OpenApiDocument doc) { - if (this.Reference != null) - { - return doc.ResolveReferenceTo(this.Reference); - } - else - { - return this; - } + return Reference != null ? doc.ResolveReferenceTo(Reference) : this; } /// From dc7cef8a3f529a920f7bbc54517f2222fa5036d2 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 14:33:02 +0300 Subject: [PATCH 14/31] Filter sequence using "Where" --- src/Microsoft.OpenApi/Models/OpenApiResponse.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index ec4058100..ab9428841 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs @@ -199,17 +199,12 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) writer.WritePropertyName("x-examples"); writer.WriteStartObject(); - foreach (var mediaTypePair in Content) + foreach (var example in Content + .Where(mediaTypePair => mediaTypePair.Value.Examples != null && mediaTypePair.Value.Examples.Any()) + .SelectMany(mediaTypePair => mediaTypePair.Value.Examples)) { - var examples = mediaTypePair.Value.Examples; - if (examples != null && examples.Any()) - { - foreach (var example in examples) - { - writer.WritePropertyName(example.Key); - writer.WriteV2Examples(writer, example.Value, OpenApiSpecVersion.OpenApi2_0); - } - } + writer.WritePropertyName(example.Key); + writer.WriteV2Examples(writer, example.Value, OpenApiSpecVersion.OpenApi2_0); } writer.WriteEndObject(); From 840c591d11b242a886dca689198a8cf5b431a615 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 11 Jan 2024 17:34:44 +0300 Subject: [PATCH 15/31] Update src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs Co-authored-by: Vincent Biret --- 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 7d482aec7..3b84a1a19 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -41,7 +41,7 @@ internal static partial class OpenApiV2Deserializer private static readonly PatternFieldMap _responsePatternFields = new() { - {s => s.StartsWith("x-") && !s.Equals("x-examples"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} + {s => s.StartsWith("x-") && !s.Equals("x-examples", StringComparison.OrdinalIgnoreCase), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} }; private static readonly AnyFieldMap _mediaTypeAnyFields = From 9d226dfae8d84b03704dbe01e0296bc4b70bd484 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Thu, 11 Jan 2024 09:45:23 -0500 Subject: [PATCH 16/31] - adds missing using --- src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 3b84a1a19..1eef8c559 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; From 3f11c6175107d27a3ce279f29b4866c3f22a1665 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 14:36:19 +0300 Subject: [PATCH 17/31] Add normalization; use constant for Examples extension --- .../V2/OpenApiResponseDeserializer.cs | 6 +++--- src/Microsoft.OpenApi/Models/OpenApiConstants.cs | 5 +++++ src/Microsoft.OpenApi/Models/OpenApiParameter.cs | 2 +- src/Microsoft.OpenApi/Models/OpenApiResponse.cs | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 1eef8c559..b1fc26f85 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -42,7 +42,7 @@ internal static partial class OpenApiV2Deserializer private static readonly PatternFieldMap _responsePatternFields = new() { - {s => s.StartsWith("x-") && !s.Equals("x-examples", StringComparison.OrdinalIgnoreCase), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} + {s => s.StartsWith("x-") && !s.Equals(OpenApiConstants.ExamplesExtension), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} }; private static readonly AnyFieldMap _mediaTypeAnyFields = @@ -106,7 +106,7 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P private static void LoadExamplesExtension(OpenApiResponse response, ParseNode node) { - var mapNode = node.CheckMapNode("x-examples"); + var mapNode = node.CheckMapNode(OpenApiConstants.ExamplesExtension); var examples = new Dictionary(); foreach (var examplesNode in mapNode) @@ -116,7 +116,7 @@ private static void LoadExamplesExtension(OpenApiResponse response, ParseNode no var exampleNode = examplesNode.Value.CheckMapNode(examplesNode.Name); foreach (var valueNode in exampleNode) { - switch (valueNode.Name) + switch (valueNode.Name.ToLowerInvariant()) { case "summary": example.Summary = valueNode.Value.GetScalarValue(); diff --git a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs index 40867d7e0..73de74228 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs @@ -565,6 +565,11 @@ public static class OpenApiConstants /// public const string BodyName = "x-bodyName"; + /// + /// Field: Examples Extension + /// + public const string ExamplesExtension = "x-examples"; + /// /// Field: version3_0_0 /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 2185e3aa0..57c4b3649 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -390,7 +390,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) //examples if (Examples != null && Examples.Any()) { - writer.WritePropertyName("x-examples"); + writer.WritePropertyName(OpenApiConstants.ExamplesExtension); writer.WriteStartObject(); foreach (var example in Examples) diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index ab9428841..369f09761 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs @@ -196,7 +196,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) if (Content.Values.Any(m => m.Examples != null && m.Examples.Any())) { - writer.WritePropertyName("x-examples"); + writer.WritePropertyName(OpenApiConstants.ExamplesExtension); writer.WriteStartObject(); foreach (var example in Content From 5d69edae1dbd6d261db1146ee89092e989bdddf6 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 11 Jan 2024 17:55:51 +0300 Subject: [PATCH 18/31] Update API interface --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 36a83b1ed..256832e42 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -480,6 +480,7 @@ namespace Microsoft.OpenApi.Models public const string Enum = "enum"; public const string Example = "example"; public const string Examples = "examples"; + public const string ExamplesExtension = "x-examples"; public const string ExclusiveMaximum = "exclusiveMaximum"; public const string ExclusiveMinimum = "exclusiveMinimum"; public const string Explode = "explode"; From 6cd8a82503449f77c4b054ca122e13df33fddf6d Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Tue, 23 Jan 2024 15:25:01 +0300 Subject: [PATCH 19/31] Code cleanup and refactoring --- .../V2/OpenApiResponseDeserializer.cs | 6 +++--- src/Microsoft.OpenApi/Models/OpenApiParameter.cs | 3 ++- src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs | 6 +++--- src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs | 3 ++- ...V2JsonWorksAsync_produceTerseOutput=False.verified.txt | 8 +++++++- ...sV2JsonWorksAsync_produceTerseOutput=True.verified.txt | 2 +- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index b1fc26f85..b624f1b3a 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.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; @@ -42,7 +42,7 @@ internal static partial class OpenApiV2Deserializer private static readonly PatternFieldMap _responsePatternFields = new() { - {s => s.StartsWith("x-") && !s.Equals(OpenApiConstants.ExamplesExtension), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} + {s => s.StartsWith("x-") && !s.Equals(OpenApiConstants.ExamplesExtension, StringComparison.OrdinalIgnoreCase), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} }; private static readonly AnyFieldMap _mediaTypeAnyFields = @@ -125,7 +125,7 @@ private static void LoadExamplesExtension(OpenApiResponse response, ParseNode no example.Description = valueNode.Value.GetScalarValue(); break; case "value": - example.Value = valueNode.Value.CreateAny(); + example.Value = OpenApiAnyConverter.GetSpecificOpenApiAny(valueNode.Value.CreateAny()); break; case "externalValue": example.ExternalValue = valueNode.Value.GetScalarValue(); diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 57c4b3649..6608025a9 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -1,8 +1,9 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs index ea3c3dcb1..7fa58be5f 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.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; @@ -145,7 +145,7 @@ internal OpenApiBodyParameter ConvertToBodyParameter() // V2 spec actually allows the body to have custom name. // To allow round-tripping we use an extension to hold the name Name = "body", - Schema = Content.Values.FirstOrDefault()?.Schema ?? new JsonSchemaBuilder().Build(), + Schema = Content.Values.FirstOrDefault()?.Schema ?? new OpenApiSchema(), Examples = Content.Values.FirstOrDefault()?.Examples, Required = Required, Extensions = Extensions.ToDictionary(static k => k.Key, static v => v.Value) // Clone extensions so we can remove the x-bodyName extensions from the output V2 model. @@ -179,7 +179,7 @@ internal IEnumerable ConvertToFormDataParameters() Name = property.Key, Schema = property.Value, Examples = Content.Values.FirstOrDefault()?.Examples, - Required = Content.First().Value.Schema.GetRequired()?.Contains(property.Key) ?? false + Required = Content.First().Value.Schema.Required?.Contains(property.Key) ?? false }; } } diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs index 06aa469b9..1edab6392 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs @@ -1,10 +1,11 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; using System.Collections.Generic; using System.IO; using Microsoft.OpenApi.Exceptions; +using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Writers diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt index 0542c58ce..744f8451c 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=False.verified.txt @@ -3,5 +3,11 @@ "name": "name1", "description": "description1", "required": true, - "type": "string" + "type": "string", + "x-examples": { + "test": { + "summary": "summary3", + "description": "description3" + } + } } \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt index b80b263d3..26b158865 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiParameterTests.SerializeParameterWithSchemaReferenceAsV2JsonWorksAsync_produceTerseOutput=True.verified.txt @@ -1 +1 @@ -{"in":"header","name":"name1","description":"description1","required":true,"type":"string"} \ No newline at end of file +{"in":"header","name":"name1","description":"description1","required":true,"type":"string","x-examples":{"test":{"summary":"summary3","description":"description3"}}} \ No newline at end of file From d4d70959c9f33da2f0cc0cd9b27bf431bd0adb51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:33:50 +0000 Subject: [PATCH 20/31] Bump Microsoft.OpenApi.OData from 1.6.0-preview.2 to 1.6.0-preview.3 Bumps [Microsoft.OpenApi.OData](https://github.com/Microsoft/OpenAPI.NET.OData) from 1.6.0-preview.2 to 1.6.0-preview.3. - [Release notes](https://github.com/Microsoft/OpenAPI.NET.OData/releases) - [Commits](https://github.com/Microsoft/OpenAPI.NET.OData/commits) --- updated-dependencies: - dependency-name: Microsoft.OpenApi.OData dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 84a64182b..505d50ef8 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -35,7 +35,7 @@ - + From de74419ed9679c0c6ecc4c70a0b0ff140931881b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 21:34:09 +0000 Subject: [PATCH 21/31] Bump Verify.Xunit from 23.0.0 to 23.0.1 Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 23.0.0 to 23.0.1. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/23.0.0...23.0.1) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 02ce8909d..6f68a8cee 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -15,7 +15,7 @@ - + From 51d7b3041f145713e0adedbbd398c16fb1e98d09 Mon Sep 17 00:00:00 2001 From: Luc Genetier <69138830+LucGenetier@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:29:58 +0100 Subject: [PATCH 22/31] Add ValidationRuleSet.Remove (#1535) * Add ValidationRuleSet.Remove * Updates following Vincent's comments * Updates following Vincent's comments Add ValidationRuleSet.Remove * Update * Apply suggestions from code review --------- Co-authored-by: Vincent Biret Co-authored-by: Eastman Co-authored-by: Darrel Co-authored-by: Maggie Kimani Co-authored-by: Irvine Sunday <40403681+irvinesunday@users.noreply.github.com> Co-authored-by: Vincent Biret --- .../Rules/OpenApiComponentsRules.cs | 2 +- .../Validations/Rules/OpenApiContactRules.cs | 2 +- .../Validations/Rules/OpenApiDocumentRules.cs | 2 +- .../Rules/OpenApiExtensionRules.cs | 2 +- .../Rules/OpenApiExternalDocsRules.cs | 2 +- .../Validations/Rules/OpenApiHeaderRules.cs | 2 +- .../Validations/Rules/OpenApiInfoRules.cs | 2 +- .../Validations/Rules/OpenApiLicenseRules.cs | 2 +- .../Rules/OpenApiMediaTypeRules.cs | 2 +- .../Rules/OpenApiOAuthFlowRules.cs | 2 +- .../Rules/OpenApiParameterRules.cs | 8 ++-- .../Validations/Rules/OpenApiPathsRules.cs | 4 +- .../Validations/Rules/OpenApiResponseRules.cs | 2 +- .../Rules/OpenApiResponsesRules.cs | 6 +-- .../Validations/Rules/OpenApiSchemaRules.cs | 4 +- .../Validations/Rules/OpenApiServerRules.cs | 2 +- .../Validations/Rules/OpenApiTagRules.cs | 2 +- .../Validations/ValidationRule.cs | 28 +++++++++++-- .../Validations/ValidationRuleSet.cs | 24 +++++++++++ .../PublicApi/PublicApi.approved.txt | 6 ++- .../Services/OpenApiValidatorTests.cs | 41 ++++++++++++++++++- .../OpenApiReferenceValidationTests.cs | 2 +- 22 files changed, 119 insertions(+), 30 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs index f2f3a649c..93eba5c71 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs @@ -24,7 +24,7 @@ public static class OpenApiComponentsRules /// that MUST use keys that match the regular expression: ^[a-zA-Z0-9\.\-_]+$. /// public static ValidationRule KeyMustBeRegularExpression => - new( + new(nameof(KeyMustBeRegularExpression), (context, components) => { ValidateKeys(context, components.Schemas?.Keys, "schemas"); diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs index cfa8d9927..e31dc1e07 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs @@ -17,7 +17,7 @@ public static class OpenApiContactRules /// Email field MUST be email address. /// public static ValidationRule EmailMustBeEmailFormat => - new( + new(nameof(EmailMustBeEmailFormat), (context, item) => { context.Enter("email"); diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs index be0dc1538..f38e2530f 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs @@ -17,7 +17,7 @@ public static class OpenApiDocumentRules /// The Info field is required. /// public static ValidationRule OpenApiDocumentFieldIsMissing => - new( + new(nameof(OpenApiDocumentFieldIsMissing), (context, item) => { // info diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs index 5d124e8de..890be82d7 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs @@ -17,7 +17,7 @@ public static class OpenApiExtensibleRules /// Extension name MUST start with "x-". /// public static ValidationRule ExtensionNameMustStartWithXDash => - new( + new(nameof(ExtensionNameMustStartWithXDash), (context, item) => { context.Enter("extensions"); diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs index ff4fde4a2..1754dbee2 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs @@ -17,7 +17,7 @@ public static class OpenApiExternalDocsRules /// Validate the field is required. /// public static ValidationRule UrlIsRequired => - new( + new(nameof(UrlIsRequired), (context, item) => { // url diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs index c446a7b56..194e426cc 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiHeaderRules.cs @@ -16,7 +16,7 @@ public static class OpenApiHeaderRules /// Validate the data matches with the given data type. /// public static ValidationRule HeaderMismatchedDataType => - new( + new(nameof(HeaderMismatchedDataType), (context, header) => { // example diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs index 88b534c02..337bf8687 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs @@ -17,7 +17,7 @@ public static class OpenApiInfoRules /// Validate the field is required. /// public static ValidationRule InfoRequiredFields => - new( + new(nameof(InfoRequiredFields), (context, item) => { // title diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs index edbf19bf5..08f67d209 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs @@ -17,7 +17,7 @@ public static class OpenApiLicenseRules /// REQUIRED. /// public static ValidationRule LicenseRequiredFields => - new( + new(nameof(LicenseRequiredFields), (context, license) => { context.Enter("name"); diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs index 6f7b65a6c..7ac09cbbf 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiMediaTypeRules.cs @@ -24,7 +24,7 @@ public static class OpenApiMediaTypeRules /// Validate the data matches with the given data type. /// public static ValidationRule MediaTypeMismatchedDataType => - new( + new(nameof(MediaTypeMismatchedDataType), (context, mediaType) => { // example diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs index de31d933d..c6db49d7c 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs @@ -17,7 +17,7 @@ public static class OpenApiOAuthFlowRules /// Validate the field is required. /// public static ValidationRule OAuthFlowRequiredFields => - new( + new(nameof(OAuthFlowRequiredFields), (context, flow) => { // authorizationUrl diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs index 5c19ce1d9..a1a228134 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiParameterRules.cs @@ -17,7 +17,7 @@ public static class OpenApiParameterRules /// Validate the field is required. /// public static ValidationRule ParameterRequiredFields => - new( + new(nameof(ParameterRequiredFields), (context, item) => { // name @@ -43,7 +43,7 @@ public static class OpenApiParameterRules /// Validate the "required" field is true when "in" is path. /// public static ValidationRule RequiredMustBeTrueWhenInIsPath => - new( + new(nameof(RequiredMustBeTrueWhenInIsPath), (context, item) => { // required @@ -62,7 +62,7 @@ public static class OpenApiParameterRules /// Validate the data matches with the given data type. /// public static ValidationRule ParameterMismatchedDataType => - new( + new(nameof(ParameterMismatchedDataType), (context, parameter) => { // example @@ -100,7 +100,7 @@ public static class OpenApiParameterRules /// Validate that a path parameter should always appear in the path /// public static ValidationRule PathParameterShouldBeInThePath => - new( + new(nameof(PathParameterShouldBeInThePath), (context, parameter) => { if (parameter.In == ParameterLocation.Path && diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs index d248edb3c..9c23f7220 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -18,7 +18,7 @@ public static class OpenApiPathsRules /// A relative path to an individual endpoint. The field name MUST begin with a slash. /// public static ValidationRule PathNameMustBeginWithSlash => - new( + new(nameof(PathNameMustBeginWithSlash), (context, item) => { foreach (var pathName in item.Keys) @@ -39,7 +39,7 @@ public static class OpenApiPathsRules /// A relative path to an individual endpoint. The field name MUST begin with a slash. /// public static ValidationRule PathMustBeUnique => - new ValidationRule( + new ValidationRule(nameof(PathMustBeUnique), (context, item) => { var hashSet = new HashSet(); diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs index 0f725c90e..f30b49ea0 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs @@ -17,7 +17,7 @@ public static class OpenApiResponseRules /// Validate the field is required. /// public static ValidationRule ResponseRequiredFields => - new( + new(nameof(ResponseRequiredFields), (context, response) => { // description diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.cs index 1afe9a388..a2b91dc31 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponsesRules.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.Linq; @@ -17,7 +17,7 @@ public static class OpenApiResponsesRules /// An OpenAPI operation must contain at least one response /// public static ValidationRule ResponsesMustContainAtLeastOneResponse => - new( + new(nameof(ResponsesMustContainAtLeastOneResponse), (context, responses) => { if (!responses.Keys.Any()) @@ -31,7 +31,7 @@ public static class OpenApiResponsesRules /// The response key must either be "default" or an HTTP status code (1xx, 2xx, 3xx, 4xx, 5xx). /// public static ValidationRule ResponsesMustBeIdentifiedByDefaultOrStatusCode => - new( + new(nameof(ResponsesMustBeIdentifiedByDefaultOrStatusCode), (context, responses) => { foreach (var key in responses.Keys) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index a936766c8..411f26fd0 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -17,7 +17,7 @@ public static class OpenApiSchemaRules /// Validate the data matches with the given data type. /// public static ValidationRule SchemaMismatchedDataType => - new( + new(nameof(SchemaMismatchedDataType), (context, schema) => { // default @@ -60,7 +60,7 @@ public static class OpenApiSchemaRules /// Validates Schema Discriminator /// public static ValidationRule ValidateSchemaDiscriminator => - new( + new(nameof(ValidateSchemaDiscriminator), (context, schema) => { // discriminator diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs index 292fd1fd0..dd11a661d 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs @@ -17,7 +17,7 @@ public static class OpenApiServerRules /// Validate the field is required. /// public static ValidationRule ServerRequiredFields => - new( + new(nameof(ServerRequiredFields), (context, server) => { context.Enter("url"); diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs index f28732e1e..cc006f971 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs @@ -17,7 +17,7 @@ public static class OpenApiTagRules /// Validate the field is required. /// public static ValidationRule TagRequiredFields => - new( + new(nameof(TagRequiredFields), (context, tag) => { context.Enter("name"); diff --git a/src/Microsoft.OpenApi/Validations/ValidationRule.cs b/src/Microsoft.OpenApi/Validations/ValidationRule.cs index 48907635d..4f0f00383 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRule.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRule.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. using System; -using System.Globalization; +using Microsoft.OpenApi.Exceptions; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Properties; @@ -18,12 +18,22 @@ public abstract class ValidationRule /// internal abstract Type ElementType { get; } + /// + /// Validation rule Name. + /// + public string Name { get; } + /// /// Validate the object. /// /// The context. /// The object item. internal abstract void Evaluate(IValidationContext context, object item); + + internal ValidationRule(string name) + { + Name = !string.IsNullOrEmpty(name) ? name : throw new ArgumentNullException(nameof(name)); + } } /// @@ -33,14 +43,26 @@ public abstract class ValidationRule public class ValidationRule : ValidationRule where T : IOpenApiElement { private readonly Action _validate; + + /// + /// Initializes a new instance of the class. + /// + /// Action to perform the validation. + [Obsolete("Please use the other constructor and specify a name")] + public ValidationRule(Action validate) + : this (Guid.NewGuid().ToString("D"), validate) + { + } /// /// Initializes a new instance of the class. /// + /// Validation rule name. /// Action to perform the validation. - public ValidationRule(Action validate) + public ValidationRule(string name, Action validate) + : base(name) { - _validate = Utils.CheckArgumentNull(validate); + _validate = Utils.CheckArgumentNull(validate); } internal override Type ElementType diff --git a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs index a5c6fee83..3dd916755 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs @@ -134,6 +134,30 @@ public void Add(ValidationRule rule) item.Add(rule); } + /// + /// Remove a rule by its name from all types it is used by. + /// + /// Name of the rule. + public void Remove(string ruleName) + { + foreach (KeyValuePair> rule in _rules) + { + _rules[rule.Key] = rule.Value.Where(vr => !vr.Name.Equals(ruleName, StringComparison.Ordinal)).ToList(); + } + + // Remove types with no rule + _rules = _rules.Where(r => r.Value.Any()).ToDictionary(r => r.Key, r => r.Value); + } + + /// + /// Remove a rule by element type. + /// + /// Type of the rule. + public void Remove(Type type) + { + _rules.Remove(type); + } + /// /// Get the enumerator. /// diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index a5309e46d..e0abf365a 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1347,7 +1347,7 @@ namespace Microsoft.OpenApi.Validations } public abstract class ValidationRule { - protected ValidationRule() { } + public string Name { get; } } public sealed class ValidationRuleSet : System.Collections.Generic.IEnumerable, System.Collections.IEnumerable { @@ -1358,13 +1358,17 @@ namespace Microsoft.OpenApi.Validations public void Add(Microsoft.OpenApi.Validations.ValidationRule rule) { } public System.Collections.Generic.IList FindRules(System.Type type) { } public System.Collections.Generic.IEnumerator GetEnumerator() { } + public void Remove(string ruleName) { } + public void Remove(System.Type type) { } public static Microsoft.OpenApi.Validations.ValidationRuleSet GetDefaultRuleSet() { } public static Microsoft.OpenApi.Validations.ValidationRuleSet GetEmptyRuleSet() { } } public class ValidationRule : Microsoft.OpenApi.Validations.ValidationRule where T : Microsoft.OpenApi.Interfaces.IOpenApiElement { + [System.Obsolete("Please use the other constructor and specify a name")] public ValidationRule(System.Action validate) { } + public ValidationRule(string name, System.Action validate) { } } } namespace Microsoft.OpenApi.Validations.Rules diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs index 02eba9347..d027f40b8 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using FluentAssertions; +using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; @@ -96,7 +97,7 @@ public void ValidateCustomExtension() var ruleset = ValidationRuleSet.GetDefaultRuleSet(); ruleset.Add( - new ValidationRule( + new ValidationRule("FooExtensionRule", (context, item) => { if (item.Bar == "hey") @@ -133,6 +134,44 @@ public void ValidateCustomExtension() new OpenApiValidatorError("FooExtensionRule", "#/info/x-foo", "Don't say hey") }); } + + [Fact] + public void RemoveRuleByName_Invalid() + { + Assert.Throws(() => new ValidationRule(null, (vc, oaa) => { })); + Assert.Throws(() => new ValidationRule(string.Empty, (vc, oaa) => { })); + } + + [Fact] + public void RemoveRuleByName() + { + var ruleset = ValidationRuleSet.GetDefaultRuleSet(); + int expected = ruleset.Rules.Count - 1; + ruleset.Remove("KeyMustBeRegularExpression"); + + Assert.Equal(expected, ruleset.Rules.Count); + + ruleset.Remove("KeyMustBeRegularExpression"); + ruleset.Remove("UnknownName"); + + Assert.Equal(expected, ruleset.Rules.Count); + } + + [Fact] + public void RemoveRuleByType() + { + var ruleset = ValidationRuleSet.GetDefaultRuleSet(); + int expected = ruleset.Rules.Count - 1; + + ruleset.Remove(typeof(OpenApiComponents)); + + Assert.Equal(expected, ruleset.Rules.Count); + + ruleset.Remove(typeof(OpenApiComponents)); + ruleset.Remove(typeof(int)); + + Assert.Equal(expected, ruleset.Rules.Count); + } } internal class FooExtension : IOpenApiExtension, IOpenApiElement diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs index 4c252716f..11e5edc9e 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiReferenceValidationTests.cs @@ -152,7 +152,7 @@ public void UnresolvedSchemaReferencedShouldNotBeValidated() public class AlwaysFailRule : ValidationRule where T : IOpenApiElement { - public AlwaysFailRule() : base((c, _) => c.CreateError("x", "y")) + public AlwaysFailRule() : base("AlwaysFailRule", (c, _) => c.CreateError("x", "y")) { } } From 7c17a099e276fb90a03f9791b140906966b15d5d Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 25 Jan 2024 13:03:03 +0300 Subject: [PATCH 23/31] Refactor the LoadExtensions method for reuse --- .../V2/OpenApiParameterDeserializer.cs | 15 +++++++++++++-- .../V2/OpenApiResponseDeserializer.cs | 17 ++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs index f74600e66..f9bdda48e 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.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; @@ -95,12 +95,17 @@ internal static partial class OpenApiV2Deserializer "schema", (o, n) => o.Schema = LoadSchema(n) }, + { + "x-examples", + LoadParameterExamplesExtension + }, }; private static readonly PatternFieldMap _parameterPatternFields = new() { - {s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} + {s => s.StartsWith("x-") && !s.Equals(OpenApiConstants.ExamplesExtension, StringComparison.OrdinalIgnoreCase), + (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} }; private static readonly AnyFieldMap _parameterAnyFields = @@ -166,6 +171,12 @@ private static void LoadStyle(OpenApiParameter p, string v) } } + private static void LoadParameterExamplesExtension(OpenApiParameter parameter, ParseNode node) + { + var examples = LoadExamplesExtension(node); + node.Context.SetTempStorage(TempStorageKeys.Examples, examples, parameter); + } + private static OpenApiSchema GetOrCreateSchema(OpenApiParameter p) { if (p.Schema == null) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index b624f1b3a..03691d14b 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs @@ -31,7 +31,7 @@ internal static partial class OpenApiV2Deserializer }, { "x-examples", - LoadExamplesExtension + LoadResponseExamplesExtension }, { "schema", @@ -42,7 +42,8 @@ internal static partial class OpenApiV2Deserializer private static readonly PatternFieldMap _responsePatternFields = new() { - {s => s.StartsWith("x-") && !s.Equals(OpenApiConstants.ExamplesExtension, StringComparison.OrdinalIgnoreCase), (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} + {s => s.StartsWith("x-") && !s.Equals(OpenApiConstants.ExamplesExtension, StringComparison.OrdinalIgnoreCase), + (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} }; private static readonly AnyFieldMap _mediaTypeAnyFields = @@ -104,7 +105,13 @@ private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, P context.SetTempStorage(TempStorageKeys.ResponseProducesSet, true, response); } - private static void LoadExamplesExtension(OpenApiResponse response, ParseNode node) + private static void LoadResponseExamplesExtension(OpenApiResponse response, ParseNode node) + { + var examples = LoadExamplesExtension(node); + node.Context.SetTempStorage(TempStorageKeys.Examples, examples, response); + } + + private static Dictionary LoadExamplesExtension(ParseNode node) { var mapNode = node.CheckMapNode(OpenApiConstants.ExamplesExtension); var examples = new Dictionary(); @@ -131,12 +138,12 @@ private static void LoadExamplesExtension(OpenApiResponse response, ParseNode no example.ExternalValue = valueNode.Value.GetScalarValue(); break; } - } + } examples.Add(examplesNode.Name, example); } - node.Context.SetTempStorage(TempStorageKeys.Examples, examples, response); + return examples; } private static void LoadExamples(OpenApiResponse response, ParseNode node) From 37c097e358e2aff66b5451f57807ac8e8a11fa87 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 25 Jan 2024 13:03:51 +0300 Subject: [PATCH 24/31] Fetch the examples from storage and append them to the resulting body parameter object --- .../V2/OpenApiOperationDeserializer.cs | 4 +++- .../V2/OpenApiParameterDeserializer.cs | 10 +++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs index f47ca1c6c..fa91f913a 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography.X509Certificates; +using System.Xml.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; @@ -194,7 +195,8 @@ internal static OpenApiRequestBody CreateRequestBody( k => k, _ => new OpenApiMediaType { - Schema = bodyParameter.Schema + Schema = bodyParameter.Schema, + Examples = bodyParameter.Examples }), Extensions = bodyParameter.Extensions }; diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs index f9bdda48e..a7d9f93d4 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.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; @@ -261,6 +261,14 @@ public static OpenApiParameter LoadParameter(ParseNode node, bool loadRequestBod node.Context.SetTempStorage("schema", null); } + // load examples from storage and add them to the parameter + var examples = node.Context.GetFromTempStorage>(TempStorageKeys.Examples, parameter); + if (examples != null) + { + parameter.Examples = examples; + node.Context.SetTempStorage("examples", null); + } + var isBodyOrFormData = (bool)node.Context.GetFromTempStorage(TempStorageKeys.ParameterIsBodyOrFormData); if (isBodyOrFormData && !loadRequestBody) { From 73cb87e053aa06042dcc1b5614e2c34dbb5f2847 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 25 Jan 2024 13:04:07 +0300 Subject: [PATCH 25/31] Revert change --- src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs index 7fa58be5f..5e5edc576 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs @@ -150,7 +150,7 @@ internal OpenApiBodyParameter ConvertToBodyParameter() Required = Required, Extensions = Extensions.ToDictionary(static k => k.Key, static v => v.Value) // Clone extensions so we can remove the x-bodyName extensions from the output V2 model. }; - if (bodyParameter.Extensions.TryGetValue(OpenApiConstants.BodyName, out var bodyParameterName)) + if (bodyParameter.Extensions.ContainsKey(OpenApiConstants.BodyName)) { bodyParameter.Name = (Extensions[OpenApiConstants.BodyName] as OpenApiString)?.Value ?? "body"; bodyParameter.Extensions.Remove(OpenApiConstants.BodyName); From c8a4a00a7150347483c6e52068a81e3e5f9dcff7 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 25 Jan 2024 13:05:32 +0300 Subject: [PATCH 26/31] Add tests to validate processing multiple examples in a body parameter as extensions --- .../V2Tests/OpenApiOperationTests.cs | 103 ++++++++++++++++++ .../opWithBodyParameterExamples.yaml | 29 +++++ .../v3OperationWithBodyParameterExamples.yaml | 27 +++++ 3 files changed, 159 insertions(+) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithBodyParameterExamples.yaml create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithBodyParameterExamples.yaml diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index fbf157b4c..86d22f8a5 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -536,5 +536,108 @@ public void LoadV3ExamplesInResponseAsExtensionsWorks() expected = expected.MakeLineBreaksEnvironmentNeutral(); actual.Should().Be(expected); } + + [Fact] + public void LoadV2OperationWithBodyParameterExamplesWorks() + { + // Arrange + MapNode node; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "opWithBodyParameterExamples.yaml"))) + { + node = TestHelper.CreateYamlMapNode(stream); + } + + // Act + var operation = OpenApiV2Deserializer.LoadOperation(node); + var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0); + + // Assert + var expected = @"summary: Get all pets +requestBody: + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + examples: + example1: + summary: Example - List of Pets + value: + - name: Buddy + age: 2 + - name: Whiskers + age: 1 + example2: + summary: Example - Playful Cat + value: + name: Whiskers + age: 1 + required: true + x-bodyName: body +responses: { }"; + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Fact] + public void LoadV3ExamplesInRequestBodyParameterAsExtensionsWorks() + { + // Arrange + MapNode node; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "v3OperationWithBodyParameterExamples.yaml"))) + { + node = TestHelper.CreateYamlMapNode(stream); + } + + // Act + var operation = OpenApiV3Deserializer.LoadOperation(node); + var actual = operation.SerializeAsYaml(OpenApiSpecVersion.OpenApi2_0); + + // Assert + var expected = @"summary: Get all pets +consumes: + - application/json +parameters: + - in: body + name: body + required: true + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + x-examples: + example1: + summary: Example - List of Pets + value: + - name: Buddy + age: 2 + - name: Whiskers + age: 1 + example2: + summary: Example - Playful Cat + value: + name: Whiskers + age: 1 +responses: { }"; + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithBodyParameterExamples.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithBodyParameterExamples.yaml new file mode 100644 index 000000000..e2ffcc7df --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/opWithBodyParameterExamples.yaml @@ -0,0 +1,29 @@ +summary: Get all pets +consumes: + - application/json +parameters: + - in: body + name: body + required: true + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + x-examples: + example1: + summary: Example - List of Pets + value: + - name: Buddy + age: 2 + - name: Whiskers + age: 1 + example2: + summary: Example - Playful Cat + value: + name: Whiskers + age: 1 \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithBodyParameterExamples.yaml b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithBodyParameterExamples.yaml new file mode 100644 index 000000000..a0358125a --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/Samples/OpenApiOperation/v3OperationWithBodyParameterExamples.yaml @@ -0,0 +1,27 @@ +summary: Get all pets +requestBody: + required: true + content: + application/json: + schema: + type: array + items: + type: object + properties: + name: + type: string + age: + type: integer + examples: + example1: + summary: Example - List of Pets + value: + - name: "Buddy" + age: 2 + - name: "Whiskers" + age: 1 + example2: + summary: Example - Playful Cat + value: + name: "Whiskers" + age: 1 \ No newline at end of file From e3e8fc73e1d99e979b6fac7af60ef4d36a708436 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 25 Jan 2024 15:15:30 +0300 Subject: [PATCH 27/31] Refactor code for reuse --- src/Microsoft.OpenApi/Models/OpenApiExample.cs | 12 +++++++++++- src/Microsoft.OpenApi/Models/OpenApiParameter.cs | 2 +- src/Microsoft.OpenApi/Models/OpenApiResponse.cs | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiExample.cs b/src/Microsoft.OpenApi/Models/OpenApiExample.cs index d70bab01d..1b9d31022 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiExample.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiExample.cs @@ -118,6 +118,16 @@ public OpenApiExample GetEffective(OpenApiDocument doc) /// Serialize to OpenAPI V3 document without using reference. /// public void SerializeAsV3WithoutReference(IOpenApiWriter writer) + { + Serialize(writer, OpenApiSpecVersion.OpenApi3_0); + } + + /// + /// Writes out existing examples in a mediatype object + /// + /// + /// + public void Serialize(IOpenApiWriter writer, OpenApiSpecVersion version) { writer.WriteStartObject(); @@ -134,7 +144,7 @@ public void SerializeAsV3WithoutReference(IOpenApiWriter writer) writer.WriteProperty(OpenApiConstants.ExternalValue, ExternalValue); // extensions - writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0); + writer.WriteExtensions(Extensions, version); writer.WriteEndObject(); } diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 6608025a9..fc1eaf8cc 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -397,7 +397,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) foreach (var example in Examples) { writer.WritePropertyName(example.Key); - writer.WriteV2Examples(writer, example.Value, OpenApiSpecVersion.OpenApi2_0); + example.Value.Serialize(writer, OpenApiSpecVersion.OpenApi2_0); } writer.WriteEndObject(); } diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index 369f09761..e300cd33d 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs @@ -204,7 +204,7 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) .SelectMany(mediaTypePair => mediaTypePair.Value.Examples)) { writer.WritePropertyName(example.Key); - writer.WriteV2Examples(writer, example.Value, OpenApiSpecVersion.OpenApi2_0); + example.Value.Serialize(writer, OpenApiSpecVersion.OpenApi2_0); } writer.WriteEndObject(); From fa9306ff720d04d214ba0e3bb194fabeb1fdee39 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 25 Jan 2024 15:16:13 +0300 Subject: [PATCH 28/31] Remove method from interface to prevent a breaking change --- .../Writers/IOpenApiWriter.cs | 12 +--------- .../Writers/OpenApiWriterBase.cs | 24 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) diff --git a/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs b/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs index d40643bfc..9ea04b400 100644 --- a/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs +++ b/src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs @@ -1,8 +1,6 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using Microsoft.OpenApi.Models; - namespace Microsoft.OpenApi.Writers { /// @@ -74,13 +72,5 @@ public interface IOpenApiWriter /// Flush the writer. /// void Flush(); - - /// - /// Writes out existing examples in a mediatype object - /// - /// - /// - /// - void WriteV2Examples(IOpenApiWriter writer, OpenApiExample example, OpenApiSpecVersion version); } } diff --git a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs index 1edab6392..5254288f3 100644 --- a/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs +++ b/src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using Microsoft.OpenApi.Exceptions; -using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Writers @@ -406,28 +405,5 @@ protected void VerifyCanWritePropertyName(string name) string.Format(SRResource.ObjectScopeNeededForPropertyNameWriting, name)); } } - - /// - public void WriteV2Examples(IOpenApiWriter writer, OpenApiExample example, OpenApiSpecVersion version) - { - writer.WriteStartObject(); - - // summary - writer.WriteProperty(OpenApiConstants.Summary, example.Summary); - - // description - writer.WriteProperty(OpenApiConstants.Description, example.Description); - - // value - writer.WriteOptionalObject(OpenApiConstants.Value, example.Value, (w, v) => w.WriteAny(v)); - - // externalValue - writer.WriteProperty(OpenApiConstants.ExternalValue, example.ExternalValue); - - // extensions - writer.WriteExtensions(example.Extensions, version); - - writer.WriteEndObject(); - } } } From 7882fdde69e60e9073eab23db3363d09e440f9b6 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Thu, 25 Jan 2024 15:16:27 +0300 Subject: [PATCH 29/31] Update public API interface --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 256832e42..701d74741 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -643,6 +643,7 @@ namespace Microsoft.OpenApi.Models public bool UnresolvedReference { get; set; } public Microsoft.OpenApi.Any.IOpenApiAny Value { get; set; } public Microsoft.OpenApi.Models.OpenApiExample GetEffective(Microsoft.OpenApi.Models.OpenApiDocument doc) { } + public void Serialize(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.OpenApiSpecVersion version) { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV2WithoutReference(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } @@ -1487,7 +1488,6 @@ namespace Microsoft.OpenApi.Writers void WriteRaw(string value); void WriteStartArray(); void WriteStartObject(); - void WriteV2Examples(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.Models.OpenApiExample example, Microsoft.OpenApi.OpenApiSpecVersion version); void WriteValue(bool value); void WriteValue(decimal value); void WriteValue(int value); @@ -1549,7 +1549,6 @@ namespace Microsoft.OpenApi.Writers public abstract void WriteRaw(string value); public abstract void WriteStartArray(); public abstract void WriteStartObject(); - public void WriteV2Examples(Microsoft.OpenApi.Writers.IOpenApiWriter writer, Microsoft.OpenApi.Models.OpenApiExample example, Microsoft.OpenApi.OpenApiSpecVersion version) { } public virtual void WriteValue(bool value) { } public virtual void WriteValue(System.DateTime value) { } public virtual void WriteValue(System.DateTimeOffset value) { } From 7fd69b92c2be5f689a3b93460a7e51f3b715c72e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 21:43:51 +0000 Subject: [PATCH 30/31] Bump Microsoft.OpenApi.OData from 1.6.0-preview.3 to 1.6.0-preview.4 Bumps [Microsoft.OpenApi.OData](https://github.com/Microsoft/OpenAPI.NET.OData) from 1.6.0-preview.3 to 1.6.0-preview.4. - [Release notes](https://github.com/Microsoft/OpenAPI.NET.OData/releases) - [Commits](https://github.com/Microsoft/OpenAPI.NET.OData/commits) --- updated-dependencies: - dependency-name: Microsoft.OpenApi.OData dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 505d50ef8..6b038a298 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -35,7 +35,7 @@ - + From b277208b65e84d47031866c8ccaa2d62cbcd3fd9 Mon Sep 17 00:00:00 2001 From: Maggiekimani1 Date: Mon, 29 Jan 2024 11:08:15 +0300 Subject: [PATCH 31/31] Bump lib versions --- src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj | 4 ++-- .../Microsoft.OpenApi.Readers.csproj | 2 +- src/Microsoft.OpenApi/Microsoft.OpenApi.csproj | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj index 6b038a298..70e04e54b 100644 --- a/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj +++ b/src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj @@ -1,4 +1,4 @@ - + Exe @@ -9,7 +9,7 @@ enable hidi ./../../artifacts - 1.3.7 + 1.3.8 OpenAPI.NET CLI tool for slicing OpenAPI documents true diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index 160253eee..c36efb50f 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -3,7 +3,7 @@ netstandard2.0 latest true - 1.6.12 + 1.6.13 OpenAPI.NET Readers for JSON and YAML documents true diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index b59136dd0..8f17fae72 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -3,7 +3,7 @@ netstandard2.0 Latest true - 1.6.12 + 1.6.13 .NET models with JSON and YAML writers for OpenAPI specification true