diff --git a/CHANGELOG.md b/CHANGELOG.md index c6ae3ebea..5fc036d34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,13 @@ * adds support for OpenAPI 3.2.0 ([765a8dd](https://github.com/microsoft/OpenAPI.NET/commit/765a8dd4d6efd1a31b6a76d282ccffa5877a845a)) +## [2.4.3](https://github.com/microsoft/OpenAPI.NET/compare/v2.4.2...v2.4.3) (2026-01-16) + +## Bug Fixes + +* Support custom tag ordering ([008576c](https://github.com/microsoft/OpenAPI.NET/commit/008576c31f8dcecf59363c9c2f85d691601faa73)) +* Support custom tag ordering ([7610d07](https://github.com/microsoft/OpenAPI.NET/commit/7610d07bd11a99a4ce1cc31338d180dbd764d952)) + ## [2.4.2](https://github.com/microsoft/OpenAPI.NET/compare/v2.4.1...v2.4.2) (2025-12-22) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index c7af543df..96973a701 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -3,6 +3,9 @@ using System; using System.Collections.Generic; +#if NET +using System.Collections.Immutable; +#endif using System.IO; using System.Linq; using System.Security.Cryptography; @@ -88,9 +91,15 @@ public ISet? Tags { return; } - _tags = value is HashSet tags && tags.Comparer is OpenApiTagComparer ? - tags : - new HashSet(value, OpenApiTagComparer.Instance); + _tags = value switch + { + HashSet tags when tags.Comparer != EqualityComparer.Default => value, + SortedSet => value, +#if NET + ImmutableSortedSet => value, +#endif + _ => new HashSet(value, OpenApiTagComparer.Instance), + }; } } diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs index f62407b7b..1acd8ce8c 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs @@ -3,8 +3,11 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; +using System.Linq; using System.Net.Http; using System.Threading.Tasks; using VerifyXunit; @@ -2126,6 +2129,79 @@ public void DeduplicatesTags() Assert.Contains(document.Tags, t => t.Name == "tag2"); } + [Fact] + public void TagsSupportsCustomComparer() + { + var document = new OpenApiDocument + { + Tags = new HashSet(new CaseInsensitiveOpenApiTagEqualityComparer()), + }; + + Assert.True(document.Tags.Add(new OpenApiTag { Name = "Tag1" })); + Assert.False(document.Tags.Add(new OpenApiTag { Name = "tag1" })); + Assert.True(document.Tags.Add(new OpenApiTag { Name = "tag2" })); + Assert.False(document.Tags.Add(new OpenApiTag { Name = "TAG1" })); + Assert.Equal(2, document.Tags.Count); + } + + private sealed class CaseInsensitiveOpenApiTagEqualityComparer : IEqualityComparer + { + public bool Equals(OpenApiTag x, OpenApiTag y) + { + return string.Equals(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); + } + + public int GetHashCode([DisallowNull] OpenApiTag obj) + { + return obj.Name.GetHashCode(StringComparison.OrdinalIgnoreCase); + } + } + + [Fact] + public void TagsSupportsSortedSets() + { + var document = new OpenApiDocument + { + Tags = new SortedSet(new DescendingOpenApiTagComparer()) + { + new OpenApiTag { Name = "tagB" }, + new OpenApiTag { Name = "tagA" }, + new OpenApiTag { Name = "tagC" }, + } + }; + + var names = document.Tags.Select(t => t.Name); + Assert.Equal(["tagC", "tagB", "tagA"], names); + Assert.IsType>(document.Tags); + } + + private sealed class DescendingOpenApiTagComparer : IComparer + { + public int Compare(OpenApiTag x, OpenApiTag y) + { + return string.Compare(y?.Name, x?.Name, StringComparison.Ordinal); + } + } + + [Fact] + public void TagsSupportsImmutableSortedSets() + { + var document = new OpenApiDocument + { + Tags = ImmutableSortedSet.Create( + new DescendingOpenApiTagComparer(), + [ + new OpenApiTag { Name = "tagB" }, + new OpenApiTag { Name = "tagA" }, + new OpenApiTag { Name = "tagC" }, + ]), + }; + + var names = document.Tags.Select(t => t.Name); + Assert.Equal(["tagC", "tagB", "tagA"], names); + Assert.IsType>(document.Tags); + } + public static TheoryData OpenApiSpecVersions() { var values = new TheoryData();