Skip to content

Commit 9d5787e

Browse files
committed
Merge branch 'main' into fix/static-factories
2 parents 9386fae + 1be8edc commit 9d5787e

26 files changed

+328
-81
lines changed

.github/workflows/docker.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ jobs:
4040
run: echo "date=$(date +'%Y%m%d')" >> $GITHUB_OUTPUT
4141
- name: Push to registry - Nightly
4242
if: contains(github.ref, env.PREVIEW_BRANCH)
43-
uses: docker/build-push-action@v6.13.0
43+
uses: docker/build-push-action@v6.14.0
4444
with:
4545
push: true
4646
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.getversion.outputs.version }}-preview.${{ steps.date.outputs.date }}${{ steps.runnumber.outputs.runnumber }}
4747
build-args: |
4848
version_suffix=preview.${{ steps.date.outputs.date }}${{ steps.runnumber.outputs.runnumber }}
4949
- name: Push to registry - Release
5050
if: contains(github.ref, 'refs/tags/v')
51-
uses: docker/build-push-action@v6.13.0
51+
uses: docker/build-push-action@v6.14.0
5252
with:
5353
push: true
5454
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.getversion.outputs.version }}

src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
</PackageReference>
3939
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
4040
<PackageReference Include="Microsoft.OData.Edm" Version="8.2.3" />
41-
<PackageReference Include="Microsoft.OpenApi.OData" Version="2.0.0-preview8" />
41+
<PackageReference Include="Microsoft.OpenApi.OData" Version="2.0.0-preview9" />
4242
<PackageReference Include="Microsoft.OpenApi.ApiManifest" Version="2.0.0-preview1" />
4343
<PackageReference Include="System.CommandLine.Hosting" Version="0.4.0-alpha.22272.1" />
4444
</ItemGroup>

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,27 @@ public void RegisterComponents()
7676
public IList<OpenApiSecurityRequirement>? SecurityRequirements { get; set; } =
7777
new List<OpenApiSecurityRequirement>();
7878

79+
private HashSet<OpenApiTag>? _tags;
7980
/// <summary>
8081
/// A list of tags used by the specification with additional metadata.
8182
/// </summary>
82-
public IList<OpenApiTag>? Tags { get; set; } = new List<OpenApiTag>();
83+
public ISet<OpenApiTag>? Tags
84+
{
85+
get
86+
{
87+
return _tags;
88+
}
89+
set
90+
{
91+
if (value is null)
92+
{
93+
return;
94+
}
95+
_tags = value is HashSet<OpenApiTag> tags && tags.Comparer is OpenApiTagComparer ?
96+
tags :
97+
new HashSet<OpenApiTag>(value, OpenApiTagComparer.Instance);
98+
}
99+
}
83100

84101
/// <summary>
85102
/// Additional external documentation.
@@ -123,7 +140,7 @@ public OpenApiDocument(OpenApiDocument? document)
123140
Webhooks = document?.Webhooks != null ? new Dictionary<string, IOpenApiPathItem>(document.Webhooks) : null;
124141
Components = document?.Components != null ? new(document?.Components) : null;
125142
SecurityRequirements = document?.SecurityRequirements != null ? new List<OpenApiSecurityRequirement>(document.SecurityRequirements) : null;
126-
Tags = document?.Tags != null ? new List<OpenApiTag>(document.Tags) : null;
143+
Tags = document?.Tags != null ? new HashSet<OpenApiTag>(document.Tags, OpenApiTagComparer.Instance) : null;
127144
ExternalDocs = document?.ExternalDocs != null ? new(document?.ExternalDocs) : null;
128145
Extensions = document?.Extensions != null ? new Dictionary<string, IOpenApiExtension>(document.Extensions) : null;
129146
Annotations = document?.Annotations != null ? new Dictionary<string, object>(document.Annotations) : null;

src/Microsoft.OpenApi/Models/OpenApiOperation.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,28 @@ public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible, IOpenA
2323
/// </summary>
2424
public const bool DeprecatedDefault = false;
2525

26+
private HashSet<OpenApiTagReference>? _tags;
2627
/// <summary>
2728
/// A list of tags for API documentation control.
2829
/// Tags can be used for logical grouping of operations by resources or any other qualifier.
2930
/// </summary>
30-
public IList<OpenApiTagReference>? Tags { get; set; } = [];
31+
public ISet<OpenApiTagReference>? Tags
32+
{
33+
get
34+
{
35+
return _tags;
36+
}
37+
set
38+
{
39+
if (value is null)
40+
{
41+
return;
42+
}
43+
_tags = value is HashSet<OpenApiTagReference> tags && tags.Comparer is OpenApiTagComparer ?
44+
tags :
45+
new HashSet<OpenApiTagReference>(value, OpenApiTagComparer.Instance);
46+
}
47+
}
3148

3249
/// <summary>
3350
/// A short summary of what the operation does.
@@ -123,7 +140,7 @@ public OpenApiOperation() { }
123140
public OpenApiOperation(OpenApiOperation operation)
124141
{
125142
Utils.CheckArgumentNull(operation);
126-
Tags = operation.Tags != null ? new List<OpenApiTagReference>(operation.Tags) : null;
143+
Tags = operation.Tags != null ? new HashSet<OpenApiTagReference>(operation.Tags) : null;
127144
Summary = operation.Summary ?? Summary;
128145
Description = operation.Description ?? Description;
129146
ExternalDocs = operation.ExternalDocs != null ? new(operation.ExternalDocs) : null;

src/Microsoft.OpenApi/Models/References/OpenApiTagReference.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public override OpenApiTag Target
2121
{
2222
get
2323
{
24-
return Reference.HostDocument?.Tags.FirstOrDefault(t => StringComparer.Ordinal.Equals(t.Name, Reference.Id));
24+
return Reference.HostDocument?.Tags?.FirstOrDefault(t => OpenApiTagComparer.StringComparer.Equals(t.Name, Reference.Id));
2525
}
2626
}
2727

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.OpenApi.Models.Interfaces;
4+
5+
namespace Microsoft.OpenApi;
6+
7+
#nullable enable
8+
/// <summary>
9+
/// This comparer is used to maintain a globally unique list of tags encountered
10+
/// in a particular OpenAPI document.
11+
/// </summary>
12+
internal sealed class OpenApiTagComparer : IEqualityComparer<IOpenApiTag>
13+
{
14+
private static readonly Lazy<OpenApiTagComparer> _lazyInstance = new(() => new OpenApiTagComparer());
15+
/// <summary>
16+
/// Default instance for the comparer.
17+
/// </summary>
18+
internal static OpenApiTagComparer Instance { get => _lazyInstance.Value; }
19+
20+
/// <inheritdoc/>
21+
public bool Equals(IOpenApiTag? x, IOpenApiTag? y)
22+
{
23+
if (x is null && y is null)
24+
{
25+
return true;
26+
}
27+
if (x is null || y is null)
28+
{
29+
return false;
30+
}
31+
if (ReferenceEquals(x, y))
32+
{
33+
return true;
34+
}
35+
return StringComparer.Equals(x.Name, y.Name);
36+
}
37+
38+
// Tag comparisons are case-sensitive by default. Although the OpenAPI specification
39+
// only outlines case sensitivity for property names, we extend this principle to
40+
// property values for tag names as well.
41+
// See https://spec.openapis.org/oas/v3.1.0#format.
42+
internal static readonly StringComparer StringComparer = StringComparer.Ordinal;
43+
44+
/// <inheritdoc/>
45+
public int GetHashCode(IOpenApiTag obj) => string.IsNullOrEmpty(obj?.Name) ? 0 : StringComparer.GetHashCode(obj!.Name);
46+
}
47+
#nullable restore

src/Microsoft.OpenApi/Reader/V2/OpenApiDocumentDeserializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ internal static partial class OpenApiV2Deserializer
104104
}
105105
},
106106
{"security", (o, n, _) => o.SecurityRequirements = n.CreateList(LoadSecurityRequirement, o)},
107-
{"tags", (o, n, _) => o.Tags = n.CreateList(LoadTag, o)},
107+
{"tags", (o, n, _) => { if (n.CreateList(LoadTag, o) is {Count:> 0} tags) {o.Tags = new HashSet<OpenApiTag>(tags, OpenApiTagComparer.Instance); } } },
108108
{"externalDocs", (o, n, _) => o.ExternalDocs = LoadExternalDocs(n, o)}
109109
};
110110

src/Microsoft.OpenApi/Reader/V2/OpenApiOperationDeserializer.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ internal static partial class OpenApiV2Deserializer
2323
new()
2424
{
2525
{
26-
"tags", (o, n, doc) => o.Tags = n.CreateSimpleList(
27-
(valueNode, doc) =>
28-
LoadTagByReference(
29-
valueNode.GetScalarValue(), doc), doc)
26+
"tags", (o, n, doc) => {
27+
if (n.CreateSimpleList((valueNode, doc) => LoadTagByReference(valueNode.GetScalarValue(), doc), doc) is {Count: > 0} tags)
28+
{
29+
o.Tags = new HashSet<OpenApiTagReference>(tags, OpenApiTagComparer.Instance);
30+
}
31+
}
3032
},
3133
{
3234
"summary",

src/Microsoft.OpenApi/Reader/V3/OpenApiDocumentDeserializer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using Microsoft.OpenApi.Extensions;
67
using Microsoft.OpenApi.Models;
78
using Microsoft.OpenApi.Reader.ParseNodes;
@@ -26,7 +27,7 @@ internal static partial class OpenApiV3Deserializer
2627
{"servers", (o, n, _) => o.Servers = n.CreateList(LoadServer, o)},
2728
{"paths", (o, n, _) => o.Paths = LoadPaths(n, o)},
2829
{"components", (o, n, _) => o.Components = LoadComponents(n, o)},
29-
{"tags", (o, n, _) => o.Tags = n.CreateList(LoadTag, o) },
30+
{"tags", (o, n, _) => { if (n.CreateList(LoadTag, o) is {Count:> 0} tags) {o.Tags = new HashSet<OpenApiTag>(tags, OpenApiTagComparer.Instance); } } },
3031
{"externalDocs", (o, n, _) => o.ExternalDocs = LoadExternalDocs(n, o)},
3132
{"security", (o, n, _) => o.SecurityRequirements = n.CreateList(LoadSecurityRequirement, o)}
3233
};

src/Microsoft.OpenApi/Reader/V3/OpenApiOperationDeserializer.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using Microsoft.OpenApi.Extensions;
67
using Microsoft.OpenApi.Models;
78
using Microsoft.OpenApi.Models.References;
@@ -19,10 +20,12 @@ internal static partial class OpenApiV3Deserializer
1920
new()
2021
{
2122
{
22-
"tags", (o, n, doc) => o.Tags = n.CreateSimpleList(
23-
(valueNode, doc) =>
24-
LoadTagByReference(
25-
valueNode.GetScalarValue(), doc), doc)
23+
"tags", (o, n, doc) => {
24+
if (n.CreateSimpleList((valueNode, doc) => LoadTagByReference(valueNode.GetScalarValue(), doc), doc) is {Count: > 0} tags)
25+
{
26+
o.Tags = new HashSet<OpenApiTagReference>(tags, OpenApiTagComparer.Instance);
27+
}
28+
}
2629
},
2730
{
2831
"summary",

0 commit comments

Comments
 (0)