Skip to content

Commit 18a152e

Browse files
committed
Simplifies code base by removing unnecessary code and delegating functionality that isn't reader-specific to the factory
1 parent 575a48a commit 18a152e

File tree

5 files changed

+428
-717
lines changed

5 files changed

+428
-717
lines changed
Lines changed: 53 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
using System;
54
using System.IO;
6-
using System.Net.Http;
7-
using System.Security;
85
using System.Text.Json.Nodes;
6+
using System.Text.Json;
97
using System.Threading;
108
using System.Threading.Tasks;
119
using Microsoft.OpenApi.Interfaces;
12-
using Microsoft.OpenApi.Models;
1310
using Microsoft.OpenApi.Reader;
11+
using SharpYaml.Serialization;
12+
using System.Linq;
1413

1514
namespace Microsoft.OpenApi.Readers
1615
{
@@ -19,156 +18,78 @@ namespace Microsoft.OpenApi.Readers
1918
/// </summary>
2019
public class OpenApiYamlReader : IOpenApiReader
2120
{
22-
private static readonly HttpClient _httpClient = HttpClientFactory.GetHttpClient();
23-
2421
/// <inheritdoc/>
25-
public OpenApiDocument Parse(string input, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null)
22+
public async Task<ReadResult> ReadAsync(TextReader input,
23+
OpenApiReaderSettings settings = null,
24+
CancellationToken cancellationToken = default)
2625
{
27-
using var reader = new StringReader(input);
28-
return Read(reader, out diagnostic, settings);
29-
}
26+
JsonNode jsonNode;
3027

31-
/// <inheritdoc/>
32-
public OpenApiDocument Read(string url, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null)
33-
{
34-
var stream = GetStream(url).GetAwaiter().GetResult();
35-
return Read(stream, out diagnostic, settings);
36-
}
28+
// Parse the YAML text in the TextReader into a sequence of JsonNodes
29+
try
30+
{
31+
jsonNode = LoadJsonNodesFromYamlDocument(input);
32+
}
33+
catch (JsonException ex)
34+
{
35+
var diagnostic = new OpenApiDiagnostic();
36+
diagnostic.Errors.Add(new($"#line={ex.LineNumber}", ex.Message));
37+
return new()
38+
{
39+
OpenApiDocument = null,
40+
OpenApiDiagnostic = diagnostic
41+
};
42+
}
3743

38-
/// <inheritdoc/>
39-
public OpenApiDocument Read(Stream stream, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null)
40-
{
41-
return new OpenApiStreamReader(settings).Read(stream, out diagnostic);
44+
return await ReadAsync(jsonNode, settings, cancellationToken);
4245
}
4346

4447
/// <inheritdoc/>
45-
public OpenApiDocument Read(TextReader input, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null)
48+
public T ReadFragment<T>(TextReader input,
49+
OpenApiSpecVersion version,
50+
out OpenApiDiagnostic diagnostic,
51+
OpenApiReaderSettings settings = null) where T : IOpenApiElement
4652
{
47-
return new OpenApiTextReaderReader(settings).Read(input, out diagnostic);
48-
}
53+
JsonNode jsonNode;
4954

50-
/// <inheritdoc/>
51-
public async Task<ReadResult> ReadAsync(string url, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default)
52-
{
53-
var stream = GetStream(url).Result;
54-
return await ReadAsync(stream, settings, cancellationToken);
55-
}
55+
// Parse the YAML
56+
try
57+
{
58+
jsonNode = LoadJsonNodesFromYamlDocument(input);
59+
}
60+
catch (JsonException ex)
61+
{
62+
diagnostic = new();
63+
diagnostic.Errors.Add(new($"#line={ex.LineNumber}", ex.Message));
64+
return default;
65+
}
5666

57-
/// <inheritdoc/>
58-
public async Task<ReadResult> ReadAsync(Stream stream, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default)
59-
{
60-
return await new OpenApiStreamReader(settings).ReadAsync(stream, cancellationToken);
67+
return ReadFragment<T>(jsonNode, version, out diagnostic);
6168
}
6269

63-
/// <inheritdoc/>
64-
public async Task<ReadResult> ReadAsync(TextReader input,
65-
OpenApiReaderSettings settings = null,
66-
CancellationToken cancellationToken = default)
67-
{
68-
return await new OpenApiTextReaderReader(settings).ReadAsync(input, cancellationToken);
69-
}
70-
71-
7270
/// <summary>
73-
/// Takes in an input URL and parses it into an Open API document
71+
/// Helper method to turn streams into a sequence of JsonNodes
7472
/// </summary>
75-
/// <param name="url">The path to the Open API file</param>
76-
/// <param name="version">The OpenAPI specification version.</param>
77-
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
78-
/// <param name="settings">The Reader settings to be used during parsing.</param>
79-
/// <returns></returns>
80-
/// <exception cref="ArgumentException"></exception>
81-
public T Read<T>(string url,
82-
OpenApiSpecVersion version,
83-
out OpenApiDiagnostic diagnostic,
84-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
85-
{
86-
settings ??= new OpenApiReaderSettings();
87-
var stream = GetStream(url).GetAwaiter().GetResult();
88-
return Read<T>(stream, version, out diagnostic, settings);
89-
}
90-
91-
/// <inheritdoc/>
92-
public T Read<T>(Stream input,
93-
OpenApiSpecVersion version,
94-
out OpenApiDiagnostic diagnostic,
95-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
73+
/// <param name="input">Stream containing YAML formatted text</param>
74+
/// <returns>Instance of a YamlDocument</returns>
75+
static JsonNode LoadJsonNodesFromYamlDocument(TextReader input)
9676
{
97-
return new OpenApiStreamReader(settings).ReadFragment<T>(input, version, out diagnostic);
77+
var yamlStream = new YamlStream();
78+
yamlStream.Load(input);
79+
var yamlDocument = yamlStream.Documents.First();
80+
return yamlDocument.ToJsonNode();
9881
}
9982

100-
/// <inheritdoc/>
101-
public T Read<T>(TextReader input,
102-
OpenApiSpecVersion version,
103-
out OpenApiDiagnostic diagnostic,
104-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
83+
/// <inheritdoc/>
84+
public async Task<ReadResult> ReadAsync(JsonNode jsonNode, OpenApiReaderSettings settings, CancellationToken cancellationToken = default)
10585
{
106-
return new OpenApiTextReaderReader(settings).ReadFragment<T>(input, version, out diagnostic);
86+
return await OpenApiReaderRegistry.DefaultReader.ReadAsync(jsonNode, settings, cancellationToken);
10787
}
10888

10989
/// <inheritdoc/>
110-
public T Read<T>(JsonNode input,
111-
OpenApiSpecVersion version,
112-
out OpenApiDiagnostic diagnostic,
113-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
90+
public T ReadFragment<T>(JsonNode input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement
11491
{
115-
return new OpenApiYamlDocumentReader(settings).ReadFragment<T>(input, version, out diagnostic);
92+
return OpenApiReaderRegistry.DefaultReader.ReadFragment<T>(input, version, out diagnostic);
11693
}
117-
118-
/// <summary>
119-
/// Parses an input string into an Open API document.
120-
/// </summary>
121-
/// <param name="input"></param>
122-
/// <param name="version"></param>
123-
/// <param name="diagnostic"></param>
124-
/// <param name="settings"></param>
125-
/// <returns></returns>
126-
public T Parse<T>(string input,
127-
OpenApiSpecVersion version,
128-
out OpenApiDiagnostic diagnostic,
129-
OpenApiReaderSettings settings = null) where T : IOpenApiElement
130-
{
131-
settings ??= new OpenApiReaderSettings();
132-
using var reader = new StringReader(input);
133-
return Read<T>(reader, version, out diagnostic, settings);
134-
}
135-
136-
private async Task<Stream> GetStream(string url)
137-
{
138-
Stream stream;
139-
if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
140-
{
141-
try
142-
{
143-
stream = await _httpClient.GetStreamAsync(new Uri(url));
144-
}
145-
catch (HttpRequestException ex)
146-
{
147-
throw new InvalidOperationException($"Could not download the file at {url}", ex);
148-
}
149-
}
150-
else
151-
{
152-
try
153-
{
154-
var fileInput = new FileInfo(url);
155-
stream = fileInput.OpenRead();
156-
}
157-
catch (Exception ex) when (
158-
ex is
159-
FileNotFoundException or
160-
PathTooLongException or
161-
DirectoryNotFoundException or
162-
IOException or
163-
UnauthorizedAccessException or
164-
SecurityException or
165-
NotSupportedException)
166-
{
167-
throw new InvalidOperationException($"Could not open the file at {url}", ex);
168-
}
169-
}
170-
171-
return stream;
172-
}
17394
}
17495
}

src/Microsoft.OpenApi/Interfaces/IOpenApiReader.cs

Lines changed: 14 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
// Licensed under the MIT license.
33

44
using System.IO;
5+
using System.Text.Json.Nodes;
56
using System.Threading;
67
using System.Threading.Tasks;
7-
using Microsoft.OpenApi.Models;
88
using Microsoft.OpenApi.Reader;
99

1010
namespace Microsoft.OpenApi.Interfaces
@@ -14,51 +14,6 @@ namespace Microsoft.OpenApi.Interfaces
1414
/// </summary>
1515
public interface IOpenApiReader
1616
{
17-
/// <summary>
18-
/// Reads the input URL and parses it into an Open API document.
19-
/// </summary>
20-
/// <param name="url">The input to read from.</param>
21-
/// <param name="diagnostic">The diagnostic entity containing information from the reading process.</param>
22-
/// <param name="settings"> The OpenApi reader settings.</param>
23-
/// <returns></returns>
24-
OpenApiDocument Read(string url, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null);
25-
26-
/// <summary>
27-
/// Reads the input stream and parses it into an Open API document.
28-
/// </summary>
29-
/// <param name="stream"> The input stream.</param>
30-
/// <param name="diagnostic">The diagnostic entity containing information from the reading process.</param>
31-
/// <param name="settings"> The OpenApi reader settings.</param>
32-
/// <returns></returns>
33-
OpenApiDocument Read(Stream stream, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null);
34-
35-
/// <summary>
36-
/// Reads the TextReader input and parses it into an Open API document.
37-
/// </summary>
38-
/// <param name="input">The TextReader input.</param>
39-
/// <param name="diagnostic">The diagnostic entity containing information from the reading process.</param>
40-
/// <param name="settings"> The OpenApi reader settings.</param>
41-
/// <returns></returns>
42-
OpenApiDocument Read(TextReader input, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null);
43-
44-
/// <summary>
45-
/// Reads the input URL and parses it into an Open API document.
46-
/// </summary>
47-
/// <param name="url">The input URL.</param>
48-
/// <param name="settings"> The OpenApi reader settings.</param>
49-
/// <param name="cancellationToken">Propagates notification that an operation should be cancelled.</param>
50-
/// <returns></returns>
51-
Task<ReadResult> ReadAsync(string url, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default);
52-
53-
/// <summary>
54-
/// Reads the input stream and parses it into an Open API document.
55-
/// </summary>
56-
/// <param name="stream">The input stream.</param>
57-
/// <param name="settings"> The OpenApi reader settings.</param>
58-
/// <param name="cancellationToken">Propagates notification that an operation should be cancelled.</param>
59-
/// <returns></returns>
60-
Task<ReadResult> ReadAsync(Stream stream, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default);
61-
6217
/// <summary>
6318
/// Reads the TextReader input and parses it into an Open API document.
6419
/// </summary>
@@ -69,53 +24,32 @@ public interface IOpenApiReader
6924
Task<ReadResult> ReadAsync(TextReader input, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default);
7025

7126
/// <summary>
72-
/// Reads the input string and parses it into an Open API document.
27+
/// Parses the JsonNode input into an Open API document.
7328
/// </summary>
74-
/// <param name="input">The input string.</param>
75-
/// <param name="diagnostic">The diagnostic entity containing information from the reading process.</param>
76-
/// <param name="settings"> The OpenApi reader settings.</param>
29+
/// <param name="jsonNode">The JsonNode input.</param>
30+
/// <param name="settings">The Reader settings to be used during parsing.</param>
31+
/// <param name="cancellationToken">Propagates notifications that operations should be cancelled.</param>
7732
/// <returns></returns>
78-
OpenApiDocument Parse(string input, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null);
79-
80-
/// <summary>
81-
/// Reads the input string and parses it into an Open API document.
82-
/// </summary>
83-
/// <typeparam name="T"></typeparam>
84-
/// <param name="input">Stream containing OpenAPI description to parse.</param>
85-
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
86-
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
87-
/// <param name="settings">The OpenApiReader settings.</param>
88-
/// <returns></returns>
89-
T Parse<T>(string input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
90-
91-
/// <summary>
92-
/// Reads the stream input and parses the fragment of an OpenAPI description into an Open API Element.
93-
/// </summary>
94-
/// <param name="input">Stream containing OpenAPI description to parse.</param>
95-
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
96-
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
97-
/// <param name="settings">The OpenApiReader settings.</param>
98-
/// <returns>Instance of newly created OpenApiDocument</returns>
99-
T Read<T>(Stream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
33+
Task<ReadResult> ReadAsync(JsonNode jsonNode, OpenApiReaderSettings settings, CancellationToken cancellationToken = default);
10034

10135
/// <summary>
10236
/// Reads the TextReader input and parses the fragment of an OpenAPI description into an Open API Element.
10337
/// </summary>
10438
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
10539
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
106-
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
40+
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
10741
/// <param name="settings">The OpenApiReader settings.</param>
108-
/// <returns>Instance of newly created OpenApiDocument</returns>
109-
T Read<T>(TextReader input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
42+
/// <returns>Instance of newly created IOpenApiElement.</returns>
43+
T ReadFragment<T>(TextReader input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
11044

11145
/// <summary>
112-
/// Reads the string input and parses the fragment of an OpenAPI description into an Open API Element.
46+
/// Reads the JsonNode input and parses the fragment of an OpenAPI description into an Open API Element.
11347
/// </summary>
114-
/// <param name="url">Url pointing to the document.</param>
48+
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
11549
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
116-
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
50+
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
11751
/// <param name="settings">The OpenApiReader settings.</param>
118-
/// <returns>Instance of newly created OpenApiDocument</returns>
119-
T Read<T>(string url, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
52+
/// <returns>Instance of newly created IOpenApiElement.</returns>
53+
T ReadFragment<T>(JsonNode input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
12054
}
12155
}

0 commit comments

Comments
 (0)