Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/release/2.0.0' into mk/sync-v2-w…
Browse files Browse the repository at this point in the history
…ith-vnext
  • Loading branch information
MaggieKimani1 committed Oct 3, 2024
2 parents 524a03d + 4e50523 commit 19463dd
Show file tree
Hide file tree
Showing 30 changed files with 563 additions and 144 deletions.
6 changes: 3 additions & 3 deletions src/Microsoft.OpenApi.Hidi/OpenApiService.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;
Expand Down Expand Up @@ -79,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 Down Expand Up @@ -768,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
}
}
}
10 changes: 10 additions & 0 deletions src/Microsoft.OpenApi/Models/OpenApiConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,16 @@ public static class OpenApiConstants
/// </summary>
public const string ComponentsSegment = "/components/";

/// <summary>
/// Field: Null
/// </summary>
public const string Null = "null";

/// <summary>
/// Field: Nullable extension
/// </summary>
public const string NullableExtension = "x-nullable";

#region V2.0

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi/Models/OpenApiDocument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Microsoft.OpenApi.Models
public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
{
/// <summary>
/// Related workspace containing OpenApiDocuments that are referenced in this document
/// Related workspace containing components that are referenced in a document
/// </summary>
public OpenApiWorkspace Workspace { get; set; }

Expand Down
11 changes: 10 additions & 1 deletion src/Microsoft.OpenApi/Models/OpenApiReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ public string ReferenceV3
{
return Id;
}
if (Id.StartsWith("http"))
{
return Id;
}

return "#/components/" + Type.Value.GetDisplayName() + "/" + Id;
}
Expand Down Expand Up @@ -228,7 +232,12 @@ private string GetExternalReferenceV3()
{
return ExternalResource + "#" + Id;
}


if (Id.StartsWith("http"))
{
return Id;
}

if (Type.HasValue)
{
return ExternalResource + "#/components/" + Type.Value.GetDisplayName() + "/"+ Id;
Expand Down
85 changes: 75 additions & 10 deletions src/Microsoft.OpenApi/Models/OpenApiSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -487,14 +487,7 @@ public void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
writer.WriteOptionalCollection(OpenApiConstants.Enum, Enum, (nodeWriter, s) => nodeWriter.WriteAny(s));

// type
if (Type?.GetType() == typeof(string))
{
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
}
else
{
writer.WriteOptionalCollection(OpenApiConstants.Type, (string[])Type, (w, s) => w.WriteRaw(s));
}
SerializeTypeProperty(Type, writer, version);

// allOf
writer.WriteOptionalCollection(OpenApiConstants.AllOf, AllOf, (w, s) => s.SerializeAsV3(w));
Expand Down Expand Up @@ -537,7 +530,10 @@ public void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
writer.WriteOptionalObject(OpenApiConstants.Default, Default, (w, d) => w.WriteAny(d));

// nullable
writer.WriteProperty(OpenApiConstants.Nullable, Nullable, false);
if (version is OpenApiSpecVersion.OpenApi3_0)
{
writer.WriteProperty(OpenApiConstants.Nullable, Nullable, false);
}

// discriminator
writer.WriteOptionalObject(OpenApiConstants.Discriminator, Discriminator, (w, s) => s.SerializeAsV3(w));
Expand Down Expand Up @@ -674,7 +670,14 @@ internal void SerializeAsV2(
writer.WriteStartObject();

// type
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
if (Type is string[] array)
{
DowncastTypeArrayToV2OrV3(array, writer, OpenApiSpecVersion.OpenApi2_0);
}
else
{
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
}

// description
writer.WriteProperty(OpenApiConstants.Description, Description);
Expand Down Expand Up @@ -803,6 +806,35 @@ internal void SerializeAsV2(
writer.WriteEndObject();
}

private void SerializeTypeProperty(object type, IOpenApiWriter writer, OpenApiSpecVersion version)
{
if (type?.GetType() == typeof(string))
{
// check whether nullable is true for upcasting purposes
if (Nullable || Extensions.ContainsKey(OpenApiConstants.NullableExtension))
{
// create a new array and insert the type and "null" as values
Type = new[] { (string)Type, OpenApiConstants.Null };
}
else
{
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
}
}
if (Type is string[] array)
{
// type
if (version is OpenApiSpecVersion.OpenApi3_0)
{
DowncastTypeArrayToV2OrV3(array, writer, OpenApiSpecVersion.OpenApi3_0);
}
else
{
writer.WriteOptionalCollection(OpenApiConstants.Type, (string[])Type, (w, s) => w.WriteRaw(s));
}
}
}

private object DeepCloneType(object type)
{
if (type == null)
Expand All @@ -826,5 +858,38 @@ private object DeepCloneType(object type)

return null;
}

private void DowncastTypeArrayToV2OrV3(string[] array, IOpenApiWriter writer, OpenApiSpecVersion version)
{
/* If the array has one non-null value, emit Type as string
* If the array has one null value, emit x-nullable as true
* If the array has two values, one null and one non-null, emit Type as string and x-nullable as true
* If the array has more than two values or two non-null values, do not emit type
* */

var nullableProp = version.Equals(OpenApiSpecVersion.OpenApi2_0)
? OpenApiConstants.NullableExtension
: OpenApiConstants.Nullable;

if (array.Length is 1)
{
var value = array[0];
if (value is OpenApiConstants.Null)
{
writer.WriteProperty(nullableProp, true);
}
else
{
writer.WriteProperty(OpenApiConstants.Type, value);
}
}
else if (array.Length is 2 && array.Contains(OpenApiConstants.Null))
{
// Find the non-null value and write it out
var nonNullValue = array.First(v => v != OpenApiConstants.Null);
writer.WriteProperty(OpenApiConstants.Type, nonNullValue);
writer.WriteProperty(nullableProp, true);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
using System;
using System.Collections.Generic;
using System.Runtime;
using System.Text.Json.Nodes;

namespace Microsoft.OpenApi.Models.References
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ private JsonNode LoadJsonNodes(TextReader input)
private async Task<OpenApiDiagnostic> LoadExternalRefs(OpenApiDocument document, CancellationToken cancellationToken, OpenApiReaderSettings settings, string format = null)
{
// Create workspace for all documents to live in.
var openApiWorkSpace = new OpenApiWorkspace();
var baseUrl = settings.BaseUrl ?? new Uri(OpenApiConstants.BaseRegistryUri);
var openApiWorkSpace = new OpenApiWorkspace(baseUrl);

// Load this root document into the workspace
var streamLoader = new DefaultStreamLoader(settings.BaseUrl);
Expand Down
4 changes: 3 additions & 1 deletion src/Microsoft.OpenApi/Reader/Services/DefaultStreamLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ public Stream Load(Uri uri)
/// <exception cref="ArgumentException"></exception>
public async Task<Stream> LoadAsync(Uri uri)
{
var absoluteUri = new Uri(baseUrl, uri);
Uri absoluteUri;
absoluteUri = baseUrl.AbsoluteUri.Equals(OpenApiConstants.BaseRegistryUri) ? new Uri(Directory.GetCurrentDirectory() + uri)
: new Uri(baseUrl, uri);

switch (absoluteUri.Scheme)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ internal async Task<OpenApiDiagnostic> LoadAsync(OpenApiReference reference,
{
_workspace.AddDocumentId(reference.ExternalResource, document.BaseUri);
var version = diagnostic?.SpecificationVersion ?? OpenApiSpecVersion.OpenApi3_0;
_workspace.RegisterComponents(document, version);
_workspace.RegisterComponents(document);
document.Workspace = _workspace;

// Collect remote references by walking document
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
FixRequestBodyReferences(openApiDoc);

// Register components
openApiDoc.Workspace.RegisterComponents(openApiDoc, OpenApiSpecVersion.OpenApi2_0);
openApiDoc.Workspace.RegisterComponents(openApiDoc);

return openApiDoc;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
ParseMap(openApiNode, openApiDoc, _openApiFixedFields, _openApiPatternFields, openApiDoc);

// Register components
openApiDoc.Workspace.RegisterComponents(openApiDoc, OpenApiSpecVersion.OpenApi3_0);
openApiDoc.Workspace.RegisterComponents(openApiDoc);

return openApiDoc;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public static OpenApiDocument LoadOpenApi(RootNode rootNode)
ParseMap(openApiNode, openApiDoc, _openApiFixedFields, _openApiPatternFields, openApiDoc);

// Register components
openApiDoc.Workspace.RegisterComponents(openApiDoc, OpenApiSpecVersion.OpenApi3_1);
openApiDoc.Workspace.RegisterComponents(openApiDoc);

return openApiDoc;
}
Expand Down
24 changes: 23 additions & 1 deletion src/Microsoft.OpenApi/Reader/V31/OpenApiSchemaDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,22 @@ internal static partial class OpenApiV31Deserializer
},
{
"nullable",
(o, n, _) => o.Nullable = bool.Parse(n.GetScalarValue())
(o, n, _) =>
{
var nullable = bool.Parse(n.GetScalarValue());
if (nullable) // if nullable, convert type into an array of type(s) and null
{
if (o.Type is string[] typeArray)
{
var typeList = new List<string>(typeArray) { OpenApiConstants.Null };
o.Type = typeList.ToArray();
}
else if (o.Type is string typeString)
{
o.Type = new string[]{typeString, OpenApiConstants.Null};
}
}
}
},
{
"discriminator",
Expand Down Expand Up @@ -242,6 +257,13 @@ public static OpenApiSchema LoadSchema(ParseNode node, OpenApiDocument hostDocum
propertyNode.ParseField(schema, _openApiSchemaFixedFields, _openApiSchemaPatternFields);
}

if (schema.Extensions.ContainsKey(OpenApiConstants.NullableExtension))
{
var type = schema.Type;
schema.Type = new string[] {(string)type, OpenApiConstants.Null};
schema.Extensions.Remove(OpenApiConstants.NullableExtension);
}

return schema;
}
}
Expand Down
8 changes: 5 additions & 3 deletions src/Microsoft.OpenApi/Reader/V31/OpenApiV31Deserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,11 @@ private static (string, string) GetReferenceIdAndExternalResource(string pointer
string refId = !pointer.Contains('#') ? pointer : refSegments.Last();

var isExternalResource = !refSegments.First().StartsWith("#");
string externalResource = isExternalResource
? $"{refSegments.First()}/{refSegments[1].TrimEnd('#')}"
: null;
string externalResource = null;
if (isExternalResource && pointer.Contains('#'))
{
externalResource = $"{refSegments.First()}/{refSegments[1].TrimEnd('#')}";
}

return (refId, externalResource);
}
Expand Down
Loading

0 comments on commit 19463dd

Please sign in to comment.