Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tags serialization bug #1939

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Readers/OpenApiYamlReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static JsonNode LoadJsonNodesFromYamlDocument(TextReader input)
{
var yamlStream = new YamlStream();
yamlStream.Load(input);
var yamlDocument = yamlStream.Documents.First();
var yamlDocument = yamlStream.Documents[0];
return yamlDocument.ToJsonNode();
}

Expand Down
50 changes: 8 additions & 42 deletions src/Microsoft.OpenApi/Models/OpenApiTag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,56 +55,31 @@
{
Name = tag?.Name ?? Name;
Description = tag?.Description ?? Description;
ExternalDocs = tag?.ExternalDocs != null ? new(tag?.ExternalDocs) : null;
ExternalDocs = tag?.ExternalDocs != null ? new(tag.ExternalDocs) : null;

Check warning

Code scanning / CodeQL

Virtual call in constructor or destructor Warning

Avoid virtual calls in a constructor or destructor.
Extensions = tag?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(tag.Extensions) : null;
UnresolvedReference = tag?.UnresolvedReference ?? UnresolvedReference;
Reference = tag?.Reference != null ? new(tag?.Reference) : null;
Reference = tag?.Reference != null ? new(tag.Reference) : null;
}

/// <summary>
/// Serialize <see cref="OpenApiTag"/> to Open Api v3.1
/// </summary>
public virtual void SerializeAsV31(IOpenApiWriter writer)
public virtual void SerializeAsV31(IOpenApiWriter writer)
{
SerializeInternal(writer, (writer, element) => element.SerializeAsV31(writer));
}

/// <summary>
/// Serialize <see cref="OpenApiTag"/> to Open Api v3.0
/// </summary>
public virtual void SerializeAsV3(IOpenApiWriter writer)
{
SerializeInternal(writer, (writer, element) => element.SerializeAsV3(writer));
}

/// <summary>
/// Serialize <see cref="OpenApiTag"/> to Open Api v3.0
/// </summary>
private void SerializeInternal(IOpenApiWriter writer, Action<IOpenApiWriter, IOpenApiSerializable> callback)
{
Utils.CheckArgumentNull(writer);
writer.WriteValue(Name);
}

/// <summary>
/// Serialize to OpenAPI V3 document without using reference.
/// </summary>
public virtual void SerializeAsV31WithoutReference(IOpenApiWriter writer)
{
SerializeInternalWithoutReference(writer, OpenApiSpecVersion.OpenApi3_1,
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1,
(writer, element) => element.SerializeAsV31(writer));
}

/// <summary>
/// Serialize to OpenAPI V3 document without using reference.
/// Serialize <see cref="OpenApiTag"/> to Open Api v3.0
/// </summary>
public virtual void SerializeAsV3WithoutReference(IOpenApiWriter writer)
public virtual void SerializeAsV3(IOpenApiWriter writer)
{
SerializeInternalWithoutReference(writer, OpenApiSpecVersion.OpenApi3_0,
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0,
(writer, element) => element.SerializeAsV3(writer));
}

internal virtual void SerializeInternalWithoutReference(IOpenApiWriter writer, OpenApiSpecVersion version,
internal virtual void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
Action<IOpenApiWriter, IOpenApiSerializable> callback)
{
writer.WriteStartObject();
Expand All @@ -128,15 +103,6 @@
/// Serialize <see cref="OpenApiTag"/> to Open Api v2.0
/// </summary>
public virtual void SerializeAsV2(IOpenApiWriter writer)
{
Utils.CheckArgumentNull(writer);
writer.WriteValue(Name);
}

/// <summary>
/// Serialize to OpenAPI V2 document without using reference.
/// </summary>
public void SerializeAsV2WithoutReference(IOpenApiWriter writer)
{
writer.WriteStartObject();

Expand Down
56 changes: 55 additions & 1 deletion test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -992,7 +992,7 @@ public OpenApiDocumentTests()
{
["my-extension"] = new OpenApiAny(4)
}
},
},
Extensions = new Dictionary<string, IOpenApiExtension>
{
["my-extension"] = new OpenApiAny(4),
Expand Down Expand Up @@ -2072,5 +2072,59 @@ public void SerializeDocWithDollarIdInDollarRefSucceeds()
var actual = doc.SerializeAsYaml(OpenApiSpecVersion.OpenApi3_1);
actual.MakeLineBreaksEnvironmentNeutral().Should().BeEquivalentTo(expected.MakeLineBreaksEnvironmentNeutral());
}

[Fact]
public void SerializeDocumentTagsWithMultipleExtensionsWorks()
{
var expected = @"{
""openapi"": ""3.0.4"",
""info"": {
""title"": ""Test"",
""version"": ""1.0.0""
},
""paths"": { },
""tags"": [
{
""name"": ""tag1"",
""x-tag1"": ""tag1""
},
{
""name"": ""tag2"",
""x-tag2"": ""tag2""
}
]
}";
var doc = new OpenApiDocument
{
Info = new OpenApiInfo
{
Title = "Test",
Version = "1.0.0"
},
Paths = new OpenApiPaths(),
Tags = new List<OpenApiTag>
{
new OpenApiTag
{
Name = "tag1",
Extensions = new Dictionary<string, IOpenApiExtension>
{
["x-tag1"] = new OpenApiAny("tag1")
}
},
new OpenApiTag
{
Name = "tag2",
Extensions = new Dictionary<string, IOpenApiExtension>
{
["x-tag2"] = new OpenApiAny("tag2")
}
}
}
};

var actual = doc.SerializeAsJson(OpenApiSpecVersion.OpenApi3_0);
actual.MakeLineBreaksEnvironmentNeutral().Should().BeEquivalentTo(expected.MakeLineBreaksEnvironmentNeutral());
}
}
}
7 changes: 0 additions & 7 deletions test/Microsoft.OpenApi.Tests/Models/OpenApiOperationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,6 @@ public class OpenApiOperationTests
{
Tags = new List<OpenApiTag>
{
new()
{
Name = "tagName1",
Description = "tagDescription1",
},
new OpenApiTagReference("tagId1", null)
},
Summary = "summary1",
Expand Down Expand Up @@ -360,7 +355,6 @@ public void SerializeAdvancedOperationWithTagAndSecurityAsV3JsonWorks()
"""
{
"tags": [
"tagName1",
"tagId1"
],
"summary": "summary1",
Expand Down Expand Up @@ -669,7 +663,6 @@ public void SerializeAdvancedOperationWithTagAndSecurityAsV2JsonWorks()
"""
{
"tags": [
"tagName1",
"tagId1"
],
"summary": "summary1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
"pet"
{
"name": "pet",
"description": "Pets operations",
"externalDocs": {
"description": "Find more info here",
"url": "https://example.com"
},
"x-tag-extension": null
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"pet"
{"name":"pet","description":"Pets operations","externalDocs":{"description":"Find more info here","url":"https://example.com"},"x-tag-extension":null}
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
"pet"
{
"name": "pet",
"description": "Pets operations",
"externalDocs": {
"description": "Find more info here",
"url": "https://example.com"
},
"x-tag-extension": null
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"pet"
{"name":"pet","description":"Pets operations","externalDocs":{"description":"Find more info here","url":"https://example.com"},"x-tag-extension":null}
81 changes: 22 additions & 59 deletions test/Microsoft.OpenApi.Tests/Models/OpenApiTagTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using FluentAssertions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Models.References;
using Microsoft.OpenApi.Writers;
using VerifyXunit;
using Xunit;
Expand All @@ -17,9 +18,9 @@ namespace Microsoft.OpenApi.Tests.Models
[Collection("DefaultSettings")]
public class OpenApiTagTests
{
public static OpenApiTag BasicTag = new();
public static readonly OpenApiTag BasicTag = new();

public static OpenApiTag AdvancedTag = new()
public static readonly OpenApiTag AdvancedTag = new()
{
Name = "pet",
Description = "Pets operations",
Expand All @@ -30,21 +31,7 @@ public class OpenApiTagTests
}
};

public static OpenApiTag ReferencedTag = new()
{
Name = "pet",
Description = "Pets operations",
ExternalDocs = OpenApiExternalDocsTests.AdvanceExDocs,
Extensions = new Dictionary<string, IOpenApiExtension>
{
{"x-tag-extension", null}
},
Reference = new()
{
Type = ReferenceType.Tag,
Id = "pet"
}
};
public static OpenApiTag ReferencedTag = new OpenApiTagReference("pet", null);

[Theory]
[InlineData(true)]
Expand All @@ -56,7 +43,7 @@ public async Task SerializeBasicTagAsV3JsonWithoutReferenceWorksAsync(bool produ
var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput });

// Act
BasicTag.SerializeAsV3WithoutReference(writer);
BasicTag.SerializeAsV3(writer);
writer.Flush();

// Assert
Expand All @@ -73,7 +60,7 @@ public async Task SerializeBasicTagAsV2JsonWithoutReferenceWorksAsync(bool produ
var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput });

// Act
BasicTag.SerializeAsV2WithoutReference(writer);
BasicTag.SerializeAsV2(writer);
writer.Flush();

// Assert
Expand All @@ -89,7 +76,7 @@ public void SerializeBasicTagAsV3YamlWithoutReferenceWorks()
var expected = "{ }";

// Act
BasicTag.SerializeAsV3WithoutReference(writer);
BasicTag.SerializeAsV3(writer);
var actual = outputStringWriter.GetStringBuilder().ToString();

// Assert
Expand All @@ -107,7 +94,7 @@ public void SerializeBasicTagAsV2YamlWithoutReferenceWorks()
var expected = "{ }";

// Act
BasicTag.SerializeAsV2WithoutReference(writer);
BasicTag.SerializeAsV2(writer);
writer.Flush();
var actual = outputStringWriter.GetStringBuilder().ToString();

Expand All @@ -117,40 +104,6 @@ public void SerializeBasicTagAsV2YamlWithoutReferenceWorks()
actual.Should().Be(expected);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task SerializeAdvancedTagAsV3JsonWithoutReferenceWorksAsync(bool produceTerseOutput)
{
// Arrange
var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput });

// Act
AdvancedTag.SerializeAsV3WithoutReference(writer);
writer.Flush();

// Assert
await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task SerializeAdvancedTagAsV2JsonWithoutReferenceWorksAsync(bool produceTerseOutput)
{
// Arrange
var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
var writer = new OpenApiJsonWriter(outputStringWriter, new() { Terse = produceTerseOutput });

// Act
AdvancedTag.SerializeAsV2WithoutReference(writer);
writer.Flush();

// Assert
await Verifier.Verify(outputStringWriter).UseParameters(produceTerseOutput);
}

[Fact]
public void SerializeAdvancedTagAsV3YamlWithoutReferenceWorks()
{
Expand All @@ -168,7 +121,7 @@ public void SerializeAdvancedTagAsV3YamlWithoutReferenceWorks()
""";

// Act
AdvancedTag.SerializeAsV3WithoutReference(writer);
AdvancedTag.SerializeAsV3(writer);
writer.Flush();
var actual = outputStringWriter.GetStringBuilder().ToString();

Expand All @@ -195,7 +148,7 @@ public void SerializeAdvancedTagAsV2YamlWithoutReferenceWorks()
""";

// Act
AdvancedTag.SerializeAsV2WithoutReference(writer);
AdvancedTag.SerializeAsV2(writer);
writer.Flush();
var actual = outputStringWriter.GetStringBuilder().ToString();

Expand Down Expand Up @@ -246,7 +199,12 @@ public void SerializeAdvancedTagAsV3YamlWorks()
var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
var writer = new OpenApiYamlWriter(outputStringWriter);

var expected = @" pet";
var expected = @"name: pet
description: Pets operations
externalDocs:
description: Find more info here
url: https://example.com
x-tag-extension:";

// Act
AdvancedTag.SerializeAsV3(writer);
Expand All @@ -266,7 +224,12 @@ public void SerializeAdvancedTagAsV2YamlWorks()
var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
var writer = new OpenApiYamlWriter(outputStringWriter);

var expected = @" pet";
var expected = @"name: pet
description: Pets operations
externalDocs:
description: Find more info here
url: https://example.com
x-tag-extension:";

// Act
AdvancedTag.SerializeAsV2(writer);
Expand Down
Loading
Loading