diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index 1e0c08695..36008032e 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -1,18 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System; using System.IO; -using System.Linq; -using Microsoft.OpenApi.Exceptions; -using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers.Interface; -using Microsoft.OpenApi.Readers.Services; -using Microsoft.OpenApi.Services; -using SharpYaml; -using SharpYaml.Serialization; namespace Microsoft.OpenApi.Readers { @@ -21,7 +13,7 @@ namespace Microsoft.OpenApi.Readers /// public class OpenApiStreamReader : IOpenApiReader { - private OpenApiReaderSettings _settings; + private readonly OpenApiReaderSettings _settings; /// /// Create stream reader with custom settings if desired. @@ -30,8 +22,8 @@ public class OpenApiStreamReader : IOpenApiReader public OpenApiStreamReader(OpenApiReaderSettings settings = null) { _settings = settings ?? new OpenApiReaderSettings(); - } + /// /// Reads the stream input and parses it into an Open API document. /// @@ -40,68 +32,10 @@ public OpenApiStreamReader(OpenApiReaderSettings settings = null) /// Instance of newly created OpenApiDocument public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) { - ParsingContext context; - YamlDocument yamlDocument; - diagnostic = new OpenApiDiagnostic(); - - // Parse the YAML/JSON - try - { - yamlDocument = LoadYamlDocument(input); - } - catch (YamlException ex) - { - diagnostic.Errors.Add(new OpenApiError($"#char={ex.Start.Line}", ex.Message)); - return new OpenApiDocument(); - } - - context = new ParsingContext - { - ExtensionParsers = _settings.ExtensionParsers, - BaseUrl = _settings.BaseUrl - }; - - OpenApiDocument document = null; - - try - { - // Parse the OpenAPI Document - document = context.Parse(yamlDocument, diagnostic); - - // Resolve References if requested - switch (_settings.ReferenceResolution) - { - case ReferenceResolutionSetting.ResolveAllReferences: - throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously); - case ReferenceResolutionSetting.ResolveLocalReferences: - var resolver = new OpenApiReferenceResolver(document); - var walker = new OpenApiWalker(resolver); - walker.Walk(document); - foreach (var item in resolver.Errors) - { - diagnostic.Errors.Add(item); - } - break; - case ReferenceResolutionSetting.DoNotResolveReferences: - break; - } - } - catch (OpenApiException ex) - { - diagnostic.Errors.Add(new OpenApiError(ex)); - } - - // Validate the document - if (_settings.RuleSet != null && _settings.RuleSet.Rules.Count > 0) + using (var reader = new StreamReader(input)) { - var errors = document.Validate(_settings.RuleSet); - foreach (var item in errors) - { - diagnostic.Errors.Add(item); - } + return new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); } - - return document; } /// @@ -113,66 +47,10 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) /// Instance of newly created OpenApiDocument public T ReadFragment(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement { - ParsingContext context; - YamlDocument yamlDocument; - diagnostic = new OpenApiDiagnostic(); - - // Parse the YAML/JSON - try - { - yamlDocument = LoadYamlDocument(input); - } - catch (YamlException ex) - { - diagnostic.Errors.Add(new OpenApiError($"#line={ex.Start.Line}", ex.Message)); - return default(T); - } - - context = new ParsingContext - { - ExtensionParsers = _settings.ExtensionParsers - }; - - IOpenApiElement element = null; - - try - { - // Parse the OpenAPI element - element = context.ParseFragment(yamlDocument, version, diagnostic); - } - catch (OpenApiException ex) - { - diagnostic.Errors.Add(new OpenApiError(ex)); - } - - // Validate the element - if (_settings.RuleSet != null && _settings.RuleSet.Rules.Count > 0) - { - var errors = element.Validate(_settings.RuleSet); - foreach (var item in errors) - { - diagnostic.Errors.Add(item); - } - } - - return (T)element; - } - - /// - /// Helper method to turn streams into YamlDocument - /// - /// Stream containing YAML formatted text - /// Instance of a YamlDocument - internal static YamlDocument LoadYamlDocument(Stream input) - { - YamlDocument yamlDocument; - using (var streamReader = new StreamReader(input)) + using (var reader = new StreamReader(input)) { - var yamlStream = new YamlStream(); - yamlStream.Load(streamReader); - yamlDocument = yamlStream.Documents.First(); + return new OpenApiTextReaderReader(_settings).ReadFragment(reader, version, out diagnostic); } - return yamlDocument; } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs index 82b3a3ce7..547698e70 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs @@ -29,14 +29,9 @@ public OpenApiStringReader(OpenApiReaderSettings settings = null) /// public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic) { - using (var memoryStream = new MemoryStream()) + using (var reader = new StringReader(input)) { - var writer = new StreamWriter(memoryStream); - writer.Write(input); - writer.Flush(); - memoryStream.Position = 0; - - return new OpenApiStreamReader(_settings).Read(memoryStream, out diagnostic); + return new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); } } @@ -45,14 +40,9 @@ public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic) /// public T ReadFragment(string input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement { - using (var memoryStream = new MemoryStream()) + using (var reader = new StringReader(input)) { - var writer = new StreamWriter(memoryStream); - writer.Write(input); - writer.Flush(); - memoryStream.Position = 0; - - return new OpenApiStreamReader(_settings).ReadFragment(memoryStream, version, out diagnostic); + return new OpenApiTextReaderReader(_settings).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs new file mode 100644 index 000000000..5c8dd55e7 --- /dev/null +++ b/src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.IO; +using System.Linq; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Readers.Interface; +using SharpYaml; +using SharpYaml.Serialization; + +namespace Microsoft.OpenApi.Readers +{ + /// + /// Service class for converting contents of TextReader into OpenApiDocument instances + /// + public class OpenApiTextReaderReader : IOpenApiReader + { + private readonly OpenApiReaderSettings _settings; + + /// + /// Create stream reader with custom settings if desired. + /// + /// + public OpenApiTextReaderReader(OpenApiReaderSettings settings = null) + { + _settings = settings ?? new OpenApiReaderSettings(); + } + + /// + /// Reads the stream input and parses it into an Open API document. + /// + /// TextReader containing OpenAPI description to parse. + /// Returns diagnostic object containing errors detected during parsing + /// Instance of newly created OpenApiDocument + public OpenApiDocument Read(TextReader input, out OpenApiDiagnostic diagnostic) + { + YamlDocument yamlDocument; + + // Parse the YAML/JSON + try + { + yamlDocument = LoadYamlDocument(input); + } + catch (YamlException ex) + { + diagnostic = new OpenApiDiagnostic(); + diagnostic.Errors.Add(new OpenApiError($"#char={ex.Start.Line}", ex.Message)); + return new OpenApiDocument(); + } + + return new OpenApiYamlDocumentReader(this._settings).Read(yamlDocument, out diagnostic); + } + /// + /// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element. + /// + /// TextReader containing OpenAPI description to parse. + /// Version of the OpenAPI specification that the fragment conforms to. + /// Returns diagnostic object containing errors detected during parsing + /// Instance of newly created OpenApiDocument + public T ReadFragment(TextReader input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement + { + YamlDocument yamlDocument; + + // Parse the YAML/JSON + try + { + yamlDocument = LoadYamlDocument(input); + } + catch (YamlException ex) + { + diagnostic = new OpenApiDiagnostic(); + diagnostic.Errors.Add(new OpenApiError($"#line={ex.Start.Line}", ex.Message)); + return default(T); + } + + return new OpenApiYamlDocumentReader(this._settings).ReadFragment(yamlDocument, version, out diagnostic); + } + + /// + /// Helper method to turn streams into YamlDocument + /// + /// Stream containing YAML formatted text + /// Instance of a YamlDocument + static YamlDocument LoadYamlDocument(TextReader input) + { + var yamlStream = new YamlStream(); + yamlStream.Load(input); + return yamlStream.Documents.First(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs new file mode 100644 index 000000000..e066fd8b7 --- /dev/null +++ b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using Microsoft.OpenApi.Exceptions; +using Microsoft.OpenApi.Extensions; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Readers.Interface; +using Microsoft.OpenApi.Readers.Services; +using Microsoft.OpenApi.Services; +using SharpYaml.Serialization; + +namespace Microsoft.OpenApi.Readers +{ + /// + /// Service class for converting contents of TextReader into OpenApiDocument instances + /// + public class OpenApiYamlDocumentReader : IOpenApiReader + { + private readonly OpenApiReaderSettings _settings; + + /// + /// Create stream reader with custom settings if desired. + /// + /// + public OpenApiYamlDocumentReader(OpenApiReaderSettings settings = null) + { + _settings = settings ?? new OpenApiReaderSettings(); + } + + /// + /// Reads the stream input and parses it into an Open API document. + /// + /// TextReader containing OpenAPI description to parse. + /// Returns diagnostic object containing errors detected during parsing + /// Instance of newly created OpenApiDocument + public OpenApiDocument Read(YamlDocument input, out OpenApiDiagnostic diagnostic) + { + diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext + { + ExtensionParsers = _settings.ExtensionParsers, + BaseUrl = _settings.BaseUrl + }; + + OpenApiDocument document = null; + try + { + // Parse the OpenAPI Document + document = context.Parse(input, diagnostic); + + // Resolve References if requested + switch (_settings.ReferenceResolution) + { + case ReferenceResolutionSetting.ResolveAllReferences: + throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously); + case ReferenceResolutionSetting.ResolveLocalReferences: + var resolver = new OpenApiReferenceResolver(document); + var walker = new OpenApiWalker(resolver); + walker.Walk(document); + foreach (var item in resolver.Errors) + { + diagnostic.Errors.Add(item); + } + break; + case ReferenceResolutionSetting.DoNotResolveReferences: + break; + } + } + catch (OpenApiException ex) + { + diagnostic.Errors.Add(new OpenApiError(ex)); + } + + // Validate the document + if (_settings.RuleSet != null && _settings.RuleSet.Rules.Count > 0) + { + var errors = document.Validate(_settings.RuleSet); + foreach (var item in errors) + { + diagnostic.Errors.Add(item); + } + } + + return document; + } + /// + /// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element. + /// + /// TextReader containing OpenAPI description to parse. + /// Version of the OpenAPI specification that the fragment conforms to. + /// Returns diagnostic object containing errors detected during parsing + /// Instance of newly created OpenApiDocument + public T ReadFragment(YamlDocument input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement + { + diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext + { + ExtensionParsers = _settings.ExtensionParsers + }; + + IOpenApiElement element = null; + try + { + // Parse the OpenAPI element + element = context.ParseFragment(input, version, diagnostic); + } + catch (OpenApiException ex) + { + diagnostic.Errors.Add(new OpenApiError(ex)); + } + + // Validate the element + if (_settings.RuleSet != null && _settings.RuleSet.Rules.Count > 0) + { + var errors = element.Validate(_settings.RuleSet); + foreach (var item in errors) + { + diagnostic.Errors.Add(item); + } + } + + return (T)element; + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiSecuritySchemeTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiSecuritySchemeTests.cs index 29e04d05f..99bbfac78 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiSecuritySchemeTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiSecuritySchemeTests.cs @@ -23,7 +23,7 @@ public void ParseHttpSecuritySchemeShouldSucceed() { using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicSecurityScheme.yaml"))) { - var document = OpenApiStreamReader.LoadYamlDocument(stream); + var document = LoadYamlDocument(stream); var context = new ParsingContext(); var diagnostic = new OpenApiDiagnostic(); @@ -48,7 +48,7 @@ public void ParseApiKeySecuritySchemeShouldSucceed() { using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "apiKeySecurityScheme.yaml"))) { - var document = OpenApiStreamReader.LoadYamlDocument(stream); + var document = LoadYamlDocument(stream); var context = new ParsingContext(); var diagnostic = new OpenApiDiagnostic(); @@ -73,7 +73,7 @@ public void ParseOAuth2ImplicitSecuritySchemeShouldSucceed() { using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "oauth2ImplicitSecurityScheme.yaml"))) { - var document = OpenApiStreamReader.LoadYamlDocument(stream); + var document = LoadYamlDocument(stream); var context = new ParsingContext(); var diagnostic = new OpenApiDiagnostic(); @@ -108,7 +108,7 @@ public void ParseOAuth2PasswordSecuritySchemeShouldSucceed() { using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "oauth2PasswordSecurityScheme.yaml"))) { - var document = OpenApiStreamReader.LoadYamlDocument(stream); + var document = LoadYamlDocument(stream); var context = new ParsingContext(); var diagnostic = new OpenApiDiagnostic(); @@ -143,7 +143,7 @@ public void ParseOAuth2ApplicationSecuritySchemeShouldSucceed() { using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "oauth2ApplicationSecurityScheme.yaml"))) { - var document = OpenApiStreamReader.LoadYamlDocument(stream); + var document = LoadYamlDocument(stream); var context = new ParsingContext(); var diagnostic = new OpenApiDiagnostic(); @@ -178,7 +178,7 @@ public void ParseOAuth2AccessCodeSecuritySchemeShouldSucceed() { using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "oauth2AccessCodeSecurityScheme.yaml"))) { - var document = OpenApiStreamReader.LoadYamlDocument(stream); + var document = LoadYamlDocument(stream); var context = new ParsingContext(); var diagnostic = new OpenApiDiagnostic(); @@ -208,5 +208,15 @@ public void ParseOAuth2AccessCodeSecuritySchemeShouldSucceed() }); } } + + static YamlDocument LoadYamlDocument(Stream input) + { + using (var reader = new StreamReader(input)) + { + var yamlStream = new YamlStream(); + yamlStream.Load(reader); + return yamlStream.Documents.First(); + } + } } } \ No newline at end of file