Skip to content

Commit

Permalink
Merge branch 'dev' into fix/missing-document-references
Browse files Browse the repository at this point in the history
Signed-off-by: Vincent Biret <[email protected]>
  • Loading branch information
baywet committed Jan 16, 2025
2 parents ff1406c + 7a7bba1 commit 4357373
Show file tree
Hide file tree
Showing 92 changed files with 1,032 additions and 808 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ jobs:
id: getversion
- name: Push to registry - Nightly
if: ${{ github.ref == 'refs/heads/dev' }}
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.11.0
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:nightly
- name: Push to registry - Release
if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/support/v1' }}
uses: docker/build-push-action@v6.10.0
uses: docker/build-push-action@v6.11.0
with:
push: true
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest,${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.getversion.outputs.version }}
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@

<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.12.19">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand Down
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
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.0" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="9.0.1" />
<!-- Microsoft.Windows.Compatibility 8.0.8 depends on 8.0.0 this dependency can be removed once they update theirs -->
<PackageReference Include="System.Formats.Asn1" Version="9.0.0" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.1" />
</ItemGroup>
<ItemGroup>
<Resource Include="Themes\Metro\HowToApplyTheme.txt" />
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)

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 @@ 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)

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 @@ 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>(

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 @@ 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
}
}
}
52 changes: 33 additions & 19 deletions src/Microsoft.OpenApi/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,47 +2,61 @@
// Licensed under the MIT license.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.OpenApi.Attributes;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Reader;

namespace Microsoft.OpenApi.Extensions
{
/// <summary>
/// String extension methods.
/// </summary>
public static class StringExtensions
internal static class StringExtensions
{
private static readonly ConcurrentDictionary<Type, ConcurrentDictionary<string, object>> EnumDisplayCache = new();
private static readonly ConcurrentDictionary<Type, ReadOnlyDictionary<string, object>> EnumDisplayCache = new();

/// <summary>
/// Gets the enum value based on the given enum type and display name.
/// </summary>
/// <param name="displayName">The display name.</param>
public static T GetEnumFromDisplayName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(this string displayName)
internal static bool TryGetEnumFromDisplayName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(this string displayName, ParsingContext parsingContext, out T result) where T : Enum
{
if (TryGetEnumFromDisplayName(displayName, out result))
{
return true;
}

parsingContext.Diagnostic.Errors.Add(new OpenApiError(parsingContext.GetLocation(), $"Enum value {displayName} is not recognized."));
return false;

}
internal static bool TryGetEnumFromDisplayName<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] T>(this string displayName, out T result) where T : Enum
{
var type = typeof(T);
if (!type.IsEnum)
return default;

var displayMap = EnumDisplayCache.GetOrAdd(type, _ => new ConcurrentDictionary<string, object>(StringComparer.OrdinalIgnoreCase));
var displayMap = EnumDisplayCache.GetOrAdd(type, GetEnumValues<T>);

if (displayMap.TryGetValue(displayName, out var cachedValue))
return (T)cachedValue;

{
result = (T)cachedValue;
return true;
}

foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
result = default;
return false;
}
private static ReadOnlyDictionary<string, object> GetEnumValues<T>(Type enumType) where T : Enum
{
var result = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
foreach (var field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
{
var displayAttribute = field.GetCustomAttribute<DisplayAttribute>();
if (displayAttribute != null && displayAttribute.Name.Equals(displayName, StringComparison.OrdinalIgnoreCase))
if (field.GetCustomAttribute<DisplayAttribute>() is {} displayAttribute)
{
var enumValue = (T)field.GetValue(null);
displayMap.TryAdd(displayName, enumValue);
return enumValue;
result.Add(displayAttribute.Name, enumValue);
}
}

return default;
return new ReadOnlyDictionary<string, object>(result);
}
internal static string ToFirstCharacterLowerCase(this string input)
=> string.IsNullOrEmpty(input) ? string.Empty : char.ToLowerInvariant(input[0]) + input.Substring(1);
Expand Down
Loading

0 comments on commit 4357373

Please sign in to comment.