mirror of https://github.com/Squidex/squidex.git
committed by
GitHub
81 changed files with 3881 additions and 5519 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