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 f74600e66..a7d9f93d4 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiParameterDeserializer.cs @@ -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) @@ -250,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) { diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs index 2fef353ea..03691d14b 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; @@ -28,6 +29,10 @@ internal static partial class OpenApiV2Deserializer "examples", LoadExamples }, + { + "x-examples", + LoadResponseExamplesExtension + }, { "schema", (o, n) => n.Context.SetTempStorage(TempStorageKeys.ResponseSchema, LoadSchema(n), o) @@ -37,7 +42,8 @@ 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(OpenApiConstants.ExamplesExtension, StringComparison.OrdinalIgnoreCase), + (o, p, n) => o.AddExtension(p, LoadExtension(p, n))} }; private static readonly AnyFieldMap _mediaTypeAnyFields = @@ -69,6 +75,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) + ?? new Dictionary(); foreach (var produce in produces) { @@ -84,7 +92,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 +101,55 @@ 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 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(); + + 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.ToLowerInvariant()) + { + case "summary": + example.Summary = valueNode.Value.GetScalarValue(); + break; + case "description": + example.Description = valueNode.Value.GetScalarValue(); + break; + case "value": + example.Value = OpenApiAnyConverter.GetSpecificOpenApiAny(valueNode.Value.CreateAny()); + break; + case "externalValue": + example.ExternalValue = valueNode.Value.GetScalarValue(); + break; + } + } + + examples.Add(examplesNode.Name, example); + } + + return examples; + } + 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 +160,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 +190,7 @@ public static OpenApiResponse LoadResponse(ParseNode node) } var response = new OpenApiResponse(); + foreach (var property in mapNode) { property.ParseField(response, _responseFixedFields, _responsePatternFields); 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"; } } 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/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/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 9d1651ad8..fc1eaf8cc 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; @@ -204,14 +205,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; } /// @@ -394,6 +388,20 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) } } + //examples + if (Examples != null && Examples.Any()) + { + writer.WritePropertyName(OpenApiConstants.ExamplesExtension); + writer.WriteStartObject(); + + foreach (var example in Examples) + { + writer.WritePropertyName(example.Key); + example.Value.Serialize(writer, 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..5e5edc576 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; } /// @@ -153,6 +146,7 @@ internal OpenApiBodyParameter ConvertToBodyParameter() // To allow round-tripping we use an extension to hold the name Name = "body", 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. }; @@ -184,7 +178,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.Required?.Contains(property.Key) ?? false }; } } diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index 320ecc484..e300cd33d 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; @@ -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; } /// @@ -201,6 +194,22 @@ public void SerializeAsV2WithoutReference(IOpenApiWriter writer) writer.WriteEndObject(); } + if (Content.Values.Any(m => m.Examples != null && m.Examples.Any())) + { + writer.WritePropertyName(OpenApiConstants.ExamplesExtension); + writer.WriteStartObject(); + + foreach (var example in Content + .Where(mediaTypePair => mediaTypePair.Value.Examples != null && mediaTypePair.Value.Examples.Any()) + .SelectMany(mediaTypePair => mediaTypePair.Value.Examples)) + { + writer.WritePropertyName(example.Key); + example.Value.Serialize(writer, OpenApiSpecVersion.OpenApi2_0); + } + + writer.WriteEndObject(); + } + writer.WriteExtensions(mediatype.Value.Extensions, OpenApiSpecVersion.OpenApi2_0); foreach (var key in mediatype.Value.Extensions.Keys) 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 diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs index 326c16969..86d22f8a5 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiOperationTests.cs @@ -11,6 +11,8 @@ 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; namespace Microsoft.OpenApi.Readers.Tests.V2Tests @@ -434,5 +436,208 @@ 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); + } + + [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); + } + + [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/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 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 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 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 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..701d74741 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"; @@ -642,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) { }