Skip to content

Commit

Permalink
Merge pull request #2051 from microsoft/fix/async-surface
Browse files Browse the repository at this point in the history
fix/async surface
  • Loading branch information
baywet authored Jan 9, 2025
2 parents 15c1122 + 1a1e013 commit e9e214f
Show file tree
Hide file tree
Showing 55 changed files with 486 additions and 433 deletions.
10 changes: 5 additions & 5 deletions src/Microsoft.OpenApi.Hidi/OpenApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog
var walker = new OpenApiWalker(powerShellFormatter);
walker.Walk(document);
}
WriteOpenApi(options, openApiFormat, openApiVersion, document, logger);
await WriteOpenApiAsync(options, openApiFormat, openApiVersion, document, logger, cancellationToken).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
Expand Down Expand Up @@ -191,7 +191,7 @@ private static OpenApiDocument ApplyFilters(HidiOptions options, ILogger logger,
return document;
}

private static void WriteOpenApi(HidiOptions options, OpenApiFormat openApiFormat, OpenApiSpecVersion openApiVersion, OpenApiDocument document, ILogger logger)
private static async Task WriteOpenApiAsync(HidiOptions options, OpenApiFormat openApiFormat, OpenApiSpecVersion openApiVersion, OpenApiDocument document, ILogger logger, CancellationToken cancellationToken)
{
using (logger.BeginScope("Output"))
{
Expand All @@ -216,11 +216,11 @@ private static void WriteOpenApi(HidiOptions options, OpenApiFormat openApiForma

var stopwatch = new Stopwatch();
stopwatch.Start();
document.Serialize(writer, openApiVersion);
await document.SerializeAsync(writer, openApiVersion, cancellationToken).ConfigureAwait(false);
stopwatch.Stop();

logger.LogTrace("Finished serializing in {ElapsedMilliseconds}ms", stopwatch.ElapsedMilliseconds);
textWriter.Flush();
await textWriter.FlushAsync(cancellationToken).ConfigureAwait(false);
}
}

Expand Down Expand Up @@ -769,7 +769,7 @@ internal static async Task PluginManifestAsync(HidiOptions options, ILogger logg
// Write OpenAPI to Output folder
options.Output = new(Path.Combine(options.OutputFolder, "openapi.json"));
options.TerseOutput = true;
WriteOpenApi(options, OpenApiFormat.Json, OpenApiSpecVersion.OpenApi3_1, document, logger);
await WriteOpenApiAsync(options, OpenApiFormat.Json, OpenApiSpecVersion.OpenApi3_1, document, logger, cancellationToken).ConfigureAwait(false);

// Create OpenAIPluginManifest from ApiDependency and OpenAPI document
var manifest = new OpenAIPluginManifest(document.Info?.Title ?? "Title", document.Info?.Title ?? "Title", "https://go.microsoft.com/fwlink/?LinkID=288890", document.Info?.Contact?.Email ?? "placeholder@contoso.com", document.Info?.License?.Url.ToString() ?? "https://placeholderlicenseurl.com")
Expand Down
10 changes: 5 additions & 5 deletions src/Microsoft.OpenApi.Workbench/MainModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ internal async Task ParseDocumentAsync()

stopwatch.Reset();
stopwatch.Start();
Output = WriteContents(document);
Output = await WriteContentsAsync(document);
stopwatch.Stop();

RenderTime = $"{stopwatch.ElapsedMilliseconds} ms";
Expand Down Expand Up @@ -299,23 +299,23 @@ internal async Task ParseDocumentAsync()
/// <summary>
/// Write content from the given document based on the format and version set in this class.
/// </summary>
private string WriteContents(OpenApiDocument document)
private async Task<string> WriteContentsAsync(OpenApiDocument document)
{
var outputStream = new MemoryStream();

document.Serialize(
await document.SerializeAsync(
outputStream,
Version,
Format,
new()
(Writers.OpenApiWriterSettings)new()
{
InlineLocalReferences = InlineLocal,
InlineExternalReferences = InlineExternal
});

outputStream.Position = 0;

return new StreamReader(outputStream).ReadToEnd();
return await new StreamReader(outputStream).ReadToEndAsync();
}

private static MemoryStream CreateStream(string text)
Expand Down
63 changes: 41 additions & 22 deletions src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.OpenApi.Exceptions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Properties;
Expand All @@ -22,10 +24,11 @@ public static class OpenApiSerializableExtensions
/// <param name="element">The Open API element.</param>
/// <param name="stream">The output stream.</param>
/// <param name="specVersion">The Open API specification version.</param>
public static void SerializeAsJson<T>(this T element, Stream stream, OpenApiSpecVersion specVersion)
/// <param name="cancellationToken">The cancellation token.</param>
public static Task SerializeAsJsonAsync<T>(this T element, Stream stream, OpenApiSpecVersion specVersion, CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
element.Serialize(stream, specVersion, OpenApiFormat.Json);
return element.SerializeAsync(stream, specVersion, OpenApiFormat.Json, cancellationToken);
}

/// <summary>
Expand All @@ -35,10 +38,11 @@ public static void SerializeAsJson<T>(this T element, Stream stream, OpenApiSpec
/// <param name="element">The Open API element.</param>
/// <param name="stream">The output stream.</param>
/// <param name="specVersion">The Open API specification version.</param>
public static void SerializeAsYaml<T>(this T element, Stream stream, OpenApiSpecVersion specVersion)
/// <param name="cancellationToken">The cancellation token.</param>
public static Task SerializeAsYamlAsync<T>(this T element, Stream stream, OpenApiSpecVersion specVersion, CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
element.Serialize(stream, specVersion, OpenApiFormat.Yaml);
return element.SerializeAsync(stream, specVersion, OpenApiFormat.Yaml, cancellationToken);
}

/// <summary>
Expand All @@ -50,14 +54,16 @@ public static void SerializeAsYaml<T>(this T element, Stream stream, OpenApiSpec
/// <param name="stream">The given stream.</param>
/// <param name="specVersion">The Open API specification version.</param>
/// <param name="format">The output format (JSON or YAML).</param>
public static void Serialize<T>(
/// <param name="cancellationToken">The cancellation token.</param>
public static Task SerializeAsync<T>(
this T element,
Stream stream,
OpenApiSpecVersion specVersion,
OpenApiFormat format)
OpenApiFormat format,
CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
element.Serialize(stream, specVersion, format, null);
return element.SerializeAsync(stream, specVersion, format, null, cancellationToken);
}

/// <summary>
Expand All @@ -70,12 +76,14 @@ public static void Serialize<T>(
/// <param name="specVersion">The Open API specification version.</param>
/// <param name="format">The output format (JSON or YAML).</param>
/// <param name="settings">Provide configuration settings for controlling writing output</param>
public static void Serialize<T>(
/// <param name="cancellationToken">The cancellation token.</param>
public static Task SerializeAsync<T>(
this T element,
Stream stream,
OpenApiSpecVersion specVersion,
OpenApiFormat format,
OpenApiWriterSettings settings)
OpenApiWriterSettings settings,
CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
Utils.CheckArgumentNull(stream);
Expand All @@ -88,7 +96,7 @@ public static void Serialize<T>(
OpenApiFormat.Yaml => new OpenApiYamlWriter(streamWriter, settings),
_ => throw new OpenApiException(string.Format(SRResource.OpenApiFormatNotSupported, format)),
};
element.Serialize(writer, specVersion);
return element.SerializeAsync(writer, specVersion, cancellationToken);
}

/// <summary>
Expand All @@ -98,7 +106,8 @@ public static void Serialize<T>(
/// <param name="element">The Open API element.</param>
/// <param name="writer">The output writer.</param>
/// <param name="specVersion">Version of the specification the output should conform to</param>
public static void Serialize<T>(this T element, IOpenApiWriter writer, OpenApiSpecVersion specVersion)
/// <param name="cancellationToken">The cancellation token.</param>
public static Task SerializeAsync<T>(this T element, IOpenApiWriter writer, OpenApiSpecVersion specVersion, CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
Utils.CheckArgumentNull(element);
Expand All @@ -122,7 +131,7 @@ public static void Serialize<T>(this T element, IOpenApiWriter writer, OpenApiSp
throw new OpenApiException(string.Format(SRResource.OpenApiSpecVersionNotSupported, specVersion));
}

writer.Flush();
return writer.FlushAsync(cancellationToken);
}

/// <summary>
Expand All @@ -131,12 +140,14 @@ public static void Serialize<T>(this T element, IOpenApiWriter writer, OpenApiSp
/// <typeparam name="T">the <see cref="IOpenApiSerializable"/></typeparam>
/// <param name="element">The Open API element.</param>
/// <param name="specVersion">The Open API specification version.</param>
public static string SerializeAsJson<T>(
/// <param name="cancellationToken">The cancellation token.</param>
public static Task<string> SerializeAsJsonAsync<T>(
this T element,
OpenApiSpecVersion specVersion)
OpenApiSpecVersion specVersion,
CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
return element.Serialize(specVersion, OpenApiFormat.Json);
return element.SerializeAsync(specVersion, OpenApiFormat.Json, cancellationToken);
}

/// <summary>
Expand All @@ -145,12 +156,14 @@ public static string SerializeAsJson<T>(
/// <typeparam name="T">the <see cref="IOpenApiSerializable"/></typeparam>
/// <param name="element">The Open API element.</param>
/// <param name="specVersion">The Open API specification version.</param>
public static string SerializeAsYaml<T>(
/// <param name="cancellationToken">The cancellation token.</param>
public static Task<string> SerializeAsYamlAsync<T>(
this T element,
OpenApiSpecVersion specVersion)
OpenApiSpecVersion specVersion,
CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
return element.Serialize(specVersion, OpenApiFormat.Yaml);
return element.SerializeAsync(specVersion, OpenApiFormat.Yaml, cancellationToken);
}

/// <summary>
Expand All @@ -160,20 +173,26 @@ public static string SerializeAsYaml<T>(
/// <param name="element">The Open API element.</param>
/// <param name="specVersion">The Open API specification version.</param>
/// <param name="format">Open API document format.</param>
public static string Serialize<T>(
/// <param name="cancellationToken">The cancellation token.</param>
public static async Task<string> SerializeAsync<T>(
this T element,
OpenApiSpecVersion specVersion,
OpenApiFormat format)
OpenApiFormat format,
CancellationToken cancellationToken = default)
where T : IOpenApiSerializable
{
Utils.CheckArgumentNull(element);

using var stream = new MemoryStream();
element.Serialize(stream, specVersion, format);
await element.SerializeAsync(stream, specVersion, format, cancellationToken).ConfigureAwait(false);
stream.Position = 0;

using var streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
#if NET7_0_OR_GREATER
return await streamReader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
#else
return await streamReader.ReadToEndAsync().ConfigureAwait(false);
#endif
}
}
}
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Microsoft.OpenApi.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<LangVersion>Latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>2.0.0-preview4</Version>
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ private void RenderComponents(IOpenApiWriter writer, Action<IOpenApiWriter, IOpe
{
var loops = writer.GetSettings().LoopDetector.Loops;
writer.WriteStartObject();
if (loops.TryGetValue(typeof(OpenApiSchema), out List<object> schemas))
if (loops.TryGetValue(typeof(OpenApiSchema), out var schemas))
{
writer.WriteOptionalMap(OpenApiConstants.Schemas, Schemas, callback);
}
Expand Down
28 changes: 13 additions & 15 deletions src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,6 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenAp
/// </summary>
public IDictionary<string, IOpenApiExtension>? Extensions { get; set; } = new Dictionary<string, IOpenApiExtension>();

/// <summary>
/// The unique hash code of the generated OpenAPI document
/// </summary>
public string HashCode => GenerateHashValue(this);

/// <inheritdoc />
public IDictionary<string, object>? Annotations { get; set; }

Expand Down Expand Up @@ -243,7 +238,7 @@ public void SerializeAsV2(IOpenApiWriter writer)
{
var loops = writer.GetSettings().LoopDetector.Loops;

if (loops.TryGetValue(typeof(OpenApiSchema), out List<object> schemas))
if (loops.TryGetValue(typeof(OpenApiSchema), out var schemas))
{
var openApiSchemas = schemas.Cast<OpenApiSchema>().Distinct().ToList()
.ToDictionary<OpenApiSchema, string>(k => k.Reference.Id);
Expand Down Expand Up @@ -414,14 +409,15 @@ private static void WriteHostInfoV2(IOpenApiWriter writer, IList<OpenApiServer>?
return url;
})
.Where(
u => Uri.Compare(
u => u is not null &&
Uri.Compare(
u,
firstServerUrl,
UriComponents.Host | UriComponents.Port | UriComponents.Path,
UriFormat.SafeUnescaped,
StringComparison.OrdinalIgnoreCase) ==
0 && u.IsAbsoluteUri)
.Select(u => u.Scheme)
.Select(u => u!.Scheme)
.Distinct()
.ToList();

Expand Down Expand Up @@ -457,22 +453,24 @@ public void SetReferenceHostDocument()
/// <summary>
/// Takes in an OpenApi document instance and generates its hash value
/// </summary>
/// <param name="doc">The OpenAPI description to hash.</param>
/// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
/// <returns>The hash value.</returns>
public static string GenerateHashValue(OpenApiDocument doc)
public async Task<string> GetHashCodeAsync(CancellationToken cancellationToken = default)
{
using HashAlgorithm sha = SHA512.Create();
using var cryptoStream = new CryptoStream(Stream.Null, sha, CryptoStreamMode.Write);
using var streamWriter = new StreamWriter(cryptoStream);

var openApiJsonWriter = new OpenApiJsonWriter(streamWriter, new() { Terse = true });
doc.SerializeAsV3(openApiJsonWriter);
openApiJsonWriter.Flush();
SerializeAsV3(openApiJsonWriter);
await openApiJsonWriter.FlushAsync(cancellationToken).ConfigureAwait(false);

#if NET5_0_OR_GREATER
await cryptoStream.FlushFinalBlockAsync(cancellationToken).ConfigureAwait(false);
#else
cryptoStream.FlushFinalBlock();
var hash = sha.Hash;

return ConvertByteArrayToString(hash);
#endif
return ConvertByteArrayToString(sha.Hash ?? []);
}

private static string ConvertByteArrayToString(byte[] hash)
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.OpenApi/Models/OpenApiOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,11 +283,11 @@ public void SerializeAsV2(IOpenApiWriter writer)
{
var produces = Responses
.Where(static r => r.Value.Content != null)
.SelectMany(static r => r.Value.Content?.Keys)
.SelectMany(static r => r.Value.Content?.Keys ?? [])
.Concat(
Responses
.Where(static r => r.Value.Reference is {HostDocument: not null})
.SelectMany(static r => r.Value.Content?.Keys))
.SelectMany(static r => r.Value.Content?.Keys ?? []))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();

Expand Down
4 changes: 4 additions & 0 deletions src/Microsoft.OpenApi/Reader/OpenApiModelFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ public static async Task<ReadResult> LoadAsync(Stream input, string format = nul
var result = await InternalLoadAsync(preparedStream, format, settings, cancellationToken).ConfigureAwait(false);
if (!settings.LeaveStreamOpen)
{
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP || NET5_0_OR_GREATER
await input.DisposeAsync().ConfigureAwait(false);
#else
input.Dispose();
#endif
}
return result;
}
Expand Down
6 changes: 5 additions & 1 deletion src/Microsoft.OpenApi/Writers/IOpenApiWriter.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.OpenApi.Writers
{
/// <summary>
Expand Down Expand Up @@ -71,6 +74,7 @@ public interface IOpenApiWriter
/// <summary>
/// Flush the writer.
/// </summary>
void Flush();
/// <param name="cancellationToken">The cancellation token.</param>
Task FlushAsync(CancellationToken cancellationToken = default);
}
}
Loading

0 comments on commit e9e214f

Please sign in to comment.