Skip to content

Commit 24be792

Browse files
Merge pull request #2 from Microsoft/master
sync
2 parents 76bdb25 + 3cdbb17 commit 24be792

27 files changed

+1085
-131
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
![Category overview screenshot](docs/images/oainet.png "Microsoft + OpenAPI = Love")
22

3-
# OpenAPI.NET
3+
# OpenAPI.NET [![nuget](https://img.shields.io/nuget/v/Microsoft.OpenApi.svg)](https://www.nuget.org/packages/Microsoft.OpenApi/)
44

55
The **OpenAPI.NET** SDK contains a useful object model for OpenAPI documents in .NET along with common serializers to extract raw OpenAPI JSON and YAML documents from the model.
66

src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<Company>Microsoft</Company>
1111
<Title>Microsoft.OpenApi.Readers</Title>
1212
<PackageId>Microsoft.OpenApi.Readers</PackageId>
13-
<Version>1.1.1</Version>
13+
<Version>1.1.2</Version>
1414
<Description>OpenAPI.NET Readers for JSON and YAML documents</Description>
1515
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
1616
<PackageTags>OpenAPI .NET</PackageTags>

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: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
using System;
54
using System.Collections.Generic;
65
using System.Linq;
76
using Microsoft.OpenApi.Extensions;
@@ -134,7 +133,7 @@ internal static OpenApiOperation LoadOperation(ParseNode node)
134133

135134
foreach (var response in operation.Responses.Values)
136135
{
137-
ProcessProduces(response, node.Context);
136+
ProcessProduces(node.CheckMapNode("responses"), response, node.Context);
138137
}
139138

140139
return operation;
@@ -168,10 +167,10 @@ private static OpenApiRequestBody CreateFormBody(ParsingContext context, List<Op
168167
Required = new HashSet<string>(formParameters.Where(p => p.Required).Select(p => p.Name))
169168
}
170169
};
171-
170+
172171
var consumes = context.GetFromTempStorage<List<string>>(TempStorageKeys.OperationConsumes) ??
173172
context.GetFromTempStorage<List<string>>(TempStorageKeys.GlobalConsumes) ??
174-
new List<string> {"application/x-www-form-urlencoded"};
173+
new List<string> { "application/x-www-form-urlencoded" };
175174

176175
var formBody = new OpenApiRequestBody
177176
{
@@ -189,7 +188,7 @@ internal static OpenApiRequestBody CreateRequestBody(
189188
{
190189
var consumes = context.GetFromTempStorage<List<string>>(TempStorageKeys.OperationConsumes) ??
191190
context.GetFromTempStorage<List<string>>(TempStorageKeys.GlobalConsumes) ??
192-
new List<string> {"application/json"};
191+
new List<string> { "application/json" };
193192

194193
var requestBody = new OpenApiRequestBody
195194
{

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,13 @@ internal static partial class OpenApiV2Deserializer
149149
{
150150
OpenApiConstants.Default,
151151
new AnyFieldMapParameter<OpenApiParameter>(
152-
p => p.Schema.Default,
153-
(p, v) => p.Schema.Default = v,
152+
p => p.Schema?.Default,
153+
(p, v) => {
154+
if (p.Schema != null || v != null)
155+
{
156+
GetOrCreateSchema(p).Default = v;
157+
}
158+
},
154159
p => p.Schema)
155160
}
156161
};
@@ -161,8 +166,13 @@ internal static partial class OpenApiV2Deserializer
161166
{
162167
OpenApiConstants.Enum,
163168
new AnyListFieldMapParameter<OpenApiParameter>(
164-
p => p.Schema.Enum,
165-
(p, v) => p.Schema.Enum = v,
169+
p => p.Schema?.Enum,
170+
(p, v) => {
171+
if (p.Schema != null || v != null && v.Count > 0)
172+
{
173+
GetOrCreateSchema(p).Enum = v;
174+
}
175+
},
166176
p => p.Schema)
167177
},
168178
};
@@ -285,7 +295,7 @@ public static OpenApiParameter LoadParameter(ParseNode node, bool loadRequestBod
285295
return null; // Don't include Form or Body parameters when normal parameters are loaded.
286296
}
287297

288-
if ( loadRequestBody && !_isBodyOrFormData )
298+
if (loadRequestBody && !_isBodyOrFormData)
289299
{
290300
return null; // Don't include non-Body or non-Form parameters when request bodies are loaded.
291301
}

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;

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,16 @@ private static void ProcessAnyListFields<T>(
7878

7979
mapNode.Context.StartObject(anyListFieldName);
8080

81-
foreach (var propertyElement in anyListFieldMap[anyListFieldName].PropertyGetter(domainObject))
81+
var list = anyListFieldMap[anyListFieldName].PropertyGetter(domainObject);
82+
if (list != null)
8283
{
83-
newProperty.Add(
84-
OpenApiAnyConverter.GetSpecificOpenApiAny(
85-
propertyElement,
86-
anyListFieldMap[anyListFieldName].SchemaGetter(domainObject)));
84+
foreach (var propertyElement in list)
85+
{
86+
newProperty.Add(
87+
OpenApiAnyConverter.GetSpecificOpenApiAny(
88+
propertyElement,
89+
anyListFieldMap[anyListFieldName].SchemaGetter(domainObject)));
90+
}
8791
}
8892

8993
anyListFieldMap[anyListFieldName].PropertySetter(domainObject, newProperty);

0 commit comments

Comments
 (0)