Skip to content
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

Replace JsonSchema with OpenApiSchema #1787

Merged
merged 47 commits into from
Aug 27, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
da64594
Replace JsonSchema with OpenApiSchema
MaggieKimani1 Aug 12, 2024
6cd1db6
Uninstall JSON schema library
MaggieKimani1 Aug 12, 2024
66a033c
Code cleanup
MaggieKimani1 Aug 12, 2024
f0233f8
Delete JSON schema extension classes
MaggieKimani1 Aug 13, 2024
8b0fb29
Add support for examples
MaggieKimani1 Aug 13, 2024
6d32cc1
Add support for pattern properties
MaggieKimani1 Aug 13, 2024
8f62e54
Revert "Add support for pattern properties"
MaggieKimani1 Aug 13, 2024
8e020d8
Merge branch 'mk/add-json-schema-model' into mk/use-json-schema-model
MaggieKimani1 Aug 13, 2024
7f754b7
Code refactoring; replace JsonSchema with OpenApiSchema
MaggieKimani1 Aug 13, 2024
396d450
Clean up tests
MaggieKimani1 Aug 13, 2024
086fc56
Create a proxy object for resolving referenced schemas
MaggieKimani1 Aug 13, 2024
e9b1c57
Mark all properties as virtual to be overriden in the proxy class
MaggieKimani1 Aug 13, 2024
ecbdd5f
Return schema proxy reference if reference pointer exists
MaggieKimani1 Aug 19, 2024
853c2f9
code cleanup
MaggieKimani1 Aug 19, 2024
d2cf6c8
Refactor validation logic for examples
MaggieKimani1 Aug 19, 2024
49a9435
code cleanup
MaggieKimani1 Aug 20, 2024
82ea7b7
Fix failing tests
MaggieKimani1 Aug 20, 2024
8261af6
Update public API
MaggieKimani1 Aug 20, 2024
e9c5c05
Use schema 'id' as a locator for schema registration and performing l…
MaggieKimani1 Aug 20, 2024
1dbd870
Add test to validate
MaggieKimani1 Aug 20, 2024
04d3952
Add support for pattern properties
MaggieKimani1 Aug 13, 2024
6bf026f
Code refactoring; replace JsonSchema with OpenApiSchema
MaggieKimani1 Aug 13, 2024
4b8c697
Clean up tests
MaggieKimani1 Aug 13, 2024
883aba1
Create a proxy object for resolving referenced schemas
MaggieKimani1 Aug 13, 2024
d79beef
Mark all properties as virtual to be overriden in the proxy class
MaggieKimani1 Aug 13, 2024
b79e374
Return schema proxy reference if reference pointer exists
MaggieKimani1 Aug 19, 2024
ca19f45
code cleanup
MaggieKimani1 Aug 19, 2024
eb0cc24
Refactor validation logic for examples
MaggieKimani1 Aug 19, 2024
919c869
code cleanup
MaggieKimani1 Aug 20, 2024
f9f01b7
Fix failing tests
MaggieKimani1 Aug 20, 2024
7fd7ca9
Update public API
MaggieKimani1 Aug 20, 2024
c11bf82
Use schema 'id' as a locator for schema registration and performing l…
MaggieKimani1 Aug 20, 2024
33b4a07
Add test to validate
MaggieKimani1 Aug 20, 2024
9f4e1b7
Merge branch 'mk/use-json-schema-model' of https://github.com/microso…
MaggieKimani1 Aug 20, 2024
2f29a30
Merge branch 'release/2.0.0' into mk/use-json-schema-model
MaggieKimani1 Aug 20, 2024
cc1439e
Use JsonNode in place of OpenApiAny for Enums and Examples
MaggieKimani1 Aug 22, 2024
38bd152
Fix codeQL warnings
MaggieKimani1 Aug 22, 2024
af42af2
Avoid virtual calls in constructors
MaggieKimani1 Aug 22, 2024
4cd04c6
Clean up tests; add test for schema examples
MaggieKimani1 Aug 22, 2024
887748e
Cleanup and add test for cloning examples
MaggieKimani1 Aug 22, 2024
5901f49
Add test to bump up test coverage
MaggieKimani1 Aug 22, 2024
8fd03c9
Clean up serializers
MaggieKimani1 Aug 27, 2024
a9803f7
clean up tests
MaggieKimani1 Aug 27, 2024
e4b0adf
Add tests and samples
MaggieKimani1 Aug 27, 2024
97a13db
Bump test coverage
MaggieKimani1 Aug 27, 2024
9f118b9
Update public API
MaggieKimani1 Aug 27, 2024
264f910
copy file to output directory
MaggieKimani1 Aug 27, 2024
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
276 changes: 67 additions & 209 deletions src/Microsoft.OpenApi.Hidi/Formatters/PowerShellFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,18 @@
using System.Text;
using System.Text.RegularExpressions;
using Humanizer;
using Json.Schema;
using Json.Schema.OpenApi;
using Humanizer.Inflections;
using Microsoft.OpenApi.Hidi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Extensions;

namespace Microsoft.OpenApi.Hidi.Formatters
{
internal class PowerShellFormatter : OpenApiVisitorBase
{
private const string DefaultPutPrefix = ".Update";
private const string PowerShellPutPrefix = ".Set";
private readonly Stack<JsonSchema> _schemaLoop = new();
private readonly Stack<OpenApiSchema> _schemaLoop = new();
private static readonly Regex s_oDataCastRegex = new("(.*(?<=[a-z]))\\.(As(?=[A-Z]).*)", RegexOptions.Compiled, TimeSpan.FromSeconds(5));
private static readonly Regex s_hashSuffixRegex = new(@"^[^-]+", RegexOptions.Compiled, TimeSpan.FromSeconds(5));
private static readonly Regex s_oDataRefRegex = new("(?<=[a-z])Ref(?=[A-Z])", RegexOptions.Compiled, TimeSpan.FromSeconds(5));
Expand All @@ -26,11 +24,11 @@ static PowerShellFormatter()
{
// Add singularization exclusions.
// Enhancement: Read exclusions from a user provided file.
Humanizer.Inflections.Vocabularies.Default.AddSingular("(drive)s$", "$1"); // drives does not properly singularize to drive.
Humanizer.Inflections.Vocabularies.Default.AddSingular("(data)$", "$1"); // exclude the following from singularization.
Humanizer.Inflections.Vocabularies.Default.AddSingular("(delta)$", "$1");
Humanizer.Inflections.Vocabularies.Default.AddSingular("(quota)$", "$1");
Humanizer.Inflections.Vocabularies.Default.AddSingular("(statistics)$", "$1");
Vocabularies.Default.AddSingular("(drive)s$", "$1"); // drives does not properly singularize to drive.
Vocabularies.Default.AddSingular("(data)$", "$1"); // exclude the following from singularization.
Vocabularies.Default.AddSingular("(delta)$", "$1");
Vocabularies.Default.AddSingular("(quota)$", "$1");
Vocabularies.Default.AddSingular("(statistics)$", "$1");
}

//FHL task for PS
Expand All @@ -43,13 +41,13 @@ static PowerShellFormatter()
// 5. Fix anyOf and oneOf schema.
// 6. Add AdditionalProperties to object schemas.

public override void Visit(ref JsonSchema schema)
{
AddAdditionalPropertiesToSchema(ref schema);
schema = ResolveAnyOfSchema(ref schema);
schema = ResolveOneOfSchema(ref schema);
public override void Visit(OpenApiSchema schema)
{
AddAdditionalPropertiesToSchema(schema);
ResolveAnyOfSchema(schema);
ResolveOneOfSchema(schema);

base.Visit(ref schema);
base.Visit(schema);
}

public override void Visit(OpenApiPathItem pathItem)
Expand Down Expand Up @@ -165,237 +163,97 @@ private static IList<OpenApiParameter> ResolveFunctionParameters(IList<OpenApiPa
// Replace content with a schema object of type array
// for structured or collection-valued function parameters
parameter.Content = null;
parameter.Schema = new JsonSchemaBuilder()
.Type(SchemaValueType.Array)
.Items(new JsonSchemaBuilder()
.Type(SchemaValueType.String))
;
parameter.Schema = new()
{
Type = "array",
Items = new()
{
Type = "string"
}
};
}
return parameters;
}

private void AddAdditionalPropertiesToSchema(ref JsonSchema schema)
private void AddAdditionalPropertiesToSchema(OpenApiSchema schema)
{
if (schema != null && !_schemaLoop.Contains(schema) && schema.GetJsonType().Equals(SchemaValueType.Object))
if (schema != null && !_schemaLoop.Contains(schema) && "object".Equals((string)schema.Type, StringComparison.OrdinalIgnoreCase))
{
var schemaBuilder = new JsonSchemaBuilder();
if (schema.Keywords != null)
{
foreach (var keyword in schema.Keywords)
{
schemaBuilder.Add(keyword);
}
}

schema = schemaBuilder.AdditionalProperties(new JsonSchemaBuilder().Type(SchemaValueType.Object));
schema.AdditionalProperties = new() { Type = "object" };

/* Because 'additionalProperties' are now being walked,
* we need a way to keep track of visited schemas to avoid
* endlessly creating and walking them in an infinite recursion.
*/
var additionalProps = schema.GetAdditionalProperties();

if (additionalProps != null)
{
_schemaLoop.Push(additionalProps);
}
_schemaLoop.Push(schema.AdditionalProperties);
}

}

private static JsonSchema ResolveOneOfSchema(ref JsonSchema schema)
private static void ResolveOneOfSchema(OpenApiSchema schema)
{
if (schema.GetOneOf()?.FirstOrDefault() is {} newSchema)
if (schema.OneOf?.FirstOrDefault() is { } newSchema)
{
var schemaBuilder = BuildSchema(schema);
schemaBuilder = schemaBuilder.Remove("oneOf");
schema = schemaBuilder.Build();

schema = FlattenSchema(schema, newSchema);
schema.OneOf = null;
FlattenSchema(schema, newSchema);
}

return schema;
}

private static JsonSchema ResolveAnyOfSchema(ref JsonSchema schema)
private static void ResolveAnyOfSchema(OpenApiSchema schema)
{
if (schema.GetAnyOf()?.FirstOrDefault() is {} newSchema)
if (schema.AnyOf?.FirstOrDefault() is { } newSchema)
{
var schemaBuilder = BuildSchema(schema);
schemaBuilder = schemaBuilder.Remove("anyOf");
schema = schemaBuilder.Build();

schema = FlattenSchema(schema, newSchema);
schema.AnyOf = null;
FlattenSchema(schema, newSchema);
}

return schema;
}

private static JsonSchema FlattenSchema(JsonSchema schema, JsonSchema newSchema)
private static void FlattenSchema(OpenApiSchema schema, OpenApiSchema newSchema)
{
if (newSchema != null)
{
var newSchemaRef = newSchema.GetRef();

if (newSchemaRef != null)
if (newSchema.Reference != null)
{
var schemaBuilder = BuildSchema(schema);
schema = schemaBuilder.Ref(newSchemaRef);
schema.Reference = newSchema.Reference;
schema.UnresolvedReference = true;
}
else
{
// Copies schema properties based on https://github.com/microsoft/OpenAPI.NET.OData/pull/264.
schema = CopySchema(schema, newSchema);
}
}

return schema;
}

private static JsonSchema CopySchema(JsonSchema schema, JsonSchema newSchema)
{
var schemaBuilder = new JsonSchemaBuilder();
var keywords = schema.Keywords;
if (keywords != null)
{
foreach (var keyword in keywords)
{
schemaBuilder.Add(keyword);
CopySchema(schema, newSchema);
}
}

if (schema.GetTitle() == null && newSchema.GetTitle() is { } title)
{
schemaBuilder.Title(title);
}
if (schema.GetJsonType() == null && newSchema.GetJsonType() is { } type)
{
schemaBuilder.Type(type);
}
if (schema.GetFormat() == null && newSchema.GetFormat() is { } format)
{
schemaBuilder.Format(format);
}
if (schema.GetDescription() == null && newSchema.GetDescription() is { } description)
{
schemaBuilder.Description(description);
}
if (schema.GetMaximum() == null && newSchema.GetMaximum() is { } max)
{
schemaBuilder.Maximum(max);
}
if (schema.GetExclusiveMaximum() == null && newSchema.GetExclusiveMaximum() is { } exclusiveMaximum)
{
schemaBuilder.ExclusiveMaximum(exclusiveMaximum);
}
if (schema.GetMinimum() == null && newSchema.GetMinimum() is { } min)
{
schemaBuilder.Minimum(min);
}
if (schema.GetExclusiveMinimum() == null && newSchema.GetExclusiveMinimum() is { } exclusiveMinimum)
{
schemaBuilder.ExclusiveMinimum(exclusiveMinimum);
}
if (schema.GetMaxLength() == null && newSchema.GetMaxLength() is { } maxLength)
{
schemaBuilder.MaxLength(maxLength);
}
if (schema.GetMinLength() == null && newSchema.GetMinLength() is { } minLength)
{
schemaBuilder.MinLength(minLength);
}
if (schema.GetPattern() == null && newSchema.GetPattern() is { } pattern)
{
schemaBuilder.Pattern(pattern);
}
if (schema.GetMultipleOf() == null && newSchema.GetMultipleOf() is { } multipleOf)
{
schemaBuilder.MultipleOf(multipleOf);
}
if (schema.GetNot() == null && newSchema.GetNot() is { } not)
{
schemaBuilder.Not(not);
}
if (schema.GetRequired() == null && newSchema.GetRequired() is { } required)
{
schemaBuilder.Required(required);
}
if (schema.GetItems() == null && newSchema.GetItems() is { } items)
{
schemaBuilder.Items(items);
}
if (schema.GetMaxItems() == null && newSchema.GetMaxItems() is { } maxItems)
{
schemaBuilder.MaxItems(maxItems);
}
if (schema.GetMinItems() == null && newSchema.GetMinItems() is { } minItems)
{
schemaBuilder.MinItems(minItems);
}
if (schema.GetUniqueItems() == null && newSchema.GetUniqueItems() is { } uniqueItems)
{
schemaBuilder.UniqueItems(uniqueItems);
}
if (schema.GetProperties() == null && newSchema.GetProperties() is { } properties)
{
schemaBuilder.Properties(properties);
}
if (schema.GetMaxProperties() == null && newSchema.GetMaxProperties() is { } maxProperties)
{
schemaBuilder.MaxProperties(maxProperties);
}
if (schema.GetMinProperties() == null && newSchema.GetMinProperties() is { } minProperties)
{
schemaBuilder.MinProperties(minProperties);
}
if (schema.GetDiscriminator() == null && newSchema.GetDiscriminator() is { } discriminator)
{
schemaBuilder.Discriminator(discriminator.PropertyName, discriminator.Mapping, discriminator.Extensions);
}
if (schema.GetOpenApiExternalDocs() == null && newSchema.GetOpenApiExternalDocs() is { } externalDocs)
{
schemaBuilder.OpenApiExternalDocs(externalDocs);
}
if (schema.GetEnum() == null && newSchema.GetEnum() is { } enumCollection)
{
schemaBuilder.Enum(enumCollection);
}

if (!schema.GetReadOnly() is { } && newSchema.GetReadOnly() is { } newValue)
{
schemaBuilder.ReadOnly(newValue);
}

if (!schema.GetWriteOnly() is { } && newSchema.GetWriteOnly() is { } newWriteOnlyValue)
{
schemaBuilder.WriteOnly(newWriteOnlyValue);
}

if (!schema.GetNullable() is { } && newSchema.GetNullable() is { } newNullableValue)
{
schemaBuilder.Nullable(newNullableValue);
}

if (!schema.GetDeprecated() is { } && newSchema.GetDeprecated() is { } newDepracatedValue)
{
schemaBuilder.Deprecated(newDepracatedValue);
}

return schemaBuilder;
}

private static JsonSchemaBuilder BuildSchema(JsonSchema schema)
private static void CopySchema(OpenApiSchema schema, OpenApiSchema newSchema)
{
var schemaBuilder = new JsonSchemaBuilder();
if (schema.Keywords != null)
{
foreach (var keyword in schema.Keywords)
{
schemaBuilder.Add(keyword);
}
}

return schemaBuilder;
schema.Title ??= newSchema.Title;
schema.Type ??= newSchema.Type;
schema.Format ??= newSchema.Format;
schema.Description ??= newSchema.Description;
schema.Maximum ??= newSchema.Maximum;
schema.ExclusiveMaximum ??= newSchema.ExclusiveMaximum;
schema.Minimum ??= newSchema.Minimum;
schema.ExclusiveMinimum ??= newSchema.ExclusiveMinimum;
schema.MaxLength ??= newSchema.MaxLength;
schema.MinLength ??= newSchema.MinLength;
schema.Pattern ??= newSchema.Pattern;
schema.MultipleOf ??= newSchema.MultipleOf;
schema.Not ??= newSchema.Not;
schema.Required ??= newSchema.Required;
schema.Items ??= newSchema.Items;
schema.MaxItems ??= newSchema.MaxItems;
schema.MinItems ??= newSchema.MinItems;
schema.UniqueItems ??= newSchema.UniqueItems;
schema.Properties ??= newSchema.Properties;
schema.MaxProperties ??= newSchema.MaxProperties;
schema.MinProperties ??= newSchema.MinProperties;
schema.Discriminator ??= newSchema.Discriminator;
schema.ExternalDocs ??= newSchema.ExternalDocs;
schema.Enum ??= newSchema.Enum;
schema.ReadOnly = !schema.ReadOnly ? newSchema.ReadOnly : schema.ReadOnly;
schema.WriteOnly = !schema.WriteOnly ? newSchema.WriteOnly : schema.WriteOnly;
schema.Nullable = !schema.Nullable ? newSchema.Nullable : schema.Nullable;
schema.Deprecated = !schema.Deprecated ? newSchema.Deprecated : schema.Deprecated;
}
}
}
3 changes: 1 addition & 2 deletions src/Microsoft.OpenApi.Hidi/StatsVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using Json.Schema;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Services;

Expand All @@ -20,7 +19,7 @@ public override void Visit(OpenApiParameter parameter)

public int SchemaCount { get; set; }

public override void Visit(ref JsonSchema schema)
public override void Visit(OpenApiSchema schema)
{
SchemaCount++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="JsonSchema.Net" Version="4.1.5" />
<PackageReference Include="JsonSchema.Net.OpenApi" Version="1.1.0" />
<PackageReference Include="SharpYaml" Version="2.1.1" />
<PackageReference Include="System.Text.Json" Version="8.0.4" />
</ItemGroup>
Expand Down
1 change: 0 additions & 1 deletion src/Microsoft.OpenApi.Workbench/MainModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.OpenApi.Extensions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Reader;
using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Validations;

Expand Down
Loading
Loading