Skip to content

Commit a648591

Browse files
committed
Refactor readers to reduce surface area
1 parent 1338905 commit a648591

File tree

7 files changed

+154
-143
lines changed

7 files changed

+154
-143
lines changed

src/Microsoft.OpenApi.Readers/OpenApiYamlReader.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,34 @@ namespace Microsoft.OpenApi.Readers
1919
/// </summary>
2020
public class OpenApiYamlReader : IOpenApiReader
2121
{
22+
private const int copyBufferSize = 4096;
23+
2224
/// <inheritdoc/>
23-
public async Task<ReadResult> ReadAsync(TextReader input,
25+
public async Task<ReadResult> ReadAsync(Stream input,
2426
OpenApiReaderSettings settings = null,
2527
CancellationToken cancellationToken = default)
28+
{
29+
if (input is MemoryStream memoryStream)
30+
{
31+
return Read(memoryStream, settings);
32+
} else {
33+
using var preparedStream = new MemoryStream();
34+
await input.CopyToAsync(preparedStream, copyBufferSize, cancellationToken);
35+
preparedStream.Position = 0;
36+
return Read(preparedStream, settings);
37+
}
38+
}
39+
40+
/// <inheritdoc/>
41+
public ReadResult Read(MemoryStream input,
42+
OpenApiReaderSettings settings = null)
2643
{
2744
JsonNode jsonNode;
2845

2946
// Parse the YAML text in the TextReader into a sequence of JsonNodes
3047
try
3148
{
32-
jsonNode = LoadJsonNodesFromYamlDocument(input);
49+
jsonNode = LoadJsonNodesFromYamlDocument(new StreamReader(input)); // Should we leave the stream open?
3350
}
3451
catch (JsonException ex)
3552
{
@@ -42,11 +59,11 @@ public async Task<ReadResult> ReadAsync(TextReader input,
4259
};
4360
}
4461

45-
return await ReadAsync(jsonNode, settings, cancellationToken: cancellationToken);
62+
return Read(jsonNode, settings);
4663
}
4764

4865
/// <inheritdoc/>
49-
public T ReadFragment<T>(TextReader input,
66+
public T ReadFragment<T>(MemoryStream input,
5067
OpenApiSpecVersion version,
5168
out OpenApiDiagnostic diagnostic,
5269
OpenApiReaderSettings settings = null) where T : IOpenApiElement
@@ -56,7 +73,7 @@ public T ReadFragment<T>(TextReader input,
5673
// Parse the YAML
5774
try
5875
{
59-
jsonNode = LoadJsonNodesFromYamlDocument(input);
76+
jsonNode = LoadJsonNodesFromYamlDocument(new StreamReader(input));
6077
}
6178
catch (JsonException ex)
6279
{
@@ -81,10 +98,10 @@ static JsonNode LoadJsonNodesFromYamlDocument(TextReader input)
8198
return yamlDocument.ToJsonNode();
8299
}
83100

84-
/// <inheritdoc/>
85-
public async Task<ReadResult> ReadAsync(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null, CancellationToken cancellationToken = default)
101+
/// <inheritdoc/>
102+
public ReadResult Read(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null)
86103
{
87-
return await OpenApiReaderRegistry.DefaultReader.ReadAsync(jsonNode, settings, OpenApiConstants.Yaml, cancellationToken);
104+
return OpenApiReaderRegistry.DefaultReader.Read(jsonNode, settings, OpenApiConstants.Yaml);
88105
}
89106

90107
/// <inheritdoc/>

src/Microsoft.OpenApi/Interfaces/IOpenApiReader.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,40 @@ namespace Microsoft.OpenApi.Interfaces
1515
public interface IOpenApiReader
1616
{
1717
/// <summary>
18-
/// Reads the TextReader input and parses it into an Open API document.
18+
/// Async method to reads the stream and parse it into an Open API document.
1919
/// </summary>
2020
/// <param name="input">The TextReader input.</param>
2121
/// <param name="settings"> The OpenApi reader settings.</param>
2222
/// <param name="cancellationToken">Propagates notification that an operation should be cancelled.</param>
2323
/// <returns></returns>
24-
Task<ReadResult> ReadAsync(TextReader input, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default);
24+
Task<ReadResult> ReadAsync(Stream input, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default);
25+
26+
/// <summary>
27+
/// Provides a synchronous method to read the input memory stream and parse it into an Open API document.
28+
/// </summary>
29+
/// <param name="input"></param>
30+
/// <param name="settings"></param>
31+
/// <returns></returns>
32+
ReadResult Read(MemoryStream input, OpenApiReaderSettings settings = null);
2533

2634
/// <summary>
2735
/// Parses the JsonNode input into an Open API document.
2836
/// </summary>
2937
/// <param name="jsonNode">The JsonNode input.</param>
3038
/// <param name="settings">The Reader settings to be used during parsing.</param>
31-
/// <param name="cancellationToken">Propagates notifications that operations should be cancelled.</param>
3239
/// <param name="format">The OpenAPI format.</param>
3340
/// <returns></returns>
34-
Task<ReadResult> ReadAsync(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null, CancellationToken cancellationToken = default);
41+
ReadResult Read(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null);
3542

3643
/// <summary>
37-
/// Reads the TextReader input and parses the fragment of an OpenAPI description into an Open API Element.
44+
/// Reads the MemoryStream and parses the fragment of an OpenAPI description into an Open API Element.
3845
/// </summary>
3946
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
4047
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
4148
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
4249
/// <param name="settings">The OpenApiReader settings.</param>
4350
/// <returns>Instance of newly created IOpenApiElement.</returns>
44-
T ReadFragment<T>(TextReader input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
51+
T ReadFragment<T>(MemoryStream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
4552

4653
/// <summary>
4754
/// Reads the JsonNode input and parses the fragment of an OpenAPI description into an Open API Element.

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -553,27 +553,13 @@ public static ReadResult Load(string url, OpenApiReaderSettings? settings = null
553553
/// <param name="format">The OpenAPI format to use during parsing.</param>
554554
/// <param name="settings">The OpenApi reader settings.</param>
555555
/// <returns></returns>
556-
public static ReadResult Load(Stream stream,
556+
public static ReadResult Load(MemoryStream stream,
557557
string format,
558558
OpenApiReaderSettings? settings = null)
559559
{
560560
return OpenApiModelFactory.Load(stream, format, settings);
561561
}
562562

563-
/// <summary>
564-
/// Reads the text reader content and parses it into an Open API document.
565-
/// </summary>
566-
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
567-
/// <param name="format"> The OpenAPI format to use during parsing.</param>
568-
/// <param name="settings">The OpenApi reader settings.</param>
569-
/// <returns></returns>
570-
public static ReadResult Load(TextReader input,
571-
string format,
572-
OpenApiReaderSettings? settings = null)
573-
{
574-
return OpenApiModelFactory.Load(input, format, settings);
575-
}
576-
577563
/// <summary>
578564
/// Parses a local file path or Url into an Open API document.
579565
/// </summary>
@@ -598,17 +584,6 @@ public static async Task<ReadResult> LoadAsync(Stream stream, string format, Ope
598584
return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken);
599585
}
600586

601-
/// <summary>
602-
/// Reads the text reader content and parses it into an Open API document.
603-
/// </summary>
604-
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
605-
/// <param name="format"> The OpenAPI format to use during parsing.</param>
606-
/// <param name="settings">The OpenApi reader settings.</param>
607-
/// <returns></returns>
608-
public static async Task<ReadResult> LoadAsync(TextReader input, string format, OpenApiReaderSettings? settings = null)
609-
{
610-
return await OpenApiModelFactory.LoadAsync(input, format, settings);
611-
}
612587

613588
/// <summary>
614589
/// Parses a string into a <see cref="OpenApiDocument"/> object.

src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,47 @@ namespace Microsoft.OpenApi.Reader
2424
/// </summary>
2525
public class OpenApiJsonReader : IOpenApiReader
2626
{
27+
2728
/// <summary>
28-
/// Reads the stream input and parses it into an Open API document.
29+
/// Reads the memory stream input and parses it into an Open API document.
30+
/// </summary>
31+
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
32+
/// <param name="settings">The Reader settings to be used during parsing.</param>
33+
/// <returns></returns>
34+
public ReadResult Read(MemoryStream input,
35+
OpenApiReaderSettings settings = null)
36+
{
37+
JsonNode jsonNode;
38+
var diagnostic = new OpenApiDiagnostic();
39+
settings ??= new OpenApiReaderSettings();
40+
41+
// Parse the JSON text in the TextReader into JsonNodes
42+
try
43+
{
44+
jsonNode = JsonNode.Parse(input);
45+
}
46+
catch (JsonException ex)
47+
{
48+
diagnostic.Errors.Add(new OpenApiError($"#line={ex.LineNumber}", $"Please provide the correct format, {ex.Message}"));
49+
return new ReadResult
50+
{
51+
OpenApiDocument = null,
52+
OpenApiDiagnostic = diagnostic
53+
};
54+
}
55+
56+
return Read(jsonNode, settings);
57+
}
58+
59+
60+
/// <summary>
61+
/// Reads the stream input asynchronously and parses it into an Open API document.
2962
/// </summary>
3063
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
3164
/// <param name="settings">The Reader settings to be used during parsing.</param>
3265
/// <param name="cancellationToken">Propagates notifications that operations should be cancelled.</param>
3366
/// <returns></returns>
34-
public async Task<ReadResult> ReadAsync(TextReader input,
67+
public async Task<ReadResult> ReadAsync(Stream input,
3568
OpenApiReaderSettings settings = null,
3669
CancellationToken cancellationToken = default)
3770
{
@@ -42,7 +75,7 @@ public async Task<ReadResult> ReadAsync(TextReader input,
4275
// Parse the JSON text in the TextReader into JsonNodes
4376
try
4477
{
45-
jsonNode = LoadJsonNodes(input);
78+
jsonNode = await JsonNode.ParseAsync(input);;
4679
}
4780
catch (JsonException ex)
4881
{
@@ -54,7 +87,7 @@ public async Task<ReadResult> ReadAsync(TextReader input,
5487
};
5588
}
5689

57-
return await ReadAsync(jsonNode, settings, cancellationToken: cancellationToken);
90+
return Read(jsonNode, settings);
5891
}
5992

6093
/// <summary>
@@ -63,12 +96,10 @@ public async Task<ReadResult> ReadAsync(TextReader input,
6396
/// <param name="jsonNode">The JsonNode input.</param>
6497
/// <param name="settings">The Reader settings to be used during parsing.</param>
6598
/// <param name="format">The OpenAPI format.</param>
66-
/// <param name="cancellationToken">Propagates notifications that operations should be cancelled.</param>
6799
/// <returns></returns>
68-
public async Task<ReadResult> ReadAsync(JsonNode jsonNode,
100+
public ReadResult Read(JsonNode jsonNode,
69101
OpenApiReaderSettings settings,
70-
string format = null,
71-
CancellationToken cancellationToken = default)
102+
string format = null)
72103
{
73104
var diagnostic = new OpenApiDiagnostic();
74105
var context = new ParsingContext(diagnostic)
@@ -84,16 +115,16 @@ public async Task<ReadResult> ReadAsync(JsonNode jsonNode,
84115
// Parse the OpenAPI Document
85116
document = context.Parse(jsonNode);
86117

87-
if (settings.LoadExternalRefs)
88-
{
89-
var diagnosticExternalRefs = await LoadExternalRefsAsync(document, cancellationToken, settings, format);
90-
// Merge diagnostics of external reference
91-
if (diagnosticExternalRefs != null)
92-
{
93-
diagnostic.Errors.AddRange(diagnosticExternalRefs.Errors);
94-
diagnostic.Warnings.AddRange(diagnosticExternalRefs.Warnings);
95-
}
96-
}
118+
// if (settings.LoadExternalRefs)
119+
// {
120+
// var diagnosticExternalRefs = await LoadExternalRefsAsync(document, cancellationToken, settings, format);
121+
// // Merge diagnostics of external reference
122+
// if (diagnosticExternalRefs != null)
123+
// {
124+
// diagnostic.Errors.AddRange(diagnosticExternalRefs.Errors);
125+
// diagnostic.Warnings.AddRange(diagnosticExternalRefs.Warnings);
126+
// }
127+
// }
97128

98129
document.SetReferenceHostDocument();
99130
}
@@ -124,7 +155,7 @@ public async Task<ReadResult> ReadAsync(JsonNode jsonNode,
124155
}
125156

126157
/// <inheritdoc/>
127-
public T ReadFragment<T>(TextReader input,
158+
public T ReadFragment<T>(MemoryStream input,
128159
OpenApiSpecVersion version,
129160
out OpenApiDiagnostic diagnostic,
130161
OpenApiReaderSettings settings = null) where T : IOpenApiElement
@@ -134,7 +165,7 @@ public T ReadFragment<T>(TextReader input,
134165
// Parse the JSON
135166
try
136167
{
137-
jsonNode = LoadJsonNodes(input);
168+
jsonNode = JsonNode.Parse(input);
138169
}
139170
catch (JsonException ex)
140171
{
@@ -183,12 +214,6 @@ public T ReadFragment<T>(JsonNode input,
183214
return (T)element;
184215
}
185216

186-
private JsonNode LoadJsonNodes(TextReader input)
187-
{
188-
var nodes = JsonNode.Parse(input.ReadToEnd());
189-
return nodes;
190-
}
191-
192217
private async Task<OpenApiDiagnostic> LoadExternalRefsAsync(OpenApiDocument document, CancellationToken cancellationToken, OpenApiReaderSettings settings, string format = null)
193218
{
194219
// Create workspace for all documents to live in.

0 commit comments

Comments
 (0)