Skip to content
Merged
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
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 ?? "[email protected]", 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 @@
/// <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)

Check warning on line 28 in src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs

View workflow job for this annotation

GitHub Actions / Build

All 'SerializeAsJsonAsync' method overloads should be adjacent. (https://rules.sonarsource.com/csharp/RSPEC-4136)
where T : IOpenApiSerializable
{
element.Serialize(stream, specVersion, OpenApiFormat.Json);
return element.SerializeAsync(stream, specVersion, OpenApiFormat.Json, cancellationToken);
}

/// <summary>
Expand All @@ -35,10 +38,11 @@
/// <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)

Check warning on line 42 in src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs

View workflow job for this annotation

GitHub Actions / Build

All 'SerializeAsYamlAsync' method overloads should be adjacent. (https://rules.sonarsource.com/csharp/RSPEC-4136)
where T : IOpenApiSerializable
{
element.Serialize(stream, specVersion, OpenApiFormat.Yaml);
return element.SerializeAsync(stream, specVersion, OpenApiFormat.Yaml, cancellationToken);
}

/// <summary>
Expand All @@ -50,14 +54,16 @@
/// <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>(

Check warning on line 58 in src/Microsoft.OpenApi/Extensions/OpenApiSerializableExtensions.cs

View workflow job for this annotation

GitHub Actions / Build

All 'SerializeAsync' method overloads should be adjacent. (https://rules.sonarsource.com/csharp/RSPEC-4136)
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 @@
/// <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 @@
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 @@
/// <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 @@
throw new OpenApiException(string.Format(SRResource.OpenApiSpecVersionNotSupported, specVersion));
}

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

/// <summary>
Expand All @@ -131,12 +140,14 @@
/// <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 @@
/// <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 @@
/// <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
Loading