diff --git a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs index b29a7890f..3925f7e3d 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWorkspace.cs @@ -94,8 +94,13 @@ public void RegisterComponents(OpenApiDocument document) foreach (var item in document.Components.Schemas) { if (item.Value == null) continue; - location = item.Value.Id ?? baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + item.Key; + location = baseUri + ReferenceType.Schema.GetDisplayName() + ComponentSegmentSeparator + item.Key; RegisterComponent(location, item.Value); + + if (item.Value is not OpenApiSchemaReference && item.Value.Id is string schemaId && schemaId.Length > 0) + { + RegisterComponent(schemaId, item.Value); + } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/RelativeReferenceTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/RelativeReferenceTests.cs index 3cae0c533..9942968e3 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V31Tests/RelativeReferenceTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V31Tests/RelativeReferenceTests.cs @@ -203,6 +203,75 @@ public async Task ParseLocalReferenceToJsonSchemaResourceWorks() Assert.Equal(JsonSchemaType.Object | JsonSchemaType.Null, schema.Type); } + [Fact] + public async Task ParseExternalSchemaReferencedDirectlyAndReExportedAtRootWorks() + { + var tempDirectory = Path.Join(Path.GetTempPath(), Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(tempDirectory); + + var rootPath = Path.Join(tempDirectory, "root.yaml"); + var sharedPath = Path.Join(tempDirectory, "shared.yaml"); + + await File.WriteAllTextAsync(rootPath, + @"openapi: 3.1.0 +info: + title: T + version: 1.0.0 +paths: + /a: + get: + responses: + '200': + description: ok + content: + application/json: + schema: + type: object + properties: + meta: + $ref: './shared.yaml#/Leaf' +components: + schemas: + Leaf: + $ref: './shared.yaml#/Leaf' +"); + + await File.WriteAllTextAsync(sharedPath, + @"Leaf: + type: object + properties: + x: + type: string + y: + type: integer +"); + + try + { + var settings = new OpenApiReaderSettings + { + LoadExternalRefs = true, + BaseUrl = new Uri(rootPath), + }; + settings.AddYamlReader(); + + var result = await OpenApiDocument.LoadAsync(rootPath, settings); + var responseSchema = result.Document.Paths["/a"].Operations[HttpMethod.Get].Responses["200"].Content["application/json"].Schema; + var metaSchema = responseSchema.Properties["meta"]; + var leafSchema = result.Document.Components.Schemas["Leaf"]; + + Assert.NotNull(result.Document); + Assert.DoesNotContain(result.Diagnostic.Errors, error => error.Message.Contains("Circular reference detected while resolving schema", StringComparison.Ordinal)); + Assert.DoesNotContain(result.Diagnostic.Warnings, warning => warning.Message.Contains("Circular reference detected while resolving schema", StringComparison.Ordinal)); + Assert.IsType(metaSchema); + Assert.IsType(leafSchema); + } + finally + { + Directory.Delete(tempDirectory, true); + } + } + [Fact] public void ResolveSubSchema_ShouldTraverseKnownKeywords() {