diff --git a/extensions/Squidex.Extensions/Squidex.Extensions.csproj b/extensions/Squidex.Extensions/Squidex.Extensions.csproj index 1756e0c3e..4f8e9fcb0 100644 --- a/extensions/Squidex.Extensions/Squidex.Extensions.csproj +++ b/extensions/Squidex.Extensions/Squidex.Extensions.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs index 1de894b81..c58263cb1 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/ArrayField.cs @@ -31,6 +31,11 @@ namespace Squidex.Domain.Apps.Core.Schemas get { return fields.ByName; } } + public FieldCollection FieldCollection + { + get { return fields; } + } + public ArrayField(long id, string name, Partitioning partitioning, ArrayFieldProperties properties = null, IFieldSettings settings = null) : base(id, name, partitioning, properties) { diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldExtensions.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldExtensions.cs index bf34e0911..3447e9271 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldExtensions.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldExtensions.cs @@ -5,12 +5,19 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Infrastructure; using System.Collections.Generic; +using NamedIdStatic = Squidex.Infrastructure.NamedId; namespace Squidex.Domain.Apps.Core.Schemas { public static class FieldExtensions { + public static NamedId NamedId(this IField field) + { + return NamedIdStatic.Of(field.Id, field.Name); + } + public static Schema ReorderFields(this Schema schema, List ids, long? parentId = null) { if (parentId != null) diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs index cd32ac6b7..17876b6e7 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/FieldRegistry.cs @@ -12,58 +12,26 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Core.Schemas { - public sealed class FieldRegistry + public static class FieldRegistry { - private readonly TypeNameRegistry typeNameRegistry; - private readonly HashSet supportedFields = new HashSet(); - - public FieldRegistry(TypeNameRegistry typeNameRegistry) + public static void Setup(TypeNameRegistry typeNameRegistry) { Guard.NotNull(typeNameRegistry, nameof(typeNameRegistry)); - this.typeNameRegistry = typeNameRegistry; - var types = typeof(FieldRegistry).Assembly.GetTypes().Where(x => x.BaseType == typeof(FieldProperties)); + var supportedFields = new HashSet(); + foreach (var type in types) { - RegisterField(type); + if (supportedFields.Add(type)) + { + typeNameRegistry.Map(type); + } } typeNameRegistry.MapObsolete(typeof(ReferencesFieldProperties), "References"); typeNameRegistry.MapObsolete(typeof(DateTimeFieldProperties), "DateTime"); } - - private void RegisterField(Type type) - { - if (supportedFields.Add(type)) - { - typeNameRegistry.Map(type); - } - } - - public RootField CreateRootField(long id, string name, Partitioning partitioning, FieldProperties properties, IFieldSettings settings = null) - { - CheckProperties(properties); - - return properties.CreateRootField(id, name, partitioning, settings); - } - - public NestedField CreateNestedField(long id, string name, FieldProperties properties, IFieldSettings settings = null) - { - CheckProperties(properties); - - return properties.CreateNestedField(id, name, settings); - } - - private void CheckProperties(FieldProperties properties) - { - Guard.NotNull(properties, nameof(properties)); - - if (!supportedFields.Contains(properties.GetType())) - { - throw new InvalidOperationException($"The field property '{properties.GetType()}' is not supported."); - } - } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs index 570e75aa5..2ae68dabe 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Fields.cs @@ -111,7 +111,7 @@ namespace Squidex.Domain.Apps.Core.Schemas return new NestedField(id, name, properties, settings); } - public static Schema AddArray(this Schema schema, long id, string name, Partitioning partitioning, Func handler, ArrayFieldProperties properties = null, IFieldSettings settings = null) + public static Schema AddArray(this Schema schema, long id, string name, Partitioning partitioning, Func handler = null, ArrayFieldProperties properties = null, IFieldSettings settings = null) { var field = Array(id, name, partitioning, properties, settings); diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/IArrayField.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/IArrayField.cs index 0ea74cfbd..825013f46 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/IArrayField.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/IArrayField.cs @@ -16,5 +16,7 @@ namespace Squidex.Domain.Apps.Core.Schemas IReadOnlyDictionary FieldsById { get; } IReadOnlyDictionary FieldsByName { get; } + + FieldCollection FieldCollection { get; } } } diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs index c7a42baaa..21f28db97 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonNestedFieldModel.cs @@ -20,17 +20,15 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json [JsonProperty] public bool IsHidden { get; set; } + [JsonProperty] + public bool IsLocked { get; set; } + [JsonProperty] public bool IsDisabled { get; set; } [JsonProperty] public FieldProperties Properties { get; set; } - public bool IsLocked - { - get { return false; } - } - public NestedField ToNestedField() { return Properties.CreateNestedField(Id, Name, this); diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs index c3c04c851..83196b881 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Json/JsonSchemaModel.cs @@ -6,8 +6,11 @@ // ========================================================================== using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Squidex.Infrastructure; +using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Core.Schemas.Json { @@ -16,24 +19,34 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json [JsonProperty] public string Name { get; set; } + [JsonProperty] + public string Category { get; set; } + + [JsonProperty] + public bool IsSingleton { get; set; } + [JsonProperty] public bool IsPublished { get; set; } [JsonProperty] public SchemaProperties Properties { get; set; } + [JsonProperty] + public SchemaScripts Scripts { get; set; } + [JsonProperty] public JsonFieldModel[] Fields { get; set; } + [JsonProperty] + public Dictionary PreviewUrls { get; set; } + public JsonSchemaModel() { } public JsonSchemaModel(Schema schema) { - Name = schema.Name; - - Properties = schema.Properties; + SimpleMapper.Map(schema, this); Fields = schema.Fields.ToArray(x => @@ -49,7 +62,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json Properties = x.RawProperties }); - IsPublished = schema.IsPublished; + PreviewUrls = schema.PreviewUrls.ToDictionary(x => x.Key, x => x.Value); } private static JsonNestedFieldModel[] CreateChildren(IField field) @@ -62,6 +75,7 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json Id = x.Id, Name = x.Name, IsHidden = x.IsHidden, + IsLocked = x.IsLocked, IsDisabled = x.IsDisabled, Properties = x.RawProperties }); @@ -74,7 +88,24 @@ namespace Squidex.Domain.Apps.Core.Schemas.Json { var fields = Fields.ToArray(f => f.ToField()) ?? Array.Empty(); - return new Schema(Name, fields, Properties, IsPublished); + var schema = new Schema(Name, fields, Properties, IsPublished, IsSingleton); + + if (!string.IsNullOrWhiteSpace(Category)) + { + schema = schema.ChangeCategory(Category); + } + + if (Scripts != null) + { + schema = schema.ConfigureScripts(Scripts); + } + + if (PreviewUrls?.Count > 0) + { + schema = schema.ConfigurePreviewUrls(PreviewUrls); + } + + return schema; } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs index c6e187087..edddf4103 100644 --- a/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/Schema.cs @@ -14,8 +14,13 @@ namespace Squidex.Domain.Apps.Core.Schemas { public sealed class Schema : Cloneable { + private static readonly Dictionary EmptyPreviewUrls = new Dictionary(); private readonly string name; + private readonly bool isSingleton; + private string category; private FieldCollection fields = FieldCollection.Empty; + private IReadOnlyDictionary previewUrls = EmptyPreviewUrls; + private SchemaScripts scripts = new SchemaScripts(); private SchemaProperties properties; private bool isPublished; @@ -24,11 +29,21 @@ namespace Squidex.Domain.Apps.Core.Schemas get { return name; } } + public string Category + { + get { return category; } + } + public bool IsPublished { get { return isPublished; } } + public bool IsSingleton + { + get { return isSingleton; } + } + public IReadOnlyList Fields { get { return fields.Ordered; } @@ -44,12 +59,27 @@ namespace Squidex.Domain.Apps.Core.Schemas get { return fields.ByName; } } + public IReadOnlyDictionary PreviewUrls + { + get { return previewUrls; } + } + + public FieldCollection FieldCollection + { + get { return fields; } + } + + public SchemaScripts Scripts + { + get { return scripts; } + } + public SchemaProperties Properties { get { return properties; } } - public Schema(string name, SchemaProperties properties = null) + public Schema(string name, SchemaProperties properties = null, bool isSingleton = false) { Guard.NotNullOrEmpty(name, nameof(name)); @@ -57,10 +87,12 @@ namespace Squidex.Domain.Apps.Core.Schemas this.properties = properties ?? new SchemaProperties(); this.properties.Freeze(); + + this.isSingleton = isSingleton; } - public Schema(string name, RootField[] fields, SchemaProperties properties, bool isPublished) - : this(name, properties) + public Schema(string name, RootField[] fields, SchemaProperties properties, bool isPublished, bool isSingleton = false) + : this(name, properties, isSingleton) { Guard.NotNull(fields, nameof(fields)); @@ -81,6 +113,16 @@ namespace Squidex.Domain.Apps.Core.Schemas }); } + [Pure] + public Schema ConfigureScripts(SchemaScripts newScripts) + { + return Clone(clone => + { + clone.scripts = newScripts ?? new SchemaScripts(); + clone.scripts.Freeze(); + }); + } + [Pure] public Schema Publish() { @@ -99,31 +141,49 @@ namespace Squidex.Domain.Apps.Core.Schemas }); } + [Pure] + public Schema ChangeCategory(string category) + { + return Clone(clone => + { + clone.category = category; + }); + } + + [Pure] + public Schema ConfigurePreviewUrls(IReadOnlyDictionary previewUrls) + { + return Clone(clone => + { + clone.previewUrls = previewUrls ?? EmptyPreviewUrls; + }); + } + [Pure] public Schema DeleteField(long fieldId) { - return Updatefields(f => f.Remove(fieldId)); + return UpdateFields(f => f.Remove(fieldId)); } [Pure] public Schema ReorderFields(List ids) { - return Updatefields(f => f.Reorder(ids)); + return UpdateFields(f => f.Reorder(ids)); } [Pure] public Schema AddField(RootField field) { - return Updatefields(f => f.Add(field)); + return UpdateFields(f => f.Add(field)); } [Pure] public Schema UpdateField(long fieldId, Func updater) { - return Updatefields(f => f.Update(fieldId, updater)); + return UpdateFields(f => f.Update(fieldId, updater)); } - private Schema Updatefields(Func, FieldCollection> updater) + private Schema UpdateFields(Func, FieldCollection> updater) { var newFields = updater(fields); diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs new file mode 100644 index 000000000..b1d672216 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaExtensions.cs @@ -0,0 +1,55 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure; +using System; + +namespace Squidex.Domain.Apps.Core.Schemas +{ + public static class SchemaExtensions + { + public static long MaxId(this Schema schema) + { + var id = 0L; + + foreach (var field in schema.Fields) + { + if (field is IArrayField arrayField) + { + foreach (var nestedField in arrayField.Fields) + { + id = Math.Max(id, nestedField.Id); + } + } + + id = Math.Max(id, field.Id); + } + + return id; + } + + public static string TypeName(this IField field) + { + return field.Name.ToPascalCase(); + } + + public static string DisplayName(this IField field) + { + return field.RawProperties.Label.WithFallback(field.TypeName()); + } + + public static string TypeName(this Schema schema) + { + return schema.Name.ToPascalCase(); + } + + public static string DisplayName(this Schema schema) + { + return schema.Properties.Label.WithFallback(schema.TypeName()); + } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaScripts.cs b/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaScripts.cs new file mode 100644 index 000000000..ae3990180 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Model/Schemas/SchemaScripts.cs @@ -0,0 +1,22 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Core.Schemas +{ + public sealed class SchemaScripts : Freezable + { + public string Change { get; set; } + + public string Create { get; set; } + + public string Update { get; set; } + + public string Delete { get; set; } + + public string Query { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Scripts.cs b/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizationOptions.cs similarity index 60% rename from src/Squidex.Domain.Apps.Entities/Apps/Templates/Scripts.cs rename to src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizationOptions.cs index 51d2c72fc..35588d0b4 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Scripts.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizationOptions.cs @@ -5,18 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -namespace Squidex.Domain.Apps.Entities.Apps.Templates +namespace Squidex.Domain.Apps.Core.EventSynchronization { - public static class Scripts + public sealed class SchemaSynchronizationOptions { - public const string Slug = -@"var data = ctx.data; - -if (data.title && data.title.iv) { - data.slug = { iv: slugify(data.title.iv) }; + public bool NoFieldDeletion { get; set; } - replace(data); -} -"; + public bool NoFieldRecreation { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs b/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs new file mode 100644 index 000000000..cebdfbf99 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SchemaSynchronizer.cs @@ -0,0 +1,215 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Schemas; +using Squidex.Infrastructure; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; + +namespace Squidex.Domain.Apps.Core.EventSynchronization +{ + public static class SchemaSynchronizer + { + public static IEnumerable Synchronize(this Schema source, Schema target, IJsonSerializer serializer, Func idGenerator, SchemaSynchronizationOptions options = null) + { + Guard.NotNull(source, nameof(source)); + Guard.NotNull(serializer, nameof(serializer)); + Guard.NotNull(idGenerator, nameof(idGenerator)); + + if (target == null) + { + yield return new SchemaDeleted(); + } + else + { + options = options ?? new SchemaSynchronizationOptions(); + + SchemaEvent E(SchemaEvent @event) + { + return @event; + } + + if (!source.Properties.EqualsJson(target.Properties, serializer)) + { + yield return E(new SchemaUpdated { Properties = target.Properties }); + } + + if (!source.Category.StringEquals(target.Category)) + { + yield return E(new SchemaCategoryChanged { Name = target.Category }); + } + + if (!source.Scripts.EqualsJson(target.Scripts, serializer)) + { + yield return E(new SchemaScriptsConfigured { Scripts = target.Scripts }); + } + + if (!source.PreviewUrls.EqualsDictionary(target.PreviewUrls)) + { + yield return E(new SchemaPreviewUrlsConfigured { PreviewUrls = target.PreviewUrls.ToDictionary(x => x.Key, x => x.Value) }); + } + + if (source.IsPublished != target.IsPublished) + { + yield return target.IsPublished ? + E(new SchemaPublished()) : + E(new SchemaUnpublished()); + } + + var events = SyncFields(source.FieldCollection, target.FieldCollection, serializer, idGenerator, null, options); + + foreach (var @event in events) + { + yield return E(@event); + } + } + } + + private static IEnumerable SyncFields( + FieldCollection source, + FieldCollection target, + IJsonSerializer serializer, + Func idGenerator, + NamedId parentId, SchemaSynchronizationOptions options) where T : class, IField + { + FieldEvent E(FieldEvent @event) + { + @event.ParentFieldId = parentId; + + return @event; + } + + var sourceIds = new List>(source.Ordered.Select(x => x.NamedId())); + var sourceNames = sourceIds.Select(x => x.Name).ToList(); + + if (!options.NoFieldDeletion) + { + foreach (var sourceField in source.Ordered) + { + if (!target.ByName.TryGetValue(sourceField.Name, out var targetField)) + { + var id = sourceField.NamedId(); + + sourceIds.Remove(id); + sourceNames.Remove(id.Name); + + yield return E(new FieldDeleted { FieldId = id }); + } + } + } + + foreach (var targetField in target.Ordered) + { + NamedId id = null; + + var canCreateField = true; + + if (source.ByName.TryGetValue(targetField.Name, out var sourceField)) + { + canCreateField = false; + + id = sourceField.NamedId(); + + if (CanUpdate(sourceField, targetField)) + { + if (!sourceField.RawProperties.EqualsJson(targetField.RawProperties, serializer)) + { + yield return E(new FieldUpdated { FieldId = id, Properties = targetField.RawProperties }); + } + } + else if (!sourceField.IsLocked && !options.NoFieldRecreation) + { + canCreateField = true; + + sourceIds.Remove(id); + sourceNames.Remove(id.Name); + + yield return E(new FieldDeleted { FieldId = id }); + } + } + + if (canCreateField) + { + var partitioning = (string)null; + + if (targetField is IRootField rootField) + { + partitioning = rootField.Partitioning.Key; + } + + id = NamedId.Of(idGenerator(), targetField.Name); + + yield return new FieldAdded + { + Name = targetField.Name, + ParentFieldId = parentId, + Partitioning = partitioning, + Properties = targetField.RawProperties, + FieldId = id + }; + + sourceIds.Add(id); + sourceNames.Add(id.Name); + } + + if (id != null && (sourceField == null || CanUpdate(sourceField, targetField))) + { + if (!targetField.IsLocked.BoolEquals(sourceField?.IsLocked)) + { + yield return E(new FieldLocked { FieldId = id }); + } + + if (!targetField.IsHidden.BoolEquals(sourceField?.IsHidden)) + { + yield return targetField.IsHidden ? + E(new FieldHidden { FieldId = id }) : + E(new FieldShown { FieldId = id }); + } + + if (!targetField.IsDisabled.BoolEquals(sourceField?.IsDisabled)) + { + yield return targetField.IsDisabled ? + E(new FieldDisabled { FieldId = id }) : + E(new FieldEnabled { FieldId = id }); + } + + if ((sourceField == null || sourceField is IArrayField) && targetField is IArrayField targetArrayField) + { + var fields = (sourceField as IArrayField)?.FieldCollection ?? FieldCollection.Empty; + + var events = SyncFields(fields, targetArrayField.FieldCollection, serializer, idGenerator, id, options); + + foreach (var @event in events) + { + yield return @event; + } + } + } + } + + if (sourceNames.Count > 1) + { + var targetNames = target.Ordered.Select(x => x.Name); + + if (sourceNames.Intersect(targetNames).Count() == target.Ordered.Count && !sourceNames.SequenceEqual(targetNames)) + { + yield return new SchemaFieldsReordered { FieldIds = sourceIds.Select(x => x.Id).ToList(), ParentFieldId = parentId }; + } + } + } + + private static bool CanUpdate(IField source, IField target) + { + return !source.IsLocked && source.Name == target.Name && source.RawProperties.TypeEquals(target.RawProperties); + } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SyncHelpers.cs b/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SyncHelpers.cs new file mode 100644 index 000000000..4670750b2 --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Operations/EventSynchronization/SyncHelpers.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Squidex.Infrastructure.Json; + +namespace Squidex.Domain.Apps.Core.EventSynchronization +{ + public static class SyncHelpers + { + public static bool BoolEquals(this bool lhs, bool? rhs) + { + return lhs == (rhs ?? false); + } + + public static bool StringEquals(this string lhs, string rhs) + { + return string.Equals(lhs ?? string.Empty, rhs ?? string.Empty, StringComparison.Ordinal); + } + + public static bool TypeEquals(this object lhs, object rhs) + { + return lhs.GetType() == rhs.GetType(); + } + + public static bool EqualsJson(this T lhs, T rhs, IJsonSerializer serializer) + { + var lhsJson = serializer.Serialize(lhs); + var rhsJson = serializer.Serialize(rhs); + + return string.Equals(lhsJson, rhsJson); + } + } +} diff --git a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj index 3fc9981a9..a2f8491f7 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj +++ b/src/Squidex.Domain.Apps.Core.Operations/Squidex.Domain.Apps.Core.Operations.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj b/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj index 8472247c5..370001c52 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/Squidex.Domain.Apps.Entities.MongoDb.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs b/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs index 05b447fc9..07891e285 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/RolePermissionsProvider.cs @@ -66,7 +66,7 @@ namespace Squidex.Domain.Apps.Entities.Apps var schemaNames = new List(); schemaNames.Add(Permission.Any); - schemaNames.AddRange(schemas.Select(x => x.Name)); + schemaNames.AddRange(schemas.Select(x => x.SchemaDef.Name)); return schemaNames; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs index 6186f354a..12da92a89 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/AssetFieldBuilder.cs @@ -12,7 +12,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class AssetFieldBuilder : FieldBuilder { - public AssetFieldBuilder(CreateSchemaField field) + public AssetFieldBuilder(UpsertSchemaField field) : base(field) { } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs index 657c1a334..49aae2545 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/BooleanFieldBuilder.cs @@ -12,7 +12,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class BooleanFieldBuilder : FieldBuilder { - public BooleanFieldBuilder(CreateSchemaField field) + public BooleanFieldBuilder(UpsertSchemaField field) : base(field) { } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs index 0c53d6cc2..72fc74810 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/DateTimeFieldBuilder.cs @@ -12,7 +12,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class DateTimeFieldBuilder : FieldBuilder { - public DateTimeFieldBuilder(CreateSchemaField field) + public DateTimeFieldBuilder(UpsertSchemaField field) : base(field) { } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs index 2d519d752..05a1cf535 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/FieldBuilder.cs @@ -13,14 +13,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public abstract class FieldBuilder { - private readonly CreateSchemaField field; + private readonly UpsertSchemaField field; protected T Properties() where T : FieldProperties { return field.Properties as T; } - protected FieldBuilder(CreateSchemaField field) + protected FieldBuilder(UpsertSchemaField field) { this.field = field; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/JsonFieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/JsonFieldBuilder.cs index 15c40c38d..bdf4f3ed7 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/JsonFieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/JsonFieldBuilder.cs @@ -11,7 +11,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class JsonFieldBuilder : FieldBuilder { - public JsonFieldBuilder(CreateSchemaField field) + public JsonFieldBuilder(UpsertSchemaField field) : base(field) { } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/NumberFieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/NumberFieldBuilder.cs index 19f30de32..67245aa34 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/NumberFieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/NumberFieldBuilder.cs @@ -11,7 +11,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class NumberFieldBuilder : FieldBuilder { - public NumberFieldBuilder(CreateSchemaField field) + public NumberFieldBuilder(UpsertSchemaField field) : base(field) { } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs index af1e3a7b4..bc588ebb5 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/SchemaBuilder.cs @@ -24,20 +24,39 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders public static SchemaBuilder Create(string name) { + var schemaName = name.ToKebabCase(); + return new SchemaBuilder(new CreateSchema { - Name = name.ToKebabCase(), - Publish = true, - Properties = new SchemaProperties - { - Label = name - } - }); + Name = schemaName + }).Published().WithLabel(name); + } + + public SchemaBuilder WithLabel(string label) + { + command.Properties = command.Properties ?? new SchemaProperties(); + command.Properties.Label = label; + + return this; + } + + public SchemaBuilder WithScripts(SchemaScripts scripts) + { + command.Scripts = scripts; + + return this; + } + + public SchemaBuilder Published() + { + command.IsPublished = true; + + return this; } public SchemaBuilder Singleton() { - command.Singleton = true; + command.IsSingleton = true; return this; } @@ -105,9 +124,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders return this; } - private CreateSchemaField AddField(string name) where T : FieldProperties, new() + private UpsertSchemaField AddField(string name) where T : FieldProperties, new() { - var field = new CreateSchemaField + var field = new UpsertSchemaField { Name = name.ToCamelCase(), Properties = new T @@ -116,7 +135,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders } }; - command.Fields = command.Fields ?? new List(); + command.Fields = command.Fields ?? new List(); command.Fields.Add(field); return field; diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs index 0fd4b857b..8cd60657d 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/StringFieldBuilder.cs @@ -13,7 +13,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class StringFieldBuilder : FieldBuilder { - public StringFieldBuilder(CreateSchemaField field) + public StringFieldBuilder(UpsertSchemaField field) : base(field) { } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs index 707100951..93b4d6611 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/Builders/TagsFieldBuilder.cs @@ -11,7 +11,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates.Builders { public class TagsFieldBuilder : FieldBuilder { - public TagsFieldBuilder(CreateSchemaField field) + public TagsFieldBuilder(UpsertSchemaField field) : base(field) { } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs index bac7d168f..35576796a 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs @@ -11,7 +11,6 @@ using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; using Squidex.Domain.Apps.Entities.Contents.Commands; -using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; @@ -106,20 +105,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates .Disabled() .Label("Slug (Autogenerated)") .Hints("Autogenerated slug that can be used to identity the post.")) + .WithScripts(DefaultScripts.GenerateSlug) .Build(); await publish(schema); - var schemaId = NamedId.Of(schema.SchemaId, schema.Name); - - await publish(new ConfigureScripts - { - SchemaId = schemaId.Id, - ScriptCreate = Scripts.Slug, - ScriptUpdate = Scripts.Slug - }); - - return schemaId; + return NamedId.Of(schema.SchemaId, schema.Name); } private static async Task> CreatePagesSchemaAsync(Func publish) @@ -140,20 +131,12 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates .Disabled() .Label("Slug (Autogenerated)") .Hints("Autogenerated slug that can be used to identity the page.")) + .WithScripts(DefaultScripts.GenerateSlug) .Build(); await publish(schema); - var schemaId = NamedId.Of(schema.SchemaId, schema.Name); - - await publish(new ConfigureScripts - { - SchemaId = schemaId.Id, - ScriptCreate = Scripts.Slug, - ScriptUpdate = Scripts.Slug - }); - - return schemaId; + return NamedId.Of(schema.SchemaId, schema.Name); } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs index 616b8a3ff..7ae11260e 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateIdentityCommandMiddleware.cs @@ -9,7 +9,6 @@ using System; using System.Threading.Tasks; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Templates.Builders; -using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; @@ -18,18 +17,6 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates public sealed class CreateIdentityCommandMiddleware : ICommandMiddleware { private const string TemplateName = "Identity"; - private const string NormalizeScript = @" - var data = ctx.data; - - if (data.userName && data.userName.iv) { - data.normalizedUserName = { iv: data.userName.iv.toUpperCase() }; - } - - if (data.email && data.email.iv) { - data.normalizedEmail = { iv: data.email.iv.toUpperCase() }; - } - - replace(data);"; public async Task HandleAsync(CommandContext context, Func next) { @@ -241,18 +228,10 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates .AddString("Security Stamp", f => f .Disabled() .Hints("Internal security stamp")) + .WithScripts(DefaultScripts.GenerateUsername) .Build(); await publish(schema); - - var schemaId = NamedId.Of(schema.SchemaId, schema.Name); - - await publish(new ConfigureScripts - { - SchemaId = schemaId.Id, - ScriptCreate = NormalizeScript, - ScriptUpdate = NormalizeScript - }); } private static Task CreateApiResourcesSchemaAsync(Func publish) diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/DefaultScripts.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/DefaultScripts.cs new file mode 100644 index 000000000..0144dc10f --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/DefaultScripts.cs @@ -0,0 +1,48 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Schemas; + +namespace Squidex.Domain.Apps.Entities.Apps.Templates +{ + public static class DefaultScripts + { + private const string ScriptToGenerateSlug = @" + var data = ctx.data; + + if (data.title && data.title.iv) { + data.slug = { iv: slugify(data.title.iv) }; + + replace(data); + }"; + + private const string ScriptToGenerateUsername = @" + var data = ctx.data; + + if (data.userName && data.userName.iv) { + data.normalizedUserName = { iv: data.userName.iv.toUpperCase() }; + } + + if (data.email && data.email.iv) { + data.normalizedEmail = { iv: data.email.iv.toUpperCase() }; + } + + replace(data);"; + + public static readonly SchemaScripts GenerateSlug = new SchemaScripts + { + Create = ScriptToGenerateSlug, + Update = ScriptToGenerateSlug + }; + + public static readonly SchemaScripts GenerateUsername = new SchemaScripts + { + Create = ScriptToGenerateUsername, + Update = ScriptToGenerateUsername + }; + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs index 4b1916655..830ace6a8 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/GuidMapper.cs @@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { var newGuid = GenerateNewGuid(namedId.Id); - strings[value] = result = new NamedId(newGuid, namedId.Name).ToString(); + strings[value] = result = NamedId.Of(newGuid, namedId.Name).ToString(); return true; } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index f9e4e3f82..3740d812b 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -301,7 +301,7 @@ namespace Squidex.Domain.Apps.Entities.Backup if (@event.Payload is AppEvent appEvent && !string.IsNullOrWhiteSpace(CurrentJob.NewAppName)) { - appEvent.AppId = new NamedId(appEvent.AppId.Id, CurrentJob.NewAppName); + appEvent.AppId = NamedId.Of(appEvent.AppId.Id, CurrentJob.NewAppName); } foreach (var handler in handlers) diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs index 06cb8efb6..f934c0deb 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentGrain.cs @@ -66,7 +66,7 @@ namespace Squidex.Domain.Apps.Entities.Contents GuardContent.CanCreate(ctx.Schema, c); - await ctx.ExecuteScriptAndTransformAsync(x => x.ScriptCreate, "Create", c, c.Data); + await ctx.ExecuteScriptAndTransformAsync(s => s.Create, "Create", c, c.Data); await ctx.EnrichAsync(c.Data); if (!c.DoNotValidate) @@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Contents if (c.Publish) { - await ctx.ExecuteScriptAsync(x => x.ScriptChange, "Published", c, c.Data); + await ctx.ExecuteScriptAsync(s => s.Change, "Published", c, c.Data); } Create(c); @@ -140,7 +140,7 @@ namespace Squidex.Domain.Apps.Entities.Contents reason = StatusChange.Restored; } - await ctx.ExecuteScriptAsync(x => x.ScriptChange, reason, c, Snapshot.Data); + await ctx.ExecuteScriptAsync(s => s.Change, reason, c, Snapshot.Data); ChangeStatus(c, reason); } @@ -166,7 +166,7 @@ namespace Squidex.Domain.Apps.Entities.Contents GuardContent.CanDelete(ctx.Schema, c); - await ctx.ExecuteScriptAsync(x => x.ScriptDelete, "Delete", c, Snapshot.Data); + await ctx.ExecuteScriptAsync(s => s.Delete, "Delete", c, Snapshot.Data); Delete(c); }); @@ -208,7 +208,7 @@ namespace Squidex.Domain.Apps.Entities.Contents await ctx.ValidateAsync(c.Data); } - newData = await ctx.ExecuteScriptAndTransformAsync(x => x.ScriptUpdate, "Update", c, newData, Snapshot.Data); + newData = await ctx.ExecuteScriptAndTransformAsync(s => s.Update, "Update", c, newData, Snapshot.Data); if (isProposal) { diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs index ca5c36a38..9d851ece7 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.EnrichContent; +using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Scripting; using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Entities.Apps; @@ -86,20 +87,20 @@ namespace Squidex.Domain.Apps.Entities.Contents return data.ValidatePartialAsync(ctx, schemaEntity.SchemaDef, appEntity.PartitionResolver(), message); } - public Task ExecuteScriptAndTransformAsync(Func script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) + public Task ExecuteScriptAndTransformAsync(Func script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) { var ctx = CreateScriptContext(operation, command, data, oldData); - var result = scriptEngine.ExecuteAndTransform(ctx, script(schemaEntity)); + var result = scriptEngine.ExecuteAndTransform(ctx, GetScript(script)); return Task.FromResult(result); } - public Task ExecuteScriptAsync(Func script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) + public Task ExecuteScriptAsync(Func script, object operation, ContentCommand command, NamedContentData data, NamedContentData oldData = null) { var ctx = CreateScriptContext(operation, command, data, oldData); - scriptEngine.Execute(ctx, script(schemaEntity)); + scriptEngine.Execute(ctx, GetScript(script)); return TaskHelper.Done; } @@ -123,5 +124,10 @@ namespace Squidex.Domain.Apps.Entities.Contents { return await contentRepository.QueryIdsAsync(appEntity.Id, filterSchemaId, filterNode); } + + private string GetScript(Func script) + { + return script(schemaEntity.SchemaDef.Scripts); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs index 89b566f2c..14570304c 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/ContentQueryService.cs @@ -158,7 +158,7 @@ namespace Squidex.Domain.Apps.Entities.Contents { var converters = GenerateConverters(context, checkType).ToArray(); - var scriptText = schema.ScriptQuery; + var scriptText = schema.SchemaDef.Scripts.Query; var isScripting = !string.IsNullOrWhiteSpace(scriptText); @@ -277,7 +277,7 @@ namespace Squidex.Domain.Apps.Entities.Contents private static void CheckPermission(ISchemaEntity schema, ClaimsPrincipal user) { var permissions = user.Permissions(); - var permission = Permissions.ForApp(Permissions.AppContentsRead, schema.AppId.Name, schema.Name); + var permission = Permissions.ForApp(Permissions.AppContentsRead, schema.AppId.Name, schema.SchemaDef.Name); if (!permissions.Allows(permission)) { diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs index e35b346dc..56521688b 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/GraphQLModel.cs @@ -50,7 +50,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.GraphQL assetType = new AssetGraphType(this); assetListType = new ListGraphType(new NonNullGraphType(assetType)); - schemasById = schemas.Where(x => x.IsPublished).ToDictionary(x => x.Id); + schemasById = schemas.Where(x => x.SchemaDef.IsPublished).ToDictionary(x => x.Id); graphQLSchema = BuildSchema(this); graphQLSchema.RegisterValueConverter(JsonConverter.Instance); diff --git a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs index 20358dd65..6c930f903 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/GraphQL/Types/ContentDataGraphType.cs @@ -10,6 +10,7 @@ using System.Linq; using GraphQL.Resolvers; using GraphQL.Types; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Json.Objects; diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs b/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs index 194432381..379bd601f 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs @@ -24,7 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards ValidateData(command, e); }); - if (schema.IsSingleton && command.ContentId != schema.Id) + if (schema.SchemaDef.IsSingleton && command.ContentId != schema.Id) { throw new DomainException("Singleton content cannot be created."); } @@ -64,7 +64,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); - if (schema.IsSingleton && command.Status != Status.Published) + if (schema.SchemaDef.IsSingleton && command.Status != Status.Published) { throw new DomainException("Singleton content archived or unpublished."); } @@ -101,7 +101,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); - if (schema.IsSingleton) + if (schema.SchemaDef.IsSingleton) { throw new DomainException("Singleton content cannot be deleted."); } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs index 401a0a67b..a9695f500 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/SingletonCommandMiddleware.cs @@ -24,9 +24,9 @@ namespace Squidex.Domain.Apps.Entities.Contents if (context.IsCompleted && context.Command is CreateSchema createSchema && - createSchema.Singleton) + createSchema.IsSingleton) { - var schemaId = new NamedId(createSchema.SchemaId, createSchema.Name); + var schemaId = NamedId.Of(createSchema.SchemaId, createSchema.Name); var data = new NamedContentData(); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs index 09cc26f0a..92d15b149 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/AddField.cs @@ -9,10 +9,8 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class AddField : SchemaCommand + public sealed class AddField : ParentFieldCommand { - public long? ParentFieldId { get; set; } - public string Name { get; set; } public string Partitioning { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs index d850076a6..5e9528a2c 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ConfigureScripts.cs @@ -5,18 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Domain.Apps.Core.Schemas; + namespace Squidex.Domain.Apps.Entities.Schemas.Commands { public sealed class ConfigureScripts : SchemaCommand { - public string ScriptQuery { get; set; } - - public string ScriptCreate { get; set; } - - public string ScriptUpdate { get; set; } - - public string ScriptDelete { get; set; } - - public string ScriptChange { get; set; } + public SchemaScripts Scripts { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs index 9c39fa5a9..cc882abe4 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchema.cs @@ -8,27 +8,25 @@ using System; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; -using SchemaFields = System.Collections.Generic.List; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class CreateSchema : SchemaCommand, IAppCommand + public sealed class CreateSchema : UpsertCommand, IAppCommand { public NamedId AppId { get; set; } public string Name { get; set; } - public SchemaFields Fields { get; set; } - - public SchemaProperties Properties { get; set; } - - public bool Singleton { get; set; } - - public bool Publish { get; set; } + public bool IsSingleton { get; set; } public CreateSchema() { SchemaId = Guid.NewGuid(); } + + public Schema ToSchema() + { + return ToSchema(Name, IsSingleton); + } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs index 6246ad94c..6e486cb11 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/FieldCommand.cs @@ -7,10 +7,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public class FieldCommand : SchemaCommand + public class FieldCommand : ParentFieldCommand { - public long? ParentFieldId { get; set; } - public long FieldId { get; set; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/PreviewUrlsDto.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ParentFieldCommand.cs similarity index 69% rename from src/Squidex/Areas/Api/Controllers/Schemas/Models/PreviewUrlsDto.cs rename to src/Squidex.Domain.Apps.Entities/Schemas/Commands/ParentFieldCommand.cs index 39c68aef1..91f067fb1 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/PreviewUrlsDto.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ParentFieldCommand.cs @@ -5,11 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; - -namespace Squidex.Areas.Api.Controllers.Schemas.Models +namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class PreviewUrlsDto : Dictionary + public abstract class ParentFieldCommand : SchemaCommand { + public long? ParentFieldId { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs index 4c452d2ff..227443431 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/ReorderFields.cs @@ -9,10 +9,8 @@ using System.Collections.Generic; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class ReorderFields : SchemaCommand + public sealed class ReorderFields : ParentFieldCommand { - public long? ParentFieldId { get; set; } - public List FieldIds { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SynchronizeSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SynchronizeSchema.cs new file mode 100644 index 000000000..cc185e52b --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/SynchronizeSchema.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +namespace Squidex.Domain.Apps.Entities.Schemas.Commands +{ + public sealed class SynchronizeSchema : UpsertCommand + { + public bool NoFieldDeletion { get; set; } + + public bool NoFieldRecreation { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertCommand.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertCommand.cs new file mode 100644 index 000000000..9b9e6bbe9 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertCommand.cs @@ -0,0 +1,116 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Schemas; +using SchemaFields = System.Collections.Generic.List; + +namespace Squidex.Domain.Apps.Entities.Schemas.Commands +{ + public abstract class UpsertCommand : SchemaCommand + { + public bool IsPublished { get; set; } + + public string Category { get; set; } + + public SchemaFields Fields { get; set; } + + public SchemaScripts Scripts { get; set; } + + public SchemaProperties Properties { get; set; } + + public Dictionary PreviewUrls { get; set; } + + public Schema ToSchema(string name, bool isSingleton) + { + var schema = new Schema(name, Properties, isSingleton); + + if (IsPublished) + { + schema = schema.Publish(); + } + + if (Scripts != null) + { + schema = schema.ConfigureScripts(Scripts); + } + + if (PreviewUrls != null) + { + schema = schema.ConfigurePreviewUrls(PreviewUrls); + } + + if (!string.IsNullOrWhiteSpace(Category)) + { + schema = schema.ChangeCategory(Category); + } + + var totalFields = 0; + + if (Fields != null) + { + foreach (var eventField in Fields) + { + totalFields++; + + var partitioning = Partitioning.FromString(eventField.Partitioning); + + var field = eventField.Properties.CreateRootField(totalFields, eventField.Name, partitioning); + + if (field is ArrayField arrayField && eventField.Nested?.Count > 0) + { + foreach (var nestedEventField in eventField.Nested) + { + totalFields++; + + var nestedField = nestedEventField.Properties.CreateNestedField(totalFields, nestedEventField.Name); + + if (nestedEventField.IsHidden) + { + nestedField = nestedField.Hide(); + } + + if (nestedEventField.IsDisabled) + { + nestedField = nestedField.Disable(); + } + + if (nestedEventField.IsLocked) + { + nestedField = nestedField.Lock(); + } + + arrayField = arrayField.AddField(nestedField); + } + + field = arrayField; + } + + if (eventField.IsHidden) + { + field = field.Hide(); + } + + if (eventField.IsDisabled) + { + field = field.Disable(); + } + + if (eventField.IsLocked) + { + field = field.Lock(); + } + + schema = schema.AddField(field); + } + } + + return schema; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaField.cs similarity index 66% rename from src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs rename to src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaField.cs index 6dfba43fc..3b6184338 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaField.cs @@ -6,13 +6,14 @@ // ========================================================================== using System.Collections.Generic; +using P = Squidex.Domain.Apps.Core.Partitioning; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class CreateSchemaField : CreateSchemaFieldBase + public sealed class UpsertSchemaField : UpsertSchemaFieldBase { - public string Partitioning { get; set; } = "invariant"; + public string Partitioning { get; set; } = P.Invariant.Key; - public List Nested { get; set; } + public List Nested { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaFieldBase.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaFieldBase.cs similarity index 93% rename from src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaFieldBase.cs rename to src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaFieldBase.cs index 3d91afc20..4b165a145 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaFieldBase.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaFieldBase.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public abstract class CreateSchemaFieldBase + public abstract class UpsertSchemaFieldBase { public string Name { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaNestedField.cs similarity index 87% rename from src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs rename to src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaNestedField.cs index 55cd4e6eb..c35218acc 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/UpsertSchemaNestedField.cs @@ -7,7 +7,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class CreateSchemaNestedField : CreateSchemaFieldBase + public sealed class UpsertSchemaNestedField : UpsertSchemaFieldBase { } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs index 2df48f6e0..65b22cbe9 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs @@ -32,60 +32,17 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards e("A schema with the same name already exists."); } - if (command.Fields?.Count > 0) - { - var fieldIndex = 0; - var fieldPrefix = string.Empty; - - foreach (var field in command.Fields) - { - fieldIndex++; - fieldPrefix = $"Fields[{fieldIndex}]"; - - if (!field.Partitioning.IsValidPartitioning()) - { - e(Not.Valid("Partitioning"), $"{fieldPrefix}.{nameof(field.Partitioning)}"); - } - - ValidateField(e, fieldPrefix, field); - - if (field.Nested?.Count > 0) - { - if (field.Properties is ArrayFieldProperties) - { - var nestedIndex = 0; - var nestedPrefix = string.Empty; - - foreach (var nestedField in field.Nested) - { - nestedIndex++; - nestedPrefix = $"{fieldPrefix}.Nested[{nestedIndex}]"; - - if (nestedField.Properties is ArrayFieldProperties) - { - e("Nested field cannot be array fields.", $"{nestedPrefix}.{nameof(nestedField.Properties)}"); - } - - ValidateField(e, nestedPrefix, nestedField); - } - } - else if (field.Nested.Count > 0) - { - e("Only array fields can have nested fields.", $"{fieldPrefix}.{nameof(field.Partitioning)}"); - } + ValidateUpsert(command, e); + }); + } - if (field.Nested.Select(x => x.Name).Distinct().Count() != field.Nested.Count) - { - e("Fields cannot have duplicate names.", $"{fieldPrefix}.Nested"); - } - } - } + public static void CanSynchronize(SynchronizeSchema command) + { + Guard.NotNull(command, nameof(command)); - if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count) - { - e("Fields cannot have duplicate names.", nameof(command.Fields)); - } - } + Validate.It(() => "Cannot synchronize schema.", e => + { + ValidateUpsert(command, e); }); } @@ -171,7 +128,65 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards Guard.NotNull(command, nameof(command)); } - private static void ValidateField(AddValidation e, string prefix, CreateSchemaFieldBase field) + private static void ValidateUpsert(UpsertCommand command, AddValidation e) + { + if (command.Fields?.Count > 0) + { + var fieldIndex = 0; + var fieldPrefix = string.Empty; + + foreach (var field in command.Fields) + { + fieldIndex++; + fieldPrefix = $"Fields[{fieldIndex}]"; + + if (!field.Partitioning.IsValidPartitioning()) + { + e(Not.Valid("Partitioning"), $"{fieldPrefix}.{nameof(field.Partitioning)}"); + } + + ValidateField(field, fieldPrefix, e); + + if (field.Nested?.Count > 0) + { + if (field.Properties is ArrayFieldProperties) + { + var nestedIndex = 0; + var nestedPrefix = string.Empty; + + foreach (var nestedField in field.Nested) + { + nestedIndex++; + nestedPrefix = $"{fieldPrefix}.Nested[{nestedIndex}]"; + + if (nestedField.Properties is ArrayFieldProperties) + { + e("Nested field cannot be array fields.", $"{nestedPrefix}.{nameof(nestedField.Properties)}"); + } + + ValidateField(nestedField, nestedPrefix, e); + } + } + else if (field.Nested.Count > 0) + { + e("Only array fields can have nested fields.", $"{fieldPrefix}.{nameof(field.Partitioning)}"); + } + + if (field.Nested.Select(x => x.Name).Distinct().Count() != field.Nested.Count) + { + e("Fields cannot have duplicate names.", $"{fieldPrefix}.Nested"); + } + } + } + + if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count) + { + e("Fields cannot have duplicate names.", nameof(command.Fields)); + } + } + } + + private static void ValidateField(UpsertSchemaFieldBase field, string prefix, AddValidation e) { if (!field.Name.IsPropertyName()) { diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs b/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs index 871d04314..05b20f405 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/ISchemaEntity.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Collections.Generic; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; @@ -20,28 +19,8 @@ namespace Squidex.Domain.Apps.Entities.Schemas { NamedId AppId { get; } - string Name { get; } - - string Category { get; } - - bool IsSingleton { get; } - - bool IsPublished { get; } - bool IsDeleted { get; } - string ScriptQuery { get; } - - string ScriptCreate { get; } - - string ScriptUpdate { get; } - - string ScriptDelete { get; } - - string ScriptChange { get; } - Schema SchemaDef { get; } - - Dictionary PreviewUrls { get; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaExtensions.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaExtensions.cs index 3a44a25f9..9d9cb5e29 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaExtensions.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaExtensions.cs @@ -8,6 +8,7 @@ using System; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; +using StaticNamedId = Squidex.Infrastructure.NamedId; namespace Squidex.Domain.Apps.Entities.Schemas { @@ -15,7 +16,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas { public static NamedId NamedId(this ISchemaEntity schema) { - return new NamedId(schema.Id, schema.Name); + return StaticNamedId.Of(schema.Id, schema.SchemaDef.Name); } public static string EscapePartition(this string value) @@ -23,34 +24,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas return value.Replace('-', '_'); } - public static string TypeName(this IField field) - { - return field.Name.ToPascalCase(); - } - public static string TypeName(this ISchemaEntity schema) { - return schema.SchemaDef.Name.ToPascalCase(); - } - - public static string DisplayName(this IField field) - { - return field.RawProperties.Label.WithFallback(field.TypeName()); + return schema.SchemaDef.TypeName(); } public static string DisplayName(this ISchemaEntity schema) { - return schema.SchemaDef.Properties.Label.WithFallback(schema.TypeName()); - } - - public static string TypeName(this Schema schema) - { - return schema.Name.ToPascalCase(); - } - - public static string DisplayName(this Schema schema) - { - return schema.Properties.Label.WithFallback(schema.TypeName()); + return schema.SchemaDef.DisplayName(); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs index 2aff00cc3..70ec38ddc 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs @@ -6,8 +6,8 @@ // ========================================================================== using System; -using System.Collections.Generic; using System.Threading.Tasks; +using Squidex.Domain.Apps.Core.EventSynchronization; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Guards; @@ -17,6 +17,7 @@ using Squidex.Domain.Apps.Events.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Json; using Squidex.Infrastructure.Log; using Squidex.Infrastructure.Orleans; using Squidex.Infrastructure.Reflection; @@ -27,17 +28,17 @@ namespace Squidex.Domain.Apps.Entities.Schemas public sealed class SchemaGrain : SquidexDomainObjectGrain, ISchemaGrain { private readonly IAppProvider appProvider; - private readonly FieldRegistry registry; + private readonly IJsonSerializer serializer; - public SchemaGrain(IStore store, ISemanticLog log, IAppProvider appProvider, FieldRegistry registry) + public SchemaGrain(IStore store, ISemanticLog log, IAppProvider appProvider, IJsonSerializer serializer) : base(store, log) { Guard.NotNull(appProvider, nameof(appProvider)); - Guard.NotNull(registry, nameof(registry)); + Guard.NotNull(serializer, nameof(serializer)); this.appProvider = appProvider; - this.registry = registry; + this.serializer = serializer; } protected override Task ExecuteAsync(IAggregateCommand command) @@ -75,6 +76,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas Create(c); }); + case SynchronizeSchema synchronizeSchema: + return UpdateAsync(synchronizeSchema, c => + { + GuardSchema.CanSynchronize(c); + + Synchronize(c); + }); + case DeleteField deleteField: return UpdateAsync(deleteField, c => { @@ -200,45 +209,38 @@ namespace Squidex.Domain.Apps.Entities.Schemas } } - public void Create(CreateSchema command) + public void Synchronize(SynchronizeSchema command) { - var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = NamedId.Of(command.SchemaId, command.Name) }); - - if (command.Fields != null) + var options = new SchemaSynchronizationOptions { - @event.Fields = new List(); + NoFieldDeletion = command.NoFieldDeletion, + NoFieldRecreation = command.NoFieldRecreation + }; - foreach (var commandField in command.Fields) - { - var eventField = SimpleMapper.Map(commandField, new SchemaCreatedField()); + var schemaSource = Snapshot.SchemaDef; + var schemaTarget = command.ToSchema(schemaSource.Name, schemaSource.IsSingleton); - @event.Fields.Add(eventField); + var @events = schemaSource.Synchronize(schemaTarget, serializer, () => Snapshot.SchemaFieldsTotal + 1, options); - if (commandField.Nested != null) - { - eventField.Nested = new List(); - - foreach (var nestedField in commandField.Nested) - { - var eventNestedField = SimpleMapper.Map(nestedField, new SchemaCreatedNestedField()); - - eventField.Nested.Add(eventNestedField); - } - } - } + foreach (var @event in @events) + { + RaiseEvent(SimpleMapper.Map(command, (SchemaEvent)@event)); } + } - RaiseEvent(@event); + public void Create(CreateSchema command) + { + RaiseEvent(command, new SchemaCreated { SchemaId = NamedId.Of(command.SchemaId, command.Name), Schema = command.ToSchema() }); } public void Add(AddField command) { - RaiseEvent(SimpleMapper.Map(command, new FieldAdded { ParentFieldId = GetFieldId(command.ParentFieldId), FieldId = CreateFieldId(command) })); + RaiseEvent(command, new FieldAdded { FieldId = CreateFieldId(command) }); } public void UpdateField(UpdateField command) { - RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); + RaiseEvent(command, new FieldUpdated()); } public void LockField(LockField command) @@ -273,88 +275,89 @@ namespace Squidex.Domain.Apps.Entities.Schemas public void Reorder(ReorderFields command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered { ParentFieldId = GetFieldId(command.ParentFieldId) })); + RaiseEvent(command, new SchemaFieldsReordered()); } public void Publish(PublishSchema command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); + RaiseEvent(command, new SchemaPublished()); } public void Unpublish(UnpublishSchema command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); + RaiseEvent(command, new SchemaUnpublished()); } public void ConfigureScripts(ConfigureScripts command) { - RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); + RaiseEvent(command, new SchemaScriptsConfigured()); } public void ChangeCategory(ChangeCategory command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaCategoryChanged())); + RaiseEvent(command, new SchemaCategoryChanged()); } public void ConfigurePreviewUrls(ConfigurePreviewUrls command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaPreviewUrlsConfigured())); + RaiseEvent(command, new SchemaPreviewUrlsConfigured()); } - public void Delete(DeleteSchema command) + public void Update(UpdateSchema command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); + RaiseEvent(command, new SchemaUpdated()); } - public void Update(UpdateSchema command) + public void Delete(DeleteSchema command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); + RaiseEvent(command, new SchemaDeleted()); } - private void RaiseEvent(FieldCommand fieldCommand, FieldEvent @event) + private void RaiseEvent(TCommand command, TEvent @event) where TCommand : SchemaCommand where TEvent : SchemaEvent { - SimpleMapper.Map(fieldCommand, @event); + SimpleMapper.Map(command, @event); - if (fieldCommand.ParentFieldId.HasValue) + NamedId GetFieldId(long? id) { - if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.ParentFieldId.Value, out var field)) + if (id.HasValue && Snapshot.SchemaDef.FieldsById.TryGetValue(id.Value, out var field)) { - @event.ParentFieldId = NamedId.Of(field.Id, field.Name); + return NamedId.Of(field.Id, field.Name); + } - if (field is IArrayField arrayField && arrayField.FieldsById.TryGetValue(fieldCommand.FieldId, out var nestedField)) + return null; + } + + if (command is ParentFieldCommand pc && @event is ParentFieldEvent pe) + { + if (pc.ParentFieldId.HasValue) + { + if (Snapshot.SchemaDef.FieldsById.TryGetValue(pc.ParentFieldId.Value, out var field)) { - @event.FieldId = NamedId.Of(nestedField.Id, nestedField.Name); + pe.ParentFieldId = NamedId.Of(field.Id, field.Name); + + if (command is FieldCommand fc && @event is FieldEvent fe) + { + if (field is IArrayField arrayField && arrayField.FieldsById.TryGetValue(fc.FieldId, out var nestedField)) + { + fe.FieldId = NamedId.Of(nestedField.Id, nestedField.Name); + } + } } } - } - else - { - @event.FieldId = GetFieldId(fieldCommand.FieldId); + else if (command is FieldCommand fc && @event is FieldEvent fe) + { + fe.FieldId = GetFieldId(fc.FieldId); + } } RaiseEvent(@event); } - private NamedId CreateFieldId(AddField command) - { - return NamedId.Of(Snapshot.TotalFields + 1L, command.Name); - } - - private NamedId GetFieldId(long? id) - { - if (id.HasValue && Snapshot.SchemaDef.FieldsById.TryGetValue(id.Value, out var field)) - { - return NamedId.Of(field.Id, field.Name); - } - - return null; - } - private void RaiseEvent(SchemaEvent @event) { if (@event.SchemaId == null) { - @event.SchemaId = NamedId.Of(Snapshot.Id, Snapshot.Name); + @event.SchemaId = NamedId.Of(Snapshot.Id, Snapshot.SchemaDef.Name); } if (@event.AppId == null) @@ -365,6 +368,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas RaiseEvent(Envelope.Create(@event)); } + private NamedId CreateFieldId(AddField command) + { + return NamedId.Of(Snapshot.SchemaFieldsTotal + 1, command.Name); + } + private void VerifyNotDeleted() { if (Snapshot.IsDeleted) @@ -375,7 +383,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas protected override SchemaState OnEvent(Envelope @event) { - return Snapshot.Apply(@event, registry); + return Snapshot.Apply(@event); } public Task> GetStateAsync() diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaHistoryEventsCreator.cs index 45fbd966f..f640cf173 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaHistoryEventsCreator.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaHistoryEventsCreator.cs @@ -19,6 +19,15 @@ namespace Squidex.Domain.Apps.Entities.Schemas public SchemaHistoryEventsCreator(TypeNameRegistry typeNameRegistry) : base(typeNameRegistry) { + AddEventMessage("SchemaCreatedEvent", + "created schema {[Name]}."); + + AddEventMessage("ScriptsConfiguredEvent", + "configured script of schema {[Name]}."); + + AddEventMessage( + "reordered fields of schema {[Name]}."); + AddEventMessage( "created schema {[Name]}."); @@ -37,6 +46,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas AddEventMessage( "reordered fields of schema {[Name]}."); + AddEventMessage( + "configured script of schema {[Name]}."); + AddEventMessage( "added field {[Field]} to schema {[Name]}."); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs index 6e8a837f3..4cc1b47c5 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs @@ -6,7 +6,6 @@ // ========================================================================== using System; -using System.Collections.Generic; using System.Runtime.Serialization; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; @@ -15,7 +14,6 @@ using Squidex.Domain.Apps.Events.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.EventSourcing; -using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.States; namespace Squidex.Domain.Apps.Entities.Schemas.State @@ -27,128 +25,27 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State public NamedId AppId { get; set; } [DataMember] - public string Name { get; set; } - - [DataMember] - public string Category { get; set; } - - [DataMember] - public int TotalFields { get; set; } + public long SchemaFieldsTotal { get; set; } [DataMember] public bool IsDeleted { get; set; } - [DataMember] - public bool IsSingleton { get; set; } - - [DataMember] - public string ScriptQuery { get; set; } - - [DataMember] - public string ScriptCreate { get; set; } - - [DataMember] - public string ScriptUpdate { get; set; } - - [DataMember] - public string ScriptDelete { get; set; } - - [DataMember] - public string ScriptChange { get; set; } - - [DataMember] - public Dictionary PreviewUrls { get; set; } - [DataMember] public Schema SchemaDef { get; set; } - [IgnoreDataMember] - public bool IsPublished + protected void On(SchemaCreated @event) { - get { return SchemaDef.IsPublished; } - } - - protected void On(SchemaCreated @event, FieldRegistry registry) - { - Name = @event.Name; - - IsSingleton = @event.Singleton; - - var schema = new Schema(@event.Name); - - if (@event.Properties != null) - { - schema = schema.Update(@event.Properties); - } - - if (@event.Publish) - { - schema = schema.Publish(); - } - - if (@event.Fields != null) - { - foreach (var eventField in @event.Fields) - { - TotalFields++; - - var partitioning = Partitioning.FromString(eventField.Partitioning); - - var field = registry.CreateRootField(TotalFields, eventField.Name, partitioning, eventField.Properties); - - if (field is ArrayField arrayField && eventField.Nested?.Count > 0) - { - foreach (var nestedEventField in eventField.Nested) - { - TotalFields++; - - var nestedField = registry.CreateNestedField(TotalFields, nestedEventField.Name, nestedEventField.Properties); - - if (nestedEventField.IsHidden) - { - nestedField = nestedField.Hide(); - } - - if (nestedEventField.IsDisabled) - { - nestedField = nestedField.Disable(); - } - - arrayField = arrayField.AddField(nestedField); - } - - field = arrayField; - } - - if (eventField.IsHidden) - { - field = field.Hide(); - } - - if (eventField.IsDisabled) - { - field = field.Disable(); - } - - if (eventField.IsLocked) - { - field = field.Lock(); - } - - schema = schema.AddField(field); - } - } - - SchemaDef = schema; + SchemaDef = @event.Schema; + SchemaFieldsTotal = @event.Schema.MaxId(); AppId = @event.AppId; } - protected void On(FieldAdded @event, FieldRegistry registry) + protected void On(FieldAdded @event) { if (@event.ParentFieldId != null) { - var field = registry.CreateNestedField(@event.FieldId.Id, @event.Name, @event.Properties); + var field = @event.Properties.CreateNestedField(@event.FieldId.Id, @event.Name); SchemaDef = SchemaDef.UpdateField(@event.ParentFieldId.Id, x => ((ArrayField)x).AddField(field)); } @@ -156,95 +53,95 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State { var partitioning = Partitioning.FromString(@event.Partitioning); - var field = registry.CreateRootField(@event.FieldId.Id, @event.Name, partitioning, @event.Properties); + var field = @event.Properties.CreateRootField(@event.FieldId.Id, @event.Name, partitioning); SchemaDef = SchemaDef.DeleteField(@event.FieldId.Id); SchemaDef = SchemaDef.AddField(field); } - TotalFields++; + SchemaFieldsTotal++; } - protected void On(SchemaCategoryChanged @event, FieldRegistry registry) + protected void On(SchemaCategoryChanged @event) { - Category = @event.Name; + SchemaDef = SchemaDef.ChangeCategory(@event.Name); } - protected void On(SchemaPreviewUrlsConfigured @event, FieldRegistry registry) + protected void On(SchemaPreviewUrlsConfigured @event) { - PreviewUrls = @event.PreviewUrls; + SchemaDef = SchemaDef.ConfigurePreviewUrls(@event.PreviewUrls); } - protected void On(SchemaPublished @event, FieldRegistry registry) + protected void On(SchemaScriptsConfigured @event) + { + SchemaDef = SchemaDef.ConfigureScripts(@event.Scripts); + } + + protected void On(SchemaPublished @event) { SchemaDef = SchemaDef.Publish(); } - protected void On(SchemaUnpublished @event, FieldRegistry registry) + protected void On(SchemaUnpublished @event) { SchemaDef = SchemaDef.Unpublish(); } - protected void On(SchemaUpdated @event, FieldRegistry registry) + protected void On(SchemaUpdated @event) { SchemaDef = SchemaDef.Update(@event.Properties); } - protected void On(SchemaFieldsReordered @event, FieldRegistry registry) + protected void On(SchemaFieldsReordered @event) { SchemaDef = SchemaDef.ReorderFields(@event.FieldIds, @event.ParentFieldId?.Id); } - protected void On(FieldUpdated @event, FieldRegistry registry) + protected void On(FieldUpdated @event) { SchemaDef = SchemaDef.UpdateField(@event.FieldId.Id, @event.Properties, @event.ParentFieldId?.Id); } - protected void On(FieldLocked @event, FieldRegistry registry) + protected void On(FieldLocked @event) { SchemaDef = SchemaDef.LockField(@event.FieldId.Id, @event.ParentFieldId?.Id); } - protected void On(FieldDisabled @event, FieldRegistry registry) + protected void On(FieldDisabled @event) { SchemaDef = SchemaDef.DisableField(@event.FieldId.Id, @event.ParentFieldId?.Id); } - protected void On(FieldEnabled @event, FieldRegistry registry) + protected void On(FieldEnabled @event) { SchemaDef = SchemaDef.EnableField(@event.FieldId.Id, @event.ParentFieldId?.Id); } - protected void On(FieldHidden @event, FieldRegistry registry) + protected void On(FieldHidden @event) { SchemaDef = SchemaDef.HideField(@event.FieldId.Id, @event.ParentFieldId?.Id); } - protected void On(FieldShown @event, FieldRegistry registry) + protected void On(FieldShown @event) { SchemaDef = SchemaDef.ShowField(@event.FieldId.Id, @event.ParentFieldId?.Id); } - protected void On(FieldDeleted @event, FieldRegistry registry) + protected void On(FieldDeleted @event) { SchemaDef = SchemaDef.DeleteField(@event.FieldId.Id, @event.ParentFieldId?.Id); } - protected void On(SchemaDeleted @event, FieldRegistry registry) + protected void On(SchemaDeleted @event) { IsDeleted = true; } - protected void On(ScriptsConfigured @event, FieldRegistry registry) - { - SimpleMapper.Map(@event, this); - } - - public SchemaState Apply(Envelope @event, FieldRegistry registry) + public SchemaState Apply(Envelope @event) { var payload = (SquidexEvent)@event.Payload; - return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload, registry)); + return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload)); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj b/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj index 5dd06b39a..6adb6a190 100644 --- a/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj +++ b/src/Squidex.Domain.Apps.Entities/Squidex.Domain.Apps.Entities.csproj @@ -16,7 +16,7 @@ - + all runtime; build; native; contentfiles; analyzers diff --git a/src/Squidex.Domain.Apps.Events/Schemas/FieldEvent.cs b/src/Squidex.Domain.Apps.Events/Schemas/FieldEvent.cs index 0c2a9690f..41e33e860 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/FieldEvent.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/FieldEvent.cs @@ -9,10 +9,8 @@ using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Events.Schemas { - public abstract class FieldEvent : SchemaEvent + public abstract class FieldEvent : ParentFieldEvent { public NamedId FieldId { get; set; } - - public NamedId ParentFieldId { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Events/Schemas/ParentFieldEvent.cs b/src/Squidex.Domain.Apps.Events/Schemas/ParentFieldEvent.cs new file mode 100644 index 000000000..0406c3c40 --- /dev/null +++ b/src/Squidex.Domain.Apps.Events/Schemas/ParentFieldEvent.cs @@ -0,0 +1,16 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Events.Schemas +{ + public abstract class ParentFieldEvent : SchemaEvent + { + public NamedId ParentFieldId { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs index 6f4f24295..703781d78 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreated.cs @@ -1,4 +1,4 @@ -// ========================================================================== +// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschränkt) @@ -7,21 +7,12 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.EventSourcing; -using SchemaFields = System.Collections.Generic.List; namespace Squidex.Domain.Apps.Events.Schemas { - [EventType(nameof(SchemaCreated))] + [EventType(nameof(SchemaCreated), 2)] public sealed class SchemaCreated : SchemaEvent { - public string Name { get; set; } - - public SchemaFields Fields { get; set; } - - public SchemaProperties Properties { get; set; } - - public bool Singleton { get; set; } - - public bool Publish { get; set; } + public Schema Schema { get; set; } } -} +} \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs index 50ad79ed8..1c1f4f7c4 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs @@ -6,16 +6,13 @@ // ========================================================================== using System.Collections.Generic; -using Squidex.Infrastructure; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Events.Schemas { [EventType(nameof(SchemaFieldsReordered))] - public sealed class SchemaFieldsReordered : SchemaEvent + public sealed class SchemaFieldsReordered : ParentFieldEvent { - public NamedId ParentFieldId { get; set; } - public List FieldIds { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Events/Schemas/ScriptsConfigured.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaScriptsConfigured.cs similarity index 57% rename from src/Squidex.Domain.Apps.Events/Schemas/ScriptsConfigured.cs rename to src/Squidex.Domain.Apps.Events/Schemas/SchemaScriptsConfigured.cs index 3d8abe58a..f0bb9bea9 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/ScriptsConfigured.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaScriptsConfigured.cs @@ -5,21 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Events.Schemas { - [EventType(nameof(ScriptsConfigured))] - public sealed class ScriptsConfigured : SchemaEvent + [EventType(nameof(SchemaScriptsConfigured))] + public sealed class SchemaScriptsConfigured : SchemaEvent { - public string ScriptQuery { get; set; } - - public string ScriptCreate { get; set; } - - public string ScriptUpdate { get; set; } - - public string ScriptDelete { get; set; } - - public string ScriptChange { get; set; } + public SchemaScripts Scripts { get; set; } } } diff --git a/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj b/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj index 0dbf9512a..351faafbf 100644 --- a/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj +++ b/src/Squidex.Domain.Users.MongoDb/Squidex.Domain.Users.MongoDb.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj b/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj index 8d568fa96..683baf450 100644 --- a/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj +++ b/src/Squidex.Infrastructure.MongoDb/Squidex.Infrastructure.MongoDb.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj index d1a86c1c2..5a7a3c178 100644 --- a/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj +++ b/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj @@ -12,12 +12,12 @@ - + all runtime; build; native; contentfiles; analyzers - + diff --git a/src/Squidex/Areas/Api/Config/Swagger/XmlResponseTypesProcessor.cs b/src/Squidex/Areas/Api/Config/Swagger/XmlResponseTypesProcessor.cs index e2297a41d..6d969c204 100644 --- a/src/Squidex/Areas/Api/Config/Swagger/XmlResponseTypesProcessor.cs +++ b/src/Squidex/Areas/Api/Config/Swagger/XmlResponseTypesProcessor.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using NJsonSchema.Infrastructure; @@ -23,8 +24,6 @@ namespace Squidex.Areas.Api.Config.Swagger public async Task ProcessAsync(OperationProcessorContext context) { - var hasOkResponse = false; - var operation = context.OperationDescription.Operation; var returnsDescription = await context.MethodInfo.GetXmlDocumentationTagAsync("returns") ?? string.Empty; @@ -41,19 +40,11 @@ namespace Squidex.Areas.Api.Config.Swagger } response.Description = match.Groups["Description"].Value; - - if (statusCode == "200" || statusCode == "204") - { - hasOkResponse = true; - } } await AddInternalErrorResponseAsync(context, operation); - if (!hasOkResponse) - { - RemoveOkResponse(operation); - } + CleanupResponses(operation); return true; } @@ -66,11 +57,14 @@ namespace Squidex.Areas.Api.Config.Swagger } } - private static void RemoveOkResponse(SwaggerOperation operation) + private static void CleanupResponses(SwaggerOperation operation) { - if (operation.Responses.TryGetValue("200", out var response) && response.Description?.Contains("=>") == true) + foreach (var (code, response) in operation.Responses.ToList()) { - operation.Responses.Remove("200"); + if (string.IsNullOrWhiteSpace(response.Description) || response.Description?.Contains("=>") == true) + { + operation.Responses.Remove(code); + } } } } diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs b/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs index 270cb5e7f..703e77ce3 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemaSwaggerGenerator.cs @@ -230,7 +230,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator }); } - private SwaggerPathItem AddOperation(SwaggerOperationMethod method, string entityName, string path, Action updater) + private SwaggerPathItem AddOperation(string method, string entityName, string path, Action updater) { var operations = document.Paths.GetOrAddNew(path); var operation = new SwaggerOperation(); diff --git a/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs b/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs index 7c90c3ed6..0ab353afb 100644 --- a/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs +++ b/src/Squidex/Areas/Api/Controllers/Contents/Generator/SchemasSwaggerGenerator.cs @@ -77,7 +77,7 @@ namespace Squidex.Areas.Api.Controllers.Contents.Generator { var appBasePath = $"/content/{app.Name}"; - foreach (var schema in schemas.Where(x => x.IsPublished).Select(x => x.SchemaDef)) + foreach (var schema in schemas.Select(x => x.SchemaDef).Where(x => x.IsPublished)) { new SchemaSwaggerGenerator(document, app.Name, appBasePath, schema, AppendSchema, app.PartitionResolver()).GenerateSchemaOperations(); } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs new file mode 100644 index 000000000..22069636e --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigurePreviewUrlsDto.cs @@ -0,0 +1,20 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using Squidex.Domain.Apps.Entities.Schemas.Commands; + +namespace Squidex.Areas.Api.Controllers.Schemas.Models +{ + public sealed class ConfigurePreviewUrlsDto : Dictionary + { + public ConfigurePreviewUrls ToCommand() + { + return new ConfigurePreviewUrls { PreviewUrls = this }; + } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs index 922647abc..8f969a4bb 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs @@ -5,15 +5,12 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; -using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Schemas.Models { - public sealed class CreateSchemaDto + public sealed class CreateSchemaDto : UpsertDto { /// /// The name of the schema. @@ -22,66 +19,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models [RegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")] public string Name { get; set; } - /// - /// The optional properties. - /// - public SchemaPropertiesDto Properties { get; set; } - - /// - /// Optional fields. - /// - public List Fields { get; set; } - /// /// Set to true to allow a single content item only. /// - public bool Singleton { get; set; } - - /// - /// Set it to true to autopublish the schema. - /// - public bool Publish { get; set; } + public bool IsSingleton { get; set; } public CreateSchema ToCommand() { - var command = new CreateSchema(); - - SimpleMapper.Map(this, command); - - if (Properties != null) - { - command.Properties = new SchemaProperties(); - - SimpleMapper.Map(Properties, command.Properties); - } - - if (Fields != null) - { - command.Fields = new List(); - - foreach (var fieldDto in Fields) - { - var rootProperties = fieldDto?.Properties.ToProperties(); - var rootField = SimpleMapper.Map(fieldDto, new CreateSchemaField { Properties = rootProperties }); - - if (fieldDto.Nested != null) - { - rootField.Nested = new List(); - - foreach (var nestedFieldDto in fieldDto.Nested) - { - var nestedProperties = nestedFieldDto?.Properties.ToProperties(); - var nestedField = SimpleMapper.Map(nestedFieldDto, new CreateSchemaNestedField { Properties = nestedProperties }); - - rootField.Nested.Add(nestedField); - } - } - - command.Fields.Add(rootField); - } - } - - return command; + return ToCommand(this, new CreateSchema()); } } } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs index 97c9647c8..a6ed2b46b 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs @@ -19,6 +19,8 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models { public sealed class SchemaDetailsDto { + private static readonly Dictionary EmptyPreviewUrls = new Dictionary(); + /// /// The id of the schema. /// @@ -47,34 +49,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models public bool IsPublished { get; set; } /// - /// The script that is executed for each query when querying contents. - /// - public string ScriptQuery { get; set; } - - /// - /// The script that is executed when creating a content. - /// - public string ScriptCreate { get; set; } - - /// - /// The script that is executed when updating a content. - /// - public string ScriptUpdate { get; set; } - - /// - /// The script that is executed when deleting a content. - /// - public string ScriptDelete { get; set; } - - /// - /// The script that is executed when changing a content status. + /// The scripts. /// - public string ScriptChange { get; set; } + public SchemaScriptsDto Scripts { get; set; } = new SchemaScriptsDto(); /// /// The preview Urls. /// - public Dictionary PreviewUrls { get; set; } + public Dictionary PreviewUrls { get; set; } = EmptyPreviewUrls; /// /// The list of fields. @@ -86,7 +68,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// The schema properties. /// [Required] - public SchemaPropertiesDto Properties { get; set; } + public SchemaPropertiesDto Properties { get; set; } = new SchemaPropertiesDto(); /// /// The user that has created the schema. @@ -117,12 +99,18 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models public static SchemaDetailsDto FromSchema(ISchemaEntity schema) { - var response = new SchemaDetailsDto { Properties = new SchemaPropertiesDto() }; + var response = new SchemaDetailsDto(); SimpleMapper.Map(schema, response); SimpleMapper.Map(schema.SchemaDef, response); + SimpleMapper.Map(schema.SchemaDef.Scripts, response.Scripts); SimpleMapper.Map(schema.SchemaDef.Properties, response.Properties); + if (schema.SchemaDef.PreviewUrls.Count > 0) + { + response.PreviewUrls = new Dictionary(schema.SchemaDef.PreviewUrls); + } + response.Fields = new List(); foreach (var field in schema.SchemaDef.Fields) diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureScriptsDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaScriptsDto.cs similarity index 73% rename from src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureScriptsDto.cs rename to src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaScriptsDto.cs index 51c8c3de1..3e2fd8a2d 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/ConfigureScriptsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaScriptsDto.cs @@ -5,41 +5,44 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Infrastructure.Reflection; namespace Squidex.Areas.Api.Controllers.Schemas.Models { - public sealed class ConfigureScriptsDto + public sealed class SchemaScriptsDto { /// /// The script that is executed for each query when querying contents. /// - public string ScriptQuery { get; set; } + public string Query { get; set; } /// /// The script that is executed when creating a content. /// - public string ScriptCreate { get; set; } + public string Create { get; set; } /// /// The script that is executed when updating a content. /// - public string ScriptUpdate { get; set; } + public string Update { get; set; } /// /// The script that is executed when deleting a content. /// - public string ScriptDelete { get; set; } + public string Delete { get; set; } /// /// The script that is executed when change a content status. /// - public string ScriptChange { get; set; } + public string Change { get; set; } public ConfigureScripts ToCommand() { - return SimpleMapper.Map(this, new ConfigureScripts()); + var scripts = SimpleMapper.Map(this, new SchemaScripts()); + + return new ConfigureScripts { Scripts = scripts }; } } -} +} \ No newline at end of file diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/SynchronizeSchemaDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SynchronizeSchemaDto.cs new file mode 100644 index 000000000..e2595140a --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SynchronizeSchemaDto.cs @@ -0,0 +1,29 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Entities.Schemas.Commands; + +namespace Squidex.Areas.Api.Controllers.Schemas.Models +{ + public sealed class SynchronizeSchemaDto : UpsertDto + { + /// + /// True, when fields should not be deleted. + /// + public bool NoFieldDeletion { get; set; } + + /// + /// True, when fields with different types should not be recreated. + /// + public bool NoFieldRecreation { get; set; } + + public SynchronizeSchema ToCommand() + { + return ToCommand(this, new SynchronizeSchema()); + } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertDto.cs new file mode 100644 index 000000000..7c3032b2e --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertDto.cs @@ -0,0 +1,98 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Collections.Generic; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Entities.Schemas.Commands; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Areas.Api.Controllers.Schemas.Models +{ + public abstract class UpsertDto + { + /// + /// The optional properties. + /// + public SchemaPropertiesDto Properties { get; set; } + + /// + /// The optional scripts. + /// + public SchemaScriptsDto Scripts { get; set; } + + /// + /// Optional fields. + /// + public List Fields { get; set; } + + /// + /// The optional preview urls. + /// + public Dictionary PreviewUrls { get; set; } + + /// + /// The category. + /// + public string Category { get; set; } + + /// + /// Set it to true to autopublish the schema. + /// + public bool IsPublished { get; set; } + + public static TCommand ToCommand(TDto dto, TCommand command) where TCommand : UpsertCommand where TDto : UpsertDto + { + SimpleMapper.Map(dto, command); + + if (dto.Properties != null) + { + command.Properties = new SchemaProperties(); + + SimpleMapper.Map(dto.Properties, command.Properties); + } + + if (dto.Scripts != null) + { + command.Scripts = new SchemaScripts(); + + SimpleMapper.Map(dto.Scripts, command.Scripts); + } + + if (dto.Fields != null) + { + command.Fields = new List(); + + foreach (var rootFieldDto in dto.Fields) + { + var rootProps = rootFieldDto?.Properties.ToProperties(); + var rootField = new UpsertSchemaField { Properties = rootProps }; + + SimpleMapper.Map(rootFieldDto, rootField); + + if (rootFieldDto.Nested?.Count > 0) + { + rootField.Nested = new List(); + + foreach (var nestedFieldDto in rootFieldDto.Nested) + { + var nestedProps = nestedFieldDto?.Properties.ToProperties(); + var nestedField = new UpsertSchemaNestedField { Properties = nestedProps }; + + SimpleMapper.Map(nestedFieldDto, nestedField); + + rootField.Nested.Add(nestedField); + } + } + + command.Fields.Add(rootField); + } + } + + return command; + } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaFieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaFieldDto.cs similarity index 93% rename from src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaFieldDto.cs rename to src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaFieldDto.cs index 75a09a657..a42fee220 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaFieldDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaFieldDto.cs @@ -10,7 +10,7 @@ using System.ComponentModel.DataAnnotations; namespace Squidex.Areas.Api.Controllers.Schemas.Models { - public sealed class CreateSchemaFieldDto + public sealed class UpsertSchemaFieldDto { /// /// The name of the field. Must be unique within the schema. @@ -48,6 +48,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// /// The nested fields. /// - public List Nested { get; set; } + public List Nested { get; set; } } } \ No newline at end of file diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaNestedFieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaNestedFieldDto.cs similarity index 96% rename from src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaNestedFieldDto.cs rename to src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaNestedFieldDto.cs index 6f4855264..823bddd69 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaNestedFieldDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpsertSchemaNestedFieldDto.cs @@ -9,7 +9,7 @@ using System.ComponentModel.DataAnnotations; namespace Squidex.Areas.Api.Controllers.Schemas.Models { - public sealed class CreateSchemaNestedFieldDto + public sealed class UpsertSchemaNestedFieldDto { /// /// The name of the field. Must be unique within the schema. diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs b/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs index 00850058e..048607c97 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs @@ -146,7 +146,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")] - [ProducesResponseType(typeof(ErrorDto), 409)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] @@ -172,7 +171,6 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] - [ProducesResponseType(typeof(ErrorDto), 409)] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs b/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs index d3f034d96..1da3dd5f3 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/SchemasController.cs @@ -132,7 +132,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The schema object that needs to updated. /// - /// 204 => Schema has been updated. + /// 204 => Schema updated. /// 400 => Schema properties are not valid. /// 404 => Schema or app not found. /// @@ -147,6 +147,28 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + /// + /// Synchronize a schema. + /// + /// The name of the app. + /// The name of the schema. + /// The schema object that needs to updated. + /// + /// 204 => Schema updated. + /// 400 => Schema properties are not valid. + /// 404 => Schema or app not found. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/sync")] + [ApiPermission(Permissions.AppSchemasUpdate)] + [ApiCosts(1)] + public async Task PutSchemaSync(string app, string name, [FromBody] SynchronizeSchemaDto request) + { + await CommandBus.PublishAsync(request.ToCommand()); + + return NoContent(); + } + /// /// Update a schema category. /// @@ -154,7 +176,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The schema object that needs to updated. /// - /// 204 => Schema has been updated. + /// 204 => Schema updated. /// 404 => Schema or app not found. /// [HttpPut] @@ -175,16 +197,16 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The preview urls for the schema. /// - /// 204 => Schema has been updated. + /// 204 => Schema updated. /// 404 => Schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/preview-urls")] [ApiPermission(Permissions.AppSchemasUpdate)] [ApiCosts(1)] - public async Task PutPreviewUrls(string app, string name, [FromBody] PreviewUrlsDto request) + public async Task PutPreviewUrls(string app, string name, [FromBody] ConfigurePreviewUrlsDto request) { - await CommandBus.PublishAsync(new ConfigurePreviewUrls { PreviewUrls = request ?? new PreviewUrlsDto() }); + await CommandBus.PublishAsync(request.ToCommand()); return NoContent(); } @@ -196,7 +218,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// The name of the schema. /// The schema scripts object that needs to updated. /// - /// 204 => Schema has been updated. + /// 204 => Schema updated. /// 400 => Schema properties are not valid. /// 404 => Schema or app not found. /// @@ -204,7 +226,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas [Route("apps/{app}/schemas/{name}/scripts/")] [ApiPermission(Permissions.AppSchemasScripts)] [ApiCosts(1)] - public async Task PutSchemaScripts(string app, string name, [FromBody] ConfigureScriptsDto request) + public async Task PutSchemaScripts(string app, string name, [FromBody] SchemaScriptsDto request) { await CommandBus.PublishAsync(request.ToCommand()); diff --git a/src/Squidex/Config/Domain/EntitiesServices.cs b/src/Squidex/Config/Domain/EntitiesServices.cs index 4401d09b8..98641c63f 100644 --- a/src/Squidex/Config/Domain/EntitiesServices.cs +++ b/src/Squidex/Config/Domain/EntitiesServices.cs @@ -251,6 +251,9 @@ namespace Squidex.Config.Domain services.AddTransientAs() .As(); + services.AddTransientAs() + .As(); + services.AddTransientAs() .As(); diff --git a/src/Squidex/Config/Domain/SerializationServices.cs b/src/Squidex/Config/Domain/SerializationServices.cs index 87916c8ac..9ec50ed28 100644 --- a/src/Squidex/Config/Domain/SerializationServices.cs +++ b/src/Squidex/Config/Domain/SerializationServices.cs @@ -30,7 +30,6 @@ namespace Squidex.Config.Domain .MapUnmapped(SquidexEvents.Assembly) .MapUnmapped(SquidexInfrastructure.Assembly) .MapUnmapped(SquidexMigrations.Assembly); - private static readonly FieldRegistry FieldRegistry = new FieldRegistry(TypeNameRegistry); public static readonly JsonSerializerSettings DefaultJsonSettings = new JsonSerializerSettings(); public static readonly JsonSerializer DefaultJsonSerializer; @@ -70,6 +69,8 @@ namespace Squidex.Config.Domain static SerializationServices() { + FieldRegistry.Setup(TypeNameRegistry); + ConfigureJson(DefaultJsonSettings, TypeNameHandling.Auto); DefaultJsonSerializer = JsonSerializer.Create(DefaultJsonSettings); @@ -77,7 +78,6 @@ namespace Squidex.Config.Domain public static IServiceCollection AddMySerializers(this IServiceCollection services) { - services.AddSingleton(FieldRegistry); services.AddSingleton(DefaultJsonSettings); services.AddSingleton(DefaultJsonSerializer); services.AddSingleton(TypeNameRegistry); diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs index c2851e609..f2d5ae42b 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs @@ -97,7 +97,7 @@ namespace Squidex.Pipeline.CommandMiddlewares throw new DomainObjectNotFoundException(schemaName, typeof(ISchemaEntity)); } - return NamedId.Of(schema.Id, schema.Name); + return schema.NamedId(); } } diff --git a/src/Squidex/Pipeline/UrlGenerator.cs b/src/Squidex/Pipeline/UrlGenerator.cs index f30395986..d2c1266e7 100644 --- a/src/Squidex/Pipeline/UrlGenerator.cs +++ b/src/Squidex/Pipeline/UrlGenerator.cs @@ -57,7 +57,7 @@ namespace Squidex.Pipeline public string GenerateContentUrl(IAppEntity app, ISchemaEntity schema, IContentEntity content) { - return urlsOptions.BuildUrl($"api/content/{app.Name}/{schema.Name}/{content.Id}"); + return urlsOptions.BuildUrl($"api/content/{app.Name}/{schema.SchemaDef.Name}/{content.Id}"); } public string GenerateContentUIUrl(NamedId appId, NamedId schemaId, Guid contentId) diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index 49cb6bcd3..cebe95677 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -62,7 +62,7 @@ - + @@ -85,15 +85,15 @@ - - - - + + + + - + diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.html b/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.html index 090907620..74f2705aa 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.html +++ b/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.html @@ -7,7 +7,7 @@ diff --git a/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.ts b/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.ts index a48809b1e..ba545655a 100644 --- a/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.ts +++ b/src/Squidex/app/features/schemas/pages/schema/schema-scripts-form.component.ts @@ -26,7 +26,7 @@ export class SchemaScriptsFormComponent implements OnInit { @Input() public schema: SchemaDetailsDto; - public selectedField = 'scriptQuery'; + public selectedField = 'query'; public editForm = new EditScriptsForm(this.formBuilder); @@ -37,7 +37,7 @@ export class SchemaScriptsFormComponent implements OnInit { } public ngOnInit() { - this.editForm.load(this.schema); + this.editForm.load(this.schema.scripts); } public complete() { diff --git a/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html b/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html index ac0fd5016..509579d6e 100644 --- a/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html +++ b/src/Squidex/app/features/schemas/pages/schemas/schema-form.component.html @@ -34,11 +34,11 @@