Skip to content

Commit 19463dd

Browse files
committed
Merge remote-tracking branch 'origin/release/2.0.0' into mk/sync-v2-with-vnext
2 parents 524a03d + 4e50523 commit 19463dd

30 files changed

+563
-144
lines changed

src/Microsoft.OpenApi.Hidi/OpenApiService.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation. All rights reserved.
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

44
using System;
@@ -79,7 +79,7 @@ public static async Task TransformOpenApiDocumentAsync(HidiOptions options, ILog
7979

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

8484
// If ApiManifest is provided, set the referenced OpenAPI document
8585
var apiDependency = await FindApiDependencyAsync(options.FilterOptions.FilterByApiManifest, logger, cancellationToken).ConfigureAwait(false);
@@ -768,7 +768,7 @@ internal static async Task PluginManifestAsync(HidiOptions options, ILogger logg
768768
// Write OpenAPI to Output folder
769769
options.Output = new(Path.Combine(options.OutputFolder, "openapi.json"));
770770
options.TerseOutput = true;
771-
WriteOpenApi(options, OpenApiFormat.Json, OpenApiSpecVersion.OpenApi3_0, document, logger);
771+
WriteOpenApi(options, OpenApiFormat.Json, OpenApiSpecVersion.OpenApi3_1, document, logger);
772772

773773
// Create OpenAIPluginManifest from ApiDependency and OpenAPI document
774774
var manifest = new OpenAIPluginManifest

src/Microsoft.OpenApi.Hidi/OpenApiSpecVersionHelper.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT license.
33

44
using System;
5-
using System.Linq;
65

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

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

27-
return OpenApiSpecVersion.OpenApi3_0; // default
25+
// Check for specific version matches
26+
if (majorVersion == 2)
27+
{
28+
return OpenApiSpecVersion.OpenApi2_0;
29+
}
30+
else if (majorVersion == 3 && minorVersion == 0)
31+
{
32+
return OpenApiSpecVersion.OpenApi3_0;
33+
}
34+
else if (majorVersion == 3 && minorVersion == 1)
35+
{
36+
return OpenApiSpecVersion.OpenApi3_1;
37+
}
38+
39+
return OpenApiSpecVersion.OpenApi3_1; // default
2840
}
2941
}
3042
}

src/Microsoft.OpenApi/Models/OpenApiConstants.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,16 @@ public static class OpenApiConstants
700700
/// </summary>
701701
public const string ComponentsSegment = "/components/";
702702

703+
/// <summary>
704+
/// Field: Null
705+
/// </summary>
706+
public const string Null = "null";
707+
708+
/// <summary>
709+
/// Field: Nullable extension
710+
/// </summary>
711+
public const string NullableExtension = "x-nullable";
712+
703713
#region V2.0
704714

705715
/// <summary>

src/Microsoft.OpenApi/Models/OpenApiDocument.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace Microsoft.OpenApi.Models
2424
public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible, IOpenApiAnnotatable
2525
{
2626
/// <summary>
27-
/// Related workspace containing OpenApiDocuments that are referenced in this document
27+
/// Related workspace containing components that are referenced in a document
2828
/// </summary>
2929
public OpenApiWorkspace Workspace { get; set; }
3030

src/Microsoft.OpenApi/Models/OpenApiReference.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ public string ReferenceV3
9595
{
9696
return Id;
9797
}
98+
if (Id.StartsWith("http"))
99+
{
100+
return Id;
101+
}
98102

99103
return "#/components/" + Type.Value.GetDisplayName() + "/" + Id;
100104
}
@@ -228,7 +232,12 @@ private string GetExternalReferenceV3()
228232
{
229233
return ExternalResource + "#" + Id;
230234
}
231-
235+
236+
if (Id.StartsWith("http"))
237+
{
238+
return Id;
239+
}
240+
232241
if (Type.HasValue)
233242
{
234243
return ExternalResource + "#/components/" + Type.Value.GetDisplayName() + "/"+ Id;

src/Microsoft.OpenApi/Models/OpenApiSchema.cs

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -487,14 +487,7 @@ public void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version,
487487
writer.WriteOptionalCollection(OpenApiConstants.Enum, Enum, (nodeWriter, s) => nodeWriter.WriteAny(s));
488488

489489
// type
490-
if (Type?.GetType() == typeof(string))
491-
{
492-
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
493-
}
494-
else
495-
{
496-
writer.WriteOptionalCollection(OpenApiConstants.Type, (string[])Type, (w, s) => w.WriteRaw(s));
497-
}
490+
SerializeTypeProperty(Type, writer, version);
498491

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

539532
// nullable
540-
writer.WriteProperty(OpenApiConstants.Nullable, Nullable, false);
533+
if (version is OpenApiSpecVersion.OpenApi3_0)
534+
{
535+
writer.WriteProperty(OpenApiConstants.Nullable, Nullable, false);
536+
}
541537

542538
// discriminator
543539
writer.WriteOptionalObject(OpenApiConstants.Discriminator, Discriminator, (w, s) => s.SerializeAsV3(w));
@@ -674,7 +670,14 @@ internal void SerializeAsV2(
674670
writer.WriteStartObject();
675671

676672
// type
677-
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
673+
if (Type is string[] array)
674+
{
675+
DowncastTypeArrayToV2OrV3(array, writer, OpenApiSpecVersion.OpenApi2_0);
676+
}
677+
else
678+
{
679+
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
680+
}
678681

679682
// description
680683
writer.WriteProperty(OpenApiConstants.Description, Description);
@@ -803,6 +806,35 @@ internal void SerializeAsV2(
803806
writer.WriteEndObject();
804807
}
805808

809+
private void SerializeTypeProperty(object type, IOpenApiWriter writer, OpenApiSpecVersion version)
810+
{
811+
if (type?.GetType() == typeof(string))
812+
{
813+
// check whether nullable is true for upcasting purposes
814+
if (Nullable || Extensions.ContainsKey(OpenApiConstants.NullableExtension))
815+
{
816+
// create a new array and insert the type and "null" as values
817+
Type = new[] { (string)Type, OpenApiConstants.Null };
818+
}
819+
else
820+
{
821+
writer.WriteProperty(OpenApiConstants.Type, (string)Type);
822+
}
823+
}
824+
if (Type is string[] array)
825+
{
826+
// type
827+
if (version is OpenApiSpecVersion.OpenApi3_0)
828+
{
829+
DowncastTypeArrayToV2OrV3(array, writer, OpenApiSpecVersion.OpenApi3_0);
830+
}
831+
else
832+
{
833+
writer.WriteOptionalCollection(OpenApiConstants.Type, (string[])Type, (w, s) => w.WriteRaw(s));
834+
}
835+
}
836+
}
837+
806838
private object DeepCloneType(object type)
807839
{
808840
if (type == null)
@@ -826,5 +858,38 @@ private object DeepCloneType(object type)
826858

827859
return null;
828860
}
861+
862+
private void DowncastTypeArrayToV2OrV3(string[] array, IOpenApiWriter writer, OpenApiSpecVersion version)
863+
{
864+
/* If the array has one non-null value, emit Type as string
865+
* If the array has one null value, emit x-nullable as true
866+
* If the array has two values, one null and one non-null, emit Type as string and x-nullable as true
867+
* If the array has more than two values or two non-null values, do not emit type
868+
* */
869+
870+
var nullableProp = version.Equals(OpenApiSpecVersion.OpenApi2_0)
871+
? OpenApiConstants.NullableExtension
872+
: OpenApiConstants.Nullable;
873+
874+
if (array.Length is 1)
875+
{
876+
var value = array[0];
877+
if (value is OpenApiConstants.Null)
878+
{
879+
writer.WriteProperty(nullableProp, true);
880+
}
881+
else
882+
{
883+
writer.WriteProperty(OpenApiConstants.Type, value);
884+
}
885+
}
886+
else if (array.Length is 2 && array.Contains(OpenApiConstants.Null))
887+
{
888+
// Find the non-null value and write it out
889+
var nonNullValue = array.First(v => v != OpenApiConstants.Null);
890+
writer.WriteProperty(OpenApiConstants.Type, nonNullValue);
891+
writer.WriteProperty(nullableProp, true);
892+
}
893+
}
829894
}
830895
}

src/Microsoft.OpenApi/Models/References/OpenApiSchemaReference.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
using Microsoft.OpenApi.Any;
54
using Microsoft.OpenApi.Interfaces;
65
using Microsoft.OpenApi.Writers;
76
using System;
87
using System.Collections.Generic;
9-
using System.Runtime;
108
using System.Text.Json.Nodes;
119

1210
namespace Microsoft.OpenApi.Models.References

src/Microsoft.OpenApi/Reader/OpenApiJsonReader.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ private JsonNode LoadJsonNodes(TextReader input)
192192
private async Task<OpenApiDiagnostic> LoadExternalRefs(OpenApiDocument document, CancellationToken cancellationToken, OpenApiReaderSettings settings, string format = null)
193193
{
194194
// Create workspace for all documents to live in.
195-
var openApiWorkSpace = new OpenApiWorkspace();
195+
var baseUrl = settings.BaseUrl ?? new Uri(OpenApiConstants.BaseRegistryUri);
196+
var openApiWorkSpace = new OpenApiWorkspace(baseUrl);
196197

197198
// Load this root document into the workspace
198199
var streamLoader = new DefaultStreamLoader(settings.BaseUrl);

src/Microsoft.OpenApi/Reader/Services/DefaultStreamLoader.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ public Stream Load(Uri uri)
4545
/// <exception cref="ArgumentException"></exception>
4646
public async Task<Stream> LoadAsync(Uri uri)
4747
{
48-
var absoluteUri = new Uri(baseUrl, uri);
48+
Uri absoluteUri;
49+
absoluteUri = baseUrl.AbsoluteUri.Equals(OpenApiConstants.BaseRegistryUri) ? new Uri(Directory.GetCurrentDirectory() + uri)
50+
: new Uri(baseUrl, uri);
4951

5052
switch (absoluteUri.Scheme)
5153
{

src/Microsoft.OpenApi/Reader/Services/OpenApiWorkspaceLoader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ internal async Task<OpenApiDiagnostic> LoadAsync(OpenApiReference reference,
2828
{
2929
_workspace.AddDocumentId(reference.ExternalResource, document.BaseUri);
3030
var version = diagnostic?.SpecificationVersion ?? OpenApiSpecVersion.OpenApi3_0;
31-
_workspace.RegisterComponents(document, version);
31+
_workspace.RegisterComponents(document);
3232
document.Workspace = _workspace;
3333

3434
// Collect remote references by walking document

0 commit comments

Comments
 (0)