Skip to content

Commit 3b67916

Browse files
Copilotbaywet
andauthored
fix(reader): document parser-level exceptions
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
1 parent ef390a8 commit 3b67916

5 files changed

Lines changed: 108 additions & 20 deletions

File tree

src/Microsoft.OpenApi.YamlReader/OpenApiYamlReader.cs

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

4+
using System;
45
using System.IO;
5-
using System.Text.Json.Nodes;
6+
using System.Text;
67
using System.Text.Json;
8+
using System.Text.Json.Nodes;
79
using System.Threading;
810
using System.Threading.Tasks;
911
using Microsoft.OpenApi.Reader;
1012
using SharpYaml;
11-
using System;
12-
using System.Text;
1313

1414
namespace Microsoft.OpenApi.YamlReader
1515
{
@@ -58,6 +58,10 @@ public OpenApiYamlReader(OpenApiYamlReaderSettings settings)
5858
}
5959

6060
/// <inheritdoc/>
61+
/// <remarks>
62+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level YAML
63+
/// errors can throw before a <see cref="ReadResult"/> is created.
64+
/// </remarks>
6165
public async Task<ReadResult> ReadAsync(Stream input,
6266
Uri location,
6367
OpenApiReaderSettings settings,
@@ -68,8 +72,8 @@ public async Task<ReadResult> ReadAsync(Stream input,
6872
if (input is MemoryStream memoryStream)
6973
{
7074
return ReadCore(memoryStream, location, settings, cancellationToken);
71-
}
72-
else
75+
}
76+
else
7377
{
7478
using var preparedStream = new MemoryStream();
7579
try
@@ -95,6 +99,10 @@ await CopyToMemoryStreamAsync(
9599
}
96100

97101
/// <inheritdoc/>
102+
/// <remarks>
103+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level YAML
104+
/// errors can throw before a <see cref="ReadResult"/> is created.
105+
/// </remarks>
98106
public ReadResult Read(MemoryStream input,
99107
Uri location,
100108
OpenApiReaderSettings settings)
@@ -118,7 +126,7 @@ private ReadResult ReadCore(MemoryStream input,
118126
// this represents net core, net5 and up
119127
using var stream = new StreamReader(input, default, true, -1, settings.LeaveStreamOpen);
120128
#else
121-
// the implementation differs and results in a null reference exception in NETFX
129+
// the implementation differs and results in a null reference exception in NETFX
122130
using var stream = new StreamReader(input, Encoding.UTF8, true, 4096, settings.LeaveStreamOpen);
123131
#endif
124132
jsonNode = LoadJsonNodesFromYamlDocument(stream, cancellationToken);
@@ -194,6 +202,10 @@ public static ReadResult Read(JsonNode jsonNode, Uri location, OpenApiReaderSett
194202
}
195203

196204
/// <inheritdoc/>
205+
/// <remarks>
206+
/// OpenAPI semantic errors are returned in the <paramref name="diagnostic"/>. Syntax-level YAML
207+
/// errors can throw before a fragment is created.
208+
/// </remarks>
197209
public T? ReadFragment<T>(MemoryStream input,
198210
OpenApiSpecVersion version,
199211
OpenApiDocument openApiDocument,

src/Microsoft.OpenApi/Interfaces/IOpenApiReader.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ public interface IOpenApiReader
2222
/// <param name="settings"> The OpenApi reader settings.</param>
2323
/// <param name="cancellationToken">Propagates notification that an operation should be cancelled.</param>
2424
/// <returns></returns>
25+
/// <remarks>
26+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
27+
/// errors can throw before a <see cref="ReadResult"/> is created.
28+
/// </remarks>
2529
Task<ReadResult> ReadAsync(Stream input, Uri location, OpenApiReaderSettings settings, CancellationToken cancellationToken = default);
2630

2731
/// <summary>
@@ -31,6 +35,10 @@ public interface IOpenApiReader
3135
/// <param name="location">Location of where the document that is getting loaded is saved</param>
3236
/// <param name="settings"></param>
3337
/// <returns></returns>
38+
/// <remarks>
39+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
40+
/// errors can throw before a <see cref="ReadResult"/> is created.
41+
/// </remarks>
3442
ReadResult Read(MemoryStream input, Uri location, OpenApiReaderSettings settings);
3543

3644
/// <summary>
@@ -42,6 +50,10 @@ public interface IOpenApiReader
4250
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
4351
/// <param name="settings">The OpenApiReader settings.</param>
4452
/// <returns>Instance of newly created IOpenApiElement.</returns>
53+
/// <remarks>
54+
/// OpenAPI semantic errors are returned in the <paramref name="diagnostic"/>. Syntax-level JSON or YAML
55+
/// errors can throw before a fragment is created.
56+
/// </remarks>
4557
T? ReadFragment<T>(MemoryStream input, OpenApiSpecVersion version, OpenApiDocument openApiDocument, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings? settings = null) where T : IOpenApiElement;
4658
}
4759
}

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ public void RegisterComponents()
7979
/// <summary>
8080
/// A list of tags used by the specification with additional metadata.
8181
/// </summary>
82-
public ISet<OpenApiTag>? Tags
83-
{
82+
public ISet<OpenApiTag>? Tags
83+
{
8484
get
8585
{
8686
return _tags;
@@ -125,14 +125,14 @@ public ISet<OpenApiTag>? Tags
125125
/// <summary>
126126
/// Parameter-less constructor
127127
/// </summary>
128-
public OpenApiDocument()
128+
public OpenApiDocument()
129129
{
130130
Workspace = new OpenApiWorkspace();
131131
BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid());
132132
Info = new OpenApiInfo();
133133
Paths = new OpenApiPaths();
134134
}
135-
135+
136136
/// <summary>
137137
/// Initializes a copy of an an <see cref="OpenApiDocument"/> object
138138
/// </summary>
@@ -527,14 +527,14 @@ private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer>?
527527
.ToList();
528528

529529
// schemes
530-
writer.WriteOptionalCollection(OpenApiConstants.Schemes, schemes, (w, s) =>
530+
writer.WriteOptionalCollection(OpenApiConstants.Schemes, schemes, (w, s) =>
531531
{
532-
if(!string.IsNullOrEmpty(s) && s is not null)
532+
if (!string.IsNullOrEmpty(s) && s is not null)
533533
{
534534
w.WriteValue(s);
535535
}
536536
});
537-
}
537+
}
538538
}
539539

540540
/// <summary>
@@ -732,6 +732,10 @@ private static bool TryGetPlainNameFragment(string reference, out string fragmen
732732
/// <param name="format">The OpenAPI format to use during parsing.</param>
733733
/// <param name="settings">The OpenApi reader settings.</param>
734734
/// <returns></returns>
735+
/// <remarks>
736+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
737+
/// errors can throw before a <see cref="ReadResult"/> is created.
738+
/// </remarks>
735739
public static ReadResult Load(MemoryStream stream,
736740
string? format = null,
737741
OpenApiReaderSettings? settings = null)
@@ -746,6 +750,10 @@ public static ReadResult Load(MemoryStream stream,
746750
/// <param name="settings">The OpenApi reader settings.</param>
747751
/// <param name="token">The cancellation token</param>
748752
/// <returns></returns>
753+
/// <remarks>
754+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
755+
/// errors can throw before a <see cref="ReadResult"/> is created.
756+
/// </remarks>
749757
public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings? settings = null, CancellationToken token = default)
750758
{
751759
return await OpenApiModelFactory.LoadAsync(url, settings, token).ConfigureAwait(false);
@@ -759,6 +767,10 @@ public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings
759767
/// <param name="settings">The OpenApi reader settings.</param>
760768
/// <param name="cancellationToken">Propagates information about operation cancelling.</param>
761769
/// <returns></returns>
770+
/// <remarks>
771+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
772+
/// errors can throw before a <see cref="ReadResult"/> is created.
773+
/// </remarks>
762774
public static async Task<ReadResult> LoadAsync(Stream stream, string? format = null, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default)
763775
{
764776
return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken).ConfigureAwait(false);
@@ -772,6 +784,10 @@ public static async Task<ReadResult> LoadAsync(Stream stream, string? format = n
772784
/// <param name="format"></param>
773785
/// <param name="settings"></param>
774786
/// <returns></returns>
787+
/// <remarks>
788+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
789+
/// errors can throw before a <see cref="ReadResult"/> is created.
790+
/// </remarks>
775791
public static ReadResult Parse(string input,
776792
string? format = null,
777793
OpenApiReaderSettings? settings = null)
@@ -930,7 +946,7 @@ public override void Visit(IOpenApiSchema schema)
930946
{
931947
Schemas.Add(id, schema);
932948
}
933-
}
949+
}
934950
base.Visit(schema);
935951
}
936952
}

src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4+
using System;
45
using System.IO;
5-
using System.Text.Json.Nodes;
6+
using System.Linq;
67
using System.Text.Json;
8+
using System.Text.Json.Nodes;
79
using System.Threading;
810
using System.Threading.Tasks;
9-
using System.Linq;
10-
using System;
1111

1212
namespace Microsoft.OpenApi.Reader
1313
{
@@ -23,6 +23,10 @@ public class OpenApiJsonReader : IOpenApiReader
2323
/// <param name="location">Location of where the document that is getting loaded is saved</param>
2424
/// <param name="settings">The Reader settings to be used during parsing.</param>
2525
/// <returns></returns>
26+
/// <remarks>
27+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON
28+
/// errors can throw before a <see cref="ReadResult"/> is created.
29+
/// </remarks>
2630
public ReadResult Read(MemoryStream input,
2731
Uri location,
2832
OpenApiReaderSettings settings)
@@ -60,6 +64,10 @@ public ReadResult Read(MemoryStream input,
6064
/// <param name="location">Location of where the document that is getting loaded is saved</param>
6165
/// <param name="settings">The Reader settings to be used during parsing.</param>
6266
/// <returns></returns>
67+
/// <remarks>
68+
/// Use this overload when JSON has already been parsed into a <see cref="JsonNode"/>. OpenAPI semantic
69+
/// errors are returned in the <see cref="ReadResult.Diagnostic"/>.
70+
/// </remarks>
6371
public ReadResult Read(JsonNode jsonNode,
6472
Uri location,
6573
OpenApiReaderSettings settings)
@@ -91,7 +99,7 @@ public ReadResult Read(JsonNode jsonNode,
9199
if (document is not null && settings.RuleSet is not null && settings.RuleSet.Rules.Any())
92100
{
93101
var openApiErrors = document.Validate(settings.RuleSet);
94-
if(openApiErrors is not null)
102+
if (openApiErrors is not null)
95103
{
96104
foreach (var item in openApiErrors.OfType<OpenApiValidatorError>())
97105
{
@@ -101,7 +109,7 @@ public ReadResult Read(JsonNode jsonNode,
101109
{
102110
diagnostic.Warnings.Add(item);
103111
}
104-
}
112+
}
105113
}
106114
diagnostic.Format = OpenApiConstants.Json;
107115
return new()
@@ -119,6 +127,10 @@ public ReadResult Read(JsonNode jsonNode,
119127
/// <param name="settings">The Reader settings to be used during parsing.</param>
120128
/// <param name="cancellationToken">Propagates notifications that operations should be cancelled.</param>
121129
/// <returns></returns>
130+
/// <remarks>
131+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON
132+
/// errors can throw before a <see cref="ReadResult"/> is created.
133+
/// </remarks>
122134
public async Task<ReadResult> ReadAsync(Stream input,
123135
Uri location,
124136
OpenApiReaderSettings settings,
@@ -151,6 +163,10 @@ public async Task<ReadResult> ReadAsync(Stream input,
151163
}
152164

153165
/// <inheritdoc/>
166+
/// <remarks>
167+
/// OpenAPI semantic errors are returned in the <paramref name="diagnostic"/>. Syntax-level JSON
168+
/// errors can throw before a fragment is created.
169+
/// </remarks>
154170
public T? ReadFragment<T>(MemoryStream input,
155171
OpenApiSpecVersion version,
156172
OpenApiDocument openApiDocument,

src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public static class OpenApiModelFactory
2424
/// <param name="settings"> The OpenApi reader settings.</param>
2525
/// <param name="format">The OpenAPI format.</param>
2626
/// <returns>An OpenAPI document instance.</returns>
27+
/// <remarks>
28+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
29+
/// errors can throw before a <see cref="ReadResult"/> is created.
30+
/// </remarks>
2731
public static ReadResult Load(MemoryStream stream,
2832
string? format = null,
2933
OpenApiReaderSettings? settings = null)
@@ -59,6 +63,10 @@ public static ReadResult Load(MemoryStream stream,
5963
/// <param name="settings">The OpenApiReader settings.</param>
6064
/// <returns>Instance of newly created IOpenApiElement.</returns>
6165
/// <returns>The OpenAPI element.</returns>
66+
/// <remarks>
67+
/// OpenAPI semantic errors are returned in the <paramref name="diagnostic"/>. Syntax-level JSON or YAML
68+
/// errors can throw before a fragment is created.
69+
/// </remarks>
6270
public static T? Load<T>(MemoryStream input, OpenApiSpecVersion version, string? format, OpenApiDocument openApiDocument, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings? settings = null) where T : IOpenApiElement
6371
{
6472
format ??= InspectStreamFormat(input);
@@ -73,6 +81,10 @@ public static ReadResult Load(MemoryStream stream,
7381
/// <param name="settings"> The OpenApi reader settings.</param>
7482
/// <param name="token">The cancellation token</param>
7583
/// <returns></returns>
84+
/// <remarks>
85+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
86+
/// errors can throw before a <see cref="ReadResult"/> is created.
87+
/// </remarks>
7688
public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings? settings = null, CancellationToken token = default)
7789
{
7890
settings ??= DefaultReaderSettings.Value;
@@ -94,6 +106,10 @@ public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings
94106
/// <param name="token"></param>
95107
/// <returns>Instance of newly created IOpenApiElement.</returns>
96108
/// <returns>The OpenAPI element.</returns>
109+
/// <remarks>
110+
/// OpenAPI semantic errors are returned by the reader diagnostic. Syntax-level JSON or YAML errors can throw
111+
/// before a fragment is created.
112+
/// </remarks>
97113
public static async Task<T?> LoadAsync<T>(string url, OpenApiSpecVersion version, OpenApiDocument openApiDocument, OpenApiReaderSettings? settings = null, CancellationToken token = default) where T : IOpenApiElement
98114
{
99115
settings ??= DefaultReaderSettings.Value;
@@ -112,6 +128,10 @@ public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings
112128
/// <param name="cancellationToken">Propagates notification that operations should be cancelled.</param>
113129
/// <param name="format">The Open API format</param>
114130
/// <returns></returns>
131+
/// <remarks>
132+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
133+
/// errors can throw before a <see cref="ReadResult"/> is created.
134+
/// </remarks>
115135
public static async Task<ReadResult> LoadAsync(Stream input, string? format = null, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default)
116136
{
117137
#if NET6_0_OR_GREATER
@@ -159,6 +179,10 @@ public static async Task<ReadResult> LoadAsync(Stream input, string? format = nu
159179
/// <param name="settings"></param>
160180
/// <param name="token"></param>
161181
/// <returns></returns>
182+
/// <remarks>
183+
/// OpenAPI semantic errors are returned by the reader diagnostic. Syntax-level JSON or YAML errors can throw
184+
/// before a fragment is created.
185+
/// </remarks>
162186
public static async Task<T?> LoadAsync<T>(Stream input,
163187
OpenApiSpecVersion version,
164188
OpenApiDocument openApiDocument,
@@ -192,6 +216,10 @@ public static async Task<ReadResult> LoadAsync(Stream input, string? format = nu
192216
/// <param name="format">The Open API format</param>
193217
/// <param name="settings">The OpenApi reader settings.</param>
194218
/// <returns>An OpenAPI document instance.</returns>
219+
/// <remarks>
220+
/// OpenAPI semantic errors are returned in the <see cref="ReadResult.Diagnostic"/>. Syntax-level JSON or YAML
221+
/// errors can throw before a <see cref="ReadResult"/> is created.
222+
/// </remarks>
195223
public static ReadResult Parse(string input,
196224
string? format = null,
197225
OpenApiReaderSettings? settings = null)
@@ -220,6 +248,10 @@ public static ReadResult Parse(string input,
220248
/// <param name="format">The Open API format</param>
221249
/// <param name="settings">The OpenApi reader settings.</param>
222250
/// <returns>An OpenAPI document instance.</returns>
251+
/// <remarks>
252+
/// OpenAPI semantic errors are returned in the <paramref name="diagnostic"/>. Syntax-level JSON or YAML
253+
/// errors can throw before a fragment is created.
254+
/// </remarks>
223255
public static T? Parse<T>(string input,
224256
OpenApiSpecVersion version,
225257
OpenApiDocument openApiDocument,
@@ -437,7 +469,7 @@ private static async Task<MemoryStream> CopyToMemoryStreamAsync(Stream input, Ca
437469
#endif
438470
bufferStream.Position = 0;
439471
return bufferStream;
440-
}
472+
}
441473

442474
private static async Task<(Stream, string)> PrepareStreamForReadingAsync(Stream input, string? format, CancellationToken token = default)
443475
{

0 commit comments

Comments
 (0)