diff --git a/src/Microsoft.OpenApi.Readers/ParsingContext.cs b/src/Microsoft.OpenApi.Readers/ParsingContext.cs
index 6c4dece2f..ac1e2a497 100644
--- a/src/Microsoft.OpenApi.Readers/ParsingContext.cs
+++ b/src/Microsoft.OpenApi.Readers/ParsingContext.cs
@@ -63,12 +63,14 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument)
VersionService = new OpenApiV2VersionService(Diagnostic);
doc = VersionService.LoadDocument(RootNode);
this.Diagnostic.SpecificationVersion = OpenApiSpecVersion.OpenApi2_0;
+ ValidateRequiredFields(doc, version);
break;
- case string version when version.StartsWith("3.0"):
+ case string version when version.StartsWith("3.0") || version.StartsWith("3.1"):
VersionService = new OpenApiV3VersionService(Diagnostic);
doc = VersionService.LoadDocument(RootNode);
this.Diagnostic.SpecificationVersion = OpenApiSpecVersion.OpenApi3_0;
+ ValidateRequiredFields(doc, version);
break;
default:
@@ -244,5 +246,21 @@ public void PopLoop(string loopid)
}
}
+ private void ValidateRequiredFields(OpenApiDocument doc, string version)
+ {
+ if ((version == "2.0" || version.StartsWith("3.0")) && (doc.Paths == null || !doc.Paths.Any()))
+ {
+ // paths is a required field in OpenAPI 3.0 but optional in 3.1
+ RootNode.Context.Diagnostic.Errors.Add(new OpenApiError("", $"Paths is a REQUIRED field at {RootNode.Context.GetLocation()}"));
+ }
+ else if (version.StartsWith("3.1"))
+ {
+ if ((doc.Paths == null || !doc.Paths.Any()) && (doc.Webhooks == null || !doc.Webhooks.Any()))
+ {
+ RootNode.Context.Diagnostic.Errors.Add(new OpenApiError(
+ "", $"The document MUST contain either a Paths or Webhooks field at {RootNode.Context.GetLocation()}"));
+ }
+ }
+ }
}
}
diff --git a/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs b/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs
index df1434cd9..4857251da 100644
--- a/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs
+++ b/src/Microsoft.OpenApi.Readers/V3/OpenApiDocumentDeserializer.cs
@@ -1,10 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
-using System.Collections.Generic;
-using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Extensions;
-using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers.ParseNodes;
@@ -26,6 +23,7 @@ internal static partial class OpenApiV3Deserializer
{"info", (o, n) => o.Info = LoadInfo(n)},
{"servers", (o, n) => o.Servers = n.CreateList(LoadServer)},
{"paths", (o, n) => o.Paths = LoadPaths(n)},
+ {"webhooks", (o, n) => o.Webhooks = LoadPaths(n)},
{"components", (o, n) => o.Components = LoadComponents(n)},
{"tags", (o, n) => {o.Tags = n.CreateList(LoadTag);
foreach (var tag in o.Tags)
@@ -50,7 +48,7 @@ internal static partial class OpenApiV3Deserializer
public static OpenApiDocument LoadOpenApi(RootNode rootNode)
{
var openApidoc = new OpenApiDocument();
-
+
var openApiNode = rootNode.GetMap();
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);
diff --git a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
index 553844764..40c082915 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiConstants.cs
@@ -20,6 +20,11 @@ public static class OpenApiConstants
///
public const string Info = "info";
+ ///
+ /// Field: Webhooks
+ ///
+ public const string Webhooks = "webhooks";
+
///
/// Field: Title
///
diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
index 5177e4f45..9544550b5 100644
--- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
+++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs
@@ -39,6 +39,13 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible
///
public OpenApiPaths Paths { get; set; }
+ ///
+ /// The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement.
+ /// A map of requests initiated other than by an API call, for example by an out of band registration.
+ /// The key name is a unique string to refer to each webhook, while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider and the expected responses
+ ///
+ public IDictionary Webhooks { get; set; } = new Dictionary();
+
///
/// An element to hold various schemas for the specification.
///
@@ -84,6 +91,7 @@ public OpenApiDocument(OpenApiDocument document)
Info = document?.Info != null ? new(document?.Info) : null;
Servers = document?.Servers != null ? new List(document.Servers) : null;
Paths = document?.Paths != null ? new(document?.Paths) : null;
+ Webhooks = document?.Webhooks != null ? new Dictionary(document.Webhooks) : null;
Components = document?.Components != null ? new(document?.Components) : null;
SecurityRequirements = document?.SecurityRequirements != null ? new List(document.SecurityRequirements) : null;
Tags = document?.Tags != null ? new List(document.Tags) : null;
@@ -115,6 +123,24 @@ public void SerializeAsV3(IOpenApiWriter writer)
// paths
writer.WriteRequiredObject(OpenApiConstants.Paths, Paths, (w, p) => p.SerializeAsV3(w));
+ // webhooks
+ writer.WriteOptionalMap(
+ OpenApiConstants.Webhooks,
+ Webhooks,
+ (w, key, component) =>
+ {
+ if (component.Reference != null &&
+ component.Reference.Type == ReferenceType.PathItem &&
+ component.Reference.Id == key)
+ {
+ component.SerializeAsV3WithoutReference(w);
+ }
+ else
+ {
+ component.SerializeAsV3(w);
+ }
+ });
+
// components
writer.WriteOptionalObject(OpenApiConstants.Components, Components, (w, c) => c.SerializeAsV3(w));
diff --git a/src/Microsoft.OpenApi/Models/ReferenceType.cs b/src/Microsoft.OpenApi/Models/ReferenceType.cs
index 6ac0c9ed2..b86f3d171 100644
--- a/src/Microsoft.OpenApi/Models/ReferenceType.cs
+++ b/src/Microsoft.OpenApi/Models/ReferenceType.cs
@@ -58,6 +58,11 @@ public enum ReferenceType
///
/// Tags item.
///
- [Display("tags")] Tag
+ [Display("tags")] Tag,
+
+ ///
+ /// Path item.
+ ///
+ [Display("pathItem")] PathItem,
}
}
diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs
index c9679381a..85a90a0ef 100644
--- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs
+++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs
@@ -99,6 +99,13 @@ public virtual void Visit(OpenApiPaths paths)
{
}
+ ///
+ /// Visits Webhooks>
+ ///
+ public virtual void Visit(IDictionary webhooks)
+ {
+ }
+
///
/// Visits
///
diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs
index 78ca5e61b..42afba695 100644
--- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs
+++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs
@@ -46,6 +46,7 @@ public void Walk(OpenApiDocument doc)
Walk(OpenApiConstants.Info, () => Walk(doc.Info));
Walk(OpenApiConstants.Servers, () => Walk(doc.Servers));
Walk(OpenApiConstants.Paths, () => Walk(doc.Paths));
+ Walk(OpenApiConstants.Webhooks, () => Walk(doc.Webhooks));
Walk(OpenApiConstants.Components, () => Walk(doc.Components));
Walk(OpenApiConstants.Security, () => Walk(doc.SecurityRequirements));
Walk(OpenApiConstants.ExternalDocs, () => Walk(doc.ExternalDocs));
@@ -221,6 +222,28 @@ internal void Walk(OpenApiPaths paths)
}
}
+ ///
+ /// Visits Webhooks and child objects
+ ///
+ internal void Walk(IDictionary webhooks)
+ {
+ if (webhooks == null)
+ {
+ return;
+ }
+
+ _visitor.Visit(webhooks);
+
+ // Visit Webhooks
+ if (webhooks != null)
+ {
+ foreach (var pathItem in webhooks)
+ {
+ Walk(pathItem.Key, () => Walk(pathItem.Value));
+ }
+ }
+ }
+
///
/// Visits list of and child objects
///
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs
index e5193b4c2..7f468f59a 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs
@@ -28,15 +28,6 @@ public static class OpenApiDocumentRules
String.Format(SRResource.Validation_FieldIsRequired, "info", "document"));
}
context.Exit();
-
- // paths
- context.Enter("paths");
- if (item.Paths == null)
- {
- context.CreateError(nameof(OpenApiDocumentFieldIsMissing),
- String.Format(SRResource.Validation_FieldIsRequired, "paths", "document"));
- }
- context.Exit();
});
}
}
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 1579f85e5..35b0595d9 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
+++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj
@@ -137,6 +137,9 @@
Never
+
+ Never
+
Never
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
index 6fbb7065a..85922f993 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
@@ -1327,5 +1327,213 @@ public void HeaderParameterShouldAllowExample()
});
}
}
+
+ [Fact]
+ public void ParseDocumentWithWebhooksShouldSucceed()
+ {
+ // Arrange and Act
+ using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "documentWithWebhooks.yaml"));
+ var actual = new OpenApiStreamReader().Read(stream, out var diagnostic);
+
+ var components = new OpenApiComponents
+ {
+ Schemas = new Dictionary
+ {
+ ["pet"] = new OpenApiSchema
+ {
+ Type = "object",
+ Required = new HashSet
+ {
+ "id",
+ "name"
+ },
+ Properties = new Dictionary
+ {
+ ["id"] = new OpenApiSchema
+ {
+ Type = "integer",
+ Format = "int64"
+ },
+ ["name"] = new OpenApiSchema
+ {
+ Type = "string"
+ },
+ ["tag"] = new OpenApiSchema
+ {
+ Type = "string"
+ },
+ },
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.Schema,
+ Id = "pet",
+ HostDocument = actual
+ }
+ },
+ ["newPet"] = new OpenApiSchema
+ {
+ Type = "object",
+ Required = new HashSet
+ {
+ "name"
+ },
+ Properties = new Dictionary
+ {
+ ["id"] = new OpenApiSchema
+ {
+ Type = "integer",
+ Format = "int64"
+ },
+ ["name"] = new OpenApiSchema
+ {
+ Type = "string"
+ },
+ ["tag"] = new OpenApiSchema
+ {
+ Type = "string"
+ },
+ },
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.Schema,
+ Id = "newPet",
+ HostDocument = actual
+ }
+ }
+ }
+ };
+
+ // Create a clone of the schema to avoid modifying things in components.
+ var petSchema = Clone(components.Schemas["pet"]);
+
+ petSchema.Reference = new OpenApiReference
+ {
+ Id = "pet",
+ Type = ReferenceType.Schema,
+ HostDocument = actual
+ };
+
+ var newPetSchema = Clone(components.Schemas["newPet"]);
+
+ newPetSchema.Reference = new OpenApiReference
+ {
+ Id = "newPet",
+ Type = ReferenceType.Schema,
+ HostDocument = actual
+ };
+
+ var expected = new OpenApiDocument
+ {
+ Info = new OpenApiInfo
+ {
+ Version = "1.0.0",
+ Title = "Webhook Example"
+ },
+ Webhooks = new Dictionary
+ {
+ ["/pets"] = new OpenApiPathItem
+ {
+ Operations = new Dictionary
+ {
+ [OperationType.Get] = new OpenApiOperation
+ {
+ Description = "Returns all pets from the system that the user has access to",
+ OperationId = "findPets",
+ Parameters = new List
+ {
+ new OpenApiParameter
+ {
+ Name = "tags",
+ In = ParameterLocation.Query,
+ Description = "tags to filter by",
+ Required = false,
+ Schema = new OpenApiSchema
+ {
+ Type = "array",
+ Items = new OpenApiSchema
+ {
+ Type = "string"
+ }
+ }
+ },
+ new OpenApiParameter
+ {
+ Name = "limit",
+ In = ParameterLocation.Query,
+ Description = "maximum number of results to return",
+ Required = false,
+ Schema = new OpenApiSchema
+ {
+ Type = "integer",
+ Format = "int32"
+ }
+ }
+ },
+ Responses = new OpenApiResponses
+ {
+ ["200"] = new OpenApiResponse
+ {
+ Description = "pet response",
+ Content = new Dictionary
+ {
+ ["application/json"] = new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Type = "array",
+ Items = petSchema
+ }
+ },
+ ["application/xml"] = new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Type = "array",
+ Items = petSchema
+ }
+ }
+ }
+ }
+ }
+ },
+ [OperationType.Post] = new OpenApiOperation
+ {
+ RequestBody = new OpenApiRequestBody
+ {
+ Description = "Information about a new pet in the system",
+ Required = true,
+ Content = new Dictionary
+ {
+ ["application/json"] = new OpenApiMediaType
+ {
+ Schema = newPetSchema
+ }
+ }
+ },
+ Responses = new OpenApiResponses
+ {
+ ["200"] = new OpenApiResponse
+ {
+ Description = "Return a 200 status to indicate that the data was received successfully",
+ Content = new Dictionary
+ {
+ ["application/json"] = new OpenApiMediaType
+ {
+ Schema = petSchema
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ Components = components
+ };
+
+ // Assert
+ diagnostic.Should().BeEquivalentTo(new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 });
+ actual.Should().BeEquivalentTo(expected);
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithWebhooks.yaml b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithWebhooks.yaml
new file mode 100644
index 000000000..189835344
--- /dev/null
+++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/documentWithWebhooks.yaml
@@ -0,0 +1,81 @@
+openapi: 3.1.0
+info:
+ title: Webhook Example
+ version: 1.0.0
+webhooks:
+ /pets:
+ get:
+ description: Returns all pets from the system that the user has access to
+ operationId: findPets
+ parameters:
+ - name: tags
+ in: query
+ description: tags to filter by
+ required: false
+ schema:
+ type: array
+ items:
+ type: string
+ - name: limit
+ in: query
+ description: maximum number of results to return
+ required: false
+ schema:
+ type: integer
+ format: int32
+ responses:
+ '200':
+ description: pet response
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ "$ref": '#/components/schemas/pet'
+ application/xml:
+ schema:
+ type: array
+ items:
+ "$ref": '#/components/schemas/pet'
+ post:
+ requestBody:
+ description: Information about a new pet in the system
+ required: true
+ content:
+ 'application/json':
+ schema:
+ "$ref": '#/components/schemas/newPet'
+ responses:
+ "200":
+ description: Return a 200 status to indicate that the data was received successfully
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/pet'
+components:
+ schemas:
+ pet:
+ type: object
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ tag:
+ type: string
+ newPet:
+ type: object
+ required:
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ tag:
+ type: string
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDocumentWithWebhooksAsV3JsonWorks_produceTerseOutput=False.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDocumentWithWebhooksAsV3JsonWorks_produceTerseOutput=False.verified.txt
new file mode 100644
index 000000000..73cc1b716
--- /dev/null
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDocumentWithWebhooksAsV3JsonWorks_produceTerseOutput=False.verified.txt
@@ -0,0 +1,51 @@
+{
+ "openapi": "3.0.1",
+ "info": {
+ "title": "Webhook Example",
+ "version": "1.0.0"
+ },
+ "paths": { },
+ "webhooks": {
+ "newPet": {
+ "post": {
+ "requestBody": {
+ "description": "Information about a new pet in the system",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/Pet"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Return a 200 status to indicate that the data was received successfully"
+ }
+ }
+ }
+ }
+ },
+ "components": {
+ "schemas": {
+ "Pet": {
+ "required": [
+ "id",
+ "name"
+ ],
+ "properties": {
+ "id": {
+ "type": "integer",
+ "format": "int64"
+ },
+ "name": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDocumentWithWebhooksAsV3JsonWorks_produceTerseOutput=True.verified.txt b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDocumentWithWebhooksAsV3JsonWorks_produceTerseOutput=True.verified.txt
new file mode 100644
index 000000000..a23dd5675
--- /dev/null
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.SerializeDocumentWithWebhooksAsV3JsonWorks_produceTerseOutput=True.verified.txt
@@ -0,0 +1 @@
+{"openapi":"3.0.1","info":{"title":"Webhook Example","version":"1.0.0"},"paths":{},"webhooks":{"newPet":{"post":{"requestBody":{"description":"Information about a new pet in the system","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"responses":{"200":{"description":"Return a 200 status to indicate that the data was received successfully"}}}}},"components":{"schemas":{"Pet":{"required":["id","name"],"properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"},"tag":{"type":"string"}}}}}}
\ No newline at end of file
diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
index 89289397f..6a185b556 100644
--- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
@@ -890,6 +890,78 @@ public class OpenApiDocumentTests
Components = AdvancedComponents
};
+ public static OpenApiDocument DocumentWithWebhooks = new OpenApiDocument()
+ {
+ Info = new OpenApiInfo
+ {
+ Title = "Webhook Example",
+ Version = "1.0.0"
+ },
+ Webhooks = new Dictionary
+ {
+ ["newPet"] = new OpenApiPathItem
+ {
+ Operations = new Dictionary
+ {
+ [OperationType.Post] = new OpenApiOperation
+ {
+ RequestBody = new OpenApiRequestBody
+ {
+ Description = "Information about a new pet in the system",
+ Content = new Dictionary
+ {
+ ["application/json"] = new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
+ {
+ Reference = new OpenApiReference
+ {
+ Id = "Pet",
+ Type = ReferenceType.Schema
+ }
+ }
+ }
+ }
+ },
+ Responses = new OpenApiResponses
+ {
+ ["200"] = new OpenApiResponse
+ {
+ Description = "Return a 200 status to indicate that the data was received successfully"
+ }
+ }
+ }
+ }
+ }
+ },
+ Components = new OpenApiComponents
+ {
+ Schemas = new Dictionary
+ {
+ ["Pet"] = new OpenApiSchema
+ {
+ Required = new HashSet { "id", "name" },
+ Properties = new Dictionary
+ {
+ ["id"] = new OpenApiSchema
+ {
+ Type = "integer",
+ Format = "int64"
+ },
+ ["name"] = new OpenApiSchema
+ {
+ Type = "string"
+ },
+ ["tag"] = new OpenApiSchema
+ {
+ Type = "string"
+ }
+ }
+ }
+ }
+ }
+ };
+
public static OpenApiDocument DuplicateExtensions = new OpenApiDocument
{
Info = new OpenApiInfo
@@ -1319,7 +1391,7 @@ public void SerializeRelativeRootPathWithHostAsV2JsonWorks()
public void TestHashCodesForSimilarOpenApiDocuments()
{
// Arrange
- var sampleFolderPath = "Models/Samples/";
+ var sampleFolderPath = "Models/Samples/";
var doc1 = ParseInputFile(Path.Combine(sampleFolderPath, "sampleDocument.yaml"));
var doc2 = ParseInputFile(Path.Combine(sampleFolderPath, "sampleDocument.yaml"));
@@ -1356,5 +1428,68 @@ public void CopyConstructorForAdvancedDocumentWorks()
Assert.Equal(2, doc.Paths.Count);
Assert.NotNull(doc.Components);
}
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public async void SerializeDocumentWithWebhooksAsV3JsonWorks(bool produceTerseOutput)
+ {
+ // Arrange
+ var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
+ var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = produceTerseOutput });
+
+ // Act
+ DocumentWithWebhooks.SerializeAsV3(writer);
+ writer.Flush();
+ var actual = outputStringWriter.GetStringBuilder().ToString();
+
+ // Assert
+ await Verifier.Verify(actual).UseParameters(produceTerseOutput);
+ }
+
+ [Fact]
+ public void SerializeDocumentWithWebhooksAsV3YamlWorks()
+ {
+ // Arrange
+ var expected = @"openapi: 3.0.1
+info:
+ title: Webhook Example
+ version: 1.0.0
+paths: { }
+webhooks:
+ newPet:
+ post:
+ requestBody:
+ description: Information about a new pet in the system
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Pet'
+ responses:
+ '200':
+ description: Return a 200 status to indicate that the data was received successfully
+components:
+ schemas:
+ Pet:
+ required:
+ - id
+ - name
+ properties:
+ id:
+ type: integer
+ format: int64
+ name:
+ type: string
+ tag:
+ type: string";
+
+ // Act
+ var actual = DocumentWithWebhooks.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_0);
+
+ // Assert
+ actual = actual.MakeLineBreaksEnvironmentNeutral();
+ expected = expected.MakeLineBreaksEnvironmentNeutral();
+ Assert.Equal(expected, actual);
+ }
}
}