diff --git a/Squidex.sln b/Squidex.sln index fd8cc9914..81f85c0f9 100644 --- a/Squidex.sln +++ b/Squidex.sln @@ -8,8 +8,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "infrastructure", "infrastru EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "domain", "domain", "{4C6B06C2-6D77-4E0E-AE32-D7050236433A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core", "src\Squidex.Domain.Apps.Core\Squidex.Domain.Apps.Core.csproj", "{47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Infrastructure", "src\Squidex.Infrastructure\Squidex.Infrastructure.csproj", "{BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Events", "src\Squidex.Domain.Apps.Events\Squidex.Domain.Apps.Events.csproj", "{25F66C64-058A-4D44-BC0C-F12A054F9A91}" @@ -65,9 +63,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution stylecop.json = stylecop.json EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Domain.Apps.Core.Model", "src\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj", "{F0A83301-50A5-40EA-A1A2-07C7858F5A3F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Model", "src\Squidex.Domain.Apps.Core.Model\Squidex.Domain.Apps.Core.Model.csproj", "{F0A83301-50A5-40EA-A1A2-07C7858F5A3F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Squidex.Domain.Apps.Core.Operations", "src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj", "{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.Domain.Apps.Core.Operations", "src\Squidex.Domain.Apps.Core.Operations\Squidex.Domain.Apps.Core.Operations.csproj", "{6B3F75B6-5888-468E-BA4F-4FC725DAEF31}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -87,14 +85,6 @@ Global {61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|Any CPU.Build.0 = Release|Any CPU {61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|x64.ActiveCfg = Release|Any CPU {61F6BBCE-A080-4400-B194-70E2F5D2096E}.Release|x86.ActiveCfg = Release|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|x64.ActiveCfg = Debug|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Debug|x86.ActiveCfg = Debug|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|Any CPU.Build.0 = Release|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|x64.ActiveCfg = Release|Any CPU - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0}.Release|x86.ActiveCfg = Release|Any CPU {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|Any CPU.Build.0 = Debug|Any CPU {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -332,7 +322,6 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {47F3C27E-698B-4EDF-A7E8-D7F4232AFBB0} = {C9809D59-6665-471E-AD87-5AC624C65892} {BD1C30A8-8FFA-4A92-A9BD-B67B1CDDD84C} = {8CF53B92-5EB1-461D-98F8-70DA9B603FBF} {25F66C64-058A-4D44-BC0C-F12A054F9A91} = {C9809D59-6665-471E-AD87-5AC624C65892} {A85201C6-6AF8-4B63-8365-08F741050438} = {C9809D59-6665-471E-AD87-5AC624C65892} diff --git a/src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs b/src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs new file mode 100644 index 000000000..a7c70286c --- /dev/null +++ b/src/Squidex.Domain.Apps.Core.Model/PartitioningExtensions.cs @@ -0,0 +1,27 @@ +// ========================================================================== +// PartitioningExtensions.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; + +namespace Squidex.Domain.Apps.Core +{ + public static class PartitioningExtensions + { + private static readonly HashSet AllowedPartitions = new HashSet(StringComparer.OrdinalIgnoreCase) + { + Partitioning.Language.Key, + Partitioning.Invariant.Key + }; + + public static bool IsValidPartitioning(this string value) + { + return value == null || AllowedPartitions.Contains(value); + } + } +} diff --git a/src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs b/src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs new file mode 100644 index 000000000..7e4442977 --- /dev/null +++ b/src/Squidex.Domain.Apps.Events/Apps/Utils/AppEventDispatcher.cs @@ -0,0 +1,72 @@ +// ========================================================================== +// MongoAppRepository_EventHandling.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Apps; + +namespace Squidex.Domain.Apps.Events.Apps.Utils +{ + public static class AppEventDispatcher + { + public static void Apply(this AppContributors contributors, AppContributorRemoved @event) + { + contributors.Remove(@event.ContributorId); + } + + public static void Apply(this AppContributors contributors, AppContributorAssigned @event) + { + contributors.Assign(@event.ContributorId, @event.Permission); + } + + public static void Apply(this LanguagesConfig languagesConfig, AppLanguageAdded @event) + { + languagesConfig.Set(new LanguageConfig(@event.Language)); + } + + public static void Apply(this LanguagesConfig languagesConfig, AppLanguageRemoved @event) + { + languagesConfig.Remove(@event.Language); + } + + public static void Apply(this AppClients clients, AppClientAttached @event) + { + clients.Add(@event.Id, @event.Secret); + } + + public static void Apply(this AppClients clients, AppClientRevoked @event) + { + clients.Revoke(@event.Id); + } + + public static void Apply(this AppClients clients, AppClientRenamed @event) + { + if (clients.Clients.TryGetValue(@event.Id, out var client)) + { + client.Rename(@event.Name); + } + } + + public static void Apply(this AppClients clients, AppClientUpdated @event) + { + if (clients.Clients.TryGetValue(@event.Id, out var client)) + { + client.Update(@event.Permission); + } + } + + public static void Apply(this LanguagesConfig languagesConfig, AppLanguageUpdated @event) + { + languagesConfig.Set(new LanguageConfig(@event.Language, @event.IsOptional, @event.Fallback)); + + if (@event.IsMaster) + { + languagesConfig.MakeMaster(@event.Language); + } + } + } +} diff --git a/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs b/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs index 924048c21..0ff340140 100644 --- a/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs +++ b/src/Squidex.Domain.Apps.Events/Schemas/Utils/SchemaEventDispatcher.cs @@ -14,9 +14,14 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils { public static class SchemaEventDispatcher { - public static Schema Dispatch(SchemaCreated @event, FieldRegistry registry) + public static Schema Create(SchemaCreated @event, FieldRegistry registry) { - var schema = Schema.Create(@event.Name, @event.Properties); + var schema = new Schema(@event.Name); + + if (@event.Properties != null) + { + schema.Update(@event.Properties); + } if (@event.Fields != null) { @@ -33,20 +38,20 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils if (eventField.IsHidden) { - field = field.Hide(); + field.Hide(); } if (eventField.IsDisabled) { - field = field.Disable(); + field.Disable(); } if (eventField.IsLocked) { - field = field.Lock(); + field.Lock(); } - schema = schema.AddField(field); + schema.AddField(field); fieldId++; } @@ -55,7 +60,7 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils return schema; } - public static Schema Dispatch(FieldAdded @event, Schema schema, FieldRegistry registry) + public static void Apply(this Schema schema, FieldAdded @event, FieldRegistry registry) { var partitioning = string.Equals(@event.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ? @@ -65,69 +70,81 @@ namespace Squidex.Domain.Apps.Events.Schemas.Utils var fieldId = @event.FieldId.Id; var field = registry.CreateField(fieldId, @event.Name, partitioning, @event.Properties); - if (schema.FieldsById.ContainsKey(fieldId)) - { - return schema.UpdateField(fieldId, f => field); - } - else - { - return schema.AddField(field); - } + schema.DeleteField(fieldId); + schema.AddField(field); } - public static Schema Dispatch(FieldUpdated @event, Schema schema) + public static void Apply(this Schema schema, FieldUpdated @event) { - return schema.UpdateField(@event.FieldId.Id, @event.Properties); + if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) + { + field.Update(@event.Properties); + } } - public static Schema Dispatch(FieldLocked @event, Schema schema) + public static void Apply(this Schema schema, FieldLocked @event) { - return schema.LockField(@event.FieldId.Id); + if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) + { + field.Lock(); + } } - public static Schema Dispatch(FieldHidden @event, Schema schema) + public static void Apply(this Schema schema, FieldHidden @event) { - return schema.HideField(@event.FieldId.Id); + if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) + { + field.Hide(); + } } - public static Schema Dispatch(FieldShown @event, Schema schema) + public static void Apply(this Schema schema, FieldShown @event) { - return schema.ShowField(@event.FieldId.Id); + if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) + { + field.Show(); + } } - public static Schema Dispatch(FieldDisabled @event, Schema schema) + public static void Apply(this Schema schema, FieldDisabled @event) { - return schema.DisableField(@event.FieldId.Id); + if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) + { + field.Disable(); + } } - public static Schema Dispatch(FieldEnabled @event, Schema schema) + public static void Apply(this Schema schema, FieldEnabled @event) { - return schema.EnableField(@event.FieldId.Id); + if (schema.FieldsById.TryGetValue(@event.FieldId.Id, out var field)) + { + field.Enable(); + } } - public static Schema Dispatch(SchemaUpdated @event, Schema schema) + public static void Apply(this Schema schema, SchemaUpdated @event) { - return schema.Update(@event.Properties); + schema.Update(@event.Properties); } - public static Schema Dispatch(SchemaFieldsReordered @event, Schema schema) + public static void Apply(this Schema schema, SchemaFieldsReordered @event) { - return schema.ReorderFields(@event.FieldIds); + schema.ReorderFields(@event.FieldIds); } - public static Schema Dispatch(FieldDeleted @event, Schema schema) + public static void Apply(this Schema schema, FieldDeleted @event) { - return schema.DeleteField(@event.FieldId.Id); + schema.DeleteField(@event.FieldId.Id); } - public static Schema Dispatch(SchemaPublished @event, Schema schema) + public static void Apply(this Schema schema, SchemaPublished @event) { - return schema.Publish(); + schema.Publish(); } - public static Schema Dispatch(SchemaUnpublished @event, Schema schema) + public static void Apply(this Schema schema, SchemaUnpublished @event) { - return schema.Unpublish(); + schema.Unpublish(); } } } diff --git a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj index af764763d..08e97944f 100644 --- a/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj +++ b/src/Squidex.Domain.Apps.Events/Squidex.Domain.Apps.Events.csproj @@ -7,7 +7,7 @@ True - + diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs index d2cff61c2..7810f2814 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntity.cs @@ -6,11 +6,9 @@ // All rights reserved. // ========================================================================== -using System; -using System.Collections.Generic; -using System.Linq; using MongoDB.Bson.Serialization.Attributes; using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Read.Apps; using Squidex.Infrastructure; using Squidex.Infrastructure.MongoDb; @@ -19,10 +17,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { public sealed class MongoAppEntity : MongoEntity, IAppEntity { - private readonly IReadOnlyDictionary clientWrapper; - private readonly IReadOnlyDictionary contributorWrapper; - private LanguagesConfig languagesConfig; - [BsonRequired] [BsonElement] public string Name { get; set; } @@ -39,93 +33,28 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps [BsonElement] public string PlanOwner { get; set; } - [BsonRequired] - [BsonElement] - public string MasterLanguage { get; set; } - - [BsonRequired] + [BsonIgnoreIfDefault] [BsonElement] - public List ContributorIds { get; set; } + public string[] ContributorIds { get; set; } [BsonRequired] [BsonElement] - public List Languages { get; set; } + [BsonSerializer(typeof(JsonBsonSerializer))] + public AppClients Clients { get; set; } = new AppClients(); [BsonRequired] [BsonElement] - public Dictionary Clients { get; set; } + [BsonSerializer(typeof(JsonBsonSerializer))] + public AppContributors Contributors { get; set; } = new AppContributors(); [BsonRequired] [BsonElement] - public Dictionary Contributors { get; set; } + [BsonSerializer(typeof(JsonBsonSerializer))] + public LanguagesConfig LanguagesConfig { get; } = LanguagesConfig.Build(Language.EN); public PartitionResolver PartitionResolver { get { return LanguagesConfig.ToResolver(); } } - - public LanguagesConfig LanguagesConfig - { - get { return languagesConfig ?? (languagesConfig = CreateLanguagesConfig()); } - } - - IReadOnlyDictionary IAppEntity.Clients - { - get { return clientWrapper; } - } - - IReadOnlyDictionary IAppEntity.Contributors - { - get { return contributorWrapper; } - } - - public MongoAppEntity() - { - clientWrapper = new DictionaryWrapper(() => Clients); - - contributorWrapper = new DictionaryWrapper(() => Contributors); - } - - public void ChangePlan(string planId, RefToken planOwner) - { - PlanId = planId; - - PlanOwner = planOwner.Identifier; - } - - public void UpdateLanguages(Func updater) - { - var newConfig = updater(LanguagesConfig); - - if (languagesConfig != newConfig) - { - languagesConfig = newConfig; - Languages = newConfig.OfType().Select(FromLanguageConfig).ToList(); - - MasterLanguage = newConfig.Master.Language; - } - } - - private LanguagesConfig CreateLanguagesConfig() - { - languagesConfig = LanguagesConfig.Create(Languages?.Select(ToLanguageConfig).ToList() ?? new List()); - - if (MasterLanguage != null) - { - languagesConfig = languagesConfig.MakeMaster(MasterLanguage); - } - - return languagesConfig; - } - - private static MongoAppEntityLanguage FromLanguageConfig(LanguageConfig l) - { - return new MongoAppEntityLanguage { Iso2Code = l.Language, IsOptional = l.IsOptional, Fallback = l.LanguageFallbacks.Select(x => x.Iso2Code).ToList() }; - } - - private static LanguageConfig ToLanguageConfig(MongoAppEntityLanguage l) - { - return new LanguageConfig(l.Iso2Code, l.IsOptional, l.Fallback?.Select(f => f)); - } } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityClient.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityClient.cs deleted file mode 100644 index fde8176cf..000000000 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityClient.cs +++ /dev/null @@ -1,43 +0,0 @@ -// ========================================================================== -// MongoAppEntityClient.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using MongoDB.Bson.Serialization.Attributes; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Read.Apps; - -namespace Squidex.Domain.Apps.Read.MongoDb.Apps -{ - public sealed class MongoAppEntityClient : IAppClientEntity - { - [BsonRequired] - [BsonElement] - public string Id { get; set; } - - [BsonRequired] - [BsonElement] - public string Secret { get; set; } - - [BsonRequired] - [BsonElement] - public string Name { get; set; } - - [BsonRequired] - [BsonElement] - public AppClientPermission Permission { get; set; } - - string IAppClientEntity.Name - { - get { return !string.IsNullOrWhiteSpace(Name) ? Name : Id; } - } - - public MongoAppEntityClient() - { - Permission = AppClientPermission.Editor; - } - } -} diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityContributor.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityContributor.cs deleted file mode 100644 index 8d4181bfe..000000000 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityContributor.cs +++ /dev/null @@ -1,21 +0,0 @@ -// ========================================================================== -// MongoAppEntityContributor.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using MongoDB.Bson.Serialization.Attributes; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Read.Apps; - -namespace Squidex.Domain.Apps.Read.MongoDb.Apps -{ - public sealed class MongoAppEntityContributor : IAppContributorEntity - { - [BsonRequired] - [BsonElement] - public AppContributorPermission Permission { get; set; } - } -} diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityLanguage.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityLanguage.cs deleted file mode 100644 index ecdf43f98..000000000 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppEntityLanguage.cs +++ /dev/null @@ -1,28 +0,0 @@ -// ========================================================================== -// MongoAppEntityLanguage.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System.Collections.Generic; -using MongoDB.Bson.Serialization.Attributes; - -namespace Squidex.Domain.Apps.Read.MongoDb.Apps -{ - public sealed class MongoAppEntityLanguage - { - [BsonRequired] - [BsonElement] - public string Iso2Code { get; set; } - - [BsonRequired] - [BsonElement] - public bool IsOptional { get; set; } - - [BsonRequired] - [BsonElement] - public List Fallback { get; set; } - } -} diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs index 0b897fada..aabfa2ebd 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository.cs @@ -8,8 +8,11 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using MongoDB.Bson.Serialization; using MongoDB.Driver; +using Newtonsoft.Json; using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps.Repositories; using Squidex.Infrastructure.CQRS.Events; @@ -19,9 +22,10 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { public partial class MongoAppRepository : MongoRepositoryBase, IAppRepository, IEventConsumer { - public MongoAppRepository(IMongoDatabase database) + public MongoAppRepository(IMongoDatabase database, JsonSerializer serializer) : base(database) { + BsonSerializer.RegisterSerializer(new JsonBsonSerializer(serializer)); } protected override string CollectionName() diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs index 22166ec3f..d4d22106b 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Apps/MongoAppRepository_EventHandling.cs @@ -7,13 +7,12 @@ // ========================================================================== using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Apps; +using Squidex.Domain.Apps.Events.Apps.Utils; using Squidex.Domain.Apps.Read.MongoDb.Utils; -using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Events; using Squidex.Infrastructure.Dispatching; using Squidex.Infrastructure.Reflection; @@ -41,10 +40,14 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return Collection.CreateAsync(@event, headers, a => { - a.Clients = new Dictionary(); - a.Contributors = new Dictionary(); - a.ContributorIds = new List(); + SimpleMapper.Map(@event, a); + }); + } + protected Task On(AppPlanChanged @event, EnvelopeHeaders headers) + { + return UpdateAppAsync(@event, headers, a => + { SimpleMapper.Map(@event, a); }); } @@ -53,7 +56,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.Clients[@event.Id] = SimpleMapper.Map(@event, new MongoAppEntityClient()); + a.Clients.Apply(@event); }); } @@ -61,7 +64,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.Clients.Remove(@event.Id); + a.Clients.Apply(@event); }); } @@ -69,7 +72,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.Clients[@event.Id].Name = @event.Name; + a.Clients.Apply(@event); }); } @@ -77,7 +80,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.Clients[@event.Id].Permission = @event.Permission; + a.Clients.Apply(@event); }); } @@ -85,7 +88,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.Contributors.Remove(@event.ContributorId); + a.Contributors.Apply(@event); }); } @@ -93,7 +96,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.Contributors[@event.ContributorId] = new MongoAppEntityContributor { Permission = @event.Permission }; + a.Contributors.Apply(@event); }); } @@ -101,7 +104,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.UpdateLanguages(c => c.Add(@event.Language)); + a.LanguagesConfig.Apply(@event); }); } @@ -109,7 +112,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.UpdateLanguages(c => c.Remove(@event.Language)); + a.LanguagesConfig.Apply(@event); }); } @@ -117,15 +120,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { return UpdateAppAsync(@event, headers, a => { - a.UpdateLanguages(c => c.Update(@event.Language, @event.IsOptional, @event.IsMaster, @event.Fallback)); - }); - } - - protected Task On(AppPlanChanged @event, EnvelopeHeaders headers) - { - return UpdateAppAsync(@event, headers, a => - { - a.ChangePlan(@event.PlanId, @event.Actor); + a.LanguagesConfig.Apply(@event); }); } @@ -135,7 +130,7 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Apps { updater(a); - a.ContributorIds = a.Contributors.Keys.ToList(); + a.ContributorIds = a.Contributors.Contributors.Keys.ToArray(); }); } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs index 9a5d1ee42..bd8e91547 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Extensions.cs @@ -14,6 +14,8 @@ using MongoDB.Bson; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Core.ConvertContent; +using Squidex.Domain.Apps.Core.ExtractReferenceIds; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure.MongoDb; diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs index e6e122a46..61900dd4f 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/MongoContentRepository_EventHandling.cs @@ -9,6 +9,7 @@ using System; using System.Threading.Tasks; using MongoDB.Driver; +using Squidex.Domain.Apps.Core.ConvertContent; using Squidex.Domain.Apps.Events.Apps; using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Contents; diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs index 37d93bebc..36ddad63c 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Contents/Visitors/PropertyVisitor.cs @@ -11,8 +11,8 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.OData.UriParser; using MongoDB.Driver; +using Squidex.Domain.Apps.Core.GenerateEdmSchema; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.Schemas.Edm; namespace Squidex.Domain.Apps.Read.MongoDb.Contents.Visitors { diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs index bedbbbbaa..bce8e652d 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaEntity.cs @@ -9,8 +9,6 @@ using System; using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Read.Schemas; using Squidex.Infrastructure; @@ -20,16 +18,10 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas { public sealed class MongoSchemaEntity : MongoEntity, ISchemaEntity { - private Lazy schema; - [BsonRequired] [BsonElement] public string Name { get; set; } - [BsonRequired] - [BsonElement] - public BsonDocument SchemaDocument { get; set; } - [BsonRequired] [BsonElement] public long Version { get; set; } @@ -74,32 +66,9 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas [BsonElement] public string ScriptChange { get; set; } - Schema ISchemaEntity.SchemaDef - { - get { return schema.Value; } - } - - public void SerializeSchema(Schema newSchema, JsonSerializer serializer) - { - SchemaDocument = JObject.FromObject(newSchema, serializer).ToBson(); - schema = new Lazy(() => newSchema); - - IsPublished = newSchema.IsPublished; - } - - public void UpdateSchema(JsonSerializer serializer, Func updater) - { - DeserializeSchema(serializer); - - SerializeSchema(updater(schema.Value), serializer); - } - - public void DeserializeSchema(JsonSerializer serializer) - { - if (schema == null) - { - schema = new Lazy(() => schema != null ? SchemaDocument.ToJson().ToObject(serializer) : null); - } - } + [BsonRequired] + [BsonElement] + [BsonSerializer(typeof(JsonBsonSerializer))] + public Schema SchemaDef { get; set; } } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs index 5e93dbcbc..a4d8ac949 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using MongoDB.Bson.Serialization; using MongoDB.Driver; using Newtonsoft.Json; using Squidex.Domain.Apps.Core.Schemas; @@ -23,17 +24,16 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas { public partial class MongoSchemaRepository : MongoRepositoryBase, ISchemaRepository, IEventConsumer { - private readonly JsonSerializer serializer; private readonly FieldRegistry registry; public MongoSchemaRepository(IMongoDatabase database, JsonSerializer serializer, FieldRegistry registry) : base(database) { Guard.NotNull(registry, nameof(registry)); - Guard.NotNull(serializer, nameof(serializer)); this.registry = registry; - this.serializer = serializer; + + BsonSerializer.RegisterSerializer(new JsonBsonSerializer(serializer)); } protected override string CollectionName() @@ -54,8 +54,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas await Collection.Find(s => s.AppId == appId && !s.IsDeleted) .ToListAsync(); - schemaEntities.ForEach(x => x.DeserializeSchema(serializer)); - return schemaEntities.OfType().ToList(); } @@ -65,8 +63,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas await Collection.Find(s => s.AppId == appId && !s.IsDeleted && s.Name == name) .FirstOrDefaultAsync(); - schemaEntity?.DeserializeSchema(serializer); - return schemaEntity; } @@ -76,8 +72,6 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas await Collection.Find(s => s.Id == schemaId) .FirstOrDefaultAsync(); - schemaEntity?.DeserializeSchema(serializer); - return schemaEntity; } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs index 8499416d8..b4a662186 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Schemas/MongoSchemaRepository_EventHandling.cs @@ -8,7 +8,6 @@ using System; using System.Threading.Tasks; -using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas.Old; @@ -41,104 +40,150 @@ namespace Squidex.Domain.Apps.Read.MongoDb.Schemas protected Task On(SchemaCreated @event, EnvelopeHeaders headers) { - var schema = SchemaEventDispatcher.Dispatch(@event, registry); + return Collection.CreateAsync(@event, headers, s => + { + s.SchemaDef = SchemaEventDispatcher.Create(@event, registry); - return Collection.CreateAsync(@event, headers, s => { UpdateSchema(s, schema); SimpleMapper.Map(@event, s); }); + SimpleMapper.Map(@event, s); + }); } protected Task On(FieldDeleted @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(FieldLocked @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(FieldHidden @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(FieldShown @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(FieldDisabled @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(FieldEnabled @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(FieldUpdated @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(SchemaFieldsReordered @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(SchemaUpdated @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(SchemaPublished @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(SchemaUnpublished @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event); + }); } protected Task On(FieldAdded @event, EnvelopeHeaders headers) { - return UpdateSchema(@event, headers, s => SchemaEventDispatcher.Dispatch(@event, s, registry)); + return UpdateSchemaAsync(@event, headers, s => + { + s.SchemaDef.Apply(@event, registry); + }); } protected Task On(ScriptsConfigured @event, EnvelopeHeaders headers) { - return Collection.UpdateAsync(@event, headers, e => SimpleMapper.Map(@event, e)); + return Collection.UpdateAsync(@event, headers, s => + { + SimpleMapper.Map(@event, s); + }); } protected Task On(SchemaDeleted @event, EnvelopeHeaders headers) { - return Collection.UpdateAsync(@event, headers, e => e.IsDeleted = true); + return Collection.UpdateAsync(@event, headers, s => + { + s.IsDeleted = true; + }); } - private Task UpdateSchema(SquidexEvent @event, EnvelopeHeaders headers, Func updater) + protected Task On(WebhookAdded @event, EnvelopeHeaders headers) { - return Collection.UpdateAsync(@event, headers, e => UpdateSchema(e, updater)); + return Collection.UpdateAsync(@event, headers, s => + { + /* NOOP */ + }); } - private void UpdateSchema(MongoSchemaEntity entity, Func updater) + protected Task On(WebhookDeleted @event, EnvelopeHeaders headers) { - entity.UpdateSchema(serializer, updater); + return Collection.UpdateAsync(@event, headers, s => + { + /* NOOP */ + }); } - private void UpdateSchema(MongoSchemaEntity entity, Schema schema) + private Task UpdateSchemaAsync(SquidexEvent @event, EnvelopeHeaders headers, Action updater) { - entity.SerializeSchema(schema, serializer); - } + return Collection.UpdateAsync(@event, headers, s => + { + updater(s); - protected Task On(WebhookAdded @event, EnvelopeHeaders headers) - { - return Collection.UpdateAsync(@event, headers, e => { }); - } - - protected Task On(WebhookDeleted @event, EnvelopeHeaders headers) - { - return Collection.UpdateAsync(@event, headers, e => { }); + s.IsPublished = s.SchemaDef.IsPublished; + }); } } } diff --git a/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj b/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj index b32facd0e..3040f8ead 100644 --- a/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj +++ b/src/Squidex.Domain.Apps.Read.MongoDb/Squidex.Domain.Apps.Read.MongoDb.csproj @@ -7,7 +7,8 @@ True - + + diff --git a/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs b/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs index 8c59f8628..0eaba9695 100644 --- a/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs +++ b/src/Squidex.Domain.Apps.Read/Apps/IAppEntity.cs @@ -6,8 +6,8 @@ // All rights reserved. // ========================================================================== -using System.Collections.Generic; using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Apps; namespace Squidex.Domain.Apps.Read.Apps { @@ -19,11 +19,11 @@ namespace Squidex.Domain.Apps.Read.Apps string PlanOwner { get; } - LanguagesConfig LanguagesConfig { get; } + AppClients Clients { get; } - IReadOnlyDictionary Clients { get; } + AppContributors Contributors { get; } - IReadOnlyDictionary Contributors { get; } + LanguagesConfig LanguagesConfig { get; } PartitionResolver PartitionResolver { get; } } diff --git a/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs b/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs index 829cb650e..14fa93c78 100644 --- a/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs +++ b/src/Squidex.Domain.Apps.Read/Contents/Edm/EdmModelBuilder.cs @@ -10,8 +10,8 @@ using System; using Microsoft.Extensions.Caching.Memory; using Microsoft.OData.Edm; using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.GenerateEdmSchema; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.Schemas.Edm; using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Schemas; using Squidex.Domain.Apps.Read.Utils; diff --git a/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj b/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj index ca4434061..17670622a 100644 --- a/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj +++ b/src/Squidex.Domain.Apps.Read/Squidex.Domain.Apps.Read.csproj @@ -7,7 +7,8 @@ True - + + diff --git a/src/Squidex.Domain.Apps.Write/Apps/AppClient.cs b/src/Squidex.Domain.Apps.Write/Apps/AppClient.cs deleted file mode 100644 index b16d46982..000000000 --- a/src/Squidex.Domain.Apps.Write/Apps/AppClient.cs +++ /dev/null @@ -1,56 +0,0 @@ -// ========================================================================== -// AppClient.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Write.Apps -{ - public sealed class AppClient - { - private readonly string name; - private readonly string secret; - private readonly AppClientPermission permission; - - public AppClient(string secret, string name, AppClientPermission permission) - { - Guard.NotNullOrEmpty(name, nameof(name)); - Guard.NotNullOrEmpty(secret, nameof(secret)); - Guard.Enum(permission, nameof(permission)); - - this.name = name; - this.secret = secret; - this.permission = permission; - } - - public AppClient Update(AppClientPermission newPermission, Func message) - { - if (permission == newPermission) - { - var error = new ValidationError("Client has already the permission.", "IsReader"); - - throw new ValidationException(message(), error); - } - - return new AppClient(secret, name, newPermission); - } - - public AppClient Rename(string newName, Func message) - { - if (string.Equals(name, newName)) - { - var error = new ValidationError("Client already has the name.", "Id"); - - throw new ValidationException(message(), error); - } - - return new AppClient(secret, newName, permission); - } - } -} diff --git a/src/Squidex.Domain.Apps.Write/Apps/AppClients.cs b/src/Squidex.Domain.Apps.Write/Apps/AppClients.cs deleted file mode 100644 index 0e5e36e43..000000000 --- a/src/Squidex.Domain.Apps.Write/Apps/AppClients.cs +++ /dev/null @@ -1,66 +0,0 @@ -// ========================================================================== -// AppClients.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Collections.Generic; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Write.Apps -{ - public class AppClients - { - private readonly Dictionary clients = new Dictionary(); - - public void Add(string id, string secret) - { - ThrowIfFound(id, () => "Cannot add client"); - - clients[id] = new AppClient(secret, id, AppClientPermission.Editor); - } - - public void Rename(string clientId, string name) - { - ThrowIfNotFound(clientId); - - clients[clientId] = clients[clientId].Rename(name, () => "Cannot rename client"); - } - - public void Update(string clientId, AppClientPermission permission) - { - ThrowIfNotFound(clientId); - - clients[clientId] = clients[clientId].Update(permission, () => "Cannot update client"); - } - - public void Revoke(string clientId) - { - ThrowIfNotFound(clientId); - - clients.Remove(clientId); - } - - private void ThrowIfNotFound(string clientId) - { - if (!clients.ContainsKey(clientId)) - { - throw new DomainObjectNotFoundException(clientId, "Contributors", typeof(AppDomainObject)); - } - } - - private void ThrowIfFound(string clientId, Func message) - { - if (clients.ContainsKey(clientId)) - { - var error = new ValidationError("Client id is alreay part of the app.", "Id"); - - throw new ValidationException(message(), error); - } - } - } -} diff --git a/src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs b/src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs deleted file mode 100644 index 9d29f5e4c..000000000 --- a/src/Squidex.Domain.Apps.Write/Apps/AppContributors.cs +++ /dev/null @@ -1,78 +0,0 @@ -// ========================================================================== -// AppContributors.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Linq; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Write.Apps -{ - public class AppContributors - { - private readonly Dictionary contributors = new Dictionary(); - - public int Count - { - get { return contributors.Count; } - } - - public void Assign(string contributorId, AppContributorPermission permission) - { - string Message() => "Cannot assign contributor"; - - ThrowIfFound(contributorId, permission, Message); - ThrowIfNoOwner(c => c[contributorId] = permission, Message); - - contributors[contributorId] = permission; - } - - public void Remove(string contributorId) - { - string Message() => "Cannot remove contributor"; - - ThrowIfNotFound(contributorId); - ThrowIfNoOwner(c => c.Remove(contributorId), Message); - - contributors.Remove(contributorId); - } - - private void ThrowIfNotFound(string contributorId) - { - if (!contributors.ContainsKey(contributorId)) - { - throw new DomainObjectNotFoundException(contributorId, "Contributors", typeof(AppDomainObject)); - } - } - - private void ThrowIfFound(string contributorId, AppContributorPermission permission, Func message) - { - if (contributors.TryGetValue(contributorId, out var currentPermission) && currentPermission == permission) - { - var error = new ValidationError("Contributor is already part of the app with same permissions.", "ContributorId"); - - throw new ValidationException(message(), error); - } - } - - private void ThrowIfNoOwner(Action> change, Func message) - { - var contributorsCopy = new Dictionary(contributors); - - change(contributorsCopy); - - if (contributorsCopy.All(x => x.Value != AppContributorPermission.Owner)) - { - var error = new ValidationError("Contributor is the last owner.", "ContributorId"); - - throw new ValidationException(message(), error); - } - } - } -} diff --git a/src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs b/src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs index 4c217c29c..46a5b486c 100644 --- a/src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs +++ b/src/Squidex.Domain.Apps.Write/Apps/AppDomainObject.cs @@ -11,6 +11,7 @@ using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Events; using Squidex.Domain.Apps.Events.Apps; +using Squidex.Domain.Apps.Events.Apps.Utils; using Squidex.Domain.Apps.Write.Apps.Commands; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS; @@ -25,7 +26,7 @@ namespace Squidex.Domain.Apps.Write.Apps private static readonly Language DefaultLanguage = Language.EN; private readonly AppContributors contributors = new AppContributors(); private readonly AppClients clients = new AppClients(); - private LanguagesConfig languagesConfig = LanguagesConfig.Empty; + private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(DefaultLanguage); private string name; private string planId; private RefToken planOwner; @@ -42,7 +43,7 @@ namespace Squidex.Domain.Apps.Write.Apps public int ContributorCount { - get { return contributors.Count; } + get { return contributors.Contributors.Count; } } public AppDomainObject(Guid id, int version) @@ -57,47 +58,47 @@ namespace Squidex.Domain.Apps.Write.Apps protected void On(AppContributorAssigned @event) { - contributors.Assign(@event.ContributorId, @event.Permission); + contributors.Apply(@event); } protected void On(AppContributorRemoved @event) { - contributors.Remove(@event.ContributorId); + contributors.Apply(@event); } protected void On(AppClientAttached @event) { - clients.Add(@event.Id, @event.Secret); + clients.Apply(@event); } protected void On(AppClientUpdated @event) { - clients.Update(@event.Id, @event.Permission); + clients.Apply(@event); } protected void On(AppClientRenamed @event) { - clients.Rename(@event.Id, @event.Name); + clients.Apply(@event); } protected void On(AppClientRevoked @event) { - clients.Revoke(@event.Id); + clients.Apply(@event); } protected void On(AppLanguageAdded @event) { - languagesConfig = languagesConfig.Add(@event.Language); + languagesConfig.Apply(@event); } protected void On(AppLanguageRemoved @event) { - languagesConfig = languagesConfig.Remove(@event.Language); + languagesConfig.Apply(@event); } protected void On(AppLanguageUpdated @event) { - languagesConfig = languagesConfig.Update(@event.Language, @event.IsOptional, @event.IsMaster, @event.Fallback); + languagesConfig.Apply(@event); } protected void On(AppPlanChanged @event) diff --git a/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentDataCommand.cs b/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentDataCommand.cs index d80886ed9..02c147574 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentDataCommand.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/Commands/ContentDataCommand.cs @@ -6,22 +6,12 @@ // All rights reserved. // ========================================================================== -using System.Collections.Generic; using Squidex.Domain.Apps.Core.Contents; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Write.Contents.Commands { - public abstract class ContentDataCommand : ContentCommand, IValidatable + public abstract class ContentDataCommand : ContentCommand { public NamedContentData Data { get; set; } - - public void Validate(IList errors) - { - if (Data == null) - { - errors.Add(new ValidationError("Data cannot be null.", nameof(Data))); - } - } } } diff --git a/src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs b/src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs index bbab13583..3065c0202 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/Commands/CreateContent.cs @@ -6,17 +6,10 @@ // All rights reserved. // ========================================================================== -using System; - namespace Squidex.Domain.Apps.Write.Contents.Commands { public sealed class CreateContent : ContentDataCommand { public bool Publish { get; set; } - - public CreateContent() - { - ContentId = Guid.NewGuid(); - } } } diff --git a/src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs b/src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs index ff38a5638..410315879 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/Commands/PatchContent.cs @@ -6,6 +6,8 @@ // All rights reserved. // ========================================================================== +using Squidex.Domain.Apps.Core.Contents; + namespace Squidex.Domain.Apps.Write.Contents.Commands { public sealed class PatchContent : ContentDataCommand diff --git a/src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs b/src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs index be9546173..47d6128ad 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/Commands/UpdateContent.cs @@ -6,6 +6,8 @@ // All rights reserved. // ========================================================================== +using Squidex.Domain.Apps.Core.Contents; + namespace Squidex.Domain.Apps.Write.Contents.Commands { public sealed class UpdateContent : ContentDataCommand diff --git a/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs b/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs index 934758bfd..810eb7b8e 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/ContentCommandMiddleware.cs @@ -10,9 +10,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Squidex.Domain.Apps.Core; -using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Core.EnrichContent; using Squidex.Domain.Apps.Core.Scripting; +using Squidex.Domain.Apps.Core.ValidateContent; using Squidex.Domain.Apps.Read.Apps; using Squidex.Domain.Apps.Read.Apps.Services; using Squidex.Domain.Apps.Read.Assets.Repositories; @@ -147,8 +147,6 @@ namespace Squidex.Domain.Apps.Write.Contents private async Task ValidateAsync((ISchemaEntity Schema, IAppEntity App) schemaAndApp, ContentDataCommand command, Func message, bool partial) { - Guard.Valid(command, nameof(command), message); - var schemaErrors = new List(); var appId = command.AppId.Id; diff --git a/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs b/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs index a9fc44fb4..0857bd78e 100644 --- a/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs +++ b/src/Squidex.Domain.Apps.Write/Contents/ContentDomainObject.cs @@ -69,8 +69,6 @@ namespace Squidex.Domain.Apps.Write.Contents public ContentDomainObject Create(CreateContent command) { - Guard.Valid(command, nameof(command), () => "Cannot create content"); - VerifyNotCreated(); RaiseEvent(SimpleMapper.Map(command, new ContentCreated())); @@ -85,8 +83,6 @@ namespace Squidex.Domain.Apps.Write.Contents public ContentDomainObject Delete(DeleteContent command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); RaiseEvent(SimpleMapper.Map(command, new ContentDeleted())); @@ -96,10 +92,7 @@ namespace Squidex.Domain.Apps.Write.Contents public ContentDomainObject ChangeStatus(ChangeContentStatus command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - VerifyCanChangeStatus(command.Status); RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); @@ -108,8 +101,6 @@ namespace Squidex.Domain.Apps.Write.Contents public ContentDomainObject Update(UpdateContent command) { - Guard.Valid(command, nameof(command), () => "Cannot update content"); - VerifyCreatedAndNotDeleted(); if (!command.Data.Equals(Data)) @@ -122,8 +113,6 @@ namespace Squidex.Domain.Apps.Write.Contents public ContentDomainObject Patch(PatchContent command) { - Guard.Valid(command, nameof(command), () => "Cannot patch content"); - VerifyCreatedAndNotDeleted(); var newData = Data.MergeInto(command.Data); @@ -136,14 +125,6 @@ namespace Squidex.Domain.Apps.Write.Contents return this; } - private void VerifyCanChangeStatus(Status newStatus) - { - if (!StatusFlow.Exists(newStatus) || !StatusFlow.CanChange(status, newStatus)) - { - throw new DomainException($"Content cannot be changed from status {status} to {newStatus}."); - } - } - private void VerifyNotCreated() { if (isCreated) diff --git a/src/Squidex.Domain.Apps.Write/Contents/Guards/GuardContent.cs b/src/Squidex.Domain.Apps.Write/Contents/Guards/GuardContent.cs new file mode 100644 index 000000000..28b7736ce --- /dev/null +++ b/src/Squidex.Domain.Apps.Write/Contents/Guards/GuardContent.cs @@ -0,0 +1,69 @@ +// ========================================================================== +// GuardContent.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Domain.Apps.Core.Contents; +using Squidex.Domain.Apps.Write.Contents.Commands; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Write.Contents.Guards +{ + public static class GuardContent + { + public static void CanCreate(CreateContent command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot created content.", error => + { + if (command.Data == null) + { + error(new ValidationError("Data cannot be null.", nameof(command.Data))); + } + }); + } + + public static void CanCreate(UpdateContent command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot update content.", error => + { + if (command.Data == null) + { + error(new ValidationError("Data cannot be null.", nameof(command.Data))); + } + }); + } + + public static void CanCreate(PatchContent command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot patch content.", error => + { + if (command.Data == null) + { + error(new ValidationError("Data cannot be null.", nameof(command.Data))); + } + }); + } + + public static void CanChangeStatus(Status status, ChangeContentStatus command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot change status.", error => + { + if (!StatusFlow.Exists(command.Status) || !StatusFlow.CanChange(status, command.Status)) + { + error(new ValidationError($"Content cannot be changed from status {status} to {command.Status}.", nameof(command.Status))); + } + }); + } + } +} diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs b/src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs index d0e7b248a..8b78836df 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/Commands/AddField.cs @@ -6,47 +6,16 @@ // All rights reserved. // ========================================================================== -using System.Collections.Generic; -using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Write.Schemas.Guards; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Write.Schemas.Commands { - public sealed class AddField : SchemaAggregateCommand, IValidatable + public sealed class AddField : SchemaAggregateCommand { public string Name { get; set; } public string Partitioning { get; set; } public FieldProperties Properties { get; set; } - - public void Validate(IList errors) - { - if (!Partitioning.IsValidPartitioning()) - { - errors.Add(new ValidationError("Partitioning is not valid.", nameof(Partitioning))); - } - - if (!Name.IsPropertyName()) - { - errors.Add(new ValidationError("Name must be a valid property name.", nameof(Name))); - } - - if (Properties == null) - { - errors.Add(new ValidationError("Properties must be defined.", nameof(Properties))); - } - else - { - var propertyErrors = FieldPropertiesValidator.Validate(Properties); - - foreach (var error in propertyErrors) - { - errors.Add(error); - } - } - } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs b/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs index d2fa51a4f..a037119df 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchema.cs @@ -7,45 +7,19 @@ // ========================================================================== using System; -using System.Collections.Generic; -using System.Linq; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Commands; using SchemaFields = System.Collections.Generic.List; namespace Squidex.Domain.Apps.Write.Schemas.Commands { - public sealed class CreateSchema : AppCommand, IValidatable, IAggregateCommand + public sealed class CreateSchema : AppCommand, IAggregateCommand { - private SchemaProperties properties; - private SchemaFields fields; - public Guid SchemaId { get; set; } - public SchemaProperties Properties - { - get - { - return properties ?? (properties = new SchemaProperties()); - } - set - { - properties = value; - } - } + public SchemaFields Fields { get; set; } - public SchemaFields Fields - { - get - { - return fields ?? (fields = new SchemaFields()); - } - set - { - fields = value; - } - } + public SchemaProperties Properties { get; set; } public string Name { get; set; } @@ -58,28 +32,5 @@ namespace Squidex.Domain.Apps.Write.Schemas.Commands { SchemaId = Guid.NewGuid(); } - - public void Validate(IList errors) - { - if (!Name.IsSlug()) - { - errors.Add(new ValidationError("Name must be a valid slug.", nameof(Name))); - } - - if (Fields.Any()) - { - var index = 0; - - foreach (var field in Fields) - { - field.Validate(index++, errors); - } - - if (Fields.Select(x => x.Name).Distinct().Count() != Fields.Count) - { - errors.Add(new ValidationError("Fields cannot have duplicate names.", nameof(Fields))); - } - } - } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchemaField.cs b/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchemaField.cs index 45a9f3c2b..d532a0a76 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchemaField.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/Commands/CreateSchemaField.cs @@ -6,11 +6,7 @@ // All rights reserved. // ========================================================================== -using System.Collections.Generic; -using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Write.Schemas.Guards; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Write.Schemas.Commands { @@ -27,34 +23,5 @@ namespace Squidex.Domain.Apps.Write.Schemas.Commands public bool IsDisabled { get; set; } public FieldProperties Properties { get; set; } - - public void Validate(int index, IList errors) - { - var prefix = $"Fields.{index}"; - - if (!Partitioning.IsValidPartitioning()) - { - errors.Add(new ValidationError("Partitioning is not valid.", $"{prefix}.{nameof(Partitioning)}")); - } - - if (!Name.IsPropertyName()) - { - errors.Add(new ValidationError("Name must be a valid property name.", $"{prefix}.{nameof(Name)}")); - } - - if (Properties == null) - { - errors.Add(new ValidationError("Properties must be defined.", $"{prefix}.{nameof(Properties)}")); - } - else - { - var propertyErrors = FieldPropertiesValidator.Validate(Properties); - - foreach (var error in propertyErrors) - { - errors.Add(error); - } - } - } } } diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs b/src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs index c930b6f09..2d3805fb1 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/Commands/ReorderFields.cs @@ -7,20 +7,11 @@ // ========================================================================== using System.Collections.Generic; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Write.Schemas.Commands { - public sealed class ReorderFields : SchemaAggregateCommand, IValidatable + public sealed class ReorderFields : SchemaAggregateCommand { public List FieldIds { get; set; } - - public void Validate(IList errors) - { - if (FieldIds == null) - { - errors.Add(new ValidationError("Field ids must be specified.", nameof(FieldIds))); - } - } } } diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs b/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs index 1f302c132..1f70668cb 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateField.cs @@ -6,22 +6,12 @@ // All rights reserved. // ========================================================================== -using System.Collections.Generic; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Write.Schemas.Commands { - public sealed class UpdateField : FieldCommand, IValidatable + public sealed class UpdateField : FieldCommand { public FieldProperties Properties { get; set; } - - public void Validate(IList errors) - { - if (Properties == null) - { - errors.Add(new ValidationError("Properties must be defined.", nameof(Properties))); - } - } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs b/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs index 4cfa1ebc3..ba711a5a0 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/Commands/UpdateSchema.cs @@ -6,22 +6,12 @@ // All rights reserved. // ========================================================================== -using System.Collections.Generic; using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Write.Schemas.Commands { - public sealed class UpdateSchema : SchemaAggregateCommand, IValidatable + public sealed class UpdateSchema : SchemaAggregateCommand { public SchemaProperties Properties { get; set; } - - public void Validate(IList errors) - { - if (Properties == null) - { - errors.Add(new ValidationError("Properties must be specified.", nameof(Properties))); - } - } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs b/src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs index c73c34f13..04b10fe84 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/Guards/FieldPropertiesValidator.cs @@ -7,6 +7,7 @@ // ========================================================================== using System.Collections.Generic; +using System.Linq; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Infrastructure; @@ -22,7 +23,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards public static IEnumerable Validate(FieldProperties properties) { - return properties.Accept(Instance); + return properties?.Accept(Instance) ?? Enumerable.Empty(); } public IEnumerable Visit(AssetsFieldProperties properties) @@ -110,7 +111,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards nameof(properties.Editor)); } - if ((properties.Editor == NumberFieldEditor.Radio || properties.Editor == NumberFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Count == 0)) + if ((properties.Editor == NumberFieldEditor.Radio || properties.Editor == NumberFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Length == 0)) { yield return new ValidationError("Radio buttons or dropdown list need allowed values.", nameof(properties.AllowedValues)); @@ -135,7 +136,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards nameof(properties.MaxValue)); } - if (properties.AllowedValues != null && properties.AllowedValues.Count > 0 && (properties.MinValue.HasValue || properties.MaxValue.HasValue)) + if (properties.AllowedValues != null && properties.AllowedValues.Length > 0 && (properties.MinValue.HasValue || properties.MaxValue.HasValue)) { yield return new ValidationError("Either allowed values or min and max value can be defined.", nameof(properties.AllowedValues), @@ -162,7 +163,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards nameof(properties.Editor)); } - if ((properties.Editor == StringFieldEditor.Radio || properties.Editor == StringFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Count == 0)) + if ((properties.Editor == StringFieldEditor.Radio || properties.Editor == StringFieldEditor.Dropdown) && (properties.AllowedValues == null || properties.AllowedValues.Length == 0)) { yield return new ValidationError("Radio buttons or dropdown list need allowed values.", nameof(properties.AllowedValues)); @@ -181,7 +182,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards nameof(properties.MaxLength)); } - if (properties.AllowedValues != null && properties.AllowedValues.Count > 0 && (properties.MinLength.HasValue || properties.MaxLength.HasValue)) + if (properties.AllowedValues != null && properties.AllowedValues.Length > 0 && (properties.MinLength.HasValue || properties.MaxLength.HasValue)) { yield return new ValidationError("Either allowed values or min and max length can be defined.", nameof(properties.AllowedValues), diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchema.cs b/src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchema.cs new file mode 100644 index 000000000..ed0e69533 --- /dev/null +++ b/src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchema.cs @@ -0,0 +1,130 @@ +// ========================================================================== +// GuardSchema.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Linq; +using System.Threading.Tasks; +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Read.Schemas.Services; +using Squidex.Domain.Apps.Write.Schemas.Commands; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Write.Schemas.Guards +{ + public static class GuardSchema + { + public static Task CanCreate(CreateSchema command, ISchemaProvider schemas) + { + Guard.NotNull(command, nameof(command)); + + return Validate.It(() => "Cannot create schema.", async error => + { + if (!command.Name.IsSlug()) + { + error(new ValidationError("Name must be a valid slug.", nameof(command.Name))); + } + + if (await schemas.FindSchemaByNameAsync(command.AppId.Id, command.Name) != null) + { + error(new ValidationError($"A schema with name '{command.Name}' already exists", nameof(command.Name))); + } + + if (command.Fields != null && command.Fields.Any()) + { + var index = 0; + + foreach (var field in command.Fields) + { + var prefix = $"Fields.{index}"; + + if (!field.Partitioning.IsValidPartitioning()) + { + error(new ValidationError("Partitioning is not valid.", $"{prefix}.{nameof(field.Partitioning)}")); + } + + if (!field.Name.IsPropertyName()) + { + error(new ValidationError("Name must be a valid property name.", $"{prefix}.{nameof(field.Name)}")); + } + + if (field.Properties == null) + { + error(new ValidationError("Properties must be defined.", $"{prefix}.{nameof(field.Properties)}")); + } + + var propertyErrors = FieldPropertiesValidator.Validate(field.Properties); + + foreach (var propertyError in propertyErrors) + { + error(propertyError); + } + } + + if (command.Fields.Select(x => x.Name).Distinct().Count() != command.Fields.Count) + { + error(new ValidationError("Fields cannot have duplicate names.", nameof(command.Fields))); + } + } + }); + } + + public static void CanReorder(Schema schema, ReorderFields command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot reorder schema fields.", error => + { + if (command.FieldIds == null) + { + error(new ValidationError("Field ids must be specified.", nameof(command.FieldIds))); + } + + if (command.FieldIds.Count != schema.Fields.Count || command.FieldIds.Any(x => !schema.FieldsById.ContainsKey(x))) + { + error(new ValidationError("Ids must cover all fields.", nameof(command.FieldIds))); + } + }); + } + + public static void CanPublish(Schema schema, PublishSchema command) + { + Guard.NotNull(command, nameof(command)); + + if (schema.IsPublished) + { + throw new DomainException("Schema is already published."); + } + } + + public static void CanUnpublish(Schema schema, UnpublishSchema command) + { + Guard.NotNull(command, nameof(command)); + + if (!schema.IsPublished) + { + throw new DomainException("Schema is not published."); + } + } + + public static void CanUpdate(Schema schema, UpdateSchema command) + { + Guard.NotNull(command, nameof(command)); + } + + public static void CanConfigureScripts(Schema schema, ConfigureScripts command) + { + Guard.NotNull(command, nameof(command)); + } + + public static void CanDelete(Schema schema, DeleteSchema command) + { + Guard.NotNull(command, nameof(command)); + } + } +} diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchemaField.cs b/src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchemaField.cs new file mode 100644 index 000000000..b02f32d45 --- /dev/null +++ b/src/Squidex.Domain.Apps.Write/Schemas/Guards/GuardSchemaField.cs @@ -0,0 +1,160 @@ +// ========================================================================== +// SchemaFieldGuard.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Write.Schemas.Commands; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Write.Schemas.Guards +{ + public static class GuardSchemaField + { + public static void CanAdd(Schema schema, AddField command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot add a new field.", error => + { + if (!command.Partitioning.IsValidPartitioning()) + { + error(new ValidationError("Partitioning is not valid.", nameof(command.Partitioning))); + } + + if (!command.Name.IsPropertyName()) + { + error(new ValidationError("Name must be a valid property name.", nameof(command.Name))); + } + + if (command.Properties == null) + { + error(new ValidationError("Properties must be defined.", nameof(command.Properties))); + } + + var propertyErrors = FieldPropertiesValidator.Validate(command.Properties); + + foreach (var propertyError in propertyErrors) + { + error(propertyError); + } + + if (schema.FieldsByName.ContainsKey(command.Name)) + { + error(new ValidationError($"There is already a field with name '{command.Name}'", nameof(command.Name))); + } + }); + } + + public static void CanUpdate(Schema schema, UpdateField command) + { + Guard.NotNull(command, nameof(command)); + + Validate.It(() => "Cannot update field.", error => + { + if (command.Properties == null) + { + error(new ValidationError("Properties must be defined.", nameof(command.Properties))); + } + + var propertyErrors = FieldPropertiesValidator.Validate(command.Properties); + + foreach (var propertyError in propertyErrors) + { + error(propertyError); + } + }); + + var field = GetFieldOrThrow(schema, command.FieldId); + + if (field.IsLocked) + { + throw new DomainException("Schema field is already locked."); + } + } + + public static void CanDelete(Schema schema, DeleteField command) + { + Guard.NotNull(command, nameof(command)); + + var field = GetFieldOrThrow(schema, command.FieldId); + + if (field.IsLocked) + { + throw new DomainException("Schema field is locked."); + } + } + + public static void CanHide(Schema schema, HideField command) + { + Guard.NotNull(command, nameof(command)); + + var field = GetFieldOrThrow(schema, command.FieldId); + + if (field.IsHidden) + { + throw new DomainException("Schema field is already hidden."); + } + } + + public static void CanShow(Schema schema, ShowField command) + { + Guard.NotNull(command, nameof(command)); + + var field = GetFieldOrThrow(schema, command.FieldId); + + if (!field.IsHidden) + { + throw new DomainException("Schema field is already visible."); + } + } + + public static void CanDisable(Schema schema, DisableField command) + { + Guard.NotNull(command, nameof(command)); + + var field = GetFieldOrThrow(schema, command.FieldId); + + if (field.IsDisabled) + { + throw new DomainException("Schema field is already disabled."); + } + } + + public static void CanEnable(Schema schema, EnableField command) + { + var field = GetFieldOrThrow(schema, command.FieldId); + + if (!field.IsDisabled) + { + throw new DomainException("Schema field is already enabled."); + } + } + + public static void CanLock(Schema schema, LockField command) + { + Guard.NotNull(command, nameof(command)); + + var field = GetFieldOrThrow(schema, command.FieldId); + + if (field.IsLocked) + { + throw new DomainException("Schema field is already locked."); + } + } + + private static Field GetFieldOrThrow(Schema schema, long fieldId) + { + if (!schema.FieldsById.TryGetValue(fieldId, out var field)) + { + throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Field)); + } + + return field; + } + } +} diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaFieldGuard.cs b/src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaFieldGuard.cs deleted file mode 100644 index 9e6f320d3..000000000 --- a/src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaFieldGuard.cs +++ /dev/null @@ -1,106 +0,0 @@ -// ========================================================================== -// SchemaFieldGuard.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Write.Schemas.Guards -{ - public static class SchemaFieldGuard - { - public static void GuardCanAdd(Schema schema, string name) - { - if (schema.FieldsByName.ContainsKey(name)) - { - var error = new ValidationError($"There is already a field with name '{name}'", "Name"); - - throw new ValidationException("Cannot add a new field.", error); - } - } - - public static void GuardCanDelete(Schema schema, long fieldId) - { - var field = GetFieldOrThrow(schema, fieldId); - - if (field.IsLocked) - { - throw new DomainException("Schema field is locked."); - } - } - - public static void GuardCanHide(Schema schema, long fieldId) - { - var field = GetFieldOrThrow(schema, fieldId); - - if (field.IsHidden) - { - throw new DomainException("Schema field is already hidden."); - } - } - - public static void GuardCanShow(Schema schema, long fieldId) - { - var field = GetFieldOrThrow(schema, fieldId); - - if (!field.IsHidden) - { - throw new DomainException("Schema field is already visible."); - } - } - - public static void GuardCanDisable(Schema schema, long fieldId) - { - var field = GetFieldOrThrow(schema, fieldId); - - if (field.IsDisabled) - { - throw new DomainException("Schema field is already disabled."); - } - } - - public static void GuardCanEnable(Schema schema, long fieldId) - { - var field = GetFieldOrThrow(schema, fieldId); - - if (!field.IsDisabled) - { - throw new DomainException("Schema field is already enabled."); - } - } - - public static void GuardCanLock(Schema schema, long fieldId) - { - var field = GetFieldOrThrow(schema, fieldId); - - if (field.IsLocked) - { - throw new DomainException("Schema field is already locked."); - } - } - - public static void GuardCanUpdate(Schema schema, long fieldId) - { - var field = GetFieldOrThrow(schema, fieldId); - - if (field.IsLocked) - { - throw new DomainException("Schema field is already locked."); - } - } - - private static Field GetFieldOrThrow(Schema schema, long fieldId) - { - if (!schema.FieldsById.TryGetValue(fieldId, out var field)) - { - throw new DomainObjectNotFoundException(fieldId.ToString(), "Fields", typeof(Field)); - } - - return field; - } - } -} diff --git a/src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaGuard.cs b/src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaGuard.cs deleted file mode 100644 index b228a7040..000000000 --- a/src/Squidex.Domain.Apps.Write/Schemas/Guards/SchemaGuard.cs +++ /dev/null @@ -1,44 +0,0 @@ -// ========================================================================== -// SchemaGuard.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System.Collections.Generic; -using System.Linq; -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure; - -namespace Squidex.Domain.Apps.Write.Schemas.Guards -{ - public static class SchemaGuard - { - public static void GuardCanReorder(Schema schema, List fieldIds) - { - if (fieldIds.Count != schema.Fields.Count || fieldIds.Any(x => !schema.FieldsById.ContainsKey(x))) - { - var error = new ValidationError("Ids must cover all fields.", "FieldIds"); - - throw new ValidationException("Cannot reorder schema fields.", error); - } - } - - public static void GuardCanPublish(Schema schema) - { - if (schema.IsPublished) - { - throw new DomainException("Schema is already published."); - } - } - - public static void GuardCanUnpublish(Schema schema) - { - if (!schema.IsPublished) - { - throw new DomainException("Schema is not published."); - } - } - } -} diff --git a/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs b/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs index 62464c92e..397622cb4 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/SchemaCommandMiddleware.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Domain.Apps.Write.Schemas.Commands; +using Squidex.Domain.Apps.Write.Schemas.Guards; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.Dispatching; @@ -31,19 +32,12 @@ namespace Squidex.Domain.Apps.Write.Schemas this.schemas = schemas; } - protected async Task On(CreateSchema command, CommandContext context) + protected Task On(CreateSchema command, CommandContext context) { - if (await schemas.FindSchemaByNameAsync(command.AppId.Id, command.Name) != null) + return handler.CreateAsync(context, async s => { - var error = - new ValidationError($"A schema with name '{command.Name}' already exists", "Name", - nameof(CreateSchema.Name)); + await GuardSchema.CanCreate(command, schemas); - throw new ValidationException("Cannot create a new schema.", error); - } - - await handler.CreateAsync(context, s => - { s.Create(command); context.Complete(EntityCreatedResult.Create(s.Id, s.Version)); @@ -54,75 +48,142 @@ namespace Squidex.Domain.Apps.Write.Schemas { return handler.UpdateAsync(context, s => { + GuardSchemaField.CanAdd(s.Schema, command); + s.Add(command); context.Complete(EntityCreatedResult.Create(s.Schema.FieldsById.Values.First(x => x.Name == command.Name).Id, s.Version)); }); } - protected Task On(DeleteSchema command, CommandContext context) - { - return handler.UpdateAsync(context, s => s.Delete(command)); - } - protected Task On(DeleteField command, CommandContext context) { - return handler.UpdateAsync(context, s => s.DeleteField(command)); + return handler.UpdateAsync(context, s => + { + GuardSchemaField.CanDelete(s.Schema, command); + + s.DeleteField(command); + }); } protected Task On(LockField command, CommandContext context) { - return handler.UpdateAsync(context, s => s.LockField(command)); + return handler.UpdateAsync(context, s => + { + GuardSchemaField.CanLock(s.Schema, command); + + s.LockField(command); + }); } protected Task On(HideField command, CommandContext context) { - return handler.UpdateAsync(context, s => s.HideField(command)); + return handler.UpdateAsync(context, s => + { + GuardSchemaField.CanHide(s.Schema, command); + + s.HideField(command); + }); } protected Task On(ShowField command, CommandContext context) { - return handler.UpdateAsync(context, s => s.ShowField(command)); + return handler.UpdateAsync(context, s => + { + GuardSchemaField.CanShow(s.Schema, command); + + s.ShowField(command); + }); } protected Task On(DisableField command, CommandContext context) { - return handler.UpdateAsync(context, s => s.DisableField(command)); + return handler.UpdateAsync(context, s => + { + GuardSchemaField.CanDisable(s.Schema, command); + + s.DisableField(command); + }); } protected Task On(EnableField command, CommandContext context) { - return handler.UpdateAsync(context, s => s.EnableField(command)); + return handler.UpdateAsync(context, s => + { + GuardSchemaField.CanEnable(s.Schema, command); + + s.EnableField(command); + }); } - protected Task On(ReorderFields command, CommandContext context) + protected Task On(UpdateField command, CommandContext context) { - return handler.UpdateAsync(context, s => s.Reorder(command)); + return handler.UpdateAsync(context, s => + { + GuardSchemaField.CanUpdate(s.Schema, command); + + s.UpdateField(command); + }); } - protected Task On(UpdateSchema command, CommandContext context) + protected Task On(ReorderFields command, CommandContext context) { - return handler.UpdateAsync(context, s => s.Update(command)); + return handler.UpdateAsync(context, s => + { + GuardSchema.CanReorder(s.Schema, command); + + s.Reorder(command); + }); } - protected Task On(UpdateField command, CommandContext context) + protected Task On(UpdateSchema command, CommandContext context) { - return handler.UpdateAsync(context, s => s.UpdateField(command)); + return handler.UpdateAsync(context, s => + { + GuardSchema.CanUpdate(s.Schema, command); + + s.Update(command); + }); } protected Task On(PublishSchema command, CommandContext context) { - return handler.UpdateAsync(context, s => s.Publish(command)); + return handler.UpdateAsync(context, s => + { + GuardSchema.CanPublish(s.Schema, command); + + s.Publish(command); + }); } protected Task On(UnpublishSchema command, CommandContext context) { - return handler.UpdateAsync(context, s => s.Unpublish(command)); + return handler.UpdateAsync(context, s => + { + GuardSchema.CanUnpublish(s.Schema, command); + + s.Unpublish(command); + }); } protected Task On(ConfigureScripts command, CommandContext context) { - return handler.UpdateAsync(context, s => s.ConfigureScripts(command)); + return handler.UpdateAsync(context, s => + { + GuardSchema.CanConfigureScripts(s.Schema, command); + + s.ConfigureScripts(command); + }); + } + + protected Task On(DeleteSchema command, CommandContext context) + { + return handler.UpdateAsync(context, s => + { + GuardSchema.CanDelete(s.Schema, command); + + s.Delete(command); + }); } public async Task HandleAsync(CommandContext context, Func next) diff --git a/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs b/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs index b82e139df..01cd3c162 100644 --- a/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs +++ b/src/Squidex.Domain.Apps.Write/Schemas/SchemaDomainObject.cs @@ -12,7 +12,6 @@ using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas.Utils; using Squidex.Domain.Apps.Write.Schemas.Commands; -using Squidex.Domain.Apps.Write.Schemas.Guards; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS; using Squidex.Infrastructure.CQRS.Events; @@ -46,73 +45,73 @@ namespace Squidex.Domain.Apps.Write.Schemas this.registry = registry; } - public void On(FieldAdded @event) + protected void On(SchemaCreated @event) { - totalFields++; + totalFields += @event.Fields?.Count ?? 0; - schema = SchemaEventDispatcher.Dispatch(@event, schema, registry); + schema = SchemaEventDispatcher.Create(@event, registry); } - protected void On(SchemaCreated @event) + public void On(FieldAdded @event) { - totalFields += @event.Fields?.Count ?? 0; + totalFields++; - schema = SchemaEventDispatcher.Dispatch(@event, registry); + schema.Apply(@event, registry); } protected void On(FieldUpdated @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(FieldLocked @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(FieldHidden @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(FieldShown @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(FieldDisabled @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(FieldEnabled @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(SchemaUpdated @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(FieldDeleted @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(SchemaFieldsReordered @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(SchemaPublished @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(SchemaUnpublished @event) { - schema = SchemaEventDispatcher.Dispatch(@event, schema); + schema.Apply(@event); } protected void On(SchemaDeleted @event) @@ -122,8 +121,6 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject Create(CreateSchema command) { - Guard.Valid(command, nameof(command), () => "Cannot create schema"); - VerifyNotCreated(); var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId(Id, command.Name) }); @@ -147,12 +144,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject Add(AddField command) { - Guard.Valid(command, nameof(command), () => $"Cannot add field to schema {Id}"); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanAdd(schema, command.Name); - RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId(totalFields + 1, command.Name) })); return this; @@ -160,12 +153,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject UpdateField(UpdateField command) { - Guard.Valid(command, nameof(command), () => $"Cannot update schema '{Id}'"); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanUpdate(schema, command.FieldId); - RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); return this; @@ -173,12 +162,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject LockField(LockField command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanLock(schema, command.FieldId); - RaiseEvent(command, new FieldLocked()); return this; @@ -186,12 +171,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject HideField(HideField command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanHide(schema, command.FieldId); - RaiseEvent(command, new FieldHidden()); return this; @@ -199,12 +180,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject ShowField(ShowField command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanShow(schema, command.FieldId); - RaiseEvent(command, new FieldShown()); return this; @@ -212,12 +189,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject DisableField(DisableField command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanDisable(schema, command.FieldId); - RaiseEvent(command, new FieldDisabled()); return this; @@ -225,12 +198,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject EnableField(EnableField command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanEnable(schema, command.FieldId); - RaiseEvent(command, new FieldEnabled()); return this; @@ -238,12 +207,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject DeleteField(DeleteField command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaFieldGuard.GuardCanDelete(schema, command.FieldId); - RaiseEvent(command, new FieldDeleted()); return this; @@ -251,12 +216,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject Reorder(ReorderFields command) { - Guard.Valid(command, nameof(command), () => $"Cannot reorder fields for schema '{Id}'"); - VerifyCreatedAndNotDeleted(); - SchemaGuard.GuardCanReorder(schema, command.FieldIds); - RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); return this; @@ -264,12 +225,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject Publish(PublishSchema command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaGuard.GuardCanPublish(schema); - RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); return this; @@ -277,12 +234,8 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject Unpublish(UnpublishSchema command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); - SchemaGuard.GuardCanUnpublish(schema); - RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); return this; @@ -290,8 +243,6 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject ConfigureScripts(ConfigureScripts command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); @@ -310,8 +261,6 @@ namespace Squidex.Domain.Apps.Write.Schemas public SchemaDomainObject Update(UpdateSchema command) { - Guard.Valid(command, nameof(command), () => $"Cannot update schema '{Id}'"); - VerifyCreatedAndNotDeleted(); RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); diff --git a/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj b/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj index 95685bf46..0efdb97e0 100644 --- a/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj +++ b/src/Squidex.Domain.Apps.Write/Squidex.Domain.Apps.Write.csproj @@ -7,7 +7,8 @@ True - + + diff --git a/src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs b/src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs index cb9d7503e..2f8cc5d3f 100644 --- a/src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs +++ b/src/Squidex.Domain.Apps.Write/Webhooks/Commands/WebhookEditCommand.cs @@ -9,34 +9,13 @@ using System; using System.Collections.Generic; using Squidex.Domain.Apps.Core.Webhooks; -using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Write.Webhooks.Commands { - public abstract class WebhookEditCommand : WebhookAggregateCommand, IValidatable + public abstract class WebhookEditCommand : WebhookAggregateCommand { - private List schemas = new List(); - public Uri Url { get; set; } - public List Schemas - { - get - { - return schemas ?? (schemas = new List()); - } - set - { - schemas = value; - } - } - - public virtual void Validate(IList errors) - { - if (Url == null || !Url.IsAbsoluteUri) - { - errors.Add(new ValidationError("Url must be specified and absolute.", nameof(Url))); - } - } + public List Schemas { get; set; } = new List(); } } diff --git a/src/Squidex.Domain.Apps.Write/Webhooks/Guards/GuardWebhook.cs b/src/Squidex.Domain.Apps.Write/Webhooks/Guards/GuardWebhook.cs new file mode 100644 index 000000000..f8a784cb1 --- /dev/null +++ b/src/Squidex.Domain.Apps.Write/Webhooks/Guards/GuardWebhook.cs @@ -0,0 +1,63 @@ +// ========================================================================== +// GuardWebhook.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Linq; +using System.Threading.Tasks; +using Squidex.Domain.Apps.Read.Schemas.Services; +using Squidex.Domain.Apps.Write.Webhooks.Commands; +using Squidex.Infrastructure; + +namespace Squidex.Domain.Apps.Write.Webhooks.Guards +{ + public static class GuardWebhook + { + public static Task CanCreate(CreateWebhook command, ISchemaProvider schemas) + { + Guard.NotNull(command, nameof(command)); + + return Validate.It(() => "Cannot create webhook.", error => ValidateCommandAsync(command, error, schemas)); + } + + public static Task CanUpdate(UpdateWebhook command, ISchemaProvider schemas) + { + Guard.NotNull(command, nameof(command)); + + return Validate.It(() => "Cannot update webhook.", error => ValidateCommandAsync(command, error, schemas)); + } + + public static void CanDelete(DeleteWebhook command) + { + Guard.NotNull(command, nameof(command)); + } + + private static async Task ValidateCommandAsync(WebhookEditCommand command, Action error, ISchemaProvider schemas) + { + if (command.Url == null || !command.Url.IsAbsoluteUri) + { + error(new ValidationError("Url must be specified and absolute.", nameof(command.Url))); + } + + if (command.Schemas == null) + { + error(new ValidationError("Schemas cannot be null.", nameof(command.Schemas))); + } + + var schemaErrors = await Task.WhenAll( + command.Schemas.Select(async s => + await schemas.FindSchemaByIdAsync(s.SchemaId) == null + ? new ValidationError($"Schema {s.SchemaId} does not exist.", nameof(command.Schemas)) + : null)); + + foreach (var schemaError in schemaErrors.Where(x => x != null)) + { + error(schemaError); + } + } + } +} diff --git a/src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs b/src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs index 611cfeb7a..13cee7474 100644 --- a/src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs +++ b/src/Squidex.Domain.Apps.Write/Webhooks/WebhookCommandMiddleware.cs @@ -7,10 +7,10 @@ // ========================================================================== using System; -using System.Linq; using System.Threading.Tasks; using Squidex.Domain.Apps.Read.Schemas.Services; using Squidex.Domain.Apps.Write.Webhooks.Commands; +using Squidex.Domain.Apps.Write.Webhooks.Guards; using Squidex.Infrastructure; using Squidex.Infrastructure.CQRS.Commands; using Squidex.Infrastructure.Dispatching; @@ -33,21 +33,32 @@ namespace Squidex.Domain.Apps.Write.Webhooks protected async Task On(CreateWebhook command, CommandContext context) { - await ValidateAsync(command, () => "Failed to create webhook"); + await handler.CreateAsync(context, async w => + { + await GuardWebhook.CanCreate(command, schemas); - await handler.CreateAsync(context, c => c.Create(command)); + w.Create(command); + }); } protected async Task On(UpdateWebhook command, CommandContext context) { - await ValidateAsync(command, () => "Failed to update content"); + await handler.UpdateAsync(context, async c => + { + await GuardWebhook.CanUpdate(command, schemas); - await handler.UpdateAsync(context, c => c.Update(command)); + c.Update(command); + }); } protected Task On(DeleteWebhook command, CommandContext context) { - return handler.UpdateAsync(context, c => c.Delete(command)); + return handler.UpdateAsync(context, c => + { + GuardWebhook.CanDelete(command); + + c.Delete(command); + }); } public async Task HandleAsync(CommandContext context, Func next) @@ -57,21 +68,5 @@ namespace Squidex.Domain.Apps.Write.Webhooks await next(); } } - - private async Task ValidateAsync(WebhookEditCommand command, Func message) - { - var results = await Task.WhenAll( - command.Schemas.Select(async schema => - await schemas.FindSchemaByIdAsync(schema.SchemaId) == null - ? new ValidationError($"Schema {schema.SchemaId} does not exist.") - : null)); - - var errors = results.Where(x => x != null).ToArray(); - - if (errors.Length > 0) - { - throw new ValidationException(message(), errors); - } - } } } diff --git a/src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs b/src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs index c3484699d..fee952cf7 100644 --- a/src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs +++ b/src/Squidex.Domain.Apps.Write/Webhooks/WebhookDomainObject.cs @@ -39,8 +39,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks public void Create(CreateWebhook command) { - Guard.Valid(command, nameof(command), () => "Cannot create webhook"); - VerifyNotCreated(); RaiseEvent(SimpleMapper.Map(command, new WebhookCreated())); @@ -48,8 +46,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks public void Update(UpdateWebhook command) { - Guard.Valid(command, nameof(command), () => "Cannot update webhook"); - VerifyCreatedAndNotDeleted(); RaiseEvent(SimpleMapper.Map(command, new WebhookUpdated())); @@ -57,8 +53,6 @@ namespace Squidex.Domain.Apps.Write.Webhooks public void Delete(DeleteWebhook command) { - Guard.NotNull(command, nameof(command)); - VerifyCreatedAndNotDeleted(); RaiseEvent(SimpleMapper.Map(command, new WebhookDeleted())); diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonConverter.cs similarity index 98% rename from src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs rename to src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonConverter.cs index 7afa68c00..100318609 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/BsonConverter.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonConverter.cs @@ -1,5 +1,5 @@ // ========================================================================== -// BsonConverter.cs +// JsonBsonConverter.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -12,7 +12,7 @@ using Newtonsoft.Json.Linq; namespace Squidex.Infrastructure.MongoDb { - public static class BsonConverter + public static class JsonBsonConverter { public static BsonDocument ToBson(this JObject source) { diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonSerializer.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonSerializer.cs new file mode 100644 index 000000000..a2385e726 --- /dev/null +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/JsonBsonSerializer.cs @@ -0,0 +1,38 @@ +// ========================================================================== +// RefTokenSerializer.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Serializers; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Squidex.Infrastructure.MongoDb +{ + public class JsonBsonSerializer : ClassSerializerBase + { + private readonly JsonSerializer serializer; + + public JsonBsonSerializer(JsonSerializer serializer) + { + Guard.NotNull(serializer, nameof(serializer)); + + this.serializer = serializer; + } + + protected override object DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args) + { + return BsonSerializer.Deserialize(context.Reader).ToJson().ToObject(args.NominalType, serializer); + } + + protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, object value) + { + BsonSerializer.Serialize(context.Writer, JObject.FromObject(value, serializer).ToBson()); + } + } +} diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs index 65c1ce74d..e3c62af39 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/RefTokenSerializer.cs @@ -12,7 +12,7 @@ using MongoDB.Bson.Serialization.Serializers; namespace Squidex.Infrastructure.MongoDb { - public class RefTokenSerializer : SerializerBase + public class RefTokenSerializer : ClassSerializerBase { private static readonly Lazy Registerer = new Lazy(() => { @@ -26,23 +26,16 @@ namespace Squidex.Infrastructure.MongoDb return !Registerer.IsValueCreated && Registerer.Value; } - public override RefToken Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + protected override RefToken DeserializeValue(BsonDeserializationContext context, BsonDeserializationArgs args) { var value = context.Reader.ReadString(); - return value != null ? RefToken.Parse(value) : null; + return RefToken.Parse(value); } - public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, RefToken value) + protected override void SerializeValue(BsonSerializationContext context, BsonSerializationArgs args, RefToken value) { - if (value != null) - { - context.Writer.WriteString(value.ToString()); - } - else - { - context.Writer.WriteNull(); - } + context.Writer.WriteString(value.ToString()); } } } diff --git a/src/Squidex.Infrastructure/Validate.cs b/src/Squidex.Infrastructure/Validate.cs new file mode 100644 index 000000000..ebeaeb4a6 --- /dev/null +++ b/src/Squidex.Infrastructure/Validate.cs @@ -0,0 +1,42 @@ +// ========================================================================== +// TypeNameRegistry.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Squidex.Infrastructure +{ + public static class Validate + { + public static void It(Func message, Action> action) + { + var errors = new List(); + + action(errors.Add); + + if (errors.Any()) + { + throw new ValidationException(message(), errors); + } + } + + public static async Task It(Func message, Func, Task> action) + { + var errors = new List(); + + await action(errors.Add); + + if (errors.Any()) + { + throw new ValidationException(message(), errors); + } + } + } +} diff --git a/src/Squidex/Squidex.csproj b/src/Squidex/Squidex.csproj index ba48b4a0f..d7f56a3ee 100644 --- a/src/Squidex/Squidex.csproj +++ b/src/Squidex/Squidex.csproj @@ -29,7 +29,6 @@ - diff --git a/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj b/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj index 24478e87f..48bba6f42 100644 --- a/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Read.Tests/Squidex.Domain.Apps.Read.Tests.csproj @@ -10,7 +10,8 @@ - + + diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppCommandMiddlewareTests.cs deleted file mode 100644 index 49a7b98ff..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppCommandMiddlewareTests.cs +++ /dev/null @@ -1,330 +0,0 @@ -// ========================================================================== -// AppCommandMiddlewareTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Threading.Tasks; -using FakeItEasy; -using Squidex.Domain.Apps.Read.Apps; -using Squidex.Domain.Apps.Read.Apps.Repositories; -using Squidex.Domain.Apps.Read.Apps.Services; -using Squidex.Domain.Apps.Read.Apps.Services.Implementations; -using Squidex.Domain.Apps.Write.Apps.Commands; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Commands; -using Squidex.Shared.Users; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Apps -{ - public class AppCommandMiddlewareTests : HandlerTestBase - { - private readonly IAppRepository appRepository = A.Fake(); - private readonly IAppPlansProvider appPlansProvider = A.Fake(); - private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake(); - private readonly IUserResolver userResolver = A.Fake(); - private readonly AppCommandMiddleware sut; - private readonly AppDomainObject app; - private readonly Language language = Language.DE; - private readonly string contributorId = Guid.NewGuid().ToString(); - private readonly string clientName = "client"; - - public AppCommandMiddlewareTests() - { - app = new AppDomainObject(AppId, -1); - - sut = new AppCommandMiddleware(Handler, appRepository, appPlansProvider, appPlansBillingManager, userResolver); - } - - [Fact] - public async Task Create_should_throw_exception_if_a_name_with_same_name_already_exists() - { - var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId }); - - A.CallTo(() => appRepository.FindAppAsync(AppName)) - .Returns(A.Dummy()); - - await TestCreate(app, async _ => - { - await Assert.ThrowsAsync(async () => await sut.HandleAsync(context)); - }, false); - - A.CallTo(() => appRepository.FindAppAsync(AppName)).MustHaveHappened(); - } - - [Fact] - public async Task Create_should_create_app_if_name_is_free() - { - var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId }); - - A.CallTo(() => appRepository.FindAppAsync(AppName)) - .Returns((IAppEntity)null); - - await TestCreate(app, async _ => - { - await sut.HandleAsync(context); - }); - - Assert.Equal(AppId, context.Result>().IdOrValue); - } - - [Fact] - public async Task AssignContributor_should_throw_exception_if_user_not_found() - { - CreateApp(); - - var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId }); - - A.CallTo(() => userResolver.FindByIdAsync(contributorId)) - .Returns((IUser)null); - - await TestUpdate(app, async _ => - { - await Assert.ThrowsAsync(() => sut.HandleAsync(context)); - }, false); - } - - [Fact] - public async Task AssignContributor_throw_exception_if_reached_max_contributor_size() - { - A.CallTo(() => appPlansProvider.GetPlan(null)) - .Returns(new ConfigAppLimitsPlan { MaxContributors = 2 }); - - CreateApp() - .AssignContributor(CreateCommand(new AssignContributor { ContributorId = "1" })) - .AssignContributor(CreateCommand(new AssignContributor { ContributorId = "2" })); - - var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId }); - - A.CallTo(() => userResolver.FindByIdAsync(A.Ignored)) - .Returns(A.Dummy()); - - await TestUpdate(app, async _ => - { - await Assert.ThrowsAsync(() => sut.HandleAsync(context)); - }, false); - } - - [Fact] - public async Task AssignContributor_should_throw_exception_if_null_user_not_found() - { - CreateApp(); - - var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId }); - - A.CallTo(() => userResolver.FindByIdAsync(contributorId)) - .Returns((IUser)null); - - await TestUpdate(app, async _ => - { - await Assert.ThrowsAsync(() => sut.HandleAsync(context)); - }, false); - } - - [Fact] - public async Task AssignContributor_should_assign_if_user_found() - { - A.CallTo(() => appPlansProvider.GetPlan(null)) - .Returns(new ConfigAppLimitsPlan { MaxContributors = -1 }); - - CreateApp(); - - var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId }); - - A.CallTo(() => userResolver.FindByIdAsync(contributorId)) - .Returns(A.Dummy()); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task RemoveContributor_should_update_domain_object() - { - CreateApp() - .AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId })); - - var context = CreateContextForCommand(new RemoveContributor { ContributorId = contributorId }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task AttachClient_should_update_domain_object() - { - CreateApp(); - - var context = CreateContextForCommand(new AttachClient { Id = clientName }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task ChangePlan_should_throw_if_plan_not_found() - { - A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) - .Returns(false); - - CreateApp() - .AttachClient(CreateCommand(new AttachClient { Id = clientName })); - - var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" }); - - await TestUpdate(app, async _ => - { - await Assert.ThrowsAsync(() => sut.HandleAsync(context)); - }, false); - } - - [Fact] - public async Task RenameClient_should_update_domain_object() - { - CreateApp() - .AttachClient(CreateCommand(new AttachClient { Id = clientName })); - - var context = CreateContextForCommand(new UpdateClient { Id = clientName, Name = "New Name" }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task RevokeClient_should_update_domain_object() - { - CreateApp() - .AttachClient(CreateCommand(new AttachClient { Id = clientName })); - - var context = CreateContextForCommand(new RevokeClient { Id = clientName }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task ChangePlan_should_update_domain_object() - { - A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) - .Returns(true); - - CreateApp(); - - var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - - A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")).MustHaveHappened(); - } - - [Fact] - public async Task ChangePlan_should_not_make_update_for_redirect_result() - { - A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) - .Returns(true); - - A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")) - .Returns(CreateRedirectResult()); - - CreateApp(); - - var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - - Assert.Null(app.PlanId); - } - - [Fact] - public async Task ChangePlan_should_not_call_billing_manager_for_callback() - { - A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) - .Returns(true); - - CreateApp(); - - var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan", FromCallback = true }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - - A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, app.Id, app.Name, "my-plan")).MustNotHaveHappened(); - } - - [Fact] - public async Task AddLanguage_should_update_domain_object() - { - CreateApp(); - - var context = CreateContextForCommand(new AddLanguage { Language = language }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task RemoveLanguage_should_update_domain_object() - { - CreateApp() - .AddLanguage(CreateCommand(new AddLanguage { Language = language })); - - var context = CreateContextForCommand(new RemoveLanguage { Language = language }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task UpdateLanguage_should_update_domain_object() - { - CreateApp() - .AddLanguage(CreateCommand(new AddLanguage { Language = language })); - - var context = CreateContextForCommand(new UpdateLanguage { Language = language }); - - await TestUpdate(app, async _ => - { - await sut.HandleAsync(context); - }); - } - - private AppDomainObject CreateApp() - { - app.Create(CreateCommand(new CreateApp { Name = AppName })); - - return app; - } - - private static Task CreateRedirectResult() - { - return Task.FromResult(new RedirectToCheckoutResult(new Uri("http://squidex.io"))); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppDomainObjectTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppDomainObjectTests.cs deleted file mode 100644 index 3301ed7e8..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppDomainObjectTests.cs +++ /dev/null @@ -1,597 +0,0 @@ -// ========================================================================== -// AppDomainObjectTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Linq; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Events.Apps; -using Squidex.Domain.Apps.Write.Apps.Commands; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Apps -{ - public class AppDomainObjectTests : HandlerTestBase - { - private readonly AppDomainObject sut; - private readonly string contributorId = Guid.NewGuid().ToString(); - private readonly string clientId = "client"; - private readonly string clientNewName = "My Client"; - private readonly string planId = "premium"; - - public AppDomainObjectTests() - { - sut = new AppDomainObject(AppId, 0); - } - - [Fact] - public void Create_should_throw_exception_if_created() - { - CreateApp(); - - Assert.Throws(() => - { - sut.Create(CreateCommand(new CreateApp { Name = AppName })); - }); - } - - [Fact] - public void Create_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.Create(CreateCommand(new CreateApp())); - }); - } - - [Fact] - public void Create_should_specify_name_and_owner() - { - sut.Create(CreateCommand(new CreateApp { Name = AppName, Actor = User, AppId = AppId })); - - Assert.Equal(AppName, sut.Name); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppCreated { Name = AppName }), - CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Permission = AppContributorPermission.Owner }), - CreateEvent(new AppLanguageAdded { Language = Language.EN }) - ); - } - - [Fact] - public void ChangePlan_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); - }); - } - - [Fact] - public void ChangePlan_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.ChangePlan(CreateCommand(new ChangePlan())); - }); - } - - [Fact] - public void ChangePlan_should_throw_exception_if_plan_configured_from_other_user() - { - CreateApp(); - - sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = "other-plan", Actor = new RefToken("User", "other") })); - - Assert.Throws(() => - { - sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); - }); - } - - [Fact] - public void ChangePlan_should_throw_exception_if_same_plan() - { - CreateApp(); - sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); - - Assert.Throws(() => - { - sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); - }); - } - - [Fact] - public void ChangePlan_should_create_events() - { - CreateApp(); - - sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppPlanChanged { PlanId = planId }) - ); - } - - [Fact] - public void AssignContributor_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId })); - }); - } - - [Fact] - public void AssignContributor_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.AssignContributor(CreateCommand(new AssignContributor { Permission = (AppContributorPermission)123 })); - }); - } - - [Fact] - public void AssignContributor_should_throw_exception_if_single_owner_becomes_non_owner() - { - CreateApp(); - - Assert.Throws(() => - { - sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = User.Identifier, Permission = AppContributorPermission.Editor })); - }); - } - - [Fact] - public void AssignContributor_should_throw_exception_if_user_already_contributor() - { - CreateApp(); - - sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); - - Assert.Throws(() => - { - sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); - }); - } - - [Fact] - public void AssignContributor_should_create_events() - { - CreateApp(); - - sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppContributorAssigned { ContributorId = contributorId, Permission = AppContributorPermission.Editor }) - ); - } - - [Fact] - public void RemoveContributor_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId })); - }); - } - - [Fact] - public void RemoveContributor_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.RemoveContributor(CreateCommand(new RemoveContributor())); - }); - } - - [Fact] - public void RemoveContributor_should_throw_exception_if_all_owners_removed() - { - CreateApp(); - - Assert.Throws(() => - { - sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = User.Identifier })); - }); - } - - [Fact] - public void RemoveContributor_should_throw_exception_if_contributor_not_found() - { - CreateApp(); - - Assert.Throws(() => - { - sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = "not-found" })); - }); - } - - [Fact] - public void RemoveContributor_should_create_events_and_remove_contributor() - { - CreateApp(); - - sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); - sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId })); - - sut.GetUncomittedEvents().Skip(1) - .ShouldHaveSameEvents( - CreateEvent(new AppContributorRemoved { ContributorId = contributorId }) - ); - } - - [Fact] - public void AttachClient_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); - }); - } - - [Fact] - public void AttachClient_should_throw_exception_if_command_is_not_valid() - { - CreateApp(); - - Assert.Throws(() => - { - sut.AttachClient(CreateCommand(new AttachClient())); - }); - - Assert.Throws(() => - { - sut.AttachClient(CreateCommand(new AttachClient { Id = string.Empty })); - }); - } - - [Fact] - public void AttachClient_should_throw_exception_if_id_already_exists() - { - CreateApp(); - - sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); - - Assert.Throws(() => - { - sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); - }); - } - - [Fact] - public void AttachClient_should_create_events() - { - var command = new AttachClient { Id = clientId }; - - CreateApp(); - - sut.AttachClient(CreateCommand(command)); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppClientAttached { Id = clientId, Secret = command.Secret }) - ); - } - - [Fact] - public void RevokeClient_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.RevokeClient(CreateCommand(new RevokeClient { Id = "not-found" })); - }); - } - - [Fact] - public void RevokeClient_should_throw_exception_if_command_is_not_valid() - { - CreateApp(); - - Assert.Throws(() => - { - sut.RevokeClient(CreateCommand(new RevokeClient())); - }); - - Assert.Throws(() => - { - sut.RevokeClient(CreateCommand(new RevokeClient { Id = string.Empty })); - }); - } - - [Fact] - public void RevokeClient_should_throw_exception_if_client_not_found() - { - CreateApp(); - - Assert.Throws(() => - { - sut.RevokeClient(CreateCommand(new RevokeClient { Id = "not-found" })); - }); - } - - [Fact] - public void RevokeClient_should_create_events() - { - CreateApp(); - CreateClient(); - - sut.RevokeClient(CreateCommand(new RevokeClient { Id = clientId })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppClientRevoked { Id = clientId }) - ); - } - - [Fact] - public void UpdateClient_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.UpdateClient(CreateCommand(new UpdateClient { Id = "not-found", Name = clientNewName })); - }); - } - - [Fact] - public void UpdateClient_should_throw_exception_if_command_is_not_valid() - { - CreateApp(); - - Assert.Throws(() => - { - sut.UpdateClient(CreateCommand(new UpdateClient())); - }); - - Assert.Throws(() => - { - sut.UpdateClient(CreateCommand(new UpdateClient { Id = string.Empty })); - }); - - Assert.Throws(() => - { - sut.UpdateClient(CreateCommand(new UpdateClient { Permission = (AppClientPermission)int.MaxValue })); - }); - } - - [Fact] - public void UpdateClient_should_throw_exception_if_client_not_found() - { - CreateApp(); - - Assert.Throws(() => - { - sut.UpdateClient(CreateCommand(new UpdateClient { Id = "not-found", Name = clientNewName })); - }); - } - - [Fact] - public void UpdateClient_should_throw_exception_if_client_has_same_reader_state() - { - CreateApp(); - CreateClient(); - - Assert.Throws(() => - { - sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Permission = AppClientPermission.Editor })); - }); - } - - [Fact] - public void UpdateClient_should_throw_exception_if_same_client_name() - { - CreateApp(); - CreateClient(); - - sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName })); - - Assert.Throws(() => - { - sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName })); - }); - } - - [Fact] - public void UpdateClient_should_create_events() - { - CreateApp(); - CreateClient(); - - sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName, Permission = AppClientPermission.Developer })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppClientRenamed { Id = clientId, Name = clientNewName }), - CreateEvent(new AppClientUpdated { Id = clientId, Permission = AppClientPermission.Developer }) - ); - } - - [Fact] - public void AddLanguage_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE })); - }); - } - - [Fact] - public void AddLanguage_should_throw_exception_if_command_is_not_valid() - { - CreateApp(); - - Assert.Throws(() => - { - sut.AddLanguage(CreateCommand(new AddLanguage())); - }); - } - - [Fact] - public void AddLanguage_should_throw_exception_if_language_already_exists() - { - CreateApp(); - - Assert.Throws(() => - { - sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.EN })); - }); - } - - [Fact] - public void AddLanguage_should_create_events() - { - CreateApp(); - - sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppLanguageAdded { Language = Language.DE }) - ); - } - - [Fact] - public void RemoveLanguage_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.EN })); - }); - } - - [Fact] - public void RemoveLanguage_should_throw_exception_if_command_is_not_valid() - { - CreateApp(); - - Assert.Throws(() => - { - sut.RemoveLanguage(CreateCommand(new RemoveLanguage())); - }); - } - - [Fact] - public void RemoveLanguage_should_throw_exception_if_language_not_found() - { - CreateApp(); - - Assert.Throws(() => - { - sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.DE })); - }); - } - - [Fact] - public void RemoveLanguage_should_throw_exception_if_master_language() - { - CreateApp(); - - Assert.Throws(() => - { - sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.EN })); - }); - } - - [Fact] - public void RemoveLanguage_should_create_events() - { - CreateApp(); - CreateLanguage(Language.DE); - - sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.DE })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppLanguageRemoved { Language = Language.DE }) - ); - } - - [Fact] - public void UpdateLanguage_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.EN })); - }); - } - - [Fact] - public void UpdateLanguage_should_throw_exception_if_command_is_not_valid() - { - CreateApp(); - - Assert.Throws(() => - { - sut.UpdateLanguage(CreateCommand(new UpdateLanguage())); - }); - } - - [Fact] - public void UpdateLanguage_should_throw_exception_if_language_not_found() - { - CreateApp(); - - Assert.Throws(() => - { - sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.DE })); - }); - } - - [Fact] - public void UpdateLanguage_should_throw_exception_if_master_language() - { - CreateApp(); - - Assert.Throws(() => - { - sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.EN, IsOptional = true })); - }); - } - - [Fact] - public void UpdateLanguage_should_create_events() - { - CreateApp(); - CreateLanguage(Language.DE); - - sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.DE, Fallback = new List { Language.EN } })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateEvent(new AppLanguageUpdated { Language = Language.DE, Fallback = new List { Language.EN } }) - ); - } - - private void CreateApp() - { - sut.Create(CreateCommand(new CreateApp { Name = AppName })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void CreateClient() - { - sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void CreateLanguage(Language language) - { - sut.AddLanguage(CreateCommand(new AddLanguage { Language = language })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppEventTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppEventTests.cs deleted file mode 100644 index acb390b7e..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Apps/AppEventTests.cs +++ /dev/null @@ -1,51 +0,0 @@ -// ========================================================================== -// AppEventTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Squidex.Domain.Apps.Core.Apps; -using Squidex.Domain.Apps.Events; -using Squidex.Domain.Apps.Events.Apps; -using Squidex.Domain.Apps.Events.Apps.Old; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure; -using Xunit; - -#pragma warning disable CS0612 // Type or member is obsolete - -namespace Squidex.Domain.Apps.Write.Apps -{ - public class AppEventTests - { - private readonly RefToken actor = new RefToken("User", Guid.NewGuid().ToString()); - private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); - - [Fact] - public void Should_migrate_client_changed_as_reader_to_client_updated() - { - var source = CreateEvent(new AppClientChanged { IsReader = true }); - - source.Migrate().ShouldBeSameEvent(CreateEvent(new AppClientUpdated { Permission = AppClientPermission.Reader })); - } - - [Fact] - public void Should_migrate_client_changed_as_writer_to_client_updated() - { - var source = CreateEvent(new AppClientChanged { IsReader = false }); - - source.Migrate().ShouldBeSameEvent(CreateEvent(new AppClientUpdated { Permission = AppClientPermission.Editor })); - } - - private T CreateEvent(T contentEvent) where T : AppEvent - { - contentEvent.Actor = actor; - contentEvent.AppId = appId; - - return contentEvent; - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetCommandMiddlewareTests.cs deleted file mode 100644 index 196c7aa4a..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetCommandMiddlewareTests.cs +++ /dev/null @@ -1,139 +0,0 @@ -// ========================================================================== -// AssetCommandMiddlewareTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.IO; -using System.Threading.Tasks; -using FakeItEasy; -using Squidex.Domain.Apps.Write.Assets.Commands; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure.Assets; -using Squidex.Infrastructure.CQRS.Commands; -using Squidex.Infrastructure.Tasks; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Assets -{ - public class AssetCommandMiddlewareTests : HandlerTestBase - { - private readonly IAssetThumbnailGenerator assetThumbnailGenerator = A.Fake(); - private readonly IAssetStore assetStore = A.Fake(); - private readonly AssetCommandMiddleware sut; - private readonly AssetDomainObject asset; - private readonly Guid assetId = Guid.NewGuid(); - private readonly Stream stream = new MemoryStream(); - private readonly ImageInfo image = new ImageInfo(2048, 2048); - private readonly AssetFile file; - - public AssetCommandMiddlewareTests() - { - file = new AssetFile("my-image.png", "image/png", 1024, () => stream); - - asset = new AssetDomainObject(assetId, -1); - - sut = new AssetCommandMiddleware(Handler, assetStore, assetThumbnailGenerator); - } - - [Fact] - public async Task Create_should_create_asset() - { - var context = CreateContextForCommand(new CreateAsset { AssetId = assetId, File = file }); - - SetupStore(0, context.ContextId); - SetupImageInfo(); - - await TestCreate(asset, async _ => - { - await sut.HandleAsync(context); - }); - - Assert.Equal(assetId, context.Result>().IdOrValue); - - VerifyStore(0, context.ContextId); - VerifyImageInfo(); - } - - [Fact] - public async Task Update_should_update_domain_object() - { - var context = CreateContextForCommand(new UpdateAsset { AssetId = assetId, File = file }); - - SetupStore(1, context.ContextId); - SetupImageInfo(); - - CreateAsset(); - - await TestUpdate(asset, async _ => - { - await sut.HandleAsync(context); - }); - - VerifyStore(1, context.ContextId); - VerifyImageInfo(); - } - - [Fact] - public async Task Rename_should_update_domain_object() - { - CreateAsset(); - - var context = CreateContextForCommand(new RenameAsset { AssetId = assetId, FileName = "my-new-image.png" }); - - await TestUpdate(asset, async _ => - { - await sut.HandleAsync(context); - }); - } - - [Fact] - public async Task Delete_should_update_domain_object() - { - CreateAsset(); - - var command = CreateContextForCommand(new DeleteAsset { AssetId = assetId }); - - await TestUpdate(asset, async _ => - { - await sut.HandleAsync(command); - }); - } - - private void CreateAsset() - { - asset.Create(new CreateAsset { File = file }); - } - - private void SetupImageInfo() - { - A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream)) - .Returns(image); - } - - private void SetupStore(long version, Guid commitId) - { - A.CallTo(() => assetStore.UploadTemporaryAsync(commitId.ToString(), stream)) - .Returns(TaskHelper.Done); - A.CallTo(() => assetStore.CopyTemporaryAsync(commitId.ToString(), assetId.ToString(), version, null)) - .Returns(TaskHelper.Done); - A.CallTo(() => assetStore.DeleteTemporaryAsync(commitId.ToString())) - .Returns(TaskHelper.Done); - } - - private void VerifyImageInfo() - { - A.CallTo(() => assetThumbnailGenerator.GetImageInfoAsync(stream)).MustHaveHappened(); - } - - private void VerifyStore(long version, Guid commitId) - { - A.CallTo(() => assetStore.UploadTemporaryAsync(commitId.ToString(), stream)).MustHaveHappened(); - A.CallTo(() => assetStore.CopyTemporaryAsync(commitId.ToString(), assetId.ToString(), version, null)).MustHaveHappened(); - A.CallTo(() => assetStore.DeleteTemporaryAsync(commitId.ToString())).MustHaveHappened(); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetDomainObjectTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetDomainObjectTests.cs deleted file mode 100644 index 58ceac5d5..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Assets/AssetDomainObjectTests.cs +++ /dev/null @@ -1,235 +0,0 @@ -// ========================================================================== -// AssetDomainObjectTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.IO; -using Squidex.Domain.Apps.Events.Assets; -using Squidex.Domain.Apps.Write.Assets.Commands; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure; -using Squidex.Infrastructure.Assets; -using Squidex.Infrastructure.CQRS; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Assets -{ - public class AssetDomainObjectTests : HandlerTestBase - { - private readonly AssetDomainObject sut; - private readonly ImageInfo image = new ImageInfo(2048, 2048); - private readonly AssetFile file = new AssetFile("my-image.png", "image/png", 1024, () => new MemoryStream()); - - public Guid AssetId { get; } = Guid.NewGuid(); - - public AssetDomainObjectTests() - { - sut = new AssetDomainObject(AssetId, 0); - } - - [Fact] - public void Create_should_throw_exception_if_created() - { - sut.Create(new CreateAsset { File = file }); - - Assert.Throws(() => - { - sut.Create(CreateAssetCommand(new CreateAsset { File = file })); - }); - } - - [Fact] - public void Create_should_create_events() - { - sut.Create(CreateAssetCommand(new CreateAsset { File = file, ImageInfo = image })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateAssetEvent(new AssetCreated - { - IsImage = true, - FileName = file.FileName, - FileSize = file.FileSize, - FileVersion = 0, - MimeType = file.MimeType, - PixelWidth = image.PixelWidth, - PixelHeight = image.PixelHeight - }) - ); - } - - [Fact] - public void Update_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Update(CreateAssetCommand(new UpdateAsset { File = file })); - }); - } - - [Fact] - public void Update_should_throw_exception_if_asset_is_deleted() - { - CreateAsset(); - DeleteAsset(); - - Assert.Throws(() => - { - sut.Update(CreateAssetCommand(new UpdateAsset())); - }); - } - - [Fact] - public void Update_should_create_events() - { - CreateAsset(); - - sut.Update(CreateAssetCommand(new UpdateAsset { File = file, ImageInfo = image })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateAssetEvent(new AssetUpdated - { - IsImage = true, - FileSize = file.FileSize, - FileVersion = 1, - MimeType = file.MimeType, - PixelWidth = image.PixelWidth, - PixelHeight = image.PixelHeight - }) - ); - } - - [Fact] - public void Rename_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "new-file.png" })); - }); - } - - [Fact] - public void Rename_should_throw_exception_if_asset_is_deleted() - { - CreateAsset(); - DeleteAsset(); - - Assert.Throws(() => - { - sut.Update(CreateAssetCommand(new UpdateAsset())); - }); - } - - [Fact] - public void Rename_should_throw_exception_if_command_is_not_valid() - { - CreateAsset(); - - Assert.Throws(() => - { - sut.Rename(CreateAssetCommand(new RenameAsset())); - }); - } - - [Fact] - public void Rename_should_throw_exception_if_new_name_is_equal_to_old_name() - { - CreateAsset(); - - Assert.Throws(() => - { - sut.Rename(CreateAssetCommand(new RenameAsset { FileName = file.FileName })); - }); - } - - [Fact] - public void Rename_should_create_events() - { - CreateAsset(); - - sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "my-new-image.png" })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateAssetEvent(new AssetRenamed { FileName = "my-new-image.png" }) - ); - } - - [Fact] - public void Delete_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Delete(CreateAssetCommand(new DeleteAsset())); - }); - } - - [Fact] - public void Delete_should_throw_exception_if_already_deleted() - { - CreateAsset(); - DeleteAsset(); - - Assert.Throws(() => - { - sut.Delete(CreateAssetCommand(new DeleteAsset())); - }); - } - - [Fact] - public void Delete_should_create_events_with_total_file_size() - { - CreateAsset(); - UpdateAsset(); - - sut.Delete(CreateAssetCommand(new DeleteAsset())); - - Assert.True(sut.IsDeleted); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateAssetEvent(new AssetDeleted { DeletedSize = 2048 }) - ); - } - - private void CreateAsset() - { - sut.Create(CreateAssetCommand(new CreateAsset { File = file })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void UpdateAsset() - { - sut.Update(CreateAssetCommand(new UpdateAsset { File = file })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void DeleteAsset() - { - sut.Delete(CreateAssetCommand(new DeleteAsset())); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - protected T CreateAssetEvent(T @event) where T : AssetEvent - { - @event.AssetId = AssetId; - - return CreateEvent(@event); - } - - protected T CreateAssetCommand(T command) where T : AssetAggregateCommand - { - command.AssetId = AssetId; - - return CreateCommand(command); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandMiddlewareTests.cs deleted file mode 100644 index 47f591378..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentCommandMiddlewareTests.cs +++ /dev/null @@ -1,240 +0,0 @@ -// ========================================================================== -// ContentCommandMiddlewareTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Security.Claims; -using System.Threading.Tasks; -using FakeItEasy; -using Squidex.Domain.Apps.Core; -using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Domain.Apps.Core.Scripting; -using Squidex.Domain.Apps.Read.Apps; -using Squidex.Domain.Apps.Read.Apps.Services; -using Squidex.Domain.Apps.Read.Assets.Repositories; -using Squidex.Domain.Apps.Read.Contents.Repositories; -using Squidex.Domain.Apps.Read.Schemas; -using Squidex.Domain.Apps.Read.Schemas.Services; -using Squidex.Domain.Apps.Write.Contents.Commands; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Commands; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Contents -{ - public class ContentCommandMiddlewareTests : HandlerTestBase - { - private readonly ContentCommandMiddleware sut; - private readonly ContentDomainObject content; - private readonly ISchemaProvider schemas = A.Fake(); - private readonly ISchemaEntity schema = A.Fake(); - private readonly IScriptEngine scriptEngine = A.Fake(); - private readonly IAppProvider appProvider = A.Fake(); - private readonly IAppEntity app = A.Fake(); - private readonly ClaimsPrincipal user = new ClaimsPrincipal(); - private readonly LanguagesConfig languagesConfig = LanguagesConfig.Create(Language.DE); - private readonly Guid contentId = Guid.NewGuid(); - - private readonly NamedContentData invalidData = - new NamedContentData() - .AddField("my-field", new ContentFieldData() - .SetValue(null)); - private readonly NamedContentData data = - new NamedContentData() - .AddField("my-field", new ContentFieldData() - .SetValue(1)); - - public ContentCommandMiddlewareTests() - { - var schemaDef = - Schema.Create("my-schema", new SchemaProperties()) - .AddField(new NumberField(1, "my-field", Partitioning.Invariant, - new NumberFieldProperties { IsRequired = true })); - - content = new ContentDomainObject(contentId, -1); - - sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy(), schemas, scriptEngine, A.Dummy()); - - A.CallTo(() => app.LanguagesConfig).Returns(languagesConfig); - A.CallTo(() => app.PartitionResolver).Returns(languagesConfig.ToResolver()); - - A.CallTo(() => appProvider.FindAppByIdAsync(AppId)).Returns(app); - - A.CallTo(() => schema.SchemaDef).Returns(schemaDef); - A.CallTo(() => schema.ScriptCreate).Returns(""); - A.CallTo(() => schema.ScriptChange).Returns(""); - A.CallTo(() => schema.ScriptUpdate).Returns(""); - A.CallTo(() => schema.ScriptDelete).Returns(""); - - A.CallTo(() => schemas.FindSchemaByIdAsync(SchemaId, false)).Returns(schema); - } - - [Fact] - public async Task Create_should_throw_exception_if_data_is_not_valid() - { - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)) - .Returns(invalidData); - - var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = invalidData, User = user }); - - await TestCreate(content, async _ => - { - await Assert.ThrowsAsync(() => sut.HandleAsync(context)); - }, false); - } - - [Fact] - public async Task Create_should_create_content() - { - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)) - .Returns(data); - - var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user }); - - await TestCreate(content, async _ => - { - await sut.HandleAsync(context); - }); - - Assert.Equal(data, context.Result>().IdOrValue); - - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")).MustHaveHappened(); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")).MustNotHaveHappened(); - } - - [Fact] - public async Task Create_should_also_invoke_publish_script_when_publishing() - { - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)) - .Returns(data); - - var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user, Publish = true }); - - await TestCreate(content, async _ => - { - await sut.HandleAsync(context); - }); - - Assert.Equal(data, context.Result>().IdOrValue); - - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")).MustHaveHappened(); - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")).MustHaveHappened(); - } - - [Fact] - public async Task Update_should_throw_exception_if_data_is_not_valid() - { - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)).Returns(invalidData); - - CreateContent(); - - var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = invalidData, User = user }); - - await TestUpdate(content, async _ => - { - await Assert.ThrowsAsync(() => sut.HandleAsync(context)); - }, false); - } - - [Fact] - public async Task Update_should_update_domain_object() - { - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)) - .Returns(data); - - CreateContent(); - - var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = data, User = user }); - - await TestUpdate(content, async _ => - { - await sut.HandleAsync(context); - }); - - Assert.Equal(data, context.Result().Data); - - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")).MustHaveHappened(); - } - - [Fact] - public async Task Patch_should_throw_exception_if_data_is_not_valid() - { - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)) - .Returns(invalidData); - - CreateContent(); - - var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = invalidData, User = user }); - - await TestUpdate(content, async _ => - { - await Assert.ThrowsAsync(() => sut.HandleAsync(context)); - }, false); - } - - [Fact] - public async Task Patch_should_update_domain_object() - { - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)) - .Returns(data); - - var patch = new NamedContentData().AddField("my-field", new ContentFieldData().SetValue(3)); - - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, A.Ignored)).Returns(patch); - - CreateContent(); - - var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = patch, User = user }); - - await TestUpdate(content, async _ => - { - await sut.HandleAsync(context); - }); - - Assert.NotNull(context.Result().Data); - - A.CallTo(() => scriptEngine.ExecuteAndTransform(A.Ignored, "")).MustHaveHappened(); - } - - [Fact] - public async Task ChangeStatus_should_publish_domain_object() - { - CreateContent(); - - var context = CreateContextForCommand(new ChangeContentStatus { ContentId = contentId, User = user, Status = Status.Published }); - - await TestUpdate(content, async _ => - { - await sut.HandleAsync(context); - }); - - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")).MustHaveHappened(); - } - - [Fact] - public async Task Delete_should_update_domain_object() - { - CreateContent(); - - var command = CreateContextForCommand(new DeleteContent { ContentId = contentId, User = user }); - - await TestUpdate(content, async _ => - { - await sut.HandleAsync(command); - }); - - A.CallTo(() => scriptEngine.Execute(A.Ignored, "")).MustHaveHappened(); - } - - private void CreateContent() - { - content.Create(new CreateContent { Data = data }); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentDomainObjectTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentDomainObjectTests.cs deleted file mode 100644 index 1cbeecbac..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentDomainObjectTests.cs +++ /dev/null @@ -1,323 +0,0 @@ -// ========================================================================== -// ContentDomainObjectTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using FluentAssertions; -using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Events.Contents; -using Squidex.Domain.Apps.Write.Contents.Commands; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Contents -{ - public class ContentDomainObjectTests : HandlerTestBase - { - private readonly ContentDomainObject sut; - private readonly NamedContentData data = - new NamedContentData() - .AddField("field1", - new ContentFieldData() - .AddValue("iv", 1)); - private readonly NamedContentData otherData = - new NamedContentData() - .AddField("field2", - new ContentFieldData() - .AddValue("iv", 2)); - - public Guid ContentId { get; } = Guid.NewGuid(); - - public ContentDomainObjectTests() - { - sut = new ContentDomainObject(ContentId, 0); - } - - [Fact] - public void Create_should_throw_exception_if_created() - { - sut.Create(new CreateContent { Data = data }); - - Assert.Throws(() => - { - sut.Create(CreateContentCommand(new CreateContent { Data = data })); - }); - } - - [Fact] - public void Create_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.Create(CreateContentCommand(new CreateContent())); - }); - } - - [Fact] - public void Create_should_create_events() - { - sut.Create(CreateContentCommand(new CreateContent { Data = data })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateContentEvent(new ContentCreated { Data = data }) - ); - } - - [Fact] - public void Create_should_also_publish_if_set_to_true() - { - sut.Create(CreateContentCommand(new CreateContent { Data = data, Publish = true })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateContentEvent(new ContentCreated { Data = data }), - CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) - ); - } - - [Fact] - public void Update_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Update(CreateContentCommand(new UpdateContent { Data = data })); - }); - } - - [Fact] - public void Update_should_throw_exception_if_content_is_deleted() - { - CreateContent(); - DeleteContent(); - - Assert.Throws(() => - { - sut.Update(CreateContentCommand(new UpdateContent())); - }); - } - - [Fact] - public void Update_should_throw_exception_if_command_is_not_valid() - { - CreateContent(); - - Assert.Throws(() => - { - sut.Update(CreateContentCommand(new UpdateContent())); - }); - } - - [Fact] - public void Update_should_create_events() - { - CreateContent(); - - sut.Update(CreateContentCommand(new UpdateContent { Data = otherData })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateContentEvent(new ContentUpdated { Data = otherData }) - ); - } - - [Fact] - public void Update_should_not_create_event_for_same_data() - { - CreateContent(); - UpdateContent(); - - sut.Update(CreateContentCommand(new UpdateContent { Data = data })); - - sut.GetUncomittedEvents().Should().BeEmpty(); - } - - [Fact] - public void Patch_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Patch(CreateContentCommand(new PatchContent { Data = data })); - }); - } - - [Fact] - public void Patch_should_throw_exception_if_content_is_deleted() - { - CreateContent(); - DeleteContent(); - - Assert.Throws(() => - { - sut.Patch(CreateContentCommand(new PatchContent())); - }); - } - - [Fact] - public void Patch_should_throw_exception_if_command_is_not_valid() - { - CreateContent(); - - Assert.Throws(() => - { - sut.Patch(CreateContentCommand(new PatchContent())); - }); - } - - [Fact] - public void Patch_should_create_events() - { - CreateContent(); - - sut.Patch(CreateContentCommand(new PatchContent { Data = otherData })); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateContentEvent(new ContentUpdated { Data = otherData }) - ); - } - - [Fact] - public void Patch_should_not_create_event_for_same_data() - { - CreateContent(); - UpdateContent(); - - sut.Patch(CreateContentCommand(new PatchContent { Data = data })); - - sut.GetUncomittedEvents().Should().BeEmpty(); - } - - [Fact] - public void ChangeStatus_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus())); - }); - } - - [Fact] - public void ChangeStatus_should_throw_exception_if_content_is_deleted() - { - CreateContent(); - DeleteContent(); - - Assert.Throws(() => - { - sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus())); - }); - } - - [Fact] - public void ChangeStatus_should_throw_exception_if_status_flow_not_valid() - { - CreateContent(); - ChangeStatus(Status.Archived); - - Assert.Throws(() => - { - sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published })); - }); - } - - [Fact] - public void ChangeStatus_should_refresh_properties_and_create_events() - { - CreateContent(); - - sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published })); - - Assert.Equal(Status.Published, sut.Status); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) - ); - } - - [Fact] - public void Delete_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Delete(CreateContentCommand(new DeleteContent())); - }); - } - - [Fact] - public void Delete_should_throw_exception_if_already_deleted() - { - CreateContent(); - DeleteContent(); - - Assert.Throws(() => - { - sut.Delete(CreateContentCommand(new DeleteContent())); - }); - } - - [Fact] - public void Delete_should_update_properties_create_events() - { - CreateContent(); - - sut.Delete(CreateContentCommand(new DeleteContent())); - - Assert.True(sut.IsDeleted); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateContentEvent(new ContentDeleted()) - ); - } - - private void CreateContent() - { - sut.Create(CreateContentCommand(new CreateContent { Data = data })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void UpdateContent() - { - sut.Update(CreateContentCommand(new UpdateContent { Data = data })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void ChangeStatus(Status status) - { - sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = status })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void DeleteContent() - { - sut.Delete(CreateContentCommand(new DeleteContent())); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - protected T CreateContentEvent(T @event) where T : ContentEvent - { - @event.ContentId = ContentId; - - return CreateEvent(@event); - } - - protected T CreateContentCommand(T command) where T : ContentCommand - { - command.ContentId = ContentId; - - return CreateCommand(command); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs deleted file mode 100644 index dcdfe4fee..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentEventTests.cs +++ /dev/null @@ -1,70 +0,0 @@ -// ========================================================================== -// SchemaEventTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Events.Contents; -using Squidex.Domain.Apps.Events.Contents.Old; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Infrastructure; -using Xunit; - -#pragma warning disable CS0612 // Type or member is obsolete - -namespace Squidex.Domain.Apps.Write.Contents -{ - public class ContentEventTests - { - private readonly RefToken actor = new RefToken("User", Guid.NewGuid().ToString()); - private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); - private readonly NamedId schemaId = new NamedId(Guid.NewGuid(), "my-schema"); - private readonly Guid contentId = Guid.NewGuid(); - - [Fact] - public void Should_migrate_content_published_to_content_status_changed() - { - var source = CreateEvent(new ContentPublished()); - - source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Published })); - } - - [Fact] - public void Should_migrate_content_unpublished_to_content_status_changed() - { - var source = CreateEvent(new ContentUnpublished()); - - source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft })); - } - - [Fact] - public void Should_migrate_content_restored_to_content_status_changed() - { - var source = CreateEvent(new ContentRestored()); - - source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Draft })); - } - - [Fact] - public void Should_migrate_content_archived_to_content_status_changed() - { - var source = CreateEvent(new ContentArchived()); - - source.Migrate().ShouldBeSameEvent(CreateEvent(new ContentStatusChanged { Status = Status.Archived })); - } - - private T CreateEvent(T contentEvent) where T : ContentEvent - { - contentEvent.Actor = actor; - contentEvent.AppId = appId; - contentEvent.SchemaId = schemaId; - contentEvent.ContentId = contentId; - - return contentEvent; - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs deleted file mode 100644 index 5a4cf45ed..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Contents/ContentVersionLoaderTests.cs +++ /dev/null @@ -1,139 +0,0 @@ -// ========================================================================== -// ContentVersionLoaderTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using FakeItEasy; -using Squidex.Domain.Apps.Core.Contents; -using Squidex.Domain.Apps.Events.Contents; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Events; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Contents -{ - public class ContentVersionLoaderTests - { - private readonly IEventStore eventStore = A.Fake(); - private readonly IStreamNameResolver nameResolver = A.Fake(); - private readonly EventDataFormatter formatter = A.Fake(); - private readonly Guid id = Guid.NewGuid(); - private readonly Guid appId = Guid.NewGuid(); - private readonly string streamName = Guid.NewGuid().ToString(); - private readonly ContentVersionLoader sut; - - public ContentVersionLoaderTests() - { - A.CallTo(() => nameResolver.GetStreamName(typeof(ContentDomainObject), id)) - .Returns(streamName); - - sut = new ContentVersionLoader(eventStore, nameResolver, formatter); - } - - [Fact] - public async Task Should_throw_exception_when_event_store_returns_no_events() - { - A.CallTo(() => eventStore.GetEventsAsync(streamName)) - .Returns(new List()); - - await Assert.ThrowsAsync(() => sut.LoadAsync(appId, id, -1)); - } - - [Fact] - public async Task Should_throw_exception_when_version_not_found() - { - A.CallTo(() => eventStore.GetEventsAsync(streamName)) - .Returns(new List()); - - await Assert.ThrowsAsync(() => sut.LoadAsync(appId, id, 3)); - } - - [Fact] - public async Task Should_throw_exception_when_content_is_from_another_event() - { - var eventData1 = new EventData(); - - var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId(Guid.NewGuid(), "my-app") }; - - var events = new List - { - new StoredEvent("0", 0, eventData1) - }; - - A.CallTo(() => eventStore.GetEventsAsync(streamName)) - .Returns(events); - - A.CallTo(() => formatter.Parse(eventData1, true)) - .Returns(new Envelope(event1)); - - await Assert.ThrowsAsync(() => sut.LoadAsync(appId, id, 0)); - } - - [Fact] - public async Task Should_load_content_from_created_event() - { - var eventData1 = new EventData(); - var eventData2 = new EventData(); - - var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId(appId, "my-app") }; - var event2 = new ContentStatusChanged(); - - var events = new List - { - new StoredEvent("0", 0, eventData1), - new StoredEvent("1", 1, eventData2) - }; - - A.CallTo(() => eventStore.GetEventsAsync(streamName)) - .Returns(events); - - A.CallTo(() => formatter.Parse(eventData1, true)) - .Returns(new Envelope(event1)); - A.CallTo(() => formatter.Parse(eventData2, true)) - .Returns(new Envelope(event2)); - - var data = await sut.LoadAsync(appId, id, 3); - - Assert.Same(event1.Data, data); - } - - [Fact] - public async Task Should_load_content_from_correct_version() - { - var eventData1 = new EventData(); - var eventData2 = new EventData(); - var eventData3 = new EventData(); - - var event1 = new ContentCreated { Data = new NamedContentData(), AppId = new NamedId(appId, "my-app") }; - var event2 = new ContentUpdated { Data = new NamedContentData() }; - var event3 = new ContentUpdated { Data = new NamedContentData() }; - - var events = new List - { - new StoredEvent("0", 0, eventData1), - new StoredEvent("1", 1, eventData2), - new StoredEvent("2", 2, eventData3) - }; - - A.CallTo(() => eventStore.GetEventsAsync(streamName)) - .Returns(events); - - A.CallTo(() => formatter.Parse(eventData1, true)) - .Returns(new Envelope(event1)); - A.CallTo(() => formatter.Parse(eventData2, true)) - .Returns(new Envelope(event2)); - A.CallTo(() => formatter.Parse(eventData3, true)) - .Returns(new Envelope(event3)); - - var data = await sut.LoadAsync(appId, id, 1); - - Assert.Equal(event2.Data, data); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs index 727a934bd..4e299748c 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/NumberFieldPropertiesTests.cs @@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties [Fact] public void Should_add_error_if_allowed_values_and_max_value_is_specified() { - var sut = new NumberFieldProperties { MaxValue = 10, AllowedValues = ImmutableList.Create(4) }; + var sut = new NumberFieldProperties { MaxValue = 10, AllowedValues = new[] { 4d } }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); @@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties [Fact] public void Should_add_error_if_allowed_values_and_min_value_is_specified() { - var sut = new NumberFieldProperties { MinValue = 10, AllowedValues = ImmutableList.Create(4) }; + var sut = new NumberFieldProperties { MinValue = 10, AllowedValues = new[] { 4d } }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs index 19c306680..5dba533d7 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/FieldProperties/StringFieldPropertiesTests.cs @@ -35,7 +35,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties [Fact] public void Should_add_error_if_allowed_values_and_max_value_is_specified() { - var sut = new StringFieldProperties { MinLength = 10, AllowedValues = ImmutableList.Create("4") }; + var sut = new StringFieldProperties { MinLength = 10, AllowedValues = new[] { "4" } }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); @@ -49,7 +49,7 @@ namespace Squidex.Domain.Apps.Write.Schemas.Guards.FieldProperties [Fact] public void Should_add_error_if_allowed_values_and_min_value_is_specified() { - var sut = new StringFieldProperties { MaxLength = 10, AllowedValues = ImmutableList.Create("4") }; + var sut = new StringFieldProperties { MaxLength = 10, AllowedValues = new string[] { "4" } }; var errors = FieldPropertiesValidator.Validate(sut).ToList(); diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs new file mode 100644 index 000000000..cf61d7f51 --- /dev/null +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaFieldTests.cs @@ -0,0 +1,234 @@ +// ========================================================================== +// GuardSchemaFieldTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Write.Schemas.Commands; +using Squidex.Domain.Apps.Write.Schemas.Guards; +using Squidex.Infrastructure; +using Xunit; + +namespace Squidex.Domain.Apps.Write.Schemas +{ + public class GuardSchemaFieldTests + { + private readonly Schema schema = new Schema("my-schema"); + + public GuardSchemaFieldTests() + { + schema.AddField(new StringField(1, "field1", Partitioning.Invariant)); + schema.AddField(new StringField(2, "field2", Partitioning.Invariant)); + } + + [Fact] + public void CanHide_should_throw_exception_if_already_hidden() + { + var command = new HideField { FieldId = 1 }; + + schema.FieldsById[1].Hide(); + + Assert.Throws(() => GuardSchemaField.CanHide(schema, command)); + } + + [Fact] + public void CanHide_should_throw_exception_if_not_found() + { + var command = new HideField { FieldId = 3 }; + + Assert.Throws(() => GuardSchemaField.CanHide(schema, command)); + } + + [Fact] + public void CanHide_hould_not_throw_exception_if_visible() + { + var command = new HideField { FieldId = 1 }; + + GuardSchemaField.CanHide(schema, command); + } + + [Fact] + public void CanDisable_should_throw_exception_if_already_disabled() + { + var command = new DisableField { FieldId = 1 }; + + schema.FieldsById[1].Disable(); + + Assert.Throws(() => GuardSchemaField.CanDisable(schema, command)); + } + + [Fact] + public void CanDisable_should_throw_exception_if_not_found() + { + var command = new DisableField { FieldId = 3 }; + + Assert.Throws(() => GuardSchemaField.CanDisable(schema, command)); + } + + [Fact] + public void CanDisable_Should_not_throw_exception_if_enabled() + { + var command = new DisableField { FieldId = 1 }; + + GuardSchemaField.CanDisable(schema, command); + } + + [Fact] + public void CanShow_should_throw_exception_if_already_shown() + { + var command = new ShowField { FieldId = 1 }; + + Assert.Throws(() => GuardSchemaField.CanShow(schema, command)); + } + + [Fact] + public void CanShow_should_throw_exception_if_not_found() + { + var command = new ShowField { FieldId = 3 }; + + Assert.Throws(() => GuardSchemaField.CanShow(schema, command)); + } + + [Fact] + public void CanShow_should_not_throw_exception_if_hidden() + { + var command = new ShowField { FieldId = 1 }; + + schema.FieldsById[1].Hide(); + + GuardSchemaField.CanShow(schema, command); + } + + [Fact] + public void CanEnable_should_throw_exception_if_already_enabled() + { + var command = new EnableField { FieldId = 1 }; + + Assert.Throws(() => GuardSchemaField.CanEnable(schema, command)); + } + + [Fact] + public void CanEnable_should_throw_exception_if_not_found() + { + var command = new EnableField { FieldId = 3 }; + + Assert.Throws(() => GuardSchemaField.CanEnable(schema, command)); + } + + [Fact] + public void CanEnable_should_not_throw_exception_if_disabled() + { + var command = new EnableField { FieldId = 1 }; + + schema.FieldsById[1].Disable(); + + GuardSchemaField.CanEnable(schema, command); + } + + [Fact] + public void CanLock_should_throw_exception_if_already_locked() + { + var command = new LockField { FieldId = 1 }; + + schema.FieldsById[1].Lock(); + + Assert.Throws(() => GuardSchemaField.CanLock(schema, command)); + } + + [Fact] + public void LockField_should_throw_exception_if_not_found() + { + var command = new LockField { FieldId = 3 }; + + Assert.Throws(() => GuardSchemaField.CanLock(schema, command)); + } + + [Fact] + public void CanLock_should_not_throw_exception_if_not_locked() + { + var command = new LockField { FieldId = 1 }; + + GuardSchemaField.CanLock(schema, command); + } + + [Fact] + public void CanDelete_should_throw_exception_if_not_found() + { + var command = new DeleteField { FieldId = 3 }; + + Assert.Throws(() => GuardSchemaField.CanDelete(schema, command)); + } + + [Fact] + public void CanDelete_should_throw_exception_if_locked() + { + var command = new DeleteField { FieldId = 1 }; + + schema.FieldsById[1].Lock(); + + Assert.Throws(() => GuardSchemaField.CanDelete(schema, command)); + } + + [Fact] + public void CanDelete_should_not_throw_exception_if_not_locked() + { + var command = new DeleteField { FieldId = 1 }; + + GuardSchemaField.CanDelete(schema, command); + } + + [Fact] + public void CanUpdate_should_throw_exception_if_locked() + { + var command = new UpdateField { FieldId = 1, Properties = new StringFieldProperties() }; + + schema.FieldsById[1].Lock(); + + Assert.Throws(() => GuardSchemaField.CanUpdate(schema, command)); + } + + [Fact] + public void CanUpdate_should_not_throw_exception_if_not_locked() + { + var command = new UpdateField { FieldId = 1, Properties = new StringFieldProperties() }; + + GuardSchemaField.CanUpdate(schema, command); + } + + [Fact] + public void CanAdd_should_throw_exception_if_field_already_exists() + { + var command = new AddField { Name = "field1", Properties = new StringFieldProperties() }; + + Assert.Throws(() => GuardSchemaField.CanAdd(schema, command)); + } + + [Fact] + public void CanAdd_should_throw_exception_if_name_not_valid() + { + var command = new AddField { Name = "INVALID_NAME", Properties = new StringFieldProperties() }; + + Assert.Throws(() => GuardSchemaField.CanAdd(schema, command)); + } + + [Fact] + public void CanAdd_should_throw_exception_if_properties_not_valid() + { + var command = new AddField { Name = "field3", Properties = new StringFieldProperties { MinLength = 10, MaxLength = 5 } }; + + Assert.Throws(() => GuardSchemaField.CanAdd(schema, command)); + } + + [Fact] + public void CanAdd_should_not_throw_exception_if_field_not_exists() + { + var command = new AddField { Name = "field3", Properties = new StringFieldProperties() }; + + GuardSchemaField.CanAdd(schema, command); + } + } +} \ No newline at end of file diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaTests.cs new file mode 100644 index 000000000..901d61b67 --- /dev/null +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/GuardSchemaTests.cs @@ -0,0 +1,197 @@ +// ========================================================================== +// GuardSchemaTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using FakeItEasy; +using Squidex.Domain.Apps.Core; +using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Read.Schemas; +using Squidex.Domain.Apps.Read.Schemas.Services; +using Squidex.Domain.Apps.Write.Schemas.Commands; +using Squidex.Infrastructure; +using Xunit; + +namespace Squidex.Domain.Apps.Write.Schemas.Guards +{ + public class GuardSchemaTests + { + private readonly ISchemaProvider schemas = A.Fake(); + private readonly Schema schema = new Schema("my-schema"); + private readonly NamedId appId = new NamedId(Guid.NewGuid(), "my-app"); + + public GuardSchemaTests() + { + schema.AddField(new StringField(1, "field1", Partitioning.Invariant)); + schema.AddField(new StringField(2, "field2", Partitioning.Invariant)); + + A.CallTo(() => schemas.FindSchemaByNameAsync(A.Ignored, "new-schema")) + .Returns(Task.FromResult(null)); + } + + [Fact] + public async Task CanCreate_should_throw_exception_if_name_not_valid() + { + var command = new CreateSchema { AppId = appId, Name = "INVALID NAME" }; + + await Assert.ThrowsAsync(() => GuardSchema.CanCreate(command, schemas)); + } + + [Fact] + public async Task CanCreate_should_throw_exception_if_name_already_in_use() + { + A.CallTo(() => schemas.FindSchemaByNameAsync(A.Ignored, "new-schema")) + .Returns(Task.FromResult(A.Fake())); + + var command = new CreateSchema { AppId = appId, Name = "new-schema" }; + + await Assert.ThrowsAsync(() => GuardSchema.CanCreate(command, schemas)); + } + + [Fact] + public async Task CanCreate_should_throw_exception_if_fields_not_valid() + { + var command = new CreateSchema + { + AppId = appId, + Fields = new List + { + new CreateSchemaField + { + Name = null, + Properties = null, + Partitioning = "invalid", + }, + new CreateSchemaField + { + Name = null, + Properties = InvalidProperties(), + Partitioning = "invalid", + } + }, + Name = "new-schema" + }; + + await Assert.ThrowsAsync(() => GuardSchema.CanCreate(command, schemas)); + } + + [Fact] + public async Task CanCreate_should_throw_exception_if_fields_contain_duplicate_names() + { + var command = new CreateSchema + { + AppId = appId, + Fields = new List + { + new CreateSchemaField + { + Name = "field1", + Properties = ValidProperties(), + Partitioning = "invariant" + }, + new CreateSchemaField + { + Name = "field1", + Properties = ValidProperties(), + Partitioning = "invariant" + } + }, + Name = "new-schema" + }; + + await Assert.ThrowsAsync(() => GuardSchema.CanCreate(command, schemas)); + } + + [Fact] + public async Task CanCreate_should_not_throw_exception_if_command_is_valid() + { + var command = new CreateSchema { AppId = appId, Name = "new-schema" }; + + await GuardSchema.CanCreate(command, schemas); + } + + [Fact] + public void CanPublish_should_throw_exception_if_already_published() + { + var command = new PublishSchema(); + + schema.Publish(); + + Assert.Throws(() => GuardSchema.CanPublish(schema, command)); + } + + [Fact] + public void CanPublish_should_not_throw_exception_if_not_published() + { + var command = new PublishSchema(); + + GuardSchema.CanPublish(schema, command); + } + + [Fact] + public void CanUnpublish_should_throw_exception_if_already_unpublished() + { + var command = new UnpublishSchema(); + + Assert.Throws(() => GuardSchema.CanUnpublish(schema, command)); + } + + [Fact] + public void CanUnpublish_should_not_throw_exception_if_already_published() + { + var command = new UnpublishSchema(); + + schema.Publish(); + + GuardSchema.CanUnpublish(schema, command); + } + + [Fact] + public void CanReorder_should_throw_exception_if_field_ids_contains_invalid_id() + { + var command = new ReorderFields { FieldIds = new List { 1, 3 } }; + + Assert.Throws(() => GuardSchema.CanReorder(schema, command)); + } + + [Fact] + public void CanReorder_should_throw_exception_if_field_ids_do_not_covers_all_fields() + { + var command = new ReorderFields { FieldIds = new List { 1 } }; + + Assert.Throws(() => GuardSchema.CanReorder(schema, command)); + } + + [Fact] + public void CanReorder_should_not_throw_exception_if_field_ids_are_valid() + { + var command = new ReorderFields { FieldIds = new List { 1, 2 } }; + + GuardSchema.CanReorder(schema, command); + } + + [Fact] + public void CanDelete_should_not_throw_exception() + { + var command = new DeleteSchema(); + + GuardSchema.CanDelete(schema, command); + } + + private static StringFieldProperties ValidProperties() + { + return new StringFieldProperties { MinLength = 10, MaxLength = 20 }; + } + + private static StringFieldProperties InvalidProperties() + { + return new StringFieldProperties { MinLength = 20, MaxLength = 10 }; + } + } +} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaFieldGuardTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaFieldGuardTests.cs deleted file mode 100644 index bbfcb89f9..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaFieldGuardTests.cs +++ /dev/null @@ -1,169 +0,0 @@ -// ========================================================================== -// SchemaFieldGuardTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using Squidex.Domain.Apps.Core; -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Schemas.Guards -{ - public class SchemaFieldGuardTests - { - private Schema schema = - Schema.Create("my-schema", new SchemaProperties()) - .AddField(new StringField(1, "field1", Partitioning.Invariant)) - .AddField(new StringField(2, "field2", Partitioning.Invariant)); - - [Fact] - public void Should_throw_exception_if_field_to_hide_already_hidden() - { - schema = schema.HideField(1); - - Assert.Throws(() => SchemaFieldGuard.GuardCanHide(schema, 1)); - } - - [Fact] - public void Should_throw_exception_if_field_to_hide_not_found() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanHide(schema, 3)); - } - - [Fact] - public void Should_not_throw_exception_if_field_to_hide_shown() - { - SchemaFieldGuard.GuardCanHide(schema, 1); - } - - [Fact] - public void Should_throw_exception_if_field_to_disable_not_found() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanDisable(schema, 3)); - } - - [Fact] - public void Should_throw_exception_if_field_to_disable_already_disabled() - { - schema = schema.DisableField(1); - - Assert.Throws(() => SchemaFieldGuard.GuardCanDisable(schema, 1)); - } - - [Fact] - public void Should_not_throw_exception_if_field_to_disable_shown() - { - SchemaFieldGuard.GuardCanDisable(schema, 1); - } - - [Fact] - public void Should_throw_exception_if_field_to_show_already_shown() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanShow(schema, 1)); - } - - [Fact] - public void Should_throw_exception_if_field_to_show_not_found() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanShow(schema, 3)); - } - - [Fact] - public void Should_not_throw_exception_if_field_to_show_hidden() - { - schema = schema.HideField(1); - - SchemaFieldGuard.GuardCanShow(schema, 1); - } - - [Fact] - public void Should_throw_exception_if_field_to_enable_already_enabled() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanEnable(schema, 1)); - } - - [Fact] - public void Should_throw_exception_if_field_to_enable_not_found() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanEnable(schema, 3)); - } - - [Fact] - public void Should_not_throw_exception_if_field_to_enable_disabled() - { - schema = schema.DisableField(1); - - SchemaFieldGuard.GuardCanEnable(schema, 1); - } - - [Fact] - public void Should_throw_exception_if_field_to_lock_already_locked() - { - schema = schema.LockField(1); - - Assert.Throws(() => SchemaFieldGuard.GuardCanLock(schema, 1)); - } - - [Fact] - public void Should_throw_exception_if_field_to_lock_not_found() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanLock(schema, 3)); - } - - [Fact] - public void Should_not_throw_exception_if_field_to_lock_not_locked() - { - SchemaFieldGuard.GuardCanLock(schema, 1); - } - - [Fact] - public void Should_throw_exception_if_field_to_delete_not_found() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanDelete(schema, 3)); - } - - [Fact] - public void Should_throw_exception_if_field_to_delete_is_locked() - { - schema = schema.LockField(1); - - Assert.Throws(() => SchemaFieldGuard.GuardCanDelete(schema, 1)); - } - - [Fact] - public void Should_throw_exception_if_field_to_update_not_locked() - { - SchemaFieldGuard.GuardCanUpdate(schema, 1); - } - - [Fact] - public void Should_throw_exception_if_field_to_update_is_locked() - { - schema = schema.LockField(1); - - Assert.Throws(() => SchemaFieldGuard.GuardCanUpdate(schema, 1)); - } - - [Fact] - public void Should_throw_exception_if_field_to_delete_not_locked() - { - SchemaFieldGuard.GuardCanDelete(schema, 1); - } - - [Fact] - public void Should_throw_exception_if_field_to_add_already_exists() - { - Assert.Throws(() => SchemaFieldGuard.GuardCanAdd(schema, "field1")); - } - - [Fact] - public void Should_not_throw_exception_if_field_to_add_not_exists() - { - SchemaFieldGuard.GuardCanAdd(schema, "field3"); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaGuardTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaGuardTests.cs deleted file mode 100644 index 2b60fe010..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/Guards/SchemaGuardTests.cs +++ /dev/null @@ -1,65 +0,0 @@ -// ========================================================================== -// SchemaGuardTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System.Collections.Generic; -using Squidex.Domain.Apps.Core; -using Squidex.Domain.Apps.Core.Schemas; -using Squidex.Infrastructure; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Schemas.Guards -{ - public class SchemaGuardTests - { - private Schema schema = - Schema.Create("my-schema", new SchemaProperties()) - .AddField(new StringField(1, "field1", Partitioning.Invariant)) - .AddField(new StringField(2, "field2", Partitioning.Invariant)); - - [Fact] - public void Should_throw_exception_if_schema_to_publish_already_published() - { - schema = schema.Publish(); - - Assert.Throws(() => SchemaGuard.GuardCanPublish(schema)); - } - - [Fact] - public void Should_not_throw_exception_if_schema_to_publish_not_published() - { - SchemaGuard.GuardCanPublish(schema); - } - - [Fact] - public void Should_throw_exception_if_schema_to_unpublish_already_unpublished() - { - Assert.Throws(() => SchemaGuard.GuardCanUnpublish(schema)); - } - - [Fact] - public void Should_not_throw_exception_if_schema_to_unpublish_published() - { - schema = schema.Publish(); - - SchemaGuard.GuardCanUnpublish(schema); - } - - [Fact] - public void Should_throw_excepotion_if_schema_fields_to_reorder_not_valid() - { - Assert.Throws(() => SchemaGuard.GuardCanReorder(schema, new List { 1 })); - Assert.Throws(() => SchemaGuard.GuardCanReorder(schema, new List { 1, 3 })); - } - - [Fact] - public void Should_not_throw_excepotion_if_schema_fields_to_reorder_are_valid() - { - SchemaGuard.GuardCanReorder(schema, new List { 1, 2 }); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandMiddlewareTests.cs index 78c4152c2..13c8b47aa 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandMiddlewareTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaCommandMiddlewareTests.cs @@ -34,32 +34,16 @@ namespace Squidex.Domain.Apps.Write.Schemas schema = new SchemaDomainObject(SchemaId, -1, registry); sut = new SchemaCommandMiddleware(Handler, schemas); - } - - [Fact] - public async Task Create_should_throw_exception_if_a_name_with_same_name_already_exists() - { - var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId }); A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)) - .Returns(A.Dummy()); - - await TestCreate(schema, async _ => - { - await Assert.ThrowsAsync(async () => await sut.HandleAsync(context)); - }, false); - - A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)).MustHaveHappened(); + .Returns((ISchemaEntity)null); } [Fact] - public async Task Create_should_create_schema_if_name_is_free() + public async Task Create_should_create_schema_domain_object() { var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId }); - A.CallTo(() => schemas.FindSchemaByNameAsync(AppId, SchemaName)) - .Returns((ISchemaEntity)null); - await TestCreate(schema, async _ => { await sut.HandleAsync(context); @@ -291,4 +275,4 @@ namespace Squidex.Domain.Apps.Write.Schemas schema.DisableField(CreateCommand(new DisableField { FieldId = 1 })); } } -} +} \ No newline at end of file diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs index 34a8d0f81..5fe9652a0 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs +++ b/tests/Squidex.Domain.Apps.Write.Tests/Schemas/SchemaDomainObjectTests.cs @@ -44,69 +44,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void Create_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.Create(CreateCommand(new CreateSchema())); - }); - } - - [Fact] - public void Create_should_throw_exception_if_fields_are_not_valid() - { - var properties = new SchemaProperties(); - - var fields = new List - { - new CreateSchemaField - { - Name = null, - Properties = null, - Partitioning = "invalid", - }, - new CreateSchemaField - { - Name = null, - Properties = InvalidProperties(), - Partitioning = "invalid", - } - }; - - Assert.Throws(() => - { - sut.Create(CreateCommand(new CreateSchema { Name = SchemaName, Properties = properties, Fields = fields })); - }); - } - - [Fact] - public void Create_should_throw_exception_if_fields_contain_duplicate_names() - { - var properties = new SchemaProperties(); - - var fields = new List - { - new CreateSchemaField - { - Name = "field1", - Properties = ValidProperties(), - Partitioning = "invariant" - }, - new CreateSchemaField - { - Name = "field1", - Properties = ValidProperties(), - Partitioning = "invariant" - } - }; - - Assert.Throws(() => - { - sut.Create(CreateCommand(new CreateSchema { Name = SchemaName, Properties = properties, Fields = fields })); - }); - } - [Fact] public void Create_should_create_schema_and_create_events() { @@ -118,7 +55,7 @@ namespace Squidex.Domain.Apps.Write.Schemas sut.GetUncomittedEvents() .ShouldHaveSameEvents( - CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties, Fields = new List() }) + CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties }) ); } @@ -162,17 +99,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void Update_should_throw_exception_if_command_is_not_valid() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.Update(CreateCommand(new UpdateSchema())); - }); - } - [Fact] public void Update_should_refresh_properties_and_create_events() { @@ -259,17 +185,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void Reorder_should_throw_exception_if_command_is_not_valid() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.Reorder(CreateCommand(new ReorderFields())); - }); - } - [Fact] public void Reorder_should_refresh_properties_and_create_events() { @@ -408,33 +323,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void AddField_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.Add(CreateCommand(new AddField())); - }); - } - - [Fact] - public void AddField_should_throw_exception_if_command_contains_invalid_partitioning() - { - Assert.Throws(() => - { - sut.Add(CreateCommand(new AddField { Name = fieldName, Partitioning = "invalid", Properties = ValidProperties() })); - }); - } - - [Fact] - public void AddField_should_throw_exception_if_command_contains_invalid_properties() - { - Assert.Throws(() => - { - sut.Add(CreateCommand(new AddField { Name = fieldName, Properties = InvalidProperties() })); - }); - } - [Fact] public void AddField_should_throw_exception_if_schema_is_deleted() { @@ -473,26 +361,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void UpdateField_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.UpdateField(CreateCommand(new UpdateField())); - }); - } - - [Fact] - public void UpdateField_should_throw_exception_if_field_is_not_found() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.UpdateField(CreateCommand(new UpdateField { FieldId = 1, Properties = new NumberFieldProperties() })); - }); - } - [Fact] public void UpdateField_should_throw_exception_if_schema_is_deleted() { @@ -532,17 +400,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void LockField_should_throw_exception_if_field_is_not_found() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.LockField(CreateCommand(new LockField { FieldId = 2 })); - }); - } - [Fact] public void LockField_should_throw_exception_if_schema_is_deleted() { @@ -580,17 +437,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void HideField_should_throw_exception_if_field_is_not_found() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.HideField(CreateCommand(new HideField { FieldId = 2 })); - }); - } - [Fact] public void HideField_should_throw_exception_if_schema_is_deleted() { @@ -628,17 +474,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void ShowField_should_throw_exception_if_field_is_not_found() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.ShowField(CreateCommand(new ShowField { FieldId = 2 })); - }); - } - [Fact] public void ShowField_should_throw_exception_if_schema_is_deleted() { @@ -677,17 +512,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void DisableField_should_throw_exception_if_field_is_not_found() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.DisableField(CreateCommand(new DisableField { FieldId = 2 })); - }); - } - [Fact] public void DisableField_should_throw_exception_if_schema_is_deleted() { @@ -725,17 +549,6 @@ namespace Squidex.Domain.Apps.Write.Schemas }); } - [Fact] - public void EnableField_should_throw_exception_if_field_is_not_found() - { - CreateSchema(); - - Assert.Throws(() => - { - sut.EnableField(CreateCommand(new EnableField { FieldId = 2 })); - }); - } - [Fact] public void EnableField_should_throw_exception_if_schema_is_deleted() { diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj b/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj index 19ac1d18e..b049b5e7c 100644 --- a/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj +++ b/tests/Squidex.Domain.Apps.Write.Tests/Squidex.Domain.Apps.Write.Tests.csproj @@ -5,9 +5,10 @@ Squidex.Domain.Apps.Write - - + + + diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs deleted file mode 100644 index b40b82739..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookCommandMiddlewareTests.cs +++ /dev/null @@ -1,130 +0,0 @@ -// ========================================================================== -// WebhookCommandMiddlewareTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using FakeItEasy; -using Squidex.Domain.Apps.Core.Webhooks; -using Squidex.Domain.Apps.Read.Schemas; -using Squidex.Domain.Apps.Read.Schemas.Services; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Domain.Apps.Write.Webhooks.Commands; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS.Commands; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Webhooks -{ - public class WebhookCommandMiddlewareTests : HandlerTestBase - { - private readonly ISchemaProvider schemas = A.Fake(); - private readonly WebhookCommandMiddleware sut; - private readonly WebhookDomainObject webhook; - private readonly Uri url = new Uri("http://squidex.io"); - private readonly Guid schemaId = Guid.NewGuid(); - private readonly Guid webhookId = Guid.NewGuid(); - private readonly List webhookSchemas; - - public WebhookCommandMiddlewareTests() - { - webhook = new WebhookDomainObject(webhookId, -1); - - webhookSchemas = new List - { - new WebhookSchema { SchemaId = schemaId } - }; - - sut = new WebhookCommandMiddleware(Handler, schemas); - } - - [Fact] - public async Task Create_should_create_webhook() - { - var context = CreateContextForCommand(new CreateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - - A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(A.Fake()); - - await TestCreate(webhook, async _ => - { - await sut.HandleAsync(context); - }); - - A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); - } - - [Fact] - public async Task Create_should_throw_exception_when_schema_is_not_found() - { - var context = CreateContextForCommand(new CreateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - - A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns((ISchemaEntity)null); - - await Assert.ThrowsAsync(async () => - { - await TestCreate(webhook, async _ => - { - await sut.HandleAsync(context); - }); - }); - } - - [Fact] - public async Task Update_should_update_domain_object() - { - var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - - A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns(A.Fake()); - - CreateWebhook(); - - await TestUpdate(webhook, async _ => - { - await sut.HandleAsync(context); - }); - - A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).MustHaveHappened(); - } - - [Fact] - public async Task Update_should_throw_exception_when_schema_is_not_found() - { - var context = CreateContextForCommand(new UpdateWebhook { Schemas = webhookSchemas, Url = url, WebhookId = webhookId }); - - A.CallTo(() => schemas.FindSchemaByIdAsync(schemaId, false)).Returns((ISchemaEntity)null); - - CreateWebhook(); - - await Assert.ThrowsAsync(async () => - { - await TestCreate(webhook, async _ => - { - await sut.HandleAsync(context); - }); - }); - } - - [Fact] - public async Task Delete_should_update_domain_object() - { - CreateWebhook(); - - var command = CreateContextForCommand(new DeleteWebhook { WebhookId = webhookId }); - - await TestUpdate(webhook, async _ => - { - await sut.HandleAsync(command); - }); - } - - private void CreateWebhook() - { - webhook.Create(new CreateWebhook { Url = url }); - } - } -} diff --git a/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookDomainObjectTests.cs b/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookDomainObjectTests.cs deleted file mode 100644 index 2e93d1ff9..000000000 --- a/tests/Squidex.Domain.Apps.Write.Tests/Webhooks/WebhookDomainObjectTests.cs +++ /dev/null @@ -1,179 +0,0 @@ -// ========================================================================== -// WebhookDomainObjectTests.cs -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex Group -// All rights reserved. -// ========================================================================== - -using System; -using Squidex.Domain.Apps.Events.Webhooks; -using Squidex.Domain.Apps.Write.TestHelpers; -using Squidex.Domain.Apps.Write.Webhooks.Commands; -using Squidex.Infrastructure; -using Squidex.Infrastructure.CQRS; -using Xunit; - -namespace Squidex.Domain.Apps.Write.Webhooks -{ - public class WebhookDomainObjectTests : HandlerTestBase - { - private readonly Uri url = new Uri("http://squidex.io"); - private readonly WebhookDomainObject sut; - - public Guid WebhookId { get; } = Guid.NewGuid(); - - public WebhookDomainObjectTests() - { - sut = new WebhookDomainObject(WebhookId, 0); - } - - [Fact] - public void Create_should_throw_exception_if_created() - { - sut.Create(new CreateWebhook { Url = url }); - - Assert.Throws(() => - { - sut.Create(CreateWebhookCommand(new CreateWebhook { Url = url })); - }); - } - - [Fact] - public void Create_should_throw_exception_if_command_is_not_valid() - { - Assert.Throws(() => - { - sut.Create(CreateWebhookCommand(new CreateWebhook { Url = new Uri("/invalid", UriKind.Relative) })); - }); - } - - [Fact] - public void Create_should_create_events() - { - var command = new CreateWebhook { Url = url }; - - sut.Create(CreateWebhookCommand(command)); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateWebhookEvent(new WebhookCreated - { - Url = url, - Schemas = command.Schemas, - SharedSecret = command.SharedSecret, - WebhookId = command.WebhookId - }) - ); - } - - [Fact] - public void Update_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = url })); - }); - } - - [Fact] - public void Update_should_throw_exception_if_webhook_is_deleted() - { - CreateWebhook(); - DeleteWebhook(); - - Assert.Throws(() => - { - sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = url })); - }); - } - - [Fact] - public void Update_should_throw_exception_if_command_is_not_valid() - { - CreateWebhook(); - - Assert.Throws(() => - { - sut.Update(CreateWebhookCommand(new UpdateWebhook { Url = new Uri("/invalid", UriKind.Relative) })); - }); - } - - [Fact] - public void Update_should_create_events() - { - CreateWebhook(); - - var command = new UpdateWebhook { Url = url }; - - sut.Update(CreateWebhookCommand(command)); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateWebhookEvent(new WebhookUpdated { Url = url, Schemas = command.Schemas }) - ); - } - - [Fact] - public void Delete_should_throw_exception_if_not_created() - { - Assert.Throws(() => - { - sut.Delete(CreateWebhookCommand(new DeleteWebhook())); - }); - } - - [Fact] - public void Delete_should_throw_exception_if_already_deleted() - { - CreateWebhook(); - DeleteWebhook(); - - Assert.Throws(() => - { - sut.Delete(CreateWebhookCommand(new DeleteWebhook())); - }); - } - - [Fact] - public void Delete_should_update_properties_create_events() - { - CreateWebhook(); - - sut.Delete(CreateWebhookCommand(new DeleteWebhook())); - - sut.GetUncomittedEvents() - .ShouldHaveSameEvents( - CreateWebhookEvent(new WebhookDeleted()) - ); - } - - private void CreateWebhook() - { - sut.Create(CreateWebhookCommand(new CreateWebhook { Url = url })); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - private void DeleteWebhook() - { - sut.Delete(CreateWebhookCommand(new DeleteWebhook())); - - ((IAggregate)sut).ClearUncommittedEvents(); - } - - protected T CreateWebhookEvent(T @event) where T : WebhookEvent - { - @event.WebhookId = WebhookId; - - return CreateEvent(@event); - } - - protected T CreateWebhookCommand(T command) where T : WebhookAggregateCommand - { - command.WebhookId = WebhookId; - - return CreateCommand(command); - } - } -}