Skip to content

Add root property jsonSchemaDialect #1138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Mar 7, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5741ae6
Add root property jsonSchemaDialect and serialization and deserializa…
MaggieKimani1 Jan 23, 2023
c0f218a
Add test for validation; refactor existing tests by updating spec ver…
MaggieKimani1 Jan 23, 2023
312abb0
Add an optional spec version parameter to serialize method to write o…
MaggieKimani1 Jan 23, 2023
3fff889
Update the spec version in the diagnostic object to be more explicit
MaggieKimani1 Jan 23, 2023
e62c71f
Update verifier text result
MaggieKimani1 Jan 23, 2023
00e19eb
Update public API surface
MaggieKimani1 Jan 23, 2023
d57aad8
Implement SerializeAs31 across model objects
MaggieKimani1 Feb 20, 2023
f381fb7
Clean up tests
MaggieKimani1 Feb 20, 2023
970a74c
Simplify null check by using a coalescing operator
MaggieKimani1 Feb 22, 2023
862504f
Clean up tests
MaggieKimani1 Feb 22, 2023
a6a68c2
Update public API
MaggieKimani1 Feb 22, 2023
f2f866a
Implement PR feedback
MaggieKimani1 Feb 27, 2023
07d66aa
Update property ordering
MaggieKimani1 Feb 27, 2023
2baa14c
Use a callback to explicitly call the Serialize methods
MaggieKimani1 Feb 28, 2023
8963468
Use action function instead of delegate; Add method for serializing v…
MaggieKimani1 Mar 1, 2023
b1a695e
Code cleanup
MaggieKimani1 Mar 6, 2023
f2b651e
Refactor to reorder how properties are written for scope to be ended …
MaggieKimani1 Mar 6, 2023
82b4c60
Use string comparison; refactor code
MaggieKimani1 Mar 7, 2023
7d7e056
Fix failing test and update public API surface
MaggieKimani1 Mar 7, 2023
06f91f6
Apply suggestions from code review
baywet Mar 7, 2023
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
2 changes: 1 addition & 1 deletion src/Microsoft.OpenApi.Readers/ParsingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument)
case string version when version.is3_0() || version.is3_1():
VersionService = new OpenApiV3VersionService(Diagnostic);
doc = VersionService.LoadDocument(RootNode);
this.Diagnostic.SpecificationVersion = OpenApiSpecVersion.OpenApi3_0;
this.Diagnostic.SpecificationVersion = version.is3_1() ? OpenApiSpecVersion.OpenApi3_1 : OpenApiSpecVersion.OpenApi3_0;
ValidateRequiredFields(doc, version);
break;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ internal static partial class OpenApiV3Deserializer
} /* Version is valid field but we already parsed it */
},
{"info", (o, n) => o.Info = LoadInfo(n)},
{"jsonSchemaDialect", (o, n) => o.JsonSchemaDialect = n.GetScalarValue() },
{"servers", (o, n) => o.Servers = n.CreateList(LoadServer)},
{"paths", (o, n) => o.Paths = LoadPaths(n)},
{"webhooks", (o, n) => o.Webhooks = LoadPaths(n)},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace Microsoft.OpenApi.Extensions
/// </summary>
public static class OpenApiSerializableExtensions
{
public delegate void SerializeDelegate(IOpenApiWriter writer, IOpenApiSerializable element);

/// <summary>
/// Serialize the <see cref="IOpenApiSerializable"/> to the Open API document (JSON) using the given stream and specification version.
/// </summary>
Expand Down Expand Up @@ -117,6 +119,10 @@ public static void Serialize<T>(this T element, IOpenApiWriter writer, OpenApiSp

switch (specVersion)
{
case OpenApiSpecVersion.OpenApi3_1:
element.SerializeAsV31(writer);
break;

case OpenApiSpecVersion.OpenApi3_0:
element.SerializeAsV3(writer);
break;
Expand Down
3 changes: 2 additions & 1 deletion src/Microsoft.OpenApi/Interfaces/IOpenApiReferenceable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using static Microsoft.OpenApi.Extensions.OpenApiSerializableExtensions;

namespace Microsoft.OpenApi.Interfaces
{
Expand All @@ -25,7 +26,7 @@ public interface IOpenApiReferenceable : IOpenApiSerializable
/// <summary>
/// Serialize to OpenAPI V3 document without using reference.
/// </summary>
void SerializeAsV3WithoutReference(IOpenApiWriter writer);
void SerializeAsV3WithoutReference(IOpenApiWriter writer, OpenApiSpecVersion version, SerializeDelegate callback);

/// <summary>
/// Serialize to OpenAPI V2 document without using reference.
Expand Down
6 changes: 6 additions & 0 deletions src/Microsoft.OpenApi/Interfaces/IOpenApiSerializable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ namespace Microsoft.OpenApi.Interfaces
/// </summary>
public interface IOpenApiSerializable : IOpenApiElement
{
/// <summary>
/// Serialize OpenAPI element into v3.1
/// </summary>
/// <param name="writer"></param>
void SerializeAsV31(IOpenApiWriter writer);

/// <summary>
/// Serialize Open API element to v3.0.
/// </summary>
Expand Down
40 changes: 29 additions & 11 deletions src/Microsoft.OpenApi/Models/OpenApiCallback.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// Licensed under the MIT license.

using System.Collections.Generic;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Expressions;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
using static Microsoft.OpenApi.Extensions.OpenApiSerializableExtensions;

namespace Microsoft.OpenApi.Models
{
Expand Down Expand Up @@ -75,32 +75,50 @@ public void AddPathItem(RuntimeExpression expression, OpenApiPathItem pathItem)

PathItems.Add(expression, pathItem);
}


/// <summary>
/// Serialize <see cref="OpenApiCallback"/> to Open Api v3.1
/// </summary>
/// <param name="writer"></param>
/// <exception cref="System.NotImplementedException"></exception>
public void SerializeAsV31(IOpenApiWriter writer)
{
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1, (writer, element) => element.SerializeAsV3(writer));
}

/// <summary>
/// Serialize <see cref="OpenApiCallback"/> to Open Api v3.0
/// </summary>
public void SerializeAsV3(IOpenApiWriter writer)
{
if (writer == null)
{
throw Error.ArgumentNull(nameof(writer));
}
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
}

/// <summary>
/// Serialize <see cref="OpenApiCallback"/>
/// </summary>
/// <param name="writer"></param>
/// <param name="version"></param>
/// <param name="callback"></param>
private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version, SerializeDelegate callback)
{
writer = writer ?? throw Error.ArgumentNull(nameof(writer));

var target = this;

if (Reference != null)
{
if (!writer.GetSettings().ShouldInlineReference(Reference))
{
Reference.SerializeAsV3(writer);
callback(writer, Reference);
return;
}
else
{
target = GetEffective(Reference.HostDocument);
}
}
target.SerializeAsV3WithoutReference(writer);
target.SerializeAsV3WithoutReference(writer, version, callback);
}

/// <summary>
Expand All @@ -125,18 +143,18 @@ public OpenApiCallback GetEffective(OpenApiDocument doc)
/// Serialize to OpenAPI V3 document without using reference.
/// </summary>

public void SerializeAsV3WithoutReference(IOpenApiWriter writer)
public void SerializeAsV3WithoutReference(IOpenApiWriter writer, OpenApiSpecVersion version, SerializeDelegate callback)
{
writer.WriteStartObject();

// path items
foreach (var item in PathItems)
{
writer.WriteRequiredObject(item.Key.Expression, item.Value, (w, p) => p.SerializeAsV3(w));
writer.WriteRequiredObject(item.Key.Expression, item.Value, (w, p) => callback(w, p));
}

// extensions
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);
writer.WriteExtensions(Extensions, version);

writer.WriteEndObject();
}
Expand Down
108 changes: 63 additions & 45 deletions src/Microsoft.OpenApi/Models/OpenApiComponents.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Writers;
using static Microsoft.OpenApi.Extensions.OpenApiSerializableExtensions;

namespace Microsoft.OpenApi.Models
{
Expand Down Expand Up @@ -95,14 +97,50 @@ public OpenApiComponents(OpenApiComponents components)
}

/// <summary>
/// Serialize <see cref="OpenApiComponents"/> to Open Api v3.0.
/// Serialize <see cref="OpenApiComponents"/> to Open API v3.1.
/// </summary>
/// <param name="writer"></param>
public void SerializeAsV31(IOpenApiWriter writer)
{
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_1, (writer, element) => element.SerializeAsV3(writer));

// pathItems - only present in v3.1
writer.WriteOptionalMap(
OpenApiConstants.PathItems,
PathItems,
(w, key, component) =>
{
if (component.Reference != null &&
component.Reference.Type == ReferenceType.Schema &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w, OpenApiSpecVersion.OpenApi3_1, callback: (w, e) => e.SerializeAsV3(w));
}
else
{
component.SerializeAsV3(w);
}
});

writer.WriteEndObject();
}

/// <summary>
/// Serialize <see cref="OpenApiComponents"/> to v3.0
/// </summary>
/// <param name="writer"></param>
public void SerializeAsV3(IOpenApiWriter writer)
{
if (writer == null)
{
throw Error.ArgumentNull(nameof(writer));
}
SerializeInternal(writer, OpenApiSpecVersion.OpenApi3_0, (writer, element) => element.SerializeAsV3(writer));
writer.WriteEndObject();
}

/// <summary>
/// Serialize <see cref="OpenApiComponents"/>.
/// </summary>
private void SerializeInternal(IOpenApiWriter writer, OpenApiSpecVersion version, SerializeDelegate callback)
{
writer = writer ?? throw Error.ArgumentNull(nameof(writer));

// If references have been inlined we don't need the to render the components section
// however if they have cycles, then we will need a component rendered
Expand All @@ -119,7 +157,7 @@ public void SerializeAsV3(IOpenApiWriter writer)
OpenApiConstants.Schemas,
Schemas,
(w, key, component) => {
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
});
}
writer.WriteEndObject();
Expand All @@ -141,11 +179,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.Schema &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -159,11 +197,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.Response &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -177,11 +215,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.Parameter &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -195,11 +233,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.Example &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -213,11 +251,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.RequestBody &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -231,11 +269,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.Header &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -249,11 +287,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.SecurityScheme &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -267,11 +305,11 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.Link &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

Expand All @@ -285,36 +323,16 @@ public void SerializeAsV3(IOpenApiWriter writer)
component.Reference.Type == ReferenceType.Callback &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
component.SerializeAsV3WithoutReference(w, version, callback);
}
else
{
component.SerializeAsV3(w);
}
});

// pathItems
writer.WriteOptionalMap(
OpenApiConstants.PathItems,
PathItems,
(w, key, component) =>
{
if (component.Reference != null &&
component.Reference.Type == ReferenceType.Schema &&
component.Reference.Id == key)
{
component.SerializeAsV3WithoutReference(w);
}
else
{
component.SerializeAsV3(w);
callback(w, component);
}
});

// extensions
writer.WriteExtensions(Extensions, OpenApiSpecVersion.OpenApi3_0);

writer.WriteEndObject();
writer.WriteExtensions(Extensions, version);
}

/// <summary>
Expand Down
Loading