Skip to content

Commit 2980d58

Browse files
committed
Merge branch 'main' into chore/main-to-feat-3-2
2 parents b310c6c + 717f154 commit 2980d58

23 files changed

+481
-59
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "2.3.2"
2+
".": "2.3.3"
33
}

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## [2.3.3](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.2...v2.3.3) (2025-10-02)
4+
5+
6+
### Bug Fixes
7+
8+
* typo in allowReserved property name for deserialization ([1633453](https://github.com/microsoft/OpenAPI.NET/commit/16334536dcb5182f26c0d58463bd15a124dd1505))
9+
* typo in allowReserved property name for deserialization ([f7e34be](https://github.com/microsoft/OpenAPI.NET/commit/f7e34be28566a4f714d43667f8c43be7159d27a2))
10+
311
## [2.3.2](https://github.com/microsoft/OpenAPI.NET/compare/v2.3.1...v2.3.2) (2025-09-19)
412

513

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<PackageProjectUrl>https://github.com/Microsoft/OpenAPI.NET</PackageProjectUrl>
1313
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
1414
<PackageTags>OpenAPI .NET</PackageTags>
15-
<Version>2.3.2</Version>
15+
<Version>2.3.3</Version>
1616
</PropertyGroup>
1717
<!-- https://github.com/clairernovotny/DeterministicBuilds#deterministic-builds -->
1818
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'">

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ internal static partial class OpenApiV3Deserializer
8787
"schema",
8888
(o, n, t) => o.Schema = LoadSchema(n, t)
8989
},
90+
{
91+
"content", (o, n, t) =>
92+
{
93+
o.Content = n.CreateMap(LoadMediaType, t);
94+
}
95+
},
9096
{
9197
"examples",
9298
(o, n, t) => o.Examples = n.CreateMap(LoadExample, t)
Lines changed: 52 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,74 @@
11
using System;
22

3-
namespace Microsoft.OpenApi.Reader.V31
3+
namespace Microsoft.OpenApi.Reader.V31;
4+
/// <summary>
5+
/// Class containing logic to deserialize Open API V31 document into
6+
/// runtime Open API object model.
7+
/// </summary>
8+
internal static partial class OpenApiV31Deserializer
49
{
5-
/// <summary>
6-
/// Class containing logic to deserialize Open API V31 document into
7-
/// runtime Open API object model.
8-
/// </summary>
9-
internal static partial class OpenApiV31Deserializer
10+
private static readonly FixedFieldMap<OpenApiEncoding> _encodingFixedFields = new()
1011
{
11-
private static readonly FixedFieldMap<OpenApiEncoding> _encodingFixedFields = new()
1212
{
13+
"contentType", (o, n, _) =>
1314
{
14-
"contentType", (o, n, _) =>
15-
{
16-
o.ContentType = n.GetScalarValue();
17-
}
18-
},
15+
o.ContentType = n.GetScalarValue();
16+
}
17+
},
18+
{
19+
"headers", (o, n, t) =>
1920
{
20-
"headers", (o, n, t) =>
21-
{
22-
o.Headers = n.CreateMap(LoadHeader, t);
23-
}
24-
},
21+
o.Headers = n.CreateMap(LoadHeader, t);
22+
}
23+
},
24+
{
25+
"style", (o, n, _) =>
2526
{
26-
"style", (o, n, _) =>
27+
if(!n.GetScalarValue().TryGetEnumFromDisplayName<ParameterStyle>(n.Context, out var style))
2728
{
28-
if(!n.GetScalarValue().TryGetEnumFromDisplayName<ParameterStyle>(n.Context, out var style))
29-
{
30-
return;
31-
}
32-
o.Style = style;
29+
return;
3330
}
34-
},
31+
o.Style = style;
32+
}
33+
},
34+
{
35+
"explode", (o, n, _) =>
3536
{
36-
"explode", (o, n, _) =>
37+
var explode = n.GetScalarValue();
38+
if (explode is not null)
3739
{
38-
var explode = n.GetScalarValue();
39-
if (explode is not null)
40-
{
41-
o.Explode = bool.Parse(explode);
42-
}
43-
}
44-
},
40+
o.Explode = bool.Parse(explode);
41+
}
42+
}
43+
},
44+
{
45+
"allowReserved", (o, n, _) =>
4546
{
46-
"allowReserved", (o, n, _) =>
47+
var allowReserved = n.GetScalarValue();
48+
if (allowReserved is not null)
4749
{
48-
var allowReserved = n.GetScalarValue();
49-
if (allowReserved is not null)
50-
{
51-
o.AllowReserved = bool.Parse(allowReserved);
52-
}
50+
o.AllowReserved = bool.Parse(allowReserved);
5351
}
54-
},
55-
};
56-
57-
private static readonly PatternFieldMap<OpenApiEncoding> _encodingPatternFields =
58-
new()
59-
{
60-
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
61-
};
52+
}
53+
},
54+
};
6255

63-
public static OpenApiEncoding LoadEncoding(ParseNode node, OpenApiDocument hostDocument)
56+
private static readonly PatternFieldMap<OpenApiEncoding> _encodingPatternFields =
57+
new()
6458
{
65-
var mapNode = node.CheckMapNode("encoding");
59+
{s => s.StartsWith(OpenApiConstants.ExtensionFieldNamePrefix, StringComparison.OrdinalIgnoreCase), (o, p, n, _) => o.AddExtension(p, LoadExtension(p,n))}
60+
};
6661

67-
var encoding = new OpenApiEncoding();
68-
foreach (var property in mapNode)
69-
{
70-
property.ParseField(encoding, _encodingFixedFields, _encodingPatternFields, hostDocument);
71-
}
62+
public static OpenApiEncoding LoadEncoding(ParseNode node, OpenApiDocument hostDocument)
63+
{
64+
var mapNode = node.CheckMapNode("encoding");
7265

73-
return encoding;
66+
var encoding = new OpenApiEncoding();
67+
foreach (var property in mapNode)
68+
{
69+
property.ParseField(encoding, _encodingFixedFields, _encodingPatternFields, hostDocument);
7470
}
71+
72+
return encoding;
7573
}
7674
}

src/Microsoft.OpenApi/Reader/V31/OpenApiHeaderDeserializer.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ internal static partial class OpenApiV31Deserializer
8787
o.Schema = LoadSchema(n, t);
8888
}
8989
},
90+
{
91+
"content", (o, n, t) =>
92+
{
93+
o.Content = n.CreateMap(LoadMediaType, t);
94+
}
95+
},
9096
{
9197
"examples", (o, n, t) =>
9298
{

test/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
<ItemGroup>
1616
<PackageReference Include="coverlet.msbuild" Version="6.0.4" PrivateAssets="all" />
17-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
17+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
1818
<PackageReference Include="Moq" Version="4.20.72" />
1919
<PackageReference Include="xunit" Version="2.9.3" />
2020
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" PrivateAssets="all" />

test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<ItemGroup>
1818
<PackageReference Include="coverlet.collector" Version="6.0.4" PrivateAssets="all" />
1919
<PackageReference Include="coverlet.msbuild" Version="6.0.4" PrivateAssets="all" />
20-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
20+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.0" />
2121
<PackageReference Include="FluentAssertions" Version="7.2.0" />
2222
<PackageReference Include="system.text.json" Version="9.0.9" />
2323
<PackageReference Include="xunit" Version="2.9.3" />
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System.IO;
5+
using System.Threading.Tasks;
6+
using Microsoft.OpenApi.Reader;
7+
using Xunit;
8+
9+
namespace Microsoft.OpenApi.Readers.Tests.V31Tests
10+
{
11+
[Collection("DefaultSettings")]
12+
public class OpenApiEncodingTests
13+
{
14+
private const string SampleFolderPath = "V31Tests/Samples/OpenApiEncoding/";
15+
16+
[Fact]
17+
public async Task ParseEncodingWithAllowReservedShouldSucceed()
18+
{
19+
// Act
20+
var encoding = await OpenApiModelFactory.LoadAsync<OpenApiEncoding>(Path.Combine(SampleFolderPath, "encodingWithAllowReserved.yaml"), OpenApiSpecVersion.OpenApi3_1, new(), SettingsFixture.ReaderSettings);
21+
22+
// Assert
23+
Assert.Equivalent(
24+
new OpenApiEncoding
25+
{
26+
ContentType = "application/x-www-form-urlencoded",
27+
Style = ParameterStyle.Form,
28+
Explode = true,
29+
AllowReserved = true
30+
}, encoding);
31+
}
32+
}
33+
}
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Threading.Tasks;
7+
using FluentAssertions;
8+
using Microsoft.OpenApi.Reader;
9+
using Xunit;
10+
11+
namespace Microsoft.OpenApi.Readers.Tests.V31Tests
12+
{
13+
[Collection("DefaultSettings")]
14+
public class OpenApiHeaderTests
15+
{
16+
private const string SampleFolderPath = "V31Tests/Samples/OpenApiHeader/";
17+
18+
[Fact]
19+
public async Task ParseBasicHeaderShouldSucceed()
20+
{
21+
// Arrange
22+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicHeader.yaml"));
23+
24+
// Act
25+
var header = await OpenApiModelFactory.LoadAsync<OpenApiHeader>(stream, OpenApiSpecVersion.OpenApi3_1, new(), settings: SettingsFixture.ReaderSettings);
26+
27+
// Assert
28+
Assert.Equivalent(
29+
new OpenApiHeader
30+
{
31+
Description = "The number of allowed requests in the current period",
32+
Schema = new OpenApiSchema()
33+
{
34+
Type = JsonSchemaType.Integer
35+
}
36+
}, header);
37+
}
38+
39+
[Fact]
40+
public async Task ParseHeaderWithContentShouldSucceed()
41+
{
42+
// Arrange
43+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "headerWithContent.yaml"));
44+
45+
// Act
46+
var header = await OpenApiModelFactory.LoadAsync<OpenApiHeader>(stream, OpenApiSpecVersion.OpenApi3_1, new(), settings: SettingsFixture.ReaderSettings);
47+
48+
// Assert
49+
Assert.Equivalent(
50+
new OpenApiHeader
51+
{
52+
Description = "A complex header with content",
53+
Content = new Dictionary<string, OpenApiMediaType>()
54+
{
55+
["application/json"] = new()
56+
{
57+
Schema = new OpenApiSchema()
58+
{
59+
Type = JsonSchemaType.Object,
60+
Properties = new Dictionary<string, IOpenApiSchema>()
61+
{
62+
["timestamp"] = new OpenApiSchema()
63+
{
64+
Type = JsonSchemaType.String,
65+
Format = "date-time"
66+
},
67+
["value"] = new OpenApiSchema()
68+
{
69+
Type = JsonSchemaType.Integer
70+
}
71+
}
72+
}
73+
}
74+
}
75+
}, header);
76+
}
77+
78+
[Fact]
79+
public async Task ParseHeaderWithMultipleContentTypesShouldSucceed()
80+
{
81+
// Arrange
82+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "headerWithMultipleContentTypes.yaml"));
83+
84+
// Act
85+
var header = await OpenApiModelFactory.LoadAsync<OpenApiHeader>(stream, OpenApiSpecVersion.OpenApi3_1, new(), settings: SettingsFixture.ReaderSettings);
86+
87+
// Assert
88+
Assert.Equivalent(
89+
new OpenApiHeader
90+
{
91+
Description = "A header that accepts multiple content types",
92+
Content = new Dictionary<string, OpenApiMediaType>()
93+
{
94+
["application/json"] = new()
95+
{
96+
Schema = new OpenApiSchema()
97+
{
98+
Type = JsonSchemaType.Object,
99+
Properties = new Dictionary<string, IOpenApiSchema>()
100+
{
101+
["data"] = new OpenApiSchema()
102+
{
103+
Type = JsonSchemaType.String
104+
}
105+
}
106+
}
107+
},
108+
["text/plain"] = new()
109+
{
110+
Schema = new OpenApiSchema()
111+
{
112+
Type = JsonSchemaType.String
113+
}
114+
}
115+
}
116+
}, header);
117+
}
118+
119+
[Fact]
120+
public async Task ParseHeaderWithStyleAndContentShouldPreferContent()
121+
{
122+
// Arrange
123+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "headerWithStyleAndContent.yaml"));
124+
125+
// Act
126+
var header = await OpenApiModelFactory.LoadAsync<OpenApiHeader>(stream, OpenApiSpecVersion.OpenApi3_1, new(), settings: SettingsFixture.ReaderSettings);
127+
128+
// Assert
129+
// Both content and style can be present, content takes precedence for serialization behavior
130+
Assert.NotNull(header.Content);
131+
Assert.Single(header.Content);
132+
Assert.True(header.Content.ContainsKey("application/json"));
133+
Assert.Equal(ParameterStyle.Simple, header.Style); // Style can still be present
134+
}
135+
}
136+
}

0 commit comments

Comments
 (0)