Browse Source

State => Snapshot

Added Cookie Hint
Improved Migration.
pull/214/head
Sebastian Stehle 8 years ago
parent
commit
00293d0f5e
  1. 5
      Squidex.ruleset
  2. 11
      src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs
  3. 2
      src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs
  4. 9
      src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs
  5. 16
      src/Squidex.Domain.Apps.Entities/AppProvider.cs
  6. 26
      src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs
  7. 46
      src/Squidex.Domain.Apps.Entities/Apps/AppDomainObject.cs
  8. 8
      src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs
  9. 12
      src/Squidex.Domain.Apps.Entities/Assets/AssetDomainObject.cs
  10. 6
      src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs
  11. 14
      src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs
  12. 4
      src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs
  13. 16
      src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs
  14. 8
      src/Squidex.Domain.Apps.Entities/Rules/RuleDomainObject.cs
  15. 10
      src/Squidex.Domain.Apps.Entities/Rules/State/RuleState.cs
  16. 30
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaCommandMiddleware.cs
  17. 12
      src/Squidex.Domain.Apps.Entities/Schemas/SchemaDomainObject.cs
  18. 11
      src/Squidex.Infrastructure/Commands/AggregateHandler.cs
  19. 75
      src/Squidex.Infrastructure/Commands/DomainObjectBase.cs
  20. 3
      src/Squidex.Infrastructure/Commands/IDomainObject.cs
  21. 4
      src/Squidex.Infrastructure/EventSourcing/CommonHeaders.cs
  22. 12
      src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs
  23. 12
      src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs
  24. 1
      src/Squidex/appsettings.json
  25. 21
      src/Squidex/wwwroot/index.html
  26. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppCommandMiddlewareTests.cs
  27. 26
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppDomainObjectTests.cs
  28. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetDomainObjectTests.cs
  29. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentDomainObjectTests.cs
  30. 16
      tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDomainObjectTests.cs
  31. 36
      tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaDomainObjectTests.cs
  32. 19
      tests/Squidex.Infrastructure.Tests/Commands/AggregateHandlerTests.cs
  33. 14
      tests/Squidex.Infrastructure.Tests/Commands/DomainObjectBaseTests.cs
  34. 11
      tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs
  35. 111
      tools/Migrate_01/Migration01_FromCqrs.cs
  36. 13
      tools/Migrate_01/Migration02_AddPatterns.cs

5
Squidex.ruleset

@ -37,7 +37,7 @@
<Rule Id="SA1311" Action="Error" />
<Rule Id="SA1400" Action="Error" />
<Rule Id="SA1401" Action="Error" />
<Rul Id="SA1402" Action="None" />
<Rule Id="SA1402" Action="None" />
<Rule Id="SA1407" Action="Error" />
<Rule Id="SA1408" Action="None" />
<Rule Id="SA1502" Action="Error" />
@ -80,4 +80,7 @@
<Rules AnalyzerId="Roslyn.Core" RuleNamespace="Roslyn.Core">
<Rule Id="AD0001" Action="None" />
</Rules>
<Rules AnalyzerId="Roslyn.Core" RuleNamespace="Microsoft.CodeAnalysis.Diagnostics">
<Rule Id="IDE0042" Action="None" />
</Rules>
</RuleSet>

11
src/Squidex.Domain.Apps.Core.Operations/HandleRules/RuleService.cs

@ -83,8 +83,15 @@ namespace Squidex.Domain.Apps.Core.HandleRules
var actionName = typeNameRegistry.GetName(actionType);
var actionData = actionHandler.CreateJob(appEventEnvelope, eventName, rule.Action);
var eventTime = @event.Headers.Contains(CommonHeaders.Timestamp) ? @event.Headers.Timestamp() : now;
var eventGuid = @event.Headers.Contains(CommonHeaders.EventId) ? @event.Headers.EventId() : Guid.NewGuid();
var eventTime =
@event.Headers.Contains(CommonHeaders.Timestamp) ?
@event.Headers.Timestamp() :
now;
var eventGuid =
@event.Headers.Contains(CommonHeaders.EventId) ?
@event.Headers.EventId() :
Guid.NewGuid();
var job = new RuleJob
{

2
src/Squidex.Domain.Apps.Entities.MongoDb/Contents/MongoContentRepository_SnapshotStore.cs

@ -61,6 +61,8 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.Contents
ReferencedIds = idData?.ToReferencedIds(schema.SchemaDef),
});
document.Version = newVersion;
try
{
await Collection.InsertOneAsync(document);

9
src/Squidex.Domain.Apps.Entities.MongoDb/History/MongoHistoryEventRepository.cs

@ -90,14 +90,7 @@ namespace Squidex.Domain.Apps.Entities.MongoDb.History
entity.AppId = appEvent.AppId.Id;
if (@event.Headers.Contains(CommonHeaders.SnapshotVersion))
{
entity.Version = @event.Headers.SnapshotVersion();
}
else
{
entity.Version = @event.Headers.EventStreamNumber();
}
entity.Version = @event.Headers.EventStreamNumber();
entity.Channel = message.Channel;
entity.Message = message.Message;

16
src/Squidex.Domain.Apps.Entities/AppProvider.cs

@ -57,12 +57,12 @@ namespace Squidex.Domain.Apps.Entities
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(id);
if (!IsFound(schema) || schema.State.IsDeleted)
if (!IsFound(schema) || schema.Snapshot.IsDeleted)
{
return (null, null);
}
return (app.State, schema.State);
return (app.Snapshot, schema.Snapshot);
}
public async Task<IAppEntity> GetAppAsync(string appName)
@ -74,7 +74,7 @@ namespace Squidex.Domain.Apps.Entities
return null;
}
return (await stateFactory.GetSingleAsync<AppDomainObject>(appId)).State;
return (await stateFactory.GetSingleAsync<AppDomainObject>(appId)).Snapshot;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, string name)
@ -86,7 +86,7 @@ namespace Squidex.Domain.Apps.Entities
return null;
}
return (await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaId)).State;
return (await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaId)).Snapshot;
}
public async Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id)
@ -98,7 +98,7 @@ namespace Squidex.Domain.Apps.Entities
return null;
}
return schema.State;
return schema.Snapshot;
}
public async Task<List<ISchemaEntity>> GetSchemasAsync(Guid appId)
@ -109,7 +109,7 @@ namespace Squidex.Domain.Apps.Entities
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<SchemaDomainObject>(id)));
return schemas.Where(IsFound).Select(s => (ISchemaEntity)s.State).ToList();
return schemas.Where(IsFound).Select(s => (ISchemaEntity)s.Snapshot).ToList();
}
public async Task<List<IRuleEntity>> GetRulesAsync(Guid appId)
@ -120,7 +120,7 @@ namespace Squidex.Domain.Apps.Entities
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<RuleDomainObject>(id)));
return rules.Where(IsFound).Select(r => (IRuleEntity)r.State).ToList();
return rules.Where(IsFound).Select(r => (IRuleEntity)r.Snapshot).ToList();
}
public async Task<List<IAppEntity>> GetUserApps(string userId)
@ -131,7 +131,7 @@ namespace Squidex.Domain.Apps.Entities
await Task.WhenAll(
ids.Select(id => stateFactory.GetSingleAsync<AppDomainObject>(id)));
return apps.Where(IsFound).Select(a => (IAppEntity)a.State).ToList();
return apps.Where(IsFound).Select(a => (IAppEntity)a.Snapshot).ToList();
}
private Task<Guid> GetAppIdAsync(string name)

26
src/Squidex.Domain.Apps.Entities/Apps/AppCommandMiddleware.cs

@ -62,7 +62,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a =>
{
await GuardAppContributors.CanAssign(a.State.Contributors, command, userResolver, appPlansProvider.GetPlan(a.State.Plan?.PlanId));
await GuardAppContributors.CanAssign(a.Snapshot.Contributors, command, userResolver, appPlansProvider.GetPlan(a.Snapshot.Plan?.PlanId));
a.AssignContributor(command);
});
@ -72,7 +72,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppContributors.CanRemove(a.State.Contributors, command);
GuardAppContributors.CanRemove(a.Snapshot.Contributors, command);
a.RemoveContributor(command);
});
@ -82,7 +82,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanAttach(a.State.Clients, command);
GuardAppClients.CanAttach(a.Snapshot.Clients, command);
a.AttachClient(command);
});
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanUpdate(a.State.Clients, command);
GuardAppClients.CanUpdate(a.Snapshot.Clients, command);
a.UpdateClient(command);
});
@ -102,7 +102,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppClients.CanRevoke(a.State.Clients, command);
GuardAppClients.CanRevoke(a.Snapshot.Clients, command);
a.RevokeClient(command);
});
@ -112,7 +112,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanAdd(a.State.LanguagesConfig, command);
GuardAppLanguages.CanAdd(a.Snapshot.LanguagesConfig, command);
a.AddLanguage(command);
});
@ -122,7 +122,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanRemove(a.State.LanguagesConfig, command);
GuardAppLanguages.CanRemove(a.Snapshot.LanguagesConfig, command);
a.RemoveLanguage(command);
});
@ -132,7 +132,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppLanguages.CanUpdate(a.State.LanguagesConfig, command);
GuardAppLanguages.CanUpdate(a.Snapshot.LanguagesConfig, command);
a.UpdateLanguage(command);
});
@ -142,7 +142,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppPattern.CanAdd(a.State.Patterns, command);
GuardAppPattern.CanAdd(a.Snapshot.Patterns, command);
a.AddPattern(command);
});
@ -152,7 +152,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppPattern.CanDelete(a.State.Patterns, command);
GuardAppPattern.CanDelete(a.Snapshot.Patterns, command);
a.DeletePattern(command);
});
@ -162,7 +162,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
await handler.UpdateSyncedAsync<AppDomainObject>(context, a =>
{
GuardAppPattern.CanUpdate(a.State.Patterns, command);
GuardAppPattern.CanUpdate(a.Snapshot.Patterns, command);
a.UpdatePattern(command);
});
@ -172,7 +172,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a =>
{
GuardApp.CanChangePlan(command, a.State.Plan, appPlansProvider);
GuardApp.CanChangePlan(command, a.Snapshot.Plan, appPlansProvider);
if (command.FromCallback)
{
@ -180,7 +180,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
}
else
{
var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, command.AppId.Id, a.State.Name, command.PlanId);
var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, command.AppId.Id, a.Snapshot.Name, command.PlanId);
if (result is PlanChangedResult)
{

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

@ -7,6 +7,7 @@
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Domain.Apps.Entities.Apps.State;
@ -36,13 +37,24 @@ namespace Squidex.Domain.Apps.Entities.Apps
var appId = new NamedId<Guid>(command.AppId, command.Name);
RaiseEvent(CreateInitalEvent(appId, command.Actor, command.Name));
RaiseEvent(CreateInitialOwner(appId, command.Actor));
RaiseEvent(CreateInitialLanguage(appId, command.Actor));
var events = new List<AppEvent>
{
CreateInitalEvent(command.Name),
CreateInitialOwner(command.Actor),
CreateInitialLanguage()
};
foreach (var pattern in initialPatterns)
{
RaiseEvent(CreateInitialPattern(appId, command.Actor, pattern.Key, pattern.Value));
events.Add(CreateInitialPattern(pattern.Key, pattern.Value));
}
foreach (var @event in events)
{
@event.Actor = command.Actor;
@event.AppId = appId;
RaiseEvent(@event);
}
return this;
@ -168,35 +180,35 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
if (@event.AppId == null)
{
@event.AppId = new NamedId<Guid>(State.Id, State.Name);
@event.AppId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name);
}
RaiseEvent(Envelope.Create(@event));
}
private static AppCreated CreateInitalEvent(NamedId<Guid> appId, RefToken actor, string name)
private static AppCreated CreateInitalEvent(string name)
{
return new AppCreated { AppId = appId, Actor = actor, Name = name };
return new AppCreated { Name = name };
}
private static AppPatternAdded CreateInitialPattern(NamedId<Guid> appId, RefToken actor, Guid id, AppPattern p)
private static AppPatternAdded CreateInitialPattern(Guid id, AppPattern pattern)
{
return new AppPatternAdded { AppId = appId, Actor = actor, PatternId = id, Name = p.Name, Pattern = p.Pattern, Message = p.Message };
return new AppPatternAdded { PatternId = id, Name = pattern.Name, Pattern = pattern.Pattern, Message = pattern.Message };
}
private static AppLanguageAdded CreateInitialLanguage(NamedId<Guid> appId, RefToken actor)
private static AppLanguageAdded CreateInitialLanguage()
{
return new AppLanguageAdded { AppId = appId, Actor = actor, Language = Language.EN };
return new AppLanguageAdded { Language = Language.EN };
}
private static AppContributorAssigned CreateInitialOwner(NamedId<Guid> appId, RefToken actor)
private static AppContributorAssigned CreateInitialOwner(RefToken actor)
{
return new AppContributorAssigned { AppId = appId, Actor = actor, ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner };
return new AppContributorAssigned { ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner };
}
private void ThrowIfNotCreated()
{
if (string.IsNullOrWhiteSpace(State.Name))
if (string.IsNullOrWhiteSpace(Snapshot.Name))
{
throw new DomainException("App has not been created.");
}
@ -204,15 +216,15 @@ namespace Squidex.Domain.Apps.Entities.Apps
private void ThrowIfCreated()
{
if (!string.IsNullOrWhiteSpace(State.Name))
if (!string.IsNullOrWhiteSpace(Snapshot.Name))
{
throw new DomainException("App has already been created.");
}
}
protected override void OnRaised(Envelope<IEvent> @event)
public override void ApplyEvent(Envelope<IEvent> @event)
{
UpdateState(State.Apply(@event));
ApplySnapshot(Snapshot.Apply(@event));
}
}
}

8
src/Squidex.Domain.Apps.Entities/Assets/AssetCommandMiddleware.cs

@ -53,7 +53,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
context.Complete(EntityCreatedResult.Create(command.AssetId, a.Version));
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.State.FileVersion, null);
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.Snapshot.FileVersion, null);
}
finally
{
@ -75,10 +75,10 @@ namespace Squidex.Domain.Apps.Entities.Assets
await assetStore.UploadTemporaryAsync(context.ContextId.ToString(), command.File.OpenRead());
context.Complete(new AssetSavedResult(a.Version, a.State.FileVersion));
context.Complete(new AssetSavedResult(a.Version, a.Snapshot.FileVersion));
});
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.State.FileVersion, null);
await assetStore.CopyTemporaryAsync(context.ContextId.ToString(), command.AssetId.ToString(), asset.Snapshot.FileVersion, null);
}
finally
{
@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
return handler.UpdateSyncedAsync<AssetDomainObject>(context, a =>
{
GuardAsset.CanRename(command, a.State.FileName);
GuardAsset.CanRename(command, a.Snapshot.FileName);
a.Rename(command);
});

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

@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
var @event = SimpleMapper.Map(command, new AssetUpdated
{
FileVersion = State.FileVersion + 1,
FileVersion = Snapshot.FileVersion + 1,
FileSize = command.File.FileSize,
MimeType = command.File.MimeType,
PixelWidth = command.ImageInfo?.PixelWidth,
@ -61,7 +61,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = State.TotalSize }));
RaiseEvent(SimpleMapper.Map(command, new AssetDeleted { DeletedSize = Snapshot.TotalSize }));
return this;
}
@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
private void VerifyNotCreated()
{
if (!string.IsNullOrWhiteSpace(State.FileName))
if (!string.IsNullOrWhiteSpace(Snapshot.FileName))
{
throw new DomainException("Asset has already been created.");
}
@ -85,15 +85,15 @@ namespace Squidex.Domain.Apps.Entities.Assets
private void VerifyCreatedAndNotDeleted()
{
if (State.IsDeleted || string.IsNullOrWhiteSpace(State.FileName))
if (Snapshot.IsDeleted || string.IsNullOrWhiteSpace(Snapshot.FileName))
{
throw new DomainException("Asset has already been deleted or not created yet.");
}
}
protected override void OnRaised(Envelope<IEvent> @event)
public override void ApplyEvent(Envelope<IEvent> @event)
{
UpdateState(State.Apply(@event));
ApplySnapshot(Snapshot.Apply(@event));
}
}
}

6
src/Squidex.Domain.Apps.Entities/Contents/ContentCommandMiddleware.cs

@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
content.Update(command);
context.Complete(new ContentDataChangedResult(content.State.Data, content.Version));
context.Complete(new ContentDataChangedResult(content.Snapshot.Data, content.Version));
});
}
@ -100,7 +100,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
content.Patch(command);
context.Complete(new ContentDataChangedResult(content.State.Data, content.Version));
context.Complete(new ContentDataChangedResult(content.Snapshot.Data, content.Version));
});
}
@ -108,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
return handler.UpdateAsync<ContentDomainObject>(context, async content =>
{
GuardContent.CanChangeContentStatus(content.State.Status, command);
GuardContent.CanChangeContentStatus(content.Snapshot.Status, command);
var operationContext = await CreateContext(command, content, () => "Failed to patch content.");

14
src/Squidex.Domain.Apps.Entities/Contents/ContentDomainObject.cs

@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
VerifyCreatedAndNotDeleted();
if (!command.Data.Equals(State.Data))
if (!command.Data.Equals(Snapshot.Data))
{
RaiseEvent(SimpleMapper.Map(command, new ContentUpdated()));
}
@ -67,9 +67,9 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
VerifyCreatedAndNotDeleted();
var newData = command.Data.MergeInto(State.Data);
var newData = command.Data.MergeInto(Snapshot.Data);
if (!newData.Equals(State.Data))
if (!newData.Equals(Snapshot.Data))
{
var @event = SimpleMapper.Map(command, new ContentUpdated());
@ -83,7 +83,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
private void VerifyNotCreated()
{
if (State.Data != null)
if (Snapshot.Data != null)
{
throw new DomainException("Content has already been created.");
}
@ -91,15 +91,15 @@ namespace Squidex.Domain.Apps.Entities.Contents
private void VerifyCreatedAndNotDeleted()
{
if (State.IsDeleted || State.Data == null)
if (Snapshot.IsDeleted || Snapshot.Data == null)
{
throw new DomainException("Content has already been deleted or not created yet.");
}
}
protected override void OnRaised(Envelope<IEvent> @event)
public override void ApplyEvent(Envelope<IEvent> @event)
{
UpdateState(State.Apply(@event));
ApplySnapshot(Snapshot.Apply(@event));
}
}
}

4
src/Squidex.Domain.Apps.Entities/Contents/ContentOperationContext.cs

@ -119,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
if (command is ContentDataCommand dataCommand)
{
var ctx = new ScriptContext { ContentId = content.State.Id, OldData = content.State.Data, User = command.User, Operation = operation.ToString(), Data = dataCommand.Data };
var ctx = new ScriptContext { ContentId = content.Snapshot.Id, OldData = content.Snapshot.Data, User = command.User, Operation = operation.ToString(), Data = dataCommand.Data };
dataCommand.Data = scriptEngine.ExecuteAndTransform(ctx, script(schemaEntity));
}
@ -129,7 +129,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
public Task ExecuteScriptAsync(Func<ISchemaEntity, string> script, object operation)
{
var ctx = new ScriptContext { ContentId = content.State.Id, OldData = content.State.Data, User = command.User, Operation = operation.ToString() };
var ctx = new ScriptContext { ContentId = content.Snapshot.Id, OldData = content.Snapshot.Data, User = command.User, Operation = operation.ToString() };
scriptEngine.Execute(ctx, script(schemaEntity));

16
src/Squidex.Domain.Apps.Entities/Rules/RuleCommandMiddleware.cs

@ -33,21 +33,21 @@ namespace Squidex.Domain.Apps.Entities.Rules
protected Task On(CreateRule command, CommandContext context)
{
return handler.CreateSyncedAsync<RuleDomainObject>(context, async w =>
return handler.CreateSyncedAsync<RuleDomainObject>(context, async r =>
{
await GuardRule.CanCreate(command, appProvider);
w.Create(command);
r.Create(command);
});
}
protected Task On(UpdateRule command, CommandContext context)
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, async c =>
return handler.UpdateSyncedAsync<RuleDomainObject>(context, async r =>
{
await GuardRule.CanUpdate(command, appProvider);
c.Update(command);
r.Update(command);
});
}
@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r =>
{
GuardRule.CanEnable(command, r.State.RuleDef);
GuardRule.CanEnable(command, r.Snapshot.RuleDef);
r.Enable(command);
});
@ -65,7 +65,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r =>
{
GuardRule.CanDisable(command, r.State.RuleDef);
GuardRule.CanDisable(command, r.Snapshot.RuleDef);
r.Disable(command);
});
@ -73,11 +73,11 @@ namespace Squidex.Domain.Apps.Entities.Rules
protected Task On(DeleteRule command, CommandContext context)
{
return handler.UpdateSyncedAsync<RuleDomainObject>(context, c =>
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r =>
{
GuardRule.CanDelete(command);
c.Delete(command);
r.Delete(command);
});
}

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

@ -55,7 +55,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
private void VerifyNotCreated()
{
if (State.RuleDef != null)
if (Snapshot.RuleDef != null)
{
throw new DomainException("Webhook has already been created.");
}
@ -63,15 +63,15 @@ namespace Squidex.Domain.Apps.Entities.Rules
private void VerifyCreatedAndNotDeleted()
{
if (State.IsDeleted || State.RuleDef == null)
if (Snapshot.IsDeleted || Snapshot.RuleDef == null)
{
throw new DomainException("Webhook has already been deleted or not created yet.");
}
}
protected override void OnRaised(Envelope<IEvent> @event)
public override void ApplyEvent(Envelope<IEvent> @event)
{
UpdateState(State.Apply(@event));
ApplySnapshot(Snapshot.Apply(@event));
}
}
}

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

@ -37,7 +37,15 @@ namespace Squidex.Domain.Apps.Entities.Rules.State
protected void On(RuleUpdated @event)
{
RuleDef = RuleDef.Update(@event.Trigger).Update(@event.Action);
if (@event.Trigger != null)
{
RuleDef = RuleDef.Update(@event.Trigger);
}
if (@event.Action != null)
{
RuleDef = RuleDef.Update(@event.Action);
}
}
protected void On(RuleEnabled @event)

30
src/Squidex.Domain.Apps.Entities/Schemas/SchemaCommandMiddleware.cs

@ -48,11 +48,11 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanAdd(s.State.SchemaDef, command);
GuardSchemaField.CanAdd(s.Snapshot.SchemaDef, command);
s.Add(command);
context.Complete(EntityCreatedResult.Create(s.State.SchemaDef.FieldsById.Values.First(x => x.Name == command.Name).Id, s.Version));
context.Complete(EntityCreatedResult.Create(s.Snapshot.SchemaDef.FieldsById.Values.First(x => x.Name == command.Name).Id, s.Version));
});
}
@ -60,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanDelete(s.State.SchemaDef, command);
GuardSchemaField.CanDelete(s.Snapshot.SchemaDef, command);
s.DeleteField(command);
});
@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanLock(s.State.SchemaDef, command);
GuardSchemaField.CanLock(s.Snapshot.SchemaDef, command);
s.LockField(command);
});
@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanHide(s.State.SchemaDef, command);
GuardSchemaField.CanHide(s.Snapshot.SchemaDef, command);
s.HideField(command);
});
@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanShow(s.State.SchemaDef, command);
GuardSchemaField.CanShow(s.Snapshot.SchemaDef, command);
s.ShowField(command);
});
@ -100,7 +100,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanDisable(s.State.SchemaDef, command);
GuardSchemaField.CanDisable(s.Snapshot.SchemaDef, command);
s.DisableField(command);
});
@ -110,7 +110,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanEnable(s.State.SchemaDef, command);
GuardSchemaField.CanEnable(s.Snapshot.SchemaDef, command);
s.EnableField(command);
});
@ -120,7 +120,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchemaField.CanUpdate(s.State.SchemaDef, command);
GuardSchemaField.CanUpdate(s.Snapshot.SchemaDef, command);
s.UpdateField(command);
});
@ -130,7 +130,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanReorder(s.State.SchemaDef, command);
GuardSchema.CanReorder(s.Snapshot.SchemaDef, command);
s.Reorder(command);
});
@ -140,7 +140,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanUpdate(s.State.SchemaDef, command);
GuardSchema.CanUpdate(s.Snapshot.SchemaDef, command);
s.Update(command);
});
@ -150,7 +150,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanPublish(s.State.SchemaDef, command);
GuardSchema.CanPublish(s.Snapshot.SchemaDef, command);
s.Publish(command);
});
@ -160,7 +160,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanUnpublish(s.State.SchemaDef, command);
GuardSchema.CanUnpublish(s.Snapshot.SchemaDef, command);
s.Unpublish(command);
});
@ -170,7 +170,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanConfigureScripts(s.State.SchemaDef, command);
GuardSchema.CanConfigureScripts(s.Snapshot.SchemaDef, command);
s.ConfigureScripts(command);
});
@ -180,7 +180,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s =>
{
GuardSchema.CanDelete(s.State.SchemaDef, command);
GuardSchema.CanDelete(s.Snapshot.SchemaDef, command);
s.Delete(command);
});

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

@ -57,7 +57,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
VerifyCreatedAndNotDeleted();
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(State.TotalFields + 1, command.Name) }));
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(Snapshot.TotalFields + 1, command.Name) }));
return this;
}
@ -183,7 +183,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
{
SimpleMapper.Map(fieldCommand, @event);
if (State.SchemaDef.FieldsById.TryGetValue(fieldCommand.FieldId, out var field))
if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.FieldId, out var field))
{
@event.FieldId = new NamedId<long>(field.Id, field.Name);
}
@ -193,7 +193,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
private void VerifyNotCreated()
{
if (State.SchemaDef != null)
if (Snapshot.SchemaDef != null)
{
throw new DomainException("Schema has already been created.");
}
@ -201,15 +201,15 @@ namespace Squidex.Domain.Apps.Entities.Schemas
private void VerifyCreatedAndNotDeleted()
{
if (State.IsDeleted || State.SchemaDef == null)
if (Snapshot.IsDeleted || Snapshot.SchemaDef == null)
{
throw new DomainException("Schema has already been deleted or not created yet.");
}
}
protected override void OnRaised(Envelope<IEvent> @event)
public override void ApplyEvent(Envelope<IEvent> @event)
{
UpdateState(State.Apply(@event, registry));
ApplySnapshot(Snapshot.Apply(@event, registry));
}
}
}

11
src/Squidex.Infrastructure/Commands/AggregateHandler.cs

@ -8,7 +8,6 @@
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.States;
using Squidex.Infrastructure.Tasks;
@ -18,19 +17,15 @@ namespace Squidex.Infrastructure.Commands
{
private readonly AsyncLockPool lockPool = new AsyncLockPool(10000);
private readonly IStateFactory stateFactory;
private readonly ISemanticLog log;
private readonly IServiceProvider serviceProvider;
public AggregateHandler(IStateFactory stateFactory, IServiceProvider serviceProvider, ISemanticLog log)
public AggregateHandler(IStateFactory stateFactory, IServiceProvider serviceProvider)
{
Guard.NotNull(stateFactory, nameof(stateFactory));
Guard.NotNull(serviceProvider, nameof(serviceProvider));
Guard.NotNull(log, nameof(log));
this.stateFactory = stateFactory;
this.serviceProvider = serviceProvider;
this.log = log;
}
public Task<T> CreateAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IDomainObject
@ -76,7 +71,7 @@ namespace Squidex.Infrastructure.Commands
await handler(domainObject);
await domainObject.WriteAsync(log);
await domainObject.WriteAsync();
if (!context.IsCompleted)
{
@ -111,7 +106,7 @@ namespace Squidex.Infrastructure.Commands
await handler(domainObject);
await domainObject.WriteAsync(log);
await domainObject.WriteAsync();
if (!context.IsCompleted)
{

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

@ -8,9 +8,9 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.States;
namespace Squidex.Infrastructure.Commands
@ -19,40 +19,24 @@ namespace Squidex.Infrastructure.Commands
{
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>();
private Guid id;
private T state;
private T snapshot = new T { Version = EtagVersion.Empty };
private IPersistence<T> persistence;
public long Version
{
get { return state.Version; }
get { return snapshot.Version; }
}
public T State
public T Snapshot
{
get { return state; }
}
protected DomainObjectBase()
{
state = new T();
state.Version = EtagVersion.Empty;
}
public IReadOnlyList<Envelope<IEvent>> GetUncomittedEvents()
{
return uncomittedEvents;
}
public void ClearUncommittedEvents()
{
uncomittedEvents.Clear();
get { return snapshot; }
}
public Task ActivateAsync(Guid key, IStore<Guid> store)
{
id = key;
persistence = store.WithSnapshots<T, Guid>(key, s => state = s);
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(key, ApplySnapshot, ApplyEvent);
return persistence.ReadAsync();
}
@ -68,49 +52,56 @@ namespace Squidex.Infrastructure.Commands
@event.SetAggregateId(id);
OnRaised(@event.To<IEvent>());
ApplyEvent(@event.To<IEvent>());
state.Version++;
snapshot.Version++;
uncomittedEvents.Add(@event.To<IEvent>());
}
public void UpdateState(T newState)
public IReadOnlyList<Envelope<IEvent>> GetUncomittedEvents()
{
state = newState;
return uncomittedEvents;
}
protected virtual void OnRaised(Envelope<IEvent> @event)
public void ClearUncommittedEvents()
{
uncomittedEvents.Clear();
}
public Task WriteStateAsync(long version)
public virtual void ApplySnapshot(T newSnapshot)
{
state.Version = version;
snapshot = newSnapshot;
}
public virtual void ApplyEvent(Envelope<IEvent> @event)
{
}
public Task WriteSnapshotAsync()
{
if (persistence.Version == EtagVersion.NotFound)
{
Debugger.Break();
}
return persistence.WriteSnapshotAsync(state);
snapshot.Version = persistence.Version;
return persistence.WriteSnapshotAsync(snapshot);
}
public async Task WriteAsync(ISemanticLog log)
public async Task WriteAsync()
{
var events = uncomittedEvents.ToArray();
if (events.Length > 0)
{
foreach (var @event in events)
{
@event.SetSnapshotVersion(state.Version);
}
await persistence.WriteSnapshotAsync(state);
try
{
snapshot.Version = persistence.Version + events.Length;
await persistence.WriteEventsAsync(events);
}
catch (Exception ex)
{
log.LogFatal(ex, w => w.WriteProperty("action", "writeEvents"));
await persistence.WriteSnapshotAsync(snapshot);
}
finally
{

3
src/Squidex.Infrastructure/Commands/IDomainObject.cs

@ -8,7 +8,6 @@
using System;
using System.Threading.Tasks;
using Squidex.Infrastructure.Log;
using Squidex.Infrastructure.States;
namespace Squidex.Infrastructure.Commands
@ -17,6 +16,6 @@ namespace Squidex.Infrastructure.Commands
{
long Version { get; }
Task WriteAsync(ISemanticLog log);
Task WriteAsync();
}
}

4
src/Squidex.Infrastructure/EventSourcing/CommonHeaders.cs

@ -20,10 +20,6 @@ namespace Squidex.Infrastructure.EventSourcing
public static readonly string EventStreamNumber = "EventStreamNumber";
public static readonly string SnapshotVersion = "SnapshotVersion";
public static readonly string Timestamp = "Timestamp";
public static readonly string Actor = "Actor";
}
}

12
src/Squidex.Infrastructure/EventSourcing/EnvelopeExtensions.cs

@ -26,18 +26,6 @@ namespace Squidex.Infrastructure.EventSourcing
return envelope;
}
public static long SnapshotVersion(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.SnapshotVersion].ToInt64(CultureInfo.InvariantCulture);
}
public static Envelope<T> SetSnapshotVersion<T>(this Envelope<T> envelope, long value) where T : class
{
envelope.Headers.Set(CommonHeaders.SnapshotVersion, value);
return envelope;
}
public static long EventStreamNumber(this EnvelopeHeaders headers)
{
return headers[CommonHeaders.EventStreamNumber].ToInt64(CultureInfo.InvariantCulture);

12
src/Squidex.Infrastructure/States/Persistence{TOwner,TState,TKey}.cs → src/Squidex.Infrastructure/States/Persistence{TOwner,TSnapshot,TKey}.cs

@ -16,17 +16,17 @@ using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Infrastructure.States
{
internal class Persistence<TOwner, TState, TKey> : IPersistence<TState>
internal class Persistence<TOwner, TSnapshot, TKey> : IPersistence<TSnapshot>
{
private readonly TKey ownerKey;
private readonly ISnapshotStore<TState, TKey> snapshotStore;
private readonly ISnapshotStore<TSnapshot, TKey> snapshotStore;
private readonly IStreamNameResolver streamNameResolver;
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
private readonly PersistenceMode persistenceMode;
private readonly Action invalidate;
private readonly Action failed;
private readonly Func<TState, Task> applyState;
private readonly Func<TSnapshot, Task> applyState;
private readonly Func<Envelope<IEvent>, Task> applyEvent;
private long versionSnapshot = EtagVersion.Empty;
private long versionEvents = EtagVersion.Empty;
@ -42,10 +42,10 @@ namespace Squidex.Infrastructure.States
Action failed,
IEventStore eventStore,
IEventDataFormatter eventDataFormatter,
ISnapshotStore<TState, TKey> snapshotStore,
ISnapshotStore<TSnapshot, TKey> snapshotStore,
IStreamNameResolver streamNameResolver,
PersistenceMode persistenceMode,
Func<TState, Task> applyState,
Func<TSnapshot, Task> applyState,
Func<Envelope<IEvent>, Task> applyEvent)
{
this.ownerKey = ownerKey;
@ -129,7 +129,7 @@ namespace Squidex.Infrastructure.States
}
}
public async Task WriteSnapshotAsync(TState state)
public async Task WriteSnapshotAsync(TSnapshot state)
{
try
{

1
src/Squidex/appsettings.json

@ -20,6 +20,7 @@
// Regex for urls.
"Url": "^(?:http(s)?:\\/\\/)?[\\w.-]+(?:\\.[\\w\\.-]+)+[\\w\\-\\._~:/?#[\\]@!\\$&'\\(\\)\\*\\+,;=.]+$"
},
"map": {
/*
* Define the type of the geolocation service.

21
src/Squidex/wwwroot/index.html

@ -15,9 +15,11 @@
}
</style>
</noscript>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/outdated-browser/1.1.3/outdatedbrowser.min.css">
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/outdated-browser/1.1.3/outdatedbrowser.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent2/3.0.3/cookieconsent.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/outdated-browser/1.1.3/outdatedbrowser.min.js"></script>
<script>
function addLoadEvent(func) {
@ -25,7 +27,7 @@
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
window.onload = function () {
if (oldonload) {
oldonload();
}
@ -33,7 +35,18 @@
}
}
}
addLoadEvent(function() {
addLoadEvent(function () {
window.cookieconsent.initialise({
palette: {
popup: {
background: '#000'
},
button: {
background: '#3389ff'
}
}
});
outdatedBrowser({ lowerThan: 'borderImage', languagePath: '' });
});
</script>

2
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppCommandMiddlewareTests.cs

@ -170,7 +170,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
await sut.HandleAsync(context);
});
Assert.Null(app.State.Plan);
Assert.Null(app.Snapshot.Plan);
}
[Fact]

26
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppDomainObjectTests.cs

@ -59,7 +59,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
app.Create(CreateCommand(new CreateApp { Name = AppName, Actor = User, AppId = AppId }));
Assert.Equal(AppName, app.State.Name);
Assert.Equal(AppName, app.Snapshot.Name);
app.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId }));
Assert.Equal(planId, sut.State.Plan.PlanId);
Assert.Equal(planId, sut.Snapshot.Plan.PlanId);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -111,7 +111,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }));
Assert.Equal(AppContributorPermission.Editor, sut.State.Contributors[contributorId]);
Assert.Equal(AppContributorPermission.Editor, sut.Snapshot.Contributors[contributorId]);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -136,7 +136,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }));
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId }));
Assert.False(sut.State.Contributors.ContainsKey(contributorId));
Assert.False(sut.Snapshot.Contributors.ContainsKey(contributorId));
sut.GetUncomittedEvents().Skip(1)
.ShouldHaveSameEvents(
@ -162,7 +162,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.AttachClient(CreateCommand(command));
Assert.True(sut.State.Clients.ContainsKey(clientId));
Assert.True(sut.Snapshot.Clients.ContainsKey(clientId));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -187,7 +187,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.RevokeClient(CreateCommand(new RevokeClient { Id = clientId }));
Assert.False(sut.State.Clients.ContainsKey(clientId));
Assert.False(sut.Snapshot.Clients.ContainsKey(clientId));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -212,7 +212,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName, Permission = AppClientPermission.Developer }));
Assert.Equal(clientNewName, sut.State.Clients[clientId].Name);
Assert.Equal(clientNewName, sut.Snapshot.Clients[clientId].Name);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -237,7 +237,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE }));
Assert.True(sut.State.LanguagesConfig.Contains(Language.DE));
Assert.True(sut.Snapshot.LanguagesConfig.Contains(Language.DE));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -262,7 +262,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.DE }));
Assert.False(sut.State.LanguagesConfig.Contains(Language.DE));
Assert.False(sut.Snapshot.LanguagesConfig.Contains(Language.DE));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -287,7 +287,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.DE, Fallback = new List<Language> { Language.EN } }));
Assert.True(sut.State.LanguagesConfig.Contains(Language.DE));
Assert.True(sut.Snapshot.LanguagesConfig.Contains(Language.DE));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -308,7 +308,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.AddPattern(CreateCommand(new AddPattern { PatternId = patternId, Name = "Any", Pattern = ".*", Message = "Msg" }));
Assert.Single(sut.State.Patterns);
Assert.Single(sut.Snapshot.Patterns);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -336,7 +336,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.DeletePattern(CreateCommand(new DeletePattern { PatternId = patternId }));
Assert.Empty(sut.State.Patterns);
Assert.Empty(sut.Snapshot.Patterns);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -358,7 +358,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
sut.UpdatePattern(CreateCommand(new UpdatePattern { PatternId = patternId, Name = "Any", Pattern = ".*", Message = "Msg" }));
Assert.Single(sut.State.Patterns);
Assert.Single(sut.Snapshot.Patterns);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(

8
tests/Squidex.Domain.Apps.Entities.Tests/Assets/AssetDomainObjectTests.cs

@ -45,7 +45,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
{
sut.Create(CreateAssetCommand(new CreateAsset { File = file, ImageInfo = image }));
Assert.Equal(0, sut.State.FileVersion);
Assert.Equal(0, sut.Snapshot.FileVersion);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -90,7 +90,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
sut.Update(CreateAssetCommand(new UpdateAsset { File = file, ImageInfo = image }));
Assert.Equal(1, sut.State.FileVersion);
Assert.Equal(1, sut.Snapshot.FileVersion);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -134,7 +134,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "my-new-image.png" }));
Assert.Equal("my-new-image.png", sut.State.FileName);
Assert.Equal("my-new-image.png", sut.Snapshot.FileName);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -171,7 +171,7 @@ namespace Squidex.Domain.Apps.Entities.Assets
sut.Delete(CreateAssetCommand(new DeleteAsset()));
Assert.True(sut.State.IsDeleted);
Assert.True(sut.Snapshot.IsDeleted);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(

4
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentDomainObjectTests.cs

@ -196,7 +196,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published }));
Assert.Equal(Status.Published, sut.State.Status);
Assert.Equal(Status.Published, sut.Snapshot.Status);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -232,7 +232,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
sut.Delete(CreateContentCommand(new DeleteContent()));
Assert.True(sut.State.IsDeleted);
Assert.True(sut.Snapshot.IsDeleted);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(

16
tests/Squidex.Domain.Apps.Entities.Tests/Rules/RuleDomainObjectTests.cs

@ -49,10 +49,10 @@ namespace Squidex.Domain.Apps.Entities.Rules
sut.Create(CreateRuleCommand(command));
Assert.Equal(AppId, sut.State.AppId);
Assert.Equal(AppId, sut.Snapshot.AppId);
Assert.Same(ruleTrigger, sut.State.RuleDef.Trigger);
Assert.Same(ruleAction, sut.State.RuleDef.Action);
Assert.Same(ruleTrigger, sut.Snapshot.RuleDef.Trigger);
Assert.Same(ruleAction, sut.Snapshot.RuleDef.Action);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -100,8 +100,8 @@ namespace Squidex.Domain.Apps.Entities.Rules
sut.Update(CreateRuleCommand(command));
Assert.Same(newTrigger, sut.State.RuleDef.Trigger);
Assert.Same(newAction, sut.State.RuleDef.Action);
Assert.Same(newTrigger, sut.Snapshot.RuleDef.Trigger);
Assert.Same(newAction, sut.Snapshot.RuleDef.Action);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -139,7 +139,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
sut.Enable(CreateRuleCommand(command));
Assert.True(sut.State.RuleDef.IsEnabled);
Assert.True(sut.Snapshot.RuleDef.IsEnabled);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -177,7 +177,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
sut.Disable(CreateRuleCommand(command));
Assert.False(sut.State.RuleDef.IsEnabled);
Assert.False(sut.Snapshot.RuleDef.IsEnabled);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -213,7 +213,7 @@ namespace Squidex.Domain.Apps.Entities.Rules
sut.Delete(CreateRuleCommand(new DeleteRule()));
Assert.True(sut.State.IsDeleted);
Assert.True(sut.Snapshot.IsDeleted);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(

36
tests/Squidex.Domain.Apps.Entities.Tests/Schemas/SchemaDomainObjectTests.cs

@ -56,10 +56,10 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties }));
Assert.Equal(AppId, sut.State.AppId);
Assert.Equal(AppId, sut.Snapshot.AppId);
Assert.Equal(SchemaName, sut.State.Name);
Assert.Equal(SchemaName, sut.State.SchemaDef.Name);
Assert.Equal(SchemaName, sut.Snapshot.Name);
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -82,9 +82,9 @@ namespace Squidex.Domain.Apps.Entities.Schemas
var @event = (SchemaCreated)sut.GetUncomittedEvents().Single().Payload;
Assert.Equal(AppId, sut.State.AppId);
Assert.Equal(SchemaName, sut.State.Name);
Assert.Equal(SchemaName, sut.State.SchemaDef.Name);
Assert.Equal(AppId, sut.Snapshot.AppId);
Assert.Equal(SchemaName, sut.Snapshot.Name);
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name);
Assert.Equal(2, @event.Fields.Count);
}
@ -119,7 +119,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.Update(CreateCommand(new UpdateSchema { Properties = properties }));
Assert.Equal(properties, sut.State.SchemaDef.Properties);
Assert.Equal(properties, sut.Snapshot.SchemaDef.Properties);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -244,7 +244,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.Publish(CreateCommand(new PublishSchema()));
Assert.True(sut.State.SchemaDef.IsPublished);
Assert.True(sut.Snapshot.SchemaDef.IsPublished);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -281,7 +281,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.Unpublish(CreateCommand(new UnpublishSchema()));
Assert.False(sut.State.SchemaDef.IsPublished);
Assert.False(sut.Snapshot.SchemaDef.IsPublished);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -317,7 +317,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.Delete(CreateCommand(new DeleteSchema()));
Assert.True(sut.State.IsDeleted);
Assert.True(sut.Snapshot.IsDeleted);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -355,7 +355,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.Add(CreateCommand(new AddField { Name = fieldName, Properties = properties }));
Assert.Equal(properties, sut.State.SchemaDef.FieldsById[1].RawProperties);
Assert.Equal(properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -394,7 +394,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.UpdateField(CreateCommand(new UpdateField { FieldId = 1, Properties = properties }));
Assert.Equal(properties, sut.State.SchemaDef.FieldsById[1].RawProperties);
Assert.Equal(properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -431,7 +431,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.LockField(CreateCommand(new LockField { FieldId = 1 }));
Assert.False(sut.State.SchemaDef.FieldsById[1].IsDisabled);
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -468,7 +468,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.HideField(CreateCommand(new HideField { FieldId = 1 }));
Assert.True(sut.State.SchemaDef.FieldsById[1].IsHidden);
Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -506,7 +506,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.HideField(CreateCommand(new HideField { FieldId = 1 }));
sut.ShowField(CreateCommand(new ShowField { FieldId = 1 }));
Assert.False(sut.State.SchemaDef.FieldsById[1].IsHidden);
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden);
sut.GetUncomittedEvents().Skip(1)
.ShouldHaveSameEvents(
@ -543,7 +543,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.DisableField(CreateCommand(new DisableField { FieldId = 1 }));
Assert.True(sut.State.SchemaDef.FieldsById[1].IsDisabled);
Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled);
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(
@ -581,7 +581,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.DisableField(CreateCommand(new DisableField { FieldId = 1 }));
sut.EnableField(CreateCommand(new EnableField { FieldId = 1 }));
Assert.False(sut.State.SchemaDef.FieldsById[1].IsDisabled);
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled);
sut.GetUncomittedEvents().Skip(1)
.ShouldHaveSameEvents(
@ -618,7 +618,7 @@ namespace Squidex.Domain.Apps.Entities.Schemas
sut.DeleteField(CreateCommand(new DeleteField { FieldId = 1 }));
Assert.False(sut.State.SchemaDef.FieldsById.ContainsKey(1));
Assert.False(sut.Snapshot.SchemaDef.FieldsById.ContainsKey(1));
sut.GetUncomittedEvents()
.ShouldHaveSameEvents(

19
tests/Squidex.Infrastructure.Tests/Commands/AggregateHandlerTests.cs

@ -21,7 +21,6 @@ namespace Squidex.Infrastructure.Commands
{
public class AggregateHandlerTests
{
private readonly ISemanticLog log = A.Fake<ISemanticLog>();
private readonly IServiceProvider serviceProvider = A.Fake<IServiceProvider>();
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>();
private readonly IStateFactory stateFactory = A.Fake<IStateFactory>();
@ -40,7 +39,7 @@ namespace Squidex.Infrastructure.Commands
command = new MyCommand { AggregateId = domainObjectId, ExpectedVersion = EtagVersion.Any };
context = new CommandContext(command);
A.CallTo(() => store.WithSnapshots(domainObjectId, A<Func<MyDomainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshotsAndEventSourcing(domainObjectId, A<Func<MyDomainState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored))
.Returns(persistence);
A.CallTo(() => stateFactory.CreateAsync<MyDomainObject>(domainObjectId))
@ -49,7 +48,7 @@ namespace Squidex.Infrastructure.Commands
A.CallTo(() => stateFactory.GetSingleAsync<MyDomainObject>(domainObjectId))
.Returns(Task.FromResult(domainObject));
sut = new AggregateHandler(stateFactory, serviceProvider, log);
sut = new AggregateHandler(stateFactory, serviceProvider);
domainObject.ActivateAsync(domainObjectId, store).Wait();
}
@ -118,7 +117,7 @@ namespace Squidex.Infrastructure.Commands
passedDomainObject = x;
});
Assert.Equal(1, domainObject.State.Version);
Assert.Equal(1, domainObject.Snapshot.Version);
Assert.Equal(domainObject, passedDomainObject);
Assert.NotNull(context.Result<EntityCreatedResult<Guid>>());
@ -139,7 +138,7 @@ namespace Squidex.Infrastructure.Commands
passedDomainObject = x;
});
Assert.Equal(1, domainObject.State.Version);
Assert.Equal(1, domainObject.Snapshot.Version);
Assert.Equal(domainObject, passedDomainObject);
Assert.NotNull(context.Result<EntityCreatedResult<Guid>>());
@ -160,7 +159,7 @@ namespace Squidex.Infrastructure.Commands
passedDomainObject = x;
});
Assert.Equal(1, domainObject.State.Version);
Assert.Equal(1, domainObject.Snapshot.Version);
Assert.Equal(domainObject, passedDomainObject);
Assert.NotNull(context.Result<EntityCreatedResult<Guid>>());
@ -211,7 +210,7 @@ namespace Squidex.Infrastructure.Commands
passedDomainObject = x;
});
Assert.Equal(1, domainObject.State.Version);
Assert.Equal(1, domainObject.Snapshot.Version);
Assert.Equal(domainObject, passedDomainObject);
Assert.NotNull(context.Result<EntitySavedResult>());
@ -234,7 +233,7 @@ namespace Squidex.Infrastructure.Commands
passedDomainObject = x;
});
Assert.Equal(1, domainObject.State.Version);
Assert.Equal(1, domainObject.Snapshot.Version);
Assert.Equal(domainObject, passedDomainObject);
Assert.NotNull(context.Result<EntitySavedResult>());
@ -255,7 +254,7 @@ namespace Squidex.Infrastructure.Commands
passedDomainObject = x;
});
Assert.Equal(1, domainObject.State.Version);
Assert.Equal(1, domainObject.Snapshot.Version);
Assert.Equal(domainObject, passedDomainObject);
Assert.NotNull(context.Result<EntitySavedResult>());
@ -276,7 +275,7 @@ namespace Squidex.Infrastructure.Commands
passedDomainObject = x;
});
Assert.Equal(1, domainObject.State.Version);
Assert.Equal(1, domainObject.Snapshot.Version);
Assert.Equal(domainObject, passedDomainObject);
Assert.NotNull(context.Result<EntitySavedResult>());

14
tests/Squidex.Infrastructure.Tests/Commands/DomainObjectBaseTests.cs

@ -28,7 +28,7 @@ namespace Squidex.Infrastructure.Commands
public DomainObjectBaseTests()
{
A.CallTo(() => store.WithSnapshots<MyDomainState>(id, A<Func<MyDomainState, Task>>.Ignored))
A.CallTo(() => store.WithSnapshotsAndEventSourcing(id, A<Func<MyDomainState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored))
.Returns(persistence);
}
@ -66,9 +66,9 @@ namespace Squidex.Infrastructure.Commands
sut.RaiseEvent(event1);
sut.RaiseEvent(event2);
sut.UpdateState(newState);
sut.ApplySnapshot(newState);
await sut.WriteAsync(A.Fake<ISemanticLog>());
await sut.WriteAsync();
A.CallTo(() => persistence.WriteSnapshotAsync(newState))
.MustHaveHappened();
@ -79,7 +79,7 @@ namespace Squidex.Infrastructure.Commands
}
[Fact]
public async Task Should_ignore_exception_when_saving()
public async Task Should_not_ignore_exception_when_saving()
{
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored))
.Throws(new InvalidOperationException());
@ -92,12 +92,12 @@ namespace Squidex.Infrastructure.Commands
sut.RaiseEvent(event1);
sut.RaiseEvent(event2);
sut.UpdateState(newState);
sut.ApplySnapshot(newState);
await sut.WriteAsync(A.Fake<ISemanticLog>());
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.WriteAsync());
A.CallTo(() => persistence.WriteSnapshotAsync(newState))
.MustHaveHappened();
.MustNotHaveHappened();
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.That.Matches(x => x.Count() == 2)))
.MustHaveHappened();

11
tests/Squidex.Infrastructure.Tests/EventSourcing/EnvelopeExtensionsTests.cs

@ -83,16 +83,5 @@ namespace Squidex.Infrastructure.EventSourcing
Assert.Equal(eventStreamNumber, sut.Headers.EventStreamNumber());
Assert.Equal(eventStreamNumber, sut.Headers["EventStreamNumber"].ToInt64(culture));
}
[Fact]
public void Should_set_and_get_snapshot_version()
{
const int snapshotVersion = 123;
sut.SetSnapshotVersion(snapshotVersion);
Assert.Equal(snapshotVersion, sut.Headers.SnapshotVersion());
Assert.Equal(snapshotVersion, sut.Headers["SnapshotVersion"].ToInt64(culture));
}
}
}

111
tools/Migrate_01/Migration01_FromCqrs.cs

@ -6,16 +6,21 @@
// All rights reserved.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Schemas;
using Squidex.Domain.Apps.Entities.Apps;
using Squidex.Domain.Apps.Entities.Apps.State;
using Squidex.Domain.Apps.Entities.Assets;
using Squidex.Domain.Apps.Entities.Contents;
using Squidex.Domain.Apps.Entities.Contents.State;
using Squidex.Domain.Apps.Entities.Rules;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Assets;
using Squidex.Domain.Apps.Events.Contents;
using Squidex.Domain.Apps.Events.Rules;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Migrations;
@ -28,6 +33,7 @@ namespace Migrate_01
private readonly FieldRegistry fieldRegistry;
private readonly IEventStore eventStore;
private readonly IEventDataFormatter eventDataFormatter;
private readonly ISnapshotStore<ContentState, Guid> snapshotStore;
private readonly IStateFactory stateFactory;
public int FromVersion { get; } = 0;
@ -38,65 +44,114 @@ namespace Migrate_01
FieldRegistry fieldRegistry,
IEventDataFormatter eventDataFormatter,
IEventStore eventStore,
ISnapshotStore<ContentState, Guid> snapshotStore,
IStateFactory stateFactory)
{
this.fieldRegistry = fieldRegistry;
this.eventDataFormatter = eventDataFormatter;
this.eventStore = eventStore;
this.snapshotStore = snapshotStore;
this.stateFactory = stateFactory;
}
public async Task UpdateAsync()
{
await eventStore.GetEventsAsync(async storedEvent =>
// await MigrateConfigAsync();
await MigrateContentAsync();
// await MigrateAssetsAsync();
}
private Task MigrateAssetsAsync()
{
const string filter = "^asset\\-";
var handledIds = new HashSet<Guid>();
return eventStore.GetEventsAsync(async storedEvent =>
{
var @event = ParseKnownEvent(storedEvent);
if (@event != null)
{
var version = storedEvent.EventStreamNumber;
if (@event.Payload is ContentEvent contentEvent)
if (@event.Payload is AssetEvent assetEvent && handledIds.Add(assetEvent.AssetId))
{
try
{
var content = await stateFactory.CreateAsync<ContentDomainObject>(contentEvent.ContentId);
var asset = await stateFactory.CreateAsync<AssetDomainObject>(assetEvent.AssetId);
content.UpdateState(content.State.Apply(@event));
asset.ApplySnapshot(asset.Snapshot.Apply(@event));
await content.WriteStateAsync(version);
}
catch (DomainObjectNotFoundException)
{
// Schema has been deleted.
}
await asset.WriteSnapshotAsync();
}
else if (@event.Payload is AssetEvent assetEvent)
{
var asset = await stateFactory.CreateAsync<AssetDomainObject>(assetEvent.AssetId);
}
}, CancellationToken.None, filter);
}
asset.UpdateState(asset.State.Apply(@event));
private Task MigrateConfigAsync()
{
const string filter = "^((app\\-)|(schema\\-)|(rule\\-))";
await asset.WriteStateAsync(version);
}
else if (@event.Payload is SchemaEvent schemaEvent)
var handledIds = new HashSet<Guid>();
return eventStore.GetEventsAsync(async storedEvent =>
{
var @event = ParseKnownEvent(storedEvent);
if (@event != null)
{
if (@event.Payload is SchemaEvent schemaEvent && handledIds.Add(schemaEvent.SchemaId.Id))
{
var schema = await stateFactory.GetSingleAsync<SchemaDomainObject>(schemaEvent.SchemaId.Id);
schema.UpdateState(schema.State.Apply(@event, fieldRegistry));
await schema.WriteSnapshotAsync();
}
else if (@event.Payload is RuleEvent ruleEvent && handledIds.Add(ruleEvent.RuleId))
{
var rule = await stateFactory.GetSingleAsync<RuleDomainObject>(ruleEvent.RuleId);
await schema.WriteStateAsync(version);
await rule.WriteSnapshotAsync();
}
else if (@event.Payload is AppEvent appEvent)
else if (@event.Payload is AppEvent appEvent && handledIds.Add(appEvent.AppId.Id))
{
var app = await stateFactory.GetSingleAsync<AppDomainObject>(appEvent.AppId.Id);
app.UpdateState(app.State.Apply(@event));
await app.WriteSnapshotAsync();
}
}
}, CancellationToken.None, filter);
}
private Task MigrateContentAsync()
{
const string filter = "^((content\\-))";
var handledIds = new HashSet<Guid>();
return eventStore.GetEventsAsync(async storedEvent =>
{
var @event = ParseKnownEvent(storedEvent);
if (@event.Payload is ContentEvent contentEvent)
{
try
{
var (content, version) = await snapshotStore.ReadAsync(contentEvent.ContentId);
if (content == null)
{
version = EtagVersion.Empty;
await app.WriteStateAsync(version);
content = new ContentState();
}
content = content.Apply(@event);
await snapshotStore.WriteAsync(contentEvent.ContentId, content, version, version + 1);
}
catch (DomainObjectNotFoundException)
{
// Schema has been deleted.
}
}
}, CancellationToken.None);
}, CancellationToken.None, filter);
}
private Envelope<IEvent> ParseKnownEvent(StoredEvent storedEvent)

13
tools/Migrate_01/Migration02_AddPatterns.cs

@ -27,10 +27,7 @@ namespace Migrate_01
public int ToVersion { get; } = 2;
public Migration02_AddPatterns(
InitialPatterns initialPatterns,
IStateFactory stateFactory,
IAppRepository appRepository)
public Migration02_AddPatterns(InitialPatterns initialPatterns, IAppRepository appRepository, IStateFactory stateFactory)
{
this.initialPatterns = initialPatterns;
this.appRepository = appRepository;
@ -45,15 +42,15 @@ namespace Migrate_01
{
var app = await stateFactory.CreateAsync<AppDomainObject>(id);
if (app.State.Patterns.Count == 0)
if (app.Snapshot.Patterns.Count == 0)
{
foreach (var pattern in initialPatterns.Values)
{
var command =
new AddPattern
{
Actor = app.State.CreatedBy,
AppId = new NamedId<Guid>(app.State.Id, app.State.Name),
Actor = app.Snapshot.CreatedBy,
AppId = new NamedId<Guid>(app.Snapshot.Id, app.Snapshot.Name),
Name = pattern.Name,
PatternId = Guid.NewGuid(),
Pattern = pattern.Pattern,
@ -63,7 +60,7 @@ namespace Migrate_01
app.AddPattern(command);
}
await app.WriteStateAsync(app.Version + initialPatterns.Count);
await app.WriteAsync();
}
}
}

Loading…
Cancel
Save