Skip to content

Commit a584014

Browse files
Copilotbaywet
andcommitted
Add itemEncoding and prefixEncoding properties, fix version checks for encoding serialization
Co-authored-by: baywet <[email protected]>
1 parent aebb9db commit a584014

File tree

6 files changed

+201
-2
lines changed

6 files changed

+201
-2
lines changed

src/Microsoft.OpenApi/Models/OpenApiEncoding.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ public class OpenApiEncoding : IOpenApiSerializable, IOpenApiExtensible
3434
/// </summary>
3535
public IDictionary<string, OpenApiEncoding>? Encoding { get; set; }
3636

37+
/// <summary>
38+
/// Encoding object for array items.
39+
/// </summary>
40+
public OpenApiEncoding? ItemEncoding { get; set; }
41+
42+
/// <summary>
43+
/// Encoding objects for tuple-style arrays.
44+
/// </summary>
45+
public IList<OpenApiEncoding>? PrefixEncoding { get; set; }
46+
3747
/// <summary>
3848
/// Describes how a specific property value will be serialized depending on its type.
3949
/// </summary>
@@ -78,6 +88,8 @@ public OpenApiEncoding(OpenApiEncoding encoding)
7888
ContentType = encoding?.ContentType ?? ContentType;
7989
Headers = encoding?.Headers != null ? new Dictionary<string, IOpenApiHeader>(encoding.Headers) : null;
8090
Encoding = encoding?.Encoding != null ? new Dictionary<string, OpenApiEncoding>(encoding.Encoding.ToDictionary(kvp => kvp.Key, kvp => new OpenApiEncoding(kvp.Value))) : null;
91+
ItemEncoding = encoding?.ItemEncoding != null ? new OpenApiEncoding(encoding.ItemEncoding) : null;
92+
PrefixEncoding = encoding?.PrefixEncoding != null ? new List<OpenApiEncoding>(encoding.PrefixEncoding.Select(e => new OpenApiEncoding(e))) : null;
8193
Style = encoding?.Style ?? Style;
8294
Explode = encoding?._explode;
8395
AllowReserved = encoding?.AllowReserved ?? AllowReserved;
@@ -127,8 +139,44 @@ private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version
127139
// headers
128140
writer.WriteOptionalMap(OpenApiConstants.Headers, Headers, callback);
129141

130-
// encoding
131-
writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, callback);
142+
// encoding - serialize as native field in v3.2+, as extension in earlier versions
143+
if (Encoding != null)
144+
{
145+
if (version >= OpenApiSpecVersion.OpenApi3_2)
146+
{
147+
writer.WriteOptionalMap(OpenApiConstants.Encoding, Encoding, callback);
148+
}
149+
else
150+
{
151+
writer.WriteOptionalMap(OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.Encoding, Encoding, callback);
152+
}
153+
}
154+
155+
// itemEncoding - serialize as native field in v3.2+, as extension in earlier versions
156+
if (ItemEncoding != null)
157+
{
158+
if (version >= OpenApiSpecVersion.OpenApi3_2)
159+
{
160+
writer.WriteOptionalObject(OpenApiConstants.ItemEncoding, ItemEncoding, callback);
161+
}
162+
else
163+
{
164+
writer.WriteOptionalObject(OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.ItemEncoding, ItemEncoding, callback);
165+
}
166+
}
167+
168+
// prefixEncoding - serialize as native field in v3.2+, as extension in earlier versions
169+
if (PrefixEncoding != null)
170+
{
171+
if (version >= OpenApiSpecVersion.OpenApi3_2)
172+
{
173+
writer.WriteOptionalCollection(OpenApiConstants.PrefixEncoding, PrefixEncoding, callback);
174+
}
175+
else
176+
{
177+
writer.WriteOptionalCollection(OpenApiConstants.ExtensionFieldNamePrefix + "oai-" + OpenApiConstants.PrefixEncoding, PrefixEncoding, callback);
178+
}
179+
}
132180

133181
// style
134182
writer.WriteProperty(OpenApiConstants.Style, Style?.GetDisplayName());

src/Microsoft.OpenApi/Reader/V32/OpenApiEncodingDeserializer.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ internal static partial class OpenApiV32Deserializer
2828
o.Encoding = n.CreateMap(LoadEncoding, t);
2929
}
3030
},
31+
{
32+
"itemEncoding", (o, n, t) =>
33+
{
34+
o.ItemEncoding = LoadEncoding(n, t);
35+
}
36+
},
37+
{
38+
"prefixEncoding", (o, n, t) =>
39+
{
40+
o.PrefixEncoding = n.CreateList(LoadEncoding, t);
41+
}
42+
},
3143
{
3244
"style", (o, n, _) =>
3345
{

test/Microsoft.OpenApi.Readers.Tests/V32Tests/OpenApiEncodingTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,25 @@ public async Task ParseEncodingWithNestedEncodingShouldSucceed()
5151
Assert.True(encoding.Encoding.ContainsKey("anotherField"));
5252
Assert.Equal("text/plain", encoding.Encoding["anotherField"].ContentType);
5353
}
54+
55+
[Fact]
56+
public async Task ParseEncodingWithItemAndPrefixEncodingShouldSucceed()
57+
{
58+
// Act
59+
var encoding = await OpenApiModelFactory.LoadAsync<OpenApiEncoding>(Path.Combine(SampleFolderPath, "encodingWithItemAndPrefixEncoding.yaml"), OpenApiSpecVersion.OpenApi3_2, new(), SettingsFixture.ReaderSettings);
60+
61+
// Assert
62+
Assert.NotNull(encoding);
63+
Assert.Equal("application/json", encoding.ContentType);
64+
Assert.NotNull(encoding.ItemEncoding);
65+
Assert.Equal("application/xml", encoding.ItemEncoding.ContentType);
66+
Assert.Equal(ParameterStyle.Form, encoding.ItemEncoding.Style);
67+
Assert.True(encoding.ItemEncoding.Explode);
68+
Assert.NotNull(encoding.PrefixEncoding);
69+
Assert.Equal(2, encoding.PrefixEncoding.Count);
70+
Assert.Equal("text/plain", encoding.PrefixEncoding[0].ContentType);
71+
Assert.Equal(ParameterStyle.Simple, encoding.PrefixEncoding[0].Style);
72+
Assert.Equal("application/octet-stream", encoding.PrefixEncoding[1].ContentType);
73+
}
5474
}
5575
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.2.0.md#encodingObject
2+
contentType: application/json
3+
itemEncoding:
4+
contentType: application/xml
5+
style: form
6+
explode: true
7+
prefixEncoding:
8+
- contentType: text/plain
9+
style: simple
10+
- contentType: application/octet-stream

test/Microsoft.OpenApi.Tests/Models/OpenApiEncodingTests.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,5 +218,112 @@ public async Task SerializeEncodingWithNestedEncodingAsV32YamlWorks()
218218
expected = expected.MakeLineBreaksEnvironmentNeutral();
219219
Assert.Equal(expected, actual);
220220
}
221+
222+
[Fact]
223+
public async Task SerializeEncodingWithItemAndPrefixEncodingAsV32JsonWorks()
224+
{
225+
// Arrange
226+
var encoding = new OpenApiEncoding
227+
{
228+
ContentType = "application/json",
229+
ItemEncoding = new OpenApiEncoding
230+
{
231+
ContentType = "application/xml",
232+
Style = ParameterStyle.Form,
233+
Explode = true
234+
},
235+
PrefixEncoding = new List<OpenApiEncoding>
236+
{
237+
new OpenApiEncoding
238+
{
239+
ContentType = "text/plain",
240+
Style = ParameterStyle.Simple
241+
},
242+
new OpenApiEncoding
243+
{
244+
ContentType = "application/octet-stream"
245+
}
246+
}
247+
};
248+
249+
var expected =
250+
"""
251+
{
252+
"contentType": "application/json",
253+
"itemEncoding": {
254+
"contentType": "application/xml",
255+
"style": "form",
256+
"explode": true
257+
},
258+
"prefixEncoding": [
259+
{
260+
"contentType": "text/plain",
261+
"style": "simple"
262+
},
263+
{
264+
"contentType": "application/octet-stream"
265+
}
266+
]
267+
}
268+
""";
269+
270+
// Act
271+
var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_2);
272+
273+
// Assert
274+
actual = actual.MakeLineBreaksEnvironmentNeutral();
275+
expected = expected.MakeLineBreaksEnvironmentNeutral();
276+
Assert.Equal(expected, actual);
277+
}
278+
279+
[Fact]
280+
public async Task SerializeEncodingAsV31ShouldUseExtensionsForNewFields()
281+
{
282+
// Arrange
283+
var encoding = new OpenApiEncoding
284+
{
285+
ContentType = "application/json",
286+
Encoding = new Dictionary<string, OpenApiEncoding>
287+
{
288+
["nested"] = new OpenApiEncoding { ContentType = "text/plain" }
289+
},
290+
ItemEncoding = new OpenApiEncoding
291+
{
292+
ContentType = "application/xml"
293+
},
294+
PrefixEncoding = new List<OpenApiEncoding>
295+
{
296+
new OpenApiEncoding { ContentType = "text/csv" }
297+
}
298+
};
299+
300+
var expected =
301+
"""
302+
{
303+
"contentType": "application/json",
304+
"x-oai-encoding": {
305+
"nested": {
306+
"contentType": "text/plain"
307+
}
308+
},
309+
"x-oai-itemEncoding": {
310+
"contentType": "application/xml"
311+
},
312+
"x-oai-prefixEncoding": [
313+
{
314+
"contentType": "text/csv"
315+
}
316+
]
317+
}
318+
""";
319+
320+
// Act
321+
var actual = await encoding.SerializeAsJsonAsync(OpenApiSpecVersion.OpenApi3_1);
322+
323+
// Assert
324+
actual = actual.MakeLineBreaksEnvironmentNeutral();
325+
expected = expected.MakeLineBreaksEnvironmentNeutral();
326+
Assert.Equal(expected, actual);
327+
}
221328
}
222329
}

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,8 @@ namespace Microsoft.OpenApi
683683
public bool? Explode { get; set; }
684684
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.IOpenApiExtension>? Extensions { get; set; }
685685
public System.Collections.Generic.IDictionary<string, Microsoft.OpenApi.IOpenApiHeader>? Headers { get; set; }
686+
public Microsoft.OpenApi.OpenApiEncoding? ItemEncoding { get; set; }
687+
public System.Collections.Generic.IList<Microsoft.OpenApi.OpenApiEncoding>? PrefixEncoding { get; set; }
686688
public Microsoft.OpenApi.ParameterStyle? Style { get; set; }
687689
public virtual void SerializeAsV2(Microsoft.OpenApi.IOpenApiWriter writer) { }
688690
public virtual void SerializeAsV3(Microsoft.OpenApi.IOpenApiWriter writer) { }

0 commit comments

Comments
 (0)