mirror of https://github.com/Squidex/squidex.git
21 changed files with 1042 additions and 733 deletions
@ -0,0 +1,15 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Assets |
||||
|
{ |
||||
|
public interface IAssetGrain : IDomainObjectGrain |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Rules |
||||
|
{ |
||||
|
public interface IRuleGrain : IDomainObjectGrain |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -1,89 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Threading.Tasks; |
|
||||
using Squidex.Domain.Apps.Entities.Rules.Commands; |
|
||||
using Squidex.Domain.Apps.Entities.Rules.Guards; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.Commands; |
|
||||
using Squidex.Infrastructure.Dispatching; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Rules |
|
||||
{ |
|
||||
public class RuleCommandMiddleware : ICommandMiddleware |
|
||||
{ |
|
||||
private readonly IAggregateHandler handler; |
|
||||
private readonly IAppProvider appProvider; |
|
||||
|
|
||||
public RuleCommandMiddleware(IAggregateHandler handler, IAppProvider appProvider) |
|
||||
{ |
|
||||
Guard.NotNull(handler, nameof(handler)); |
|
||||
Guard.NotNull(appProvider, nameof(appProvider)); |
|
||||
|
|
||||
this.handler = handler; |
|
||||
|
|
||||
this.appProvider = appProvider; |
|
||||
} |
|
||||
|
|
||||
protected Task On(CreateRule command, CommandContext context) |
|
||||
{ |
|
||||
return handler.CreateSyncedAsync<RuleDomainObject>(context, async r => |
|
||||
{ |
|
||||
await GuardRule.CanCreate(command, appProvider); |
|
||||
|
|
||||
r.Create(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(UpdateRule command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<RuleDomainObject>(context, async r => |
|
||||
{ |
|
||||
await GuardRule.CanUpdate(command, r.Snapshot.AppId.Id, appProvider); |
|
||||
|
|
||||
r.Update(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(EnableRule command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r => |
|
||||
{ |
|
||||
GuardRule.CanEnable(command, r.Snapshot.RuleDef); |
|
||||
|
|
||||
r.Enable(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(DisableRule command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r => |
|
||||
{ |
|
||||
GuardRule.CanDisable(command, r.Snapshot.RuleDef); |
|
||||
|
|
||||
r.Disable(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(DeleteRule command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<RuleDomainObject>(context, r => |
|
||||
{ |
|
||||
GuardRule.CanDelete(command); |
|
||||
|
|
||||
r.Delete(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public async Task HandleAsync(CommandContext context, Func<Task> next) |
|
||||
{ |
|
||||
await this.DispatchActionAsync(context.Command, context); |
|
||||
await next(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,86 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using Squidex.Domain.Apps.Entities.Rules.Commands; |
|
||||
using Squidex.Domain.Apps.Entities.Rules.State; |
|
||||
using Squidex.Domain.Apps.Events; |
|
||||
using Squidex.Domain.Apps.Events.Rules; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.EventSourcing; |
|
||||
using Squidex.Infrastructure.Reflection; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Rules |
|
||||
{ |
|
||||
public sealed class RuleDomainObject : SquidexDomainObjectBase<RuleState> |
|
||||
{ |
|
||||
public void Create(CreateRule command) |
|
||||
{ |
|
||||
VerifyNotCreated(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new RuleCreated())); |
|
||||
} |
|
||||
|
|
||||
public void Update(UpdateRule command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new RuleUpdated())); |
|
||||
} |
|
||||
|
|
||||
public void Enable(EnableRule command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new RuleEnabled())); |
|
||||
} |
|
||||
|
|
||||
public void Disable(DisableRule command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new RuleDisabled())); |
|
||||
} |
|
||||
|
|
||||
public void Delete(DeleteRule command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new RuleDeleted())); |
|
||||
} |
|
||||
|
|
||||
private void RaiseEvent(AppEvent @event) |
|
||||
{ |
|
||||
if (@event.AppId == null) |
|
||||
{ |
|
||||
@event.AppId = Snapshot.AppId; |
|
||||
} |
|
||||
|
|
||||
RaiseEvent(Envelope.Create(@event)); |
|
||||
} |
|
||||
|
|
||||
private void VerifyNotCreated() |
|
||||
{ |
|
||||
if (Snapshot.RuleDef != null) |
|
||||
{ |
|
||||
throw new DomainException("Webhook has already been created."); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void VerifyCreatedAndNotDeleted() |
|
||||
{ |
|
||||
if (Snapshot.IsDeleted || Snapshot.RuleDef == null) |
|
||||
{ |
|
||||
throw new DomainException("Webhook has already been deleted or not created yet."); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public override void ApplyEvent(Envelope<IEvent> @event) |
|
||||
{ |
|
||||
ApplySnapshot(Snapshot.Apply(@event)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,135 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Entities.Rules.Commands; |
||||
|
using Squidex.Domain.Apps.Entities.Rules.Guards; |
||||
|
using Squidex.Domain.Apps.Entities.Rules.State; |
||||
|
using Squidex.Domain.Apps.Events; |
||||
|
using Squidex.Domain.Apps.Events.Rules; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Rules |
||||
|
{ |
||||
|
public sealed class RuleGrain : DomainObjectGrain<RuleState>, IRuleGrain |
||||
|
{ |
||||
|
private readonly IAppProvider appProvider; |
||||
|
|
||||
|
public RuleGrain(IStore<Guid> store, IAppProvider appProvider) |
||||
|
: base(store) |
||||
|
{ |
||||
|
Guard.NotNull(appProvider, nameof(appProvider)); |
||||
|
|
||||
|
this.appProvider = appProvider; |
||||
|
} |
||||
|
|
||||
|
public override Task<object> ExecuteAsync(IAggregateCommand command) |
||||
|
{ |
||||
|
switch (command) |
||||
|
{ |
||||
|
case CreateRule createRule: |
||||
|
return CreateAsync(createRule, c => |
||||
|
{ |
||||
|
GuardRule.CanCreate(c, appProvider); |
||||
|
|
||||
|
Create(c); |
||||
|
}); |
||||
|
case UpdateRule updateRule: |
||||
|
return UpdateAsync(updateRule, c => |
||||
|
{ |
||||
|
GuardRule.CanUpdate(c, Snapshot.AppId.Id, appProvider); |
||||
|
|
||||
|
Update(c); |
||||
|
}); |
||||
|
case EnableRule enableRule: |
||||
|
return UpdateAsync(enableRule, c => |
||||
|
{ |
||||
|
GuardRule.CanEnable(c, Snapshot.RuleDef); |
||||
|
|
||||
|
Enable(c); |
||||
|
}); |
||||
|
case DisableRule disableRule: |
||||
|
return UpdateAsync(disableRule, c => |
||||
|
{ |
||||
|
GuardRule.CanDisable(c, Snapshot.RuleDef); |
||||
|
|
||||
|
Disable(c); |
||||
|
}); |
||||
|
case DeleteRule deleteRule: |
||||
|
return UpdateAsync(deleteRule, c => |
||||
|
{ |
||||
|
GuardRule.CanDelete(deleteRule); |
||||
|
|
||||
|
Delete(c); |
||||
|
}); |
||||
|
default: |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Create(CreateRule command) |
||||
|
{ |
||||
|
RaiseEvent(SimpleMapper.Map(command, new RuleCreated())); |
||||
|
} |
||||
|
|
||||
|
public void Update(UpdateRule command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new RuleUpdated())); |
||||
|
} |
||||
|
|
||||
|
public void Enable(EnableRule command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new RuleEnabled())); |
||||
|
} |
||||
|
|
||||
|
public void Disable(DisableRule command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new RuleDisabled())); |
||||
|
} |
||||
|
|
||||
|
public void Delete(DeleteRule command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new RuleDeleted())); |
||||
|
} |
||||
|
|
||||
|
private void RaiseEvent(AppEvent @event) |
||||
|
{ |
||||
|
if (@event.AppId == null) |
||||
|
{ |
||||
|
@event.AppId = Snapshot.AppId; |
||||
|
} |
||||
|
|
||||
|
RaiseEvent(Envelope.Create(@event)); |
||||
|
} |
||||
|
|
||||
|
private void VerifyNotDeleted() |
||||
|
{ |
||||
|
if (Snapshot.IsDeleted || Snapshot.RuleDef == null) |
||||
|
{ |
||||
|
throw new DomainException("Webhook has already been deleted."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void ApplyEvent(Envelope<IEvent> @event) |
||||
|
{ |
||||
|
ApplySnapshot(Snapshot.Apply(@event)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Schemas |
||||
|
{ |
||||
|
public interface ISchemaGrain : IDomainObjectGrain |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -1,194 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Linq; |
|
||||
using System.Threading.Tasks; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas.Commands; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas.Guards; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.Commands; |
|
||||
using Squidex.Infrastructure.Dispatching; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Schemas |
|
||||
{ |
|
||||
public class SchemaCommandMiddleware : ICommandMiddleware |
|
||||
{ |
|
||||
private readonly IAppProvider appProvider; |
|
||||
private readonly IAggregateHandler handler; |
|
||||
|
|
||||
public SchemaCommandMiddleware(IAggregateHandler handler, IAppProvider appProvider) |
|
||||
{ |
|
||||
Guard.NotNull(handler, nameof(handler)); |
|
||||
Guard.NotNull(appProvider, nameof(appProvider)); |
|
||||
|
|
||||
this.handler = handler; |
|
||||
|
|
||||
this.appProvider = appProvider; |
|
||||
} |
|
||||
|
|
||||
protected Task On(CreateSchema command, CommandContext context) |
|
||||
{ |
|
||||
return handler.CreateSyncedAsync<SchemaDomainObject>(context, async s => |
|
||||
{ |
|
||||
await GuardSchema.CanCreate(command, appProvider); |
|
||||
|
|
||||
s.Create(command); |
|
||||
|
|
||||
context.Complete(EntityCreatedResult.Create(command.SchemaId, s.Version)); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(AddField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanAdd(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.Add(command); |
|
||||
|
|
||||
context.Complete(EntityCreatedResult.Create(s.Snapshot.SchemaDef.FieldsById.Values.First(x => x.Name == command.Name).Id, s.Version)); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(DeleteField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanDelete(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.DeleteField(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(LockField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanLock(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.LockField(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(HideField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanHide(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.HideField(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(ShowField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanShow(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.ShowField(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(DisableField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanDisable(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.DisableField(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(EnableField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanEnable(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.EnableField(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(UpdateField command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchemaField.CanUpdate(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.UpdateField(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(ReorderFields command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchema.CanReorder(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.Reorder(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(UpdateSchema command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchema.CanUpdate(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.Update(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(PublishSchema command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchema.CanPublish(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.Publish(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(UnpublishSchema command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchema.CanUnpublish(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.Unpublish(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(ConfigureScripts command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchema.CanConfigureScripts(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.ConfigureScripts(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
protected Task On(DeleteSchema command, CommandContext context) |
|
||||
{ |
|
||||
return handler.UpdateSyncedAsync<SchemaDomainObject>(context, s => |
|
||||
{ |
|
||||
GuardSchema.CanDelete(s.Snapshot.SchemaDef, command); |
|
||||
|
|
||||
s.Delete(command); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
public async Task HandleAsync(CommandContext context, Func<Task> next) |
|
||||
{ |
|
||||
await this.DispatchActionAsync(context.Command, context); |
|
||||
await next(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,229 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using System; |
|
||||
using System.Collections.Generic; |
|
||||
using Squidex.Domain.Apps.Core.Schemas; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas.Commands; |
|
||||
using Squidex.Domain.Apps.Entities.Schemas.State; |
|
||||
using Squidex.Domain.Apps.Events; |
|
||||
using Squidex.Domain.Apps.Events.Schemas; |
|
||||
using Squidex.Infrastructure; |
|
||||
using Squidex.Infrastructure.EventSourcing; |
|
||||
using Squidex.Infrastructure.Reflection; |
|
||||
|
|
||||
namespace Squidex.Domain.Apps.Entities.Schemas |
|
||||
{ |
|
||||
public sealed class SchemaDomainObject : SquidexDomainObjectBase<SchemaState> |
|
||||
{ |
|
||||
private readonly FieldRegistry registry; |
|
||||
|
|
||||
public SchemaDomainObject(FieldRegistry registry) |
|
||||
{ |
|
||||
Guard.NotNull(registry, nameof(registry)); |
|
||||
|
|
||||
this.registry = registry; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject Create(CreateSchema command) |
|
||||
{ |
|
||||
VerifyNotCreated(); |
|
||||
|
|
||||
var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId<Guid>(command.SchemaId, command.Name) }); |
|
||||
|
|
||||
if (command.Fields != null) |
|
||||
{ |
|
||||
@event.Fields = new List<SchemaCreatedField>(); |
|
||||
|
|
||||
foreach (var commandField in command.Fields) |
|
||||
{ |
|
||||
var eventField = SimpleMapper.Map(commandField, new SchemaCreatedField()); |
|
||||
|
|
||||
@event.Fields.Add(eventField); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
RaiseEvent(@event); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject Add(AddField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(Snapshot.TotalFields + 1, command.Name) })); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject UpdateField(UpdateField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject LockField(LockField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(command, new FieldLocked()); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject HideField(HideField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(command, new FieldHidden()); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject ShowField(ShowField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(command, new FieldShown()); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject DisableField(DisableField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(command, new FieldDisabled()); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject EnableField(EnableField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(command, new FieldEnabled()); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject DeleteField(DeleteField command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(command, new FieldDeleted()); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject Reorder(ReorderFields command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject Publish(PublishSchema command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject Unpublish(UnpublishSchema command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject ConfigureScripts(ConfigureScripts command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject Delete(DeleteSchema command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
public SchemaDomainObject Update(UpdateSchema command) |
|
||||
{ |
|
||||
VerifyCreatedAndNotDeleted(); |
|
||||
|
|
||||
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); |
|
||||
|
|
||||
return this; |
|
||||
} |
|
||||
|
|
||||
private void RaiseEvent(FieldCommand fieldCommand, FieldEvent @event) |
|
||||
{ |
|
||||
SimpleMapper.Map(fieldCommand, @event); |
|
||||
|
|
||||
if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.FieldId, out var field)) |
|
||||
{ |
|
||||
@event.FieldId = new NamedId<long>(field.Id, field.Name); |
|
||||
} |
|
||||
|
|
||||
RaiseEvent(@event); |
|
||||
} |
|
||||
|
|
||||
private void RaiseEvent(SchemaEvent @event) |
|
||||
{ |
|
||||
if (@event.SchemaId == null) |
|
||||
{ |
|
||||
@event.SchemaId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name); |
|
||||
} |
|
||||
|
|
||||
if (@event.AppId == null) |
|
||||
{ |
|
||||
@event.AppId = Snapshot.AppId; |
|
||||
} |
|
||||
|
|
||||
RaiseEvent(Envelope.Create(@event)); |
|
||||
} |
|
||||
|
|
||||
private void VerifyNotCreated() |
|
||||
{ |
|
||||
if (Snapshot.SchemaDef != null) |
|
||||
{ |
|
||||
throw new DomainException("Schema has already been created."); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
private void VerifyCreatedAndNotDeleted() |
|
||||
{ |
|
||||
if (Snapshot.IsDeleted || Snapshot.SchemaDef == null) |
|
||||
{ |
|
||||
throw new DomainException("Schema has already been deleted or not created yet."); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
public override void ApplyEvent(Envelope<IEvent> @event) |
|
||||
{ |
|
||||
ApplySnapshot(Snapshot.Apply(@event, registry)); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,330 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Squidex.Domain.Apps.Core.Schemas; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas.Commands; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas.Guards; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas.State; |
||||
|
using Squidex.Domain.Apps.Events; |
||||
|
using Squidex.Domain.Apps.Events.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Commands; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Reflection; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
|
||||
|
namespace Squidex.Domain.Apps.Entities.Schemas |
||||
|
{ |
||||
|
public sealed class SchemaGrain : DomainObjectGrain<SchemaState>, ISchemaGrain |
||||
|
{ |
||||
|
private readonly IAppProvider appProvider; |
||||
|
private readonly FieldRegistry registry; |
||||
|
|
||||
|
public SchemaGrain(IStore<Guid> store, IAppProvider appProvider, FieldRegistry registry) |
||||
|
: base(store) |
||||
|
{ |
||||
|
Guard.NotNull(appProvider, nameof(appProvider)); |
||||
|
Guard.NotNull(registry, nameof(registry)); |
||||
|
|
||||
|
this.appProvider = appProvider; |
||||
|
|
||||
|
this.registry = registry; |
||||
|
} |
||||
|
|
||||
|
public override Task<object> ExecuteAsync(IAggregateCommand command) |
||||
|
{ |
||||
|
switch (command) |
||||
|
{ |
||||
|
case CreateSchema createSchema: |
||||
|
return CreateAsync(createSchema, async c => |
||||
|
{ |
||||
|
await GuardSchema.CanCreate(c, appProvider); |
||||
|
|
||||
|
Create(c); |
||||
|
}); |
||||
|
|
||||
|
case AddField addField: |
||||
|
return UpdateReturnAsync(addField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanAdd(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
Add(c); |
||||
|
|
||||
|
return EntityCreatedResult.Create(Snapshot.SchemaDef.FieldsById.Values.First(x => x.Name == addField.Name).Id, Version); |
||||
|
}); |
||||
|
|
||||
|
case DeleteField deleteField: |
||||
|
return UpdateAsync(deleteField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanDelete(Snapshot.SchemaDef, deleteField); |
||||
|
|
||||
|
DeleteField(c); |
||||
|
}); |
||||
|
|
||||
|
case LockField lockField: |
||||
|
return UpdateAsync(lockField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanLock(Snapshot.SchemaDef, lockField); |
||||
|
|
||||
|
LockField(c); |
||||
|
}); |
||||
|
|
||||
|
case HideField hideField: |
||||
|
return UpdateAsync(hideField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanHide(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
HideField(c); |
||||
|
}); |
||||
|
|
||||
|
case ShowField showField: |
||||
|
return UpdateAsync(showField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanShow(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
ShowField(c); |
||||
|
}); |
||||
|
|
||||
|
case DisableField disableField: |
||||
|
return UpdateAsync(disableField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanDisable(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
DisableField(c); |
||||
|
}); |
||||
|
|
||||
|
case EnableField enableField: |
||||
|
return UpdateAsync(enableField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanEnable(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
EnableField(c); |
||||
|
}); |
||||
|
|
||||
|
case UpdateField updateField: |
||||
|
return UpdateAsync(updateField, c => |
||||
|
{ |
||||
|
GuardSchemaField.CanUpdate(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
UpdateField(c); |
||||
|
}); |
||||
|
|
||||
|
case ReorderFields reorderFields: |
||||
|
return UpdateAsync(reorderFields, c => |
||||
|
{ |
||||
|
GuardSchema.CanReorder(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
Reorder(c); |
||||
|
}); |
||||
|
|
||||
|
case UpdateSchema updateSchema: |
||||
|
return UpdateAsync(updateSchema, c => |
||||
|
{ |
||||
|
GuardSchema.CanUpdate(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
Update(c); |
||||
|
}); |
||||
|
|
||||
|
case PublishSchema publishSchema: |
||||
|
return UpdateAsync(publishSchema, c => |
||||
|
{ |
||||
|
GuardSchema.CanPublish(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
Publish(c); |
||||
|
}); |
||||
|
|
||||
|
case UnpublishSchema unpublishSchema: |
||||
|
return UpdateAsync(unpublishSchema, c => |
||||
|
{ |
||||
|
GuardSchema.CanUnpublish(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
Unpublish(c); |
||||
|
}); |
||||
|
|
||||
|
case ConfigureScripts configureScripts: |
||||
|
return UpdateAsync(configureScripts, c => |
||||
|
{ |
||||
|
GuardSchema.CanConfigureScripts(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
ConfigureScripts(c); |
||||
|
}); |
||||
|
|
||||
|
case DeleteSchema deleteSchema: |
||||
|
return UpdateAsync(deleteSchema, c => |
||||
|
{ |
||||
|
GuardSchema.CanDelete(Snapshot.SchemaDef, c); |
||||
|
|
||||
|
Delete(c); |
||||
|
}); |
||||
|
|
||||
|
default: |
||||
|
throw new NotSupportedException(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Create(CreateSchema command) |
||||
|
{ |
||||
|
var @event = SimpleMapper.Map(command, new SchemaCreated { SchemaId = new NamedId<Guid>(command.SchemaId, command.Name) }); |
||||
|
|
||||
|
if (command.Fields != null) |
||||
|
{ |
||||
|
@event.Fields = new List<SchemaCreatedField>(); |
||||
|
|
||||
|
foreach (var commandField in command.Fields) |
||||
|
{ |
||||
|
var eventField = SimpleMapper.Map(commandField, new SchemaCreatedField()); |
||||
|
|
||||
|
@event.Fields.Add(eventField); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
RaiseEvent(@event); |
||||
|
} |
||||
|
|
||||
|
public void Add(AddField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(Snapshot.TotalFields + 1, command.Name) })); |
||||
|
} |
||||
|
|
||||
|
public void UpdateField(UpdateField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); |
||||
|
} |
||||
|
|
||||
|
public void LockField(LockField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(command, new FieldLocked()); |
||||
|
} |
||||
|
|
||||
|
public void HideField(HideField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(command, new FieldHidden()); |
||||
|
} |
||||
|
|
||||
|
public void ShowField(ShowField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(command, new FieldShown()); |
||||
|
} |
||||
|
|
||||
|
public void DisableField(DisableField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(command, new FieldDisabled()); |
||||
|
} |
||||
|
|
||||
|
public void EnableField(EnableField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(command, new FieldEnabled()); |
||||
|
} |
||||
|
|
||||
|
public void DeleteField(DeleteField command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(command, new FieldDeleted()); |
||||
|
} |
||||
|
|
||||
|
public void Reorder(ReorderFields command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); |
||||
|
} |
||||
|
|
||||
|
public void Publish(PublishSchema command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); |
||||
|
} |
||||
|
|
||||
|
public void Unpublish(UnpublishSchema command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); |
||||
|
} |
||||
|
|
||||
|
public void ConfigureScripts(ConfigureScripts command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); |
||||
|
} |
||||
|
|
||||
|
public void Delete(DeleteSchema command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); |
||||
|
} |
||||
|
|
||||
|
public void Update(UpdateSchema command) |
||||
|
{ |
||||
|
VerifyNotDeleted(); |
||||
|
|
||||
|
RaiseEvent(SimpleMapper.Map(command, new SchemaUpdated())); |
||||
|
} |
||||
|
|
||||
|
private void RaiseEvent(FieldCommand fieldCommand, FieldEvent @event) |
||||
|
{ |
||||
|
SimpleMapper.Map(fieldCommand, @event); |
||||
|
|
||||
|
if (Snapshot.SchemaDef.FieldsById.TryGetValue(fieldCommand.FieldId, out var field)) |
||||
|
{ |
||||
|
@event.FieldId = new NamedId<long>(field.Id, field.Name); |
||||
|
} |
||||
|
|
||||
|
RaiseEvent(@event); |
||||
|
} |
||||
|
|
||||
|
private void RaiseEvent(SchemaEvent @event) |
||||
|
{ |
||||
|
if (@event.SchemaId == null) |
||||
|
{ |
||||
|
@event.SchemaId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name); |
||||
|
} |
||||
|
|
||||
|
if (@event.AppId == null) |
||||
|
{ |
||||
|
@event.AppId = Snapshot.AppId; |
||||
|
} |
||||
|
|
||||
|
RaiseEvent(Envelope.Create(@event)); |
||||
|
} |
||||
|
|
||||
|
private void VerifyNotDeleted() |
||||
|
{ |
||||
|
if (Snapshot.IsDeleted) |
||||
|
{ |
||||
|
throw new DomainException("Schema has already been deleted."); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public override void ApplyEvent(Envelope<IEvent> @event) |
||||
|
{ |
||||
|
ApplySnapshot(Snapshot.Apply(@event, registry)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,204 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Infrastructure.EventSourcing; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
using Squidex.Infrastructure.States; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.Commands |
||||
|
{ |
||||
|
public abstract class DomainObjectGrain<T> : Grain, IDomainObjectGrain where T : IDomainState, new() |
||||
|
{ |
||||
|
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>(); |
||||
|
private readonly IStore<Guid> store; |
||||
|
private T snapshot = new T { Version = EtagVersion.Empty }; |
||||
|
private IPersistence<T> persistence; |
||||
|
|
||||
|
public long Version |
||||
|
{ |
||||
|
get { return snapshot.Version; } |
||||
|
} |
||||
|
|
||||
|
public long NewVersion |
||||
|
{ |
||||
|
get { return snapshot.Version + uncomittedEvents.Count; } |
||||
|
} |
||||
|
|
||||
|
protected T Snapshot |
||||
|
{ |
||||
|
get { return snapshot; } |
||||
|
} |
||||
|
|
||||
|
protected DomainObjectGrain(IStore<Guid> store) |
||||
|
{ |
||||
|
Guard.NotNull(store, nameof(store)); |
||||
|
|
||||
|
this.store = store; |
||||
|
} |
||||
|
|
||||
|
public override Task OnActivateAsync() |
||||
|
{ |
||||
|
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(GetType(), this.GetPrimaryKey(), ApplySnapshot, ApplyEvent); |
||||
|
|
||||
|
return persistence.ReadAsync(); |
||||
|
} |
||||
|
|
||||
|
public void RaiseEvent(IEvent @event) |
||||
|
{ |
||||
|
RaiseEvent(Envelope.Create(@event)); |
||||
|
} |
||||
|
|
||||
|
public virtual void RaiseEvent(Envelope<IEvent> @event) |
||||
|
{ |
||||
|
Guard.NotNull(@event, nameof(@event)); |
||||
|
|
||||
|
@event.SetAggregateId(this.GetPrimaryKey()); |
||||
|
|
||||
|
ApplyEvent(@event); |
||||
|
|
||||
|
uncomittedEvents.Add(@event); |
||||
|
} |
||||
|
|
||||
|
public IReadOnlyList<Envelope<IEvent>> GetUncomittedEvents() |
||||
|
{ |
||||
|
return uncomittedEvents; |
||||
|
} |
||||
|
|
||||
|
public void ClearUncommittedEvents() |
||||
|
{ |
||||
|
uncomittedEvents.Clear(); |
||||
|
} |
||||
|
|
||||
|
public virtual void ApplySnapshot(T newSnapshot) |
||||
|
{ |
||||
|
snapshot = newSnapshot; |
||||
|
} |
||||
|
|
||||
|
public virtual void ApplyEvent(Envelope<IEvent> @event) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public Task WriteSnapshotAsync() |
||||
|
{ |
||||
|
snapshot.Version = persistence.Version; |
||||
|
|
||||
|
return persistence.WriteSnapshotAsync(snapshot); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, handler, false); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> CreateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, x => Task.FromResult(handler(x)), false); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> CreateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, x => handler(x).ContinueWith<object>(t => null), false); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> CreateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, x => { handler(x); return Task.FromResult<object>(null); }, false); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, handler, true); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> UpdateReturnAsync<TCommand>(TCommand command, Func<TCommand, object> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, x => Task.FromResult(handler(x)), true); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> UpdateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, x => handler(x).ContinueWith<object>(t => null), true); |
||||
|
} |
||||
|
|
||||
|
protected Task<object> UpdateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
return InvokeAsync(command, x => { handler(x); return Task.FromResult<object>(null); }, true); |
||||
|
} |
||||
|
|
||||
|
private async Task<object> InvokeAsync<TCommand>(TCommand command, Func<TCommand, Task<object>> handler, bool isUpdate) where TCommand : class, IAggregateCommand |
||||
|
{ |
||||
|
Guard.NotNull(command, nameof(command)); |
||||
|
|
||||
|
if (command.ExpectedVersion != EtagVersion.Any && command.ExpectedVersion != Version) |
||||
|
{ |
||||
|
throw new DomainObjectVersionException(this.GetPrimaryKey().ToString(), GetType(), Version, command.ExpectedVersion); |
||||
|
} |
||||
|
|
||||
|
if (isUpdate && Version < 0) |
||||
|
{ |
||||
|
DeactivateOnIdle(); |
||||
|
|
||||
|
throw new DomainObjectNotFoundException(this.GetPrimaryKey().ToString(), GetType()); |
||||
|
} |
||||
|
else if (!isUpdate && Version >= 0) |
||||
|
{ |
||||
|
throw new DomainException("Object has already been created."); |
||||
|
} |
||||
|
|
||||
|
var previousSnapshot = snapshot; |
||||
|
try |
||||
|
{ |
||||
|
var result = await handler(command); |
||||
|
|
||||
|
var events = uncomittedEvents.ToArray(); |
||||
|
|
||||
|
if (events.Length > 0) |
||||
|
{ |
||||
|
snapshot.Version = NewVersion; |
||||
|
|
||||
|
await persistence.WriteEventsAsync(events); |
||||
|
await persistence.WriteSnapshotAsync(snapshot); |
||||
|
} |
||||
|
|
||||
|
if (result == null) |
||||
|
{ |
||||
|
if (isUpdate) |
||||
|
{ |
||||
|
result = new EntitySavedResult(Version); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
result = EntityCreatedResult.Create(this.GetPrimaryKey(), Version); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
catch |
||||
|
{ |
||||
|
snapshot = previousSnapshot; |
||||
|
|
||||
|
throw; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
uncomittedEvents.Clear(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Task<J<object>> ExecuteAsync(J<IAggregateCommand> command) |
||||
|
{ |
||||
|
return ExecuteAsync(command.Value).ContinueWith(x => x.Result.AsJ()); |
||||
|
} |
||||
|
|
||||
|
public abstract Task<object> ExecuteAsync(IAggregateCommand command); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.Commands |
||||
|
{ |
||||
|
public class GrainCommandMiddleware<TCommand, TGrain> : ICommandMiddleware where TCommand : IAggregateCommand where TGrain : IDomainObjectGrain |
||||
|
{ |
||||
|
private readonly IGrainFactory grainFactory; |
||||
|
|
||||
|
public GrainCommandMiddleware(IGrainFactory grainFactory) |
||||
|
{ |
||||
|
Guard.NotNull(grainFactory, nameof(grainFactory)); |
||||
|
|
||||
|
this.grainFactory = grainFactory; |
||||
|
} |
||||
|
|
||||
|
public async virtual Task HandleAsync(CommandContext context, Func<Task> next) |
||||
|
{ |
||||
|
if (context.Command is TCommand typedCommand) |
||||
|
{ |
||||
|
var result = await ExecuteCommandAsync(typedCommand); |
||||
|
|
||||
|
context.Complete(result); |
||||
|
} |
||||
|
|
||||
|
await next(); |
||||
|
} |
||||
|
|
||||
|
protected async Task<object> ExecuteCommandAsync(TCommand typedCommand) |
||||
|
{ |
||||
|
var grain = grainFactory.GetGrain<TGrain>(typedCommand.AggregateId); |
||||
|
|
||||
|
var result = await grain.ExecuteAsync(typedCommand); |
||||
|
|
||||
|
return result.Value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,20 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using Orleans; |
||||
|
using Squidex.Infrastructure.Orleans; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.Commands |
||||
|
{ |
||||
|
public interface IDomainObjectGrain : IGrainWithGuidKey |
||||
|
{ |
||||
|
Task WriteSnapshotAsync(); |
||||
|
|
||||
|
Task<J<object>> ExecuteAsync(J<IAggregateCommand> command); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Newtonsoft.Json; |
||||
|
|
||||
|
#pragma warning disable SA1401 // Fields must be private
|
||||
|
|
||||
|
namespace Squidex.Infrastructure.Orleans |
||||
|
{ |
||||
|
public static class J |
||||
|
{ |
||||
|
public static JsonSerializer Serializer = new JsonSerializer(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
namespace Squidex.Infrastructure.Orleans |
||||
|
{ |
||||
|
public static class JExtensions |
||||
|
{ |
||||
|
public static J<T> AsJ<T>(this T value) |
||||
|
{ |
||||
|
return new J<T>(value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Threading.Tasks; |
||||
|
using Newtonsoft.Json; |
||||
|
using Orleans.CodeGeneration; |
||||
|
using Orleans.Serialization; |
||||
|
|
||||
|
namespace Squidex.Infrastructure.Orleans |
||||
|
{ |
||||
|
public struct J<T> |
||||
|
{ |
||||
|
private readonly T value; |
||||
|
|
||||
|
public T Value |
||||
|
{ |
||||
|
get { return value; } |
||||
|
} |
||||
|
|
||||
|
[JsonConstructor] |
||||
|
public J(T value) |
||||
|
{ |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
public static implicit operator T(J<T> value) |
||||
|
{ |
||||
|
return value.value; |
||||
|
} |
||||
|
|
||||
|
public static implicit operator J<T>(T d) |
||||
|
{ |
||||
|
return new J<T>(d); |
||||
|
} |
||||
|
|
||||
|
public static Task<J<T>> AsTask(T value) |
||||
|
{ |
||||
|
return Task.FromResult<J<T>>(value); |
||||
|
} |
||||
|
|
||||
|
[CopierMethod] |
||||
|
private static object Copy(object input, ICopyContext context) |
||||
|
{ |
||||
|
return input; |
||||
|
} |
||||
|
|
||||
|
[SerializerMethod] |
||||
|
private static void Serialize(object input, ISerializationContext context, Type expected) |
||||
|
{ |
||||
|
var stream = new MemoryStream(); |
||||
|
|
||||
|
using (var writer = new JsonTextWriter(new StreamWriter(stream))) |
||||
|
{ |
||||
|
J.Serializer.Serialize(writer, input); |
||||
|
|
||||
|
writer.Flush(); |
||||
|
} |
||||
|
|
||||
|
var outBytes = stream.ToArray(); |
||||
|
|
||||
|
context.StreamWriter.Write(outBytes.Length); |
||||
|
context.StreamWriter.Write(outBytes); |
||||
|
} |
||||
|
|
||||
|
[DeserializerMethod] |
||||
|
private static object Deserialize(Type expected, IDeserializationContext context) |
||||
|
{ |
||||
|
var outLength = context.StreamReader.ReadInt(); |
||||
|
var outBytes = context.StreamReader.ReadBytes(outLength); |
||||
|
|
||||
|
var stream = new MemoryStream(outBytes); |
||||
|
|
||||
|
using (var reader = new JsonTextReader(new StreamReader(stream))) |
||||
|
{ |
||||
|
return J.Serializer.Deserialize(reader, expected); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue