Skip to content

Commit e256d29

Browse files
committed
fix: missing deserialization for header content property in 3.0
1 parent b5bbecc commit e256d29

File tree

6 files changed

+212
-0
lines changed

6 files changed

+212
-0
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ internal static partial class OpenApiV3Deserializer
4343
"explode",
4444
(o, n) => o.Explode = bool.Parse(n.GetScalarValue())
4545
},
46+
{
47+
"content",
48+
(o, n) => o.Content = n.CreateMap(LoadMediaType)
49+
},
4650
{
4751
"schema",
4852
(o, n) => o.Schema = LoadSchema(n)
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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.Linq;
7+
using System.Threading.Tasks;
8+
using FluentAssertions;
9+
using Microsoft.OpenApi.Models;
10+
using Microsoft.OpenApi.Readers.ParseNodes;
11+
using Microsoft.OpenApi.Readers.V3;
12+
using SharpYaml.Serialization;
13+
using Xunit;
14+
15+
namespace Microsoft.OpenApi.Readers.Tests.V3Tests
16+
{
17+
[Collection("DefaultSettings")]
18+
public class OpenApiHeaderTests
19+
{
20+
private const string SampleFolderPath = "V3Tests/Samples/OpenApiHeader/";
21+
22+
[Fact]
23+
public void ParseBasicHeaderShouldSucceed()
24+
{
25+
// Arrange
26+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicHeader.yaml"));
27+
var yamlStream = new YamlStream();
28+
yamlStream.Load(new StreamReader(stream));
29+
var yamlNode = yamlStream.Documents.First().RootNode;
30+
31+
var diagnostic = new OpenApiDiagnostic();
32+
var context = new ParsingContext(diagnostic);
33+
34+
var node = new MapNode(context, (YamlMappingNode)yamlNode);
35+
36+
// Act
37+
var header = OpenApiV3Deserializer.LoadHeader(node);
38+
39+
// Assert
40+
Assert.Equivalent(
41+
new OpenApiHeader
42+
{
43+
Description = "The number of allowed requests in the current period",
44+
Schema = new OpenApiSchema()
45+
{
46+
Type = "integer"
47+
}
48+
}, header);
49+
}
50+
51+
[Fact]
52+
public void ParseHeaderWithContentShouldSucceed()
53+
{
54+
// Arrange
55+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "headerWithContent.yaml"));
56+
var yamlStream = new YamlStream();
57+
yamlStream.Load(new StreamReader(stream));
58+
var yamlNode = yamlStream.Documents.First().RootNode;
59+
60+
var diagnostic = new OpenApiDiagnostic();
61+
var context = new ParsingContext(diagnostic);
62+
63+
var node = new MapNode(context, (YamlMappingNode)yamlNode);
64+
65+
// Act
66+
var header = OpenApiV3Deserializer.LoadHeader(node);
67+
68+
// Assert
69+
Assert.Equivalent(
70+
new OpenApiHeader
71+
{
72+
Description = "A complex header with content",
73+
Content = new Dictionary<string, OpenApiMediaType>()
74+
{
75+
["application/json"] = new()
76+
{
77+
Schema = new OpenApiSchema()
78+
{
79+
Type = "object",
80+
Properties = new Dictionary<string, OpenApiSchema>()
81+
{
82+
["timestamp"] = new OpenApiSchema()
83+
{
84+
Type = "string",
85+
Format = "date-time"
86+
},
87+
["value"] = new OpenApiSchema()
88+
{
89+
Type = "integer"
90+
}
91+
}
92+
}
93+
}
94+
}
95+
}, header);
96+
}
97+
98+
[Fact]
99+
public void ParseHeaderWithMultipleContentTypesShouldSucceed()
100+
{
101+
// Arrange
102+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "headerWithMultipleContentTypes.yaml"));
103+
var yamlStream = new YamlStream();
104+
yamlStream.Load(new StreamReader(stream));
105+
var yamlNode = yamlStream.Documents.First().RootNode;
106+
107+
var diagnostic = new OpenApiDiagnostic();
108+
var context = new ParsingContext(diagnostic);
109+
110+
var node = new MapNode(context, (YamlMappingNode)yamlNode);
111+
112+
// Act
113+
var header = OpenApiV3Deserializer.LoadHeader(node);
114+
115+
// Assert
116+
Assert.Equivalent(
117+
new OpenApiHeader
118+
{
119+
Description = "A header that accepts multiple content types",
120+
Content = new Dictionary<string, OpenApiMediaType>()
121+
{
122+
["application/json"] = new()
123+
{
124+
Schema = new OpenApiSchema()
125+
{
126+
Type = "object",
127+
Properties = new Dictionary<string, OpenApiSchema>()
128+
{
129+
["data"] = new OpenApiSchema()
130+
{
131+
Type = "string"
132+
}
133+
}
134+
}
135+
},
136+
["text/plain"] = new()
137+
{
138+
Schema = new OpenApiSchema()
139+
{
140+
Type = "string"
141+
}
142+
}
143+
}
144+
}, header);
145+
}
146+
147+
[Fact]
148+
public void ParseHeaderWithStyleAndContentShouldPreferContent()
149+
{
150+
// Arrange
151+
using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "headerWithStyleAndContent.yaml"));
152+
var yamlStream = new YamlStream();
153+
yamlStream.Load(new StreamReader(stream));
154+
var yamlNode = yamlStream.Documents.First().RootNode;
155+
156+
var diagnostic = new OpenApiDiagnostic();
157+
var context = new ParsingContext(diagnostic);
158+
159+
var node = new MapNode(context, (YamlMappingNode)yamlNode);
160+
161+
// Act
162+
var header = OpenApiV3Deserializer.LoadHeader(node);
163+
164+
// Assert
165+
// Both content and style can be present, content takes precedence for serialization behavior
166+
Assert.NotNull(header.Content);
167+
Assert.Single(header.Content);
168+
Assert.True(header.Content.ContainsKey("application/json"));
169+
Assert.Equal(ParameterStyle.Simple, header.Style); // Style can still be present
170+
}
171+
}
172+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
description: "The number of allowed requests in the current period"
2+
schema:
3+
type: integer
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
description: "A complex header with content"
2+
content:
3+
application/json:
4+
schema:
5+
type: object
6+
properties:
7+
timestamp:
8+
type: string
9+
format: date-time
10+
value:
11+
type: integer
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
description: "A header that accepts multiple content types"
2+
content:
3+
application/json:
4+
schema:
5+
type: object
6+
properties:
7+
data:
8+
type: string
9+
text/plain:
10+
schema:
11+
type: string
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
description: "A header with both style and content (content should take precedence)"
2+
style: simple
3+
schema:
4+
type: string
5+
content:
6+
application/json:
7+
schema:
8+
type: object
9+
properties:
10+
value:
11+
type: string

0 commit comments

Comments
 (0)