Skip to content

Commit c07809f

Browse files
committed
Read fragments
1 parent ee0725f commit c07809f

File tree

9 files changed

+277
-3
lines changed

9 files changed

+277
-3
lines changed

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ internal interface IOpenApiVersionService
2222
/// <returns>The <see cref="OpenApiReference"/> object or null.</returns>
2323
OpenApiReference ConvertToOpenApiReference(string reference, ReferenceType? type);
2424

25+
/// <summary>
26+
/// Loads an OpenAPI Element from a document fragment
27+
/// </summary>
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;
32+
2533
/// <summary>
2634
/// Function that converts a MapNode into a Tag object in a version specific way
2735
/// </summary>

src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Linq;
77
using Microsoft.OpenApi.Exceptions;
88
using Microsoft.OpenApi.Extensions;
9+
using Microsoft.OpenApi.Interfaces;
910
using Microsoft.OpenApi.Models;
1011
using Microsoft.OpenApi.Readers.Interface;
1112
using Microsoft.OpenApi.Readers.Services;
@@ -102,6 +103,51 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic)
102103
return document;
103104
}
104105

106+
/// <summary>
107+
/// Reads the stream input and parses the fragment of an OpenAPI descinto an Open API Element.
108+
/// </summary>
109+
/// <param name="input">Stream containing OpenAPI description to parse.</param>
110+
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
111+
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
112+
/// <returns>Instance of newly created OpenApiDocument</returns>
113+
public T ReadFragment<T>(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic) where T : IOpenApiElement
114+
{
115+
ParsingContext context;
116+
YamlDocument yamlDocument;
117+
diagnostic = new OpenApiDiagnostic();
118+
119+
// Parse the YAML/JSON
120+
try
121+
{
122+
yamlDocument = LoadYamlDocument(input);
123+
}
124+
catch (SyntaxErrorException ex)
125+
{
126+
diagnostic.Errors.Add(new OpenApiReaderError(ex));
127+
return default(T);
128+
}
129+
130+
context = new ParsingContext
131+
{
132+
ExtensionParsers = _settings.ExtensionParsers
133+
};
134+
135+
IOpenApiElement element = null;
136+
137+
try
138+
{
139+
// Parse the OpenAPI element
140+
element = context.ParseFragment<T>(yamlDocument, version, diagnostic);
141+
}
142+
catch (OpenApiException ex)
143+
{
144+
diagnostic.Errors.Add(new OpenApiError(ex));
145+
}
146+
147+
return (T)element;
148+
}
149+
150+
105151
/// <summary>
106152
/// Helper method to turn streams into YamlDocument
107153
/// </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: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@ 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="diagnostic"></param>
72+
/// <returns>An OpenApiDocument populated based on the passed yamlDocument </returns>
73+
internal T ParseFragment<T>(YamlDocument yamlDocument, OpenApiSpecVersion version, OpenApiDiagnostic diagnostic) where T: IOpenApiElement
74+
{
75+
var node = ParseNode.Create(this, diagnostic, yamlDocument.RootNode);
76+
77+
T element = default(T);
78+
79+
switch (version)
80+
{
81+
case OpenApiSpecVersion.OpenApi2_0:
82+
VersionService = new OpenApiV2VersionService();
83+
element = this.VersionService.LoadElement<T>(node);
84+
break;
85+
86+
case OpenApiSpecVersion.OpenApi3_0:
87+
this.VersionService = new OpenApiV3VersionService();
88+
element = this.VersionService.LoadElement<T>(node);
89+
break;
90+
}
91+
92+
return element;
93+
}
6794

6895
/// <summary>
6996
/// Gets the version of the Open API document.
@@ -108,7 +135,7 @@ internal IOpenApiVersionService VersionService
108135
set
109136
{
110137
_versionService = value;
111-
ComputeTags(Tags, VersionService.TagLoader);
138+
//ComputeTags(Tags, VersionService.TagLoader);
112139
}
113140
}
114141

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: 28 additions & 0 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,6 +19,27 @@ namespace Microsoft.OpenApi.Readers.V2
1719
/// </summary>
1820
internal class OpenApiV2VersionService : IOpenApiVersionService
1921
{
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+
};
41+
42+
2043
/// <summary>
2144
/// Return a function that converts a MapNode into a V2 OpenApiTag
2245
/// </summary>
@@ -155,5 +178,10 @@ public OpenApiDocument LoadDocument(RootNode rootNode)
155178
{
156179
return OpenApiV2Deserializer.LoadOpenApi(rootNode);
157180
}
181+
182+
public T LoadElement<T>(ParseNode node) where T : IOpenApiElement
183+
{
184+
return (T)_loaders[typeof(T)](node);
185+
}
158186
}
159187
}

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 & 0 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,6 +19,36 @@ namespace Microsoft.OpenApi.Readers.V3
1819
/// </summary>
1920
internal class OpenApiV3VersionService : IOpenApiVersionService
2021
{
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+
2152
/// <summary>
2253
/// Return a function that converts a MapNode into a V3 OpenApiTag
2354
/// </summary>
@@ -80,6 +111,11 @@ public OpenApiDocument LoadDocument(RootNode rootNode)
80111
return OpenApiV3Deserializer.LoadOpenApi(rootNode);
81112
}
82113

114+
public T LoadElement<T>(ParseNode node) where T : IOpenApiElement
115+
{
116+
return (T)_loaders[typeof(T)](node);
117+
}
118+
83119
private OpenApiReference ParseLocalReference(string localReference)
84120
{
85121
if (string.IsNullOrWhiteSpace(localReference))

0 commit comments

Comments
 (0)