Skip to content

Commit

Permalink
Merge pull request #1906 from microsoft/vnext
Browse files Browse the repository at this point in the history
V2-preview1 release
  • Loading branch information
MaggieKimani1 authored Nov 6, 2024
2 parents 3e0a83f + 7f4f935 commit 18482f3
Show file tree
Hide file tree
Showing 498 changed files with 19,638 additions and 11,333 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ internal static class OpenApiExtensibleExtensions
/// <returns>A <see cref="string"/> value matching the provided extensionKey. Return null when extensionKey is not found. </returns>
internal static string GetExtension(this IDictionary<string, IOpenApiExtension> extensions, string extensionKey)
{
if (extensions.TryGetValue(extensionKey, out var value) && value is OpenApiString castValue)
if (extensions.TryGetValue(extensionKey, out var value) && value is OpenApiAny castValue)
{
return castValue.Value;
return castValue.Node.GetValue<string>();
}
return string.Empty;
}
Expand Down
21 changes: 11 additions & 10 deletions src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ public override void Visit(OpenApiSchema schema)

public override void Visit(OpenApiPathItem pathItem)
{
if (pathItem.Operations.TryGetValue(OperationType.Put, out var value))
if (pathItem.Operations.TryGetValue(OperationType.Put, out var value) &&
value.OperationId != null)
{
var operationId = value.OperationId;
pathItem.Operations[OperationType.Put].OperationId = ResolvePutOperationId(operationId);
Expand All @@ -67,14 +68,14 @@ public override void Visit(OpenApiOperation operation)
throw new ArgumentException($"OperationId is required {PathString}", nameof(operation));

var operationId = operation.OperationId;
var operationTypeExtension = operation.Extensions.GetExtension("x-ms-docs-operation-type");
var operationTypeExtension = operation.Extensions?.GetExtension("x-ms-docs-operation-type");
if (operationTypeExtension.IsEquals("function"))
operation.Parameters = ResolveFunctionParameters(operation.Parameters);
operation.Parameters = ResolveFunctionParameters(operation.Parameters ?? new List<OpenApiParameter>());

// Order matters. Resolve operationId.
operationId = RemoveHashSuffix(operationId);
if (operationTypeExtension.IsEquals("action") || operationTypeExtension.IsEquals("function"))
operationId = RemoveKeyTypeSegment(operationId, operation.Parameters);
operationId = RemoveKeyTypeSegment(operationId, operation.Parameters ?? new List<OpenApiParameter>());
operationId = SingularizeAndDeduplicateOperationId(operationId.SplitByChar('.'));
operationId = ResolveODataCastOperationId(operationId);
operationId = ResolveByRefOperationId(operationId);
Expand Down Expand Up @@ -165,10 +166,10 @@ private static IList<OpenApiParameter> ResolveFunctionParameters(IList<OpenApiPa
parameter.Content = null;
parameter.Schema = new()
{
Type = "array",
Type = JsonSchemaType.Array,
Items = new()
{
Type = "string"
Type = JsonSchemaType.String
}
};
}
Expand All @@ -177,9 +178,9 @@ private static IList<OpenApiParameter> ResolveFunctionParameters(IList<OpenApiPa

private void AddAdditionalPropertiesToSchema(OpenApiSchema schema)
{
if (schema != null && !_schemaLoop.Contains(schema) && "object".Equals(schema.Type, StringComparison.OrdinalIgnoreCase))
if (schema != null && !_schemaLoop.Contains(schema) && schema.Type.Equals(JsonSchemaType.Object))
{
schema.AdditionalProperties = new() { Type = "object" };
schema.AdditionalProperties = new() { Type = JsonSchemaType.Object };

/* Because 'additionalProperties' are now being walked,
* we need a way to keep track of visited schemas to avoid
Expand All @@ -191,7 +192,7 @@ private void AddAdditionalPropertiesToSchema(OpenApiSchema schema)

private static void ResolveOneOfSchema(OpenApiSchema schema)
{
if (schema.OneOf?.FirstOrDefault() is {} newSchema)
if (schema.OneOf?.FirstOrDefault() is { } newSchema)
{
schema.OneOf = null;
FlattenSchema(schema, newSchema);
Expand All @@ -200,7 +201,7 @@ private static void ResolveOneOfSchema(OpenApiSchema schema)

private static void ResolveAnyOfSchema(OpenApiSchema schema)
{
if (schema.AnyOf?.FirstOrDefault() is {} newSchema)
if (schema.AnyOf?.FirstOrDefault() is { } newSchema)
{
schema.AnyOf = null;
FlattenSchema(schema, newSchema);
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Hidi/Microsoft.OpenApi.Hidi.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
Expand Down
50 changes: 34 additions & 16 deletions src/Microsoft.OpenApi.Hidi/OpenApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
using Microsoft.OpenApi.Hidi.Utilities;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.OData;
using Microsoft.OpenApi.Reader;
using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Writers;
Expand All @@ -38,6 +39,12 @@ namespace Microsoft.OpenApi.Hidi
{
internal static class OpenApiService
{
static OpenApiService()
{
OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yaml, new OpenApiYamlReader());
OpenApiReaderRegistry.RegisterReader(OpenApiConstants.Yml, new OpenApiYamlReader());
}

/// <summary>
/// Implementation of the transform command
/// </summary>
Expand All @@ -52,7 +59,12 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog
{
if (options.Output == null)
{
var inputExtension = GetInputPathExtension(options.OpenApi, options.Csdl);
#pragma warning disable CA1308 // Normalize strings to uppercase
var extension = options.OpenApiFormat?.GetDisplayName().ToLowerInvariant();
var inputExtension = !string.IsNullOrEmpty(extension) ? string.Concat(".", extension)
: GetInputPathExtension(options.OpenApi, options.Csdl);

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

Expand All @@ -67,7 +79,7 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog

// Default to yaml and OpenApiVersion 3 during csdl to OpenApi conversion
var openApiFormat = options.OpenApiFormat ?? (!string.IsNullOrEmpty(options.OpenApi) ? GetOpenApiFormat(options.OpenApi, logger) : OpenApiFormat.Yaml);
var openApiVersion = options.Version != null ? TryParseOpenApiSpecVersion(options.Version) : OpenApiSpecVersion.OpenApi3_0;
var openApiVersion = options.Version != null ? TryParseOpenApiSpecVersion(options.Version) : OpenApiSpecVersion.OpenApi3_1;

// If ApiManifest is provided, set the referenced OpenAPI document
var apiDependency = await FindApiDependencyAsync(options.FilterOptions.FilterByApiManifest, logger, cancellationToken).ConfigureAwait(false);
Expand All @@ -85,7 +97,8 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog
}

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

if (options.FilterOptions != null)
{
Expand Down Expand Up @@ -212,7 +225,7 @@ private static void WriteOpenApi(HidiOptions options, OpenApiFormat openApiForma
}

// Get OpenAPI document either from OpenAPI or CSDL
private static async Task<OpenApiDocument> GetOpenApiAsync(HidiOptions options, ILogger logger, string? metadataVersion = null, CancellationToken cancellationToken = default)
private static async Task<OpenApiDocument> GetOpenApiAsync(HidiOptions options, string format, ILogger logger, string? metadataVersion = null, CancellationToken cancellationToken = default)
{
OpenApiDocument document;
Stream stream;
Expand All @@ -233,7 +246,7 @@ private static async Task<OpenApiDocument> GetOpenApiAsync(HidiOptions options,
await stream.DisposeAsync().ConfigureAwait(false);
}

document = await ConvertCsdlToOpenApiAsync(filteredStream ?? stream, metadataVersion, options.SettingsConfig, cancellationToken).ConfigureAwait(false);
document = await ConvertCsdlToOpenApiAsync(filteredStream ?? stream, format, metadataVersion, options.SettingsConfig, cancellationToken).ConfigureAwait(false);
stopwatch.Stop();
logger.LogTrace("{Timestamp}ms: Generated OpenAPI with {Paths} paths.", stopwatch.ElapsedMilliseconds, document.Paths.Count);
}
Expand Down Expand Up @@ -375,14 +388,16 @@ private static async Task<ReadResult> ParseOpenApiAsync(string openApiFile, bool
{
stopwatch.Start();

result = await new OpenApiStreamReader(new()
{
var settings = new OpenApiReaderSettings
{
LoadExternalRefs = inlineExternal,
BaseUrl = openApiFile.StartsWith("http", StringComparison.OrdinalIgnoreCase) ?
new(openApiFile) :
new Uri("file://" + new FileInfo(openApiFile).DirectoryName + Path.DirectorySeparatorChar)
}
).ReadAsync(stream, cancellationToken).ConfigureAwait(false);
};

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

logger.LogTrace("{Timestamp}ms: Completed parsing.", stopwatch.ElapsedMilliseconds);

Expand All @@ -398,15 +413,15 @@ private static async Task<ReadResult> ParseOpenApiAsync(string openApiFile, bool
/// </summary>
/// <param name="csdl">The CSDL stream.</param>
/// <returns>An OpenAPI document.</returns>
public static async Task<OpenApiDocument> ConvertCsdlToOpenApiAsync(Stream csdl, string? metadataVersion = null, IConfiguration? settings = null, CancellationToken token = default)
public static async Task<OpenApiDocument> ConvertCsdlToOpenApiAsync(Stream csdl, string format, string? metadataVersion = null, IConfiguration? settings = null, CancellationToken token = default)
{
using var reader = new StreamReader(csdl);
var csdlText = await reader.ReadToEndAsync(token).ConfigureAwait(false);
var edmModel = CsdlReader.Parse(XElement.Parse(csdlText).CreateReader());
settings ??= SettingsUtilities.GetConfiguration();

var document = edmModel.ConvertToOpenApi(SettingsUtilities.GetOpenApiConvertSettings(settings, metadataVersion));
document = FixReferences(document);
document = FixReferences(document, format);

return document;
}
Expand All @@ -416,14 +431,15 @@ public static async Task<OpenApiDocument> ConvertCsdlToOpenApiAsync(Stream csdl,
/// </summary>
/// <param name="document"> The converted OpenApiDocument.</param>
/// <returns> A valid OpenApiDocument instance.</returns>
public static OpenApiDocument FixReferences(OpenApiDocument document)
public static OpenApiDocument FixReferences(OpenApiDocument document, string format)
{
// This method is only needed because the output of ConvertToOpenApi isn't quite a valid OpenApiDocument instance.
// So we write it out, and read it back in again to fix it up.

var sb = new StringBuilder();
document.SerializeAsV3(new OpenApiYamlWriter(new StringWriter(sb)));
var doc = new OpenApiStringReader().Read(sb.ToString(), out _);

var doc = OpenApiDocument.Parse(sb.ToString(), format).OpenApiDocument;

return doc;
}
Expand Down Expand Up @@ -571,7 +587,8 @@ private static string GetInputPathExtension(string? openapi = null, string? csdl
throw new ArgumentException("Please input a file path or URL");
}

var document = await GetOpenApiAsync(options, logger, null, cancellationToken).ConfigureAwait(false);
var format = OpenApiModelFactory.GetFormat(options.OpenApi);
var document = await GetOpenApiAsync(options, format, logger, null, cancellationToken).ConfigureAwait(false);

using (logger.BeginScope("Creating diagram"))
{
Expand Down Expand Up @@ -732,7 +749,8 @@ internal static async Task PluginManifestAsync(HidiOptions options, ILogger logg
}

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

cancellationToken.ThrowIfCancellationRequested();

Expand All @@ -750,7 +768,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_0, document, logger);
WriteOpenApi(options, OpenApiFormat.Json, OpenApiSpecVersion.OpenApi3_1, document, logger);

// Create OpenAIPluginManifest from ApiDependency and OpenAPI document
var manifest = new OpenAIPluginManifest
Expand Down
28 changes: 20 additions & 8 deletions src/Microsoft.OpenApi.Hidi/OpenApiSpecVersionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT license.

using System;
using System.Linq;

namespace Microsoft.OpenApi.Hidi
{
Expand All @@ -14,17 +13,30 @@ public static OpenApiSpecVersion TryParseOpenApiSpecVersion(string value)
{
throw new InvalidOperationException("Please provide a version");
}
var res = value.Split('.', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
// Split the version string by the dot
var versionSegments = value.Split('.', StringSplitOptions.RemoveEmptyEntries);

if (int.TryParse(res, out var result))
if (!int.TryParse(versionSegments[0], out var majorVersion)
|| !int.TryParse(versionSegments[1], out var minorVersion))
{
if (result is >= 2 and < 3)
{
return OpenApiSpecVersion.OpenApi2_0;
}
throw new InvalidOperationException("Invalid version format. Please provide a valid OpenAPI version (e.g., 2.0, 3.0, 3.1).");
}

return OpenApiSpecVersion.OpenApi3_0; // default
// Check for specific version matches
if (majorVersion == 2)
{
return OpenApiSpecVersion.OpenApi2_0;
}
else if (majorVersion == 3 && minorVersion == 0)
{
return OpenApiSpecVersion.OpenApi3_0;
}
else if (majorVersion == 3 && minorVersion == 1)
{
return OpenApiSpecVersion.OpenApi3_1;
}

return OpenApiSpecVersion.OpenApi3_1; // default
}
}
}
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Hidi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.CommandLine;
Expand Down
23 changes: 0 additions & 23 deletions src/Microsoft.OpenApi.Readers/Interface/IOpenApiReader.cs

This file was deleted.

11 changes: 9 additions & 2 deletions src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>1.6.22</Version>
<Version>2.0.0-preview1</Version>
<Description>OpenAPI.NET Readers for JSON and YAML documents</Description>
<SignAssembly>true</SignAssembly>
<!-- https://github.com/dotnet/sourcelink/blob/main/docs/README.md#embeduntrackedsources -->
Expand All @@ -17,6 +17,12 @@
<AssemblyOriginatorKeyFile>..\Microsoft.OpenApi.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<ItemGroup>
<Compile Remove="V31\**" />
<EmbeddedResource Remove="V31\**" />
<None Remove="V31\**" />
</ItemGroup>

<ItemGroup>

<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.20">
Expand All @@ -25,6 +31,7 @@
</PackageReference>

<PackageReference Include="SharpYaml" Version="2.1.1" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading

0 comments on commit 18482f3

Please sign in to comment.