diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index b2e92e560..e3a40ffb7 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -33,7 +33,7 @@ - + diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index 2231ad6f8..5f0cb79cc 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -23,7 +23,7 @@ true - + diff --git a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs index e01545c61..2f402b8cc 100644 --- a/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs +++ b/src/Microsoft.OpenApi/Models/References/BaseOpenApiReferenceHolder.cs @@ -63,7 +63,8 @@ protected BaseOpenApiReferenceHolder(string referenceId, OpenApiDocument hostDoc /// public virtual void SerializeAsV3(IOpenApiWriter writer) { - if (!writer.GetSettings().ShouldInlineReference(Reference)) + if (!writer.GetSettings().ShouldInlineReference(Reference) + || Reference.Type == ReferenceType.Tag) // tags are held as references need to drop in. { Reference.SerializeAsV3(writer); } diff --git a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs index 513355b50..753f2e9d9 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs +++ b/test/Microsoft.OpenApi.Hidi.Tests/Services/OpenApiFilterServiceTests.cs @@ -1,13 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System.Globalization; using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models.Interfaces; using Microsoft.OpenApi.Models.References; using Microsoft.OpenApi.Reader; using Microsoft.OpenApi.Services; using Microsoft.OpenApi.Tests.UtilityFiles; +using Microsoft.OpenApi.Writers; using Moq; using Xunit; @@ -235,7 +238,14 @@ public async Task CopiesOverAllReferencedComponentsToTheSubsetDocumentCorrectly( using var stream = File.OpenRead(filePath); var settings = new OpenApiReaderSettings(); settings.AddYamlReader(); - var doc = (await OpenApiDocument.LoadAsync(stream, "yaml", settings)).Document; + var doc = (await OpenApiDocument.LoadAsync(stream, "yaml", settings)).Document; + + // validated the tags are read as references + var openApiOperationTags = doc.Paths["/items"].Operations[OperationType.Get].Tags?.ToArray(); + Assert.NotNull(openApiOperationTags); + Assert.Single(openApiOperationTags); + Assert.True(openApiOperationTags[0].UnresolvedReference); + var predicate = OpenApiFilterService.CreatePredicate(operationIds: operationIds); var subsetOpenApiDocument = OpenApiFilterService.CreateFilteredDocument(doc, predicate); @@ -255,6 +265,26 @@ public async Task CopiesOverAllReferencedComponentsToTheSubsetDocumentCorrectly( Assert.Single(targetHeaders); Assert.NotNull(targetExamples); Assert.Single(targetExamples); + // validated the tags of the trimmed document are read as references + var trimmedOpenApiOperationTags = subsetOpenApiDocument.Paths["/items"].Operations[OperationType.Get].Tags?.ToArray(); + Assert.NotNull(trimmedOpenApiOperationTags); + Assert.Single(trimmedOpenApiOperationTags); + Assert.True(trimmedOpenApiOperationTags[0].UnresolvedReference); + + // Finally try to write the trimmed document as v3 document + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter) + { + Settings = new OpenApiWriterSettings() + { + InlineExternalReferences = true, + InlineLocalReferences = true + } + }; + subsetOpenApiDocument.SerializeAsV3(writer); + await writer.FlushAsync(); + var result = outputStringWriter.ToString(); + Assert.NotEmpty(result); } [Theory] diff --git a/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/docWithReusableHeadersAndExamples.yaml b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/docWithReusableHeadersAndExamples.yaml index 60ee7e5c8..8edeb1945 100644 --- a/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/docWithReusableHeadersAndExamples.yaml +++ b/test/Microsoft.OpenApi.Hidi.Tests/UtilityFiles/docWithReusableHeadersAndExamples.yaml @@ -7,6 +7,8 @@ servers: paths: /items: get: + tags: + - list.items operationId: getItems summary: Get a list of items responses: diff --git a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs index bed3500d3..d08ad76f9 100644 --- a/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs +++ b/test/Microsoft.OpenApi.Tests/Models/References/OpenApiTagReferenceTest.cs @@ -45,6 +45,7 @@ public class OpenApiTagReferenceTest description: The user was not found. tags: - $ref: '#/tags/user' + - users.user components: schemas: User: @@ -60,11 +61,13 @@ public class OpenApiTagReferenceTest "; readonly OpenApiTagReference _openApiTagReference; + readonly OpenApiTagReference _openApiTagReference2; public OpenApiTagReferenceTest() { var result = OpenApiDocument.Parse(OpenApi, "yaml", SettingsFixture.ReaderSettings); _openApiTagReference = new("user", result.Document); + _openApiTagReference2 = new("users.user", result.Document); } [Fact] @@ -73,6 +76,7 @@ public void TagReferenceResolutionWorks() // Assert Assert.Equal("user", _openApiTagReference.Name); Assert.Equal("Operations about users.", _openApiTagReference.Description); + Assert.True(_openApiTagReference2.UnresolvedReference);// the target is null } [Theory] @@ -108,5 +112,39 @@ public async Task SerializeTagReferenceAsV31JsonWorks(bool produceTerseOutput) // Assert await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeTagAsV3JsonWorks(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = produceTerseOutput }); + + // Act + _openApiTagReference2.SerializeAsV3(writer); + await writer.FlushAsync(); + + // Assert + Assert.Equal("\"users.user\"", outputStringWriter.ToString()); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task SerializeTagAsV31JsonWorks(bool produceTerseOutput) + { + // Arrange + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var writer = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = produceTerseOutput }); + + // Act + _openApiTagReference2.SerializeAsV31(writer); + await writer.FlushAsync(); + + // Assert + Assert.Equal("\"users.user\"", outputStringWriter.ToString()); + } } }