diff --git a/src/Microsoft.OpenApi.Readers/OpenApiDocumentExtensions.cs b/src/Microsoft.OpenApi.Readers/OpenApiDocumentExtensions.cs
new file mode 100644
index 000000000..eb2b8fcdf
--- /dev/null
+++ b/src/Microsoft.OpenApi.Readers/OpenApiDocumentExtensions.cs
@@ -0,0 +1,113 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers;
+
+namespace Microsoft.OpenApi
+{
+ ///
+ /// Loads an OpenApiDocument instance through Load/LoadAsync/Parse pattern
+ ///
+ public static class OpenApiDocumentExtensions
+ {
+ ///
+ /// Loads an OpenApiDocument from a file stream
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static OpenApiDocument Load(this OpenApiDocument document,
+ Stream stream,
+ out OpenApiDiagnostic diagnostic,
+ OpenApiReaderSettings settings = null)
+ {
+ return new OpenApiStreamReader(settings).Read(stream, out diagnostic);
+ }
+
+ ///
+ /// Loads an OpenApiDocument from a TextReader
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static OpenApiDocument Load(this OpenApiDocument document,
+ TextReader reader,
+ out OpenApiDiagnostic diagnostic,
+ OpenApiReaderSettings settings = null)
+ {
+ return new OpenApiTextReaderReader(settings).Read(reader, out diagnostic);
+ }
+
+ ///
+ /// Loads an OpenApiDocument from a string input
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static OpenApiDocument Load(this OpenApiDocument document,
+ string input,
+ out OpenApiDiagnostic diagnostic,
+ OpenApiReaderSettings settings = null)
+ {
+ return new OpenApiStringReader(settings).Read(input, out diagnostic);
+ }
+
+ ///
+ /// Loads an OpenApiDocument from a string input
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static OpenApiDocument Parse(this OpenApiDocument document,
+ string input,
+ out OpenApiDiagnostic diagnostic,
+ OpenApiReaderSettings settings = null)
+ {
+ return new OpenApiStringReader(settings).Read(input, out diagnostic);
+ }
+
+ ///
+ /// Loads an OpenApiDocument asynchronously from a TextReader
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Task LoadAsync(this OpenApiDocument document,
+ TextReader reader,
+ OpenApiReaderSettings settings = null,
+ CancellationToken cancellationToken = default)
+ {
+ return new OpenApiTextReaderReader(settings).ReadAsync(reader, cancellationToken);
+ }
+
+ ///
+ /// Loads an OpenApiDocument from a file stream
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static Task LoadAsync(this OpenApiDocument document,
+ Stream stream,
+ OpenApiReaderSettings settings = null,
+ CancellationToken cancellationToken = default)
+ {
+ return new OpenApiStreamReader(settings).ReadAsync(stream, cancellationToken);
+ }
+ }
+}
diff --git a/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs
index 95482bfa6..e0f727a2a 100644
--- a/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs
+++ b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs
@@ -3,9 +3,7 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
@@ -17,7 +15,6 @@
using Microsoft.OpenApi.Readers.Services;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Validations;
-using SharpYaml.Serialization;
namespace Microsoft.OpenApi.Readers
{
@@ -37,6 +34,7 @@ public OpenApiYamlDocumentReader(OpenApiReaderSettings settings = null)
_settings = settings ?? new OpenApiReaderSettings();
}
+
///
/// Reads the stream input and parses it into an Open API document.
///
diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs
index e79a6539d..e3b2d8f3a 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs
@@ -18,12 +18,12 @@ public class OpenApiWorkspaceStreamTests
public async Task LoadingDocumentWithResolveAllReferencesShouldLoadDocumentIntoWorkspace()
{
// Create a reader that will resolve all references
- var reader = new OpenApiStreamReader(new OpenApiReaderSettings()
+ var settings = new OpenApiReaderSettings()
{
LoadExternalRefs = true,
CustomExternalLoader = new MockLoader(),
BaseUrl = new Uri("file://c:\\")
- });
+ };
// Todo: this should be ReadAsync
var stream = new MemoryStream();
@@ -37,7 +37,7 @@ public async Task LoadingDocumentWithResolveAllReferencesShouldLoadDocumentIntoW
wr.Flush();
stream.Position = 0;
- var result = await reader.ReadAsync(stream);
+ var result = await new OpenApiDocument().LoadAsync(stream, settings: settings);
Assert.NotNull(result.OpenApiDocument.Workspace);
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs
index 984c4cdcd..39731ccb0 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs
@@ -8,7 +8,6 @@
using FluentAssertions;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Exceptions;
-using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Xunit;
@@ -41,8 +40,7 @@ public void ShouldThrowWhenReferenceTypeIsInvalid()
$ref: '#/defi888nition/does/notexist'
";
- var reader = new OpenApiStringReader();
- var doc = reader.Read(input, out var diagnostic);
+ var doc = new OpenApiDocument().Parse(input, out var diagnostic);
diagnostic.Errors.Should().BeEquivalentTo(new List {
new OpenApiError( new OpenApiException("Unknown reference type 'defi888nition'")) });
@@ -68,9 +66,7 @@ public void ShouldThrowWhenReferenceDoesNotExist()
$ref: '#/definitions/doesnotexist'
";
- var reader = new OpenApiStringReader();
-
- var doc = reader.Read(input, out var diagnostic);
+ var doc = new OpenApiDocument().Load(input, out var diagnostic);
diagnostic.Errors.Should().BeEquivalentTo(new List {
new OpenApiError( new OpenApiException("Invalid Reference identifier 'doesnotexist'.")) });
@@ -88,7 +84,7 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture)
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
- var openApiDoc = new OpenApiStringReader().Read(
+ var openApiDoc = new OpenApiDocument().Load(
@"
swagger: 2.0
info:
@@ -166,112 +162,110 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture)
[Fact]
public void ShouldParseProducesInAnyOrder()
{
- using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "twoResponses.json")))
- {
- var reader = new OpenApiStreamReader();
- var doc = reader.Read(stream, out var diagnostic);
+ using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "twoResponses.json"));
+ var doc = new OpenApiDocument().Load(stream, out var diagnostic);
- var successSchema = new OpenApiSchema()
+ var successSchema = new OpenApiSchema()
+ {
+ Type = "array",
+ Reference = new OpenApiReference
{
- Type = "array",
- Reference = new OpenApiReference
+ Type = ReferenceType.Schema,
+ Id = "Item",
+ HostDocument = doc
+ },
+ Items = new OpenApiSchema()
+ {
+ Reference = new OpenApiReference()
{
Type = ReferenceType.Schema,
Id = "Item",
HostDocument = doc
- },
- Items = new OpenApiSchema()
- {
- Reference = new OpenApiReference()
+ }
+ }
+ };
+
+ var okSchema = new OpenApiSchema()
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.Schema,
+ Id = "Item",
+ HostDocument = doc
+ },
+ Properties = new Dictionary()
+ {
+ { "id", new OpenApiSchema()
{
- Type = ReferenceType.Schema,
- Id = "Item",
- HostDocument = doc
+ Type = "string",
+ Description = "Item identifier."
}
}
- };
+ }
+ };
- var okSchema = new OpenApiSchema()
+ var errorSchema = new OpenApiSchema()
+ {
+ Reference = new OpenApiReference
{
- Reference = new OpenApiReference
- {
- Type = ReferenceType.Schema,
- Id = "Item",
- HostDocument = doc
- },
- Properties = new Dictionary()
- {
- { "id", new OpenApiSchema()
- {
- Type = "string",
- Description = "Item identifier."
- }
- }
- }
- };
-
- var errorSchema = new OpenApiSchema()
+ Type = ReferenceType.Schema,
+ Id = "Error",
+ HostDocument = doc
+ },
+ Properties = new Dictionary()
{
- Reference = new OpenApiReference
- {
- Type = ReferenceType.Schema,
- Id = "Error",
- HostDocument = doc
+ { "code", new OpenApiSchema()
+ {
+ Type = "integer",
+ Format = "int32"
+ }
},
- Properties = new Dictionary()
- {
- { "code", new OpenApiSchema()
- {
- Type = "integer",
- Format = "int32"
- }
- },
- { "message", new OpenApiSchema()
- {
- Type = "string"
- }
- },
- { "fields", new OpenApiSchema()
- {
- Type = "string"
- }
- }
- }
- };
-
- var okMediaType = new OpenApiMediaType
- {
- Schema = new OpenApiSchema
- {
- Type = "array",
- Items = okSchema
+ { "message", new OpenApiSchema()
+ {
+ Type = "string"
+ }
+ },
+ { "fields", new OpenApiSchema()
+ {
+ Type = "string"
+ }
}
- };
+ }
+ };
- var errorMediaType = new OpenApiMediaType
+ var okMediaType = new OpenApiMediaType
+ {
+ Schema = new OpenApiSchema
{
- Schema = errorSchema
- };
+ Type = "array",
+ Items = okSchema
+ }
+ };
- doc.Should().BeEquivalentTo(new OpenApiDocument
+ var errorMediaType = new OpenApiMediaType
+ {
+ Schema = errorSchema
+ };
+
+ doc.Should().BeEquivalentTo(new OpenApiDocument
+ {
+ Info = new OpenApiInfo
{
- Info = new OpenApiInfo
- {
- Title = "Two responses",
- Version = "1.0.0"
- },
- Servers =
+ Title = "Two responses",
+ Version = "1.0.0"
+ },
+ Servers =
{
new OpenApiServer
{
Url = "https://"
}
},
- Paths = new OpenApiPaths
+ Paths = new OpenApiPaths
+ {
+ ["/items"] = new OpenApiPathItem
{
- ["/items"] = new OpenApiPathItem
- {
- Operations =
+ Operations =
{
[OperationType.Get] = new OpenApiOperation
{
@@ -344,29 +338,26 @@ public void ShouldParseProducesInAnyOrder()
}
}
}
- }
- },
- Components = new OpenApiComponents
- {
- Schemas =
+ }
+ },
+ Components = new OpenApiComponents
+ {
+ Schemas =
{
["Item"] = okSchema,
["Error"] = errorSchema
}
- }
- });
- }
+ }
+ });
}
[Fact]
public void ShouldAssignSchemaToAllResponses()
{
OpenApiDocument document;
- OpenApiDiagnostic diagnostic;
- using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "multipleProduces.json")))
- {
- document = new OpenApiStreamReader().Read(stream, out diagnostic);
- }
+
+ using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "multipleProduces.json"));
+ document = new OpenApiDocument().Load(stream, out var diagnostic);
Assert.Equal(OpenApiSpecVersion.OpenApi2_0, diagnostic.SpecificationVersion);
@@ -437,18 +428,15 @@ public void ShouldAssignSchemaToAllResponses()
[Fact]
public void ShouldAllowComponentsThatJustContainAReference()
{
- using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "ComponentRootReference.json")))
+ using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "ComponentRootReference.json"));
+ OpenApiDocument doc = new OpenApiDocument().Load(stream, out OpenApiDiagnostic diags);
+ OpenApiSchema schema1 = doc.Components.Schemas["AllPets"];
+ Assert.False(schema1.UnresolvedReference);
+ OpenApiSchema schema2 = doc.ResolveReferenceTo(schema1.Reference);
+ if (schema2.UnresolvedReference && schema1.Reference.Id == schema2.Reference.Id)
{
- OpenApiStreamReader reader = new OpenApiStreamReader();
- OpenApiDocument doc = reader.Read(stream, out OpenApiDiagnostic diags);
- OpenApiSchema schema1 = doc.Components.Schemas["AllPets"];
- Assert.False(schema1.UnresolvedReference);
- OpenApiSchema schema2 = doc.ResolveReferenceTo(schema1.Reference);
- if (schema2.UnresolvedReference && schema1.Reference.Id == schema2.Reference.Id)
- {
- // detected a cycle - this code gets triggered
- Assert.True(false, "A cycle should not be detected");
- }
+ // detected a cycle - this code gets triggered
+ Assert.True(false, "A cycle should not be detected");
}
}
}
diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
index 254a37ef9..a26e677ba 100644
--- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
+++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs
@@ -3,11 +3,9 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Text;
using System.Threading;
using FluentAssertions;
using Microsoft.OpenApi.Any;
@@ -81,7 +79,7 @@ public OpenApiDocumentTests(ITestOutputHelper output)
[Fact]
public void ParseDocumentFromInlineStringShouldSucceed()
{
- var openApiDoc = new OpenApiStringReader().Read(
+ var openApiDoc = new OpenApiDocument().Parse(
@"
openapi : 3.0.0
info:
@@ -123,7 +121,7 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture)
Thread.CurrentThread.CurrentCulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
- var openApiDoc = new OpenApiStringReader().Read(
+ var openApiDoc = new OpenApiDocument().Load(
@"
openapi : 3.0.0
info:
@@ -196,7 +194,7 @@ public void ParseBasicDocumentWithMultipleServersShouldSucceed()
{
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDocumentWithMultipleServers.yaml")))
{
- var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic);
+ var openApiDoc = new OpenApiDocument().Load(stream, out var diagnostic);
diagnostic.Should().BeEquivalentTo(
new OpenApiDiagnostic()
@@ -238,7 +236,7 @@ public void ParseBasicDocumentWithMultipleServersShouldSucceed()
public void ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic()
{
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "brokenMinimalDocument.yaml"));
- var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic);
+ var openApiDoc = new OpenApiDocument().Load(stream, out var diagnostic);
openApiDoc.Should().BeEquivalentTo(
new OpenApiDocument
@@ -267,7 +265,7 @@ public void ParseMinimalDocumentShouldSucceed()
{
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml")))
{
- var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic);
+ var openApiDoc = new OpenApiDocument().Load(stream, out var diagnostic);
openApiDoc.Should().BeEquivalentTo(
new OpenApiDocument
@@ -298,7 +296,7 @@ public void ParseStandardPetStoreDocumentShouldSucceed()
OpenApiDiagnostic context;
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStore.yaml")))
{
- var actual = new OpenApiStreamReader().Read(stream, out context);
+ var actual = new OpenApiDocument().Load(stream, out context);
var components = new OpenApiComponents
{
@@ -728,7 +726,7 @@ public void ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed()
OpenApiDiagnostic context;
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStoreWithTagAndSecurity.yaml")))
{
- var actual = new OpenApiStreamReader().Read(stream, out context);
+ var actual = new OpenApiDocument().Load(stream, out context);
var components = new OpenApiComponents
{
@@ -1262,7 +1260,7 @@ public void ParsePetStoreExpandedShouldSucceed()
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "petStoreExpanded.yaml")))
{
- var actual = new OpenApiStreamReader().Read(stream, out context);
+ var actual = new OpenApiDocument().Load(stream, out context);
// TODO: Create the object in memory and compare with the one read from YAML file.
}
@@ -1276,7 +1274,7 @@ public void GlobalSecurityRequirementShouldReferenceSecurityScheme()
{
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "securedApi.yaml")))
{
- var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic);
+ var openApiDoc = new OpenApiDocument().Load(stream, out var diagnostic);
var securityRequirement = openApiDoc.SecurityRequirements.First();
@@ -1289,7 +1287,7 @@ public void HeaderParameterShouldAllowExample()
{
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "apiWithFullHeaderComponent.yaml")))
{
- var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic);
+ var openApiDoc = new OpenApiDocument().Load(stream, out var diagnostic);
var exampleHeader = openApiDoc.Components?.Headers?["example-header"];
Assert.NotNull(exampleHeader);
@@ -1365,10 +1363,9 @@ public void DoesNotChangeExternalReferences()
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "documentWithExternalRefs.yaml"));
// Act
- var doc = new OpenApiStreamReader(
- new OpenApiReaderSettings { ReferenceResolution = ReferenceResolutionSetting.DoNotResolveReferences })
- .Read(stream, out var diagnostic);
-
+ var settings = new OpenApiReaderSettings { ReferenceResolution = ReferenceResolutionSetting.DoNotResolveReferences };
+ var doc = new OpenApiDocument().Load(stream, out var diagnostic, settings);
+
var externalRef = doc.Components.Schemas["Nested"].Properties["AnyOf"].AnyOf.First().Reference.ReferenceV3;
var externalRef2 = doc.Components.Schemas["Nested"].Properties["AnyOf"].AnyOf.Last().Reference.ReferenceV3;
@@ -1384,10 +1381,12 @@ public void ParseDocumentWithReferencedSecuritySchemeWorks()
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "docWithSecuritySchemeReference.yaml"));
// Act
- var doc = new OpenApiStreamReader(new OpenApiReaderSettings
+ var settings = new OpenApiReaderSettings
{
ReferenceResolution = ReferenceResolutionSetting.ResolveLocalReferences
- }).Read(stream, out var diagnostic);
+ };
+
+ var doc = new OpenApiDocument().Load(stream, out var diagnostic, settings);
var securityScheme = doc.Components.SecuritySchemes["OAuth2"];
@@ -1401,7 +1400,7 @@ 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 actual = new OpenApiDocument().Load(stream, out var diagnostic);
var components = new OpenApiComponents
{
@@ -1609,7 +1608,7 @@ public void ParseDocumentsWithReusablePathItemInWebhooksSucceeds()
{
// Arrange && Act
using var stream = Resources.GetStream("V3Tests/Samples/OpenApiDocument/documentWithReusablePaths.yaml");
- var actual = new OpenApiStreamReader().Read(stream, out var context);
+ var actual = new OpenApiDocument().Load(stream, out var context);
var components = new OpenApiComponents
{
@@ -1829,7 +1828,7 @@ public void ParseDocumentWithDescriptionInDollarRefsShouldSucceed()
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "documentWithSummaryAndDescriptionInReference.yaml"));
// Act
- var actual = new OpenApiStreamReader().Read(stream, out var diagnostic);
+ var actual = new OpenApiDocument().Load(stream, out var diagnostic);
var schema = actual.Paths["/pets"].Operations[OperationType.Get].Responses["200"].Content["application/json"].Schema;
var header = actual.Components.Responses["Test"].Headers["X-Test"];