From 03786ba2c024100f2bf2252b97000968cf7d6942 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sat, 16 Jun 2018 20:14:15 +0200 Subject: [PATCH] Guards simplified. --- .../Apps/Guards/GuardApp.cs | 35 +++-- .../Apps/Guards/GuardAppClients.cs | 42 +++--- .../Apps/Guards/GuardAppContributors.cs | 59 ++++---- .../Apps/Guards/GuardAppLanguages.cs | 42 +++--- .../Apps/Guards/GuardAppPattern.cs | 26 ++-- .../Assets/Guards/GuardAsset.cs | 6 +- .../Contents/Guards/GuardContent.cs | 39 +++--- .../Rules/Guards/GuardRule.cs | 18 +-- .../Schemas/Commands/CreateSchemaField.cs | 19 +-- .../Schemas/Commands/CreateSchemaFieldBase.cs | 24 ++++ .../Commands/CreateSchemaNestedField.cs | 9 +- .../Schemas/Guards/GuardHelper.cs | 47 +++++++ .../Schemas/Guards/GuardSchema.cs | 132 +++++++----------- .../Schemas/Guards/GuardSchemaField.cs | 71 +++------- .../Schemas/SchemaCreatedField.cs | 17 +-- .../Schemas/SchemaCreatedFieldBase.cs | 24 ++++ .../Schemas/SchemaCreatedNestedField.cs | 11 +- src/Squidex.Infrastructure/Validate.cs | 38 +++-- src/Squidex.Infrastructure/ValidationError.cs | 5 + .../Apps/Guards/GuardAppContributorsTests.cs | 4 +- .../Apps/Guards/GuardAppPatternsTests.cs | 4 +- .../Schemas/Guards/GuardSchemaTests.cs | 56 +++----- 22 files changed, 366 insertions(+), 362 deletions(-) create mode 100644 src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaFieldBase.cs create mode 100644 src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardHelper.cs create mode 100644 src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs index bc75dc123..ce81ef926 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs @@ -20,15 +20,15 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - return Validate.It(() => "Cannot create app.", async error => + return Validate.It(() => "Cannot create app.", async e => { if (!command.Name.IsSlug()) { - error(new ValidationError("Name must be a valid slug.", nameof(command.Name))); + e("Name must be a valid slug.", nameof(command.Name)); } else if (await appProvider.GetAppAsync(command.Name) != null) { - error(new ValidationError($"An app with the same name already exists.", nameof(command.Name))); + e("An app with the same name already exists.", nameof(command.Name)); } }); } @@ -37,28 +37,27 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot change plan.", error => + Validate.It(() => "Cannot change plan.", e => { if (string.IsNullOrWhiteSpace(command.PlanId)) { - error(new ValidationError("Plan id is required.", nameof(command.PlanId))); + e("Plan id is required.", nameof(command.PlanId)); + return; } - else + + if (appPlans.GetPlan(command.PlanId) == null) { - if (appPlans.GetPlan(command.PlanId) == null) - { - error(new ValidationError("A plan with this id does not exist.", nameof(command.PlanId))); - } + e("A plan with this id does not exist.", nameof(command.PlanId)); + } - if (!string.IsNullOrWhiteSpace(command.PlanId) && plan != null && !plan.Owner.Equals(command.Actor)) - { - error(new ValidationError("Plan can only changed from the user who configured the plan initially.")); - } + if (!string.IsNullOrWhiteSpace(command.PlanId) && plan != null && !plan.Owner.Equals(command.Actor)) + { + e("Plan can only changed from the user who configured the plan initially."); + } - if (string.Equals(command.PlanId, plan?.PlanId, StringComparison.OrdinalIgnoreCase)) - { - error(new ValidationError("App has already this plan.")); - } + if (string.Equals(command.PlanId, plan?.PlanId, StringComparison.OrdinalIgnoreCase)) + { + e("App has already this plan."); } }); } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs index 3a1dd36e0..bced69cc1 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs @@ -17,15 +17,15 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot attach client.", error => + Validate.It(() => "Cannot attach client.", e => { if (string.IsNullOrWhiteSpace(command.Id)) { - error(new ValidationError("Client id is required.", nameof(command.Id))); + e("Client id is required.", nameof(command.Id)); } else if (clients.ContainsKey(command.Id)) { - error(new ValidationError($"A client with the same id already exists.")); + e($"A client with the same id already exists."); } }); } @@ -36,11 +36,11 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards GetClientOrThrow(clients, command.Id); - Validate.It(() => "Cannot revoke client.", error => + Validate.It(() => "Cannot revoke client.", e => { if (string.IsNullOrWhiteSpace(command.Id)) { - error(new ValidationError("Client id is required.", nameof(command.Id))); + e("Client id is required.", nameof(command.Id)); } }); } @@ -51,41 +51,43 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards var client = GetClientOrThrow(clients, command.Id); - Validate.It(() => "Cannot revoke client.", error => + Validate.It(() => "Cannot update client.", e => { if (string.IsNullOrWhiteSpace(command.Id)) { - error(new ValidationError("Client id is required.", nameof(command.Id))); + e("Client id is required.", nameof(command.Id)); } if (string.IsNullOrWhiteSpace(command.Name) && command.Permission == null) { - error(new ValidationError("Either name or permission must be defined.", nameof(command.Name), nameof(command.Permission))); + e("Either name or permission must be defined.", nameof(command.Name), nameof(command.Permission)); } if (command.Permission.HasValue && !command.Permission.Value.IsEnumValue()) { - error(new ValidationError("Permission is not valid.", nameof(command.Permission))); + e("Permission is not valid.", nameof(command.Permission)); } - if (client != null) + if (client == null) { - if (!string.IsNullOrWhiteSpace(command.Name) && string.Equals(client.Name, command.Name)) - { - error(new ValidationError("Client has already this name.", nameof(command.Name))); - } - - if (command.Permission == client.Permission) - { - error(new ValidationError("Client has already this permission.", nameof(command.Permission))); - } + return; + } + + if (!string.IsNullOrWhiteSpace(command.Name) && string.Equals(client.Name, command.Name)) + { + e("Client has already this name.", nameof(command.Name)); + } + + if (command.Permission == client.Permission) + { + e("Client has already this permission.", nameof(command.Permission)); } }); } private static AppClient GetClientOrThrow(AppClients clients, string id) { - if (id == null) + if (string.IsNullOrWhiteSpace(id)) { return null; } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs index d79f0ade0..e2066c487 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs @@ -7,6 +7,7 @@ using System; using System.Linq; +using System.Security; using System.Threading.Tasks; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; @@ -22,46 +23,44 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - return Validate.It(() => "Cannot assign contributor.", async error => + return Validate.It(() => "Cannot assign contributor.", async e => { if (!command.Permission.IsEnumValue()) { - error(new ValidationError("Permission is not valid.", nameof(command.Permission))); + e("Permission is not valid.", nameof(command.Permission)); } if (string.IsNullOrWhiteSpace(command.ContributorId)) { - error(new ValidationError("Contributor id is required.", nameof(command.ContributorId))); + e("Contributor id is required.", nameof(command.ContributorId)); + return; } - else + + var user = await users.FindByIdOrEmailAsync(command.ContributorId); + + if (user == null) { - var user = await users.FindByIdOrEmailAsync(command.ContributorId); + throw new DomainObjectNotFoundException(command.ContributorId, "Contributors", typeof(IAppEntity)); + } - if (user == null) - { - throw new DomainObjectNotFoundException(command.ContributorId, "Contributors", typeof(IAppEntity)); - } - else - { - command.ContributorId = user.Id; + command.ContributorId = user.Id; - if (string.Equals(command.ContributorId, command.Actor?.Identifier, StringComparison.OrdinalIgnoreCase)) - { - error(new ValidationError("You cannot change your own permission.")); - } - else if (contributors.TryGetValue(command.ContributorId, out var existing)) - { - if (existing == command.Permission) - { - error(new ValidationError("Contributor has already this permission.", nameof(command.Permission))); - } - } - else if (plan.MaxContributors == contributors.Count) - { - error(new ValidationError("You have reached the maximum number of contributors for your plan.")); - } + if (string.Equals(command.ContributorId, command.Actor?.Identifier, StringComparison.OrdinalIgnoreCase)) + { + throw new SecurityException("You cannot change your own permission."); + } + + if (contributors.TryGetValue(command.ContributorId, out var existing)) + { + if (existing == command.Permission) + { + e("Contributor has already this permission.", nameof(command.Permission)); } } + else if (plan.MaxContributors == contributors.Count) + { + e("You have reached the maximum number of contributors for your plan."); + } }); } @@ -69,18 +68,18 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot remove contributor.", error => + Validate.It(() => "Cannot remove contributor.", e => { if (string.IsNullOrWhiteSpace(command.ContributorId)) { - error(new ValidationError("Contributor id is required.", nameof(command.ContributorId))); + e("Contributor id is required.", nameof(command.ContributorId)); } var ownerIds = contributors.Where(x => x.Value == AppContributorPermission.Owner).Select(x => x.Key).ToList(); if (ownerIds.Count == 1 && ownerIds.Contains(command.ContributorId)) { - error(new ValidationError("Cannot remove the only owner.")); + e("Cannot remove the only owner."); } }); diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs index aac2fb15a..183249ec3 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs @@ -17,15 +17,15 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot add language.", error => + Validate.It(() => "Cannot add language.", e => { if (command.Language == null) { - error(new ValidationError("Language code is required.", nameof(command.Language))); + e("Language code is required.", nameof(command.Language)); } else if (languages.Contains(command.Language)) { - error(new ValidationError("Language has already been added.")); + e("Language has already been added."); } }); } @@ -34,18 +34,18 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - var languageConfig = GetLanguageConfigOrThrow(languages, command.Language); + var config = GetConfigOrThrow(languages, command.Language); - Validate.It(() => "Cannot remove language.", error => + Validate.It(() => "Cannot remove language.", e => { if (command.Language == null) { - error(new ValidationError("Language code is required.", nameof(command.Language))); + e("Language code is required.", nameof(command.Language)); } - if (languages.Master == languageConfig) + if (languages.Master == config) { - error(new ValidationError("Master language cannot be removed.")); + e("Master language cannot be removed."); } }); } @@ -54,34 +54,36 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - var languageConfig = GetLanguageConfigOrThrow(languages, command.Language); + var config = GetConfigOrThrow(languages, command.Language); - Validate.It(() => "Cannot update language.", error => + Validate.It(() => "Cannot update language.", e => { if (command.Language == null) { - error(new ValidationError("Language is required.", nameof(command.Language))); + e("Language is required.", nameof(command.Language)); } - if ((languages.Master == languageConfig || command.IsMaster) && command.IsOptional) + if ((languages.Master == config || command.IsMaster) && command.IsOptional) { - error(new ValidationError("Master language cannot be made optional.", nameof(command.IsMaster))); + e("Master language cannot be made optional.", nameof(command.IsMaster)); } - if (command.Fallback != null) + if (command.Fallback == null) { - foreach (var fallback in command.Fallback) + return; + } + + foreach (var fallback in command.Fallback) + { + if (!languages.Contains(fallback)) { - if (!languages.Contains(fallback)) - { - error(new ValidationError($"App does not have fallback language '{fallback}'.", nameof(command.Fallback))); - } + e($"App does not have fallback language '{fallback}'.", nameof(command.Fallback)); } } }); } - private static LanguageConfig GetLanguageConfigOrThrow(LanguagesConfig languages, Language language) + private static LanguageConfig GetConfigOrThrow(LanguagesConfig languages, Language language) { if (language == null) { diff --git a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppPattern.cs b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppPattern.cs index 1a7f9e2c2..49996f253 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppPattern.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppPattern.cs @@ -18,35 +18,35 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot add pattern.", error => + Validate.It(() => "Cannot add pattern.", e => { if (command.PatternId == Guid.Empty) { - error(new ValidationError("Id is required.", nameof(command.PatternId))); + e("Id is required.", nameof(command.PatternId)); } if (string.IsNullOrWhiteSpace(command.Name)) { - error(new ValidationError("Name is required.", nameof(command.Name))); + e("Name is required.", nameof(command.Name)); } if (patterns.Values.Any(x => x.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase))) { - error(new ValidationError("An pattern with the same name already exists.")); + e("A pattern with the same name already exists."); } if (string.IsNullOrWhiteSpace(command.Pattern)) { - error(new ValidationError("Pattern is required.", nameof(command.Pattern))); + e("Pattern is required.", nameof(command.Pattern)); } else if (!command.Pattern.IsValidRegex()) { - error(new ValidationError("Pattern is not a valid regular expression.", nameof(command.Pattern))); + e("Pattern is not a valid regular expression.", nameof(command.Pattern)); } if (patterns.Values.Any(x => x.Pattern == command.Pattern)) { - error(new ValidationError("This pattern already exists but with another name.")); + e("This pattern already exists but with another name."); } }); } @@ -70,30 +70,30 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards throw new DomainObjectNotFoundException(command.PatternId.ToString(), typeof(AppPattern)); } - Validate.It(() => "Cannot update pattern.", error => + Validate.It(() => "Cannot update pattern.", e => { if (string.IsNullOrWhiteSpace(command.Name)) { - error(new ValidationError("Name is required.", nameof(command.Name))); + e("Name is required.", nameof(command.Name)); } if (patterns.Any(x => x.Key != command.PatternId && x.Value.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase))) { - error(new ValidationError("An pattern with the same name already exists.")); + e("A pattern with the same name already exists."); } if (string.IsNullOrWhiteSpace(command.Pattern)) { - error(new ValidationError("Pattern is required.", nameof(command.Pattern))); + e("Pattern is required.", nameof(command.Pattern)); } else if (!command.Pattern.IsValidRegex()) { - error(new ValidationError("Pattern is not a valid regular expression.", nameof(command.Pattern))); + e("Pattern is not a valid regular expression.", nameof(command.Pattern)); } if (patterns.Any(x => x.Key != command.PatternId && x.Value.Pattern == command.Pattern)) { - error(new ValidationError("This pattern already exists but with another name.")); + e("This pattern already exists but with another name."); } }); } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs b/src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs index 552b5bd2c..dc63eb46b 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs @@ -16,16 +16,16 @@ namespace Squidex.Domain.Apps.Entities.Assets.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot rename asset.", error => + Validate.It(() => "Cannot rename asset.", e => { if (string.IsNullOrWhiteSpace(command.FileName)) { - error(new ValidationError("Name is required.", nameof(command.FileName))); + e("Name is required.", nameof(command.FileName)); } if (string.Equals(command.FileName, oldName)) { - error(new ValidationError("Asset has already this name.", nameof(command.FileName))); + e("Asset has already this name.", nameof(command.FileName)); } }); } diff --git a/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs b/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs index 64d8f8a39..5cf7b1777 100644 --- a/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs +++ b/src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs @@ -18,12 +18,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot created content.", error => + Validate.It(() => "Cannot created content.", e => { - if (command.Data == null) - { - error(new ValidationError("Data cannot be null.", nameof(command.Data))); - } + ValidateData(command, e); }); } @@ -31,12 +28,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot update content.", error => + Validate.It(() => "Cannot update content.", e => { - if (command.Data == null) - { - error(new ValidationError("Data cannot be null.", nameof(command.Data))); - } + ValidateData(command, e); }); } @@ -44,12 +38,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot patch content.", error => + Validate.It(() => "Cannot patch content.", e => { - if (command.Data == null) - { - error(new ValidationError("Data cannot be null.", nameof(command.Data))); - } + ValidateData(command, e); }); } @@ -57,11 +48,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot discard pending changes.", error => + Validate.It(() => "Cannot discard pending changes.", e => { if (!isPending) { - error(new ValidationError("The content has no pending changes.")); + e("The content has no pending changes."); } }); } @@ -70,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot change status.", error => + Validate.It(() => "Cannot change status.", e => { var isAllowedPendingUpdate = status == command.Status && @@ -79,12 +70,12 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards if (!StatusFlow.Exists(command.Status) || (!StatusFlow.CanChange(status, command.Status) && !isAllowedPendingUpdate)) { - error(new ValidationError($"Content cannot be changed from status {status} to {command.Status}.", nameof(command.Status))); + e($"Content cannot be changed from status {status} to {command.Status}.", nameof(command.Status)); } if (command.DueTime.HasValue && command.DueTime.Value < SystemClock.Instance.GetCurrentInstant()) { - error(new ValidationError("DueTime must be in the future.", nameof(command.DueTime))); + e("DueTime must be in the future.", nameof(command.DueTime)); } }); } @@ -93,5 +84,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards { Guard.NotNull(command, nameof(command)); } + + private static void ValidateData(ContentDataCommand command, AddValidation e) + { + if (command.Data == null) + { + e("Data is required.", nameof(command.Data)); + } + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs b/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs index e2fcca363..64974f4e0 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs @@ -19,28 +19,28 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards { Guard.NotNull(command, nameof(command)); - return Validate.It(() => "Cannot create rule.", async error => + return Validate.It(() => "Cannot create rule.", async e => { if (command.Trigger == null) { - error(new ValidationError("Trigger is required.", nameof(command.Trigger))); + e("Trigger is required.", nameof(command.Trigger)); } else { var errors = await RuleTriggerValidator.ValidateAsync(command.AppId.Id, command.Trigger, appProvider); - errors.Foreach(error); + errors.Foreach(x => x.AddTo(e)); } if (command.Action == null) { - error(new ValidationError("Action is required.", nameof(command.Action))); + e("Action is required.", nameof(command.Action)); } else { var errors = await RuleActionValidator.ValidateAsync(command.Action); - errors.Foreach(error); + errors.Foreach(x => x.AddTo(e)); } }); } @@ -49,25 +49,25 @@ namespace Squidex.Domain.Apps.Entities.Rules.Guards { Guard.NotNull(command, nameof(command)); - return Validate.It(() => "Cannot update rule.", async error => + return Validate.It(() => "Cannot update rule.", async e => { if (command.Trigger == null && command.Action == null) { - error(new ValidationError("Either trigger or action is required.", nameof(command.Trigger), nameof(command.Action))); + e("Either trigger or action is required.", nameof(command.Trigger), nameof(command.Action)); } if (command.Trigger != null) { var errors = await RuleTriggerValidator.ValidateAsync(appId, command.Trigger, appProvider); - errors.Foreach(error); + errors.Foreach(x => x.AddTo(e)); } if (command.Action != null) { var errors = await RuleActionValidator.ValidateAsync(command.Action); - errors.Foreach(error); + errors.Foreach(x => x.AddTo(e)); } }); } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs index dd5dd16e1..6dfba43fc 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs @@ -5,25 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Core.Schemas; -using FieldNested = System.Collections.Generic.List; +using System.Collections.Generic; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class CreateSchemaField + public sealed class CreateSchemaField : CreateSchemaFieldBase { - public string Partitioning { get; set; } = Core.Partitioning.Invariant.Key; + public string Partitioning { get; set; } = "invariant"; - public string Name { get; set; } - - public bool IsHidden { get; set; } - - public bool IsLocked { get; set; } - - public bool IsDisabled { get; set; } - - public FieldNested Nested { get; set; } - - public FieldProperties Properties { 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/CreateSchemaFieldBase.cs new file mode 100644 index 000000000..3d91afc20 --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaFieldBase.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// 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.Schemas.Commands +{ + public abstract class CreateSchemaFieldBase + { + public string Name { get; set; } + + public bool IsLocked { get; set; } + + public bool IsHidden { get; set; } + + public bool IsDisabled { get; set; } + + public FieldProperties Properties { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs index 409d79f99..a20292880 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs @@ -9,14 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Entities.Schemas.Commands { - public sealed class CreateSchemaNestedField + public sealed class CreateSchemaNestedField : CreateSchemaFieldBase { - public string Name { get; set; } - - public bool IsHidden { get; set; } - - public bool IsDisabled { get; set; } - - public FieldProperties Properties { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardHelper.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardHelper.cs new file mode 100644 index 000000000..b0a2b8d5d --- /dev/null +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardHelper.cs @@ -0,0 +1,47 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Entities.Schemas.Guards +{ + public static class GuardHelper + { + public static IArrayField GetArrayFieldOrThrow(Schema schema, long parentId) + { + if (!schema.FieldsById.TryGetValue(parentId, out var rootField) || !(rootField is IArrayField arrayField)) + { + throw new DomainObjectNotFoundException(parentId.ToString(), "Fields", typeof(Schema)); + } + + return arrayField; + } + + public static IField GetFieldOrThrow(Schema schema, long fieldId, long? parentId) + { + if (parentId.HasValue) + { + var arrayField = GetArrayFieldOrThrow(schema, parentId.Value); + + if (!arrayField.FieldsById.TryGetValue(fieldId, out var nestedField)) + { + throw new DomainObjectNotFoundException(fieldId.ToString(), $"Fields[{parentId}].Fields", typeof(Schema)); + } + + return nestedField; + } + + if (!schema.FieldsById.TryGetValue(fieldId, out var field)) + { + throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Schema)); + } + + return field; + } + } +} diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs index aaa06014b..943c3791c 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.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; @@ -22,106 +21,69 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - return Validate.It(() => "Cannot create schema.", async error => + return Validate.It(() => "Cannot create schema.", async e => { if (!command.Name.IsSlug()) { - error(new ValidationError("Name is not a valid slug.", nameof(command.Name))); + e("Name is not a valid slug.", nameof(command.Name)); } else if (await appProvider.GetSchemaAsync(command.AppId.Id, command.Name) != null) { - error(new ValidationError("A schema with the same name already exists.")); + e("A schema with the same name already exists."); } if (command.Fields?.Count > 0) { - var index = 0; + var fieldIndex = 0; + var fieldPrefix = string.Empty; foreach (var field in command.Fields) { - index++; - - var prefix = $"Fields[{index}]"; + fieldIndex++; + fieldPrefix = $"Fields[{fieldIndex}]"; if (!field.Partitioning.IsValidPartitioning()) { - error(new ValidationError("Field partitioning is not valid.", - $"{prefix}.{nameof(field.Partitioning)}")); - } - - if (!field.Name.IsPropertyName()) - { - error(new ValidationError("Field name must be a valid javascript property name.", - $"{prefix}.{nameof(field.Name)}")); + e("Field partitioning is not valid.", $"{fieldPrefix}.{nameof(field.Partitioning)}"); } - if (field.Properties == null) - { - error(new ValidationError("Field properties is required.", - $"{prefix}.{nameof(field.Properties)}")); - } - else - { - var errors = FieldPropertiesValidator.Validate(field.Properties); - - foreach (var e in errors) - { - error(e.WithPrefix($"{prefix}.Properties")); - } - } + ValidateField(e, fieldPrefix, field); if (field.Nested?.Count > 0) { - if (!(field.Properties is ArrayFieldProperties)) - { - error(new ValidationError("Only array fields can have nested fields.", - $"{prefix}.{nameof(field.Partitioning)}")); - } - else + if (field.Properties is ArrayFieldProperties) { var nestedIndex = 0; + var nestedPrefix = string.Empty; foreach (var nestedField in field.Nested) { nestedIndex++; + nestedPrefix = $"{fieldPrefix}.Nested[{nestedIndex}]"; - var nestedPrefix = $"{prefix}.Nested[{nestedIndex}]"; - - if (!nestedField.Name.IsPropertyName()) - { - error(new ValidationError("Nested field name must be a valid javascript property name.", - $"{nestedPrefix}.{nameof(nestedField.Name)}")); - } - - if (nestedField.Properties == null) + if (nestedField.Properties is ArrayFieldProperties) { - error(new ValidationError("Nested field properties is required.", - $"{nestedPrefix}.{nameof(nestedField.Properties)}")); + e("Nested field cannot be array fields.", $"{nestedPrefix}.{nameof(nestedField.Properties)}"); } - else - { - var errors = FieldPropertiesValidator.Validate(nestedField.Properties); - foreach (var e in errors) - { - error(e.WithPrefix($"{nestedPrefix}.Properties")); - } - } + ValidateField(e, nestedPrefix, nestedField); } } + 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) { - error(new ValidationError("Fields cannot have duplicate names.", - $"{prefix}.Nested")); + e("Fields cannot have duplicate names.", $"{fieldPrefix}.Nested"); } } } if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count) { - error(new ValidationError("Fields cannot have duplicate names.", - nameof(command.Fields))); + e("Fields cannot have duplicate names.", nameof(command.Fields)); } } }); @@ -135,44 +97,27 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards if (command.ParentFieldId.HasValue) { - 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)); - } + arrayField = GuardHelper.GetArrayFieldOrThrow(schema, command.ParentFieldId.Value); } Validate.It(() => "Cannot reorder schema fields.", error => { if (command.FieldIds == null) { - error(new ValidationError("Field ids is required.", nameof(command.FieldIds))); + error("Field ids is required.", nameof(command.FieldIds)); } if (arrayField == null) { - CheckFields(error, command, schema.FieldsById); + ValidateFieldIds(error, command, schema.FieldsById); } else { - CheckFields(error, command, arrayField.FieldsById); + ValidateFieldIds(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("Field ids do not cover all fields.", nameof(c.FieldIds))); - } - } - public static void CanPublish(Schema schema, PublishSchema command) { Guard.NotNull(command, nameof(command)); @@ -212,5 +157,32 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); } + + private static void ValidateField(AddValidation e, string prefix, CreateSchemaFieldBase field) + { + if (!field.Name.IsPropertyName()) + { + e("Field name must be a valid javascript property name.", $"{prefix}.{nameof(field.Name)}"); + } + + if (field.Properties == null) + { + e("Field properties is required.", $"{prefix}.{nameof(field.Properties)}"); + } + else + { + var errors = FieldPropertiesValidator.Validate(field.Properties); + + errors.Foreach(x => x.WithPrefix($"{prefix}.{nameof(field.Properties)}").AddTo(e)); + } + } + + private static void ValidateFieldIds(AddValidation error, ReorderFields c, IReadOnlyDictionary fields) + { + if (c.FieldIds != null && (c.FieldIds.Count != fields.Count || c.FieldIds.Any(x => !fields.ContainsKey(x)))) + { + error("Field ids do not cover all fields.", nameof(c.FieldIds)); + } + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs index 62b348955..aaeb7167c 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs @@ -18,50 +18,43 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - Validate.It(() => "Cannot add a new field.", error => + Validate.It(() => "Cannot add a new field.", e => { if (!command.Name.IsPropertyName()) { - error(new ValidationError("Name must be a valid javascript property name.", nameof(command.Name))); + e("Name must be a valid javascript property name.", nameof(command.Name)); } if (command.Properties == null) { - error(new ValidationError("Properties is required.", nameof(command.Properties))); + e("Properties is required.", nameof(command.Properties)); } else { var errors = FieldPropertiesValidator.Validate(command.Properties); - foreach (var e in errors) - { - error(e.WithPrefix(nameof(command.Properties))); - } + errors.Foreach(x => x.WithPrefix(nameof(command.Properties)).AddTo(e)); } if (command.ParentFieldId.HasValue) { - var parentId = command.ParentFieldId.Value; + var arrayField = GuardHelper.GetArrayFieldOrThrow(schema, command.ParentFieldId.Value); - if (!schema.FieldsById.TryGetValue(parentId, out var rootField) || !(rootField is IArrayField arrayField)) - { - throw new DomainObjectNotFoundException(parentId.ToString(), "Fields", typeof(Schema)); - } if (arrayField.FieldsByName.ContainsKey(command.Name)) { - error(new ValidationError($"A field with the same name already exists.")); + e($"A field with the same name already exists."); } } else { if (command.ParentFieldId == null && !command.Partitioning.IsValidPartitioning()) { - error(new ValidationError("Partitioning is not valid.", nameof(command.Partitioning))); + e("Partitioning is not valid.", nameof(command.Partitioning)); } if (schema.FieldsByName.ContainsKey(command.Name)) { - error(new ValidationError($"A field with the same name already exists.")); + e($"A field with the same name already exists."); } } }); @@ -71,27 +64,24 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); + var field = GuardHelper.GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); if (field.IsLocked) { throw new DomainException("Schema field is already locked."); } - Validate.It(() => "Cannot update field.", error => + Validate.It(() => "Cannot update field.", e => { if (command.Properties == null) { - error(new ValidationError("Properties is required.", nameof(command.Properties))); + e("Properties is required.", nameof(command.Properties)); } else { var errors = FieldPropertiesValidator.Validate(command.Properties); - foreach (var e in errors) - { - error(e.WithPrefix(nameof(command.Properties))); - } + errors.Foreach(x => x.WithPrefix(nameof(command.Properties)).AddTo(e)); } }); } @@ -100,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); + var field = GuardHelper.GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); if (field.IsLocked) { @@ -117,7 +107,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); + var field = GuardHelper.GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); if (field.IsDisabled) { @@ -129,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); + var field = GuardHelper.GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); if (field.IsLocked) { @@ -141,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); + var field = GuardHelper.GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); if (!field.IsHidden) { @@ -153,7 +143,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); + var field = GuardHelper.GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); if (!field.IsDisabled) { @@ -165,37 +155,12 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { Guard.NotNull(command, nameof(command)); - var field = GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); + var field = GuardHelper.GetFieldOrThrow(schema, command.FieldId, command.ParentFieldId); if (field.IsLocked) { throw new DomainException("Schema field is already locked."); } } - - private static IField GetFieldOrThrow(Schema schema, long fieldId, long? parentId) - { - if (parentId.HasValue) - { - if (!schema.FieldsById.TryGetValue(parentId.Value, out var rootField) || !(rootField is IArrayField arrayField)) - { - throw new DomainObjectNotFoundException(parentId.ToString(), "Fields", typeof(Schema)); - } - - if (!arrayField.FieldsById.TryGetValue(fieldId, out var nestedField)) - { - throw new DomainObjectNotFoundException(fieldId.ToString(), $"Fields[{parentId}].Fields", typeof(Schema)); - } - - return nestedField; - } - - if (!schema.FieldsById.TryGetValue(fieldId, out var field)) - { - throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Schema)); - } - - return field; - } } } diff --git a/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedField.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedField.cs index ce040cba5..1cf2fc2a8 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedField.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedField.cs @@ -5,25 +5,14 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using Squidex.Domain.Apps.Core.Schemas; -using FieldNested = System.Collections.Generic.List; +using System.Collections.Generic; namespace Squidex.Domain.Apps.Events.Schemas { - public sealed class SchemaCreatedField + public sealed class SchemaCreatedField : SchemaCreatedFieldBase { public string Partitioning { get; set; } - public string Name { get; set; } - - public bool IsHidden { get; set; } - - public bool IsLocked { get; set; } - - public bool IsDisabled { get; set; } - - public FieldNested Nested { get; set; } - - public FieldProperties Properties { get; set; } + public List Nested { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs new file mode 100644 index 000000000..903c0c04b --- /dev/null +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs @@ -0,0 +1,24 @@ +// ========================================================================== +// 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.Events.Schemas +{ + public abstract class SchemaCreatedFieldBase + { + public string Name { get; set; } + + public bool IsHidden { get; set; } + + public bool IsLocked { get; set; } + + public bool IsDisabled { get; set; } + + public FieldProperties Properties { get; set; } + } +} diff --git a/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedNestedField.cs b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedNestedField.cs index 7d8fb5878..331f89513 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedNestedField.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedNestedField.cs @@ -9,16 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas; namespace Squidex.Domain.Apps.Events.Schemas { - public sealed class SchemaCreatedNestedField + public sealed class SchemaCreatedNestedField : SchemaCreatedFieldBase { - public string Name { get; set; } - - public bool IsHidden { get; set; } - - public bool IsLocked { get; set; } - - public bool IsDisabled { get; set; } - - public FieldProperties Properties { get; set; } } } diff --git a/src/Squidex.Infrastructure/Validate.cs b/src/Squidex.Infrastructure/Validate.cs index 04b483152..8f69aeeca 100644 --- a/src/Squidex.Infrastructure/Validate.cs +++ b/src/Squidex.Infrastructure/Validate.cs @@ -12,27 +12,49 @@ using System.Threading.Tasks; namespace Squidex.Infrastructure { + public delegate void AddValidation(string message, params string[] propertyNames); + public static class Validate { - public static void It(Func message, Action> action) + public static void It(Func message, Action action) { - var errors = new List(); + List errors = null; + + var addValidation = new AddValidation((m, p) => + { + if (errors == null) + { + errors = new List(); + } + + errors.Add(new ValidationError(m, p)); + }); - action(errors.Add); + action(addValidation); - if (errors.Any()) + if (errors != null) { throw new ValidationException(message(), errors); } } - public static async Task It(Func message, Func, Task> action) + public static async Task It(Func message, Func action) { - var errors = new List(); + List errors = null; + + var addValidation = new AddValidation((m, p) => + { + if (errors == null) + { + errors = new List(); + } + + errors.Add(new ValidationError(m, p)); + }); - await action(errors.Add); + await action(addValidation); - if (errors.Any()) + if (errors != null) { throw new ValidationException(message(), errors); } diff --git a/src/Squidex.Infrastructure/ValidationError.cs b/src/Squidex.Infrastructure/ValidationError.cs index 47b4f8fe1..0324f151a 100644 --- a/src/Squidex.Infrastructure/ValidationError.cs +++ b/src/Squidex.Infrastructure/ValidationError.cs @@ -46,5 +46,10 @@ namespace Squidex.Infrastructure return new ValidationError(Message, prefix); } } + + public void AddTo(AddValidation e) + { + e(Message, propertyNames); + } } } diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs index cc236ae1d..a61f1f099 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Security; using System.Threading.Tasks; using FakeItEasy; using Squidex.Domain.Apps.Core.Apps; @@ -91,8 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards { var command = new AssignContributor { ContributorId = "3", Permission = AppContributorPermission.Editor, Actor = new RefToken("user", "3") }; - return ValidationAssert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan), - new ValidationError("You cannot change your own permission.")); + return Assert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan)); } [Fact] diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs index 1f7a6e208..9951809ef 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs @@ -56,7 +56,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = ".*" }; ValidationAssert.Throws(() => GuardAppPattern.CanAdd(patterns_1, command), - new ValidationError("An pattern with the same name already exists.")); + new ValidationError("A pattern with the same name already exists.")); } [Fact] @@ -141,7 +141,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards var command = new UpdatePattern { PatternId = id2, Name = "Pattern1", Pattern = "[0-4]" }; ValidationAssert.Throws(() => GuardAppPattern.CanUpdate(patterns_2, command), - new ValidationError("An pattern with the same name already exists.")); + new ValidationError("A pattern with the same name already exists.")); } [Fact] 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 df1c1a245..7a931964d 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs @@ -207,7 +207,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards }; return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), - new ValidationError("Nested field name must be a valid javascript property name.", + new ValidationError("Field name must be a valid javascript property name.", "Fields[1].Nested[1].Name")); } @@ -238,12 +238,12 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards }; return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), - new ValidationError("Nested field properties is required.", + new ValidationError("Field properties is required.", "Fields[1].Nested[1].Properties")); } [Fact] - public Task CanCreate_should_throw_exception_if_nested_field_properties_not_valid() + public Task CanCreate_should_throw_exception_if_nested_field_is_array() { var command = new CreateSchema { @@ -260,7 +260,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards new CreateSchemaNestedField { Name = "nested1", - Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 } + Properties = new ArrayFieldProperties() } } } @@ -269,13 +269,12 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards }; return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), - new ValidationError("Max length must be greater than min length.", - "Fields[1].Nested[1].Properties.MinLength", - "Fields[1].Nested[1].Properties.MaxLength")); + new ValidationError("Nested field cannot be array fields.", + "Fields[1].Nested[1].Properties")); } [Fact] - public Task CanCreate_should_throw_exception_if_nested_field_have_duplicate_names() + public Task CanCreate_should_throw_exception_if_nested_field_properties_not_valid() { var command = new CreateSchema { @@ -292,12 +291,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards new CreateSchemaNestedField { Name = "nested1", - Properties = new StringFieldProperties() - }, - new CreateSchemaNestedField - { - Name = "nested1", - Properties = new StringFieldProperties() + Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 } } } } @@ -306,14 +300,13 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards }; return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), - new ValidationError("Fields cannot have duplicate names.", - "Fields[1].Nested")); + new ValidationError("Max length must be greater than min length.", + "Fields[1].Nested[1].Properties.MinLength", + "Fields[1].Nested[1].Properties.MaxLength")); } - /* - [Fact] - public Task CanCreate_should_throw_exception_if_fields_contain_duplicate_names() + public Task CanCreate_should_throw_exception_if_nested_field_have_duplicate_names() { var command = new CreateSchema { @@ -322,32 +315,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards { new CreateSchemaField { - Name = "field1", - Properties = ValidProperties(), - Partitioning = "invariant" - }, - new CreateSchemaField - { - Name = "field1", - Properties = ValidProperties(), - Partitioning = "invariant" - }, - new CreateSchemaField - { - Name = "field1", + Name = "array", Properties = new ArrayFieldProperties(), - Partitioning = "invariant", + Partitioning = Partitioning.Invariant.Key, Nested = new List { new CreateSchemaNestedField { Name = "nested1", - Properties = ValidProperties() + Properties = new StringFieldProperties() }, new CreateSchemaNestedField { Name = "nested1", - Properties = ValidProperties() + Properties = new StringFieldProperties() } } } @@ -355,9 +336,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards Name = "new-schema" }; - return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider)); + return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), + new ValidationError("Fields cannot have duplicate names.", + "Fields[1].Nested")); } - */ [Fact] public Task CanCreate_should_not_throw_exception_if_command_is_valid()