Skip to content

Commit 24bb7f4

Browse files
committed
Merge remote-tracking branch 'origin/vnext' into dm/openapiworkspace
2 parents 1e735f4 + c9d1bae commit 24bb7f4

File tree

12 files changed

+329
-35
lines changed

12 files changed

+329
-35
lines changed

src/Microsoft.OpenApi.Readers/Interface/IOpenApiVersionService.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ internal interface IOpenApiVersionService
2323
OpenApiReference ConvertToOpenApiReference(string reference, ReferenceType? type);
2424

2525
/// <summary>
26-
/// Function that converts a MapNode into a Tag object in a version specific way
26+
/// Loads an OpenAPI Element from a document fragment
2727
/// </summary>
28-
Func<MapNode, OpenApiTag> TagLoader { get; }
28+
/// <typeparam name="T">Type of element to load</typeparam>
29+
/// <param name="node">document fragment node</param>
30+
/// <returns>Instance of OpenAPIElement</returns>
31+
T LoadElement<T>(ParseNode node) where T : IOpenApiElement;
2932

3033
/// <summary>
3134
/// Converts a generic RootNode instance into a strongly typed OpenApiDocument

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.0.0</Version>
13+
<Version>1.1.0-preview.1</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/OpenApiStreamReader.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Threading.Tasks;
88
using Microsoft.OpenApi.Exceptions;
99
using Microsoft.OpenApi.Extensions;
10+
using Microsoft.OpenApi.Interfaces;
1011
using Microsoft.OpenApi.Models;
1112
using Microsoft.OpenApi.Readers.Interface;
1213
using Microsoft.OpenApi.Readers.Services;
@@ -157,6 +158,61 @@ private void ValidateDocument(OpenApiDiagnostic diagnostic, OpenApiDocument docu
157158
return document;
158159
}
159160

161+
/// <summary>
162+
/// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element.
163+
/// </summary>
164+
/// <param name="input">Stream containing OpenAPI description to parse.</param>
165+
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
166+
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
167+
/// <returns>Instance of newly created OpenApiDocument</returns>
168+
public T ReadFragment<T>(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement
169+
{
170+
ParsingContext context;
171+
YamlDocument yamlDocument;
172+
diagnostic = new OpenApiDiagnostic();
173+
174+
// Parse the YAML/JSON
175+
try
176+
{
177+
yamlDocument = LoadYamlDocument(input);
178+
}
179+
catch (SyntaxErrorException ex)
180+
{
181+
diagnostic.Errors.Add(new OpenApiReaderError(ex));
182+
return default(T);
183+
}
184+
185+
context = new ParsingContext
186+
{
187+
ExtensionParsers = _settings.ExtensionParsers
188+
};
189+
190+
IOpenApiElement element = null;
191+
192+
try
193+
{
194+
// Parse the OpenAPI element
195+
element = context.ParseFragment<T>(yamlDocument, version, diagnostic);
196+
}
197+
catch (OpenApiException ex)
198+
{
199+
diagnostic.Errors.Add(new OpenApiError(ex));
200+
}
201+
202+
// Validate the element
203+
if (_settings.RuleSet != null && _settings.RuleSet.Rules.Count > 0)
204+
{
205+
var errors = element.Validate(_settings.RuleSet);
206+
foreach (var item in errors)
207+
{
208+
diagnostic.Errors.Add(item);
209+
}
210+
}
211+
212+
return (T)element;
213+
}
214+
215+
160216
/// <summary>
161217
/// Helper method to turn streams into YamlDocument
162218
/// </summary>

src/Microsoft.OpenApi.Readers/OpenApiStringReader.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33

44
using System.IO;
5+
using Microsoft.OpenApi.Interfaces;
56
using Microsoft.OpenApi.Models;
67
using Microsoft.OpenApi.Readers.Interface;
78

@@ -20,7 +21,7 @@ public class OpenApiStringReader : IOpenApiReader<string, OpenApiDiagnostic>
2021
/// <param name="settings"></param>
2122
public OpenApiStringReader(OpenApiReaderSettings settings = null)
2223
{
23-
_settings = settings ?? new OpenApiReaderSettings();
24+
_settings = settings ?? new OpenApiReaderSettings();
2425
}
2526

2627
/// <summary>
@@ -38,5 +39,21 @@ public OpenApiDocument Read(string input, out OpenApiDiagnostic diagnostic)
3839
return new OpenApiStreamReader(_settings).Read(memoryStream, out diagnostic);
3940
}
4041
}
42+
43+
/// <summary>
44+
/// Reads the string input and parses it into an Open API element.
45+
/// </summary>
46+
public T ReadFragment<T>(string input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement
47+
{
48+
using (var memoryStream = new MemoryStream())
49+
{
50+
var writer = new StreamWriter(memoryStream);
51+
writer.Write(input);
52+
writer.Flush();
53+
memoryStream.Position = 0;
54+
55+
return new OpenApiStreamReader(_settings).ReadFragment<T>(memoryStream, version, out diagnostic);
56+
}
57+
}
4158
}
4259
}

src/Microsoft.OpenApi.Readers/ParsingContext.cs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ public class ParsingContext
3232
/// <summary>
3333
/// Initiates the parsing process. Not thread safe and should only be called once on a parsing context
3434
/// </summary>
35-
/// <param name="yamlDocument"></param>
36-
/// <param name="diagnostic"></param>
35+
/// <param name="yamlDocument">Yaml document to parse.</param>
36+
/// <param name="diagnostic">Diagnostic object which will return diagnostic results of the operation.</param>
3737
/// <returns>An OpenApiDocument populated based on the passed yamlDocument </returns>
3838
internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diagnostic)
3939
{
@@ -47,13 +47,13 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diag
4747
{
4848
case string version when version == "2.0":
4949
VersionService = new OpenApiV2VersionService();
50-
doc = this.VersionService.LoadDocument(this.RootNode);
50+
doc = VersionService.LoadDocument(RootNode);
5151
diagnostic.SpecificationVersion = OpenApiSpecVersion.OpenApi2_0;
5252
break;
5353

5454
case string version when version.StartsWith("3.0"):
55-
this.VersionService = new OpenApiV3VersionService();
56-
doc = this.VersionService.LoadDocument(this.RootNode);
55+
VersionService = new OpenApiV3VersionService();
56+
doc = VersionService.LoadDocument(RootNode);
5757
diagnostic.SpecificationVersion = OpenApiSpecVersion.OpenApi3_0;
5858
break;
5959

@@ -64,6 +64,34 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument, OpenApiDiagnostic diag
6464
return doc;
6565
}
6666

67+
/// <summary>
68+
/// Initiates the parsing process of a fragment. Not thread safe and should only be called once on a parsing context
69+
/// </summary>
70+
/// <param name="yamlDocument"></param>
71+
/// <param name="version">OpenAPI version of the fragment</param>
72+
/// <param name="diagnostic">Diagnostic object which will return diagnostic results of the operation.</param>
73+
/// <returns>An OpenApiDocument populated based on the passed yamlDocument </returns>
74+
internal T ParseFragment<T>(YamlDocument yamlDocument, OpenApiSpecVersion version, OpenApiDiagnostic diagnostic) where T: IOpenApiElement
75+
{
76+
var node = ParseNode.Create(this, diagnostic, yamlDocument.RootNode);
77+
78+
T element = default(T);
79+
80+
switch (version)
81+
{
82+
case OpenApiSpecVersion.OpenApi2_0:
83+
VersionService = new OpenApiV2VersionService();
84+
element = this.VersionService.LoadElement<T>(node);
85+
break;
86+
87+
case OpenApiSpecVersion.OpenApi3_0:
88+
this.VersionService = new OpenApiV3VersionService();
89+
element = this.VersionService.LoadElement<T>(node);
90+
break;
91+
}
92+
93+
return element;
94+
}
6795

6896
/// <summary>
6997
/// Gets the version of the Open API document.
@@ -82,20 +110,6 @@ private static string GetVersion(RootNode rootNode)
82110
return versionNode?.GetScalarValue();
83111
}
84112

85-
private void ComputeTags(List<OpenApiTag> tags, Func<MapNode, OpenApiTag> loadTag)
86-
{
87-
// Precompute the tags array so that each tag reference does not require a new deserialization.
88-
var tagListPointer = new JsonPointer("#/tags");
89-
90-
var tagListNode = RootNode.Find(tagListPointer);
91-
92-
if (tagListNode != null && tagListNode is ListNode)
93-
{
94-
var tagListNodeAsListNode = (ListNode)tagListNode;
95-
tags.AddRange(tagListNodeAsListNode.CreateList(loadTag));
96-
}
97-
}
98-
99113
/// <summary>
100114
/// Service providing all Version specific conversion functions
101115
/// </summary>
@@ -108,7 +122,6 @@ internal IOpenApiVersionService VersionService
108122
set
109123
{
110124
_versionService = value;
111-
ComputeTags(Tags, VersionService.TagLoader);
112125
}
113126
}
114127

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Generic;
55
using System.Linq;
6+
using Microsoft.OpenApi.Any;
67
using Microsoft.OpenApi.Readers.ParseNodes;
78

89
namespace Microsoft.OpenApi.Readers.V2
@@ -32,6 +33,10 @@ private static void ParseMap<T>(
3233
}
3334
}
3435

36+
public static IOpenApiAny LoadAny(ParseNode node)
37+
{
38+
return node.CreateAny();
39+
}
3540

3641
private static string LoadString(ParseNode node)
3742
{

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

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
// Licensed under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
6+
using Microsoft.OpenApi.Any;
57
using Microsoft.OpenApi.Exceptions;
68
using Microsoft.OpenApi.Interfaces;
79
using Microsoft.OpenApi.Models;
@@ -17,10 +19,25 @@ namespace Microsoft.OpenApi.Readers.V2
1719
/// </summary>
1820
internal class OpenApiV2VersionService : IOpenApiVersionService
1921
{
20-
/// <summary>
21-
/// Return a function that converts a MapNode into a V2 OpenApiTag
22-
/// </summary>
23-
public Func<MapNode, OpenApiTag> TagLoader => OpenApiV2Deserializer.LoadTag;
22+
private IDictionary<Type, Func<ParseNode, object>> _loaders = new Dictionary<Type, Func<ParseNode, object>>
23+
{
24+
[typeof(IOpenApiAny)] = OpenApiV2Deserializer.LoadAny,
25+
[typeof(OpenApiExternalDocs)] = OpenApiV2Deserializer.LoadExternalDocs,
26+
[typeof(OpenApiHeader)] = OpenApiV2Deserializer.LoadHeader,
27+
[typeof(OpenApiInfo)] = OpenApiV2Deserializer.LoadInfo,
28+
[typeof(OpenApiLicense)] = OpenApiV2Deserializer.LoadLicense,
29+
[typeof(OpenApiOperation)] = OpenApiV2Deserializer.LoadOperation,
30+
[typeof(OpenApiParameter)] = OpenApiV2Deserializer.LoadParameter,
31+
[typeof(OpenApiPathItem)] = OpenApiV2Deserializer.LoadPathItem,
32+
[typeof(OpenApiPaths)] = OpenApiV2Deserializer.LoadPaths,
33+
[typeof(OpenApiResponse)] = OpenApiV2Deserializer.LoadResponse,
34+
[typeof(OpenApiResponses)] = OpenApiV2Deserializer.LoadResponses,
35+
[typeof(OpenApiSchema)] = OpenApiV2Deserializer.LoadSchema,
36+
[typeof(OpenApiSecurityRequirement)] = OpenApiV2Deserializer.LoadSecurityRequirement,
37+
[typeof(OpenApiSecurityScheme)] = OpenApiV2Deserializer.LoadSecurityScheme,
38+
[typeof(OpenApiTag)] = OpenApiV2Deserializer.LoadTag,
39+
[typeof(OpenApiXml)] = OpenApiV2Deserializer.LoadXml
40+
};
2441

2542
private static OpenApiReference ParseLocalReference(string localReference)
2643
{
@@ -155,5 +172,10 @@ public OpenApiDocument LoadDocument(RootNode rootNode)
155172
{
156173
return OpenApiV2Deserializer.LoadOpenApi(rootNode);
157174
}
175+
176+
public T LoadElement<T>(ParseNode node) where T : IOpenApiElement
177+
{
178+
return (T)_loaders[typeof(T)](node);
179+
}
158180
}
159181
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Generic;
55
using System.Linq;
6+
using Microsoft.OpenApi.Any;
67
using Microsoft.OpenApi.Expressions;
78
using Microsoft.OpenApi.Models;
89
using Microsoft.OpenApi.Readers.ParseNodes;
@@ -57,7 +58,10 @@ private static RuntimeExpressionAnyWrapper LoadRuntimeExpressionAnyWrapper(Parse
5758
};
5859
}
5960

60-
61+
public static IOpenApiAny LoadAny(ParseNode node)
62+
{
63+
return node.CreateAny();
64+
}
6165

6266
private static string LoadString(ParseNode node)
6367
{

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

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System;
55
using System.Collections.Generic;
6+
using Microsoft.OpenApi.Any;
67
using Microsoft.OpenApi.Exceptions;
78
using Microsoft.OpenApi.Extensions;
89
using Microsoft.OpenApi.Interfaces;
@@ -18,11 +19,36 @@ namespace Microsoft.OpenApi.Readers.V3
1819
/// </summary>
1920
internal class OpenApiV3VersionService : IOpenApiVersionService
2021
{
21-
/// <summary>
22-
/// Return a function that converts a MapNode into a V3 OpenApiTag
23-
/// </summary>
24-
public Func<MapNode, OpenApiTag> TagLoader => OpenApiV3Deserializer.LoadTag;
25-
22+
private IDictionary<Type, Func<ParseNode, object>> _loaders = new Dictionary<Type, Func<ParseNode, object>> {
23+
[typeof(IOpenApiAny)] = OpenApiV3Deserializer.LoadAny,
24+
[typeof(OpenApiCallback)] = OpenApiV3Deserializer.LoadCallback,
25+
[typeof(OpenApiComponents)] = OpenApiV3Deserializer.LoadComponents,
26+
[typeof(OpenApiEncoding)] = OpenApiV3Deserializer.LoadEncoding,
27+
[typeof(OpenApiExample)] = OpenApiV3Deserializer.LoadExample,
28+
[typeof(OpenApiExternalDocs)] = OpenApiV3Deserializer.LoadExternalDocs,
29+
[typeof(OpenApiHeader)] = OpenApiV3Deserializer.LoadHeader,
30+
[typeof(OpenApiInfo)] = OpenApiV3Deserializer.LoadInfo,
31+
[typeof(OpenApiLicense)] = OpenApiV3Deserializer.LoadLicense,
32+
[typeof(OpenApiLink)] = OpenApiV3Deserializer.LoadLink,
33+
[typeof(OpenApiMediaType)] = OpenApiV3Deserializer.LoadMediaType,
34+
[typeof(OpenApiOAuthFlow)] = OpenApiV3Deserializer.LoadOAuthFlow,
35+
[typeof(OpenApiOAuthFlows)] = OpenApiV3Deserializer.LoadOAuthFlows,
36+
[typeof(OpenApiOperation)] = OpenApiV3Deserializer.LoadOperation,
37+
[typeof(OpenApiParameter)] = OpenApiV3Deserializer.LoadParameter,
38+
[typeof(OpenApiPathItem)] = OpenApiV3Deserializer.LoadPathItem,
39+
[typeof(OpenApiPaths)] = OpenApiV3Deserializer.LoadPaths,
40+
[typeof(OpenApiRequestBody)] = OpenApiV3Deserializer.LoadRequestBody,
41+
[typeof(OpenApiResponse)] = OpenApiV3Deserializer.LoadResponse,
42+
[typeof(OpenApiResponses)] = OpenApiV3Deserializer.LoadResponses,
43+
[typeof(OpenApiSchema)] = OpenApiV3Deserializer.LoadSchema,
44+
[typeof(OpenApiSecurityRequirement)] = OpenApiV3Deserializer.LoadSecurityRequirement,
45+
[typeof(OpenApiSecurityScheme)] = OpenApiV3Deserializer.LoadSecurityScheme,
46+
[typeof(OpenApiServer)] = OpenApiV3Deserializer.LoadServer,
47+
[typeof(OpenApiServerVariable)] = OpenApiV3Deserializer.LoadServerVariable,
48+
[typeof(OpenApiTag)] = OpenApiV3Deserializer.LoadTag,
49+
[typeof(OpenApiXml)] = OpenApiV3Deserializer.LoadXml
50+
};
51+
2652
/// <summary>
2753
/// Parse the string to a <see cref="OpenApiReference"/> object.
2854
/// </summary>
@@ -80,6 +106,11 @@ public OpenApiDocument LoadDocument(RootNode rootNode)
80106
return OpenApiV3Deserializer.LoadOpenApi(rootNode);
81107
}
82108

109+
public T LoadElement<T>(ParseNode node) where T : IOpenApiElement
110+
{
111+
return (T)_loaders[typeof(T)](node);
112+
}
113+
83114
private OpenApiReference ParseLocalReference(string localReference)
84115
{
85116
if (string.IsNullOrWhiteSpace(localReference))

src/Microsoft.OpenApi/Microsoft.OpenApi.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</Title>
1212
<PackageId>Microsoft.OpenApi</PackageId>
13-
<Version>1.0.0</Version>
13+
<Version>1.1.0-preview.1</Version>
1414
<Description>.NET models with JSON and YAML writers for OpenAPI specification</Description>
1515
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
1616
<PackageTags>OpenAPI .NET</PackageTags>

0 commit comments

Comments
 (0)