diff --git a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs index 5d251802..e337fbb3 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiReaderSettings.cs @@ -5,6 +5,7 @@ using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.MicrosoftExtensions; using Microsoft.OpenApi.Readers.Interface; +using Microsoft.OpenApi.Services; using Microsoft.OpenApi.Validations; using System; using System.Collections.Generic; @@ -81,6 +82,11 @@ public class OpenApiReaderSettings /// public bool LeaveStreamOpen { get; set; } + /// + /// Yolo + /// + public OpenApiWorkspace Workspace { get; set; } = new(); + /// /// Adds parsers for Microsoft OpenAPI extensions: /// - diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index c6c8add2..e334e232 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -9,6 +9,7 @@ using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers.Interface; +using Microsoft.OpenApi.Services; namespace Microsoft.OpenApi.Readers { @@ -18,14 +19,23 @@ namespace Microsoft.OpenApi.Readers public class OpenApiStreamReader : IOpenApiReader { private readonly OpenApiReaderSettings _settings; + private string _reference; /// /// Create stream reader with custom settings if desired. /// /// - public OpenApiStreamReader(OpenApiReaderSettings settings = null) + public OpenApiStreamReader(OpenApiReaderSettings settings = null) : this(settings, null) { } + + /// + /// Create stream reader with custom settings if desired. + /// + /// + /// + public OpenApiStreamReader(OpenApiReaderSettings settings = null, string reference = null) { _settings = settings ?? new OpenApiReaderSettings(); + _reference = reference; if((_settings.ReferenceResolution == ReferenceResolutionSetting.ResolveAllReferences || _settings.LoadExternalRefs) && _settings.BaseUrl == null) @@ -43,7 +53,7 @@ public OpenApiStreamReader(OpenApiReaderSettings settings = null) public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) { using var reader = new StreamReader(input, Encoding.UTF8, true, 4096, _settings.LeaveStreamOpen); - return new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); + return new OpenApiTextReaderReader(_settings, _reference).Read(reader, out diagnostic); } /// @@ -71,7 +81,7 @@ public async Task ReadAsync(Stream input, CancellationToken cancella } using var reader = new StreamReader(bufferedStream, Encoding.UTF8, true, bufferSize, _settings.LeaveStreamOpen); - return await new OpenApiTextReaderReader(_settings).ReadAsync(reader, cancellationToken); + return await new OpenApiTextReaderReader(_settings, _reference).ReadAsync(reader, cancellationToken); } /// @@ -84,7 +94,7 @@ public async Task ReadAsync(Stream input, CancellationToken cancella public T ReadFragment(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiReferenceable { using var reader = new StreamReader(input, Encoding.UTF8, true, 4096, _settings.LeaveStreamOpen); - return new OpenApiTextReaderReader(_settings).ReadFragment(reader, version, out diagnostic); + return new OpenApiTextReaderReader(_settings, _reference).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs index 1a694f25..51eb7f75 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs @@ -14,14 +14,23 @@ namespace Microsoft.OpenApi.Readers public class OpenApiStringReader : IOpenApiReader { private readonly OpenApiReaderSettings _settings; + private string _reference; + + /// + /// Create stream reader with custom settings if desired. + /// + /// + public OpenApiStringReader(OpenApiReaderSettings settings = null) : this(settings, null) { } /// /// Constructor tha allows reader to use non-default settings /// /// - public OpenApiStringReader(OpenApiReaderSettings settings = null) + /// + public OpenApiStringReader(OpenApiReaderSettings settings = null, string reference = null) { _settings = settings ?? new OpenApiReaderSettings(); + _reference = reference; } /// @@ -30,7 +39,7 @@ public OpenApiStringReader(OpenApiReaderSettings settings = null) public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic) { using var reader = new StringReader(input); - return new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); + return new OpenApiTextReaderReader(_settings, _reference).Read(reader, out diagnostic); } /// @@ -39,7 +48,7 @@ public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic) public T ReadFragment(string input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement { using var reader = new StringReader(input); - return new OpenApiTextReaderReader(_settings).ReadFragment(reader, version, out diagnostic); + return new OpenApiTextReaderReader(_settings, _reference).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs index dff20fc7..a491866b 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiTextReaderReader.cs @@ -19,14 +19,17 @@ namespace Microsoft.OpenApi.Readers public class OpenApiTextReaderReader : IOpenApiReader { private readonly OpenApiReaderSettings _settings; + private string _reference; /// /// Create stream reader with custom settings if desired. /// /// - public OpenApiTextReaderReader(OpenApiReaderSettings settings = null) + /// + public OpenApiTextReaderReader(OpenApiReaderSettings settings = null, string reference = null) { _settings = settings ?? new OpenApiReaderSettings(); + _reference = reference; } /// @@ -51,7 +54,7 @@ public OpenApiDocument Read(TextReader input, out OpenApiDiagnostic diagnostic) return new(); } - return new OpenApiYamlDocumentReader(this._settings).Read(yamlDocument, out diagnostic); + return new OpenApiYamlDocumentReader(this._settings, _reference).Read(yamlDocument, out diagnostic); } /// @@ -80,7 +83,7 @@ public async Task ReadAsync(TextReader input, CancellationToken canc }; } - return await new OpenApiYamlDocumentReader(this._settings).ReadAsync(yamlDocument, cancellationToken); + return await new OpenApiYamlDocumentReader(this._settings, _reference).ReadAsync(yamlDocument, cancellationToken); } /// @@ -106,7 +109,7 @@ public async Task ReadAsync(TextReader input, CancellationToken canc return default; } - return new OpenApiYamlDocumentReader(this._settings).ReadFragment(yamlDocument, version, out diagnostic); + return new OpenApiYamlDocumentReader(this._settings, _reference).ReadFragment(yamlDocument, version, out diagnostic); } /// diff --git a/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs index af9ebcad..2fccd9aa 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs @@ -24,14 +24,23 @@ namespace Microsoft.OpenApi.Readers internal class OpenApiYamlDocumentReader : IOpenApiReader { private readonly OpenApiReaderSettings _settings; + private readonly string _reference; /// /// Create stream reader with custom settings if desired. /// /// - public OpenApiYamlDocumentReader(OpenApiReaderSettings settings = null) + public OpenApiYamlDocumentReader(OpenApiReaderSettings settings = null) : this(settings, null) { } + + /// + /// Create stream reader with custom settings if desired. + /// + /// + /// + public OpenApiYamlDocumentReader(OpenApiReaderSettings settings = null, string reference = null) { _settings = settings ?? new OpenApiReaderSettings(); + _reference = reference; } /// @@ -102,7 +111,7 @@ public async Task ReadAsync(YamlDocument input, CancellationToken ca if (_settings.LoadExternalRefs) { - var diagnosticExternalRefs = await LoadExternalRefsAsync(document, cancellationToken); + var diagnosticExternalRefs = await LoadExternalRefsAsync(document, _reference, cancellationToken); // Merge diagnostics of external reference if (diagnosticExternalRefs != null) { @@ -139,15 +148,15 @@ public async Task ReadAsync(YamlDocument input, CancellationToken ca }; } - private Task LoadExternalRefsAsync(OpenApiDocument document, CancellationToken cancellationToken = default) + private Task LoadExternalRefsAsync(OpenApiDocument document, string reference = null, CancellationToken cancellationToken = default) { // Create workspace for all documents to live in. - var openApiWorkSpace = new OpenApiWorkspace(); + var openApiWorkSpace = _settings.Workspace; // Load this root document into the workspace var streamLoader = new DefaultStreamLoader(_settings.BaseUrl); var workspaceLoader = new OpenApiWorkspaceLoader(openApiWorkSpace, _settings.CustomExternalLoader ?? streamLoader, _settings); - return workspaceLoader.LoadAsync(new() { ExternalResource = "/" }, document, null, cancellationToken); + return workspaceLoader.LoadAsync(new() { ExternalResource = reference ?? "/" }, document, null, cancellationToken); } private void ResolveReferences(OpenApiDiagnostic diagnostic, OpenApiDocument document) diff --git a/src/Microsoft.OpenApi.Readers/Services/OpenApiWorkspaceLoader.cs b/src/Microsoft.OpenApi.Readers/Services/OpenApiWorkspaceLoader.cs index c2d1cfe3..8c925608 100644 --- a/src/Microsoft.OpenApi.Readers/Services/OpenApiWorkspaceLoader.cs +++ b/src/Microsoft.OpenApi.Readers/Services/OpenApiWorkspaceLoader.cs @@ -22,6 +22,11 @@ public OpenApiWorkspaceLoader(OpenApiWorkspace workspace, IStreamLoader loader, internal async Task LoadAsync(OpenApiReference reference, OpenApiDocument document, OpenApiDiagnostic diagnostic = null, CancellationToken cancellationToken = default) { + if (_workspace.Contains(reference.ExternalResource)) + { + return diagnostic; + } + _workspace.AddDocument(reference.ExternalResource, document); document.Workspace = _workspace; @@ -30,8 +35,6 @@ internal async Task LoadAsync(OpenApiReference reference, Ope var collectorWalker = new OpenApiWalker(referenceCollector); collectorWalker.Walk(document); - var reader = new OpenApiStreamReader(_readerSettings); - if (diagnostic is null) { diagnostic = new(); @@ -43,6 +46,7 @@ internal async Task LoadAsync(OpenApiReference reference, Ope // If not already in workspace, load it and process references if (!_workspace.Contains(item.ExternalResource)) { + var reader = new OpenApiStreamReader(_readerSettings, item.ExternalResource); var input = await _loader.LoadAsync(new(item.ExternalResource, UriKind.RelativeOrAbsolute)); var result = await reader.ReadAsync(input, cancellationToken); // Merge diagnostics