diff --git a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs index 18d9eeb6b..97af235e0 100644 --- a/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs +++ b/src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs @@ -241,11 +241,10 @@ private static async Task InternalLoadAsync(Stream input, string for { settings ??= DefaultReaderSettings.Value; var reader = settings.GetReader(format); - var location = new Uri(OpenApiConstants.BaseRegistryUri); - if (input is FileStream fileStream) - { - location = new Uri(fileStream.Name); - } + var location = + (input is FileStream fileStream ? new Uri(fileStream.Name) : null) ?? + settings.BaseUrl ?? + new Uri(OpenApiConstants.BaseRegistryUri); var readResult = await reader.ReadAsync(input, location, settings, cancellationToken).ConfigureAwait(false); @@ -290,7 +289,7 @@ private static ReadResult InternalLoad(MemoryStream input, string format, OpenAp return readResult; } - private static async Task<(Stream, string?)> RetrieveStreamAndFormatAsync(string url, OpenApiReaderSettings settings, CancellationToken token = default) + private static async Task<(Stream, string?)> RetrieveStreamAndFormatAsync(string url, OpenApiReaderSettings settings, CancellationToken token = default) { if (string.IsNullOrEmpty(url)) { @@ -308,8 +307,8 @@ private static ReadResult InternalLoad(MemoryStream input, string format, OpenAp var mediaType = response.Content.Headers.ContentType?.MediaType; var contentType = mediaType?.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)[0]; format = contentType?.Split('/').Last().Split('+').Last().Split('-').Last(); - - // for non-standard MIME types e.g. text/x-yaml used in older libs or apps + + // for non-standard MIME types e.g. text/x-yaml used in older libs or apps #if NETSTANDARD2_0 stream = await response.Content.ReadAsStreamAsync(); #else diff --git a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs index 5519696d9..04d6971c4 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs @@ -18,9 +18,9 @@ public class OpenApiWorkspace { private readonly Dictionary _documentsIdRegistry = new(); private readonly Dictionary _artifactsRegistry = new(); - private readonly Dictionary _IOpenApiReferenceableRegistry = new(new UriWithFragmentEquailityComparer()); + private readonly Dictionary _IOpenApiReferenceableRegistry = new(new UriWithFragmentEqualityComparer()); - private class UriWithFragmentEquailityComparer : IEqualityComparer + private sealed class UriWithFragmentEqualityComparer : IEqualityComparer { public bool Equals(Uri? x, Uri? y) { diff --git a/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs b/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs index a8eb30cc3..0f29e3044 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/OpenApiWorkspaceTests/OpenApiWorkspaceStreamTests.cs @@ -78,11 +78,10 @@ public async Task LoadDocumentWithExternalReferencesInSubDirectories() var sampleFolderPath = $"V3Tests/Samples/OpenApiWorkspace/ExternalReferencesInSubDirectories"; var referenceBaseUri = "file://" + Path.GetFullPath(sampleFolderPath); - // Create a reader that will resolve all references also of documentes located in the non-root directory + // Create a reader that will resolve all references also of documents located in the non-root directory var settings = new OpenApiReaderSettings() { LoadExternalRefs = true, - BaseUrl = new Uri("file://") }; settings.AddYamlReader(); diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs index 0919471b2..8fac3953e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/OpenApiDocumentTests.cs @@ -514,7 +514,8 @@ public async Task ParseDocumentWithReferenceByIdGetsResolved() public async Task ExternalDocumentDereferenceToOpenApiDocumentUsingJsonPointerWorks() { // Arrange - var path = Path.Combine(Directory.GetCurrentDirectory(), SampleFolderPath); + var documentName = "externalRefByJsonPointer.yaml"; + var path = Path.Combine(Directory.GetCurrentDirectory(), SampleFolderPath, documentName); var settings = new OpenApiReaderSettings { @@ -524,7 +525,7 @@ public async Task ExternalDocumentDereferenceToOpenApiDocumentUsingJsonPointerWo settings.AddYamlReader(); // Act - var result = await OpenApiDocument.LoadAsync(Path.Combine(SampleFolderPath, "externalRefByJsonPointer.yaml"), settings); + var result = await OpenApiDocument.LoadAsync(Path.Combine(SampleFolderPath, documentName), settings); var responseSchema = result.Document.Paths["/resource"].Operations[HttpMethod.Get].Responses["200"].Content["application/json"].Schema; // Assert diff --git a/test/Microsoft.OpenApi.Tests/Reader/OpenApiModelFactoryTests.cs b/test/Microsoft.OpenApi.Tests/Reader/OpenApiModelFactoryTests.cs new file mode 100644 index 000000000..e19343d46 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Reader/OpenApiModelFactoryTests.cs @@ -0,0 +1,122 @@ +using Xunit; +using Microsoft.OpenApi.Reader; +using Microsoft.OpenApi.Models; +using System.Threading.Tasks; +using System.IO; +using System; + +namespace Microsoft.OpenApi.Tests.Reader; + +public class OpenApiModelFactoryTests +{ + [Fact] + public async Task UsesSettingsBaseUrl() + { + var tempFilePathReferee = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await File.WriteAllTextAsync(tempFilePathReferee, +""" +{ + "openapi": "3.1.1", + "info": { + "title": "OData Service for namespace microsoft.graph", + "description": "This OData service is located at https://graph.microsoft.com/v1.0", + "version": "1.0.1" + }, + "servers": [ + { + "url": "https://graph.microsoft.com/v1.0" + } + ], + "paths": { + "/placeholder": { + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "MySchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + } + } +} +"""); + var tempFilePathReferrer = Path.Combine(Path.GetTempPath(), Path.GetTempFileName()); + await File.WriteAllTextAsync(tempFilePathReferrer, +$$$""" +{ + "openapi": "3.1.1", + "info": { + "title": "OData Service for namespace microsoft.graph", + "description": "This OData service is located at https://graph.microsoft.com/v1.0", + "version": "1.0.1" + }, + "servers": [ + { + "url": "https://graph.microsoft.com/v1.0" + } + ], + "paths": { + "/placeholder": { + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "./{{{Path.GetFileName(tempFilePathReferee)}}}#/components/schemas/MySchema" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "MySchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + } + } + } +} +"""); + // read referrer document to a memory stream + using var stream = new MemoryStream(); + using var reader = new StreamReader(tempFilePathReferrer); + await reader.BaseStream.CopyToAsync(stream); + stream.Position = 0; + var baseUri = new Uri(tempFilePathReferrer); + var settings = new OpenApiReaderSettings + { + BaseUrl = baseUri, + }; + var readResult = await OpenApiDocument.LoadAsync(stream, settings: settings); + Assert.NotNull(readResult.Document); + Assert.NotNull(readResult.Document.Components); + Assert.Equal(baseUri, readResult.Document.BaseUri); + } +}