From 322980d9c9c39f3836d6b4817e013831be5c6c2e Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Wed, 6 Dec 2017 22:08:07 +0100 Subject: [PATCH] State updates fixed. --- .../MongoCollectionExtensions.cs | 2 +- .../Apps/AppDomainObject.cs | 63 +------- .../Apps/AppHistoryEventsCreator.cs | 18 +-- .../Apps/State/AppState.cs | 74 ++++++++- .../Assets/AssetDomainObject.cs | 24 +-- .../Assets/State/AssetState.cs | 38 ++++- .../EntityMapper.cs | 55 ++++--- .../Rules/RuleDequeuer.cs | 2 +- .../Rules/RuleDomainObject.cs | 22 +-- .../Rules/State/RuleState.cs | 36 ++++- .../Schemas/SchemaDomainObject.cs | 54 +------ .../Schemas/State/SchemaState.cs | 143 +++++++++++++++++- .../MongoXmlRepository.cs | 2 - .../Commands/DomainObjectBase.cs | 14 +- .../Triggers/ContentChangedTriggerTests.cs | 1 - .../TestHelpers/HandlerTestBase.cs | 10 ++ 16 files changed, 354 insertions(+), 204 deletions(-) diff --git a/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs b/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs index 2893111cc..b358f8511 100644 --- a/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs +++ b/src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs @@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb { public static Task CreateAsync(this IMongoCollection collection, SquidexEvent @event, EnvelopeHeaders headers, Action updater) where T : class, IEntity, new() { - var entity = EntityMapper.Create(@event, headers, updater); + var entity = new T().Update(@event, headers, updater); return collection.InsertOneIfNotExistsAsync(entity); } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs index 40593cc2c..d39102795 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs @@ -7,7 +7,6 @@ // ========================================================================== using System; -using System.Linq; using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.State; @@ -28,10 +27,6 @@ namespace Squidex.Domain.Apps.Entities.Apps var appId = new NamedId(command.AppId, command.Name); - UpdateState(command, s => { s.Id = appId.Id; s.Name = command.Name; }); - - UpdateContributors(command, c => c.Assign(command.Actor.Identifier, AppContributorPermission.Owner)); - RaiseEvent(SimpleMapper.Map(command, CreateInitalEvent(appId))); RaiseEvent(SimpleMapper.Map(command, CreateInitialOwner(appId, command))); RaiseEvent(SimpleMapper.Map(command, CreateInitialLanguage(appId))); @@ -43,27 +38,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateLanguages(command, l => - { - var fallback = command.Fallback; - - if (fallback != null && fallback.Count > 0) - { - var existingLangauges = l.OfType().Select(x => x.Language); - - fallback = fallback.Intersect(existingLangauges).ToList(); - } - - l = l.Set(new LanguageConfig(command.Language, command.IsOptional, fallback)); - - if (command.IsMaster) - { - l = l.MakeMaster(command.Language); - } - - return l; - }); - RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated())); return this; @@ -75,15 +49,11 @@ namespace Squidex.Domain.Apps.Entities.Apps if (!string.IsNullOrWhiteSpace(command.Name)) { - UpdateClients(command, c => c.Rename(command.Id, command.Name)); - RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed())); } if (command.Permission.HasValue) { - UpdateClients(command, c => c.Update(command.Id, command.Permission.Value)); - RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value })); } @@ -94,8 +64,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateContributors(command, c => c.Assign(command.ContributorId, command.Permission)); - RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned())); return this; @@ -105,8 +73,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateContributors(command, c => c.Remove(command.ContributorId)); - RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved())); return this; @@ -116,8 +82,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateClients(command, c => c.Add(command.Id, command.Secret)); - RaiseEvent(SimpleMapper.Map(command, new AppClientAttached())); return this; @@ -127,8 +91,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateClients(command, c => c.Revoke(command.Id)); - RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked())); return this; @@ -138,8 +100,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateLanguages(command, l => l.Set(new LanguageConfig(command.Language))); - RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded())); return this; @@ -149,8 +109,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateLanguages(command, l => l.Remove(command.Language)); - RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved())); return this; @@ -160,8 +118,6 @@ namespace Squidex.Domain.Apps.Entities.Apps { ThrowIfNotCreated(); - UpdateState(command, s => s.Plan = command.PlanId != null ? new AppPlan(command.Actor, command.PlanId) : null); - RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged())); return this; @@ -208,24 +164,9 @@ namespace Squidex.Domain.Apps.Entities.Apps } } - private void UpdateClients(ICommand command, Func updater) - { - UpdateState(command, s => s.Clients = updater(s.Clients)); - } - - private void UpdateContributors(ICommand command, Func updater) - { - UpdateState(command, s => s.Contributors = updater(s.Contributors)); - } - - private void UpdateLanguages(ICommand command, Func updater) - { - UpdateState(command, s => s.LanguagesConfig = updater(s.LanguagesConfig)); - } - - protected override AppState CloneState(ICommand command, Action updater) + protected override void OnRaised(Envelope @event) { - return State.Clone().Update((SquidexCommand)command, updater); + UpdateState(State.Apply(@event)); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs index 2d2cdd628..635387de5 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs @@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.Apps "changed master language to {[Language]}"); } - protected Task On(AppContributorRemoved @event, EnvelopeHeaders headers) + protected Task On(AppContributorRemoved @event) { const string channel = "settings.contributors"; @@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Contributor", @event.ContributorId)); } - protected Task On(AppContributorAssigned @event, EnvelopeHeaders headers) + protected Task On(AppContributorAssigned @event) { const string channel = "settings.contributors"; @@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Contributor", @event.ContributorId).AddParameter("Permission", @event.Permission)); } - protected Task On(AppClientAttached @event, EnvelopeHeaders headers) + protected Task On(AppClientAttached @event) { const string channel = "settings.clients"; @@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Id", @event.Id)); } - protected Task On(AppClientRevoked @event, EnvelopeHeaders headers) + protected Task On(AppClientRevoked @event) { const string channel = "settings.clients"; @@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Id", @event.Id)); } - protected Task On(AppClientRenamed @event, EnvelopeHeaders headers) + protected Task On(AppClientRenamed @event) { const string channel = "settings.clients"; @@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Id", @event.Id).AddParameter("Name", ClientName(@event))); } - protected Task On(AppLanguageAdded @event, EnvelopeHeaders headers) + protected Task On(AppLanguageAdded @event) { const string channel = "settings.languages"; @@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Language", @event.Language)); } - protected Task On(AppLanguageRemoved @event, EnvelopeHeaders headers) + protected Task On(AppLanguageRemoved @event) { const string channel = "settings.languages"; @@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Language", @event.Language)); } - protected Task On(AppLanguageUpdated @event, EnvelopeHeaders headers) + protected Task On(AppLanguageUpdated @event) { const string channel = "settings.languages"; @@ -123,7 +123,7 @@ namespace Squidex.Domain.Apps.Entities.Apps .AddParameter("Language", @event.Language)); } - protected Task On(AppMasterLanguageSet @event, EnvelopeHeaders headers) + protected Task On(AppMasterLanguageSet @event) { const string channel = "settings.languages"; diff --git a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs index 857f66678..34988ff0f 100644 --- a/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs +++ b/src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs @@ -8,11 +8,16 @@ using Newtonsoft.Json; using Squidex.Domain.Apps.Core.Apps; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Apps; using Squidex.Infrastructure; +using Squidex.Infrastructure.Dispatching; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Apps.State { - public sealed class AppState : DomainObjectState, IAppEntity + public class AppState : DomainObjectState, IAppEntity { private static readonly LanguagesConfig English = LanguagesConfig.Build(Language.EN); @@ -30,5 +35,72 @@ namespace Squidex.Domain.Apps.Entities.Apps.State [JsonProperty] public LanguagesConfig LanguagesConfig { get; set; } = English; + + protected void On(AppCreated @event) + { + SimpleMapper.Map(@event, this); + } + + protected void On(AppPlanChanged @event) + { + Plan = @event.PlanId == null ? null : new AppPlan(@event.Actor, @event.PlanId); + } + + protected void On(AppContributorAssigned @event) + { + Contributors = Contributors.Assign(@event.ContributorId, @event.Permission); + } + + protected void On(AppContributorRemoved @event) + { + Contributors = Contributors.Remove(@event.ContributorId); + } + + protected void On(AppClientAttached @event) + { + Clients = Clients.Add(@event.Id, @event.Secret); + } + + protected void On(AppClientUpdated @event) + { + Clients = Clients.Update(@event.Id, @event.Permission); + } + + protected void On(AppClientRenamed @event) + { + Clients = Clients.Rename(@event.Id, @event.Name); + } + + protected void On(AppClientRevoked @event) + { + Clients = Clients.Revoke(@event.Id); + } + + protected void On(AppLanguageAdded @event) + { + LanguagesConfig = LanguagesConfig.Set(new LanguageConfig(@event.Language)); + } + + protected void On(AppLanguageRemoved @event) + { + LanguagesConfig = LanguagesConfig.Remove(@event.Language); + } + + protected void On(AppLanguageUpdated @event) + { + LanguagesConfig = LanguagesConfig.Set(new LanguageConfig(@event.Language, @event.IsOptional, @event.Fallback)); + + if (@event.IsMaster) + { + LanguagesConfig = LanguagesConfig.MakeMaster(@event.Language); + } + } + + public AppState Apply(Envelope @event) + { + var payload = (SquidexEvent)@event.Payload; + + return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload)); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs index d66b14a94..52677b5c4 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs @@ -6,12 +6,12 @@ // All rights reserved. // ========================================================================== -using System; using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.State; using Squidex.Domain.Apps.Events.Assets; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Assets @@ -33,13 +33,6 @@ namespace Squidex.Domain.Apps.Entities.Assets IsImage = command.ImageInfo != null }); - UpdateState(command, s => - { - s.TotalSize = @event.FileSize; - - SimpleMapper.Map(@event, s); - }); - RaiseEvent(@event); return this; @@ -59,13 +52,6 @@ namespace Squidex.Domain.Apps.Entities.Assets IsImage = command.ImageInfo != null }); - UpdateState(command, s => - { - s.TotalSize += @event.FileSize; - - SimpleMapper.Map(@event, s); - }); - RaiseEvent(@event); return this; @@ -75,8 +61,6 @@ namespace Squidex.Domain.Apps.Entities.Assets { VerifyCreatedAndNotDeleted(); - UpdateState(command, s => s.IsDeleted = true); - RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = State.TotalSize })); return this; @@ -86,8 +70,6 @@ namespace Squidex.Domain.Apps.Entities.Assets { VerifyCreatedAndNotDeleted(); - UpdateState(command, s => s.FileName = command.FileName); - RaiseEvent(SimpleMapper.Map(command, new AssetRenamed())); return this; @@ -109,9 +91,9 @@ namespace Squidex.Domain.Apps.Entities.Assets } } - protected override AssetState CloneState(ICommand command, Action updater) + protected override void OnRaised(Envelope @event) { - return State.Clone().Update((SquidexCommand)command, updater); + UpdateState(State.Apply(@event)); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs index 77e6ca5c9..b8ce1c92e 100644 --- a/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs +++ b/src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs @@ -9,10 +9,15 @@ using System; using Newtonsoft.Json; using Squidex.Domain.Apps.Core.ValidateContent; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Assets; +using Squidex.Infrastructure.Dispatching; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Assets.State { - public sealed class AssetState : DomainObjectState, + public class AssetState : DomainObjectState, IAssetEntity, IAssetInfo, IUpdateableEntityWithAppRef @@ -51,5 +56,36 @@ namespace Squidex.Domain.Apps.Entities.Assets.State { get { return Id; } } + + protected void On(AssetCreated @event) + { + SimpleMapper.Map(@event, this); + + TotalSize += @event.FileSize; + } + + protected void On(AssetUpdated @event) + { + SimpleMapper.Map(@event, this); + + TotalSize += @event.FileSize; + } + + protected void On(AssetRenamed @event) + { + FileName = @event.FileName; + } + + protected void On(AssetDeleted @event) + { + IsDeleted = true; + } + + public AssetState Apply(Envelope @event) + { + var payload = (SquidexEvent)@event.Payload; + + return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload)); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/EntityMapper.cs b/src/Squidex.Domain.Apps.Entities/EntityMapper.cs index b7fbf2671..3e6243a34 100644 --- a/src/Squidex.Domain.Apps.Entities/EntityMapper.cs +++ b/src/Squidex.Domain.Apps.Entities/EntityMapper.cs @@ -1,5 +1,5 @@ // ========================================================================== -// EntityMapper.cs +// EntityMapper2.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -8,82 +8,81 @@ using System; using NodaTime; -using Squidex.Infrastructure.Commands; +using Squidex.Domain.Apps.Events; +using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Entities { public static class EntityMapper { - public static T Update(this T entity, SquidexCommand command, Action updater = null) where T : IEntity + public static T Update(this T entity, SquidexEvent @event, EnvelopeHeaders headers, Action updater = null) where T : IEntity { - var timestamp = SystemClock.Instance.GetCurrentInstant(); - - SetId(entity, command); - SetAppId(entity, command); - SetCreated(entity, timestamp); - SetCreatedBy(entity, command); - SetLastModified(entity, timestamp); - SetLastModifiedBy(entity, command); - SetVersion(entity); + SetId(entity, headers); + SetAppId(entity, @event); + SetCreated(entity, headers); + SetCreatedBy(entity, @event); + SetLastModified(entity, headers); + SetLastModifiedBy(entity, @event); + SetVersion(entity, headers); updater?.Invoke(entity); return entity; } - private static void SetId(IEntity entity, SquidexCommand command) + private static void SetId(IEntity entity, EnvelopeHeaders headers) { - if (entity is IUpdateableEntity updateable && command is IAggregateCommand aggregateCommand) + if (entity is IUpdateableEntity updateable) { - updateable.Id = aggregateCommand.AggregateId; + updateable.Id = headers.AggregateId(); } } - private static void SetVersion(IEntity entity) + private static void SetVersion(IEntity entity, EnvelopeHeaders headers) { if (entity is IUpdateableEntityWithVersion withVersion) { - withVersion.Version++; + withVersion.Version = headers.EventStreamNumber(); } } - private static void SetCreated(IEntity entity, Instant timestamp) + private static void SetCreated(IEntity entity, EnvelopeHeaders headers) { if (entity is IUpdateableEntity updateable && updateable.Created == default(Instant)) { - updateable.Created = timestamp; + updateable.Created = headers.Timestamp(); } } - private static void SetCreatedBy(IEntity entity, SquidexCommand command) + private static void SetCreatedBy(IEntity entity, SquidexEvent @event) { if (entity is IUpdateableEntityWithCreatedBy withCreatedBy && withCreatedBy.CreatedBy == null) { - withCreatedBy.CreatedBy = command.Actor; + withCreatedBy.CreatedBy = @event.Actor; } } - private static void SetLastModified(IEntity entity, Instant timestamp) + private static void SetLastModified(IEntity entity, EnvelopeHeaders headers) { if (entity is IUpdateableEntity updateable) { - updateable.LastModified = timestamp; + updateable.LastModified = headers.Timestamp(); } } - private static void SetLastModifiedBy(IEntity entity, SquidexCommand command) + private static void SetLastModifiedBy(IEntity entity, SquidexEvent @event) { if (entity is IUpdateableEntityWithLastModifiedBy withModifiedBy) { - withModifiedBy.LastModifiedBy = command.Actor; + withModifiedBy.LastModifiedBy = @event.Actor; } } - private static void SetAppId(IEntity entity, SquidexCommand command) + private static void SetAppId(IEntity entity, SquidexEvent @event) { - if (entity is IUpdateableEntityWithAppRef appEntity && command is AppCommand appCommand) + if (entity is IUpdateableEntityWithAppRef appEntity && @event is AppEvent appEvent) { - appEntity.AppId = appCommand.AppId.Id; + appEntity.AppId = appEvent.AppId.Id; } } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuer.cs b/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuer.cs index a3e07ea47..5ae2477bf 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuer.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuer.cs @@ -21,7 +21,7 @@ using Squidex.Infrastructure.Timers; namespace Squidex.Domain.Apps.Entities.Rules { - public sealed class RuleDequeuer : DisposableObjectBase, IExternalSystem + public class RuleDequeuer : DisposableObjectBase, IExternalSystem { private readonly ActionBlock requestBlock; private readonly IRuleEventRepository ruleEventRepository; diff --git a/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs index 80522b6d5..6aa105077 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs @@ -6,13 +6,12 @@ // All rights reserved. // ========================================================================== -using System; -using Squidex.Domain.Apps.Core.Rules; using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.State; using Squidex.Domain.Apps.Events.Rules; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Rules @@ -23,8 +22,6 @@ namespace Squidex.Domain.Apps.Entities.Rules { VerifyNotCreated(); - UpdateRule(command, r => new Rule(command.Trigger, command.Action)); - RaiseEvent(SimpleMapper.Map(command, new RuleCreated())); } @@ -32,8 +29,6 @@ namespace Squidex.Domain.Apps.Entities.Rules { VerifyCreatedAndNotDeleted(); - UpdateRule(command, r => r.Update(command.Trigger).Update(command.Action)); - RaiseEvent(SimpleMapper.Map(command, new RuleUpdated())); } @@ -41,8 +36,6 @@ namespace Squidex.Domain.Apps.Entities.Rules { VerifyCreatedAndNotDeleted(); - UpdateRule(command, r => r.Enable()); - RaiseEvent(SimpleMapper.Map(command, new RuleEnabled())); } @@ -50,8 +43,6 @@ namespace Squidex.Domain.Apps.Entities.Rules { VerifyCreatedAndNotDeleted(); - UpdateRule(command, r => r.Disable()); - RaiseEvent(SimpleMapper.Map(command, new RuleDisabled())); } @@ -59,8 +50,6 @@ namespace Squidex.Domain.Apps.Entities.Rules { VerifyCreatedAndNotDeleted(); - UpdateState(command, s => s.IsDeleted = true); - RaiseEvent(SimpleMapper.Map(command, new RuleDeleted())); } @@ -80,14 +69,9 @@ namespace Squidex.Domain.Apps.Entities.Rules } } - private void UpdateRule(ICommand command, Func updater) - { - UpdateState(command, s => s.RuleDef = updater(s.RuleDef)); - } - - protected override RuleState CloneState(ICommand command, Action updater) + protected override void OnRaised(Envelope @event) { - return State.Clone().Update((SquidexCommand)command, updater); + UpdateState(State.Apply(@event)); } } } diff --git a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs index c41d1761d..f08e8ddae 100644 --- a/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs +++ b/src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs @@ -9,10 +9,17 @@ using System; using Newtonsoft.Json; using Squidex.Domain.Apps.Core.Rules; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Rules; +using Squidex.Infrastructure.Dispatching; +using Squidex.Infrastructure.EventSourcing; namespace Squidex.Domain.Apps.Entities.Rules.State { - public sealed class RuleState : DomainObjectState, IRuleEntity + public class RuleState : DomainObjectState, + IRuleEntity, + IEntityWithAppRef, + IUpdateableEntityWithAppRef { [JsonProperty] public Guid AppId { get; set; } @@ -22,5 +29,32 @@ namespace Squidex.Domain.Apps.Entities.Rules.State [JsonProperty] public bool IsDeleted { get; set; } + + protected void On(RuleCreated @event) + { + RuleDef = new Rule(@event.Trigger, @event.Action); + } + + protected void On(RuleUpdated @event) + { + RuleDef = RuleDef.Update(@event.Trigger).Update(@event.Action); + } + + protected void On(RuleEnabled @event) + { + RuleDef = RuleDef.Enable(); + } + + protected void On(RuleDisabled @event) + { + RuleDef = RuleDef.Disable(); + } + + public RuleState Apply(Envelope @event) + { + var payload = (SquidexEvent)@event.Payload; + + return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload)); + } } } diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs index 6d3eb5277..c3ec2642b 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs @@ -8,13 +8,13 @@ using System; using System.Collections.Generic; -using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.State; using Squidex.Domain.Apps.Events.Schemas; using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; +using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Schemas @@ -57,22 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - var partitioning = - string.Equals(command.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ? - Partitioning.Language : - Partitioning.Invariant; - - var fieldId = State.TotalFields; - - var field = registry.CreateField(fieldId, command.Name, partitioning, command.Properties); - - UpdateState(command, state => - { - state.SchemaDef = state.SchemaDef.AddField(field); - state.TotalFields = fieldId + 1; - }); - - RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId(fieldId + 1, command.Name) })); + RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId(State.TotalFields + 1, command.Name) })); return this; } @@ -81,8 +66,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.UpdateField(command.FieldId, command.Properties)); - RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); return this; @@ -92,8 +75,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.LockField(command.FieldId)); - RaiseEvent(command, new FieldLocked()); return this; @@ -103,8 +84,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.HideField(command.FieldId)); - RaiseEvent(command, new FieldHidden()); return this; @@ -114,8 +93,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.ShowField(command.FieldId)); - RaiseEvent(command, new FieldShown()); return this; @@ -125,8 +102,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.DisableField(command.FieldId)); - RaiseEvent(command, new FieldDisabled()); return this; @@ -136,8 +111,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.EnableField(command.FieldId)); - RaiseEvent(command, new FieldEnabled()); return this; @@ -147,8 +120,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.DeleteField(command.FieldId)); - RaiseEvent(command, new FieldDeleted()); return this; @@ -158,8 +129,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.ReorderFields(command.FieldIds)); - RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); return this; @@ -169,8 +138,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.Publish()); - RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); return this; @@ -180,8 +147,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateSchema(command, s => s.Unpublish()); - RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); return this; @@ -191,8 +156,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateState(command, s => SimpleMapper.Map(command, s)); - RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); return this; @@ -202,8 +165,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateState(command, s => s.IsDeleted = true); - RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); return this; @@ -213,8 +174,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas { VerifyCreatedAndNotDeleted(); - UpdateState(command, s => SimpleMapper.Map(command, s)); - RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); return this; @@ -248,14 +207,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas } } - private void UpdateSchema(ICommand command, Func updater) - { - UpdateState(command, s => s.SchemaDef = updater(s.SchemaDef)); - } - - protected override SchemaState CloneState(ICommand command, Action updater) + protected override void OnRaised(Envelope @event) { - return State.Clone().Update((SquidexCommand)command, updater); + UpdateState(State.Apply(@event)); } } } \ No newline at end of file diff --git a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs index 9fd80f57b..585e890e4 100644 --- a/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs +++ b/src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs @@ -1,5 +1,5 @@ // ========================================================================== -// JsonSchemaEntity.cs +// SchemaState.cs // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex Group @@ -8,11 +8,17 @@ using System; using Newtonsoft.Json; +using Squidex.Domain.Apps.Core; using Squidex.Domain.Apps.Core.Schemas; +using Squidex.Domain.Apps.Events; +using Squidex.Domain.Apps.Events.Schemas; +using Squidex.Infrastructure.Dispatching; +using Squidex.Infrastructure.EventSourcing; +using Squidex.Infrastructure.Reflection; namespace Squidex.Domain.Apps.Entities.Schemas.State { - public sealed class SchemaState : DomainObjectState, + public class SchemaState : DomainObjectState, ISchemaEntity, IUpdateableEntityWithAppRef, IUpdateableEntityWithCreatedBy, @@ -25,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State public Guid AppId { get; set; } [JsonProperty] - public int TotalFields { get; set; } + public int TotalFields { get; set; } = 1; [JsonProperty] public bool IsDeleted { get; set; } @@ -53,5 +59,136 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State { get { return SchemaDef.IsPublished; } } + + protected void On(SchemaCreated @event, FieldRegistry registry) + { + var schema = new Schema(@event.Name); + + if (@event.Properties != null) + { + schema = schema.Update(@event.Properties); + } + + if (@event.Fields != null) + { + foreach (var eventField in @event.Fields) + { + var partitioning = + string.Equals(eventField.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ? + Partitioning.Language : + Partitioning.Invariant; + + var field = registry.CreateField(TotalFields, eventField.Name, partitioning, eventField.Properties); + + if (eventField.IsHidden) + { + field = field.Hide(); + } + + if (eventField.IsDisabled) + { + field = field.Disable(); + } + + if (eventField.IsLocked) + { + field = field.Lock(); + } + + schema = schema.AddField(field); + + TotalFields++; + } + } + + SchemaDef = schema; + } + + protected void On(FieldAdded @event, FieldRegistry registry) + { + var partitioning = + string.Equals(@event.Partitioning, Partitioning.Language.Key, StringComparison.OrdinalIgnoreCase) ? + Partitioning.Language : + Partitioning.Invariant; + + var field = registry.CreateField(@event.FieldId.Id, @event.Name, partitioning, @event.Properties); + + SchemaDef = SchemaDef.DeleteField(@event.FieldId.Id); + SchemaDef = SchemaDef.AddField(field); + + TotalFields++; + } + + protected void On(SchemaPublished @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.Publish(); + } + + protected void On(SchemaUnpublished @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.Unpublish(); + } + + protected void On(SchemaUpdated @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.Update(@event.Properties); + } + + protected void On(SchemaFieldsReordered @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.ReorderFields(@event.FieldIds); + } + + protected void On(FieldUpdated @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.UpdateField(@event.FieldId.Id, @event.Properties); + } + + protected void On(FieldLocked @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.LockField(@event.FieldId.Id); + } + + protected void On(FieldDisabled @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.DisableField(@event.FieldId.Id); + } + + protected void On(FieldEnabled @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.EnableField(@event.FieldId.Id); + } + + protected void On(FieldHidden @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.HideField(@event.FieldId.Id); + } + + protected void On(FieldShown @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.ShowField(@event.FieldId.Id); + } + + protected void On(FieldDeleted @event, FieldRegistry registry) + { + SchemaDef = SchemaDef.DeleteField(@event.FieldId.Id); + } + + protected void On(SchemaDeleted @event, FieldRegistry registry) + { + IsDeleted = true; + } + + protected void On(ScriptsConfigured @event, FieldRegistry registry) + { + SimpleMapper.Map(@event, this); + } + + public SchemaState Apply(Envelope @event) + { + var payload = (SquidexEvent)@event.Payload; + + return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload)); + } } } diff --git a/src/Squidex.Domain.Users.MongoDb/MongoXmlRepository.cs b/src/Squidex.Domain.Users.MongoDb/MongoXmlRepository.cs index ad7b7c953..c730af9d3 100644 --- a/src/Squidex.Domain.Users.MongoDb/MongoXmlRepository.cs +++ b/src/Squidex.Domain.Users.MongoDb/MongoXmlRepository.cs @@ -18,8 +18,6 @@ namespace Squidex.Domain.Users.MongoDb { public sealed class MongoXmlRepository : MongoRepositoryBase, IXmlRepository { - private static readonly UpdateOptions Upsert = new UpdateOptions { IsUpsert = true }; - public MongoXmlRepository(IMongoDatabase database) : base(database) { diff --git a/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs b/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs index 40c1a0c03..62ba1fdcc 100644 --- a/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs +++ b/src/Squidex.Infrastructure/Commands/DomainObjectBase.cs @@ -49,24 +49,28 @@ namespace Squidex.Infrastructure.Commands return persistence.ReadAsync(); } - protected void RaiseEvent(IEvent @event) + public void RaiseEvent(IEvent @event) { RaiseEvent(Envelope.Create(@event)); } - protected void RaiseEvent(Envelope @event) where TEvent : class, IEvent + public void RaiseEvent(Envelope @event) where TEvent : class, IEvent { Guard.NotNull(@event, nameof(@event)); + OnRaised(@event.To()); + uncomittedEvents.Add(@event.To()); } - public void UpdateState(ICommand command, Action updater) + public void UpdateState(TState newState) { - state = CloneState(command, updater); + state = newState; } - protected abstract TState CloneState(ICommand command, Action updater); + protected virtual void OnRaised(Envelope @event) + { + } public async Task WriteAsync(ISemanticLog log) { diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs index cb495d264..f86e99df0 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs @@ -11,7 +11,6 @@ using System.Collections.Immutable; using System.Threading.Tasks; using FakeItEasy; using Squidex.Domain.Apps.Core.Rules.Triggers; -using Squidex.Domain.Apps.Entities; using Squidex.Domain.Apps.Entities.Schemas; using Xunit; diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs b/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs index 763b0db25..c97d25f1c 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs @@ -33,6 +33,16 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers IsUpdated = false; } + public Task CreateSyncedAsync(CommandContext context, Func creator) where V : class, IDomainObject + { + return CreateAsync(context, creator); + } + + public Task UpdateSyncedAsync(CommandContext context, Func creator) where V : class, IDomainObject + { + return UpdateAsync(context, creator); + } + public async Task CreateAsync(CommandContext context, Func creator) where V : class, IDomainObject { IsCreated = true;