diff --git a/src/Squidex.Core/Schemas/BooleanField.cs b/src/Squidex.Core/Schemas/BooleanField.cs index 1097d3eb9..31d3c840b 100644 --- a/src/Squidex.Core/Schemas/BooleanField.cs +++ b/src/Squidex.Core/Schemas/BooleanField.cs @@ -6,8 +6,10 @@ // All rights reserved. // ========================================================================== +using System; using System.Collections.Generic; using Newtonsoft.Json.Linq; +using NJsonSchema; using Squidex.Core.Schemas.Validators; namespace Squidex.Core.Schemas @@ -31,5 +33,10 @@ namespace Squidex.Core.Schemas { return (bool?)value; } + + protected override void PrepareJsonSchema(JsonProperty jsonProperty) + { + jsonProperty.Type = JsonObjectType.Boolean; + } } } diff --git a/src/Squidex.Core/Schemas/Field.cs b/src/Squidex.Core/Schemas/Field.cs index 57805b577..44a22ea0a 100644 --- a/src/Squidex.Core/Schemas/Field.cs +++ b/src/Squidex.Core/Schemas/Field.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using NJsonSchema; using Squidex.Infrastructure; // ReSharper disable InvertIf // ReSharper disable ConvertIfStatementToReturnStatement @@ -127,8 +128,64 @@ namespace Squidex.Core.Schemas return Clone(clone => clone.name = newName); } + public void AddToSchema(JsonSchema4 schema, HashSet languages) + { + Guard.NotNull(schema, nameof(schema)); + Guard.NotEmpty(languages, nameof(languages)); + + if (RawProperties.IsLocalizable) + { + var localizableProperty = new JsonProperty { IsRequired = true, Type = JsonObjectType.Object }; + var localizableType = new JsonSchema4 { Id = $"{Name}ByLanguage" }; + + foreach (var language in languages) + { + var languageProperty = CreateProperty(); + + if (!string.IsNullOrWhiteSpace(languageProperty.Title)) + { + languageProperty.Title += $" ({language.EnglishName})"; + } + + localizableType.Properties.Add(language.Iso2Code, languageProperty); + } + + localizableProperty.OneOf.Add(localizableType); + + schema.Properties.Add(Name, localizableProperty); + } + else + { + schema.Properties.Add(Name, CreateProperty()); + } + } + + public JsonProperty CreateProperty() + { + var jsonProperty = new JsonProperty { IsRequired = RawProperties.IsRequired }; + + if (!string.IsNullOrWhiteSpace(RawProperties.Hints)) + { + jsonProperty.Title = RawProperties.Hints; + } + else if (!string.IsNullOrWhiteSpace(RawProperties.Label)) + { + jsonProperty.Title = $"The {RawProperties.Label} field"; + } + else + { + jsonProperty.Title = $"The {Name} field"; + } + + PrepareJsonSchema(jsonProperty); + + return jsonProperty; + } + protected abstract IEnumerable CreateValidators(); + protected abstract void PrepareJsonSchema(JsonProperty jsonProperty); + protected abstract object ConvertValue(JToken value); } } \ No newline at end of file diff --git a/src/Squidex.Core/Schemas/NumberField.cs b/src/Squidex.Core/Schemas/NumberField.cs index c9095c45f..c07bcf360 100644 --- a/src/Squidex.Core/Schemas/NumberField.cs +++ b/src/Squidex.Core/Schemas/NumberField.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; +using NJsonSchema; using Squidex.Core.Schemas.Validators; namespace Squidex.Core.Schemas @@ -38,6 +39,21 @@ namespace Squidex.Core.Schemas } } + protected override void PrepareJsonSchema(JsonProperty jsonProperty) + { + jsonProperty.Type = JsonObjectType.Number; + + if (Properties.MinValue.HasValue) + { + jsonProperty.Minimum = (decimal)Properties.MinValue.Value; + } + + if (Properties.MaxValue.HasValue) + { + jsonProperty.Maximum = (decimal)Properties.MaxValue.Value; + } + } + protected override object ConvertValue(JToken value) { return (double?)value; diff --git a/src/Squidex.Core/Schemas/Schema.cs b/src/Squidex.Core/Schemas/Schema.cs index 15cb646c7..f7caa72e4 100644 --- a/src/Squidex.Core/Schemas/Schema.cs +++ b/src/Squidex.Core/Schemas/Schema.cs @@ -12,6 +12,7 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; using Newtonsoft.Json.Linq; +using NJsonSchema; using Squidex.Infrastructure; // ReSharper disable InvertIf @@ -165,6 +166,33 @@ namespace Squidex.Core.Schemas return AddOrUpdateField(newField); } + public JsonSchema4 BuildSchema(HashSet languages) + { + Guard.NotEmpty(languages, nameof(languages)); + + var schema = new JsonSchema4 { Id = Name }; + + if (!string.IsNullOrWhiteSpace(Properties.Hints)) + { + schema.Title = Properties.Hints; + } + else if (!string.IsNullOrWhiteSpace(Properties.Label)) + { + schema.Title = $"The {Properties.Label} field"; + } + else + { + schema.Title = $"The {Name} field"; + } + + foreach (var field in fieldsByName.Values) + { + field.AddToSchema(schema, languages); + } + + return schema; + } + public async Task ValidateAsync(JObject data, IList errors, HashSet languages) { Guard.NotNull(data, nameof(data)); diff --git a/src/Squidex.Core/Schemas/StringField.cs b/src/Squidex.Core/Schemas/StringField.cs index 412c6f23a..8b57f3490 100644 --- a/src/Squidex.Core/Schemas/StringField.cs +++ b/src/Squidex.Core/Schemas/StringField.cs @@ -7,8 +7,10 @@ // ========================================================================== using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using Newtonsoft.Json.Linq; +using NJsonSchema; using Squidex.Core.Schemas.Validators; namespace Squidex.Core.Schemas @@ -47,5 +49,18 @@ namespace Squidex.Core.Schemas { return value.ToString(); } + + protected override void PrepareJsonSchema(JsonProperty jsonProperty) + { + jsonProperty.Type = JsonObjectType.String; + + jsonProperty.MinLength = Properties.MinLength; + jsonProperty.MaxLength = Properties.MaxLength; + + if (Properties.AllowedValues != null) + { + jsonProperty.EnumerationNames = new Collection(Properties.AllowedValues); + } + } } } diff --git a/src/Squidex.Core/project.json b/src/Squidex.Core/project.json index 8692a4f1a..1a55ec103 100644 --- a/src/Squidex.Core/project.json +++ b/src/Squidex.Core/project.json @@ -4,7 +4,8 @@ "Squidex.Infrastructure": "1.0.0-*", "protobuf-net": "2.1.0", "NETStandard.Library": "1.6.1", - "Microsoft.NETCore.App": "1.1.0" + "Microsoft.NETCore.App": "1.1.0", + "NJsonSchema": "7.6.6221.22528" }, "frameworks": { "netcoreapp1.0": { diff --git a/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs b/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs index 39577e06c..5b6e33bbc 100644 --- a/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/Json/JsonSerializerTests.cs @@ -32,12 +32,13 @@ namespace Squidex.Core.Schemas.Json { var schema = Schema.Create("my-schema", new SchemaProperties()) - .AddOrUpdateField(new StringField(1, "field1", new StringFieldProperties { Label = "Field1", Pattern = "[0-9]{3}" })) - .AddOrUpdateField(new NumberField(2, "field2", new NumberFieldProperties { Hints = "Hints" })) - .AddOrUpdateField(new BooleanField(2, "field2", new BooleanFieldProperties())) - .Publish() - .HideField(2) - .DisableField(1); + .AddOrUpdateField(new StringField(1, "field1", + new StringFieldProperties { Label = "Field1", Pattern = "[0-9]{3}" })).DisableField(1) + .AddOrUpdateField(new NumberField(2, "field2", + new NumberFieldProperties { Hints = "Hints" })) + .AddOrUpdateField(new BooleanField(3, "field2", + new BooleanFieldProperties())).HideField(2) + .Publish(); var sut = new SchemaJsonSerializer(new FieldRegistry(), serializerSettings); diff --git a/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs b/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs index 38f2ca803..7770d1937 100644 --- a/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs +++ b/tests/Squidex.Core.Tests/Schemas/SchemaTests.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using Squidex.Infrastructure; using Xunit; @@ -243,6 +244,25 @@ namespace Squidex.Core.Schemas Assert.Throws(() => sut.Unpublish()); } + [Fact] + public void Should_build_schema() + { + var schema = + Schema.Create("user", new SchemaProperties { Hints = "The User" }) + .AddOrUpdateField(new StringField(1, "firstName", + new StringFieldProperties { Label = "FirstName", IsLocalizable = true, IsRequired = true, AllowedValues = new [] { "1", "2" }.ToImmutableList() })) + .AddOrUpdateField(new StringField(2, "lastName", + new StringFieldProperties { Hints = "Last Name" })) + .AddOrUpdateField(new BooleanField(3, "admin", + new BooleanFieldProperties())) + .AddOrUpdateField(new NumberField(4, "age", + new NumberFieldProperties())); + + var json = schema.BuildSchema(new HashSet(new [] { Language.GetLanguage("de"), Language.GetLanguage("en") })).ToJson(); + + Assert.NotNull(json); + } + private NumberField AddField() { var field = new NumberField(1, "my-field", new NumberFieldProperties());