|
|
|
@ -5,7 +5,6 @@ |
|
|
|
// All rights reserved. Licensed under the MIT license.
|
|
|
|
// ==========================================================================
|
|
|
|
|
|
|
|
using System; |
|
|
|
using System.Collections.ObjectModel; |
|
|
|
using System.Linq; |
|
|
|
using NJsonSchema; |
|
|
|
@ -14,76 +13,86 @@ using Squidex.Domain.Apps.Core.Schemas; |
|
|
|
using Squidex.Infrastructure; |
|
|
|
using Squidex.Infrastructure.Collections; |
|
|
|
using Squidex.Infrastructure.Json; |
|
|
|
using Squidex.Text; |
|
|
|
|
|
|
|
#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
|
|
|
|
|
|
|
|
namespace Squidex.Domain.Apps.Core.GenerateJsonSchema |
|
|
|
{ |
|
|
|
public delegate JsonSchema SchemaResolver(string name, Func<JsonSchema> schema); |
|
|
|
|
|
|
|
internal sealed class JsonTypeVisitor : IFieldVisitor<JsonSchemaProperty?, JsonTypeVisitor.Args> |
|
|
|
{ |
|
|
|
private const int MaxDepth = 5; |
|
|
|
private static readonly JsonTypeVisitor Instance = new JsonTypeVisitor(); |
|
|
|
|
|
|
|
public sealed record Args(ResolvedComponents Components, SchemaResolver? SchemaResolver, bool WithHiddenFields, int Level = 0); |
|
|
|
public sealed record Args(ResolvedComponents Components, Schema Schema, |
|
|
|
JsonTypeFactory Factory, |
|
|
|
bool WithHidden, |
|
|
|
bool WithComponents, |
|
|
|
int Level = 0); |
|
|
|
|
|
|
|
private JsonTypeVisitor() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
public static JsonSchemaProperty? BuildProperty(IField field, ResolvedComponents components, SchemaResolver? schemaResolver = null, bool withHiddenFields = false) |
|
|
|
public static JsonSchemaProperty? BuildProperty(IField field, ResolvedComponents components, Schema schema, |
|
|
|
JsonTypeFactory factory, |
|
|
|
bool withHidden, |
|
|
|
bool withComponents) |
|
|
|
{ |
|
|
|
var args = new Args(components, schemaResolver, withHiddenFields, 0); |
|
|
|
var args = new Args(components, schema, factory, withHidden, withComponents); |
|
|
|
|
|
|
|
return field.Accept(Instance, args); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IArrayField field, Args args) |
|
|
|
private JsonSchemaProperty? Accept(Args args, NestedField nestedField) |
|
|
|
{ |
|
|
|
if (args.Level > MaxDepth) |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
var itemSchema = SchemaBuilder.Object(); |
|
|
|
return nestedField.Accept(this, args); |
|
|
|
} |
|
|
|
|
|
|
|
var nestedArgs = args with { Level = args.Level + 1 }; |
|
|
|
public JsonSchemaProperty? Visit(IArrayField field, Args args) |
|
|
|
{ |
|
|
|
// Create a reference to give it a nice name in code generation.
|
|
|
|
var (reference, actual) = args.Factory($"{args.Schema.TypeName()}{field.Name.ToPascalCase()}ItemDto"); |
|
|
|
|
|
|
|
foreach (var nestedField in field.Fields.ForApi(args.WithHiddenFields)) |
|
|
|
if (actual != null) |
|
|
|
{ |
|
|
|
var nestedProperty = nestedField.Accept(this, nestedArgs); |
|
|
|
var nestedArgs = args with { Level = args.Level + 1 }; |
|
|
|
|
|
|
|
if (nestedProperty != null) |
|
|
|
foreach (var nestedField in field.Fields.ForApi(args.WithHidden)) |
|
|
|
{ |
|
|
|
nestedProperty.Description = nestedField.RawProperties.Hints; |
|
|
|
nestedProperty.SetRequired(nestedField.RawProperties.IsRequired); |
|
|
|
var nestedProperty = Accept(nestedArgs, nestedField); |
|
|
|
|
|
|
|
if (nestedProperty != null) |
|
|
|
{ |
|
|
|
nestedProperty.Description = nestedField.RawProperties.Hints; |
|
|
|
nestedProperty.SetRequired(nestedField.RawProperties.IsRequired); |
|
|
|
|
|
|
|
itemSchema.Properties.Add(nestedField.Name, nestedProperty); |
|
|
|
actual.Properties.Add(nestedField.Name, nestedProperty); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return SchemaBuilder.ArrayProperty(itemSchema); |
|
|
|
return JsonTypeBuilder.ArrayProperty(reference); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<AssetsFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
return SchemaBuilder.ArrayProperty(SchemaBuilder.String()); |
|
|
|
return JsonTypeBuilder.ArrayProperty(JsonTypeBuilder.String()); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<BooleanFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
return SchemaBuilder.BooleanProperty(); |
|
|
|
return JsonTypeBuilder.BooleanProperty(); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<ComponentFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
if (args.Level > MaxDepth) |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
var property = SchemaBuilder.ObjectProperty(); |
|
|
|
var property = JsonTypeBuilder.ObjectProperty(); |
|
|
|
|
|
|
|
BuildComponent(property, field.Properties.SchemaIds, args); |
|
|
|
|
|
|
|
@ -92,26 +101,21 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<ComponentsFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
if (args.Level > MaxDepth) |
|
|
|
{ |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
var itemSchema = SchemaBuilder.Object(); |
|
|
|
var itemSchema = JsonTypeBuilder.Object(); |
|
|
|
|
|
|
|
BuildComponent(itemSchema, field.Properties.SchemaIds, args); |
|
|
|
|
|
|
|
return SchemaBuilder.ArrayProperty(itemSchema); |
|
|
|
return JsonTypeBuilder.ArrayProperty(itemSchema); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<DateTimeFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
return SchemaBuilder.DateTimeProperty(); |
|
|
|
return JsonTypeBuilder.DateTimeProperty(); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<GeolocationFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
var property = SchemaBuilder.ObjectProperty(); |
|
|
|
var property = JsonTypeBuilder.ObjectProperty(); |
|
|
|
|
|
|
|
property.Format = GeoJson.Format; |
|
|
|
|
|
|
|
@ -120,12 +124,12 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<JsonFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
return SchemaBuilder.JsonProperty(); |
|
|
|
return JsonTypeBuilder.JsonProperty(); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<NumberFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
var property = SchemaBuilder.NumberProperty(); |
|
|
|
var property = JsonTypeBuilder.NumberProperty(); |
|
|
|
|
|
|
|
if (field.Properties.MinValue != null) |
|
|
|
{ |
|
|
|
@ -142,12 +146,12 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<ReferencesFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
return SchemaBuilder.ArrayProperty(SchemaBuilder.String()); |
|
|
|
return JsonTypeBuilder.ArrayProperty(JsonTypeBuilder.String()); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<StringFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
var property = SchemaBuilder.StringProperty(); |
|
|
|
var property = JsonTypeBuilder.StringProperty(); |
|
|
|
|
|
|
|
property.MaxLength = field.Properties.MaxLength; |
|
|
|
property.MinLength = field.Properties.MinLength; |
|
|
|
@ -169,7 +173,7 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<TagsFieldProperties> field, Args args) |
|
|
|
{ |
|
|
|
return SchemaBuilder.ArrayProperty(SchemaBuilder.String()); |
|
|
|
return JsonTypeBuilder.ArrayProperty(JsonTypeBuilder.String()); |
|
|
|
} |
|
|
|
|
|
|
|
public JsonSchemaProperty? Visit(IField<UIFieldProperties> field, Args args) |
|
|
|
@ -177,53 +181,50 @@ namespace Squidex.Domain.Apps.Core.GenerateJsonSchema |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
private void BuildComponent(JsonSchema jsonSchema, ReadonlyList<DomainId>? schemaIds, Args args) |
|
|
|
private static void BuildComponent(JsonSchema jsonSchema, ReadonlyList<DomainId>? schemaIds, Args args) |
|
|
|
{ |
|
|
|
jsonSchema.Properties.Add(Component.Discriminator, SchemaBuilder.StringProperty(isRequired: true)); |
|
|
|
|
|
|
|
if (args.SchemaResolver != null) |
|
|
|
if (args.WithComponents) |
|
|
|
{ |
|
|
|
var schemas = schemaIds?.Select(x => args.Components.Get(x)).NotNull() ?? Enumerable.Empty<Schema>(); |
|
|
|
|
|
|
|
var discriminator = new OpenApiDiscriminator |
|
|
|
{ |
|
|
|
PropertyName = Component.Discriminator |
|
|
|
}; |
|
|
|
|
|
|
|
foreach (var schema in schemas) |
|
|
|
foreach (var schema in args.Components.Resolve(schemaIds).Values) |
|
|
|
{ |
|
|
|
var schemaName = $"{schema.TypeName()}ComponentDto"; |
|
|
|
// Create a reference to give it a nice name in code generation.
|
|
|
|
var (reference, actual) = args.Factory($"{schema.TypeName()}ComponentDto"); |
|
|
|
|
|
|
|
var componentSchema = args.SchemaResolver(schemaName, () => |
|
|
|
if (actual != null) |
|
|
|
{ |
|
|
|
var nestedArgs = args with { Level = args.Level + 1 }; |
|
|
|
|
|
|
|
var componentSchema = SchemaBuilder.Object(); |
|
|
|
|
|
|
|
foreach (var sharedField in schema.Fields.ForApi(nestedArgs.WithHiddenFields)) |
|
|
|
foreach (var field in schema.Fields.ForApi(args.WithHidden)) |
|
|
|
{ |
|
|
|
var sharedProperty = sharedField.Accept(this, nestedArgs); |
|
|
|
|
|
|
|
if (sharedProperty != null) |
|
|
|
var property = |
|
|
|
BuildProperty( |
|
|
|
field, |
|
|
|
args.Components, |
|
|
|
schema, |
|
|
|
args.Factory, |
|
|
|
args.WithHidden, |
|
|
|
args.WithComponents); |
|
|
|
|
|
|
|
if (property != null) |
|
|
|
{ |
|
|
|
sharedProperty.Description = sharedField.RawProperties.Hints; |
|
|
|
sharedProperty.SetRequired(sharedField.RawProperties.IsRequired); |
|
|
|
property.SetRequired(field.RawProperties.IsRequired); |
|
|
|
property.SetDescription(field); |
|
|
|
|
|
|
|
componentSchema.Properties.Add(sharedField.Name, sharedProperty); |
|
|
|
actual.Properties.Add(field.Name, property); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
componentSchema.Properties.Add(Component.Discriminator, SchemaBuilder.StringProperty(isRequired: true)); |
|
|
|
|
|
|
|
return componentSchema; |
|
|
|
}); |
|
|
|
|
|
|
|
jsonSchema.OneOf.Add(componentSchema); |
|
|
|
jsonSchema.OneOf.Add(reference); |
|
|
|
|
|
|
|
discriminator.Mapping[schemaName] = componentSchema; |
|
|
|
discriminator.Mapping[schema.Name] = reference; |
|
|
|
} |
|
|
|
|
|
|
|
jsonSchema.DiscriminatorObject = discriminator; |
|
|
|
jsonSchema.Properties.Add(Component.Discriminator, JsonTypeBuilder.StringProperty(isRequired: true)); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
|