diff --git a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs index c62931490..38517df83 100644 --- a/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs +++ b/src/Squidex.Domain.Apps.Core.Operations/ValidateContent/ContentValidator.cs @@ -5,11 +5,9 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs index 370ad85de..975b1cd36 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs @@ -200,7 +200,7 @@ namespace Squidex.Domain.Apps.Entities.Apps public void Create(CreateApp command) { - var appId = new NamedId(command.AppId, command.Name); + var appId = NamedId.Of(command.AppId, command.Name); var events = new List { @@ -308,7 +308,7 @@ namespace Squidex.Domain.Apps.Entities.Apps { if (@event.AppId == null) { - @event.AppId = new NamedId(Snapshot.Id, Snapshot.Name); + @event.AppId = NamedId.Of(Snapshot.Id, Snapshot.Name); } RaiseEvent(Envelope.Create(@event)); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs index 536a4cd42..f2d0bf83d 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateBlogCommandMiddleware.cs @@ -32,7 +32,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates { if (context.IsCompleted && context.Command is CreateApp createApp && IsRightTemplate(createApp)) { - var appId = new NamedId(createApp.AppId, createApp.Name); + var appId = NamedId.Of(createApp.AppId, createApp.Name); var publish = new Func(command => { @@ -156,7 +156,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - var schemaId = new NamedId(command.SchemaId, command.Name); + var schemaId = NamedId.Of(command.SchemaId, command.Name); await publish(new ConfigureScripts { @@ -222,7 +222,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - var schemaId = new NamedId(command.SchemaId, command.Name); + var schemaId = NamedId.Of(command.SchemaId, command.Name); await publish(new ConfigureScripts { diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs index 936b0a055..15f710fd2 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Templates/CreateProfileCommandMiddleware.cs @@ -27,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates { if (context.IsCompleted && context.Command is CreateApp createApp && IsRightTemplate(createApp)) { - var appId = new NamedId(createApp.AppId, createApp.Name); + var appId = NamedId.Of(createApp.AppId, createApp.Name); var publish = new Func(command => { @@ -233,7 +233,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - return new NamedId(command.SchemaId, command.Name); + return NamedId.Of(command.SchemaId, command.Name); } private async Task> CreateProjectsSchemaAsync(Func publish) @@ -324,7 +324,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - return new NamedId(command.SchemaId, command.Name); + return NamedId.Of(command.SchemaId, command.Name); } private async Task> CreateExperienceSchemaAsync(Func publish) @@ -404,7 +404,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - return new NamedId(command.SchemaId, command.Name); + return NamedId.Of(command.SchemaId, command.Name); } private async Task> CreateEducationSchemaAsync(Func publish) @@ -484,7 +484,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - return new NamedId(command.SchemaId, command.Name); + return NamedId.Of(command.SchemaId, command.Name); } private async Task> CreatePublicationsSchemaAsync(Func publish) @@ -552,7 +552,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - return new NamedId(command.SchemaId, command.Name); + return NamedId.Of(command.SchemaId, command.Name); } private async Task> CreateSkillsSchemaAsync(Func publish) @@ -597,7 +597,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Templates await publish(command); - return new NamedId(command.SchemaId, command.Name); + return NamedId.Of(command.SchemaId, command.Name); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs index 084f5efce..dd5dd16e1 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs @@ -6,7 +6,7 @@ // ========================================================================== using Squidex.Domain.Apps.Core.Schemas; -using FieldNested = System.Collections.Generic.List; +using FieldNested = System.Collections.Generic.List; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateNestedSchemaField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs similarity index 93% rename from src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateNestedSchemaField.cs rename to src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs index a1a10edc3..409d79f99 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateNestedSchemaField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs @@ -9,7 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class CreateNestedSchemaField + public sealed class CreateSchemaNestedField { public string Name { get; set; } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs index 68e3dd80c..8beb2b8f1 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/FieldPropertiesValidator.cs @@ -6,7 +6,6 @@ // ========================================================================== using System.Collections.Generic; -using System.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs index 41a4463e3..2348c1f45 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs @@ -5,6 +5,8 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Core; @@ -20,7 +22,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - return Validate.It(() => "Cannot create schema.", (System.Func, Task>)(async (System.Action error) => + return Validate.It(() => "Cannot create schema.", async error => { if (!command.Name.IsSlug()) { @@ -115,16 +117,27 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards error(new ValidationError("Fields cannot have duplicate names.", nameof(command.Fields))); } } - })); + }); } public static void CanReorder(Schema schema, ReorderFields command) { Guard.NotNull(command, nameof(command)); - if (command.ParentFieldId.HasValue && !schema.FieldsById.ContainsKey(command.ParentFieldId.Value)) + IArrayField arrayField = null; + + if (command.ParentFieldId.HasValue) { - throw new DomainObjectNotFoundException(command.ParentFieldId.ToString(), "Fields", typeof(Schema)); + var parentId = command.ParentFieldId.Value; + + if (schema.FieldsById.TryGetValue(parentId, out var field) && field is IArrayField a) + { + arrayField = a; + } + else + { + throw new DomainObjectNotFoundException(parentId.ToString(), "Fields", typeof(Schema)); + } } Validate.It(() => "Cannot reorder schema fields.", error => @@ -134,13 +147,25 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards error(new ValidationError("Field ids is required.", nameof(command.FieldIds))); } - if (command.FieldIds != null && (command.FieldIds.Count != schema.Fields.Count || command.FieldIds.Any(x => !schema.FieldsById.ContainsKey(x)))) + if (arrayField == null) { - error(new ValidationError("Ids must cover all fields.", nameof(command.FieldIds))); + CheckFields(error, command, schema.FieldsById); + } + else + { + CheckFields(error, command, arrayField.FieldsById); } }); } + private static void CheckFields(Action error, ReorderFields c, IReadOnlyDictionary fields) + { + if (c.FieldIds != null && (c.FieldIds.Count != fields.Count || c.FieldIds.Any(x => !fields.ContainsKey(x)))) + { + error(new ValidationError("Ids must cover all fields.", nameof(c.FieldIds))); + } + } + public static void CanPublish(Schema schema, PublishSchema command) { Guard.NotNull(command, nameof(command)); diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs index 17a766f9a..36ba731fe 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System.Linq; using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs index 62a30e545..bb30542e7 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaGrain.cs @@ -47,14 +47,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas switch (command) { - case CreateSchema createSchema: - return CreateAsync(createSchema, async c => - { - await GuardSchema.CanCreate(c, appProvider); - - Create(c); - }); - case AddField addField: return UpdateReturnAsync(addField, c => { @@ -62,7 +54,26 @@ namespace Squidex.Domain.Apps.Entities.Schemas Add(c); - return EntityCreatedResult.Create(Snapshot.SchemaDef.FieldsById.Values.First(x => x.Name == addField.Name).Id, NewVersion); + var id = 0L; + + if (c.ParentFieldId == null) + { + id = Snapshot.SchemaDef.FieldsByName[c.Name].Id; + } + else + { + id = ((IArrayField)Snapshot.SchemaDef.FieldsById[c.ParentFieldId.Value]).FieldsByName[c.Name].Id; + } + + return EntityCreatedResult.Create(id, NewVersion); + }); + + case CreateSchema createSchema: + return CreateAsync(createSchema, async c => + { + await GuardSchema.CanCreate(c, appProvider); + + Create(c); }); case DeleteField deleteField: @@ -184,7 +195,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas public void Create(CreateSchema command) { - var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId(command.SchemaId, command.Name) }); + var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = NamedId.Of(command.SchemaId, command.Name) }); if (command.Fields != null) { @@ -195,6 +206,18 @@ namespace Squidex.Domain.Apps.Entities.Schemas var eventField = SimpleMapper.Map(commandField, new SchemaCreatedField()); @event.Fields.Add(eventField); + + 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); + } + } } } @@ -203,7 +226,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas public void Add(AddField command) { - RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId(Snapshot.TotalFields + 1, command.Name) })); + RaiseEvent(SimpleMapper.Map(command, new FieldAdded { ParentFieldId = GetFieldId(command.ParentFieldId), FieldId = CreateFieldId(command) })); } public void UpdateField(UpdateField command) @@ -243,7 +266,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas public void Reorder(ReorderFields command) { - RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); + RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered { ParentFieldId = GetFieldId(command.ParentFieldId) })); } public void Publish(PublishSchema command) @@ -280,19 +303,46 @@ namespace Squidex.Domain.Apps.Entities.Schemas { SimpleMapper.Map(fieldCommand, @event); - if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.FieldId, out var field)) + if (fieldCommand.ParentFieldId.HasValue) + { + if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.ParentFieldId.Value, out var field)) + { + @event.ParentFieldId = NamedId.Of(field.Id, field.Name); + + if (field is IArrayField arrayField && arrayField.FieldsById.TryGetValue(fieldCommand.FieldId, out var nestedField)) + { + @event.FieldId = NamedId.Of(nestedField.Id, nestedField.Name); + } + } + } + else { - @event.FieldId = new NamedId(field.Id, field.Name); + @event.FieldId = GetFieldId(fieldCommand.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 = new NamedId(Snapshot.Id, Snapshot.Name); + @event.SchemaId = NamedId.Of(Snapshot.Id, Snapshot.Name); } if (@event.AppId == null) diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs index c4f554bfb..ca1c3afee 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs @@ -178,7 +178,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State protected void On(SchemaFieldsReordered @event, FieldRegistry registry) { - SchemaDef = SchemaDef.ReorderFields(@event.FieldIds, @event.ParentFieldIdId?.Id); + SchemaDef = SchemaDef.ReorderFields(@event.FieldIds, @event.ParentFieldId?.Id); } protected void On(FieldUpdated @event, FieldRegistry registry) diff --git a/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs index 154f1db1e..50ad79ed8 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaFieldsReordered.cs @@ -14,7 +14,7 @@ namespace Squidex.Domain.Apps.Events.Schemas [EventType(nameof(SchemaFieldsReordered))] public sealed class SchemaFieldsReordered : SchemaEvent { - public NamedId ParentFieldIdId { get; set; } + public NamedId ParentFieldId { get; set; } public List FieldIds { get; set; } } diff --git a/src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs b/src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs index 32e113711..3076ef02c 100644 --- a/src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs +++ b/src/Squidex.Infrastructure/Json/NamedStringIdConverter.cs @@ -32,7 +32,7 @@ namespace Squidex.Infrastructure.Json throw new JsonException("Named id must have more than 2 parts divided by colon."); } - return new NamedId(parts[0], string.Join(",", parts.Skip(1))); + return NamedId.Of(parts[0], string.Join(",", parts.Skip(1))); } } } diff --git a/src/Squidex.Infrastructure/NamedId.cs b/src/Squidex.Infrastructure/NamedId.cs index e8f99f6d4..e0c8106be 100644 --- a/src/Squidex.Infrastructure/NamedId.cs +++ b/src/Squidex.Infrastructure/NamedId.cs @@ -1,70 +1,17 @@ // ========================================================================== // Squidex Headless CMS // ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschränkt) +// Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; -using System.Linq; - namespace Squidex.Infrastructure { - public delegate bool Parser(string input, out T result); - - public sealed class NamedId : IEquatable> + public static class NamedId { - public T Id { get; } - - public string Name { get; } - - public NamedId(T id, string name) + public static NamedId Of(T id, string name) { - Guard.NotNull(id, nameof(id)); - Guard.NotNull(name, nameof(name)); - - Id = id; - - Name = name; - } - - public override string ToString() - { - return $"{Id},{Name}"; - } - - public override bool Equals(object obj) - { - return Equals(obj as NamedId); - } - - public bool Equals(NamedId other) - { - return other != null && (ReferenceEquals(this, other) || (Id.Equals(other.Id) && Name.Equals(other.Name))); - } - - public override int GetHashCode() - { - return (Id.GetHashCode() * 397) ^ Name.GetHashCode(); - } - - public static NamedId Parse(string value, Parser parser) - { - Guard.NotNull(value, nameof(value)); - - var parts = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); - - if (parts.Length < 2) - { - throw new ArgumentException("Named id must have more than 2 parts divided by commata."); - } - - if (!parser(parts[0], out var id)) - { - throw new ArgumentException("Named id must be a valid guid."); - } - - return new NamedId(id, string.Join(",", parts.Skip(1))); + return new NamedId(id, name); } } } diff --git a/src/Squidex.Infrastructure/NamedId{T}.cs b/src/Squidex.Infrastructure/NamedId{T}.cs new file mode 100644 index 000000000..e8f99f6d4 --- /dev/null +++ b/src/Squidex.Infrastructure/NamedId{T}.cs @@ -0,0 +1,70 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Linq; + +namespace Squidex.Infrastructure +{ + public delegate bool Parser(string input, out T result); + + public sealed class NamedId : IEquatable> + { + public T Id { get; } + + public string Name { get; } + + public NamedId(T id, string name) + { + Guard.NotNull(id, nameof(id)); + Guard.NotNull(name, nameof(name)); + + Id = id; + + Name = name; + } + + public override string ToString() + { + return $"{Id},{Name}"; + } + + public override bool Equals(object obj) + { + return Equals(obj as NamedId); + } + + public bool Equals(NamedId other) + { + return other != null && (ReferenceEquals(this, other) || (Id.Equals(other.Id) && Name.Equals(other.Name))); + } + + public override int GetHashCode() + { + return (Id.GetHashCode() * 397) ^ Name.GetHashCode(); + } + + public static NamedId Parse(string value, Parser parser) + { + Guard.NotNull(value, nameof(value)); + + var parts = value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + if (parts.Length < 2) + { + throw new ArgumentException("Named id must have more than 2 parts divided by commata."); + } + + if (!parser(parts[0], out var id)) + { + throw new ArgumentException("Named id must be a valid guid."); + } + + return new NamedId(id, string.Join(",", parts.Skip(1))); + } + } +} diff --git a/src/Squidex.Infrastructure/ValidationError.cs b/src/Squidex.Infrastructure/ValidationError.cs index e0a9505ab..47b4f8fe1 100644 --- a/src/Squidex.Infrastructure/ValidationError.cs +++ b/src/Squidex.Infrastructure/ValidationError.cs @@ -41,8 +41,10 @@ namespace Squidex.Infrastructure { return new ValidationError(Message, propertyNames.Select(x => $"{prefix}.{x}").ToArray()); } - - return this; + else + { + return new ValidationError(Message, prefix); + } } } } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs index 8ce6cd996..b2330cfd8 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/AddFieldDto.cs @@ -31,9 +31,9 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models [Required] public FieldPropertiesDto Properties { get; set; } - public AddField ToCommand() + public AddField ToCommand(long? parentId = null) { - return SimpleMapper.Map(this, new AddField { Properties = Properties.ToProperties() }); + return SimpleMapper.Map(this, new AddField { ParentFieldId = parentId, Properties = Properties.ToProperties() }); } } } \ No newline at end of file diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs index a2953af7e..9750fc7c4 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Converters/FieldPropertiesDtoFactory.cs @@ -20,14 +20,14 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models.Converters { } - public FieldPropertiesDto Visit(ArrayFieldProperties properties) + public static FieldPropertiesDto Create(FieldProperties properties) { - throw new System.NotImplementedException(); + return properties.Accept(Instance); } - public static FieldPropertiesDto Create(FieldProperties properties) + public FieldPropertiesDto Visit(ArrayFieldProperties properties) { - return properties.Accept(Instance); + return SimpleMapper.Map(properties, new ArrayFieldPropertiesDto()); } public FieldPropertiesDto Visit(BooleanFieldProperties properties) diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs index 9399ba0d0..540dcfdc2 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaDto.cs @@ -57,9 +57,22 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models foreach (var fieldDto in Fields) { var fieldProperties = fieldDto?.Properties.ToProperties(); - var fieldInstance = SimpleMapper.Map(fieldDto, new CreateSchemaField { Properties = fieldProperties }); + var field = SimpleMapper.Map(fieldDto, new CreateSchemaField { Properties = fieldProperties }); - command.Fields.Add(fieldInstance); + if (fieldDto.Nested != null) + { + field.Nested = new List(); + + foreach (var nestedFieldDto in fieldDto.Nested) + { + var nestedFieldProperties = nestedFieldDto?.Properties.ToProperties(); + var nestedField = SimpleMapper.Map(fieldDto, new CreateSchemaNestedField { Properties = fieldProperties }); + + field.Nested.Add(nestedField); + } + } + + command.Fields.Add(field); } } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaFieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaFieldDto.cs index a96975669..75a09a657 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaFieldDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaFieldDto.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace Squidex.Areas.Api.Controllers.Schemas.Models @@ -43,5 +44,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// [Required] public FieldPropertiesDto Properties { get; set; } + + /// + /// The nested fields. + /// + 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/CreateSchemaNestedFieldDto.cs new file mode 100644 index 000000000..f25e2cadb --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/CreateSchemaNestedFieldDto.cs @@ -0,0 +1,37 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; + +namespace Squidex.Areas.Api.Controllers.Schemas.Models +{ + public sealed class CreateSchemaNestedFieldDto + { + /// + /// The name of the field. Must be unique within the schema. + /// + [Required] + [RegularExpression("^[a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*$")] + public string Name { get; set; } + + /// + /// Defines if the field is hidden. + /// + public bool IsHidden { get; set; } + + /// + /// Defines if the field is disabled. + /// + public bool IsDisabled { get; set; } + + /// + /// The field properties. + /// + [Required] + public FieldPropertiesDto Properties { get; set; } + } +} \ No newline at end of file diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs index 5296b1ece..d9eab980f 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/FieldDto.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace Squidex.Areas.Api.Controllers.Schemas.Models @@ -49,5 +50,10 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models /// [Required] public FieldPropertiesDto Properties { get; set; } + + /// + /// The nested fields. + /// + public List Nested { get; set; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ArrayFieldPropertiesDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ArrayFieldPropertiesDto.cs new file mode 100644 index 000000000..b818b6815 --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/Fields/ArrayFieldPropertiesDto.cs @@ -0,0 +1,34 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using NJsonSchema.Annotations; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure.Reflection; + +namespace Squidex.Areas.Api.Controllers.Schemas.Models.Fields +{ + [JsonSchema("Array")] + public sealed class ArrayFieldPropertiesDto : FieldPropertiesDto + { + /// + /// The minimum allowed items for the field value. + /// + public int? MinItems { get; set; } + + /// + /// The maximum allowed items for the field value. + /// + public int? MaxItems { get; set; } + + public override FieldProperties ToProperties() + { + var result = SimpleMapper.Map(this, new ArrayFieldProperties()); + + return result; + } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/NestedFieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/NestedFieldDto.cs new file mode 100644 index 000000000..c9285e413 --- /dev/null +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/NestedFieldDto.cs @@ -0,0 +1,42 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschränkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.ComponentModel.DataAnnotations; + +namespace Squidex.Areas.Api.Controllers.Schemas.Models +{ + public sealed class NestedFieldDto + { + /// + /// The id of the field. + /// + public long FieldId { get; set; } + + /// + /// The name of the field. Must be unique within the schema. + /// + [Required] + [RegularExpression("^[a-z0-9]+(\\-[a-z0-9]+)*$")] + public string Name { get; set; } + + /// + /// Defines if the field is hidden. + /// + public bool IsHidden { get; set; } + + /// + /// Defines if the field is disabled. + /// + public bool IsDisabled { get; set; } + + /// + /// The field properties. + /// + [Required] + public FieldPropertiesDto Properties { get; set; } + } +} diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/ReorderFieldsDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/ReorderFieldsDto.cs index c7a7812ac..ab28c7f99 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/ReorderFieldsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/ReorderFieldsDto.cs @@ -19,9 +19,9 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models [Required] public List FieldIds { get; set; } - public ReorderFields ToCommand() + public ReorderFields ToCommand(long? parentId = null) { - return new ReorderFields { FieldIds = FieldIds }; + return new ReorderFields { ParentFieldId = parentId, FieldIds = FieldIds }; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs index c61a19654..850c6f938 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/SchemaDetailsDto.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using NodaTime; using Squidex.Areas.Api.Controllers.Schemas.Models.Converters; +using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Reflection; @@ -117,7 +118,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models foreach (var field in schema.SchemaDef.Fields) { var fieldPropertiesDto = FieldPropertiesDtoFactory.Create(field.RawProperties); - var fieldInstanceDto = SimpleMapper.Map(field, + var fieldDto = SimpleMapper.Map(field, new FieldDto { FieldId = field.Id, @@ -125,7 +126,25 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models Partitioning = field.Partitioning.Key }); - response.Fields.Add(fieldInstanceDto); + if (field is IArrayField arrayField) + { + fieldDto.Nested = new List(); + + foreach (var nestedField in arrayField.Fields) + { + var nestedFieldPropertiesDto = FieldPropertiesDtoFactory.Create(nestedField.RawProperties); + var nestedFieldDto = SimpleMapper.Map(nestedField, + new NestedFieldDto + { + FieldId = nestedField.Id, + Properties = nestedFieldPropertiesDto, + }); + + fieldDto.Nested.Add(nestedFieldDto); + } + } + + response.Fields.Add(fieldDto); } return response; diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateFieldDto.cs b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateFieldDto.cs index 36cff85e4..0a55fe8e9 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateFieldDto.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/Models/UpdateFieldDto.cs @@ -18,9 +18,9 @@ namespace Squidex.Areas.Api.Controllers.Schemas.Models [Required] public FieldPropertiesDto Properties { get; set; } - public UpdateField ToCommand(long id) + public UpdateField ToCommand(long id, long? parentId = null) { - return new UpdateField { FieldId = id, Properties = Properties?.ToProperties() }; + return new UpdateField { ParentFieldId = parentId, FieldId = id, Properties = Properties?.ToProperties() }; } } } diff --git a/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs b/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs index 245205873..90608aa46 100644 --- a/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs +++ b/src/Squidex/Areas/Api/Controllers/Schemas/SchemaFieldsController.cs @@ -58,6 +58,35 @@ namespace Squidex.Areas.Api.Controllers.Schemas return StatusCode(201, response); } + /// + /// Add a nested schema field. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The field object that needs to be added to the schema. + /// + /// 201 => Schema field created. + /// 400 => Schema field properties not valid. + /// 409 => Schema field name already in use. + /// 404 => Field, schema or app not found. + /// + [HttpPost] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/")] + [ProducesResponseType(typeof(EntityCreatedDto), 201)] + [ProducesResponseType(typeof(ErrorDto), 409)] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ApiCosts(1)] + public async Task PostNestedField(string app, string name, long parentId, [FromBody] AddFieldDto request) + { + var context = await CommandBus.PublishAsync(request.ToCommand(parentId)); + + var result = context.Result>(); + var response = EntityCreatedDto.FromResult(result); + + return StatusCode(201, response); + } + /// /// Reorders the fields. /// @@ -73,13 +102,36 @@ namespace Squidex.Areas.Api.Controllers.Schemas [Route("apps/{app}/schemas/{name}/fields/ordering/")] [ProducesResponseType(typeof(ErrorDto), 400)] [ApiCosts(1)] - public async Task PutFieldOrdering(string app, string name, [FromBody] ReorderFieldsDto request) + public async Task PutSchemaFieldOrdering(string app, string name, [FromBody] ReorderFieldsDto request) { await CommandBus.PublishAsync(request.ToCommand()); return NoContent(); } + /// + /// Reorders the nested fields. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The request that contains the field ids. + /// + /// 204 => Schema fields reorderd. + /// 400 => Schema field ids do not cover the fields of the schema. + /// 404 => Field, schema or app not found. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/ordering/")] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ApiCosts(1)] + public async Task PutNestedFieldOrdering(string app, string name, long parentId, [FromBody] ReorderFieldsDto request) + { + await CommandBus.PublishAsync(request.ToCommand(parentId)); + + return NoContent(); + } + /// /// Update a schema field. /// @@ -90,7 +142,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// /// 204 => Schema field updated. /// 400 => Schema field properties not valid or field is locked. - /// 404 => Schema, field or app not found. + /// 404 => Field, schema or app not found. /// [HttpPut] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")] @@ -104,6 +156,31 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + /// + /// Update a nested schema field. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The id of the field to update. + /// The field object that needs to be added to the schema. + /// + /// 204 => Schema field updated. + /// 400 => Schema field properties not valid or field is locked. + /// 404 => Field, schema or app not found. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] + [ProducesResponseType(typeof(ErrorDto), 409)] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ApiCosts(1)] + public async Task PutNestedField(string app, string name, long parentId, long id, [FromBody] UpdateFieldDto request) + { + await CommandBus.PublishAsync(request.ToCommand(id, parentId)); + + return NoContent(); + } + /// /// Lock a schema field. /// @@ -113,7 +190,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// /// 204 => Schema field shown. /// 400 => Schema field already locked. - /// 404 => Schema, field or app not found. + /// 404 => Field, schema or app not found. /// /// /// A hidden field is not part of the API response, but can still be edited in the portal. @@ -138,7 +215,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// /// 204 => Schema field hidden. /// 400 => Schema field already hidden. - /// 404 => Schema, field or app not found. + /// 404 => Field, schema or app not found. /// /// /// A locked field cannot be edited or deleted. @@ -154,6 +231,32 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + /// + /// Hide a nested schema field. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The id of the field to hide. + /// + /// 204 => Schema field hidden. + /// 400 => Schema field already hidden. + /// 404 => Field, schema, or app not found. + /// + /// + /// A locked field cannot be edited or deleted. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/hide/")] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ApiCosts(1)] + public async Task HideNestedField(string app, string name, long parentId, long id) + { + await CommandBus.PublishAsync(new HideField { ParentFieldId = parentId, FieldId = id }); + + return NoContent(); + } + /// /// Show a schema field. /// @@ -163,7 +266,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// /// 204 => Schema field shown. /// 400 => Schema field already visible. - /// 404 => Schema, field or app not found. + /// 404 => Field, schema or app not found. /// /// /// A hidden field is not part of the API response, but can still be edited in the portal. @@ -179,6 +282,32 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + /// + /// Show a nested schema field. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The id of the field to show. + /// + /// 204 => Schema field shown. + /// 400 => Schema field already visible. + /// 404 => Field, schema or app not found. + /// + /// + /// A hidden field is not part of the API response, but can still be edited in the portal. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/show/")] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ApiCosts(1)] + public async Task ShowNestedField(string app, string name, long parentId, long id) + { + await CommandBus.PublishAsync(new ShowField { ParentFieldId = parentId, FieldId = id }); + + return NoContent(); + } + /// /// Enable a schema field. /// @@ -188,7 +317,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// /// 204 => Schema field enabled. /// 400 => Schema field already enabled. - /// 404 => Schema, field or app not found. + /// 404 => Field, schema or app not found. /// /// /// A disabled field cannot not be edited in the squidex portal anymore, @@ -205,6 +334,33 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + /// + /// Enable a nested schema field. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The id of the field to enable. + /// + /// 204 => Schema field enabled. + /// 400 => Schema field already enabled. + /// 404 => Field, schema or app not found. + /// + /// + /// A disabled field cannot not be edited in the squidex portal anymore, + /// but will be part of the API response. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/enable/")] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ApiCosts(1)] + public async Task EnableNestedField(string app, string name, long parentId, long id) + { + await CommandBus.PublishAsync(new EnableField { ParentFieldId = parentId, FieldId = id }); + + return NoContent(); + } + /// /// Disable a schema field. /// @@ -214,7 +370,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// /// 204 => Schema field disabled. /// 400 => Schema field already disabled. - /// 404 => Schema, field or app not found. + /// 404 => Field, schema or app not found. /// /// /// A disabled field cannot not be edited in the squidex portal anymore, @@ -231,6 +387,33 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + /// + /// Disable nested a schema field. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The id of the field to disable. + /// + /// 204 => Schema field disabled. + /// 400 => Schema field already disabled. + /// 404 => Field, schema or app not found. + /// + /// + /// A disabled field cannot not be edited in the squidex portal anymore, + /// but will be part of the API response. + /// + [HttpPut] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/disable/")] + [ProducesResponseType(typeof(ErrorDto), 400)] + [ApiCosts(1)] + public async Task DisableNestedField(string app, string name, long parentId, long id) + { + await CommandBus.PublishAsync(new DisableField { ParentFieldId = parentId, FieldId = id }); + + return NoContent(); + } + /// /// Delete a schema field. /// @@ -240,7 +423,7 @@ namespace Squidex.Areas.Api.Controllers.Schemas /// /// 204 => Schema field deleted. /// 400 => Field is locked. - /// 404 => Schema, field or app not found. + /// 404 => Field, schema or app not found. /// [HttpDelete] [Route("apps/{app}/schemas/{name}/fields/{id:long}/")] @@ -251,5 +434,27 @@ namespace Squidex.Areas.Api.Controllers.Schemas return NoContent(); } + + /// + /// Delete a nested schema field. + /// + /// The name of the app. + /// The name of the schema. + /// The parent field id. + /// The id of the field to disable. + /// + /// 204 => Schema field deleted. + /// 400 => Field is locked. + /// 404 => Field, schema or app not found. + /// + [HttpDelete] + [Route("apps/{app}/schemas/{name}/fields/{parentId:long}/nested/{id:long}/")] + [ApiCosts(1)] + public async Task DeleteNestedField(string app, string name, long parentId, long id) + { + await CommandBus.PublishAsync(new DeleteField { ParentFieldId = parentId, FieldId = id }); + + return NoContent(); + } } } \ No newline at end of file diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs index 8f724b1a9..f41214737 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddleware.cs @@ -57,7 +57,7 @@ namespace Squidex.Pipeline.CommandMiddlewares throw new InvalidOperationException("Cannot resolve app."); } - return new NamedId(appFeature.App.Id, appFeature.App.Name); + return NamedId.Of(appFeature.App.Id, appFeature.App.Name); } } } \ No newline at end of file diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs index e385b6025..2825ad8f6 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddleware.cs @@ -68,7 +68,7 @@ namespace Squidex.Pipeline.CommandMiddlewares if (appFeature != null && appFeature.App != null) { - appId = new NamedId(appFeature.App.Id, appFeature.App.Name); + appId = NamedId.Of(appFeature.App.Id, appFeature.App.Name); } } @@ -96,7 +96,7 @@ namespace Squidex.Pipeline.CommandMiddlewares throw new DomainObjectNotFoundException(schemaName, typeof(ISchemaEntity)); } - return new NamedId(schema.Id, schema.Name); + return NamedId.Of(schema.Id, schema.Name); } } diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs index 4dd9c0b59..7112afd42 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleEventFormatterTests.cs @@ -34,8 +34,8 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules private readonly IUserResolver userResolver = A.Fake(); private readonly IUser user = A.Fake(); private readonly IRuleUrlGenerator urlGenerator = A.Fake(); - private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); - private readonly NamedId schemaId = new NamedId(Guid.NewGuid(), "my-schema"); + private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); + private readonly NamedId schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); private readonly Guid contentId = Guid.NewGuid(); private readonly RuleEventFormatter sut; diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs index 8fecceb38..f0bbc9d29 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/RuleServiceTests.cs @@ -116,7 +116,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_not_create_job_if_too_old() { - var e = new ContentCreated { SchemaId = new NamedId(Guid.NewGuid(), "my-schema"), AppId = new NamedId(Guid.NewGuid(), "my-event") }; + var e = new ContentCreated { SchemaId = NamedId.Of(Guid.NewGuid(), "my-schema"), AppId = NamedId.Of(Guid.NewGuid(), "my-event") }; var now = SystemClock.Instance.GetCurrentInstant(); @@ -147,7 +147,7 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules [Fact] public async Task Should_create_job_if_triggered() { - var e = new ContentCreated { SchemaId = new NamedId(Guid.NewGuid(), "my-schema"), AppId = new NamedId(Guid.NewGuid(), "my-event") }; + var e = new ContentCreated { SchemaId = NamedId.Of(Guid.NewGuid(), "my-schema"), AppId = NamedId.Of(Guid.NewGuid(), "my-event") }; var now = SystemClock.Instance.GetCurrentInstant(); diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs index 6b31152bb..719c2bd45 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/HandleRules/Triggers/ContentChangedTriggerTests.cs @@ -27,8 +27,8 @@ namespace Squidex.Domain.Apps.Core.Operations.HandleRules.Triggers public class ContentChangedTriggerTests { private readonly IRuleTriggerHandler sut = new ContentChangedTriggerHandler(); - private static readonly NamedId SchemaMatch = new NamedId(Guid.NewGuid(), "my-schema1"); - private static readonly NamedId SchemaNonMatch = new NamedId(Guid.NewGuid(), "my-schema2"); + private static readonly NamedId SchemaMatch = NamedId.Of(Guid.NewGuid(), "my-schema1"); + private static readonly NamedId SchemaNonMatch = NamedId.Of(Guid.NewGuid(), "my-schema2"); public static IEnumerable TestData = new[] { diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs index 488cf757f..4c6030662 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/ArrayFieldTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs index 1a069900c..6b96f8df2 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/Operations/ValidateContent/TagsFieldTests.cs @@ -5,7 +5,6 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; diff --git a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs b/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs index 2f9cc397a..6434b9bee 100644 --- a/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs +++ b/tests/Squidex.Domain.Apps.Core.Tests/TestData.cs @@ -62,6 +62,7 @@ namespace Squidex.Domain.Apps.Core .AddDateTime(203, "nested-datetime") .AddGeolocation(204, "nested-geolocation") .AddJson(205, "nested-json") + .AddJson(211, "nested-json2") .AddNumber(206, "nested-number") .AddReferences(207, "nested-references") .AddString(208, "nested-string") @@ -89,9 +90,11 @@ namespace Squidex.Domain.Apps.Core .AddTags(112, "root-tags", Partitioning.Language, new TagsFieldProperties()) .Update(new SchemaProperties { Hints = "The User" }) - .UpdateField(104, f => f.Hide()) - .UpdateField(108, f => f.Lock()) - .UpdateField(109, f => f.Disable()); + .HideField(104) + .HideField(211, 101) + .DisableField(109) + .DisableField(212, 101) + .LockField(105); return schema; } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs index 4934a4a33..e225709e2 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/GuardRuleTests.cs @@ -25,7 +25,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards { private readonly Uri validUrl = new Uri("https://squidex.io"); private readonly Rule rule_0 = new Rule(new ContentChangedTrigger(), new WebhookAction()); - private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); + private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); private readonly IAppProvider appProvider = A.Fake(); public GuardRuleTests() diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexCommandMiddlewareTests.cs index 30fada75b..cb4fd2a23 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Indexes/RulesByAppIndexCommandMiddlewareTests.cs @@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes public async Task Should_add_rule_to_index_on_create() { var context = - new CommandContext(new CreateRule { RuleId = appId, AppId = new NamedId(appId, "my-app") }, commandBus) + new CommandContext(new CreateRule { RuleId = appId, AppId = NamedId.Of(appId, "my-app") }, commandBus) .Complete(); await sut.HandleAsync(context); @@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Rules.Indexes .Returns(J.AsTask(ruleState)); A.CallTo(() => ruleState.AppId) - .Returns(new NamedId(appId, "my-app")); + .Returns(NamedId.Of(appId, "my-app")); var context = new CommandContext(new DeleteRule { RuleId = appId }, commandBus) diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs index de5eed0bb..e27cceb12 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleEnqueuerTests.cs @@ -28,7 +28,7 @@ namespace Squidex.Domain.Apps.Entities.Rules private readonly IRuleEventRepository ruleEventRepository = A.Fake(); private readonly RuleService ruleService = A.Fake(); private readonly Instant now = SystemClock.Instance.GetCurrentInstant(); - private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); + private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); private readonly RuleEnqueuer sut; public RuleEnqueuerTests() diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs index 7b86caf6f..3c8732370 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs @@ -23,7 +23,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { private readonly IAppProvider appProvider = A.Fake(); private readonly Schema schema_0; - private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); + private readonly NamedId appId = NamedId.Of(Guid.NewGuid(), "my-app"); public GuardSchemaTests() { @@ -80,14 +80,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards Name = null, Properties = new ArrayFieldProperties(), Partitioning = "invalid", - Nested = new List + Nested = new List { - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = null, Properties = InvalidProperties() }, - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = null, Properties = InvalidProperties() @@ -99,14 +99,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards Name = null, Properties = InvalidProperties(), Partitioning = "invalid", - Nested = new List + Nested = new List { - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = null, Properties = InvalidProperties() }, - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = null, Properties = InvalidProperties() @@ -145,14 +145,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards Name = "field1", Properties = new ArrayFieldProperties(), Partitioning = "invariant", - Nested = new List + Nested = new List { - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = "nested1", Properties = ValidProperties() }, - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = "nested1", Properties = ValidProperties() @@ -191,14 +191,14 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards Name = "field3", Properties = new ArrayFieldProperties(), Partitioning = "invariant", - Nested = new List + Nested = new List { - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = "nested1", Properties = ValidProperties() }, - new CreateNestedSchemaField + new CreateSchemaNestedField { Name = "nested2", Properties = ValidProperties() diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexCommandMiddlewareTests.cs index 32fe82c5c..15091edd8 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Indexes/SchemasByAppIndexCommandMiddlewareTests.cs @@ -38,7 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes public async Task Should_add_schema_to_index_on_create() { var context = - new CommandContext(new CreateSchema { SchemaId = appId, Name = "my-schema", AppId = new NamedId(appId, "my-app") }, commandBus) + new CommandContext(new CreateSchema { SchemaId = appId, Name = "my-schema", AppId = NamedId.Of(appId, "my-app") }, commandBus) .Complete(); await sut.HandleAsync(context); @@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Indexes .Returns(J.AsTask(schemaState)); A.CallTo(() => schemaState.AppId) - .Returns(new NamedId(appId, "my-app")); + .Returns(NamedId.Of(appId, "my-app")); var context = new CommandContext(new DeleteSchema { SchemaId = appId }, commandBus) diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs index f45e200c1..2b4b10fee 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaGrainTests.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FakeItEasy; +using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.State; @@ -27,7 +28,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas private readonly IAppProvider appProvider = A.Fake(); private readonly FieldRegistry registry = new FieldRegistry(new TypeNameRegistry()); private readonly string fieldName = "age"; - private readonly NamedId fieldId; + private readonly string arrayName = "array"; + private readonly NamedId fieldId = NamedId.Of(1L, "age"); + private readonly NamedId arrayId = NamedId.Of(1L, "array"); + private readonly NamedId nestedId = NamedId.Of(2L, "age"); private readonly SchemaGrain sut; protected override Guid Id @@ -40,8 +44,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName)) .Returns((ISchemaEntity)null); - fieldId = new NamedId(1, fieldName); - sut = new SchemaGrain(Store, A.Dummy(), appProvider, registry); sut.OnActivateAsync(Id).Wait(); } @@ -85,7 +87,18 @@ namespace Squidex.Domain.Apps.Entities.Schemas var fields = new List { new CreateSchemaField { Name = "field1", Properties = ValidProperties() }, - new CreateSchemaField { Name = "field2", Properties = ValidProperties() } + new CreateSchemaField { Name = "field2", Properties = ValidProperties() }, + new CreateSchemaField + { + Name = "field3", + Partitioning = Partitioning.Language.Key, + Properties = new ArrayFieldProperties(), + Nested = new List + { + new CreateSchemaNestedField { Name = "nested1", Properties = ValidProperties() }, + new CreateSchemaNestedField { Name = "nested2", Properties = ValidProperties() } + } + } }; var command = new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties, Fields = fields }; @@ -100,7 +113,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas Assert.Equal(SchemaName, sut.Snapshot.Name); Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name); - Assert.Equal(2, @event.Fields.Count); + Assert.Equal(3, @event.Fields.Count); } [Fact] @@ -153,25 +166,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } - [Fact] - public async Task Reorder_should_create_events_and_update_state() - { - var command = new ReorderFields { FieldIds = new List { 1, 2 } }; - - await ExecuteCreateAsync(); - await ExecuteAddFieldAsync("field1"); - await ExecuteAddFieldAsync("field2"); - - var result = await sut.ExecuteAsync(CreateCommand(command)); - - result.ShouldBeEquivalent(new EntitySavedResult(3)); - - LastEvents - .ShouldHaveSameEvents( - CreateEvent(new SchemaFieldsReordered { FieldIds = command.FieldIds }) - ); - } - [Fact] public async Task Publish_should_create_events_and_update_state() { @@ -249,6 +243,65 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task LockField_should_create_events_and_update_state() + { + var command = new LockField { FieldId = 1 }; + + await ExecuteCreateAsync(); + await ExecuteAddFieldAsync(fieldName); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(2)); + + Assert.False(GetField(1).IsDisabled); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new FieldLocked { FieldId = fieldId }) + ); + } + + [Fact] + public async Task Reorder_should_create_events_and_update_state() + { + var command = new ReorderFields { FieldIds = new List { 1, 2 } }; + + await ExecuteCreateAsync(); + await ExecuteAddFieldAsync("field1"); + await ExecuteAddFieldAsync("field2"); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(3)); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new SchemaFieldsReordered { FieldIds = command.FieldIds }) + ); + } + + [Fact] + public async Task Reorder_should_create_events_and_update_state_for_array() + { + var command = new ReorderFields { ParentFieldId = 1, FieldIds = new List { 2, 3 } }; + + await ExecuteCreateAsync(); + await ExecuteAddArrayFieldAsync(); + await ExecuteAddFieldAsync("field1", 1); + await ExecuteAddFieldAsync("field2", 1); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(4)); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new SchemaFieldsReordered { ParentFieldId = arrayId, FieldIds = command.FieldIds }) + ); + } + [Fact] public async Task Add_should_create_events_and_update_state() { @@ -260,7 +313,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas result.ShouldBeEquivalent(EntityCreatedResult.Create(1, 1)); - Assert.Equal(command.Properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties); + Assert.Equal(command.Properties, GetField(1).RawProperties); LastEvents .ShouldHaveSameEvents( @@ -268,6 +321,27 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task Add_should_create_events_and_update_state_for_array() + { + var command = new AddField { ParentFieldId = 1, Name = fieldName, Properties = ValidProperties() }; + + await ExecuteCreateAsync(); + await ExecuteAddArrayFieldAsync(); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(EntityCreatedResult.Create(2, 2)); + + Assert.NotEqual(command.Properties, GetField(1).RawProperties); + Assert.Equal(command.Properties, GetNestedField(1, 2).RawProperties); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new FieldAdded { ParentFieldId = arrayId, Name = fieldName, FieldId = nestedId, Properties = command.Properties }) + ); + } + [Fact] public async Task UpdateField_should_create_events_and_update_state() { @@ -280,7 +354,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas result.ShouldBeEquivalent(new EntitySavedResult(2)); - Assert.Equal(command.Properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties); + Assert.Equal(command.Properties, GetField(1).RawProperties); LastEvents .ShouldHaveSameEvents( @@ -289,22 +363,24 @@ namespace Squidex.Domain.Apps.Entities.Schemas } [Fact] - public async Task LockField_should_create_events_and_update_state() + public async Task UpdateField_should_create_events_and_update_state_for_array() { - var command = new LockField { FieldId = 1 }; + var command = new UpdateField { ParentFieldId = 1, FieldId = 2, Properties = new StringFieldProperties() }; await ExecuteCreateAsync(); - await ExecuteAddFieldAsync(fieldName); + await ExecuteAddArrayFieldAsync(); + await ExecuteAddFieldAsync(fieldName, 1); var result = await sut.ExecuteAsync(CreateCommand(command)); - result.ShouldBeEquivalent(new EntitySavedResult(2)); + result.ShouldBeEquivalent(new EntitySavedResult(3)); - Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); + Assert.NotEqual(command.Properties, GetField(1).RawProperties); + Assert.Equal(command.Properties, GetNestedField(1, 2).RawProperties); LastEvents .ShouldHaveSameEvents( - CreateEvent(new FieldLocked { FieldId = fieldId }) + CreateEvent(new FieldUpdated { ParentFieldId = arrayId, FieldId = nestedId, Properties = command.Properties }) ); } @@ -320,7 +396,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas result.ShouldBeEquivalent(new EntitySavedResult(2)); - Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden); + Assert.True(GetField(1).IsHidden); LastEvents .ShouldHaveSameEvents( @@ -328,6 +404,28 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task HideField_should_create_events_and_update_state_for_array() + { + var command = new HideField { ParentFieldId = 1, FieldId = 2 }; + + await ExecuteCreateAsync(); + await ExecuteAddArrayFieldAsync(); + await ExecuteAddFieldAsync(fieldName, 1); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(3)); + + Assert.False(GetField(1).IsHidden); + Assert.True(GetNestedField(1, 2).IsHidden); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new FieldHidden { ParentFieldId = arrayId, FieldId = nestedId }) + ); + } + [Fact] public async Task ShowField_should_create_events_and_update_state() { @@ -341,7 +439,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas result.ShouldBeEquivalent(new EntitySavedResult(3)); - Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden); + Assert.False(GetField(1).IsHidden); LastEvents .ShouldHaveSameEvents( @@ -349,6 +447,29 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task ShowField_should_create_events_and_update_state_for_array() + { + var command = new ShowField { ParentFieldId = 1, FieldId = 2 }; + + await ExecuteCreateAsync(); + await ExecuteAddArrayFieldAsync(); + await ExecuteAddFieldAsync(fieldName, 1); + await ExecuteHideFieldAsync(2, 1); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(4)); + + Assert.False(GetField(1).IsHidden); + Assert.False(GetNestedField(1, 2).IsHidden); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new FieldShown { ParentFieldId = arrayId, FieldId = nestedId }) + ); + } + [Fact] public async Task DisableField_should_create_events_and_update_state() { @@ -361,7 +482,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas result.ShouldBeEquivalent(new EntitySavedResult(2)); - Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); + Assert.True(GetField(1).IsDisabled); LastEvents .ShouldHaveSameEvents( @@ -369,6 +490,28 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task DisableField_should_create_events_and_update_state_for_array() + { + var command = new DisableField { ParentFieldId = 1, FieldId = 2 }; + + await ExecuteCreateAsync(); + await ExecuteAddArrayFieldAsync(); + await ExecuteAddFieldAsync(fieldName, 1); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(3)); + + Assert.False(GetField(1).IsDisabled); + Assert.True(GetNestedField(1, 2).IsDisabled); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new FieldDisabled { ParentFieldId = arrayId, FieldId = nestedId }) + ); + } + [Fact] public async Task EnableField_should_create_events_and_update_state() { @@ -382,7 +525,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas result.ShouldBeEquivalent(new EntitySavedResult(3)); - Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); + Assert.False(GetField(1).IsDisabled); LastEvents .ShouldHaveSameEvents( @@ -390,6 +533,29 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task EnableField_should_create_events_and_update_state_for_array() + { + var command = new EnableField { ParentFieldId = 1, FieldId = 2 }; + + await ExecuteCreateAsync(); + await ExecuteAddArrayFieldAsync(); + await ExecuteAddFieldAsync(fieldName, 1); + await ExecuteDisableFieldAsync(2, 1); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(4)); + + Assert.False(GetField(1).IsDisabled); + Assert.False(GetNestedField(1, 2).IsDisabled); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new FieldEnabled { ParentFieldId = arrayId, FieldId = nestedId }) + ); + } + [Fact] public async Task DeleteField_should_create_events_and_update_state() { @@ -402,7 +568,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas result.ShouldBeEquivalent(new EntitySavedResult(2)); - Assert.False(sut.Snapshot.SchemaDef.FieldsById.ContainsKey(1)); + Assert.Null(GetField(1)); LastEvents .ShouldHaveSameEvents( @@ -410,24 +576,51 @@ namespace Squidex.Domain.Apps.Entities.Schemas ); } + [Fact] + public async Task DeleteField_should_create_events_and_update_state_for_array() + { + var command = new DeleteField { ParentFieldId = 1, FieldId = 2 }; + + await ExecuteCreateAsync(); + await ExecuteAddArrayFieldAsync(); + await ExecuteAddFieldAsync(fieldName, 1); + + var result = await sut.ExecuteAsync(CreateCommand(command)); + + result.ShouldBeEquivalent(new EntitySavedResult(3)); + + Assert.NotNull(GetField(1)); + Assert.Null(GetNestedField(1, 2)); + + LastEvents + .ShouldHaveSameEvents( + CreateEvent(new FieldDeleted { ParentFieldId = arrayId, FieldId = nestedId }) + ); + } + private Task ExecuteCreateAsync() { return sut.ExecuteAsync(CreateCommand(new CreateSchema { Name = SchemaName })); } - private Task ExecuteAddFieldAsync(string name) + private Task ExecuteAddArrayFieldAsync() { - return sut.ExecuteAsync(CreateCommand(new AddField { Properties = ValidProperties(), Name = name })); + return sut.ExecuteAsync(CreateCommand(new AddField { Properties = new ArrayFieldProperties(), Name = arrayName })); } - private Task ExecuteHideFieldAsync(long id) + private Task ExecuteAddFieldAsync(string name, long? parentId = null) { - return sut.ExecuteAsync(CreateCommand(new HideField { FieldId = id })); + return sut.ExecuteAsync(CreateCommand(new AddField { ParentFieldId = parentId, Properties = ValidProperties(), Name = name })); } - private Task ExecuteDisableFieldAsync(long id) + private Task ExecuteHideFieldAsync(long id, long? parentId = null) { - return sut.ExecuteAsync(CreateCommand(new DisableField { FieldId = id })); + return sut.ExecuteAsync(CreateCommand(new HideField { ParentFieldId = parentId, FieldId = id })); + } + + private Task ExecuteDisableFieldAsync(long id, long? parentId = null) + { + return sut.ExecuteAsync(CreateCommand(new DisableField { ParentFieldId = parentId, FieldId = id })); } private Task ExecutePublishAsync() @@ -440,6 +633,16 @@ namespace Squidex.Domain.Apps.Entities.Schemas return sut.ExecuteAsync(CreateCommand(new DeleteSchema())); } + private IField GetField(int id) + { + return sut.Snapshot.SchemaDef.FieldsById.GetOrDefault(id); + } + + private IField GetNestedField(int parentId, int childId) + { + return ((IArrayField)sut.Snapshot.SchemaDef.FieldsById[parentId]).FieldsById.GetOrDefault(childId); + } + private static StringFieldProperties ValidProperties() { return new StringFieldProperties { MinLength = 10, MaxLength = 20 }; diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs b/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs index 67377c1bf..c8148df60 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs @@ -38,12 +38,12 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers protected NamedId AppNamedId { - get { return new NamedId(AppId, AppName); } + get { return NamedId.Of(AppId, AppName); } } protected NamedId SchemaNamedId { - get { return new NamedId(SchemaId, SchemaName); } + get { return NamedId.Of(SchemaId, SchemaName); } } protected abstract Guid Id { get; } diff --git a/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs b/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs index 2c06652a1..09a9d1548 100644 --- a/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs +++ b/tests/Squidex.Infrastructure.Tests/NamedIdTests.cs @@ -19,7 +19,7 @@ namespace Squidex.Infrastructure { var id = Guid.NewGuid(); - var namedId = new NamedId(id, "my-name"); + var namedId = NamedId.Of(id, "my-name"); Assert.Equal(id, namedId.Id); Assert.Equal("my-name", namedId.Name); @@ -30,7 +30,7 @@ namespace Squidex.Infrastructure { var id = Guid.NewGuid(); - var namedId = new NamedId(id, "my-name"); + var namedId = NamedId.Of(id, "my-name"); Assert.Equal($"{id},my-name", namedId.ToString()); } @@ -41,10 +41,10 @@ namespace Squidex.Infrastructure var id1 = Guid.NewGuid(); var id2 = Guid.NewGuid(); - var token1a = new NamedId(id1, "my-name1"); - var token1b = new NamedId(id1, "my-name1"); - var token1c = new NamedId(id1, "my-name2"); - var token2a = new NamedId(id2, "my-name1"); + var token1a = NamedId.Of(id1, "my-name1"); + var token1b = NamedId.Of(id1, "my-name1"); + var token1c = NamedId.Of(id1, "my-name2"); + var token2a = NamedId.Of(id2, "my-name1"); Assert.True(token1a.Equals(token1b)); @@ -58,10 +58,10 @@ namespace Squidex.Infrastructure var id1 = Guid.NewGuid(); var id2 = Guid.NewGuid(); - object token1a = new NamedId(id1, "my-name1"); - object token1b = new NamedId(id1, "my-name1"); - object token1c = new NamedId(id1, "my-name2"); - object token2a = new NamedId(id2, "my-name1"); + object token1a = NamedId.Of(id1, "my-name1"); + object token1b = NamedId.Of(id1, "my-name1"); + object token1c = NamedId.Of(id1, "my-name2"); + object token2a = NamedId.Of(id2, "my-name1"); Assert.True(token1a.Equals(token1b)); @@ -75,10 +75,10 @@ namespace Squidex.Infrastructure var id1 = Guid.NewGuid(); var id2 = Guid.NewGuid(); - object token1a = new NamedId(id1, "my-name1"); - object token1b = new NamedId(id1, "my-name1"); - object token1c = new NamedId(id1, "my-name2"); - object token2a = new NamedId(id2, "my-name1"); + object token1a = NamedId.Of(id1, "my-name1"); + object token1b = NamedId.Of(id1, "my-name1"); + object token1c = NamedId.Of(id1, "my-name2"); + object token2a = NamedId.Of(id2, "my-name1"); Assert.Equal(token1a.GetHashCode(), token1b.GetHashCode()); @@ -97,7 +97,7 @@ namespace Squidex.Infrastructure [Fact] public void Should_serialize_and_deserialize_valid_guid_token() { - var value = new NamedId(Guid.NewGuid(), "my-name"); + var value = NamedId.Of(Guid.NewGuid(), "my-name"); value.SerializeAndDeserialize(new NamedGuidIdConverter()); } @@ -113,7 +113,7 @@ namespace Squidex.Infrastructure [Fact] public void Should_serialize_and_deserialize_valid_long_token() { - var value = new NamedId(123, "my-name"); + var value = NamedId.Of(123L, "my-name"); value.SerializeAndDeserialize(new NamedLongIdConverter()); } @@ -129,7 +129,7 @@ namespace Squidex.Infrastructure [Fact] public void Should_serialize_and_deserialize_valid_string_token() { - var value = new NamedId(Guid.NewGuid().ToString(), "my-name"); + var value = NamedId.Of(Guid.NewGuid().ToString(), "my-name"); value.SerializeAndDeserialize(new NamedStringIdConverter()); } diff --git a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs index 6c5633b24..9f808290d 100644 --- a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs +++ b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithAppIdCommandMiddlewareTests.cs @@ -66,7 +66,7 @@ namespace Squidex.Pipeline.CommandMiddlewares await sut.HandleAsync(context); - Assert.Equal(new NamedId(appId, appName), command.AppId); + Assert.Equal(NamedId.Of(appId, appName), command.AppId); } [Fact] @@ -100,12 +100,12 @@ namespace Squidex.Pipeline.CommandMiddlewares { SetupApp(out var appId, out var appName); - var command = new CreateContent { AppId = new NamedId(Guid.NewGuid(), "other-app") }; + var command = new CreateContent { AppId = NamedId.Of(Guid.NewGuid(), "other-app") }; var context = new CommandContext(command, commandBus); await sut.HandleAsync(context); - Assert.NotEqual(new NamedId(appId, appName), command.AppId); + Assert.NotEqual(NamedId.Of(appId, appName), command.AppId); } private void SetupApp(out Guid appId, out string appName) diff --git a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs index d00ae9453..7f962523b 100644 --- a/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs +++ b/tests/Squidex.Tests/Pipeline/CommandMiddlewares/EnrichWithSchemaIdCommandMiddlewareTests.cs @@ -101,7 +101,7 @@ namespace Squidex.Pipeline.CommandMiddlewares await sut.HandleAsync(context); - Assert.Equal(new NamedId(schemaId, schemaName), command.SchemaId); + Assert.Equal(NamedId.Of(schemaId, schemaName), command.SchemaId); } [Fact] @@ -117,7 +117,7 @@ namespace Squidex.Pipeline.CommandMiddlewares await sut.HandleAsync(context); - Assert.Equal(new NamedId(schemaId, schemaName), command.SchemaId); + Assert.Equal(NamedId.Of(schemaId, schemaName), command.SchemaId); } [Fact] @@ -139,7 +139,7 @@ namespace Squidex.Pipeline.CommandMiddlewares [Fact] public async Task Should_use_app_id_from_command() { - var appId = new NamedId(Guid.NewGuid(), "my-app"); + var appId = NamedId.Of(Guid.NewGuid(), "my-app"); SetupSchema(appId.Id, out var schemaId, out var schemaName); @@ -150,7 +150,7 @@ namespace Squidex.Pipeline.CommandMiddlewares await sut.HandleAsync(context); - Assert.Equal(new NamedId(schemaId, schemaName), command.SchemaId); + Assert.Equal(NamedId.Of(schemaId, schemaName), command.SchemaId); } [Fact] @@ -173,12 +173,12 @@ namespace Squidex.Pipeline.CommandMiddlewares SetupApp(out var appId, out var appName); SetupSchema(appId, out var schemaId, out var schemaName); - var command = new CreateContent { SchemaId = new NamedId(Guid.NewGuid(), "other-schema") }; + var command = new CreateContent { SchemaId = NamedId.Of(Guid.NewGuid(), "other-schema") }; var context = new CommandContext(command, commandBus); await sut.HandleAsync(context); - Assert.NotEqual(new NamedId(appId, appName), command.AppId); + Assert.NotEqual(NamedId.Of(appId, appName), command.AppId); } private void SetupSchema(Guid appId, out Guid schemaId, out string schemaName)