Browse Source

Guards simplified.

pull/303/head
Sebastian 8 years ago
parent
commit
03786ba2c0
  1. 35
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardApp.cs
  2. 42
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppClients.cs
  3. 59
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs
  4. 42
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppLanguages.cs
  5. 26
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppPattern.cs
  6. 6
      src/Squidex.Domain.Apps.Entities/Assets/Guards/GuardAsset.cs
  7. 39
      src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs
  8. 18
      src/Squidex.Domain.Apps.Entities/Rules/Guards/GuardRule.cs
  9. 19
      src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs
  10. 24
      src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaFieldBase.cs
  11. 9
      src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaNestedField.cs
  12. 47
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardHelper.cs
  13. 132
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs
  14. 71
      src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchemaField.cs
  15. 17
      src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedField.cs
  16. 24
      src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedFieldBase.cs
  17. 11
      src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedNestedField.cs
  18. 38
      src/Squidex.Infrastructure/Validate.cs
  19. 5
      src/Squidex.Infrastructure/ValidationError.cs
  20. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs
  21. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs
  22. 56
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/Guards/GuardSchemaTests.cs

35
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)); 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()) 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) 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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot change plan.", error => Validate.It(() => "Cannot change plan.", e =>
{ {
if (string.IsNullOrWhiteSpace(command.PlanId)) 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) e("A plan with this id does not exist.", nameof(command.PlanId));
{ }
error(new ValidationError("A plan with this id does not exist.", nameof(command.PlanId)));
}
if (!string.IsNullOrWhiteSpace(command.PlanId) && plan != null && !plan.Owner.Equals(command.Actor)) 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.")); e("Plan can only changed from the user who configured the plan initially.");
} }
if (string.Equals(command.PlanId, plan?.PlanId, StringComparison.OrdinalIgnoreCase)) if (string.Equals(command.PlanId, plan?.PlanId, StringComparison.OrdinalIgnoreCase))
{ {
error(new ValidationError("App has already this plan.")); e("App has already this plan.");
}
} }
}); });
} }

42
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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot attach client.", error => Validate.It(() => "Cannot attach client.", e =>
{ {
if (string.IsNullOrWhiteSpace(command.Id)) 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)) 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); GetClientOrThrow(clients, command.Id);
Validate.It(() => "Cannot revoke client.", error => Validate.It(() => "Cannot revoke client.", e =>
{ {
if (string.IsNullOrWhiteSpace(command.Id)) 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); var client = GetClientOrThrow(clients, command.Id);
Validate.It(() => "Cannot revoke client.", error => Validate.It(() => "Cannot update client.", e =>
{ {
if (string.IsNullOrWhiteSpace(command.Id)) 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) 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()) 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)) return;
{ }
error(new ValidationError("Client has already this name.", nameof(command.Name)));
} if (!string.IsNullOrWhiteSpace(command.Name) && string.Equals(client.Name, command.Name))
{
if (command.Permission == client.Permission) e("Client has already this name.", nameof(command.Name));
{ }
error(new ValidationError("Client has already this permission.", nameof(command.Permission)));
} if (command.Permission == client.Permission)
{
e("Client has already this permission.", nameof(command.Permission));
} }
}); });
} }
private static AppClient GetClientOrThrow(AppClients clients, string id) private static AppClient GetClientOrThrow(AppClients clients, string id)
{ {
if (id == null) if (string.IsNullOrWhiteSpace(id))
{ {
return null; return null;
} }

59
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppContributors.cs

@ -7,6 +7,7 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Security;
using System.Threading.Tasks; using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
@ -22,46 +23,44 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{ {
Guard.NotNull(command, nameof(command)); 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()) 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)) 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) command.ContributorId = user.Id;
{
throw new DomainObjectNotFoundException(command.ContributorId, "Contributors", typeof(IAppEntity));
}
else
{
command.ContributorId = user.Id;
if (string.Equals(command.ContributorId, command.Actor?.Identifier, StringComparison.OrdinalIgnoreCase)) if (string.Equals(command.ContributorId, command.Actor?.Identifier, StringComparison.OrdinalIgnoreCase))
{ {
error(new ValidationError("You cannot change your own permission.")); throw new SecurityException("You cannot change your own permission.");
} }
else if (contributors.TryGetValue(command.ContributorId, out var existing))
{ if (contributors.TryGetValue(command.ContributorId, out var existing))
if (existing == command.Permission) {
{ if (existing == command.Permission)
error(new ValidationError("Contributor has already this permission.", nameof(command.Permission))); {
} e("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."));
}
} }
} }
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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot remove contributor.", error => Validate.It(() => "Cannot remove contributor.", e =>
{ {
if (string.IsNullOrWhiteSpace(command.ContributorId)) 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(); var ownerIds = contributors.Where(x => x.Value == AppContributorPermission.Owner).Select(x => x.Key).ToList();
if (ownerIds.Count == 1 && ownerIds.Contains(command.ContributorId)) if (ownerIds.Count == 1 && ownerIds.Contains(command.ContributorId))
{ {
error(new ValidationError("Cannot remove the only owner.")); e("Cannot remove the only owner.");
} }
}); });

42
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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot add language.", error => Validate.It(() => "Cannot add language.", e =>
{ {
if (command.Language == null) 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)) 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)); 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) 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)); 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) 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)) e($"App does not have fallback language '{fallback}'.", nameof(command.Fallback));
{
error(new ValidationError($"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) if (language == null)
{ {

26
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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot add pattern.", error => Validate.It(() => "Cannot add pattern.", e =>
{ {
if (command.PatternId == Guid.Empty) 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)) 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))) 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)) 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()) 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)) 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)); 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)) 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))) 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)) 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()) 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)) 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.");
} }
}); });
} }

6
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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot rename asset.", error => Validate.It(() => "Cannot rename asset.", e =>
{ {
if (string.IsNullOrWhiteSpace(command.FileName)) 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)) 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));
} }
}); });
} }

39
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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot created content.", error => Validate.It(() => "Cannot created content.", e =>
{ {
if (command.Data == null) ValidateData(command, e);
{
error(new ValidationError("Data cannot be null.", nameof(command.Data)));
}
}); });
} }
@ -31,12 +28,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
{ {
Guard.NotNull(command, nameof(command)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot update content.", error => Validate.It(() => "Cannot update content.", e =>
{ {
if (command.Data == null) ValidateData(command, e);
{
error(new ValidationError("Data cannot be null.", nameof(command.Data)));
}
}); });
} }
@ -44,12 +38,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
{ {
Guard.NotNull(command, nameof(command)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot patch content.", error => Validate.It(() => "Cannot patch content.", e =>
{ {
if (command.Data == null) ValidateData(command, e);
{
error(new ValidationError("Data cannot be null.", nameof(command.Data)));
}
}); });
} }
@ -57,11 +48,11 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
{ {
Guard.NotNull(command, nameof(command)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot discard pending changes.", error => Validate.It(() => "Cannot discard pending changes.", e =>
{ {
if (!isPending) 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)); Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot change status.", error => Validate.It(() => "Cannot change status.", e =>
{ {
var isAllowedPendingUpdate = var isAllowedPendingUpdate =
status == command.Status && 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)) 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()) 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)); Guard.NotNull(command, nameof(command));
} }
private static void ValidateData(ContentDataCommand command, AddValidation e)
{
if (command.Data == null)
{
e("Data is required.", nameof(command.Data));
}
}
} }
} }

18
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)); 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) if (command.Trigger == null)
{ {
error(new ValidationError("Trigger is required.", nameof(command.Trigger))); e("Trigger is required.", nameof(command.Trigger));
} }
else else
{ {
var errors = await RuleTriggerValidator.ValidateAsync(command.AppId.Id, command.Trigger, appProvider); var errors = await RuleTriggerValidator.ValidateAsync(command.AppId.Id, command.Trigger, appProvider);
errors.Foreach(error); errors.Foreach(x => x.AddTo(e));
} }
if (command.Action == null) if (command.Action == null)
{ {
error(new ValidationError("Action is required.", nameof(command.Action))); e("Action is required.", nameof(command.Action));
} }
else else
{ {
var errors = await RuleActionValidator.ValidateAsync(command.Action); 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)); 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) 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) if (command.Trigger != null)
{ {
var errors = await RuleTriggerValidator.ValidateAsync(appId, command.Trigger, appProvider); var errors = await RuleTriggerValidator.ValidateAsync(appId, command.Trigger, appProvider);
errors.Foreach(error); errors.Foreach(x => x.AddTo(e));
} }
if (command.Action != null) if (command.Action != null)
{ {
var errors = await RuleActionValidator.ValidateAsync(command.Action); var errors = await RuleActionValidator.ValidateAsync(command.Action);
errors.Foreach(error); errors.Foreach(x => x.AddTo(e));
} }
}); });
} }

19
src/Squidex.Domain.Apps.Entities/Schemas/Commands/CreateSchemaField.cs

@ -5,25 +5,14 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.Schemas; using System.Collections.Generic;
using FieldNested = System.Collections.Generic.List<Squidex.Domain.Apps.Entities.Schemas.Commands.CreateSchemaNestedField>;
namespace Squidex.Domain.Apps.Entities.Schemas.Commands 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 List<CreateSchemaNestedField> Nested { 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; }
} }
} }

24
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; }
}
}

9
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 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; }
} }
} }

47
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;
}
}
}

132
src/Squidex.Domain.Apps.Entities/Schemas/Guards/GuardSchema.cs

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -22,106 +21,69 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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()) 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) 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) if (command.Fields?.Count > 0)
{ {
var index = 0; var fieldIndex = 0;
var fieldPrefix = string.Empty;
foreach (var field in command.Fields) foreach (var field in command.Fields)
{ {
index++; fieldIndex++;
fieldPrefix = $"Fields[{fieldIndex}]";
var prefix = $"Fields[{index}]";
if (!field.Partitioning.IsValidPartitioning()) if (!field.Partitioning.IsValidPartitioning())
{ {
error(new ValidationError("Field partitioning is not valid.", e("Field partitioning is not valid.", $"{fieldPrefix}.{nameof(field.Partitioning)}");
$"{prefix}.{nameof(field.Partitioning)}"));
}
if (!field.Name.IsPropertyName())
{
error(new ValidationError("Field name must be a valid javascript property name.",
$"{prefix}.{nameof(field.Name)}"));
} }
if (field.Properties == null) ValidateField(e, fieldPrefix, field);
{
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"));
}
}
if (field.Nested?.Count > 0) if (field.Nested?.Count > 0)
{ {
if (!(field.Properties is ArrayFieldProperties)) if (field.Properties is ArrayFieldProperties)
{
error(new ValidationError("Only array fields can have nested fields.",
$"{prefix}.{nameof(field.Partitioning)}"));
}
else
{ {
var nestedIndex = 0; var nestedIndex = 0;
var nestedPrefix = string.Empty;
foreach (var nestedField in field.Nested) foreach (var nestedField in field.Nested)
{ {
nestedIndex++; nestedIndex++;
nestedPrefix = $"{fieldPrefix}.Nested[{nestedIndex}]";
var nestedPrefix = $"{prefix}.Nested[{nestedIndex}]"; if (nestedField.Properties is ArrayFieldProperties)
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)
{ {
error(new ValidationError("Nested field properties is required.", e("Nested field cannot be array fields.", $"{nestedPrefix}.{nameof(nestedField.Properties)}");
$"{nestedPrefix}.{nameof(nestedField.Properties)}"));
} }
else
{
var errors = FieldPropertiesValidator.Validate(nestedField.Properties);
foreach (var e in errors) ValidateField(e, nestedPrefix, nestedField);
{
error(e.WithPrefix($"{nestedPrefix}.Properties"));
}
}
} }
} }
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) if (field.Nested.Select(x => x.Name).Distinct().Count() != field.Nested.Count)
{ {
error(new ValidationError("Fields cannot have duplicate names.", e("Fields cannot have duplicate names.", $"{fieldPrefix}.Nested");
$"{prefix}.Nested"));
} }
} }
} }
if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count) if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count)
{ {
error(new ValidationError("Fields cannot have duplicate names.", e("Fields cannot have duplicate names.", nameof(command.Fields));
nameof(command.Fields)));
} }
} }
}); });
@ -135,44 +97,27 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
if (command.ParentFieldId.HasValue) if (command.ParentFieldId.HasValue)
{ {
var parentId = command.ParentFieldId.Value; arrayField = GuardHelper.GetArrayFieldOrThrow(schema, 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 => Validate.It(() => "Cannot reorder schema fields.", error =>
{ {
if (command.FieldIds == null) 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) if (arrayField == null)
{ {
CheckFields(error, command, schema.FieldsById); ValidateFieldIds(error, command, schema.FieldsById);
} }
else else
{ {
CheckFields(error, command, arrayField.FieldsById); ValidateFieldIds(error, command, arrayField.FieldsById);
} }
}); });
} }
private static void CheckFields<T>(Action<ValidationError> error, ReorderFields c, IReadOnlyDictionary<long, T> 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) public static void CanPublish(Schema schema, PublishSchema command)
{ {
Guard.NotNull(command, nameof(command)); Guard.NotNull(command, nameof(command));
@ -212,5 +157,32 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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<T>(AddValidation error, ReorderFields c, IReadOnlyDictionary<long, T> 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));
}
}
} }
} }

71
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)); 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()) 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) if (command.Properties == null)
{ {
error(new ValidationError("Properties is required.", nameof(command.Properties))); e("Properties is required.", nameof(command.Properties));
} }
else else
{ {
var errors = FieldPropertiesValidator.Validate(command.Properties); var errors = FieldPropertiesValidator.Validate(command.Properties);
foreach (var e in errors) errors.Foreach(x => x.WithPrefix(nameof(command.Properties)).AddTo(e));
{
error(e.WithPrefix(nameof(command.Properties)));
}
} }
if (command.ParentFieldId.HasValue) 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)) 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 else
{ {
if (command.ParentFieldId == null && !command.Partitioning.IsValidPartitioning()) 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)) 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)); 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) if (field.IsLocked)
{ {
throw new DomainException("Schema field is already locked."); throw new DomainException("Schema field is already locked.");
} }
Validate.It(() => "Cannot update field.", error => Validate.It(() => "Cannot update field.", e =>
{ {
if (command.Properties == null) if (command.Properties == null)
{ {
error(new ValidationError("Properties is required.", nameof(command.Properties))); e("Properties is required.", nameof(command.Properties));
} }
else else
{ {
var errors = FieldPropertiesValidator.Validate(command.Properties); var errors = FieldPropertiesValidator.Validate(command.Properties);
foreach (var e in errors) errors.Foreach(x => x.WithPrefix(nameof(command.Properties)).AddTo(e));
{
error(e.WithPrefix(nameof(command.Properties)));
}
} }
}); });
} }
@ -100,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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) if (field.IsLocked)
{ {
@ -117,7 +107,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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) if (field.IsDisabled)
{ {
@ -129,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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) if (field.IsLocked)
{ {
@ -141,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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) if (!field.IsHidden)
{ {
@ -153,7 +143,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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) if (!field.IsDisabled)
{ {
@ -165,37 +155,12 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
Guard.NotNull(command, nameof(command)); 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) if (field.IsLocked)
{ {
throw new DomainException("Schema field is already locked."); 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;
}
} }
} }

17
src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedField.cs

@ -5,25 +5,14 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using Squidex.Domain.Apps.Core.Schemas; using System.Collections.Generic;
using FieldNested = System.Collections.Generic.List<Squidex.Domain.Apps.Events.Schemas.SchemaCreatedNestedField>;
namespace Squidex.Domain.Apps.Events.Schemas namespace Squidex.Domain.Apps.Events.Schemas
{ {
public sealed class SchemaCreatedField public sealed class SchemaCreatedField : SchemaCreatedFieldBase
{ {
public string Partitioning { get; set; } public string Partitioning { get; set; }
public string Name { get; set; } public List<SchemaCreatedNestedField> Nested { 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; }
} }
} }

24
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; }
}
}

11
src/Squidex.Domain.Apps.Events/Schemas/SchemaCreatedNestedField.cs

@ -9,16 +9,7 @@ using Squidex.Domain.Apps.Core.Schemas;
namespace Squidex.Domain.Apps.Events.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; }
} }
} }

38
src/Squidex.Infrastructure/Validate.cs

@ -12,27 +12,49 @@ using System.Threading.Tasks;
namespace Squidex.Infrastructure namespace Squidex.Infrastructure
{ {
public delegate void AddValidation(string message, params string[] propertyNames);
public static class Validate public static class Validate
{ {
public static void It(Func<string> message, Action<Action<ValidationError>> action) public static void It(Func<string> message, Action<AddValidation> action)
{ {
var errors = new List<ValidationError>(); List<ValidationError> errors = null;
var addValidation = new AddValidation((m, p) =>
{
if (errors == null)
{
errors = new List<ValidationError>();
}
errors.Add(new ValidationError(m, p));
});
action(errors.Add); action(addValidation);
if (errors.Any()) if (errors != null)
{ {
throw new ValidationException(message(), errors); throw new ValidationException(message(), errors);
} }
} }
public static async Task It(Func<string> message, Func<Action<ValidationError>, Task> action) public static async Task It(Func<string> message, Func<AddValidation, Task> action)
{ {
var errors = new List<ValidationError>(); List<ValidationError> errors = null;
var addValidation = new AddValidation((m, p) =>
{
if (errors == null)
{
errors = new List<ValidationError>();
}
errors.Add(new ValidationError(m, p));
});
await action(errors.Add); await action(addValidation);
if (errors.Any()) if (errors != null)
{ {
throw new ValidationException(message(), errors); throw new ValidationException(message(), errors);
} }

5
src/Squidex.Infrastructure/ValidationError.cs

@ -46,5 +46,10 @@ namespace Squidex.Infrastructure
return new ValidationError(Message, prefix); return new ValidationError(Message, prefix);
} }
} }
public void AddTo(AddValidation e)
{
e(Message, propertyNames);
}
} }
} }

4
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppContributorsTests.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System.Security;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Apps; 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") }; var command = new AssignContributor { ContributorId = "3", Permission = AppContributorPermission.Editor, Actor = new RefToken("user", "3") };
return ValidationAssert.ThrowsAsync(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan), return Assert.ThrowsAsync<SecurityException>(() => GuardAppContributors.CanAssign(contributors_0, command, users, appPlan));
new ValidationError("You cannot change your own permission."));
} }
[Fact] [Fact]

4
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 = ".*" }; var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = ".*" };
ValidationAssert.Throws(() => GuardAppPattern.CanAdd(patterns_1, command), 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] [Fact]
@ -141,7 +141,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
var command = new UpdatePattern { PatternId = id2, Name = "Pattern1", Pattern = "[0-4]" }; var command = new UpdatePattern { PatternId = id2, Name = "Pattern1", Pattern = "[0-4]" };
ValidationAssert.Throws(() => GuardAppPattern.CanUpdate(patterns_2, command), 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] [Fact]

56
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), 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")); "Fields[1].Nested[1].Name"));
} }
@ -238,12 +238,12 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
}; };
return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), 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")); "Fields[1].Nested[1].Properties"));
} }
[Fact] [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 var command = new CreateSchema
{ {
@ -260,7 +260,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
new CreateSchemaNestedField new CreateSchemaNestedField
{ {
Name = "nested1", 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), return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider),
new ValidationError("Max length must be greater than min length.", new ValidationError("Nested field cannot be array fields.",
"Fields[1].Nested[1].Properties.MinLength", "Fields[1].Nested[1].Properties"));
"Fields[1].Nested[1].Properties.MaxLength"));
} }
[Fact] [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 var command = new CreateSchema
{ {
@ -292,12 +291,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
new CreateSchemaNestedField new CreateSchemaNestedField
{ {
Name = "nested1", Name = "nested1",
Properties = new StringFieldProperties() Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 }
},
new CreateSchemaNestedField
{
Name = "nested1",
Properties = new StringFieldProperties()
} }
} }
} }
@ -306,14 +300,13 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
}; };
return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider), return ValidationAssert.ThrowsAsync(() => GuardSchema.CanCreate(command, appProvider),
new ValidationError("Fields cannot have duplicate names.", new ValidationError("Max length must be greater than min length.",
"Fields[1].Nested")); "Fields[1].Nested[1].Properties.MinLength",
"Fields[1].Nested[1].Properties.MaxLength"));
} }
/*
[Fact] [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 var command = new CreateSchema
{ {
@ -322,32 +315,20 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
{ {
new CreateSchemaField new CreateSchemaField
{ {
Name = "field1", Name = "array",
Properties = ValidProperties(),
Partitioning = "invariant"
},
new CreateSchemaField
{
Name = "field1",
Properties = ValidProperties(),
Partitioning = "invariant"
},
new CreateSchemaField
{
Name = "field1",
Properties = new ArrayFieldProperties(), Properties = new ArrayFieldProperties(),
Partitioning = "invariant", Partitioning = Partitioning.Invariant.Key,
Nested = new List<CreateSchemaNestedField> Nested = new List<CreateSchemaNestedField>
{ {
new CreateSchemaNestedField new CreateSchemaNestedField
{ {
Name = "nested1", Name = "nested1",
Properties = ValidProperties() Properties = new StringFieldProperties()
}, },
new CreateSchemaNestedField new CreateSchemaNestedField
{ {
Name = "nested1", Name = "nested1",
Properties = ValidProperties() Properties = new StringFieldProperties()
} }
} }
} }
@ -355,9 +336,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas.Guards
Name = "new-schema" 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] [Fact]
public Task CanCreate_should_not_throw_exception_if_command_is_valid() public Task CanCreate_should_not_throw_exception_if_command_is_valid()

Loading…
Cancel
Save