Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions src/Microsoft.OpenApi.YamlReader/OpenApiYamlReader.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.IO;
using System.Text.Json.Nodes;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.OpenApi.Reader;
using SharpYaml;
using System;
using System.Text;

namespace Microsoft.OpenApi.YamlReader
{
Expand Down Expand Up @@ -58,6 +58,9 @@ public OpenApiYamlReader(OpenApiYamlReaderSettings settings)
}

/// <inheritdoc/>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
public async Task<ReadResult> ReadAsync(Stream input,
Uri location,
OpenApiReaderSettings settings,
Expand All @@ -68,8 +71,8 @@ public async Task<ReadResult> ReadAsync(Stream input,
if (input is MemoryStream memoryStream)
{
return ReadCore(memoryStream, location, settings, cancellationToken);
}
else
}
else
{
using var preparedStream = new MemoryStream();
try
Expand All @@ -95,6 +98,9 @@ await CopyToMemoryStreamAsync(
}

/// <inheritdoc/>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
public ReadResult Read(MemoryStream input,
Uri location,
OpenApiReaderSettings settings)
Expand All @@ -118,7 +124,7 @@ private ReadResult ReadCore(MemoryStream input,
// this represents net core, net5 and up
using var stream = new StreamReader(input, default, true, -1, settings.LeaveStreamOpen);
#else
// the implementation differs and results in a null reference exception in NETFX
// the implementation differs and results in a null reference exception in NETFX
using var stream = new StreamReader(input, Encoding.UTF8, true, 4096, settings.LeaveStreamOpen);
#endif
jsonNode = LoadJsonNodesFromYamlDocument(stream, cancellationToken);
Expand Down Expand Up @@ -194,6 +200,9 @@ public static ReadResult Read(JsonNode jsonNode, Uri location, OpenApiReaderSett
}

/// <inheritdoc/>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <paramref name="diagnostic"/>.
/// </remarks>
public T? ReadFragment<T>(MemoryStream input,
OpenApiSpecVersion version,
OpenApiDocument openApiDocument,
Expand Down
9 changes: 9 additions & 0 deletions src/Microsoft.OpenApi/Interfaces/IOpenApiReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ public interface IOpenApiReader
/// <param name="settings"> The OpenApi reader settings.</param>
/// <param name="cancellationToken">Propagates notification that an operation should be cancelled.</param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
Comment thread
baywet marked this conversation as resolved.
Comment thread
baywet marked this conversation as resolved.
Task<ReadResult> ReadAsync(Stream input, Uri location, OpenApiReaderSettings settings, CancellationToken cancellationToken = default);

/// <summary>
Expand All @@ -31,6 +34,9 @@ public interface IOpenApiReader
/// <param name="location">Location of where the document that is getting loaded is saved</param>
/// <param name="settings"></param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
ReadResult Read(MemoryStream input, Uri location, OpenApiReaderSettings settings);

/// <summary>
Expand All @@ -42,6 +48,9 @@ public interface IOpenApiReader
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing.</param>
/// <param name="settings">The OpenApiReader settings.</param>
/// <returns>Instance of newly created IOpenApiElement.</returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <paramref name="diagnostic"/>.
/// </remarks>
T? ReadFragment<T>(MemoryStream input, OpenApiSpecVersion version, OpenApiDocument openApiDocument, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings? settings = null) where T : IOpenApiElement;
}
}
28 changes: 20 additions & 8 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@
/// <summary>
/// A list of tags used by the specification with additional metadata.
/// </summary>
public ISet<OpenApiTag>? Tags
{
public ISet<OpenApiTag>? Tags
{
get
{
return _tags;
Expand Down Expand Up @@ -125,14 +125,14 @@
/// <summary>
/// Parameter-less constructor
/// </summary>
public OpenApiDocument()
public OpenApiDocument()
{
Workspace = new OpenApiWorkspace();
BaseUri = new(OpenApiConstants.BaseRegistryUri + Guid.NewGuid());
Info = new OpenApiInfo();
Paths = new OpenApiPaths();
}

/// <summary>
/// Initializes a copy of an an <see cref="OpenApiDocument"/> object
/// </summary>
Expand Down Expand Up @@ -311,7 +311,7 @@
/// <summary>
/// Serialize <see cref="OpenApiDocument"/> to OpenAPI object V2.0.
/// </summary>
public void SerializeAsV2(IOpenApiWriter writer)

Check warning on line 314 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 31 to the 15 allowed.
{
Utils.CheckArgumentNull(writer);

Expand Down Expand Up @@ -460,7 +460,7 @@
return server.ReplaceServerUrlVariables([]);
}

private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer>? servers)

Check warning on line 463 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 21 to the 15 allowed.
{
if (servers == null || !servers.Any())
{
Expand Down Expand Up @@ -522,19 +522,19 @@
UriFormat.SafeUnescaped,
StringComparison.OrdinalIgnoreCase) ==
0 && u.IsAbsoluteUri)
.Select(u => u!.Scheme)

Check warning on line 525 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Remove this null-forgiving operator; the compiler already knows this expression is not null here.
.Distinct()
.ToList();

// schemes
writer.WriteOptionalCollection(OpenApiConstants.Schemes, schemes, (w, s) =>
writer.WriteOptionalCollection(OpenApiConstants.Schemes, schemes, (w, s) =>
{
if(!string.IsNullOrEmpty(s) && s is not null)
if (!string.IsNullOrEmpty(s) && s is not null)

Check warning on line 532 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Change this condition so that it does not always evaluate to 'True'.
Comment thread
baywet marked this conversation as resolved.
Dismissed
Comment thread
baywet marked this conversation as resolved.
Dismissed
{
w.WriteValue(s);
}
});
}
}
}

/// <summary>
Expand Down Expand Up @@ -593,7 +593,7 @@

return ConvertByteArrayToString(hash ?? []);

async Task WriteDocumentAsync(TextWriter writer, CancellationToken token)

Check warning on line 596 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Remove this unused method parameter 'token'.
{
var openApiJsonWriter = new OpenApiJsonWriter(writer, new() { Terse = true });
SerializeAsV31(openApiJsonWriter);
Expand Down Expand Up @@ -621,7 +621,7 @@
/// <summary>
/// Load the referenced <see cref="IOpenApiReferenceable"/> object from a <see cref="BaseOpenApiReference"/> object
/// </summary>
internal IOpenApiReferenceable? ResolveReference(BaseOpenApiReference? reference, bool useExternal, IOpenApiSchema? parentSchema)

Check warning on line 624 in src/Microsoft.OpenApi/Models/OpenApiDocument.cs

View workflow job for this annotation

GitHub Actions / Build

Refactor this method to reduce its Cognitive Complexity from 24 to the 15 allowed.
{
if (reference == null)
{
Expand Down Expand Up @@ -732,6 +732,9 @@
/// <param name="format">The OpenAPI format to use during parsing.</param>
/// <param name="settings">The OpenApi reader settings.</param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
Comment thread
baywet marked this conversation as resolved.
public static ReadResult Load(MemoryStream stream,
string? format = null,
OpenApiReaderSettings? settings = null)
Expand All @@ -746,6 +749,9 @@
/// <param name="settings">The OpenApi reader settings.</param>
/// <param name="token">The cancellation token</param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
public static async Task<ReadResult> LoadAsync(string url, OpenApiReaderSettings? settings = null, CancellationToken token = default)
{
return await OpenApiModelFactory.LoadAsync(url, settings, token).ConfigureAwait(false);
Expand All @@ -759,6 +765,9 @@
/// <param name="settings">The OpenApi reader settings.</param>
/// <param name="cancellationToken">Propagates information about operation cancelling.</param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
Comment thread
baywet marked this conversation as resolved.
public static async Task<ReadResult> LoadAsync(Stream stream, string? format = null, OpenApiReaderSettings? settings = null, CancellationToken cancellationToken = default)
{
return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken).ConfigureAwait(false);
Expand All @@ -772,6 +781,9 @@
/// <param name="format"></param>
/// <param name="settings"></param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>; a null or empty input string throws <see cref="ArgumentException"/>.
/// </remarks>
public static ReadResult Parse(string input,
string? format = null,
OpenApiReaderSettings? settings = null)
Expand Down Expand Up @@ -930,7 +942,7 @@
{
Schemas.Add(id, schema);
}
}
}
base.Visit(schema);
}
}
Expand Down
47 changes: 35 additions & 12 deletions src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.IO;
using System.Text.Json.Nodes;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using System;

namespace Microsoft.OpenApi.Reader
{
Expand All @@ -23,6 +23,9 @@
/// <param name="location">Location of where the document that is getting loaded is saved</param>
/// <param name="settings">The Reader settings to be used during parsing.</param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
Comment thread
baywet marked this conversation as resolved.
public ReadResult Read(MemoryStream input,
Uri location,
OpenApiReaderSettings settings)
Expand All @@ -32,16 +35,16 @@

JsonNode? jsonNode;
var diagnostic = new OpenApiDiagnostic();
settings ??= new OpenApiReaderSettings();

Check warning on line 38 in src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

View workflow job for this annotation

GitHub Actions / Build

Remove this unnecessary check for null. Some code paths are unreachable.

// Parse the JSON text in the stream into JsonNodes
try
{
jsonNode = JsonNode.Parse(input) ?? throw new InvalidOperationException($"Cannot parse input stream, {nameof(input)}.");
}
catch (JsonException ex)
catch (Exception ex) when (ex is JsonException or InvalidOperationException)
{
diagnostic.Errors.Add(new OpenApiError($"#line={ex.LineNumber}", $"Please provide the correct format, {ex.Message}"));
diagnostic.Errors.Add(CreateOpenApiError(ex, true));
diagnostic.Format = OpenApiConstants.Json;
return new ReadResult
{
Expand All @@ -60,6 +63,10 @@
/// <param name="location">Location of where the document that is getting loaded is saved</param>
/// <param name="settings">The Reader settings to be used during parsing.</param>
/// <returns></returns>
/// <remarks>
/// Use this overload when JSON has already been parsed into a <see cref="JsonNode"/>. OpenAPI semantic
/// errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
public ReadResult Read(JsonNode jsonNode,
Uri location,
OpenApiReaderSettings settings)
Expand Down Expand Up @@ -91,7 +98,7 @@
if (document is not null && settings.RuleSet is not null && settings.RuleSet.Rules.Any())
{
var openApiErrors = document.Validate(settings.RuleSet);
if(openApiErrors is not null)
if (openApiErrors is not null)
{
foreach (var item in openApiErrors.OfType<OpenApiValidatorError>())
{
Expand All @@ -101,7 +108,7 @@
{
diagnostic.Warnings.Add(item);
}
}
}
}
diagnostic.Format = OpenApiConstants.Json;
return new()
Expand All @@ -119,6 +126,9 @@
/// <param name="settings">The Reader settings to be used during parsing.</param>
/// <param name="cancellationToken">Propagates notifications that operations should be cancelled.</param>
/// <returns></returns>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <see cref="ReadResult.Diagnostic"/>.
/// </remarks>
public async Task<ReadResult> ReadAsync(Stream input,
Uri location,
OpenApiReaderSettings settings,
Expand All @@ -136,9 +146,9 @@
jsonNode = await JsonNode.ParseAsync(input, cancellationToken: cancellationToken).ConfigureAwait(false) ??
throw new InvalidOperationException($"failed to parse input stream, {nameof(input)}");
}
catch (JsonException ex)
catch (Exception ex) when (ex is JsonException or InvalidOperationException)
{
diagnostic.Errors.Add(new OpenApiError($"#line={ex.LineNumber}", $"Please provide the correct format, {ex.Message}"));
diagnostic.Errors.Add(CreateOpenApiError(ex, true));
diagnostic.Format = OpenApiConstants.Json;
return new ReadResult
{
Expand All @@ -151,7 +161,10 @@
}

/// <inheritdoc/>
/// <remarks>
/// OpenAPI semantic and parser errors are returned in the <paramref name="diagnostic"/>.
/// </remarks>
Comment thread
baywet marked this conversation as resolved.
public T? ReadFragment<T>(MemoryStream input,

Check warning on line 167 in src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

View workflow job for this annotation

GitHub Actions / Build

All 'ReadFragment' method overloads should be adjacent.

Check warning on line 167 in src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

All 'ReadFragment' method overloads should be adjacent.

See more on https://sonarcloud.io/project/issues?id=microsoft_OpenAPI.NET&issues=AaCQzHPVr_5U7sVKRuRu&open=AaCQzHPVr_5U7sVKRuRu&pullRequest=3078
OpenApiSpecVersion version,
OpenApiDocument openApiDocument,
out OpenApiDiagnostic diagnostic,
Expand All @@ -167,16 +180,26 @@
{
jsonNode = JsonNode.Parse(input) ?? throw new InvalidOperationException($"Failed to parse stream, {nameof(input)}");
}
catch (JsonException ex)
catch (Exception ex) when (ex is JsonException or InvalidOperationException)
{
diagnostic = new();
diagnostic.Errors.Add(new($"#line={ex.LineNumber}", ex.Message));
diagnostic = new()
{
Format = OpenApiConstants.Json,
};
diagnostic.Errors.Add(CreateOpenApiError(ex, false));
return default;
}

return ReadFragment<T>(jsonNode, version, openApiDocument, out diagnostic);
}

private static OpenApiError CreateOpenApiError(Exception ex, bool includeFormatHint)
{
return ex is JsonException jsonException
? new OpenApiError($"#line={jsonException.LineNumber}", includeFormatHint ? $"Please provide the correct format, {jsonException.Message}" : jsonException.Message)

Check warning on line 199 in src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

View workflow job for this annotation

GitHub Actions / Build

Extract this nested ternary operation into an independent statement.

Check warning on line 199 in src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested ternary operation into an independent statement.

See more on https://sonarcloud.io/project/issues?id=microsoft_OpenAPI.NET&issues=AaCQzHPVr_5U7sVKRuRv&open=AaCQzHPVr_5U7sVKRuRv&pullRequest=3078
: new OpenApiError(null, ex.Message);
}

/// <inheritdoc/>
public T? ReadFragment<T>(JsonNode input,
OpenApiSpecVersion version,
Expand Down
Loading
Loading