Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor readers to reduce surface area #1975

Open
wants to merge 18 commits into
base: vnext
Choose a base branch
from
Open
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
24 changes: 12 additions & 12 deletions src/Microsoft.OpenApi.Hidi/OpenApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog

#pragma warning restore CA1308 // Normalize strings to uppercase
options.Output = new($"./output{inputExtension}");
};
}

if (options.CleanOutput && options.Output.Exists)
{
Expand Down Expand Up @@ -97,8 +97,7 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog
}

// Load OpenAPI document
var format = OpenApiModelFactory.GetFormat(options.OpenApi);
var document = await GetOpenApiAsync(options, format, logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false);
var document = await GetOpenApiAsync(options, openApiFormat.GetDisplayName(), logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false);

if (options.FilterOptions != null)
{
Expand Down Expand Up @@ -254,7 +253,7 @@ private static async Task<OpenApiDocument> GetOpenApiAsync(HidiOptions options,
else if (!string.IsNullOrEmpty(options.OpenApi))
{
stream = await GetStreamAsync(options.OpenApi, logger, cancellationToken).ConfigureAwait(false);
var result = await ParseOpenApiAsync(options.OpenApi, options.InlineExternal, logger, stream, cancellationToken).ConfigureAwait(false);
var result = await ParseOpenApiAsync(options.OpenApi, format, options.InlineExternal, logger, stream, cancellationToken).ConfigureAwait(false);
document = result.OpenApiDocument;
}
else throw new InvalidOperationException("No input file path or URL provided");
Expand Down Expand Up @@ -351,8 +350,8 @@ private static MemoryStream ApplyFilterToCsdl(Stream csdlStream, string entitySe
try
{
using var stream = await GetStreamAsync(openApi, logger, cancellationToken).ConfigureAwait(false);

result = await ParseOpenApiAsync(openApi, false, logger, stream, cancellationToken).ConfigureAwait(false);
var openApiFormat = !string.IsNullOrEmpty(openApi) ? GetOpenApiFormat(openApi, logger) : OpenApiFormat.Yaml;
result = await ParseOpenApiAsync(openApi, openApiFormat.GetDisplayName(),false, logger, stream, cancellationToken).ConfigureAwait(false);

using (logger.BeginScope("Calculating statistics"))
{
Expand Down Expand Up @@ -380,7 +379,7 @@ private static MemoryStream ApplyFilterToCsdl(Stream csdlStream, string entitySe
return result.OpenApiDiagnostic.Errors.Count == 0;
}

private static async Task<ReadResult> ParseOpenApiAsync(string openApiFile, bool inlineExternal, ILogger logger, Stream stream, CancellationToken cancellationToken = default)
private static async Task<ReadResult> ParseOpenApiAsync(string openApiFile, string format, bool inlineExternal, ILogger logger, Stream stream, CancellationToken cancellationToken = default)
{
ReadResult result;
var stopwatch = Stopwatch.StartNew();
Expand All @@ -396,7 +395,6 @@ private static async Task<ReadResult> ParseOpenApiAsync(string openApiFile, bool
new Uri("file://" + new FileInfo(openApiFile).DirectoryName + Path.DirectorySeparatorChar)
};

var format = OpenApiModelFactory.GetFormat(openApiFile);
result = await OpenApiDocument.LoadAsync(stream, format, settings, cancellationToken).ConfigureAwait(false);

logger.LogTrace("{Timestamp}ms: Completed parsing.", stopwatch.ElapsedMilliseconds);
Expand Down Expand Up @@ -587,8 +585,8 @@ private static string GetInputPathExtension(string? openapi = null, string? csdl
throw new ArgumentException("Please input a file path or URL");
}

var format = OpenApiModelFactory.GetFormat(options.OpenApi);
var document = await GetOpenApiAsync(options, format, logger, null, cancellationToken).ConfigureAwait(false);
var openApiFormat = options.OpenApiFormat ?? (!string.IsNullOrEmpty(options.OpenApi) ? GetOpenApiFormat(options.OpenApi, logger) : OpenApiFormat.Yaml);
var document = await GetOpenApiAsync(options, openApiFormat.GetDisplayName(), logger, null, cancellationToken).ConfigureAwait(false);

using (logger.BeginScope("Creating diagram"))
{
Expand Down Expand Up @@ -748,9 +746,11 @@ internal static async Task PluginManifestAsync(HidiOptions options, ILogger logg
options.OpenApi = apiDependency.ApiDescripionUrl;
}

var openApiFormat = options.OpenApiFormat ?? (!string.IsNullOrEmpty(options.OpenApi)
? GetOpenApiFormat(options.OpenApi, logger) : OpenApiFormat.Yaml);

// Load OpenAPI document
var format = OpenApiModelFactory.GetFormat(options.OpenApi);
var document = await GetOpenApiAsync(options, format, logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false);
var document = await GetOpenApiAsync(options, openApiFormat.GetDisplayName(), logger, options.MetadataVersion, cancellationToken).ConfigureAwait(false);

cancellationToken.ThrowIfCancellationRequested();

Expand Down
39 changes: 30 additions & 9 deletions src/Microsoft.OpenApi.Readers/OpenApiYamlReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,37 @@ namespace Microsoft.OpenApi.Readers
/// </summary>
public class OpenApiYamlReader : IOpenApiReader
{
private const int copyBufferSize = 4096;

/// <inheritdoc/>
public async Task<ReadResult> ReadAsync(TextReader input,
OpenApiReaderSettings settings = null,
public async Task<ReadResult> ReadAsync(Stream input,
OpenApiReaderSettings settings,
CancellationToken cancellationToken = default)
{
if (input is MemoryStream memoryStream)
{
return Read(memoryStream, settings);
}
else
{
using var preparedStream = new MemoryStream();
await input.CopyToAsync(preparedStream, copyBufferSize, cancellationToken);
preparedStream.Position = 0;
return Read(preparedStream, settings);
}
}

/// <inheritdoc/>
public ReadResult Read(MemoryStream input,
OpenApiReaderSettings settings)
{
JsonNode jsonNode;

// Parse the YAML text in the TextReader into a sequence of JsonNodes
try
{
jsonNode = LoadJsonNodesFromYamlDocument(input);
using var stream = new StreamReader(input, default, true, -1, settings.LeaveStreamOpen);
Fixed Show fixed Hide fixed
jsonNode = LoadJsonNodesFromYamlDocument(stream);
}
catch (JsonException ex)
{
Expand All @@ -42,11 +62,11 @@ public async Task<ReadResult> ReadAsync(TextReader input,
};
}

return await ReadAsync(jsonNode, settings, cancellationToken: cancellationToken);
return Read(jsonNode, settings);
}

/// <inheritdoc/>
public T ReadFragment<T>(TextReader input,
public T ReadFragment<T>(MemoryStream input,
OpenApiSpecVersion version,
out OpenApiDiagnostic diagnostic,
OpenApiReaderSettings settings = null) where T : IOpenApiElement
Expand All @@ -56,7 +76,8 @@ public T ReadFragment<T>(TextReader input,
// Parse the YAML
try
{
jsonNode = LoadJsonNodesFromYamlDocument(input);
using var stream = new StreamReader(input);
jsonNode = LoadJsonNodesFromYamlDocument(stream);
}
catch (JsonException ex)
{
Expand All @@ -81,10 +102,10 @@ static JsonNode LoadJsonNodesFromYamlDocument(TextReader input)
return yamlDocument.ToJsonNode();
}

/// <inheritdoc/>
public async Task<ReadResult> ReadAsync(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null, CancellationToken cancellationToken = default)
/// <inheritdoc/>
public ReadResult Read(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null)
{
return await OpenApiReaderRegistry.DefaultReader.ReadAsync(jsonNode, settings, OpenApiConstants.Yaml, cancellationToken);
return OpenApiReaderRegistry.DefaultReader.Read(jsonNode, settings, OpenApiConstants.Yaml);
}

/// <inheritdoc/>
Expand Down
19 changes: 13 additions & 6 deletions src/Microsoft.OpenApi/Interfaces/IOpenApiReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,40 @@ namespace Microsoft.OpenApi.Interfaces
public interface IOpenApiReader
{
/// <summary>
/// Reads the TextReader input and parses it into an Open API document.
/// Async method to reads the stream and parse it into an Open API document.
/// </summary>
/// <param name="input">The TextReader input.</param>
/// <param name="settings"> The OpenApi reader settings.</param>
/// <param name="cancellationToken">Propagates notification that an operation should be cancelled.</param>
/// <returns></returns>
Task<ReadResult> ReadAsync(TextReader input, OpenApiReaderSettings settings = null, CancellationToken cancellationToken = default);
Task<ReadResult> ReadAsync(Stream input, OpenApiReaderSettings settings, CancellationToken cancellationToken = default);

/// <summary>
/// Provides a synchronous method to read the input memory stream and parse it into an Open API document.
/// </summary>
/// <param name="input"></param>
/// <param name="settings"></param>
/// <returns></returns>
ReadResult Read(MemoryStream input, OpenApiReaderSettings settings);

/// <summary>
/// Parses the JsonNode input into an Open API document.
/// </summary>
/// <param name="jsonNode">The JsonNode input.</param>
/// <param name="settings">The Reader settings to be used during parsing.</param>
/// <param name="cancellationToken">Propagates notifications that operations should be cancelled.</param>
/// <param name="format">The OpenAPI format.</param>
/// <returns></returns>
Task<ReadResult> ReadAsync(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null, CancellationToken cancellationToken = default);
ReadResult Read(JsonNode jsonNode, OpenApiReaderSettings settings, string format = null);

/// <summary>
/// Reads the TextReader input and parses the fragment of an OpenAPI description into an Open API Element.
/// Reads the MemoryStream and parses the fragment of an OpenAPI description into an Open API Element.
/// </summary>
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
/// <param name="version">Version of the OpenAPI specification that the fragment conforms to.</param>
/// <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>
T ReadFragment<T>(TextReader input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;
T ReadFragment<T>(MemoryStream input, OpenApiSpecVersion version, out OpenApiDiagnostic diagnostic, OpenApiReaderSettings settings = null) where T : IOpenApiElement;

/// <summary>
/// Reads the JsonNode input and parses the fragment of an OpenAPI description into an Open API Element.
Expand Down
40 changes: 2 additions & 38 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public void SerializeAsV31(IOpenApiWriter writer)

writer.WriteStartObject();

// openApi;
// openApi
writer.WriteProperty(OpenApiConstants.OpenApi, "3.1.1");

// jsonSchemaDialect
Expand Down Expand Up @@ -535,45 +535,20 @@ private static string ConvertByteArrayToString(byte[] hash)
return Workspace?.ResolveReference<IOpenApiReferenceable>(uriLocation);
}

/// <summary>
/// Parses a local file path or Url into an Open API document.
/// </summary>
/// <param name="url"> The path to the OpenAPI file.</param>
/// <param name="settings"></param>
/// <returns></returns>
public static ReadResult Load(string url, OpenApiReaderSettings? settings = null)
{
return OpenApiModelFactory.Load(url, settings);
}

/// <summary>
/// Reads the stream input and parses it into an Open API document.
/// </summary>
/// <param name="stream">Stream containing OpenAPI description to parse.</param>
/// <param name="format">The OpenAPI format to use during parsing.</param>
/// <param name="settings">The OpenApi reader settings.</param>
/// <returns></returns>
public static ReadResult Load(Stream stream,
public static ReadResult Load(MemoryStream stream,
string format,
OpenApiReaderSettings? settings = null)
{
return OpenApiModelFactory.Load(stream, format, settings);
}

/// <summary>
/// Reads the text reader content and parses it into an Open API document.
/// </summary>
/// <param name="input">TextReader containing OpenAPI description to parse.</param>
/// <param name="format"> The OpenAPI format to use during parsing.</param>
/// <param name="settings">The OpenApi reader settings.</param>
/// <returns></returns>
public static ReadResult Load(TextReader input,
string format,
OpenApiReaderSettings? settings = null)
{
return OpenApiModelFactory.Load(input, format, settings);
}

/// <summary>
/// Parses a local file path or Url into an Open API document.
/// </summary>
Expand All @@ -598,17 +573,6 @@ public static async Task<ReadResult> LoadAsync(Stream stream, string format, Ope
return await OpenApiModelFactory.LoadAsync(stream, format, settings, cancellationToken);
}

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

/// <summary>
/// Parses a string into a <see cref="OpenApiDocument"/> object.
Expand Down
Loading
Loading