mirror of https://github.com/Squidex/squidex.git
77 changed files with 3851 additions and 5481 deletions
@ -1,200 +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.Apps.Commands; |
|||
using Squidex.Domain.Apps.Entities.Apps.Guards; |
|||
using Squidex.Domain.Apps.Entities.Apps.Services; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Dispatching; |
|||
using Squidex.Shared.Users; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public class AppCommandMiddleware : ICommandMiddleware |
|||
{ |
|||
private readonly IAggregateHandler handler; |
|||
private readonly IAppProvider appProvider; |
|||
private readonly IAppPlansProvider appPlansProvider; |
|||
private readonly IAppPlanBillingManager appPlansBillingManager; |
|||
private readonly IUserResolver userResolver; |
|||
|
|||
public AppCommandMiddleware( |
|||
IAggregateHandler handler, |
|||
IAppProvider appProvider, |
|||
IAppPlansProvider appPlansProvider, |
|||
IAppPlanBillingManager appPlansBillingManager, |
|||
IUserResolver userResolver) |
|||
{ |
|||
Guard.NotNull(handler, nameof(handler)); |
|||
Guard.NotNull(appProvider, nameof(appProvider)); |
|||
Guard.NotNull(userResolver, nameof(userResolver)); |
|||
Guard.NotNull(appPlansProvider, nameof(appPlansProvider)); |
|||
Guard.NotNull(appPlansBillingManager, nameof(appPlansBillingManager)); |
|||
|
|||
this.handler = handler; |
|||
this.userResolver = userResolver; |
|||
this.appProvider = appProvider; |
|||
this.appPlansProvider = appPlansProvider; |
|||
this.appPlansBillingManager = appPlansBillingManager; |
|||
} |
|||
|
|||
protected async Task On(CreateApp command, CommandContext context) |
|||
{ |
|||
var app = await handler.CreateSyncedAsync<AppDomainObject>(context, async a => |
|||
{ |
|||
await GuardApp.CanCreate(command, appProvider); |
|||
|
|||
a.Create(command); |
|||
|
|||
context.Complete(EntityCreatedResult.Create(command.AppId, a.Version)); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AssignContributor command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a => |
|||
{ |
|||
await GuardAppContributors.CanAssign(a.Snapshot.Contributors, command, userResolver, appPlansProvider.GetPlan(a.Snapshot.Plan?.PlanId)); |
|||
|
|||
a.AssignContributor(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(RemoveContributor command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppContributors.CanRemove(a.Snapshot.Contributors, command); |
|||
|
|||
a.RemoveContributor(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AttachClient command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppClients.CanAttach(a.Snapshot.Clients, command); |
|||
|
|||
a.AttachClient(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(UpdateClient command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppClients.CanUpdate(a.Snapshot.Clients, command); |
|||
|
|||
a.UpdateClient(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(RevokeClient command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppClients.CanRevoke(a.Snapshot.Clients, command); |
|||
|
|||
a.RevokeClient(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AddLanguage command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppLanguages.CanAdd(a.Snapshot.LanguagesConfig, command); |
|||
|
|||
a.AddLanguage(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(RemoveLanguage command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppLanguages.CanRemove(a.Snapshot.LanguagesConfig, command); |
|||
|
|||
a.RemoveLanguage(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(UpdateLanguage command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppLanguages.CanUpdate(a.Snapshot.LanguagesConfig, command); |
|||
|
|||
a.UpdateLanguage(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(AddPattern command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppPattern.CanAdd(a.Snapshot.Patterns, command); |
|||
|
|||
a.AddPattern(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(DeletePattern command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppPattern.CanDelete(a.Snapshot.Patterns, command); |
|||
|
|||
a.DeletePattern(command); |
|||
}); |
|||
} |
|||
|
|||
protected async Task On(UpdatePattern command, CommandContext context) |
|||
{ |
|||
await handler.UpdateSyncedAsync<AppDomainObject>(context, a => |
|||
{ |
|||
GuardAppPattern.CanUpdate(a.Snapshot.Patterns, command); |
|||
|
|||
a.UpdatePattern(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(ChangePlan command, CommandContext context) |
|||
{ |
|||
return handler.UpdateSyncedAsync<AppDomainObject>(context, async a => |
|||
{ |
|||
GuardApp.CanChangePlan(command, a.Snapshot.Plan, appPlansProvider); |
|||
|
|||
if (command.FromCallback) |
|||
{ |
|||
a.ChangePlan(command); |
|||
} |
|||
else |
|||
{ |
|||
var result = await appPlansBillingManager.ChangePlanAsync(command.Actor.Identifier, a.Snapshot.Id, a.Snapshot.Name, command.PlanId); |
|||
|
|||
if (result is PlanChangedResult) |
|||
{ |
|||
a.ChangePlan(command); |
|||
} |
|||
|
|||
context.Complete(result); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public async Task HandleAsync(CommandContext context, Func<Task> next) |
|||
{ |
|||
await this.DispatchActionAsync(context.Command, context); |
|||
await next(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,228 +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.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Domain.Apps.Entities.Apps.State; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public sealed class AppDomainObject : SquidexDomainObjectBase<AppState> |
|||
{ |
|||
private readonly InitialPatterns initialPatterns; |
|||
|
|||
public AppDomainObject(InitialPatterns initialPatterns) |
|||
{ |
|||
Guard.NotNull(initialPatterns, nameof(initialPatterns)); |
|||
|
|||
this.initialPatterns = initialPatterns; |
|||
} |
|||
|
|||
public AppDomainObject Create(CreateApp command) |
|||
{ |
|||
ThrowIfCreated(); |
|||
|
|||
var appId = new NamedId<Guid>(command.AppId, command.Name); |
|||
|
|||
var events = new List<AppEvent> |
|||
{ |
|||
CreateInitalEvent(command.Name), |
|||
CreateInitialOwner(command.Actor), |
|||
CreateInitialLanguage() |
|||
}; |
|||
|
|||
foreach (var pattern in initialPatterns) |
|||
{ |
|||
events.Add(CreateInitialPattern(pattern.Key, pattern.Value)); |
|||
} |
|||
|
|||
foreach (var @event in events) |
|||
{ |
|||
@event.Actor = command.Actor; |
|||
@event.AppId = appId; |
|||
|
|||
RaiseEvent(@event); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject UpdateLanguage(UpdateLanguage command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject UpdateClient(UpdateClient command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
if (!string.IsNullOrWhiteSpace(command.Name)) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed())); |
|||
} |
|||
|
|||
if (command.Permission.HasValue) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value })); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject AssignContributor(AssignContributor command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject RemoveContributor(RemoveContributor command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject AttachClient(AttachClient command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject RevokeClient(RevokeClient command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject AddLanguage(AddLanguage command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject RemoveLanguage(RemoveLanguage command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject ChangePlan(ChangePlan command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject AddPattern(AddPattern command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppPatternAdded())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject DeletePattern(DeletePattern command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppPatternDeleted())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public AppDomainObject UpdatePattern(UpdatePattern command) |
|||
{ |
|||
ThrowIfNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new AppPatternUpdated())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
private void RaiseEvent(AppEvent @event) |
|||
{ |
|||
if (@event.AppId == null) |
|||
{ |
|||
@event.AppId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name); |
|||
} |
|||
|
|||
RaiseEvent(Envelope.Create(@event)); |
|||
} |
|||
|
|||
private static AppCreated CreateInitalEvent(string name) |
|||
{ |
|||
return new AppCreated { Name = name }; |
|||
} |
|||
|
|||
private static AppPatternAdded CreateInitialPattern(Guid id, AppPattern pattern) |
|||
{ |
|||
return new AppPatternAdded { PatternId = id, Name = pattern.Name, Pattern = pattern.Pattern, Message = pattern.Message }; |
|||
} |
|||
|
|||
private static AppLanguageAdded CreateInitialLanguage() |
|||
{ |
|||
return new AppLanguageAdded { Language = Language.EN }; |
|||
} |
|||
|
|||
private static AppContributorAssigned CreateInitialOwner(RefToken actor) |
|||
{ |
|||
return new AppContributorAssigned { ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner }; |
|||
} |
|||
|
|||
private void ThrowIfNotCreated() |
|||
{ |
|||
if (string.IsNullOrWhiteSpace(Snapshot.Name)) |
|||
{ |
|||
throw new DomainException("App has not been created."); |
|||
} |
|||
} |
|||
|
|||
private void ThrowIfCreated() |
|||
{ |
|||
if (!string.IsNullOrWhiteSpace(Snapshot.Name)) |
|||
{ |
|||
throw new DomainException("App has already been created."); |
|||
} |
|||
} |
|||
|
|||
public override void ApplyEvent(Envelope<IEvent> @event) |
|||
{ |
|||
ApplySnapshot(Snapshot.Apply(@event)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,314 @@ |
|||
// ==========================================================================
|
|||
// 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 Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Domain.Apps.Entities.Apps.Guards; |
|||
using Squidex.Domain.Apps.Entities.Apps.Services; |
|||
using Squidex.Domain.Apps.Entities.Apps.State; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Reflection; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Shared.Users; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public class AppGrain : DomainObjectGrain<AppState> |
|||
{ |
|||
private readonly InitialPatterns initialPatterns; |
|||
private readonly IAppProvider appProvider; |
|||
private readonly IAppPlansProvider appPlansProvider; |
|||
private readonly IAppPlanBillingManager appPlansBillingManager; |
|||
private readonly IUserResolver userResolver; |
|||
|
|||
public AppGrain( |
|||
InitialPatterns initialPatterns, |
|||
IStore<Guid> store, |
|||
IAppProvider appProvider, |
|||
IAppPlansProvider appPlansProvider, |
|||
IAppPlanBillingManager appPlansBillingManager, |
|||
IUserResolver userResolver) |
|||
: base(store) |
|||
{ |
|||
Guard.NotNull(initialPatterns, nameof(initialPatterns)); |
|||
Guard.NotNull(appProvider, nameof(appProvider)); |
|||
Guard.NotNull(userResolver, nameof(userResolver)); |
|||
Guard.NotNull(appPlansProvider, nameof(appPlansProvider)); |
|||
Guard.NotNull(appPlansBillingManager, nameof(appPlansBillingManager)); |
|||
|
|||
this.userResolver = userResolver; |
|||
this.appProvider = appProvider; |
|||
this.appPlansProvider = appPlansProvider; |
|||
this.appPlansBillingManager = appPlansBillingManager; |
|||
this.initialPatterns = initialPatterns; |
|||
} |
|||
|
|||
public override Task<object> ExecuteAsync(IAggregateCommand command) |
|||
{ |
|||
switch (command) |
|||
{ |
|||
case CreateApp createApp: |
|||
return CreateAsync(createApp, async c => |
|||
{ |
|||
await GuardApp.CanCreate(c, appProvider); |
|||
|
|||
Create(c); |
|||
}); |
|||
|
|||
case AssignContributor assigneContributor: |
|||
return UpdateAsync(assigneContributor, async c => |
|||
{ |
|||
await GuardAppContributors.CanAssign(Snapshot.Contributors, c, userResolver, appPlansProvider.GetPlan(Snapshot.Plan?.PlanId)); |
|||
|
|||
AssignContributor(c); |
|||
}); |
|||
|
|||
case RemoveContributor removeContributor: |
|||
return UpdateAsync(removeContributor, c => |
|||
{ |
|||
GuardAppContributors.CanRemove(Snapshot.Contributors, c); |
|||
|
|||
RemoveContributor(c); |
|||
}); |
|||
|
|||
case AttachClient attachClient: |
|||
return UpdateAsync(attachClient, c => |
|||
{ |
|||
GuardAppClients.CanAttach(Snapshot.Clients, c); |
|||
|
|||
AttachClient(c); |
|||
}); |
|||
|
|||
case UpdateClient updateClient: |
|||
return UpdateAsync(updateClient, c => |
|||
{ |
|||
GuardAppClients.CanUpdate(Snapshot.Clients, c); |
|||
|
|||
UpdateClient(c); |
|||
}); |
|||
|
|||
case RevokeClient revokeClient: |
|||
return UpdateAsync(revokeClient, c => |
|||
{ |
|||
GuardAppClients.CanRevoke(Snapshot.Clients, c); |
|||
|
|||
RevokeClient(c); |
|||
}); |
|||
|
|||
case AddLanguage addLanguage: |
|||
return UpdateAsync(addLanguage, c => |
|||
{ |
|||
GuardAppLanguages.CanAdd(Snapshot.LanguagesConfig, c); |
|||
|
|||
AddLanguage(c); |
|||
}); |
|||
|
|||
case RemoveLanguage removeLanguage: |
|||
return UpdateAsync(removeLanguage, c => |
|||
{ |
|||
GuardAppLanguages.CanRemove(Snapshot.LanguagesConfig, c); |
|||
|
|||
RemoveLanguage(c); |
|||
}); |
|||
|
|||
case UpdateLanguage updateLanguage: |
|||
return UpdateAsync(updateLanguage, c => |
|||
{ |
|||
GuardAppLanguages.CanUpdate(Snapshot.LanguagesConfig, c); |
|||
|
|||
UpdateLanguage(c); |
|||
}); |
|||
|
|||
case AddPattern addPattern: |
|||
return UpdateAsync(addPattern, c => |
|||
{ |
|||
GuardAppPattern.CanAdd(Snapshot.Patterns, c); |
|||
|
|||
AddPattern(c); |
|||
}); |
|||
|
|||
case DeletePattern deletePattern: |
|||
return UpdateAsync(deletePattern, c => |
|||
{ |
|||
GuardAppPattern.CanDelete(Snapshot.Patterns, c); |
|||
|
|||
DeletePattern(c); |
|||
}); |
|||
|
|||
case UpdatePattern updatePattern: |
|||
return UpdateAsync(updatePattern, c => |
|||
{ |
|||
GuardAppPattern.CanUpdate(Snapshot.Patterns, c); |
|||
|
|||
UpdatePattern(c); |
|||
}); |
|||
|
|||
case ChangePlan changePlan: |
|||
return UpdateReturnAsync(changePlan, async c => |
|||
{ |
|||
GuardApp.CanChangePlan(c, Snapshot.Plan, appPlansProvider); |
|||
|
|||
if (c.FromCallback) |
|||
{ |
|||
ChangePlan(c); |
|||
|
|||
return null; |
|||
} |
|||
else |
|||
{ |
|||
var result = await appPlansBillingManager.ChangePlanAsync(c.Actor.Identifier, Snapshot.Id, Snapshot.Name, c.PlanId); |
|||
|
|||
if (result is PlanChangedResult) |
|||
{ |
|||
ChangePlan(c); |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
}); |
|||
|
|||
default: |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
|
|||
public void Create(CreateApp command) |
|||
{ |
|||
var appId = new NamedId<Guid>(command.AppId, command.Name); |
|||
|
|||
var events = new List<AppEvent> |
|||
{ |
|||
CreateInitalEvent(command.Name), |
|||
CreateInitialOwner(command.Actor), |
|||
CreateInitialLanguage() |
|||
}; |
|||
|
|||
foreach (var pattern in initialPatterns) |
|||
{ |
|||
events.Add(CreateInitialPattern(pattern.Key, pattern.Value)); |
|||
} |
|||
|
|||
foreach (var @event in events) |
|||
{ |
|||
@event.Actor = command.Actor; |
|||
@event.AppId = appId; |
|||
|
|||
RaiseEvent(@event); |
|||
} |
|||
} |
|||
|
|||
public void UpdateClient(UpdateClient command) |
|||
{ |
|||
if (!string.IsNullOrWhiteSpace(command.Name)) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientRenamed())); |
|||
} |
|||
|
|||
if (command.Permission.HasValue) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientUpdated { Permission = command.Permission.Value })); |
|||
} |
|||
} |
|||
|
|||
public void UpdateLanguage(UpdateLanguage command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppLanguageUpdated())); |
|||
} |
|||
|
|||
public void AssignContributor(AssignContributor command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppContributorAssigned())); |
|||
} |
|||
|
|||
public void RemoveContributor(RemoveContributor command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppContributorRemoved())); |
|||
} |
|||
|
|||
public void AttachClient(AttachClient command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientAttached())); |
|||
} |
|||
|
|||
public void RevokeClient(RevokeClient command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked())); |
|||
} |
|||
|
|||
public void AddLanguage(AddLanguage command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppLanguageAdded())); |
|||
} |
|||
|
|||
public void RemoveLanguage(RemoveLanguage command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppLanguageRemoved())); |
|||
} |
|||
|
|||
public void ChangePlan(ChangePlan command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppPlanChanged())); |
|||
} |
|||
|
|||
public void AddPattern(AddPattern command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppPatternAdded())); |
|||
} |
|||
|
|||
public void DeletePattern(DeletePattern command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppPatternDeleted())); |
|||
} |
|||
|
|||
public void UpdatePattern(UpdatePattern command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new AppPatternUpdated())); |
|||
} |
|||
|
|||
private void RaiseEvent(AppEvent @event) |
|||
{ |
|||
if (@event.AppId == null) |
|||
{ |
|||
@event.AppId = new NamedId<Guid>(Snapshot.Id, Snapshot.Name); |
|||
} |
|||
|
|||
RaiseEvent(Envelope.Create(@event)); |
|||
} |
|||
|
|||
private static AppCreated CreateInitalEvent(string name) |
|||
{ |
|||
return new AppCreated { Name = name }; |
|||
} |
|||
|
|||
private static AppPatternAdded CreateInitialPattern(Guid id, AppPattern pattern) |
|||
{ |
|||
return new AppPatternAdded { PatternId = id, Name = pattern.Name, Pattern = pattern.Pattern, Message = pattern.Message }; |
|||
} |
|||
|
|||
private static AppLanguageAdded CreateInitialLanguage() |
|||
{ |
|||
return new AppLanguageAdded { Language = Language.EN }; |
|||
} |
|||
|
|||
private static AppContributorAssigned CreateInitialOwner(RefToken actor) |
|||
{ |
|||
return new AppContributorAssigned { ContributorId = actor.Identifier, Permission = AppContributorPermission.Owner }; |
|||
} |
|||
|
|||
public override void ApplyEvent(Envelope<IEvent> @event) |
|||
{ |
|||
ApplySnapshot(Snapshot.Apply(@event)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,158 +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.Core.Scripting; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Domain.Apps.Entities.Contents.Guards; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.Dispatching; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class ContentCommandMiddleware : ICommandMiddleware |
|||
{ |
|||
private readonly IAggregateHandler handler; |
|||
private readonly IAppProvider appProvider; |
|||
private readonly IAssetRepository assetRepository; |
|||
private readonly IContentRepository contentRepository; |
|||
private readonly IScriptEngine scriptEngine; |
|||
|
|||
public ContentCommandMiddleware( |
|||
IAggregateHandler handler, |
|||
IAppProvider appProvider, |
|||
IAssetRepository assetRepository, |
|||
IScriptEngine scriptEngine, |
|||
IContentRepository contentRepository) |
|||
{ |
|||
Guard.NotNull(handler, nameof(handler)); |
|||
Guard.NotNull(appProvider, nameof(appProvider)); |
|||
Guard.NotNull(scriptEngine, nameof(scriptEngine)); |
|||
Guard.NotNull(assetRepository, nameof(assetRepository)); |
|||
Guard.NotNull(contentRepository, nameof(contentRepository)); |
|||
|
|||
this.handler = handler; |
|||
this.appProvider = appProvider; |
|||
this.scriptEngine = scriptEngine; |
|||
this.assetRepository = assetRepository; |
|||
this.contentRepository = contentRepository; |
|||
} |
|||
|
|||
protected async Task On(CreateContent command, CommandContext context) |
|||
{ |
|||
await handler.CreateAsync<ContentDomainObject>(context, async content => |
|||
{ |
|||
GuardContent.CanCreate(command); |
|||
|
|||
var operationContext = await CreateContext(command, content, () => "Failed to create content."); |
|||
|
|||
if (command.Publish) |
|||
{ |
|||
await operationContext.ExecuteScriptAsync(x => x.ScriptChange, "Published"); |
|||
} |
|||
|
|||
await operationContext.ExecuteScriptAndTransformAsync(x => x.ScriptCreate, "Create"); |
|||
await operationContext.EnrichAsync(); |
|||
await operationContext.ValidateAsync(false); |
|||
|
|||
content.Create(command); |
|||
|
|||
context.Complete(EntityCreatedResult.Create(command.Data, content.Version)); |
|||
}); |
|||
} |
|||
|
|||
protected async Task On(UpdateContent command, CommandContext context) |
|||
{ |
|||
await handler.UpdateAsync<ContentDomainObject>(context, async content => |
|||
{ |
|||
GuardContent.CanUpdate(command); |
|||
|
|||
var operationContext = await CreateContext(command, content, () => "Failed to update content."); |
|||
|
|||
await operationContext.ValidateAsync(true); |
|||
await operationContext.ExecuteScriptAndTransformAsync(x => x.ScriptUpdate, "Update"); |
|||
|
|||
content.Update(command); |
|||
|
|||
context.Complete(new ContentDataChangedResult(content.Snapshot.Data, content.Version)); |
|||
}); |
|||
} |
|||
|
|||
protected async Task On(PatchContent command, CommandContext context) |
|||
{ |
|||
await handler.UpdateAsync<ContentDomainObject>(context, async content => |
|||
{ |
|||
GuardContent.CanPatch(command); |
|||
|
|||
var operationContext = await CreateContext(command, content, () => "Failed to patch content."); |
|||
|
|||
await operationContext.ValidateAsync(true); |
|||
await operationContext.ExecuteScriptAndTransformAsync(x => x.ScriptUpdate, "Patch"); |
|||
|
|||
content.Patch(command); |
|||
|
|||
context.Complete(new ContentDataChangedResult(content.Snapshot.Data, content.Version)); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(ChangeContentStatus command, CommandContext context) |
|||
{ |
|||
return handler.UpdateAsync<ContentDomainObject>(context, async content => |
|||
{ |
|||
GuardContent.CanChangeContentStatus(content.Snapshot.Status, command); |
|||
|
|||
if (!command.DueTime.HasValue) |
|||
{ |
|||
var operationContext = await CreateContext(command, content, () => "Failed to patch content."); |
|||
|
|||
await operationContext.ExecuteScriptAsync(x => x.ScriptChange, command.Status); |
|||
} |
|||
|
|||
content.ChangeStatus(command); |
|||
}); |
|||
} |
|||
|
|||
protected Task On(DeleteContent command, CommandContext context) |
|||
{ |
|||
return handler.UpdateAsync<ContentDomainObject>(context, async content => |
|||
{ |
|||
GuardContent.CanDelete(command); |
|||
|
|||
var operationContext = await CreateContext(command, content, () => "Failed to delete content."); |
|||
|
|||
await operationContext.ExecuteScriptAsync(x => x.ScriptDelete, "Delete"); |
|||
|
|||
content.Delete(command); |
|||
}); |
|||
} |
|||
|
|||
public async Task HandleAsync(CommandContext context, Func<Task> next) |
|||
{ |
|||
await this.DispatchActionAsync(context.Command, context); |
|||
await next(); |
|||
} |
|||
|
|||
private async Task<ContentOperationContext> CreateContext(ContentCommand command, ContentDomainObject content, Func<string> message) |
|||
{ |
|||
var operationContext = |
|||
await ContentOperationContext.CreateAsync( |
|||
contentRepository, |
|||
content, |
|||
command, |
|||
appProvider, |
|||
assetRepository, |
|||
scriptEngine, |
|||
message); |
|||
|
|||
return operationContext; |
|||
} |
|||
} |
|||
} |
|||
@ -1,126 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Domain.Apps.Entities.Contents.State; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Reflection; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public sealed class ContentDomainObject : SquidexDomainObjectBase<ContentState> |
|||
{ |
|||
public ContentDomainObject Create(CreateContent command) |
|||
{ |
|||
VerifyNotCreated(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new ContentCreated())); |
|||
|
|||
if (command.Publish) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged { Status = Status.Published })); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public ContentDomainObject Delete(DeleteContent command) |
|||
{ |
|||
VerifyCreatedAndNotDeleted(); |
|||
|
|||
RaiseEvent(SimpleMapper.Map(command, new ContentDeleted())); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public ContentDomainObject ChangeStatus(ChangeContentStatus command) |
|||
{ |
|||
VerifyCreatedAndNotDeleted(); |
|||
|
|||
if (command.DueTime.HasValue) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentStatusScheduled { DueTime = command.DueTime.Value })); |
|||
} |
|||
else |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public ContentDomainObject Update(UpdateContent command) |
|||
{ |
|||
VerifyCreatedAndNotDeleted(); |
|||
|
|||
if (!command.Data.Equals(Snapshot.Data)) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentUpdated())); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
public ContentDomainObject Patch(PatchContent command) |
|||
{ |
|||
VerifyCreatedAndNotDeleted(); |
|||
|
|||
var newData = command.Data.MergeInto(Snapshot.Data); |
|||
|
|||
if (!newData.Equals(Snapshot.Data)) |
|||
{ |
|||
var @event = SimpleMapper.Map(command, new ContentUpdated()); |
|||
|
|||
@event.Data = newData; |
|||
|
|||
RaiseEvent(@event); |
|||
} |
|||
|
|||
return this; |
|||
} |
|||
|
|||
private void RaiseEvent(SchemaEvent @event) |
|||
{ |
|||
if (@event.AppId == null) |
|||
{ |
|||
@event.AppId = Snapshot.AppId; |
|||
} |
|||
|
|||
if (@event.SchemaId == null) |
|||
{ |
|||
@event.SchemaId = Snapshot.SchemaId; |
|||
} |
|||
|
|||
RaiseEvent(Envelope.Create(@event)); |
|||
} |
|||
|
|||
private void VerifyNotCreated() |
|||
{ |
|||
if (Snapshot.Data != null) |
|||
{ |
|||
throw new DomainException("Content has already been created."); |
|||
} |
|||
} |
|||
|
|||
private void VerifyCreatedAndNotDeleted() |
|||
{ |
|||
if (Snapshot.IsDeleted || Snapshot.Data == null) |
|||
{ |
|||
throw new DomainException("Content has already been deleted or not created yet."); |
|||
} |
|||
} |
|||
|
|||
public override void ApplyEvent(Envelope<IEvent> @event) |
|||
{ |
|||
ApplySnapshot(Snapshot.Apply(@event)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,232 @@ |
|||
// ==========================================================================
|
|||
// 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.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Scripting; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Domain.Apps.Entities.Contents.Guards; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.State; |
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.Reflection; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class ContentGrain : DomainObjectGrain<ContentState> |
|||
{ |
|||
private readonly IAppProvider appProvider; |
|||
private readonly IAssetRepository assetRepository; |
|||
private readonly IContentRepository contentRepository; |
|||
private readonly IScriptEngine scriptEngine; |
|||
|
|||
public ContentGrain( |
|||
IStore<Guid> store, |
|||
IAppProvider appProvider, |
|||
IAssetRepository assetRepository, |
|||
IScriptEngine scriptEngine, |
|||
IContentRepository contentRepository) |
|||
: base(store) |
|||
{ |
|||
Guard.NotNull(appProvider, nameof(appProvider)); |
|||
Guard.NotNull(scriptEngine, nameof(scriptEngine)); |
|||
Guard.NotNull(assetRepository, nameof(assetRepository)); |
|||
Guard.NotNull(contentRepository, nameof(contentRepository)); |
|||
|
|||
this.appProvider = appProvider; |
|||
this.scriptEngine = scriptEngine; |
|||
this.assetRepository = assetRepository; |
|||
this.contentRepository = contentRepository; |
|||
} |
|||
|
|||
public override Task<object> ExecuteAsync(IAggregateCommand command) |
|||
{ |
|||
VerifyNotDeleted(); |
|||
|
|||
switch (command) |
|||
{ |
|||
case CreateContent createContent: |
|||
return CreateReturnAsync(createContent, async c => |
|||
{ |
|||
GuardContent.CanCreate(c); |
|||
|
|||
var operationContext = await CreateContext(c, () => "Failed to create content."); |
|||
|
|||
if (c.Publish) |
|||
{ |
|||
await operationContext.ExecuteScriptAsync(x => x.ScriptChange, "Published"); |
|||
} |
|||
|
|||
await operationContext.ExecuteScriptAndTransformAsync(x => x.ScriptCreate, "Create"); |
|||
await operationContext.EnrichAsync(); |
|||
await operationContext.ValidateAsync(); |
|||
|
|||
Create(c); |
|||
|
|||
return EntityCreatedResult.Create(c.Data, NewVersion); |
|||
}); |
|||
|
|||
case UpdateContent updateContent: |
|||
return UpdateReturnAsync(updateContent, async c => |
|||
{ |
|||
GuardContent.CanUpdate(c); |
|||
|
|||
var operationContext = await CreateContext(c, () => "Failed to update content."); |
|||
|
|||
await operationContext.ValidateAsync(); |
|||
await operationContext.ExecuteScriptAndTransformAsync(x => x.ScriptUpdate, "Update"); |
|||
|
|||
Update(c); |
|||
|
|||
return new ContentDataChangedResult(Snapshot.Data, NewVersion); |
|||
}); |
|||
|
|||
case PatchContent patchContent: |
|||
return UpdateReturnAsync(patchContent, async c => |
|||
{ |
|||
GuardContent.CanPatch(c); |
|||
|
|||
var operationContext = await CreateContext(c, () => "Failed to patch content."); |
|||
|
|||
await operationContext.ValidatePartialAsync(); |
|||
await operationContext.ExecuteScriptAndTransformAsync(x => x.ScriptUpdate, "Patch"); |
|||
|
|||
Patch(c); |
|||
|
|||
return new ContentDataChangedResult(Snapshot.Data, NewVersion); |
|||
}); |
|||
|
|||
case ChangeContentStatus patchContent: |
|||
return UpdateAsync(patchContent, async c => |
|||
{ |
|||
GuardContent.CanChangeContentStatus(Snapshot.Status, c); |
|||
|
|||
if (!c.DueTime.HasValue) |
|||
{ |
|||
var operationContext = await CreateContext(c, () => "Failed to patch content."); |
|||
|
|||
await operationContext.ExecuteScriptAsync(x => x.ScriptChange, c.Status); |
|||
} |
|||
|
|||
ChangeStatus(c); |
|||
}); |
|||
|
|||
case DeleteContent deleteContent: |
|||
return UpdateAsync(deleteContent, async c => |
|||
{ |
|||
GuardContent.CanDelete(c); |
|||
|
|||
var operationContext = await CreateContext(c, () => "Failed to delete content."); |
|||
|
|||
await operationContext.ExecuteScriptAsync(x => x.ScriptDelete, "Delete"); |
|||
|
|||
Delete(c); |
|||
}); |
|||
|
|||
default: |
|||
throw new NotSupportedException(); |
|||
} |
|||
} |
|||
|
|||
public void Create(CreateContent command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentCreated())); |
|||
|
|||
if (command.Publish) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged { Status = Status.Published })); |
|||
} |
|||
} |
|||
|
|||
public void Update(UpdateContent command) |
|||
{ |
|||
if (!command.Data.Equals(Snapshot.Data)) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentUpdated())); |
|||
} |
|||
} |
|||
|
|||
public void ChangeStatus(ChangeContentStatus command) |
|||
{ |
|||
if (command.DueTime.HasValue) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentStatusScheduled { DueTime = command.DueTime.Value })); |
|||
} |
|||
else |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentStatusChanged())); |
|||
} |
|||
} |
|||
|
|||
public void Patch(PatchContent command) |
|||
{ |
|||
var newData = command.Data.MergeInto(Snapshot.Data); |
|||
|
|||
if (!newData.Equals(Snapshot.Data)) |
|||
{ |
|||
var @event = SimpleMapper.Map(command, new ContentUpdated()); |
|||
|
|||
@event.Data = newData; |
|||
|
|||
RaiseEvent(@event); |
|||
} |
|||
} |
|||
|
|||
public void Delete(DeleteContent command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ContentDeleted())); |
|||
} |
|||
|
|||
private void RaiseEvent(SchemaEvent @event) |
|||
{ |
|||
if (@event.AppId == null) |
|||
{ |
|||
@event.AppId = Snapshot.AppId; |
|||
} |
|||
|
|||
if (@event.SchemaId == null) |
|||
{ |
|||
@event.SchemaId = Snapshot.SchemaId; |
|||
} |
|||
|
|||
RaiseEvent(Envelope.Create(@event)); |
|||
} |
|||
|
|||
private void VerifyNotDeleted() |
|||
{ |
|||
if (Snapshot.IsDeleted) |
|||
{ |
|||
throw new DomainException("Content has already been deleted."); |
|||
} |
|||
} |
|||
|
|||
public override void ApplyEvent(Envelope<IEvent> @event) |
|||
{ |
|||
ApplySnapshot(Snapshot.Apply(@event)); |
|||
} |
|||
|
|||
private async Task<ContentOperationContext> CreateContext(ContentCommand command, Func<string> message) |
|||
{ |
|||
var operationContext = |
|||
await ContentOperationContext.CreateAsync(command, Snapshot, |
|||
contentRepository, |
|||
appProvider, |
|||
assetRepository, |
|||
scriptEngine, |
|||
message); |
|||
|
|||
return operationContext; |
|||
} |
|||
} |
|||
} |
|||
@ -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,129 @@ |
|||
// ==========================================================================
|
|||
// 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 class RuleGrain : DomainObjectGrain<RuleState> |
|||
{ |
|||
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) |
|||
{ |
|||
VerifyNotDeleted(); |
|||
|
|||
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) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new RuleUpdated())); |
|||
} |
|||
|
|||
public void Enable(EnableRule command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new RuleEnabled())); |
|||
} |
|||
|
|||
public void Disable(DisableRule command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new RuleDisabled())); |
|||
} |
|||
|
|||
public void Delete(DeleteRule command) |
|||
{ |
|||
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) |
|||
{ |
|||
throw new DomainException("Webhook has already been deleted."); |
|||
} |
|||
} |
|||
|
|||
public override void ApplyEvent(Envelope<IEvent> @event) |
|||
{ |
|||
ApplySnapshot(Snapshot.Apply(@event)); |
|||
} |
|||
} |
|||
} |
|||
@ -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,304 @@ |
|||
// ==========================================================================
|
|||
// 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 class SchemaGrain : DomainObjectGrain<SchemaState> |
|||
{ |
|||
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) |
|||
{ |
|||
VerifyNotDeleted(); |
|||
|
|||
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, NewVersion); |
|||
}); |
|||
|
|||
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) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new FieldAdded { FieldId = new NamedId<long>(Snapshot.TotalFields + 1, command.Name) })); |
|||
} |
|||
|
|||
public void UpdateField(UpdateField command) |
|||
{ |
|||
RaiseEvent(command, SimpleMapper.Map(command, new FieldUpdated())); |
|||
} |
|||
|
|||
public void LockField(LockField command) |
|||
{ |
|||
RaiseEvent(command, new FieldLocked()); |
|||
} |
|||
|
|||
public void HideField(HideField command) |
|||
{ |
|||
RaiseEvent(command, new FieldHidden()); |
|||
} |
|||
|
|||
public void ShowField(ShowField command) |
|||
{ |
|||
RaiseEvent(command, new FieldShown()); |
|||
} |
|||
|
|||
public void DisableField(DisableField command) |
|||
{ |
|||
RaiseEvent(command, new FieldDisabled()); |
|||
} |
|||
|
|||
public void EnableField(EnableField command) |
|||
{ |
|||
RaiseEvent(command, new FieldEnabled()); |
|||
} |
|||
|
|||
public void DeleteField(DeleteField command) |
|||
{ |
|||
RaiseEvent(command, new FieldDeleted()); |
|||
} |
|||
|
|||
public void Reorder(ReorderFields command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new SchemaFieldsReordered())); |
|||
} |
|||
|
|||
public void Publish(PublishSchema command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new SchemaPublished())); |
|||
} |
|||
|
|||
public void Unpublish(UnpublishSchema command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new SchemaUnpublished())); |
|||
} |
|||
|
|||
public void ConfigureScripts(ConfigureScripts command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new ScriptsConfigured())); |
|||
} |
|||
|
|||
public void Delete(DeleteSchema command) |
|||
{ |
|||
RaiseEvent(SimpleMapper.Map(command, new SchemaDeleted())); |
|||
} |
|||
|
|||
public void Update(UpdateSchema command) |
|||
{ |
|||
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)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Domain.Apps.Events; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities |
|||
{ |
|||
public abstract class SquidexDomainObjectBase<T> : DomainObjectBase<T> where T : IDomainState, new() |
|||
{ |
|||
public override void RaiseEvent(Envelope<IEvent> @event) |
|||
{ |
|||
if (@event.Payload is AppEvent appEvent) |
|||
{ |
|||
@event.SetAppId(appEvent.AppId.Id); |
|||
} |
|||
|
|||
base.RaiseEvent(@event); |
|||
} |
|||
} |
|||
} |
|||
@ -1,149 +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.Infrastructure.States; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public sealed class AggregateHandler : IAggregateHandler |
|||
{ |
|||
private readonly AsyncLockPool lockPool = new AsyncLockPool(10000); |
|||
private readonly IStateFactory stateFactory; |
|||
private readonly IServiceProvider serviceProvider; |
|||
|
|||
public AggregateHandler(IStateFactory stateFactory, IServiceProvider serviceProvider) |
|||
{ |
|||
Guard.NotNull(stateFactory, nameof(stateFactory)); |
|||
Guard.NotNull(serviceProvider, nameof(serviceProvider)); |
|||
|
|||
this.stateFactory = stateFactory; |
|||
this.serviceProvider = serviceProvider; |
|||
} |
|||
|
|||
public Task<T> CreateAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IDomainObject |
|||
{ |
|||
Guard.NotNull(creator, nameof(creator)); |
|||
|
|||
return InvokeAsync(context, creator, false); |
|||
} |
|||
|
|||
public Task<T> UpdateAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IDomainObject |
|||
{ |
|||
Guard.NotNull(updater, nameof(updater)); |
|||
|
|||
return InvokeAsync(context, updater, true); |
|||
} |
|||
|
|||
public Task<T> CreateSyncedAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IDomainObject |
|||
{ |
|||
Guard.NotNull(creator, nameof(creator)); |
|||
|
|||
return InvokeSyncedAsync(context, creator, false); |
|||
} |
|||
|
|||
public Task<T> UpdateSyncedAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IDomainObject |
|||
{ |
|||
Guard.NotNull(updater, nameof(updater)); |
|||
|
|||
return InvokeSyncedAsync(context, updater, true); |
|||
} |
|||
|
|||
private async Task<T> InvokeAsync<T>(CommandContext context, Func<T, Task> handler, bool isUpdate) where T : class, IDomainObject |
|||
{ |
|||
Guard.NotNull(context, nameof(context)); |
|||
|
|||
var domainCommand = GetCommand(context); |
|||
var domainObjectId = domainCommand.AggregateId; |
|||
var domainObject = await stateFactory.CreateAsync<T>(domainObjectId); |
|||
|
|||
if (domainCommand.ExpectedVersion != EtagVersion.Any && domainCommand.ExpectedVersion != domainObject.Version) |
|||
{ |
|||
throw new DomainObjectVersionException(domainObjectId.ToString(), typeof(T), domainObject.Version, domainCommand.ExpectedVersion); |
|||
} |
|||
|
|||
await handler(domainObject); |
|||
|
|||
await domainObject.WriteAsync(); |
|||
|
|||
if (!context.IsCompleted) |
|||
{ |
|||
if (isUpdate) |
|||
{ |
|||
context.Complete(new EntitySavedResult(domainObject.Version)); |
|||
} |
|||
else |
|||
{ |
|||
context.Complete(EntityCreatedResult.Create(domainObjectId, domainObject.Version)); |
|||
} |
|||
} |
|||
|
|||
return domainObject; |
|||
} |
|||
|
|||
private async Task<T> InvokeSyncedAsync<T>(CommandContext context, Func<T, Task> handler, bool isUpdate) where T : class, IDomainObject |
|||
{ |
|||
Guard.NotNull(context, nameof(context)); |
|||
|
|||
var domainCommand = GetCommand(context); |
|||
var domainObjectId = domainCommand.AggregateId; |
|||
|
|||
using (await lockPool.LockAsync(Tuple.Create(typeof(T), domainObjectId))) |
|||
{ |
|||
var domainObject = await stateFactory.GetSingleAsync<T>(domainObjectId); |
|||
|
|||
if (domainCommand.ExpectedVersion != EtagVersion.Any && domainCommand.ExpectedVersion != domainObject.Version) |
|||
{ |
|||
throw new DomainObjectVersionException(domainObjectId.ToString(), typeof(T), domainObject.Version, domainCommand.ExpectedVersion); |
|||
} |
|||
|
|||
await handler(domainObject); |
|||
|
|||
try |
|||
{ |
|||
await domainObject.WriteAsync(); |
|||
|
|||
stateFactory.Synchronize<T, Guid>(domainObjectId); |
|||
} |
|||
catch |
|||
{ |
|||
stateFactory.Remove<T, Guid>(domainObjectId); |
|||
|
|||
throw; |
|||
} |
|||
|
|||
if (!context.IsCompleted) |
|||
{ |
|||
if (isUpdate) |
|||
{ |
|||
context.Complete(new EntitySavedResult(domainObject.Version)); |
|||
} |
|||
else |
|||
{ |
|||
context.Complete(EntityCreatedResult.Create(domainObjectId, domainObject.Version)); |
|||
} |
|||
} |
|||
|
|||
return domainObject; |
|||
} |
|||
} |
|||
|
|||
private static IAggregateCommand GetCommand(CommandContext context) |
|||
{ |
|||
if (!(context.Command is IAggregateCommand command)) |
|||
{ |
|||
throw new ArgumentException("Context must have an aggregate command.", nameof(context)); |
|||
} |
|||
|
|||
Guard.NotEmpty(command.AggregateId, "context.Command.AggregateId"); |
|||
|
|||
return command; |
|||
} |
|||
} |
|||
} |
|||
@ -1,106 +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 System.Threading.Tasks; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public abstract class DomainObjectBase<T> : IDomainObject where T : IDomainState, new() |
|||
{ |
|||
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>(); |
|||
private Guid id; |
|||
private T snapshot = new T { Version = EtagVersion.Empty }; |
|||
private IPersistence<T> persistence; |
|||
|
|||
public long Version |
|||
{ |
|||
get { return snapshot.Version; } |
|||
} |
|||
|
|||
public T Snapshot |
|||
{ |
|||
get { return snapshot; } |
|||
} |
|||
|
|||
public Task ActivateAsync(Guid key, IStore<Guid> store) |
|||
{ |
|||
id = key; |
|||
|
|||
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(key, 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(id); |
|||
|
|||
ApplyEvent(@event); |
|||
|
|||
snapshot.Version++; |
|||
|
|||
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); |
|||
} |
|||
|
|||
public async Task WriteAsync() |
|||
{ |
|||
var events = uncomittedEvents.ToArray(); |
|||
|
|||
if (events.Length > 0) |
|||
{ |
|||
try |
|||
{ |
|||
snapshot.Version = persistence.Version + events.Length; |
|||
|
|||
await persistence.WriteEventsAsync(events); |
|||
await persistence.WriteSnapshotAsync(snapshot); |
|||
} |
|||
finally |
|||
{ |
|||
uncomittedEvents.Clear(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -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 Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public abstract class DomainObjectGrain<T> : IDomainObjectGrain where T : IDomainState, new() |
|||
{ |
|||
private readonly List<Envelope<IEvent>> uncomittedEvents = new List<Envelope<IEvent>>(); |
|||
private readonly IStore<Guid> store; |
|||
private Guid id; |
|||
private T snapshot = new T { Version = EtagVersion.Empty }; |
|||
private IPersistence<T> persistence; |
|||
|
|||
public Guid Id |
|||
{ |
|||
get { return id; } |
|||
} |
|||
|
|||
public long Version |
|||
{ |
|||
get { return snapshot.Version; } |
|||
} |
|||
|
|||
public long NewVersion |
|||
{ |
|||
get { return snapshot.Version + uncomittedEvents.Count; } |
|||
} |
|||
|
|||
public T Snapshot |
|||
{ |
|||
get { return snapshot; } |
|||
} |
|||
|
|||
protected DomainObjectGrain(IStore<Guid> store) |
|||
{ |
|||
Guard.NotNull(store, nameof(store)); |
|||
|
|||
this.store = store; |
|||
} |
|||
|
|||
public Task ActivateAsync(Guid key) |
|||
{ |
|||
id = key; |
|||
|
|||
persistence = store.WithSnapshotsAndEventSourcing<T, Guid>(GetType(), key, 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(Id); |
|||
|
|||
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, handler?.ToAsync(), false); |
|||
} |
|||
|
|||
protected Task<object> CreateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand |
|||
{ |
|||
return InvokeAsync(command, handler.ToDefault<TCommand, object>(), false); |
|||
} |
|||
|
|||
protected Task<object> CreateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand |
|||
{ |
|||
return InvokeAsync(command, handler?.ToDefault<TCommand, object>()?.ToAsync(), 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, handler?.ToAsync(), true); |
|||
} |
|||
|
|||
protected Task<object> UpdateAsync<TCommand>(TCommand command, Func<TCommand, Task> handler) where TCommand : class, IAggregateCommand |
|||
{ |
|||
return InvokeAsync(command, handler?.ToDefault<TCommand, object>(), true); |
|||
} |
|||
|
|||
protected Task<object> UpdateAsync<TCommand>(TCommand command, Action<TCommand> handler) where TCommand : class, IAggregateCommand |
|||
{ |
|||
return InvokeAsync(command, handler?.ToDefault<TCommand, object>()?.ToAsync(), 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(Id.ToString(), GetType(), Version, command.ExpectedVersion); |
|||
} |
|||
|
|||
if (isUpdate && Version < 0) |
|||
{ |
|||
throw new DomainObjectNotFoundException(Id.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(Id, Version); |
|||
} |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
catch |
|||
{ |
|||
snapshot = previousSnapshot; |
|||
|
|||
throw; |
|||
} |
|||
finally |
|||
{ |
|||
ClearUncommittedEvents(); |
|||
} |
|||
} |
|||
|
|||
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 Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public class GrainCommandMiddleware<TCommand, TGrain> : ICommandMiddleware where TCommand : IAggregateCommand where TGrain : IDomainObjectGrain |
|||
{ |
|||
private readonly IStateFactory stateFactory; |
|||
|
|||
public GrainCommandMiddleware(IStateFactory stateFactory) |
|||
{ |
|||
Guard.NotNull(stateFactory, nameof(stateFactory)); |
|||
|
|||
this.stateFactory = stateFactory; |
|||
} |
|||
|
|||
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 = await stateFactory.CreateAsync<TGrain>(typedCommand.AggregateId); |
|||
|
|||
var result = await grain.ExecuteAsync(typedCommand); |
|||
|
|||
return result; |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +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; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public interface IAggregateHandler |
|||
{ |
|||
Task<T> CreateAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IDomainObject; |
|||
|
|||
Task<T> CreateSyncedAsync<T>(CommandContext context, Func<T, Task> creator) where T : class, IDomainObject; |
|||
|
|||
Task<T> UpdateAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IDomainObject; |
|||
|
|||
Task<T> UpdateSyncedAsync<T>(CommandContext context, Func<T, Task> updater) where T : class, IDomainObject; |
|||
} |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Infrastructure.Tasks; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public class SyncedGrainCommandMiddleware<TCommand, TGrain> : ICommandMiddleware where TCommand : IAggregateCommand where TGrain : IDomainObjectGrain |
|||
{ |
|||
private readonly AsyncLockPool lockPool = new AsyncLockPool(10000); |
|||
private readonly IStateFactory stateFactory; |
|||
|
|||
public SyncedGrainCommandMiddleware(IStateFactory stateFactory) |
|||
{ |
|||
Guard.NotNull(stateFactory, nameof(stateFactory)); |
|||
|
|||
this.stateFactory = stateFactory; |
|||
} |
|||
|
|||
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 id = typedCommand.AggregateId; |
|||
|
|||
using (await lockPool.LockAsync(typedCommand.AggregateId)) |
|||
{ |
|||
try |
|||
{ |
|||
var grain = await stateFactory.GetSingleAsync<TGrain>(id); |
|||
|
|||
var result = await grain.ExecuteAsync(typedCommand); |
|||
|
|||
stateFactory.Synchronize<TGrain, Guid>(id); |
|||
|
|||
return result; |
|||
} |
|||
catch |
|||
{ |
|||
stateFactory.Remove<TGrain, Guid>(id); |
|||
throw; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,291 +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 FakeItEasy; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Domain.Apps.Entities.Apps.Services; |
|||
using Squidex.Domain.Apps.Entities.Apps.Services.Implementations; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Shared.Users; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public class AppCommandMiddlewareTests : HandlerTestBase<AppDomainObject> |
|||
{ |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>(); |
|||
private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>(); |
|||
private readonly IUserResolver userResolver = A.Fake<IUserResolver>(); |
|||
private readonly Language language = Language.DE; |
|||
private readonly string contributorId = Guid.NewGuid().ToString(); |
|||
private readonly string clientName = "client"; |
|||
private readonly Guid patternId = Guid.NewGuid(); |
|||
private readonly AppDomainObject app = new AppDomainObject(new InitialPatterns()); |
|||
private readonly AppCommandMiddleware sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return AppId; } |
|||
} |
|||
|
|||
public AppCommandMiddlewareTests() |
|||
{ |
|||
A.CallTo(() => appProvider.GetAppAsync(AppName)) |
|||
.Returns((IAppEntity)null); |
|||
|
|||
A.CallTo(() => userResolver.FindByIdAsync(contributorId)) |
|||
.Returns(A.Fake<IUser>()); |
|||
|
|||
sut = new AppCommandMiddleware(Handler, appProvider, appPlansProvider, appPlansBillingManager, userResolver); |
|||
|
|||
app.ActivateAsync(Id, A.Fake<IStore<Guid>>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_domain_object() |
|||
{ |
|||
var context = CreateContextForCommand(new CreateApp { Name = AppName, AppId = AppId }); |
|||
|
|||
await TestCreate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(AppId, context.Result<EntityCreatedResult<Guid>>().IdOrValue); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AssignContributor_should_update_domain_object_if_user_found() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.GetPlan(null)) |
|||
.Returns(new ConfigAppLimitsPlan { MaxContributors = -1 }); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new AssignContributor { ContributorId = contributorId }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveContributor_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId })); |
|||
|
|||
var context = CreateContextForCommand(new RemoveContributor { ContributorId = contributorId }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AttachClient_should_update_domain_object() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new AttachClient { Id = clientName }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RenameClient_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AttachClient(CreateCommand(new AttachClient { Id = clientName })); |
|||
|
|||
var context = CreateContextForCommand(new UpdateClient { Id = clientName, Name = "New Name" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RevokeClient_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AttachClient(CreateCommand(new AttachClient { Id = clientName })); |
|||
|
|||
var context = CreateContextForCommand(new RevokeClient { Id = clientName }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_update_domain_object() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) |
|||
.Returns(true); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, "my-plan")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_not_make_update_for_redirect_result() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) |
|||
.Returns(true); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, "my-plan")) |
|||
.Returns(CreateRedirectResult()); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Null(app.Snapshot.Plan); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_not_call_billing_manager_for_callback() |
|||
{ |
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan("my-plan")) |
|||
.Returns(true); |
|||
|
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new ChangePlan { PlanId = "my-plan", FromCallback = true }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, "my-plan")) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddLanguage_should_update_domain_object() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new AddLanguage { Language = language }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveLanguage_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AddLanguage(CreateCommand(new AddLanguage { Language = language })); |
|||
|
|||
var context = CreateContextForCommand(new RemoveLanguage { Language = language }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateLanguage_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AddLanguage(CreateCommand(new AddLanguage { Language = language })); |
|||
|
|||
var context = CreateContextForCommand(new UpdateLanguage { Language = language }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddPattern_should_update_domain_object() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
var context = CreateContextForCommand(new AddPattern { Name = "Any", Pattern = ".*" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdatePattern_should_update_domain() |
|||
{ |
|||
CreateApp() |
|||
.AddPattern(CreateCommand(new AddPattern { PatternId = patternId, Name = "Any", Pattern = "." })); |
|||
|
|||
var context = CreateContextForCommand(new UpdatePattern { PatternId = patternId, Name = "Number", Pattern = "[0-9]" }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeletePattern_should_update_domain_object() |
|||
{ |
|||
CreateApp() |
|||
.AddPattern(CreateCommand(new AddPattern { PatternId = patternId, Name = "Any", Pattern = "." })); |
|||
|
|||
var context = CreateContextForCommand(new DeletePattern { PatternId = patternId }); |
|||
|
|||
await TestUpdate(app, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
private AppDomainObject CreateApp() |
|||
{ |
|||
app.Create(CreateCommand(new CreateApp { AppId = AppId, Name = AppName })); |
|||
|
|||
return app; |
|||
} |
|||
|
|||
private static Task<IChangePlanResult> CreateRedirectResult() |
|||
{ |
|||
return Task.FromResult<IChangePlanResult>(new RedirectToCheckoutResult(new Uri("http://squidex.io"))); |
|||
} |
|||
} |
|||
} |
|||
@ -1,399 +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 System.Linq; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public class AppDomainObjectTests : HandlerTestBase<AppDomainObject> |
|||
{ |
|||
private readonly string contributorId = Guid.NewGuid().ToString(); |
|||
private readonly string clientId = "client"; |
|||
private readonly string clientNewName = "My Client"; |
|||
private readonly string planId = "premium"; |
|||
private readonly Guid patternId = Guid.NewGuid(); |
|||
private readonly AppDomainObject sut = new AppDomainObject(new InitialPatterns()); |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return AppId; } |
|||
} |
|||
|
|||
public AppDomainObjectTests() |
|||
{ |
|||
sut.ActivateAsync(Id, A.Fake<IStore<Guid>>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateCommand(new CreateApp { Name = AppName })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_specify_name_and_owner() |
|||
{ |
|||
var id1 = Guid.NewGuid(); |
|||
var id2 = Guid.NewGuid(); |
|||
|
|||
var initialPatterns = new InitialPatterns |
|||
{ |
|||
{ id1, new AppPattern("Number", "[0-9]") }, |
|||
{ id2, new AppPattern("Numbers", "[0-9]*") } |
|||
}; |
|||
|
|||
var app = new AppDomainObject(initialPatterns); |
|||
|
|||
app.Create(CreateCommand(new CreateApp { Name = AppName, Actor = User, AppId = AppId })); |
|||
|
|||
Assert.Equal(AppName, app.Snapshot.Name); |
|||
|
|||
app.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppCreated { Name = AppName }), |
|||
CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Permission = AppContributorPermission.Owner }), |
|||
CreateEvent(new AppLanguageAdded { Language = Language.EN }), |
|||
CreateEvent(new AppPatternAdded { PatternId = id1, Name = "Number", Pattern = "[0-9]" }), |
|||
CreateEvent(new AppPatternAdded { PatternId = id2, Name = "Numbers", Pattern = "[0-9]*" }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangePlan_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangePlan_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.ChangePlan(CreateCommand(new ChangePlan { PlanId = planId })); |
|||
|
|||
Assert.Equal(planId, sut.Snapshot.Plan.PlanId); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPlanChanged { PlanId = planId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AssignContributor_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AssignContributor_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); |
|||
|
|||
Assert.Equal(AppContributorPermission.Editor, sut.Snapshot.Contributors[contributorId]); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppContributorAssigned { ContributorId = contributorId, Permission = AppContributorPermission.Editor }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveContributor_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveContributor_should_create_events_and_remove_contributor() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.AssignContributor(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); |
|||
sut.RemoveContributor(CreateCommand(new RemoveContributor { ContributorId = contributorId })); |
|||
|
|||
Assert.False(sut.Snapshot.Contributors.ContainsKey(contributorId)); |
|||
|
|||
sut.GetUncomittedEvents().Skip(1) |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppContributorRemoved { ContributorId = contributorId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AttachClient_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AttachClient_should_create_events() |
|||
{ |
|||
var command = new AttachClient { Id = clientId }; |
|||
|
|||
CreateApp(); |
|||
|
|||
sut.AttachClient(CreateCommand(command)); |
|||
|
|||
Assert.True(sut.Snapshot.Clients.ContainsKey(clientId)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientAttached { Id = clientId, Secret = command.Secret }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RevokeClient_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.RevokeClient(CreateCommand(new RevokeClient { Id = "not-found" })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RevokeClient_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateClient(); |
|||
|
|||
sut.RevokeClient(CreateCommand(new RevokeClient { Id = clientId })); |
|||
|
|||
Assert.False(sut.Snapshot.Clients.ContainsKey(clientId)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientRevoked { Id = clientId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateClient_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.UpdateClient(CreateCommand(new UpdateClient { Id = "not-found", Name = clientNewName })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateClient_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateClient(); |
|||
|
|||
sut.UpdateClient(CreateCommand(new UpdateClient { Id = clientId, Name = clientNewName, Permission = AppClientPermission.Developer })); |
|||
|
|||
Assert.Equal(clientNewName, sut.Snapshot.Clients[clientId].Name); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientRenamed { Id = clientId, Name = clientNewName }), |
|||
CreateEvent(new AppClientUpdated { Id = clientId, Permission = AppClientPermission.Developer }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddLanguage_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddLanguage_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.AddLanguage(CreateCommand(new AddLanguage { Language = Language.DE })); |
|||
|
|||
Assert.True(sut.Snapshot.LanguagesConfig.Contains(Language.DE)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageAdded { Language = Language.DE }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveLanguage_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.EN })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void RemoveLanguage_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateLanguage(Language.DE); |
|||
|
|||
sut.RemoveLanguage(CreateCommand(new RemoveLanguage { Language = Language.DE })); |
|||
|
|||
Assert.False(sut.Snapshot.LanguagesConfig.Contains(Language.DE)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageRemoved { Language = Language.DE }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateLanguage_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.EN })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateLanguage_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreateLanguage(Language.DE); |
|||
|
|||
sut.UpdateLanguage(CreateCommand(new UpdateLanguage { Language = Language.DE, Fallback = new List<Language> { Language.EN } })); |
|||
|
|||
Assert.True(sut.Snapshot.LanguagesConfig.Contains(Language.DE)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageUpdated { Language = Language.DE, Fallback = new List<Language> { Language.EN } }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddPattern_should_throw_exception_if_app_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => sut.AddPattern(CreateCommand(new AddPattern { PatternId = patternId, Name = "Any", Pattern = ".*" }))); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddPattern_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
|
|||
sut.AddPattern(CreateCommand(new AddPattern { PatternId = patternId, Name = "Any", Pattern = ".*", Message = "Msg" })); |
|||
|
|||
Assert.Single(sut.Snapshot.Patterns); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPatternAdded { PatternId = patternId, Name = "Any", Pattern = ".*", Message = "Msg" }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DeletePattern_should_throw_exception_if_app_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.DeletePattern(CreateCommand(new DeletePattern |
|||
{ |
|||
PatternId = Guid.NewGuid() |
|||
})); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DeletePattern_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreatePattern(); |
|||
|
|||
sut.DeletePattern(CreateCommand(new DeletePattern { PatternId = patternId })); |
|||
|
|||
Assert.Empty(sut.Snapshot.Patterns); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPatternDeleted { PatternId = patternId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdatePattern_should_throw_exception_if_app_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => sut.UpdatePattern(CreateCommand(new UpdatePattern { PatternId = patternId, Name = "Any", Pattern = ".*" }))); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdatePattern_should_create_events() |
|||
{ |
|||
CreateApp(); |
|||
CreatePattern(); |
|||
|
|||
sut.UpdatePattern(CreateCommand(new UpdatePattern { PatternId = patternId, Name = "Any", Pattern = ".*", Message = "Msg" })); |
|||
|
|||
Assert.Single(sut.Snapshot.Patterns); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPatternUpdated { PatternId = patternId, Name = "Any", Pattern = ".*", Message = "Msg" }) |
|||
); |
|||
} |
|||
|
|||
private void CreatePattern() |
|||
{ |
|||
sut.AddPattern(CreateCommand(new AddPattern { PatternId = patternId, Name = "Name", Pattern = ".*" })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void CreateApp() |
|||
{ |
|||
sut.Create(CreateCommand(new CreateApp { Name = AppName })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void CreateClient() |
|||
{ |
|||
sut.AttachClient(CreateCommand(new AttachClient { Id = clientId })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void CreateLanguage(Language language) |
|||
{ |
|||
sut.AddLanguage(CreateCommand(new AddLanguage { Language = language })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,388 @@ |
|||
// ==========================================================================
|
|||
// 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 FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Entities.Apps.Commands; |
|||
using Squidex.Domain.Apps.Entities.Apps.Services; |
|||
using Squidex.Domain.Apps.Entities.Apps.State; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Apps; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Shared.Users; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Apps |
|||
{ |
|||
public class AppGrainTests : HandlerTestBase<AppGrain, AppState> |
|||
{ |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly IAppPlansProvider appPlansProvider = A.Fake<IAppPlansProvider>(); |
|||
private readonly IAppPlanBillingManager appPlansBillingManager = A.Fake<IAppPlanBillingManager>(); |
|||
private readonly IUserResolver userResolver = A.Fake<IUserResolver>(); |
|||
private readonly string contributorId = Guid.NewGuid().ToString(); |
|||
private readonly string clientId = "client"; |
|||
private readonly string clientNewName = "My Client"; |
|||
private readonly string planId = "premium"; |
|||
private readonly AppGrain sut; |
|||
private readonly Guid patternId1 = Guid.NewGuid(); |
|||
private readonly Guid patternId2 = Guid.NewGuid(); |
|||
private readonly Guid patternId3 = Guid.NewGuid(); |
|||
private readonly InitialPatterns initialPatterns; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return AppId; } |
|||
} |
|||
|
|||
public AppGrainTests() |
|||
{ |
|||
A.CallTo(() => appProvider.GetAppAsync(AppName)) |
|||
.Returns((IAppEntity)null); |
|||
|
|||
A.CallTo(() => userResolver.FindByIdAsync(contributorId)) |
|||
.Returns(A.Fake<IUser>()); |
|||
|
|||
initialPatterns = new InitialPatterns |
|||
{ |
|||
{ patternId1, new AppPattern("Number", "[0-9]") }, |
|||
{ patternId2, new AppPattern("Numbers", "[0-9]*") } |
|||
}; |
|||
|
|||
sut = new AppGrain(initialPatterns, Store, appProvider, appPlansProvider, appPlansBillingManager, userResolver); |
|||
sut.ActivateAsync(Id).Wait(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_events_and_update_state() |
|||
{ |
|||
var command = new CreateApp { Name = AppName, Actor = User, AppId = AppId }; |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(EntityCreatedResult.Create(Id, 4)); |
|||
|
|||
Assert.Equal(AppName, sut.Snapshot.Name); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppCreated { Name = AppName }), |
|||
CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Permission = AppContributorPermission.Owner }), |
|||
CreateEvent(new AppLanguageAdded { Language = Language.EN }), |
|||
CreateEvent(new AppPatternAdded { PatternId = patternId1, Name = "Number", Pattern = "[0-9]" }), |
|||
CreateEvent(new AppPatternAdded { PatternId = patternId2, Name = "Numbers", Pattern = "[0-9]*" }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_create_events_and_update_state() |
|||
{ |
|||
var command = new ChangePlan { PlanId = planId }; |
|||
|
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan(planId)) |
|||
.Returns(true); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planId)) |
|||
.Returns(new PlanChangedResult()); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
Assert.True(result is PlanChangedResult); |
|||
|
|||
Assert.Equal(planId, sut.Snapshot.Plan.PlanId); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPlanChanged { PlanId = planId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_not_make_update_for_redirect_result() |
|||
{ |
|||
var command = new ChangePlan { PlanId = planId }; |
|||
|
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan(planId)) |
|||
.Returns(true); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planId)) |
|||
.Returns(new RedirectToCheckoutResult(new Uri("http://squidex.io"))); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new RedirectToCheckoutResult(new Uri("http://squidex.io"))); |
|||
|
|||
Assert.Null(sut.Snapshot.Plan); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangePlan_should_not_call_billing_manager_for_callback() |
|||
{ |
|||
var command = new ChangePlan { PlanId = planId, FromCallback = true }; |
|||
|
|||
A.CallTo(() => appPlansProvider.IsConfiguredPlan(planId)) |
|||
.Returns(true); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(5)); |
|||
|
|||
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppId, AppName, planId)) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AssignContributor_should_create_events_and_update_state() |
|||
{ |
|||
var command = new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(5)); |
|||
|
|||
Assert.Equal(AppContributorPermission.Editor, sut.Snapshot.Contributors[contributorId]); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppContributorAssigned { ContributorId = contributorId, Permission = AppContributorPermission.Editor }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveContributor_should_create_events_and_update_state() |
|||
{ |
|||
var command = new RemoveContributor { ContributorId = contributorId }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAssignContributorAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(6)); |
|||
|
|||
Assert.False(sut.Snapshot.Contributors.ContainsKey(contributorId)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppContributorRemoved { ContributorId = contributorId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AttachClient_should_create_events_and_update_state() |
|||
{ |
|||
var command = new AttachClient { Id = clientId }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(5)); |
|||
|
|||
Assert.True(sut.Snapshot.Clients.ContainsKey(clientId)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientAttached { Id = clientId, Secret = command.Secret }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RevokeClient_should_create_events_and_update_state() |
|||
{ |
|||
var command = new RevokeClient { Id = clientId }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAttachClientAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(6)); |
|||
|
|||
Assert.False(sut.Snapshot.Clients.ContainsKey(clientId)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientRevoked { Id = clientId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateClient_should_create_events_and_update_state() |
|||
{ |
|||
var command = new UpdateClient { Id = clientId, Name = clientNewName, Permission = AppClientPermission.Developer }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAttachClientAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(7)); |
|||
|
|||
Assert.Equal(clientNewName, sut.Snapshot.Clients[clientId].Name); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppClientRenamed { Id = clientId, Name = clientNewName }), |
|||
CreateEvent(new AppClientUpdated { Id = clientId, Permission = AppClientPermission.Developer }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddLanguage_should_create_events_and_update_state() |
|||
{ |
|||
var command = new AddLanguage { Language = Language.DE }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(5)); |
|||
|
|||
Assert.True(sut.Snapshot.LanguagesConfig.Contains(Language.DE)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageAdded { Language = Language.DE }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task RemoveLanguage_should_create_events_and_update_state() |
|||
{ |
|||
var command = new RemoveLanguage { Language = Language.DE }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddLanguageAsync(Language.DE); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(6)); |
|||
|
|||
Assert.False(sut.Snapshot.LanguagesConfig.Contains(Language.DE)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageRemoved { Language = Language.DE }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateLanguage_should_create_events_and_update_state() |
|||
{ |
|||
var command = new UpdateLanguage { Language = Language.DE, Fallback = new List<Language> { Language.EN } }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddLanguageAsync(Language.DE); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(6)); |
|||
|
|||
Assert.True(sut.Snapshot.LanguagesConfig.Contains(Language.DE)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppLanguageUpdated { Language = Language.DE, Fallback = new List<Language> { Language.EN } }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task AddPattern_should_create_events_and_update_state() |
|||
{ |
|||
var command = new AddPattern { PatternId = patternId3, Name = "Any", Pattern = ".*", Message = "Msg" }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(5)); |
|||
|
|||
Assert.Equal(initialPatterns.Count + 1, sut.Snapshot.Patterns.Count); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPatternAdded { PatternId = patternId3, Name = "Any", Pattern = ".*", Message = "Msg" }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeletePattern_should_create_events_and_update_state() |
|||
{ |
|||
var command = new DeletePattern { PatternId = patternId3 }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddPatternAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(6)); |
|||
|
|||
Assert.Equal(initialPatterns.Count, sut.Snapshot.Patterns.Count); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPatternDeleted { PatternId = patternId3 }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdatePattern_should_create_events_and_update_state() |
|||
{ |
|||
var command = new UpdatePattern { PatternId = patternId3, Name = "Any", Pattern = ".*", Message = "Msg" }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddPatternAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(6)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new AppPatternUpdated { PatternId = patternId3, Name = "Any", Pattern = ".*", Message = "Msg" }) |
|||
); |
|||
} |
|||
|
|||
private Task ExecuteAddPatternAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new AddPattern { PatternId = patternId3, Name = "Name", Pattern = ".*" })); |
|||
} |
|||
|
|||
private Task ExecuteCreateAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new CreateApp { Name = AppName })); |
|||
} |
|||
|
|||
private Task ExecuteAssignContributorAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new AssignContributor { ContributorId = contributorId, Permission = AppContributorPermission.Editor })); |
|||
} |
|||
|
|||
private Task ExecuteAttachClientAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new AttachClient { Id = clientId })); |
|||
} |
|||
|
|||
private Task ExecuteAddLanguageAsync(Language language) |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new AddLanguage { Language = language })); |
|||
} |
|||
} |
|||
} |
|||
@ -1,220 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Entities.Assets.Commands; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Assets; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Assets; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Assets |
|||
{ |
|||
public class AssetDomainObjectTests : HandlerTestBase<AssetDomainObject> |
|||
{ |
|||
private readonly ImageInfo image = new ImageInfo(2048, 2048); |
|||
private readonly Guid assetId = Guid.NewGuid(); |
|||
private readonly AssetFile file = new AssetFile("my-image.png", "image/png", 1024, () => new MemoryStream()); |
|||
private readonly AssetDomainObject sut = new AssetDomainObject(); |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return assetId; } |
|||
} |
|||
|
|||
public AssetDomainObjectTests() |
|||
{ |
|||
sut.ActivateAsync(Id, A.Fake<IStore<Guid>>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
CreateAsset(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateAssetCommand(new CreateAsset { File = file })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_create_events() |
|||
{ |
|||
sut.Create(CreateAssetCommand(new CreateAsset { File = file, ImageInfo = image })); |
|||
|
|||
Assert.Equal(0, sut.Snapshot.FileVersion); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetCreated |
|||
{ |
|||
IsImage = true, |
|||
FileName = file.FileName, |
|||
FileSize = file.FileSize, |
|||
FileVersion = 0, |
|||
MimeType = file.MimeType, |
|||
PixelWidth = image.PixelWidth, |
|||
PixelHeight = image.PixelHeight |
|||
}) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateAssetCommand(new UpdateAsset { File = file })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_asset_is_deleted() |
|||
{ |
|||
CreateAsset(); |
|||
DeleteAsset(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateAssetCommand(new UpdateAsset())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_create_events() |
|||
{ |
|||
CreateAsset(); |
|||
|
|||
sut.Update(CreateAssetCommand(new UpdateAsset { File = file, ImageInfo = image })); |
|||
|
|||
Assert.Equal(1, sut.Snapshot.FileVersion); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetUpdated |
|||
{ |
|||
IsImage = true, |
|||
FileSize = file.FileSize, |
|||
FileVersion = 1, |
|||
MimeType = file.MimeType, |
|||
PixelWidth = image.PixelWidth, |
|||
PixelHeight = image.PixelHeight |
|||
}) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Rename_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "new-file.png" })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Rename_should_throw_exception_if_asset_is_deleted() |
|||
{ |
|||
CreateAsset(); |
|||
DeleteAsset(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateAssetCommand(new UpdateAsset())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Rename_should_create_events() |
|||
{ |
|||
CreateAsset(); |
|||
|
|||
sut.Rename(CreateAssetCommand(new RenameAsset { FileName = "my-new-image.png" })); |
|||
|
|||
Assert.Equal("my-new-image.png", sut.Snapshot.FileName); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetRenamed { FileName = "my-new-image.png" }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateAssetCommand(new DeleteAsset())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_already_deleted() |
|||
{ |
|||
CreateAsset(); |
|||
DeleteAsset(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateAssetCommand(new DeleteAsset())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_create_events_with_total_file_size() |
|||
{ |
|||
CreateAsset(); |
|||
UpdateAsset(); |
|||
|
|||
sut.Delete(CreateAssetCommand(new DeleteAsset())); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetDeleted { DeletedSize = 2048 }) |
|||
); |
|||
} |
|||
|
|||
private void CreateAsset() |
|||
{ |
|||
sut.Create(CreateAssetCommand(new CreateAsset { File = file })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void UpdateAsset() |
|||
{ |
|||
sut.Update(CreateAssetCommand(new UpdateAsset { File = file })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void DeleteAsset() |
|||
{ |
|||
sut.Delete(CreateAssetCommand(new DeleteAsset())); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
protected T CreateAssetEvent<T>(T @event) where T : AssetEvent |
|||
{ |
|||
@event.AssetId = assetId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateAssetCommand<T>(T command) where T : AssetCommand |
|||
{ |
|||
command.AssetId = assetId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,170 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.IO; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Domain.Apps.Entities.Assets.Commands; |
|||
using Squidex.Domain.Apps.Entities.Assets.State; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Assets; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Assets; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Assets |
|||
{ |
|||
public class AssetGrainTests : HandlerTestBase<AssetGrain, AssetState> |
|||
{ |
|||
private readonly ImageInfo image = new ImageInfo(2048, 2048); |
|||
private readonly Guid assetId = Guid.NewGuid(); |
|||
private readonly AssetFile file = new AssetFile("my-image.png", "image/png", 1024, () => new MemoryStream()); |
|||
private readonly AssetGrain sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return assetId; } |
|||
} |
|||
|
|||
public AssetGrainTests() |
|||
{ |
|||
sut = new AssetGrain(Store); |
|||
sut.ActivateAsync(Id).Wait(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Command_should_throw_exception_if_rule_is_deleted() |
|||
{ |
|||
await ExecuteCreateAsync(); |
|||
await ExecuteDeleteAsync(); |
|||
|
|||
await Assert.ThrowsAsync<DomainException>(ExecuteUpdateAsync); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_events() |
|||
{ |
|||
var command = new CreateAsset { File = file, ImageInfo = image }; |
|||
|
|||
var result = await sut.ExecuteAsync(CreateAssetCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new AssetSavedResult(0, 0)); |
|||
|
|||
Assert.Equal(0, sut.Snapshot.FileVersion); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetCreated |
|||
{ |
|||
IsImage = true, |
|||
FileName = file.FileName, |
|||
FileSize = file.FileSize, |
|||
FileVersion = 0, |
|||
MimeType = file.MimeType, |
|||
PixelWidth = image.PixelWidth, |
|||
PixelHeight = image.PixelHeight |
|||
}) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_create_events() |
|||
{ |
|||
var command = new UpdateAsset { File = file, ImageInfo = image }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateAssetCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new AssetSavedResult(1, 1)); |
|||
|
|||
Assert.Equal(1, sut.Snapshot.FileVersion); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetUpdated |
|||
{ |
|||
IsImage = true, |
|||
FileSize = file.FileSize, |
|||
FileVersion = 1, |
|||
MimeType = file.MimeType, |
|||
PixelWidth = image.PixelWidth, |
|||
PixelHeight = image.PixelHeight |
|||
}) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Rename_should_create_events() |
|||
{ |
|||
var command = new RenameAsset { FileName = "my-new-image.png" }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateAssetCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.Equal("my-new-image.png", sut.Snapshot.FileName); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetRenamed { FileName = "my-new-image.png" }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_create_events_with_total_file_size() |
|||
{ |
|||
var command = new DeleteAsset(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteUpdateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateAssetCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateAssetEvent(new AssetDeleted { DeletedSize = 2048 }) |
|||
); |
|||
} |
|||
|
|||
private Task ExecuteCreateAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateAssetCommand(new CreateAsset { File = file })); |
|||
} |
|||
|
|||
private Task ExecuteUpdateAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateAssetCommand(new UpdateAsset { File = file })); |
|||
} |
|||
|
|||
private Task ExecuteDeleteAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateAssetCommand(new DeleteAsset())); |
|||
} |
|||
|
|||
protected T CreateAssetEvent<T>(T @event) where T : AssetEvent |
|||
{ |
|||
@event.AssetId = assetId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateAssetCommand<T>(T command) where T : AssetCommand |
|||
{ |
|||
command.AssetId = assetId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
} |
|||
} |
|||
@ -1,264 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using NodaTime; |
|||
using Squidex.Domain.Apps.Core; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.Scripting; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class ContentCommandMiddlewareTests : HandlerTestBase<ContentDomainObject> |
|||
{ |
|||
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>(); |
|||
private readonly IScriptEngine scriptEngine = A.Fake<IScriptEngine>(); |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly IAppEntity app = A.Fake<IAppEntity>(); |
|||
private readonly ClaimsPrincipal user = new ClaimsPrincipal(); |
|||
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.DE); |
|||
private readonly Guid contentId = Guid.NewGuid(); |
|||
private readonly ContentDomainObject content = new ContentDomainObject(); |
|||
private readonly ContentCommandMiddleware sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return contentId; } |
|||
} |
|||
|
|||
private readonly NamedContentData invalidData = |
|||
new NamedContentData() |
|||
.AddField("my-field1", new ContentFieldData() |
|||
.AddValue(null)) |
|||
.AddField("my-field2", new ContentFieldData() |
|||
.AddValue(1)); |
|||
private readonly NamedContentData data = |
|||
new NamedContentData() |
|||
.AddField("my-field1", new ContentFieldData() |
|||
.AddValue(1)) |
|||
.AddField("my-field2", new ContentFieldData() |
|||
.AddValue(1)); |
|||
private readonly NamedContentData patch = |
|||
new NamedContentData() |
|||
.AddField("my-field1", new ContentFieldData() |
|||
.AddValue(1)); |
|||
|
|||
public ContentCommandMiddlewareTests() |
|||
{ |
|||
var schemaDef = |
|||
new Schema("my-schema") |
|||
.AddField(new NumberField(1, "my-field1", Partitioning.Invariant, |
|||
new NumberFieldProperties { IsRequired = true })) |
|||
.AddField(new NumberField(2, "my-field2", Partitioning.Invariant, |
|||
new NumberFieldProperties { IsRequired = false })); |
|||
|
|||
sut = new ContentCommandMiddleware(Handler, appProvider, A.Dummy<IAssetRepository>(), scriptEngine, A.Dummy<IContentRepository>()); |
|||
|
|||
A.CallTo(() => app.LanguagesConfig).Returns(languagesConfig); |
|||
|
|||
A.CallTo(() => appProvider.GetAppAsync(AppName)).Returns(app); |
|||
|
|||
A.CallTo(() => schema.SchemaDef).Returns(schemaDef); |
|||
A.CallTo(() => schema.ScriptCreate).Returns("<create-script>"); |
|||
A.CallTo(() => schema.ScriptChange).Returns("<change-script>"); |
|||
A.CallTo(() => schema.ScriptUpdate).Returns("<update-script>"); |
|||
A.CallTo(() => schema.ScriptDelete).Returns("<delete-script>"); |
|||
|
|||
A.CallTo(() => appProvider.GetAppWithSchemaAsync(AppId, SchemaId)).Returns((app, schema)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_throw_exception_if_data_is_not_valid() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(invalidData); |
|||
|
|||
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = invalidData, User = user }); |
|||
|
|||
await TestCreate(content, async _ => |
|||
{ |
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); |
|||
}, false); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_content() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user }); |
|||
|
|||
await TestCreate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(data, context.Result<EntityCreatedResult<NamedContentData>>().IdOrValue); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")).MustHaveHappened(); |
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_also_invoke_publish_script_when_publishing() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
var context = CreateContextForCommand(new CreateContent { ContentId = contentId, Data = data, User = user, Publish = true }); |
|||
|
|||
await TestCreate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(data, context.Result<EntityCreatedResult<NamedContentData>>().IdOrValue); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")).MustHaveHappened(); |
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_throw_exception_if_data_is_not_valid() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(invalidData); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = invalidData, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); |
|||
}, false); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_update_domain_object() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new UpdateContent { ContentId = contentId, Data = data, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(data, context.Result<ContentDataChangedResult>().Data); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Patch_should_throw_exception_if_data_is_not_valid() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(invalidData); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = invalidData, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.HandleAsync(context)); |
|||
}, false); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Patch_should_update_domain_object() |
|||
{ |
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, A<string>.Ignored)).Returns(patch); |
|||
|
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new PatchContent { ContentId = contentId, Data = patch, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.NotNull(context.Result<ContentDataChangedResult>().Data); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangeStatus_should_publish_domain_object() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new ChangeContentStatus { ContentId = contentId, User = user, Status = Status.Published }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangeStatus_should_not_invoke_scripts_when_scheduled() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
var context = CreateContextForCommand(new ChangeContentStatus { ContentId = contentId, User = user, Status = Status.Published, DueTime = Instant.MaxValue }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")).MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_update_domain_object() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
var command = CreateContextForCommand(new DeleteContent { ContentId = contentId, User = user }); |
|||
|
|||
await TestUpdate(content, async _ => |
|||
{ |
|||
await sut.HandleAsync(command); |
|||
}); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<delete-script>")).MustHaveHappened(); |
|||
} |
|||
|
|||
private void CreateContent() |
|||
{ |
|||
content.Create(CreateCommand(new CreateContent { Data = data })); |
|||
} |
|||
} |
|||
} |
|||
@ -1,304 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using FakeItEasy; |
|||
using FluentAssertions; |
|||
using NodaTime; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class ContentDomainObjectTests : HandlerTestBase<ContentDomainObject> |
|||
{ |
|||
private readonly NamedContentData data = |
|||
new NamedContentData() |
|||
.AddField("field1", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 1)); |
|||
private readonly NamedContentData otherData = |
|||
new NamedContentData() |
|||
.AddField("field2", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 2)); |
|||
private readonly NamedContentData patched; |
|||
private readonly Guid contentId = Guid.NewGuid(); |
|||
private readonly ContentDomainObject sut = new ContentDomainObject(); |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return contentId; } |
|||
} |
|||
|
|||
public ContentDomainObjectTests() |
|||
{ |
|||
patched = otherData.MergeInto(data); |
|||
|
|||
sut.ActivateAsync(Id, A.Fake<IStore<Guid>>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
sut.Create(CreateCommand(new CreateContent { Data = data })); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_create_events() |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_also_publish_if_set_to_true() |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data, Publish = true })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }), |
|||
CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = data })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_content_is_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateContentCommand(new UpdateContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_create_events() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = otherData })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentUpdated { Data = otherData }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_not_create_event_for_same_data() |
|||
{ |
|||
CreateContent(); |
|||
UpdateContent(); |
|||
|
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = data })); |
|||
|
|||
sut.GetUncomittedEvents().Should().BeEmpty(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Patch(CreateContentCommand(new PatchContent { Data = data })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_throw_exception_if_content_is_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Patch(CreateContentCommand(new PatchContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_create_events() |
|||
{ |
|||
CreateContent(); |
|||
UpdateContent(); |
|||
|
|||
sut.Patch(CreateContentCommand(new PatchContent { Data = otherData })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentUpdated { Data = patched }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Patch_should_not_create_event_for_same_data() |
|||
{ |
|||
CreateContent(); |
|||
UpdateContent(); |
|||
|
|||
sut.Patch(CreateContentCommand(new PatchContent { Data = data })); |
|||
|
|||
sut.GetUncomittedEvents().Should().BeEmpty(); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangeStatus_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangeStatus_should_throw_exception_if_content_is_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangeStatus_should_refresh_properties_and_create_events() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published })); |
|||
|
|||
Assert.Equal(Status.Published, sut.Snapshot.Status); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ChangeStatus_should_refresh_properties_and_create_scheduled_events_when_command_has_due_time() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
var dueTime = Instant.MaxValue; |
|||
|
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = Status.Published, DueTime = dueTime })); |
|||
|
|||
Assert.Equal(Status.Draft, sut.Snapshot.Status); |
|||
Assert.Equal(Status.Published, sut.Snapshot.ScheduledTo); |
|||
Assert.Equal(dueTime, sut.Snapshot.ScheduledAt); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentStatusScheduled { Status = Status.Published, DueTime = dueTime }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_already_deleted() |
|||
{ |
|||
CreateContent(); |
|||
DeleteContent(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_update_properties_and_create_events() |
|||
{ |
|||
CreateContent(); |
|||
|
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentDeleted()) |
|||
); |
|||
} |
|||
|
|||
private void CreateContent() |
|||
{ |
|||
sut.Create(CreateContentCommand(new CreateContent { Data = data })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void UpdateContent() |
|||
{ |
|||
sut.Update(CreateContentCommand(new UpdateContent { Data = data })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void ChangeStatus(Status status) |
|||
{ |
|||
sut.ChangeStatus(CreateContentCommand(new ChangeContentStatus { Status = status })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void DeleteContent() |
|||
{ |
|||
sut.Delete(CreateContentCommand(new DeleteContent())); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
protected T CreateContentEvent<T>(T @event) where T : ContentEvent |
|||
{ |
|||
@event.ContentId = contentId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateContentCommand<T>(T command) where T : ContentCommand |
|||
{ |
|||
command.ContentId = contentId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,361 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using NodaTime; |
|||
using Squidex.Domain.Apps.Core; |
|||
using Squidex.Domain.Apps.Core.Apps; |
|||
using Squidex.Domain.Apps.Core.Contents; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Core.Scripting; |
|||
using Squidex.Domain.Apps.Entities.Apps; |
|||
using Squidex.Domain.Apps.Entities.Assets.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.Commands; |
|||
using Squidex.Domain.Apps.Entities.Contents.Repositories; |
|||
using Squidex.Domain.Apps.Entities.Contents.State; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Contents; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Contents |
|||
{ |
|||
public class ContentGrainTests : HandlerTestBase<ContentGrain, ContentState> |
|||
{ |
|||
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>(); |
|||
private readonly IScriptEngine scriptEngine = A.Fake<IScriptEngine>(); |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly IAppEntity app = A.Fake<IAppEntity>(); |
|||
private readonly ClaimsPrincipal user = new ClaimsPrincipal(); |
|||
private readonly LanguagesConfig languagesConfig = LanguagesConfig.Build(Language.DE); |
|||
|
|||
private readonly NamedContentData invalidData = |
|||
new NamedContentData() |
|||
.AddField("my-field1", |
|||
new ContentFieldData() |
|||
.AddValue(null)) |
|||
.AddField("my-field2", |
|||
new ContentFieldData() |
|||
.AddValue(1)); |
|||
private readonly NamedContentData data = |
|||
new NamedContentData() |
|||
.AddField("my-field1", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 1)); |
|||
private readonly NamedContentData patch = |
|||
new NamedContentData() |
|||
.AddField("my-field2", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 2)); |
|||
private readonly NamedContentData otherData = |
|||
new NamedContentData() |
|||
.AddField("my-field1", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 2)) |
|||
.AddField("my-field2", |
|||
new ContentFieldData() |
|||
.AddValue("iv", 2)); |
|||
private readonly NamedContentData patched; |
|||
private readonly Guid contentId = Guid.NewGuid(); |
|||
private readonly ContentGrain sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return contentId; } |
|||
} |
|||
|
|||
public ContentGrainTests() |
|||
{ |
|||
var schemaDef = |
|||
new Schema("my-schema") |
|||
.AddField(new NumberField(1, "my-field1", Partitioning.Invariant, |
|||
new NumberFieldProperties { IsRequired = true })) |
|||
.AddField(new NumberField(2, "my-field2", Partitioning.Invariant, |
|||
new NumberFieldProperties { IsRequired = false })); |
|||
|
|||
A.CallTo(() => app.LanguagesConfig).Returns(languagesConfig); |
|||
|
|||
A.CallTo(() => appProvider.GetAppAsync(AppName)).Returns(app); |
|||
A.CallTo(() => appProvider.GetAppWithSchemaAsync(AppId, SchemaId)).Returns((app, schema)); |
|||
|
|||
A.CallTo(() => schema.SchemaDef).Returns(schemaDef); |
|||
A.CallTo(() => schema.ScriptCreate).Returns("<create-script>"); |
|||
A.CallTo(() => schema.ScriptChange).Returns("<change-script>"); |
|||
A.CallTo(() => schema.ScriptUpdate).Returns("<update-script>"); |
|||
A.CallTo(() => schema.ScriptDelete).Returns("<delete-script>"); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.That.Matches(x => ReferenceEquals(x.Data, data)), A<string>.Ignored)) |
|||
.Returns(data); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.That.Matches(x => ReferenceEquals(x.Data, invalidData)), A<string>.Ignored)) |
|||
.Returns(invalidData); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.That.Matches(x => ReferenceEquals(x.Data, patch)), A<string>.Ignored)) |
|||
.Returns(patch); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.That.Matches(x => ReferenceEquals(x.Data, otherData)), A<string>.Ignored)) |
|||
.Returns(otherData); |
|||
|
|||
patched = patch.MergeInto(data); |
|||
|
|||
sut = new ContentGrain(Store, appProvider, A.Dummy<IAssetRepository>(), scriptEngine, A.Dummy<IContentRepository>()); |
|||
sut.ActivateAsync(Id).Wait(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Command_should_throw_exception_if_content_is_deleted() |
|||
{ |
|||
await ExecuteCreateAsync(); |
|||
await ExecuteDeleteAsync(); |
|||
|
|||
await Assert.ThrowsAsync<DomainException>(ExecuteUpdateAsync); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_events_and_update_state() |
|||
{ |
|||
var command = new CreateContent { Data = data }; |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(EntityCreatedResult.Create(data, 0)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")) |
|||
.MustHaveHappened(); |
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_also_publish() |
|||
{ |
|||
var command = new CreateContent { Data = data, Publish = true }; |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(EntityCreatedResult.Create(data, 1)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }), |
|||
CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<create-script>")) |
|||
.MustHaveHappened(); |
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_throw_when_invalid_data_is_passed() |
|||
{ |
|||
var command = new CreateContent { Data = invalidData }; |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.ExecuteAsync(CreateContentCommand(command))); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_create_events_and_update_state() |
|||
{ |
|||
var command = new UpdateContent { Data = otherData }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new ContentDataChangedResult(otherData, 1)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentUpdated { Data = otherData }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_not_create_event_for_same_data() |
|||
{ |
|||
var command = new UpdateContent { Data = data }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new ContentDataChangedResult(data, 0)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_throw_when_invalid_data_is_passed() |
|||
{ |
|||
var command = new UpdateContent { Data = invalidData }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
await Assert.ThrowsAsync<ValidationException>(() => sut.ExecuteAsync(CreateContentCommand(command))); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Patch_should_create_events_and_update_state() |
|||
{ |
|||
var command = new PatchContent { Data = patch }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new ContentDataChangedResult(otherData, 1)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentUpdated { Data = patched }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Patch_should_not_create_event_for_same_data() |
|||
{ |
|||
var command = new PatchContent { Data = data }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new ContentDataChangedResult(data, 0)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentCreated { Data = data }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.ExecuteAndTransform(A<ScriptContext>.Ignored, "<update-script>")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangedStatus_should_create_events_and_update_state() |
|||
{ |
|||
var command = new ChangeContentStatus { Status = Status.Published }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.Equal(Status.Published, sut.Snapshot.Status); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentStatusChanged { Status = Status.Published }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ChangeStatus_should_refresh_properties_and_create_scheduled_events_when_command_has_due_time() |
|||
{ |
|||
var dueTime = Instant.MaxValue; |
|||
|
|||
var command = new ChangeContentStatus { Status = Status.Published, DueTime = dueTime }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.Equal(Status.Draft, sut.Snapshot.Status); |
|||
Assert.Equal(Status.Published, sut.Snapshot.ScheduledTo); |
|||
Assert.Equal(dueTime, sut.Snapshot.ScheduledAt); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentStatusScheduled { Status = Status.Published, DueTime = dueTime }) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<change-script>")) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_update_properties_and_create_events() |
|||
{ |
|||
var command = new DeleteContent(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateContentCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateContentEvent(new ContentDeleted()) |
|||
); |
|||
|
|||
A.CallTo(() => scriptEngine.Execute(A<ScriptContext>.Ignored, "<delete-script>")) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
private Task ExecuteCreateAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateContentCommand(new CreateContent { Data = data })); |
|||
} |
|||
|
|||
private Task ExecuteUpdateAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateContentCommand(new UpdateContent { Data = data })); |
|||
} |
|||
|
|||
private Task ExecuteDeleteAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateContentCommand(new DeleteContent())); |
|||
} |
|||
|
|||
protected T CreateContentEvent<T>(T @event) where T : ContentEvent |
|||
{ |
|||
@event.ContentId = contentId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateContentCommand<T>(T command) where T : ContentCommand |
|||
{ |
|||
command.ContentId = contentId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
} |
|||
} |
|||
@ -1,118 +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 FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Domain.Apps.Core.Rules.Actions; |
|||
using Squidex.Domain.Apps.Core.Rules.Triggers; |
|||
using Squidex.Domain.Apps.Entities.Rules.Commands; |
|||
using Squidex.Domain.Apps.Entities.Schemas; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Rules |
|||
{ |
|||
public class RuleCommandMiddlewareTests : HandlerTestBase<RuleDomainObject> |
|||
{ |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly RuleDomainObject rule = new RuleDomainObject(); |
|||
private readonly RuleTrigger ruleTrigger = new ContentChangedTrigger(); |
|||
private readonly RuleAction ruleAction = new WebhookAction { Url = new Uri("https://squidex.io") }; |
|||
private readonly Guid ruleId = Guid.NewGuid(); |
|||
private readonly RuleCommandMiddleware sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return ruleId; } |
|||
} |
|||
|
|||
public RuleCommandMiddlewareTests() |
|||
{ |
|||
A.CallTo(() => appProvider.GetSchemaAsync(A<Guid>.Ignored, A<Guid>.Ignored, false)) |
|||
.Returns(A.Fake<ISchemaEntity>()); |
|||
|
|||
sut = new RuleCommandMiddleware(Handler, appProvider); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_domain_object() |
|||
{ |
|||
var context = CreateContextForCommand(new CreateRule { Trigger = ruleTrigger, Action = ruleAction }); |
|||
|
|||
await TestCreate(rule, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_update_domain_object() |
|||
{ |
|||
var context = CreateContextForCommand(new UpdateRule { Trigger = ruleTrigger, Action = ruleAction }); |
|||
|
|||
CreateRule(); |
|||
|
|||
await TestUpdate(rule, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Enable_should_update_domain_object() |
|||
{ |
|||
CreateRule(); |
|||
DisableRule(); |
|||
|
|||
var command = CreateContextForCommand(new EnableRule { RuleId = ruleId }); |
|||
|
|||
await TestUpdate(rule, async _ => |
|||
{ |
|||
await sut.HandleAsync(command); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Disable_should_update_domain_object() |
|||
{ |
|||
CreateRule(); |
|||
|
|||
var command = CreateContextForCommand(new DisableRule { RuleId = ruleId }); |
|||
|
|||
await TestUpdate(rule, async _ => |
|||
{ |
|||
await sut.HandleAsync(command); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_update_domain_object() |
|||
{ |
|||
CreateRule(); |
|||
|
|||
var command = CreateContextForCommand(new DeleteRule { RuleId = ruleId }); |
|||
|
|||
await TestUpdate(rule, async _ => |
|||
{ |
|||
await sut.HandleAsync(command); |
|||
}); |
|||
} |
|||
|
|||
private void DisableRule() |
|||
{ |
|||
rule.Disable(CreateCommand(new DisableRule())); |
|||
} |
|||
|
|||
private void CreateRule() |
|||
{ |
|||
rule.Create(CreateCommand(new CreateRule { Trigger = ruleTrigger, Action = ruleAction })); |
|||
} |
|||
} |
|||
} |
|||
@ -1,256 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Immutable; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Rules; |
|||
using Squidex.Domain.Apps.Core.Rules.Actions; |
|||
using Squidex.Domain.Apps.Core.Rules.Triggers; |
|||
using Squidex.Domain.Apps.Entities.Rules.Commands; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Rules; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Rules |
|||
{ |
|||
public class RuleDomainObjectTests : HandlerTestBase<RuleDomainObject> |
|||
{ |
|||
private readonly Guid ruleId = Guid.NewGuid(); |
|||
private readonly RuleTrigger ruleTrigger = new ContentChangedTrigger(); |
|||
private readonly RuleAction ruleAction = new WebhookAction { Url = new Uri("https://squidex.io") }; |
|||
private readonly RuleDomainObject sut = new RuleDomainObject(); |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return ruleId; } |
|||
} |
|||
|
|||
public RuleDomainObjectTests() |
|||
{ |
|||
sut.ActivateAsync(Id, A.Fake<IStore<Guid>>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
sut.Create(CreateRuleCommand(new CreateRule { Trigger = ruleTrigger, Action = ruleAction })); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateRuleCommand(new CreateRule { Trigger = ruleTrigger, Action = ruleAction })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_create_events() |
|||
{ |
|||
var command = new CreateRule { Trigger = ruleTrigger, Action = ruleAction }; |
|||
|
|||
sut.Create(CreateRuleCommand(command)); |
|||
|
|||
Assert.Equal(AppId, sut.Snapshot.AppId.Id); |
|||
|
|||
Assert.Same(ruleTrigger, sut.Snapshot.RuleDef.Trigger); |
|||
Assert.Same(ruleAction, sut.Snapshot.RuleDef.Action); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleCreated { Trigger = ruleTrigger, Action = ruleAction }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateRuleCommand(new UpdateRule { Trigger = ruleTrigger, Action = ruleAction })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_rule_is_deleted() |
|||
{ |
|||
CreateRule(); |
|||
DeleteRule(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateRuleCommand(new UpdateRule { Trigger = ruleTrigger, Action = ruleAction })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_create_events() |
|||
{ |
|||
var newTrigger = new ContentChangedTrigger |
|||
{ |
|||
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty |
|||
}; |
|||
|
|||
var newAction = new WebhookAction |
|||
{ |
|||
Url = new Uri("https://squidex.io/v2") |
|||
}; |
|||
|
|||
CreateRule(); |
|||
|
|||
var command = new UpdateRule { Trigger = newTrigger, Action = newAction }; |
|||
|
|||
sut.Update(CreateRuleCommand(command)); |
|||
|
|||
Assert.Same(newTrigger, sut.Snapshot.RuleDef.Trigger); |
|||
Assert.Same(newAction, sut.Snapshot.RuleDef.Action); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleUpdated { Trigger = newTrigger, Action = newAction }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Enable_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Enable(CreateRuleCommand(new EnableRule())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Enable_should_throw_exception_if_rule_is_deleted() |
|||
{ |
|||
CreateRule(); |
|||
DeleteRule(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Enable(CreateRuleCommand(new EnableRule())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Enable_should_create_events() |
|||
{ |
|||
CreateRule(); |
|||
|
|||
var command = new EnableRule(); |
|||
|
|||
sut.Enable(CreateRuleCommand(command)); |
|||
|
|||
Assert.True(sut.Snapshot.RuleDef.IsEnabled); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleEnabled()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Disable_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Disable(CreateRuleCommand(new DisableRule())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Disable_should_throw_exception_if_rule_is_deleted() |
|||
{ |
|||
CreateRule(); |
|||
DeleteRule(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Disable(CreateRuleCommand(new DisableRule())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Disable_should_create_events() |
|||
{ |
|||
CreateRule(); |
|||
|
|||
var command = new DisableRule(); |
|||
|
|||
sut.Disable(CreateRuleCommand(command)); |
|||
|
|||
Assert.False(sut.Snapshot.RuleDef.IsEnabled); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleDisabled()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateRuleCommand(new DeleteRule())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_already_deleted() |
|||
{ |
|||
CreateRule(); |
|||
DeleteRule(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateRuleCommand(new DeleteRule())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_update_create_events() |
|||
{ |
|||
CreateRule(); |
|||
|
|||
sut.Delete(CreateRuleCommand(new DeleteRule())); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleDeleted()) |
|||
); |
|||
} |
|||
|
|||
private void CreateRule() |
|||
{ |
|||
sut.Create(CreateRuleCommand(new CreateRule { Trigger = ruleTrigger, Action = ruleAction })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void DeleteRule() |
|||
{ |
|||
sut.Delete(CreateRuleCommand(new DeleteRule())); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
protected T CreateRuleEvent<T>(T @event) where T : RuleEvent |
|||
{ |
|||
@event.RuleId = ruleId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateRuleCommand<T>(T command) where T : RuleCommand |
|||
{ |
|||
command.RuleId = ruleId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,214 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Collections.Immutable; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Rules.Actions; |
|||
using Squidex.Domain.Apps.Core.Rules.Triggers; |
|||
using Squidex.Domain.Apps.Entities.Rules.Commands; |
|||
using Squidex.Domain.Apps.Entities.Rules.State; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Rules; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Rules |
|||
{ |
|||
public class RuleGrainTests : HandlerTestBase<RuleGrain, RuleState> |
|||
{ |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly Guid ruleId = Guid.NewGuid(); |
|||
private readonly RuleGrain sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return ruleId; } |
|||
} |
|||
|
|||
public RuleGrainTests() |
|||
{ |
|||
sut = new RuleGrain(Store, appProvider); |
|||
sut.ActivateAsync(Id).Wait(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Command_should_throw_exception_if_rule_is_deleted() |
|||
{ |
|||
await ExecuteCreateAsync(); |
|||
await ExecuteDeleteAsync(); |
|||
|
|||
await Assert.ThrowsAsync<DomainException>(ExecuteDisableAsync); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_events_and_update_state() |
|||
{ |
|||
var command = MakeCreateCommand(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateRuleCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(EntityCreatedResult.Create(Id, 0)); |
|||
|
|||
Assert.Equal(AppId, sut.Snapshot.AppId.Id); |
|||
|
|||
Assert.Same(command.Trigger, sut.Snapshot.RuleDef.Trigger); |
|||
Assert.Same(command.Action, sut.Snapshot.RuleDef.Action); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleCreated { Trigger = command.Trigger, Action = command.Action }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_create_events_and_update_state() |
|||
{ |
|||
var command = MakeUpdateCommand(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateRuleCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.Same(command.Trigger, sut.Snapshot.RuleDef.Trigger); |
|||
Assert.Same(command.Action, sut.Snapshot.RuleDef.Action); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleUpdated { Trigger = command.Trigger, Action = command.Action }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Enable_should_handle_command() |
|||
{ |
|||
await sut.ExecuteAsync(CreateRuleCommand(MakeCreateCommand())); |
|||
await sut.ExecuteAsync(CreateRuleCommand(new DisableRule())); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Enable_should_create_events_and_update_state() |
|||
{ |
|||
var command = new EnableRule(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteDisableAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateRuleCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.True(sut.Snapshot.RuleDef.IsEnabled); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleEnabled()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Disable_should_create_events_and_update_state() |
|||
{ |
|||
var command = new DisableRule(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateRuleCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.False(sut.Snapshot.RuleDef.IsEnabled); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleDisabled()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_update_create_events() |
|||
{ |
|||
var command = new DeleteRule(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateRuleCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateRuleEvent(new RuleDeleted()) |
|||
); |
|||
} |
|||
|
|||
private Task ExecuteCreateAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateRuleCommand(MakeCreateCommand())); |
|||
} |
|||
|
|||
private Task ExecuteDisableAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateRuleCommand(new DisableRule())); |
|||
} |
|||
|
|||
private Task ExecuteDeleteAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateRuleCommand(new DeleteRule())); |
|||
} |
|||
|
|||
protected T CreateRuleEvent<T>(T @event) where T : RuleEvent |
|||
{ |
|||
@event.RuleId = ruleId; |
|||
|
|||
return CreateEvent(@event); |
|||
} |
|||
|
|||
protected T CreateRuleCommand<T>(T command) where T : RuleCommand |
|||
{ |
|||
command.RuleId = ruleId; |
|||
|
|||
return CreateCommand(command); |
|||
} |
|||
|
|||
private CreateRule MakeCreateCommand() |
|||
{ |
|||
var newTrigger = new ContentChangedTrigger |
|||
{ |
|||
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty |
|||
}; |
|||
|
|||
var newAction = new WebhookAction |
|||
{ |
|||
Url = new Uri("https://squidex.io/v2") |
|||
}; |
|||
|
|||
return new CreateRule { Trigger = newTrigger, Action = newAction }; |
|||
} |
|||
|
|||
private static UpdateRule MakeUpdateCommand() |
|||
{ |
|||
var newTrigger = new ContentChangedTrigger |
|||
{ |
|||
Schemas = ImmutableList<ContentChangedTriggerSchema>.Empty |
|||
}; |
|||
|
|||
var newAction = new WebhookAction |
|||
{ |
|||
Url = new Uri("https://squidex.io/v2") |
|||
}; |
|||
|
|||
return new UpdateRule { Trigger = newTrigger, Action = newAction }; |
|||
} |
|||
} |
|||
} |
|||
@ -1,280 +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 System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Entities.Schemas.Commands; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Schemas |
|||
{ |
|||
public class SchemaCommandMiddlewareTests : HandlerTestBase<SchemaDomainObject> |
|||
{ |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly SchemaCommandMiddleware sut; |
|||
private readonly SchemaDomainObject schema; |
|||
private readonly FieldRegistry registry = new FieldRegistry(new TypeNameRegistry()); |
|||
private readonly string fieldName = "age"; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return SchemaId; } |
|||
} |
|||
|
|||
public SchemaCommandMiddlewareTests() |
|||
{ |
|||
schema = new SchemaDomainObject(registry); |
|||
|
|||
sut = new SchemaCommandMiddleware(Handler, appProvider); |
|||
|
|||
A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName)) |
|||
.Returns((ISchemaEntity)null); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_schema_domain_object() |
|||
{ |
|||
var context = CreateContextForCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId }); |
|||
|
|||
await TestCreate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(SchemaId, context.Result<EntityCreatedResult<Guid>>().IdOrValue); |
|||
|
|||
A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName)).MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateSchema_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
var context = CreateContextForCommand(new UpdateSchema { Properties = new SchemaProperties() }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ReorderSchema_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
var context = CreateContextForCommand(new ReorderFields { FieldIds = new List<long>() }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task PublishSchema_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
var context = CreateContextForCommand(new PublishSchema()); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UnpublishSchema_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
PublishSchema(); |
|||
|
|||
var context = CreateContextForCommand(new UnpublishSchema()); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ConfigureScripts_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
var context = CreateContextForCommand(new ConfigureScripts()); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeleteSchema_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
var context = CreateContextForCommand(new DeleteSchema()); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Add_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
var context = CreateContextForCommand(new AddField { Name = fieldName, Properties = new NumberFieldProperties() }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
|
|||
Assert.Equal(1, context.Result<EntityCreatedResult<long>>().IdOrValue); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateField_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
var context = CreateContextForCommand(new UpdateField { FieldId = 1, Properties = new NumberFieldProperties() }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task LockField_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
var context = CreateContextForCommand(new LockField { FieldId = 1 }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task HideField_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
var context = CreateContextForCommand(new HideField { FieldId = 1 }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ShowField_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
HideField(); |
|||
|
|||
var context = CreateContextForCommand(new ShowField { FieldId = 1 }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DisableField_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
var context = CreateContextForCommand(new DisableField { FieldId = 1 }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task EnableField_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
DisableField(); |
|||
|
|||
var context = CreateContextForCommand(new EnableField { FieldId = 1 }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeleteField_should_update_domain_object() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
var context = CreateContextForCommand(new DeleteField { FieldId = 1 }); |
|||
|
|||
await TestUpdate(schema, async _ => |
|||
{ |
|||
await sut.HandleAsync(context); |
|||
}); |
|||
} |
|||
|
|||
private void CreateSchema() |
|||
{ |
|||
schema.Create(CreateCommand(new CreateSchema { Name = SchemaName })); |
|||
} |
|||
|
|||
private void PublishSchema() |
|||
{ |
|||
schema.Publish(CreateCommand(new PublishSchema())); |
|||
} |
|||
|
|||
private void CreateField() |
|||
{ |
|||
schema.Add(CreateCommand(new AddField { Name = fieldName, Properties = new NumberFieldProperties() })); |
|||
} |
|||
|
|||
private void HideField() |
|||
{ |
|||
schema.HideField(CreateCommand(new HideField { FieldId = 1 })); |
|||
} |
|||
|
|||
private void DisableField() |
|||
{ |
|||
schema.DisableField(CreateCommand(new DisableField { FieldId = 1 })); |
|||
} |
|||
} |
|||
} |
|||
@ -1,665 +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 System.Linq; |
|||
using FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Entities.Schemas.Commands; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.States; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Schemas |
|||
{ |
|||
public class SchemaDomainObjectTests : HandlerTestBase<SchemaDomainObject> |
|||
{ |
|||
private readonly string fieldName = "age"; |
|||
private readonly NamedId<long> fieldId; |
|||
private readonly SchemaDomainObject sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return SchemaId; } |
|||
} |
|||
|
|||
public SchemaDomainObjectTests() |
|||
{ |
|||
fieldId = new NamedId<long>(1, fieldName); |
|||
|
|||
var fieldRegistry = new FieldRegistry(new TypeNameRegistry()); |
|||
|
|||
sut = new SchemaDomainObject(fieldRegistry); |
|||
sut.ActivateAsync(Id, A.Fake<IStore<Guid>>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_throw_exception_if_created() |
|||
{ |
|||
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName })); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_create_schema_and_create_events() |
|||
{ |
|||
var properties = new SchemaProperties(); |
|||
|
|||
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties })); |
|||
|
|||
Assert.Equal(AppId, sut.Snapshot.AppId.Id); |
|||
|
|||
Assert.Equal(SchemaName, sut.Snapshot.Name); |
|||
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Create_should_create_schema_with_initial_fields() |
|||
{ |
|||
var properties = new SchemaProperties(); |
|||
|
|||
var fields = new List<CreateSchemaField> |
|||
{ |
|||
new CreateSchemaField { Name = "field1", Properties = ValidProperties() }, |
|||
new CreateSchemaField { Name = "field2", Properties = ValidProperties() } |
|||
}; |
|||
|
|||
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName, Properties = properties, Fields = fields })); |
|||
|
|||
var @event = (SchemaCreated)sut.GetUncomittedEvents().Single().Payload; |
|||
|
|||
Assert.Equal(AppId, sut.Snapshot.AppId.Id); |
|||
Assert.Equal(SchemaName, sut.Snapshot.Name); |
|||
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name); |
|||
|
|||
Assert.Equal(2, @event.Fields.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateCommand(new UpdateSchema { Properties = new SchemaProperties() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Update(CreateCommand(new UpdateSchema { Properties = new SchemaProperties() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Update_should_refresh_properties_and_create_events() |
|||
{ |
|||
var properties = new SchemaProperties(); |
|||
|
|||
CreateSchema(); |
|||
|
|||
sut.Update(CreateCommand(new UpdateSchema { Properties = properties })); |
|||
|
|||
Assert.Equal(properties, sut.Snapshot.SchemaDef.Properties); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaUpdated { Properties = properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ConfigureScripts_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ConfigureScripts(CreateCommand(new ConfigureScripts())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ConfigureScripts_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ConfigureScripts(CreateCommand(new ConfigureScripts())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ConfigureScripts_should_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
sut.ConfigureScripts(CreateCommand(new ConfigureScripts |
|||
{ |
|||
ScriptQuery = "<script-query>", |
|||
ScriptCreate = "<script-create>", |
|||
ScriptUpdate = "<script-update>", |
|||
ScriptDelete = "<script-delete>", |
|||
ScriptChange = "<script-change>" |
|||
})); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new ScriptsConfigured |
|||
{ |
|||
ScriptQuery = "<script-query>", |
|||
ScriptCreate = "<script-create>", |
|||
ScriptUpdate = "<script-update>", |
|||
ScriptDelete = "<script-delete>", |
|||
ScriptChange = "<script-change>" |
|||
}) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Reorder_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Reorder(CreateCommand(new ReorderFields { FieldIds = new List<long>() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Reorder_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Reorder(CreateCommand(new ReorderFields { FieldIds = new List<long>() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Reorder_should_refresh_properties_and_create_events() |
|||
{ |
|||
var fieldIds = new List<long> { 1, 2 }; |
|||
|
|||
CreateSchema(); |
|||
|
|||
sut.Add(CreateCommand(new AddField { Name = "field1", Properties = ValidProperties() })); |
|||
sut.Add(CreateCommand(new AddField { Name = "field2", Properties = ValidProperties() })); |
|||
|
|||
sut.ClearUncommittedEvents(); |
|||
|
|||
sut.Reorder(CreateCommand(new ReorderFields { FieldIds = fieldIds })); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaFieldsReordered { FieldIds = fieldIds }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Publish_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Publish(CreateCommand(new PublishSchema())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Publish_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Publish(CreateCommand(new PublishSchema())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Publish_should_refresh_properties_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
sut.Publish(CreateCommand(new PublishSchema())); |
|||
|
|||
Assert.True(sut.Snapshot.SchemaDef.IsPublished); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaPublished()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Unpublish_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Unpublish(CreateCommand(new UnpublishSchema())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Unpublish_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Unpublish(CreateCommand(new UnpublishSchema())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Unpublish_should_refresh_properties_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
PublishSchema(); |
|||
|
|||
sut.Unpublish(CreateCommand(new UnpublishSchema())); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.IsPublished); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaUnpublished()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateCommand(new DeleteSchema())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_throw_exception_if_already_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Delete(CreateCommand(new DeleteSchema())); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Delete_should_refresh_properties_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
|
|||
sut.Delete(CreateCommand(new DeleteSchema())); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaDeleted()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Add(CreateCommand(new AddField { Name = fieldName, Properties = ValidProperties() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void AddField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.Add(CreateCommand(new AddField { Name = fieldName, Properties = new NumberFieldProperties() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Add_should_update_schema_and_create_events() |
|||
{ |
|||
var properties = new NumberFieldProperties(); |
|||
|
|||
CreateSchema(); |
|||
|
|||
sut.Add(CreateCommand(new AddField { Name = fieldName, Properties = properties })); |
|||
|
|||
Assert.Equal(properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldAdded { Name = fieldName, FieldId = fieldId, Properties = properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.UpdateField(CreateCommand(new UpdateField { FieldId = 1, Properties = new NumberFieldProperties() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.UpdateField(CreateCommand(new UpdateField { FieldId = 1, Properties = new NumberFieldProperties() })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void UpdateField_should_update_schema_and_create_events() |
|||
{ |
|||
var properties = new NumberFieldProperties(); |
|||
|
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
sut.UpdateField(CreateCommand(new UpdateField { FieldId = 1, Properties = properties })); |
|||
|
|||
Assert.Equal(properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldUpdated { FieldId = fieldId, Properties = properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void LockField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.LockField(CreateCommand(new LockField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void LockField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.LockField(CreateCommand(new LockField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void LockField_should_update_schema_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
sut.LockField(CreateCommand(new LockField { FieldId = 1 })); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldLocked { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void HideField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.HideField(CreateCommand(new HideField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void HideField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.HideField(CreateCommand(new HideField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void HideField_should_update_schema_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
sut.HideField(CreateCommand(new HideField { FieldId = 1 })); |
|||
|
|||
Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldHidden { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ShowField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ShowField(CreateCommand(new ShowField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ShowField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.ShowField(CreateCommand(new ShowField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void ShowField_should_update_schema_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
sut.HideField(CreateCommand(new HideField { FieldId = 1 })); |
|||
sut.ShowField(CreateCommand(new ShowField { FieldId = 1 })); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden); |
|||
|
|||
sut.GetUncomittedEvents().Skip(1) |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldShown { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DisableField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.DisableField(CreateCommand(new DisableField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DisableField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.DisableField(CreateCommand(new DisableField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DisableField_should_update_schema_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
sut.DisableField(CreateCommand(new DisableField { FieldId = 1 })); |
|||
|
|||
Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldDisabled { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void EnableField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.EnableField(CreateCommand(new EnableField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void EnableField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.EnableField(CreateCommand(new EnableField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void EnableField_should_update_schema_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
sut.DisableField(CreateCommand(new DisableField { FieldId = 1 })); |
|||
sut.EnableField(CreateCommand(new EnableField { FieldId = 1 })); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); |
|||
|
|||
sut.GetUncomittedEvents().Skip(1) |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldEnabled { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DeleteField_should_throw_exception_if_not_created() |
|||
{ |
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.DeleteField(CreateCommand(new DeleteField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DeleteField_should_throw_exception_if_schema_is_deleted() |
|||
{ |
|||
CreateSchema(); |
|||
DeleteSchema(); |
|||
|
|||
Assert.Throws<DomainException>(() => |
|||
{ |
|||
sut.DeleteField(CreateCommand(new DeleteField { FieldId = 1 })); |
|||
}); |
|||
} |
|||
|
|||
[Fact] |
|||
public void DeleteField_should_update_schema_and_create_events() |
|||
{ |
|||
CreateSchema(); |
|||
CreateField(); |
|||
|
|||
sut.DeleteField(CreateCommand(new DeleteField { FieldId = 1 })); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById.ContainsKey(1)); |
|||
|
|||
sut.GetUncomittedEvents() |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldDeleted { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
private void CreateField() |
|||
{ |
|||
sut.Add(CreateCommand(new AddField { Name = fieldName, Properties = new NumberFieldProperties() })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void CreateSchema() |
|||
{ |
|||
sut.Create(CreateCommand(new CreateSchema { Name = SchemaName })); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void PublishSchema() |
|||
{ |
|||
sut.Publish(CreateCommand(new PublishSchema())); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private void DeleteSchema() |
|||
{ |
|||
sut.Delete(CreateCommand(new DeleteSchema())); |
|||
sut.ClearUncommittedEvents(); |
|||
} |
|||
|
|||
private static StringFieldProperties ValidProperties() |
|||
{ |
|||
return new StringFieldProperties { MinLength = 10, MaxLength = 20 }; |
|||
} |
|||
|
|||
private static StringFieldProperties InvalidProperties() |
|||
{ |
|||
return new StringFieldProperties { MinLength = 20, MaxLength = 10 }; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,428 @@ |
|||
// ==========================================================================
|
|||
// 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 FakeItEasy; |
|||
using Squidex.Domain.Apps.Core.Schemas; |
|||
using Squidex.Domain.Apps.Entities.Schemas.Commands; |
|||
using Squidex.Domain.Apps.Entities.Schemas.State; |
|||
using Squidex.Domain.Apps.Entities.TestHelpers; |
|||
using Squidex.Domain.Apps.Events.Schemas; |
|||
using Squidex.Infrastructure; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Domain.Apps.Entities.Schemas |
|||
{ |
|||
public class SchemaGrainTests : HandlerTestBase<SchemaGrain, SchemaState> |
|||
{ |
|||
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
|||
private readonly FieldRegistry registry = new FieldRegistry(new TypeNameRegistry()); |
|||
private readonly string fieldName = "age"; |
|||
private readonly NamedId<long> fieldId; |
|||
private readonly SchemaGrain sut; |
|||
|
|||
protected override Guid Id |
|||
{ |
|||
get { return SchemaId; } |
|||
} |
|||
|
|||
public SchemaGrainTests() |
|||
{ |
|||
A.CallTo(() => appProvider.GetSchemaAsync(AppId, SchemaName)) |
|||
.Returns((ISchemaEntity)null); |
|||
|
|||
fieldId = new NamedId<long>(1, fieldName); |
|||
|
|||
sut = new SchemaGrain(Store, appProvider, registry); |
|||
sut.ActivateAsync(Id).Wait(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Command_should_throw_exception_if_rule_is_deleted() |
|||
{ |
|||
await ExecuteCreateAsync(); |
|||
await ExecuteDeleteAsync(); |
|||
|
|||
await Assert.ThrowsAsync<DomainException>(ExecutePublishAsync); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_schema_and_create_events() |
|||
{ |
|||
var properties = new SchemaProperties(); |
|||
|
|||
var command = new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties }; |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(EntityCreatedResult.Create(Id, 0)); |
|||
|
|||
Assert.Equal(AppId, sut.Snapshot.AppId.Id); |
|||
|
|||
Assert.Equal(SchemaName, sut.Snapshot.Name); |
|||
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaCreated { Name = SchemaName, Properties = properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_schema_with_initial_fields() |
|||
{ |
|||
var properties = new SchemaProperties(); |
|||
|
|||
var fields = new List<CreateSchemaField> |
|||
{ |
|||
new CreateSchemaField { Name = "field1", Properties = ValidProperties() }, |
|||
new CreateSchemaField { Name = "field2", Properties = ValidProperties() } |
|||
}; |
|||
|
|||
var command = new CreateSchema { Name = SchemaName, SchemaId = SchemaId, Properties = properties, Fields = fields }; |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(EntityCreatedResult.Create(Id, 0)); |
|||
|
|||
var @event = (SchemaCreated)LastEvents.Single().Payload; |
|||
|
|||
Assert.Equal(AppId, sut.Snapshot.AppId.Id); |
|||
Assert.Equal(SchemaName, sut.Snapshot.Name); |
|||
Assert.Equal(SchemaName, sut.Snapshot.SchemaDef.Name); |
|||
|
|||
Assert.Equal(2, @event.Fields.Count); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_create_events_and_update_state() |
|||
{ |
|||
var command = new UpdateSchema { Properties = new SchemaProperties() }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.Equal(command.Properties, sut.Snapshot.SchemaDef.Properties); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaUpdated { Properties = command.Properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ConfigureScripts_should_create_events() |
|||
{ |
|||
var command = new ConfigureScripts |
|||
{ |
|||
ScriptQuery = "<script-query>", |
|||
ScriptCreate = "<script-create>", |
|||
ScriptUpdate = "<script-update>", |
|||
ScriptDelete = "<script-delete>", |
|||
ScriptChange = "<script-change>" |
|||
}; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new ScriptsConfigured |
|||
{ |
|||
ScriptQuery = "<script-query>", |
|||
ScriptCreate = "<script-create>", |
|||
ScriptUpdate = "<script-update>", |
|||
ScriptDelete = "<script-delete>", |
|||
ScriptChange = "<script-change>" |
|||
}) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Reorder_should_create_events_and_update_state() |
|||
{ |
|||
var command = new ReorderFields { FieldIds = new List<long> { 1, 2 } }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync("field1"); |
|||
await ExecuteAddFieldAsync("field2"); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(3)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaFieldsReordered { FieldIds = command.FieldIds }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Publish_should_create_events_and_update_state() |
|||
{ |
|||
var command = new PublishSchema(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.True(sut.Snapshot.SchemaDef.IsPublished); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaPublished()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Unpublish_should_create_events_and_update_state() |
|||
{ |
|||
var command = new UnpublishSchema(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecutePublishAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.IsPublished); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaUnpublished()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Delete_should_create_events_and_update_state() |
|||
{ |
|||
var command = new DeleteSchema(); |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(1)); |
|||
|
|||
Assert.True(sut.Snapshot.IsDeleted); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new SchemaDeleted()) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Add_should_create_events_and_update_state() |
|||
{ |
|||
var command = new AddField { Name = fieldName, Properties = ValidProperties() }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(EntityCreatedResult.Create(1, 1)); |
|||
|
|||
Assert.Equal(command.Properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldAdded { Name = fieldName, FieldId = fieldId, Properties = command.Properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task UpdateField_should_create_events_and_update_state() |
|||
{ |
|||
var command = new UpdateField { FieldId = 1, Properties = new StringFieldProperties() }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync(fieldName); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.Equal(command.Properties, sut.Snapshot.SchemaDef.FieldsById[1].RawProperties); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldUpdated { FieldId = fieldId, Properties = command.Properties }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task LockField_should_create_events_and_update_state() |
|||
{ |
|||
var command = new LockField { FieldId = 1 }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync(fieldName); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldLocked { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task HideField_should_create_events_and_update_state() |
|||
{ |
|||
var command = new HideField { FieldId = 1 }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync(fieldName); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldHidden { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task ShowField_should_create_events_and_update_state() |
|||
{ |
|||
var command = new ShowField { FieldId = 1 }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync(fieldName); |
|||
await ExecuteHideFieldAsync(1); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(3)); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsHidden); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldShown { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DisableField_should_create_events_and_update_state() |
|||
{ |
|||
var command = new DisableField { FieldId = 1 }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync(fieldName); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.True(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldDisabled { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task EnableField_should_create_events_and_update_state() |
|||
{ |
|||
var command = new EnableField { FieldId = 1 }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync(fieldName); |
|||
await ExecuteDisableFieldAsync(1); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(3)); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById[1].IsDisabled); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldEnabled { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task DeleteField_should_create_events_and_update_state() |
|||
{ |
|||
var command = new DeleteField { FieldId = 1 }; |
|||
|
|||
await ExecuteCreateAsync(); |
|||
await ExecuteAddFieldAsync(fieldName); |
|||
|
|||
var result = await sut.ExecuteAsync(CreateCommand(command)); |
|||
|
|||
result.ShouldBeEquivalent(new EntitySavedResult(2)); |
|||
|
|||
Assert.False(sut.Snapshot.SchemaDef.FieldsById.ContainsKey(1)); |
|||
|
|||
LastEvents |
|||
.ShouldHaveSameEvents( |
|||
CreateEvent(new FieldDeleted { FieldId = fieldId }) |
|||
); |
|||
} |
|||
|
|||
private Task ExecuteCreateAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new CreateSchema { Name = SchemaName })); |
|||
} |
|||
|
|||
private Task ExecuteAddFieldAsync(string name) |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new AddField { Properties = ValidProperties(), Name = name })); |
|||
} |
|||
|
|||
private Task ExecuteHideFieldAsync(long id) |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new HideField { FieldId = id })); |
|||
} |
|||
|
|||
private Task ExecuteDisableFieldAsync(long id) |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new DisableField { FieldId = id })); |
|||
} |
|||
|
|||
private Task ExecutePublishAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new PublishSchema())); |
|||
} |
|||
|
|||
private Task ExecuteDeleteAsync() |
|||
{ |
|||
return sut.ExecuteAsync(CreateCommand(new DeleteSchema())); |
|||
} |
|||
|
|||
private static StringFieldProperties ValidProperties() |
|||
{ |
|||
return new StringFieldProperties { MinLength = 10, MaxLength = 20 }; |
|||
} |
|||
} |
|||
} |
|||
@ -1,284 +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 System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Infrastructure.Tasks; |
|||
using Squidex.Infrastructure.TestHelpers; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public class AggregateHandlerTests |
|||
{ |
|||
private readonly IServiceProvider serviceProvider = A.Fake<IServiceProvider>(); |
|||
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>(); |
|||
private readonly IStateFactory stateFactory = A.Fake<IStateFactory>(); |
|||
private readonly IPersistence<MyDomainState> persistence = A.Fake<IPersistence<MyDomainState>>(); |
|||
private readonly Envelope<IEvent> event1 = new Envelope<IEvent>(new MyEvent()); |
|||
private readonly Envelope<IEvent> event2 = new Envelope<IEvent>(new MyEvent()); |
|||
private readonly CommandContext context; |
|||
private readonly CommandContext invalidContext = new CommandContext(A.Dummy<ICommand>(), A.Dummy<ICommandBus>()); |
|||
private readonly Guid domainObjectId = Guid.NewGuid(); |
|||
private readonly MyCommand command; |
|||
private readonly MyDomainObject domainObject = new MyDomainObject(); |
|||
private readonly AggregateHandler sut; |
|||
|
|||
public AggregateHandlerTests() |
|||
{ |
|||
command = new MyCommand { AggregateId = domainObjectId, ExpectedVersion = EtagVersion.Any }; |
|||
context = new CommandContext(command, A.Dummy<ICommandBus>()); |
|||
|
|||
A.CallTo(() => store.WithSnapshotsAndEventSourcing(domainObjectId, A<Func<MyDomainState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored)) |
|||
.Returns(persistence); |
|||
|
|||
A.CallTo(() => stateFactory.CreateAsync<MyDomainObject>(domainObjectId)) |
|||
.Returns(Task.FromResult(domainObject)); |
|||
|
|||
A.CallTo(() => stateFactory.GetSingleAsync<MyDomainObject>(domainObjectId)) |
|||
.Returns(Task.FromResult(domainObject)); |
|||
|
|||
sut = new AggregateHandler(stateFactory, serviceProvider); |
|||
|
|||
domainObject.ActivateAsync(domainObjectId, store).Wait(); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Create_with_task_should_throw_exception_if_not_aggregate_command() |
|||
{ |
|||
return Assert.ThrowsAnyAsync<ArgumentException>(() => sut.CreateAsync<MyDomainObject>(invalidContext, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Create_synced_with_task_should_throw_exception_if_not_aggregate_command() |
|||
{ |
|||
return Assert.ThrowsAnyAsync<ArgumentException>(() => sut.CreateSyncedAsync<MyDomainObject>(invalidContext, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Create_with_task_should_should_throw_exception_if_version_is_wrong() |
|||
{ |
|||
command.ExpectedVersion = 2; |
|||
|
|||
return Assert.ThrowsAnyAsync<DomainObjectVersionException>(() => sut.CreateAsync<MyDomainObject>(context, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Create_synced_with_task_should_should_throw_exception_if_version_is_wrong() |
|||
{ |
|||
command.ExpectedVersion = 2; |
|||
|
|||
return Assert.ThrowsAnyAsync<DomainObjectVersionException>(() => sut.CreateSyncedAsync<MyDomainObject>(context, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_with_task_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.CreateAsync<MyDomainObject>(context, async x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
await Task.Yield(); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntityCreatedResult<Guid>>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_synced_with_task_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.CreateSyncedAsync<MyDomainObject>(context, async x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
await Task.Yield(); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(2, domainObject.Snapshot.Version); |
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntityCreatedResult<Guid>>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.CreateAsync<MyDomainObject>(context, x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(2, domainObject.Snapshot.Version); |
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntityCreatedResult<Guid>>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Create_synced_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.CreateSyncedAsync<MyDomainObject>(context, x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(2, domainObject.Snapshot.Version); |
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntityCreatedResult<Guid>>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Update_with_task_should_throw_exception_if_not_aggregate_command() |
|||
{ |
|||
return Assert.ThrowsAnyAsync<ArgumentException>(() => sut.UpdateAsync<MyDomainObject>(invalidContext, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Update_synced_with_task_should_throw_exception_if_not_aggregate_command() |
|||
{ |
|||
return Assert.ThrowsAnyAsync<ArgumentException>(() => sut.UpdateSyncedAsync<MyDomainObject>(invalidContext, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Update_with_task_should_should_throw_exception_if_version_is_wrong() |
|||
{ |
|||
command.ExpectedVersion = 2; |
|||
|
|||
return Assert.ThrowsAnyAsync<DomainObjectVersionException>(() => sut.UpdateAsync<MyDomainObject>(context, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public Task Update_synced_with_task_should_should_throw_exception_if_version_is_wrong() |
|||
{ |
|||
command.ExpectedVersion = 2; |
|||
|
|||
return Assert.ThrowsAnyAsync<DomainObjectVersionException>(() => sut.UpdateSyncedAsync<MyDomainObject>(context, x => TaskHelper.False)); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_with_task_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.UpdateAsync<MyDomainObject>(context, async x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
await Task.Yield(); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(2, domainObject.Snapshot.Version); |
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntitySavedResult>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_synced_with_task_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.UpdateSyncedAsync<MyDomainObject>(context, async x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
await Task.Yield(); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(2, domainObject.Snapshot.Version); |
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntitySavedResult>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.UpdateAsync<MyDomainObject>(context, x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(2, domainObject.Snapshot.Version); |
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntitySavedResult>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Update_synced_should_create_domain_object_and_save() |
|||
{ |
|||
MyDomainObject passedDomainObject = null; |
|||
|
|||
await sut.UpdateSyncedAsync<MyDomainObject>(context, x => |
|||
{ |
|||
x.RaiseEvent(new MyEvent()); |
|||
x.RaiseEvent(new MyEvent()); |
|||
|
|||
passedDomainObject = x; |
|||
}); |
|||
|
|||
Assert.Equal(2, domainObject.Snapshot.Version); |
|||
Assert.Equal(domainObject, passedDomainObject); |
|||
Assert.NotNull(context.Result<EntitySavedResult>()); |
|||
|
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.MustHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,88 +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 System.Linq; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Infrastructure.TestHelpers; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public class DomainObjectBaseTests |
|||
{ |
|||
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>(); |
|||
private readonly IPersistence<MyDomainState> persistence = A.Fake<IPersistence<MyDomainState>>(); |
|||
private readonly Guid id = Guid.NewGuid(); |
|||
private readonly MyDomainObject sut = new MyDomainObject(); |
|||
|
|||
public DomainObjectBaseTests() |
|||
{ |
|||
A.CallTo(() => store.WithSnapshotsAndEventSourcing(id, A<Func<MyDomainState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored)) |
|||
.Returns(persistence); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_instantiate() |
|||
{ |
|||
Assert.Equal(EtagVersion.Empty, sut.Version); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_write_state_and_events_when_saved() |
|||
{ |
|||
await sut.ActivateAsync(id, store); |
|||
|
|||
var event1 = new MyEvent(); |
|||
var event2 = new MyEvent(); |
|||
var newState = new MyDomainState(); |
|||
|
|||
sut.RaiseEvent(event1); |
|||
sut.RaiseEvent(event2); |
|||
sut.ApplySnapshot(newState); |
|||
|
|||
await sut.WriteAsync(); |
|||
|
|||
A.CallTo(() => persistence.WriteSnapshotAsync(newState)) |
|||
.MustHaveHappened(); |
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.That.Matches(x => x.Count() == 2))) |
|||
.MustHaveHappened(); |
|||
|
|||
Assert.Empty(sut.GetUncomittedEvents()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_ignore_exception_when_saving() |
|||
{ |
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.Ignored)) |
|||
.Throws(new InvalidOperationException()); |
|||
|
|||
await sut.ActivateAsync(id, store); |
|||
|
|||
var event1 = new MyEvent(); |
|||
var event2 = new MyEvent(); |
|||
var newState = new MyDomainState(); |
|||
|
|||
sut.RaiseEvent(event1); |
|||
sut.RaiseEvent(event2); |
|||
sut.ApplySnapshot(newState); |
|||
|
|||
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.WriteAsync()); |
|||
|
|||
A.CallTo(() => persistence.WriteSnapshotAsync(newState)) |
|||
.MustNotHaveHappened(); |
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.That.Matches(x => x.Count() == 2))) |
|||
.MustHaveHappened(); |
|||
|
|||
Assert.Empty(sut.GetUncomittedEvents()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,245 @@ |
|||
// ==========================================================================
|
|||
// 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 FakeItEasy; |
|||
using Squidex.Infrastructure.EventSourcing; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Infrastructure.TestHelpers; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public class DomainObjectGrainTests |
|||
{ |
|||
private readonly IStore<Guid> store = A.Fake<IStore<Guid>>(); |
|||
private readonly IPersistence<MyDomainState> persistence = A.Fake<IPersistence<MyDomainState>>(); |
|||
private readonly Guid id = Guid.NewGuid(); |
|||
private readonly MyGrain sut; |
|||
|
|||
public sealed class MyDomainState : IDomainState |
|||
{ |
|||
public long Version { get; set; } |
|||
|
|||
public int Value { get; set; } |
|||
} |
|||
|
|||
public sealed class ValueChanged : IEvent |
|||
{ |
|||
public int Value { get; set; } |
|||
} |
|||
|
|||
public sealed class CreateAuto : MyCommand |
|||
{ |
|||
public int Value { get; set; } |
|||
} |
|||
|
|||
public sealed class CreateCustom : MyCommand |
|||
{ |
|||
public int Value { get; set; } |
|||
} |
|||
|
|||
public sealed class UpdateAuto : MyCommand |
|||
{ |
|||
public int Value { get; set; } |
|||
} |
|||
|
|||
public sealed class UpdateCustom : MyCommand |
|||
{ |
|||
public int Value { get; set; } |
|||
} |
|||
|
|||
public sealed class MyGrain : DomainObjectGrain<MyDomainState> |
|||
{ |
|||
public MyGrain(IStore<Guid> store) |
|||
: base(store) |
|||
{ |
|||
} |
|||
|
|||
public override Task<object> ExecuteAsync(IAggregateCommand command) |
|||
{ |
|||
switch (command) |
|||
{ |
|||
case CreateAuto createAuto: |
|||
return CreateAsync(createAuto, c => |
|||
{ |
|||
RaiseEvent(new ValueChanged { Value = c.Value }); |
|||
}); |
|||
|
|||
case CreateCustom createCustom: |
|||
return CreateReturnAsync(createCustom, c => |
|||
{ |
|||
RaiseEvent(new ValueChanged { Value = c.Value }); |
|||
|
|||
return "CREATED"; |
|||
}); |
|||
|
|||
case UpdateAuto updateAuto: |
|||
return UpdateAsync(updateAuto, c => |
|||
{ |
|||
RaiseEvent(new ValueChanged { Value = c.Value }); |
|||
}); |
|||
|
|||
case UpdateCustom updateCustom: |
|||
return UpdateReturnAsync(updateCustom, c => |
|||
{ |
|||
RaiseEvent(new ValueChanged { Value = c.Value }); |
|||
|
|||
return "UPDATED"; |
|||
}); |
|||
} |
|||
|
|||
return Task.FromResult<object>(null); |
|||
} |
|||
|
|||
public override void ApplyEvent(Envelope<IEvent> @event) |
|||
{ |
|||
if (@event.Payload is ValueChanged valueChanged) |
|||
{ |
|||
ApplySnapshot(new MyDomainState { Value = valueChanged.Value }); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public DomainObjectGrainTests() |
|||
{ |
|||
A.CallTo(() => store.WithSnapshotsAndEventSourcing(typeof(MyGrain), id, A<Func<MyDomainState, Task>>.Ignored, A<Func<Envelope<IEvent>, Task>>.Ignored)) |
|||
.Returns(persistence); |
|||
|
|||
sut = new MyGrain(store); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Should_instantiate() |
|||
{ |
|||
Assert.Equal(EtagVersion.Empty, sut.Version); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_write_state_and_events_when_created() |
|||
{ |
|||
await SetupEmptyAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(new CreateAuto { Value = 5 }); |
|||
|
|||
A.CallTo(() => persistence.WriteSnapshotAsync(A<MyDomainState>.That.Matches(x => x.Value == 5))) |
|||
.MustHaveHappened(); |
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.That.Matches(x => x.Count() == 1))) |
|||
.MustHaveHappened(); |
|||
|
|||
Assert.True(result is EntityCreatedResult<Guid>); |
|||
|
|||
Assert.Empty(sut.GetUncomittedEvents()); |
|||
Assert.Equal(5, sut.Snapshot.Value); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_write_state_and_events_when_updated() |
|||
{ |
|||
await SetupCreatedAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(new UpdateAuto { Value = 5 }); |
|||
|
|||
A.CallTo(() => persistence.WriteSnapshotAsync(A<MyDomainState>.That.Matches(x => x.Value == 5))) |
|||
.MustHaveHappened(); |
|||
A.CallTo(() => persistence.WriteEventsAsync(A<IEnumerable<Envelope<IEvent>>>.That.Matches(x => x.Count() == 1))) |
|||
.MustHaveHappened(); |
|||
|
|||
Assert.True(result is EntitySavedResult); |
|||
|
|||
Assert.Empty(sut.GetUncomittedEvents()); |
|||
Assert.Equal(5, sut.Snapshot.Value); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_throw_exception_when_already_created() |
|||
{ |
|||
await SetupCreatedAsync(); |
|||
|
|||
await Assert.ThrowsAsync<DomainException>(() => sut.ExecuteAsync(new CreateAuto())); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_throw_exception_when_not_created() |
|||
{ |
|||
await SetupEmptyAsync(); |
|||
|
|||
await Assert.ThrowsAsync<DomainObjectNotFoundException>(() => sut.ExecuteAsync(new UpdateAuto())); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_return_custom_result_on_create() |
|||
{ |
|||
await SetupEmptyAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(new CreateCustom()); |
|||
|
|||
Assert.Equal("CREATED", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_return_custom_result_on_update() |
|||
{ |
|||
await SetupCreatedAsync(); |
|||
|
|||
var result = await sut.ExecuteAsync(new UpdateCustom()); |
|||
|
|||
Assert.Equal("UPDATED", result); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_throw_exception_when_other_verison_expected() |
|||
{ |
|||
await SetupCreatedAsync(); |
|||
|
|||
await Assert.ThrowsAsync<DomainObjectVersionException>(() => sut.ExecuteAsync(new UpdateCustom { ExpectedVersion = 3 })); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_reset_state_when_writing_snapshot_for_create_failed() |
|||
{ |
|||
await SetupEmptyAsync(); |
|||
|
|||
A.CallTo(() => persistence.WriteSnapshotAsync(A<MyDomainState>.Ignored)) |
|||
.Throws(new InvalidOperationException()); |
|||
|
|||
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.ExecuteAsync(new CreateAuto())); |
|||
|
|||
Assert.Empty(sut.GetUncomittedEvents()); |
|||
Assert.Equal(0, sut.Snapshot.Value); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_reset_state_when_writing_snapshot_for_update_failed() |
|||
{ |
|||
await SetupCreatedAsync(); |
|||
|
|||
A.CallTo(() => persistence.WriteSnapshotAsync(A<MyDomainState>.Ignored)) |
|||
.Throws(new InvalidOperationException()); |
|||
|
|||
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.ExecuteAsync(new UpdateAuto())); |
|||
|
|||
Assert.Empty(sut.GetUncomittedEvents()); |
|||
Assert.Equal(0, sut.Snapshot.Value); |
|||
} |
|||
|
|||
private async Task SetupCreatedAsync() |
|||
{ |
|||
await sut.ActivateAsync(id); |
|||
|
|||
await sut.ExecuteAsync(new CreateAuto()); |
|||
} |
|||
|
|||
private async Task SetupEmptyAsync() |
|||
{ |
|||
await sut.ActivateAsync(id); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Infrastructure.TestHelpers; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public class GrainCommandMiddlewareTests |
|||
{ |
|||
private readonly IStateFactory factory = A.Fake<IStateFactory>(); |
|||
private readonly MyGrain grain = A.Fake<MyGrain>(); |
|||
private readonly Guid id = Guid.NewGuid(); |
|||
private readonly GrainCommandMiddleware<MatchingCommand, MyGrain> sut; |
|||
|
|||
public class MatchingCommand : MyCommand |
|||
{ |
|||
} |
|||
|
|||
public GrainCommandMiddlewareTests() |
|||
{ |
|||
A.CallTo(() => factory.CreateAsync<MyGrain>(id)) |
|||
.Returns(grain); |
|||
|
|||
sut = new GrainCommandMiddleware<MatchingCommand, MyGrain>(factory); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_invoke_grain_when_command_is_correct() |
|||
{ |
|||
var command = new MatchingCommand { AggregateId = id }; |
|||
var context = new CommandContext(command, A.Fake<ICommandBus>()); |
|||
|
|||
A.CallTo(() => grain.ExecuteAsync(command)) |
|||
.Returns(100); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
Assert.Equal(100, context.Result<int>()); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_invoke_grain_when_command_is_not_correct() |
|||
{ |
|||
var command = new MyCommand { AggregateId = id }; |
|||
var context = new CommandContext(command, A.Fake<ICommandBus>()); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
Assert.Null(context.Result<object>()); |
|||
|
|||
A.CallTo(() => grain.ExecuteAsync(command)) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using FakeItEasy; |
|||
using Squidex.Infrastructure.States; |
|||
using Squidex.Infrastructure.TestHelpers; |
|||
using Xunit; |
|||
|
|||
namespace Squidex.Infrastructure.Commands |
|||
{ |
|||
public class SyncedGrainCommandMiddlewareTests |
|||
{ |
|||
private readonly IStateFactory factory = A.Fake<IStateFactory>(); |
|||
private readonly MyGrain grain = A.Fake<MyGrain>(); |
|||
private readonly Guid id = Guid.NewGuid(); |
|||
private readonly SyncedGrainCommandMiddleware<MatchingCommand, MyGrain> sut; |
|||
|
|||
public class MatchingCommand : MyCommand |
|||
{ |
|||
} |
|||
|
|||
public SyncedGrainCommandMiddlewareTests() |
|||
{ |
|||
A.CallTo(() => factory.GetSingleAsync<MyGrain>(id)) |
|||
.Returns(grain); |
|||
|
|||
sut = new SyncedGrainCommandMiddleware<MatchingCommand, MyGrain>(factory); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_invoke_grain_when_command_is_correct() |
|||
{ |
|||
var command = new MatchingCommand { AggregateId = id }; |
|||
var context = new CommandContext(command, A.Fake<ICommandBus>()); |
|||
|
|||
A.CallTo(() => grain.ExecuteAsync(command)) |
|||
.Returns(100); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
Assert.Equal(100, context.Result<int>()); |
|||
|
|||
A.CallTo(() => factory.Synchronize<MyGrain, Guid>(id)) |
|||
.MustHaveHappened(); |
|||
A.CallTo(() => factory.Remove<MyGrain, Guid>(id)) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_remove_grain_from_cache_when_failed() |
|||
{ |
|||
var command = new MatchingCommand { AggregateId = id }; |
|||
var context = new CommandContext(command, A.Fake<ICommandBus>()); |
|||
|
|||
A.CallTo(() => grain.ExecuteAsync(command)) |
|||
.Throws(new InvalidOperationException()); |
|||
|
|||
await Assert.ThrowsAsync<InvalidOperationException>(() => sut.HandleAsync(context)); |
|||
|
|||
A.CallTo(() => factory.Synchronize<MyGrain, Guid>(id)) |
|||
.MustNotHaveHappened(); |
|||
A.CallTo(() => factory.Remove<MyGrain, Guid>(id)) |
|||
.MustHaveHappened(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_not_invoke_grain_when_command_is_not_correct() |
|||
{ |
|||
var command = new MyCommand { AggregateId = id }; |
|||
var context = new CommandContext(command, A.Fake<ICommandBus>()); |
|||
|
|||
await sut.HandleAsync(context); |
|||
|
|||
Assert.Null(context.Result<object>()); |
|||
|
|||
A.CallTo(() => grain.ExecuteAsync(command)) |
|||
.MustNotHaveHappened(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,15 +0,0 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using Squidex.Infrastructure.Commands; |
|||
|
|||
namespace Squidex.Infrastructure.TestHelpers |
|||
{ |
|||
internal sealed class MyDomainObject : DomainObjectBase<MyDomainState> |
|||
{ |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
// ==========================================================================
|
|||
// Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
|||
// All rights reserved. Licensed under the MIT license.
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Squidex.Infrastructure.Commands; |
|||
using Squidex.Infrastructure.States; |
|||
|
|||
namespace Squidex.Infrastructure.TestHelpers |
|||
{ |
|||
public class MyGrain : DomainObjectGrain<MyDomainState> |
|||
{ |
|||
public MyGrain(IStore<Guid> store) |
|||
: base(store) |
|||
{ |
|||
} |
|||
|
|||
public override Task<object> ExecuteAsync(IAggregateCommand command) |
|||
{ |
|||
return Task.FromResult<object>(null); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue