Browse Source

State updates fixed.

pull/206/head
Sebastian Stehle 8 years ago
parent
commit
322980d9c9
  1. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs
  2. 63
      src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs
  3. 18
      src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs
  4. 74
      src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs
  5. 24
      src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs
  6. 38
      src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs
  7. 55
      src/Squidex.Domain.Apps.Entities/EntityMapper.cs
  8. 2
      src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuer.cs
  9. 22
      src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs
  10. 36
      src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs
  11. 54
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs
  12. 143
      src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs
  13. 2
      src/Squidex.Domain.Users.MongoDb/MongoXmlRepository.cs
  14. 14
      src/Squidex.Infrastructure/Commands/DomainObjectBase.cs
  15. 1
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs
  16. 10
      tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs

2
src/Squidex.Domain.Apps.Entities.MongoDb/MongoCollectionExtensions.cs

@ -20,7 +20,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb
{ {
public static Task CreateAsync<T>(this IMongoCollection<T> collection, SquidexEvent @event, EnvelopeHeaders headers, Action<T> updater) where T : class, IEntity, new() public static Task CreateAsync<T>(this IMongoCollection<T> collection, SquidexEvent @event, EnvelopeHeaders headers, Action<T> 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); return collection.InsertOneIfNotExistsAsync(entity);
} }

63
src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs

@ -7,7 +7,6 @@
// ========================================================================== // ==========================================================================
using System; using System;
using System.Linq;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.State; using Squidex.Domain.Apps.Entities.Apps.State;
@ -28,10 +27,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
var appId = new NamedId<Guid>(command.AppId, command.Name); var appId = new NamedId<Guid>(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, CreateInitalEvent(appId)));
RaiseEvent(SimpleMapper.Map(command, CreateInitialOwner(appId, command))); RaiseEvent(SimpleMapper.Map(command, CreateInitialOwner(appId, command)));
RaiseEvent(SimpleMapper.Map(command, CreateInitialLanguage(appId))); RaiseEvent(SimpleMapper.Map(command, CreateInitialLanguage(appId)));
@ -43,27 +38,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateLanguages(command, l =>
{
var fallback = command.Fallback;
if (fallback != null && fallback.Count > 0)
{
var existingLangauges = l.OfType<LanguageConfig>().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())); RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated()));
return this; return this;
@ -75,15 +49,11 @@ namespace Squidex.Domain.Apps.Entities.Apps
if (!string.IsNullOrWhiteSpace(command.Name)) if (!string.IsNullOrWhiteSpace(command.Name))
{ {
UpdateClients(command, c => c.Rename(command.Id, command.Name));
RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed())); RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed()));
} }
if (command.Permission.HasValue) if (command.Permission.HasValue)
{ {
UpdateClients(command, c => c.Update(command.Id, command.Permission.Value));
RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value })); RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value }));
} }
@ -94,8 +64,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateContributors(command, c => c.Assign(command.ContributorId, command.Permission));
RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned())); RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned()));
return this; return this;
@ -105,8 +73,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateContributors(command, c => c.Remove(command.ContributorId));
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved())); RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved()));
return this; return this;
@ -116,8 +82,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateClients(command, c => c.Add(command.Id, command.Secret));
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached())); RaiseEvent(SimpleMapper.Map(command, new AppClientAttached()));
return this; return this;
@ -127,8 +91,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateClients(command, c => c.Revoke(command.Id));
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked())); RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked()));
return this; return this;
@ -138,8 +100,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateLanguages(command, l => l.Set(new LanguageConfig(command.Language)));
RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded())); RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded()));
return this; return this;
@ -149,8 +109,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateLanguages(command, l => l.Remove(command.Language));
RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved())); RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved()));
return this; return this;
@ -160,8 +118,6 @@ namespace Squidex.Domain.Apps.Entities.Apps
{ {
ThrowIfNotCreated(); ThrowIfNotCreated();
UpdateState(command, s => s.Plan = command.PlanId != null ? new AppPlan(command.Actor, command.PlanId) : null);
RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged())); RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged()));
return this; return this;
@ -208,24 +164,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
} }
} }
private void UpdateClients(ICommand command, Func<AppClients, AppClients> updater) protected override void OnRaised(Envelope<IEvent> @event)
{
UpdateState(command, s => s.Clients = updater(s.Clients));
}
private void UpdateContributors(ICommand command, Func<AppContributors, AppContributors> updater)
{
UpdateState(command, s => s.Contributors = updater(s.Contributors));
}
private void UpdateLanguages(ICommand command, Func<LanguagesConfig, LanguagesConfig> updater)
{
UpdateState(command, s => s.LanguagesConfig = updater(s.LanguagesConfig));
}
protected override AppState CloneState(ICommand command, Action<AppState> updater)
{ {
return State.Clone().Update((SquidexCommand)command, updater); UpdateState(State.Apply(@event));
} }
} }
} }

18
src/Squidex.Domain.Apps.Entities/Apps/AppHistoryEventsCreator.cs

@ -51,7 +51,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
"changed master language to {[Language]}"); "changed master language to {[Language]}");
} }
protected Task<HistoryEventToStore> On(AppContributorRemoved @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppContributorRemoved @event)
{ {
const string channel = "settings.contributors"; const string channel = "settings.contributors";
@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Contributor", @event.ContributorId)); .AddParameter("Contributor", @event.ContributorId));
} }
protected Task<HistoryEventToStore> On(AppContributorAssigned @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppContributorAssigned @event)
{ {
const string channel = "settings.contributors"; const string channel = "settings.contributors";
@ -69,7 +69,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Contributor", @event.ContributorId).AddParameter("Permission", @event.Permission)); .AddParameter("Contributor", @event.ContributorId).AddParameter("Permission", @event.Permission));
} }
protected Task<HistoryEventToStore> On(AppClientAttached @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppClientAttached @event)
{ {
const string channel = "settings.clients"; const string channel = "settings.clients";
@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Id", @event.Id)); .AddParameter("Id", @event.Id));
} }
protected Task<HistoryEventToStore> On(AppClientRevoked @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppClientRevoked @event)
{ {
const string channel = "settings.clients"; const string channel = "settings.clients";
@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Id", @event.Id)); .AddParameter("Id", @event.Id));
} }
protected Task<HistoryEventToStore> On(AppClientRenamed @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppClientRenamed @event)
{ {
const string channel = "settings.clients"; const string channel = "settings.clients";
@ -96,7 +96,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Id", @event.Id).AddParameter("Name", ClientName(@event))); .AddParameter("Id", @event.Id).AddParameter("Name", ClientName(@event)));
} }
protected Task<HistoryEventToStore> On(AppLanguageAdded @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppLanguageAdded @event)
{ {
const string channel = "settings.languages"; const string channel = "settings.languages";
@ -105,7 +105,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Language", @event.Language)); .AddParameter("Language", @event.Language));
} }
protected Task<HistoryEventToStore> On(AppLanguageRemoved @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppLanguageRemoved @event)
{ {
const string channel = "settings.languages"; const string channel = "settings.languages";
@ -114,7 +114,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Language", @event.Language)); .AddParameter("Language", @event.Language));
} }
protected Task<HistoryEventToStore> On(AppLanguageUpdated @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppLanguageUpdated @event)
{ {
const string channel = "settings.languages"; const string channel = "settings.languages";
@ -123,7 +123,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
.AddParameter("Language", @event.Language)); .AddParameter("Language", @event.Language));
} }
protected Task<HistoryEventToStore> On(AppMasterLanguageSet @event, EnvelopeHeaders headers) protected Task<HistoryEventToStore> On(AppMasterLanguageSet @event)
{ {
const string channel = "settings.languages"; const string channel = "settings.languages";

74
src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs

@ -8,11 +8,16 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Apps; using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Dispatching;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Apps.State namespace Squidex.Domain.Apps.Entities.Apps.State
{ {
public sealed class AppState : DomainObjectState<AppState>, IAppEntity public class AppState : DomainObjectState<AppState>, IAppEntity
{ {
private static readonly LanguagesConfig English = LanguagesConfig.Build(Language.EN); private static readonly LanguagesConfig English = LanguagesConfig.Build(Language.EN);
@ -30,5 +35,72 @@ namespace Squidex.Domain.Apps.Entities.Apps.State
[JsonProperty] [JsonProperty]
public LanguagesConfig LanguagesConfig { get; set; } = English; 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<IEvent> @event)
{
var payload = (SquidexEvent)@event.Payload;
return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload));
}
} }
} }

24
src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs

@ -6,12 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using Squidex.Domain.Apps.Entities.Assets.Commands; using Squidex.Domain.Apps.Entities.Assets.Commands;
using Squidex.Domain.Apps.Entities.Assets.State; using Squidex.Domain.Apps.Entities.Assets.State;
using Squidex.Domain.Apps.Events.Assets; using Squidex.Domain.Apps.Events.Assets;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Assets namespace Squidex.Domain.Apps.Entities.Assets
@ -33,13 +33,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
IsImage = command.ImageInfo != null IsImage = command.ImageInfo != null
}); });
UpdateState(command, s =>
{
s.TotalSize = @event.FileSize;
SimpleMapper.Map(@event, s);
});
RaiseEvent(@event); RaiseEvent(@event);
return this; return this;
@ -59,13 +52,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
IsImage = command.ImageInfo != null IsImage = command.ImageInfo != null
}); });
UpdateState(command, s =>
{
s.TotalSize += @event.FileSize;
SimpleMapper.Map(@event, s);
});
RaiseEvent(@event); RaiseEvent(@event);
return this; return this;
@ -75,8 +61,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateState(command, s => s.IsDeleted = true);
RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = State.TotalSize })); RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = State.TotalSize }));
return this; return this;
@ -86,8 +70,6 @@ namespace Squidex.Domain.Apps.Entities.Assets
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateState(command, s => s.FileName = command.FileName);
RaiseEvent(SimpleMapper.Map(command, new AssetRenamed())); RaiseEvent(SimpleMapper.Map(command, new AssetRenamed()));
return this; return this;
@ -109,9 +91,9 @@ namespace Squidex.Domain.Apps.Entities.Assets
} }
} }
protected override AssetState CloneState(ICommand command, Action<AssetState> updater) protected override void OnRaised(Envelope<IEvent> @event)
{ {
return State.Clone().Update((SquidexCommand)command, updater); UpdateState(State.Apply(@event));
} }
} }
} }

38
src/Squidex.Domain.Apps.Entities/Assets/State/AssetState.cs

@ -9,10 +9,15 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.ValidateContent; 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 namespace Squidex.Domain.Apps.Entities.Assets.State
{ {
public sealed class AssetState : DomainObjectState<AssetState>, public class AssetState : DomainObjectState<AssetState>,
IAssetEntity, IAssetEntity,
IAssetInfo, IAssetInfo,
IUpdateableEntityWithAppRef IUpdateableEntityWithAppRef
@ -51,5 +56,36 @@ namespace Squidex.Domain.Apps.Entities.Assets.State
{ {
get { return Id; } 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<IEvent> @event)
{
var payload = (SquidexEvent)@event.Payload;
return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload));
}
} }
} }

55
src/Squidex.Domain.Apps.Entities/EntityMapper.cs

@ -1,5 +1,5 @@
// ========================================================================== // ==========================================================================
// EntityMapper.cs // EntityMapper2.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
@ -8,82 +8,81 @@
using System; using System;
using NodaTime; using NodaTime;
using Squidex.Infrastructure.Commands; using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Entities namespace Squidex.Domain.Apps.Entities
{ {
public static class EntityMapper public static class EntityMapper
{ {
public static T Update<T>(this T entity, SquidexCommand command, Action<T> updater = null) where T : IEntity public static T Update<T>(this T entity, SquidexEvent @event, EnvelopeHeaders headers, Action<T> updater = null) where T : IEntity
{ {
var timestamp = SystemClock.Instance.GetCurrentInstant(); SetId(entity, headers);
SetAppId(entity, @event);
SetId(entity, command); SetCreated(entity, headers);
SetAppId(entity, command); SetCreatedBy(entity, @event);
SetCreated(entity, timestamp); SetLastModified(entity, headers);
SetCreatedBy(entity, command); SetLastModifiedBy(entity, @event);
SetLastModified(entity, timestamp); SetVersion(entity, headers);
SetLastModifiedBy(entity, command);
SetVersion(entity);
updater?.Invoke(entity); updater?.Invoke(entity);
return 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) 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)) 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) 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) 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) 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;
} }
} }
} }

2
src/Squidex.Domain.Apps.Entities/Rules/RuleDequeuer.cs

@ -21,7 +21,7 @@ using Squidex.Infrastructure.Timers;
namespace Squidex.Domain.Apps.Entities.Rules namespace Squidex.Domain.Apps.Entities.Rules
{ {
public sealed class RuleDequeuer : DisposableObjectBase, IExternalSystem public class RuleDequeuer : DisposableObjectBase, IExternalSystem
{ {
private readonly ActionBlock<IRuleEventEntity> requestBlock; private readonly ActionBlock<IRuleEventEntity> requestBlock;
private readonly IRuleEventRepository ruleEventRepository; private readonly IRuleEventRepository ruleEventRepository;

22
src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs

@ -6,13 +6,12 @@
// All rights reserved. // All rights reserved.
// ========================================================================== // ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Rules;
using Squidex.Domain.Apps.Entities.Rules.Commands; using Squidex.Domain.Apps.Entities.Rules.Commands;
using Squidex.Domain.Apps.Entities.Rules.State; using Squidex.Domain.Apps.Entities.Rules.State;
using Squidex.Domain.Apps.Events.Rules; using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Rules namespace Squidex.Domain.Apps.Entities.Rules
@ -23,8 +22,6 @@ namespace Squidex.Domain.Apps.Entities.Rules
{ {
VerifyNotCreated(); VerifyNotCreated();
UpdateRule(command, r => new Rule(command.Trigger, command.Action));
RaiseEvent(SimpleMapper.Map(command, new RuleCreated())); RaiseEvent(SimpleMapper.Map(command, new RuleCreated()));
} }
@ -32,8 +29,6 @@ namespace Squidex.Domain.Apps.Entities.Rules
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateRule(command, r => r.Update(command.Trigger).Update(command.Action));
RaiseEvent(SimpleMapper.Map(command, new RuleUpdated())); RaiseEvent(SimpleMapper.Map(command, new RuleUpdated()));
} }
@ -41,8 +36,6 @@ namespace Squidex.Domain.Apps.Entities.Rules
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateRule(command, r => r.Enable());
RaiseEvent(SimpleMapper.Map(command, new RuleEnabled())); RaiseEvent(SimpleMapper.Map(command, new RuleEnabled()));
} }
@ -50,8 +43,6 @@ namespace Squidex.Domain.Apps.Entities.Rules
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateRule(command, r => r.Disable());
RaiseEvent(SimpleMapper.Map(command, new RuleDisabled())); RaiseEvent(SimpleMapper.Map(command, new RuleDisabled()));
} }
@ -59,8 +50,6 @@ namespace Squidex.Domain.Apps.Entities.Rules
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateState(command, s => s.IsDeleted = true);
RaiseEvent(SimpleMapper.Map(command, new RuleDeleted())); RaiseEvent(SimpleMapper.Map(command, new RuleDeleted()));
} }
@ -80,14 +69,9 @@ namespace Squidex.Domain.Apps.Entities.Rules
} }
} }
private void UpdateRule(ICommand command, Func<Rule, Rule> updater) protected override void OnRaised(Envelope<IEvent> @event)
{
UpdateState(command, s => s.RuleDef = updater(s.RuleDef));
}
protected override RuleState CloneState(ICommand command, Action<RuleState> updater)
{ {
return State.Clone().Update((SquidexCommand)command, updater); UpdateState(State.Apply(@event));
} }
} }
} }

36
src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs

@ -9,10 +9,17 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Domain.Apps.Core.Rules; 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 namespace Squidex.Domain.Apps.Entities.Rules.State
{ {
public sealed class RuleState : DomainObjectState<RuleState>, IRuleEntity public class RuleState : DomainObjectState<RuleState>,
IRuleEntity,
IEntityWithAppRef,
IUpdateableEntityWithAppRef
{ {
[JsonProperty] [JsonProperty]
public Guid AppId { get; set; } public Guid AppId { get; set; }
@ -22,5 +29,32 @@ namespace Squidex.Domain.Apps.Entities.Rules.State
[JsonProperty] [JsonProperty]
public bool IsDeleted { get; set; } 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<IEvent> @event)
{
var payload = (SquidexEvent)@event.Payload;
return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload));
}
} }
} }

54
src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs

@ -8,13 +8,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Schemas.Commands; using Squidex.Domain.Apps.Entities.Schemas.Commands;
using Squidex.Domain.Apps.Entities.Schemas.State; using Squidex.Domain.Apps.Entities.Schemas.State;
using Squidex.Domain.Apps.Events.Schemas; using Squidex.Domain.Apps.Events.Schemas;
using Squidex.Infrastructure; using Squidex.Infrastructure;
using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection; using Squidex.Infrastructure.Reflection;
namespace Squidex.Domain.Apps.Entities.Schemas namespace Squidex.Domain.Apps.Entities.Schemas
@ -57,22 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
var partitioning = RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(State.TotalFields + 1, command.Name) }));
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<long>(fieldId + 1, command.Name) }));
return this; return this;
} }
@ -81,8 +66,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.UpdateField(command.FieldId, command.Properties));
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated()));
return this; return this;
@ -92,8 +75,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.LockField(command.FieldId));
RaiseEvent(command, new FieldLocked()); RaiseEvent(command, new FieldLocked());
return this; return this;
@ -103,8 +84,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.HideField(command.FieldId));
RaiseEvent(command, new FieldHidden()); RaiseEvent(command, new FieldHidden());
return this; return this;
@ -114,8 +93,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.ShowField(command.FieldId));
RaiseEvent(command, new FieldShown()); RaiseEvent(command, new FieldShown());
return this; return this;
@ -125,8 +102,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.DisableField(command.FieldId));
RaiseEvent(command, new FieldDisabled()); RaiseEvent(command, new FieldDisabled());
return this; return this;
@ -136,8 +111,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.EnableField(command.FieldId));
RaiseEvent(command, new FieldEnabled()); RaiseEvent(command, new FieldEnabled());
return this; return this;
@ -147,8 +120,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.DeleteField(command.FieldId));
RaiseEvent(command, new FieldDeleted()); RaiseEvent(command, new FieldDeleted());
return this; return this;
@ -158,8 +129,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.ReorderFields(command.FieldIds));
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered()));
return this; return this;
@ -169,8 +138,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.Publish());
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); RaiseEvent(SimpleMapper.Map(command, new SchemaPublished()));
return this; return this;
@ -180,8 +147,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateSchema(command, s => s.Unpublish());
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished()));
return this; return this;
@ -191,8 +156,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateState(command, s => SimpleMapper.Map(command, s));
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured()));
return this; return this;
@ -202,8 +165,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateState(command, s => s.IsDeleted = true);
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted()));
return this; return this;
@ -213,8 +174,6 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{ {
VerifyCreatedAndNotDeleted(); VerifyCreatedAndNotDeleted();
UpdateState(command, s => SimpleMapper.Map(command, s));
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated()));
return this; return this;
@ -248,14 +207,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas
} }
} }
private void UpdateSchema(ICommand command, Func<Schema, Schema> updater) protected override void OnRaised(Envelope<IEvent> @event)
{
UpdateState(command, s => s.SchemaDef = updater(s.SchemaDef));
}
protected override SchemaState CloneState(ICommand command, Action<SchemaState> updater)
{ {
return State.Clone().Update((SquidexCommand)command, updater); UpdateState(State.Apply(@event));
} }
} }
} }

143
src/Squidex.Domain.Apps.Entities/Schemas/State/SchemaState.cs

@ -1,5 +1,5 @@
// ========================================================================== // ==========================================================================
// JsonSchemaEntity.cs // SchemaState.cs
// Squidex Headless CMS // Squidex Headless CMS
// ========================================================================== // ==========================================================================
// Copyright (c) Squidex Group // Copyright (c) Squidex Group
@ -8,11 +8,17 @@
using System; using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using Squidex.Domain.Apps.Core;
using Squidex.Domain.Apps.Core.Schemas; 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 namespace Squidex.Domain.Apps.Entities.Schemas.State
{ {
public sealed class SchemaState : DomainObjectState<SchemaState>, public class SchemaState : DomainObjectState<SchemaState>,
ISchemaEntity, ISchemaEntity,
IUpdateableEntityWithAppRef, IUpdateableEntityWithAppRef,
IUpdateableEntityWithCreatedBy, IUpdateableEntityWithCreatedBy,
@ -25,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
public Guid AppId { get; set; } public Guid AppId { get; set; }
[JsonProperty] [JsonProperty]
public int TotalFields { get; set; } public int TotalFields { get; set; } = 1;
[JsonProperty] [JsonProperty]
public bool IsDeleted { get; set; } public bool IsDeleted { get; set; }
@ -53,5 +59,136 @@ namespace Squidex.Domain.Apps.Entities.Schemas.State
{ {
get { return SchemaDef.IsPublished; } 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<IEvent> @event)
{
var payload = (SquidexEvent)@event.Payload;
return Clone().Update(payload, @event.Headers, r => r.DispatchAction(payload));
}
} }
} }

2
src/Squidex.Domain.Users.MongoDb/MongoXmlRepository.cs

@ -18,8 +18,6 @@ namespace Squidex.Domain.Users.MongoDb
{ {
public sealed class MongoXmlRepository : MongoRepositoryBase<MongoXmlDocument>, IXmlRepository public sealed class MongoXmlRepository : MongoRepositoryBase<MongoXmlDocument>, IXmlRepository
{ {
private static readonly UpdateOptions Upsert = new UpdateOptions { IsUpsert = true };
public MongoXmlRepository(IMongoDatabase database) public MongoXmlRepository(IMongoDatabase database)
: base(database) : base(database)
{ {

14
src/Squidex.Infrastructure/Commands/DomainObjectBase.cs

@ -49,24 +49,28 @@ namespace Squidex.Infrastructure.Commands
return persistence.ReadAsync(); return persistence.ReadAsync();
} }
protected void RaiseEvent(IEvent @event) public void RaiseEvent(IEvent @event)
{ {
RaiseEvent(Envelope.Create(@event)); RaiseEvent(Envelope.Create(@event));
} }
protected void RaiseEvent<TEvent>(Envelope<TEvent> @event) where TEvent : class, IEvent public void RaiseEvent<TEvent>(Envelope<TEvent> @event) where TEvent : class, IEvent
{ {
Guard.NotNull(@event, nameof(@event)); Guard.NotNull(@event, nameof(@event));
OnRaised(@event.To<IEvent>());
uncomittedEvents.Add(@event.To<IEvent>()); uncomittedEvents.Add(@event.To<IEvent>());
} }
public void UpdateState(ICommand command, Action<TState> updater) public void UpdateState(TState newState)
{ {
state = CloneState(command, updater); state = newState;
} }
protected abstract TState CloneState(ICommand command, Action<TState> updater); protected virtual void OnRaised(Envelope<IEvent> @event)
{
}
public async Task WriteAsync(ISemanticLog log) public async Task WriteAsync(ISemanticLog log)
{ {

1
tests/Squidex.Domain.Apps.Entities.Tests/Rules/Guards/Triggers/ContentChangedTriggerTests.cs

@ -11,7 +11,6 @@ using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using FakeItEasy; using FakeItEasy;
using Squidex.Domain.Apps.Core.Rules.Triggers; using Squidex.Domain.Apps.Core.Rules.Triggers;
using Squidex.Domain.Apps.Entities;
using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Domain.Apps.Entities.Schemas;
using Xunit; using Xunit;

10
tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs

@ -33,6 +33,16 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
IsUpdated = false; IsUpdated = false;
} }
public Task<V> CreateSyncedAsync<V>(CommandContext context, Func<V, Task> creator) where V : class, IDomainObject
{
return CreateAsync(context, creator);
}
public Task<V> UpdateSyncedAsync<V>(CommandContext context, Func<V, Task> creator) where V : class, IDomainObject
{
return UpdateAsync(context, creator);
}
public async Task<V> CreateAsync<V>(CommandContext context, Func<V, Task> creator) where V : class, IDomainObject public async Task<V> CreateAsync<V>(CommandContext context, Func<V, Task> creator) where V : class, IDomainObject
{ {
IsCreated = true; IsCreated = true;

Loading…
Cancel
Save