Skip to content

Commit 4a8991c

Browse files
Produces property parsing fixed
1 parent c0f2f25 commit 4a8991c

File tree

11 files changed

+364
-107
lines changed

11 files changed

+364
-107
lines changed

src/Microsoft.OpenApi.Readers/ParsingContext.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ public class ParsingContext
2323
{
2424
private readonly Stack<string> _currentLocation = new Stack<string>();
2525
private readonly Dictionary<string, object> _tempStorage = new Dictionary<string, object>();
26+
private readonly Dictionary<object, Dictionary<string, object>> _scopedTempStorage = new Dictionary<object, Dictionary<string, object>>();
2627
private IOpenApiVersionService _versionService;
27-
private readonly Dictionary<string, Stack<string>> _loopStacks = new Dictionary<string, Stack<string>>();
28-
internal Dictionary<string, Func<IOpenApiAny, OpenApiSpecVersion, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, OpenApiSpecVersion, IOpenApiExtension>>();
28+
private readonly Dictionary<string, Stack<string>> _loopStacks = new Dictionary<string, Stack<string>>();
29+
internal Dictionary<string, Func<IOpenApiAny, OpenApiSpecVersion, IOpenApiExtension>> ExtensionParsers { get; set; } = new Dictionary<string, Func<IOpenApiAny, OpenApiSpecVersion, IOpenApiExtension>>();
2930
internal RootNode RootNode { get; set; }
3031
internal List<OpenApiTag> Tags { get; private set; } = new List<OpenApiTag>();
3132
internal Uri BaseUrl { get; set; }
@@ -72,7 +73,7 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diag
7273
/// <param name="version">OpenAPI version of the fragment</param>
7374
/// <param name="diagnostic">Diagnostic object which will return diagnostic results of the operation.</param>
7475
/// <returns>An OpenApiDocument populated based on the passed yamlDocument </returns>
75-
internal T ParseFragment<T>(YamlDocument yamlDocument, OpenApiSpecVersion version, OpenApiDiagnostic diagnostic) where T: IOpenApiElement
76+
internal T ParseFragment<T>(YamlDocument yamlDocument, OpenApiSpecVersion version, OpenApiDiagnostic diagnostic) where T : IOpenApiElement
7677
{
7778
var node = ParseNode.Create(this, diagnostic, yamlDocument.RootNode);
7879

@@ -145,22 +146,46 @@ public string GetLocation()
145146
/// <summary>
146147
/// Gets the value from the temporary storage matching the given key.
147148
/// </summary>
148-
public T GetFromTempStorage<T>(string key) where T : class
149+
public T GetFromTempStorage<T>(string key, object scope = null) where T : class
149150
{
150-
if (_tempStorage.TryGetValue(key, out var value))
151+
Dictionary<string, object> storage;
152+
153+
if (scope == null)
154+
{
155+
storage = _tempStorage;
156+
}
157+
else if (!_scopedTempStorage.TryGetValue(scope, out storage))
151158
{
152-
return (T)value;
159+
return null;
153160
}
154161

155-
return null;
162+
return storage.TryGetValue(key, out var value) ? (T)value : null;
156163
}
157164

158165
/// <summary>
159166
/// Sets the temporary storge for this key and value.
160167
/// </summary>
161-
public void SetTempStorage(string key, object value)
168+
public void SetTempStorage(string key, object value, object scope = null)
162169
{
163-
_tempStorage[key] = value;
170+
Dictionary<string, object> storage;
171+
172+
if (scope == null)
173+
{
174+
storage = _tempStorage;
175+
}
176+
else if (!_scopedTempStorage.TryGetValue(scope, out storage))
177+
{
178+
storage = _scopedTempStorage[scope] = new Dictionary<string, object>();
179+
}
180+
181+
if (value == null)
182+
{
183+
storage.Remove(key);
184+
}
185+
else
186+
{
187+
storage[key] = value;
188+
}
164189
}
165190

166191
/// <summary>
@@ -190,7 +215,8 @@ public bool PushLoop(string loopId, string key)
190215
{
191216
stack.Push(key);
192217
return true;
193-
} else
218+
}
219+
else
194220
{
195221
return false; // Loop detected
196222
}

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

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System.Globalization;
77
using System.Linq;
88
using Microsoft.OpenApi.Extensions;
9-
using Microsoft.OpenApi.Interfaces;
109
using Microsoft.OpenApi.Models;
1110
using Microsoft.OpenApi.Readers.ParseNodes;
1211
using Microsoft.OpenApi.Services;
@@ -71,12 +70,12 @@ internal static partial class OpenApiV2Deserializer
7170
ReferenceType.Parameter,
7271
LoadParameter);
7372

74-
o.Components.RequestBodies = n.CreateMapWithReference(ReferenceType.RequestBody, p =>
73+
o.Components.RequestBodies = n.CreateMapWithReference(ReferenceType.RequestBody, p =>
7574
{
7675
var parameter = LoadParameter(p, loadRequestBody: true);
7776
if (parameter != null)
7877
{
79-
return CreateRequestBody(n.Context, parameter);
78+
return CreateRequestBody(n.Context, parameter);
8079
}
8180

8281
return null;
@@ -141,7 +140,7 @@ private static void MakeServers(IList<OpenApiServer> servers, ParsingContext con
141140
rootNode.Diagnostic.Errors.Add(new OpenApiError(rootNode.Context.GetLocation(), "Invalid host"));
142141
return;
143142
}
144-
143+
145144
// Fill in missing information based on the defaultUrl
146145
if (defaultUrl != null)
147146
{
@@ -153,7 +152,7 @@ private static void MakeServers(IList<OpenApiServer> servers, ParsingContext con
153152
{
154153
return; // Can't make a server object out of just a Scheme
155154
}
156-
155+
157156
// Create the Server objects
158157
if (schemes != null && schemes.Count > 0)
159158
{
@@ -227,6 +226,17 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
227226

228227
ParseMap(openApiNode, openApidoc, _openApiFixedFields, _openApiPatternFields);
229228

229+
if (openApidoc.Paths != null)
230+
{
231+
ProcessResponsesMediaTypes(
232+
rootNode.GetMap(),
233+
openApidoc.Paths.Values
234+
.SelectMany(path => path.Operations?.Values ?? Enumerable.Empty<OpenApiOperation>())
235+
.SelectMany(operation => operation.Responses?.Values ?? Enumerable.Empty<OpenApiResponse>()),
236+
openApiNode.Context);
237+
}
238+
239+
ProcessResponsesMediaTypes(rootNode.GetMap(), openApidoc.Components?.Responses?.Values, openApiNode.Context);
230240

231241
// Post Process OpenApi Object
232242
if (openApidoc.Servers == null)
@@ -240,6 +250,25 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
240250
return openApidoc;
241251
}
242252

253+
private static void ProcessResponsesMediaTypes(MapNode mapNode, IEnumerable<OpenApiResponse> responses, ParsingContext context)
254+
{
255+
if (responses != null)
256+
{
257+
foreach (var response in responses)
258+
{
259+
ProcessProduces(mapNode, response, context);
260+
261+
if (response.Content != null)
262+
{
263+
foreach (var mediaType in response.Content.Values)
264+
{
265+
ProcessAnyFields(mapNode, mediaType, _mediaTypeAnyFields);
266+
}
267+
}
268+
}
269+
}
270+
}
271+
243272
private static void FixRequestBodyReferences(OpenApiDocument doc)
244273
{
245274
// Walk all unresolved parameter references
@@ -259,7 +288,7 @@ private static bool IsHostValid(string host)
259288
{
260289
return false;
261290
}
262-
291+
263292
//Check if the host (excluding port number) is a valid dns/ip address.
264293
var hostPart = host.Split(':').First();
265294
return Uri.CheckHostName(hostPart) != UriHostNameType.Unknown;
@@ -293,6 +322,6 @@ public override void Visit(OpenApiOperation operation)
293322
}
294323
}
295324

296-
325+
297326
}
298327
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ internal static OpenApiOperation LoadOperation(ParseNode node)
131131
}
132132
}
133133

134+
foreach (var response in operation.Responses.Values)
135+
{
136+
ProcessProduces(node.CheckMapNode("responses"), response, node.Context);
137+
}
138+
134139
return operation;
135140
}
136141

src/Microsoft.OpenApi.Readers/V2/OpenApiResponseDeserializer.cs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ internal static partial class OpenApiV2Deserializer
3737
{
3838
"schema", (o, n) =>
3939
{
40-
n.Context.SetTempStorage(TempStorageKeys.ResponseSchema, LoadSchema(n));
40+
n.Context.SetTempStorage(TempStorageKeys.ResponseSchema, LoadSchema(n), o);
4141
}
4242
},
4343
};
@@ -60,7 +60,7 @@ internal static partial class OpenApiV2Deserializer
6060
}
6161
};
6262

63-
private static void ProcessProduces(OpenApiResponse response, ParsingContext context)
63+
private static void ProcessProduces(MapNode mapNode, OpenApiResponse response, ParsingContext context)
6464
{
6565
var produces = context.GetFromTempStorage<List<string>>(TempStorageKeys.OperationProduces) ??
6666
context.GetFromTempStorage<List<string>>(TempStorageKeys.GlobalProduces) ?? new List<string>();
@@ -72,11 +72,16 @@ private static void ProcessProduces(OpenApiResponse response, ParsingContext con
7272

7373
foreach (var produce in produces)
7474
{
75-
var schema = context.GetFromTempStorage<OpenApiSchema>(TempStorageKeys.ResponseSchema);
75+
var schema = context.GetFromTempStorage<OpenApiSchema>(TempStorageKeys.ResponseSchema, response);
76+
context.SetTempStorage(TempStorageKeys.ResponseSchema, null, response);
7677

7778
if (response.Content.ContainsKey(produce) && response.Content[produce] != null)
7879
{
79-
response.Content[produce].Schema = schema;
80+
if (schema != null)
81+
{
82+
response.Content[produce].Schema = schema;
83+
ProcessAnyFields(mapNode, response.Content[produce], _mediaTypeAnyFields);
84+
}
8085
}
8186
else
8287
{
@@ -124,8 +129,6 @@ private static void LoadExample(OpenApiResponse response, string mediaType, Pars
124129

125130
public static OpenApiResponse LoadResponse(ParseNode node)
126131
{
127-
node.Context.SetTempStorage(TempStorageKeys.ResponseSchema, null);
128-
129132
var mapNode = node.CheckMapNode("response");
130133

131134
var pointer = mapNode.GetReferencePointer();
@@ -140,11 +143,12 @@ public static OpenApiResponse LoadResponse(ParseNode node)
140143
property.ParseField(response, _responseFixedFields, _responsePatternFields);
141144
}
142145

143-
ProcessProduces(response, node.Context);
144-
145146
foreach (var mediaType in response.Content.Values)
146147
{
147-
ProcessAnyFields(mapNode, mediaType, _mediaTypeAnyFields);
148+
if (mediaType.Schema != null)
149+
{
150+
ProcessAnyFields(mapNode, mediaType, _mediaTypeAnyFields);
151+
}
148152
}
149153

150154
return response;

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<SignAssembly>true</SignAssembly>
1313
<AssemblyOriginatorKeyFile>..\..\src\Microsoft.OpenApi.snk</AssemblyOriginatorKeyFile>
1414
</PropertyGroup>
15+
<ItemGroup>
16+
<None Remove="V2Tests\Samples\OpenApiOperation\operationWithResponseExamples.yaml" />
17+
<None Remove="V2Tests\Samples\twoResponses.json" />
18+
</ItemGroup>
1519
<ItemGroup>
1620
<EmbeddedResource Include="OpenApiReaderTests\Samples\unsupported.v1.yaml">
1721
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
@@ -40,6 +44,7 @@
4044
<EmbeddedResource Include="V2Tests\Samples\OpenApiOperation\operationWithFormData.yaml">
4145
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
4246
</EmbeddedResource>
47+
<EmbeddedResource Include="V2Tests\Samples\OpenApiOperation\operationWithResponseExamples.yaml" />
4348
<EmbeddedResource Include="V2Tests\Samples\OpenApiParameter\bodyParameter.yaml">
4449
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
4550
</EmbeddedResource>
@@ -76,7 +81,6 @@
7681
<EmbeddedResource Include="V2Tests\Samples\OpenApiPathItem\basicPathItemWithFormData.yaml">
7782
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
7883
</EmbeddedResource>
79-
<EmbeddedResource Include="V2Tests\Samples\OpenApiResponse\responseWithExamples.yaml" />
8084
<EmbeddedResource Include="V2Tests\Samples\OpenApiHeader\headerWithDefault.yaml" />
8185
<EmbeddedResource Include="V2Tests\Samples\OpenApiHeader\headerWithEnum.yaml" />
8286
<EmbeddedResource Include="V2Tests\Samples\OpenApiSchema\schemaWithDefault.yaml" />
@@ -94,6 +98,9 @@
9498
<EmbeddedResource Include="V2Tests\Samples\OpenApiSecurityScheme\oauth2PasswordSecurityScheme.yaml">
9599
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
96100
</EmbeddedResource>
101+
<EmbeddedResource Include="V2Tests\Samples\twoResponses.json">
102+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
103+
</EmbeddedResource>
97104
<EmbeddedResource Include="V3Tests\Samples\OpenApiCallback\multipleCallbacksWithReference.yaml">
98105
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
99106
</EmbeddedResource>

0 commit comments

Comments
 (0)