Browse Source

Adjustments to steps.

pull/379/head
Sebastian Stehle 7 years ago
parent
commit
60b126aef4
  1. 111
      src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs
  2. 32
      src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowStep.cs
  3. 23
      src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowTransition.cs
  4. 37
      src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs
  5. 32
      src/Squidex.Domain.Apps.Entities/AppProvider.cs
  6. 3
      src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs
  7. 4
      src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs
  8. 15
      src/Squidex.Domain.Apps.Entities/Contents/ContentEnricher.cs
  9. 9
      src/Squidex.Domain.Apps.Entities/Contents/DefaultContentWorkflow.cs
  10. 131
      src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs
  11. 2
      src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs
  12. 7
      src/Squidex.Domain.Apps.Entities/Contents/IContentWorkflow.cs
  13. 2
      src/Squidex.Domain.Apps.Entities/IAppProvider.cs
  14. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs
  15. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEnricherTests.cs
  16. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs
  17. 8
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs
  18. 14
      tests/Squidex.Domain.Apps.Entities.Tests/Contents/Guard/GuardContentTests.cs
  19. 14
      tests/Squidex.Domain.Apps.Entities.Tests/TestHelpers/HandlerTestBase.cs

111
src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs

@ -0,0 +1,111 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class Workflow
{
public static readonly Workflow Default = new Workflow(
new Dictionary<Status, WorkflowStep>
{
[Status.Archived] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>
{
[Status.Draft] = new WorkflowTransition()
},
StatusColors.Archived, true),
[Status.Draft] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>
{
[Status.Archived] = new WorkflowTransition(),
[Status.Published] = new WorkflowTransition()
},
StatusColors.Draft),
[Status.Published] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>
{
[Status.Archived] = new WorkflowTransition(),
[Status.Published] = new WorkflowTransition()
},
StatusColors.Archived)
}, Status.Draft);
public IReadOnlyDictionary<Status, WorkflowStep> Steps { get; }
public Status Initial { get; }
public Workflow(IReadOnlyDictionary<Status, WorkflowStep> steps, Status initial)
{
Guard.NotNull(steps, nameof(steps));
Steps = steps;
Initial = initial;
}
public static Workflow Create(IReadOnlyDictionary<Status, WorkflowStep> steps, Status initial)
{
Guard.NotNull(steps, nameof(steps));
foreach (var step in steps.Values)
{
foreach (var transition in step.Transitions)
{
if (steps.ContainsKey(transition.Key))
{
throw new ArgumentException("Transitions ends to an unknown step.", nameof(initial));
}
}
}
if (steps.ContainsKey(initial))
{
throw new ArgumentException("Initial step not known.", nameof(initial));
}
return new Workflow(steps, initial);
}
public IEnumerable<(Status Status, WorkflowStep Step, WorkflowTransition Transition)> GetTransitions(Status status)
{
if (TryGetStep(status, out var step))
{
foreach (var transition in step.Transitions)
{
yield return (transition.Key, Steps[transition.Key], transition.Value);
}
}
}
public WorkflowTransition GetTransition(Status from, Status to)
{
if (TryGetStep(from, out var step) && step.Transitions.TryGetValue(to, out var transition))
{
return transition;
}
return null;
}
public bool TryGetStep(Status status, out WorkflowStep step)
{
return Steps.TryGetValue(status, out step);
}
public (Status Key, WorkflowStep) GetInitialStep()
{
return (Initial, Steps[Initial]);
}
}
}

32
src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowStep.cs

@ -0,0 +1,32 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class WorkflowStep
{
public IReadOnlyDictionary<Status, WorkflowTransition> Transitions { get; }
public string Color { get; }
public bool NoUpdate { get; }
public WorkflowStep(IReadOnlyDictionary<Status, WorkflowTransition> transitions, string color, bool noUpdate = false)
{
Guard.NotNull(transitions, nameof(transitions));
Transitions = transitions;
Color = color;
NoUpdate = noUpdate;
}
}
}

23
src/Squidex.Domain.Apps.Core.Model/Contents/WorkflowTransition.cs

@ -0,0 +1,23 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschränkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class WorkflowTransition
{
public string Expression { get; }
public string Role { get; }
public WorkflowTransition(string expression = null, string role = null)
{
Expression = expression;
Role = role;
}
}
}

37
src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs

@ -0,0 +1,37 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Squidex.Infrastructure;
using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class Workflows : ArrayDictionary<Guid, Workflows>
{
public static readonly Workflows Empty = new Workflows();
private Workflows()
{
}
public Workflows(KeyValuePair<Guid, Workflows>[] items)
: base(items)
{
}
[Pure]
public Workflows Set(Workflow workflow)
{
Guard.NotNull(workflow, nameof(workflow));
return new Workflows(With(Guid.Empty, workflow));
}
}
}

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

@ -65,6 +65,17 @@ namespace Squidex.Domain.Apps.Entities
});
}
public Task<IAppEntity> GetAppAsync(Guid appId)
{
return localCache.GetOrCreateAsync($"GetAppAsync({appId})", async () =>
{
using (Profiler.TraceMethod<AppProvider>())
{
return await GetAppByIdAsync(appId);
}
});
}
public Task<IAppEntity> GetAppAsync(string appName)
{
return localCache.GetOrCreateAsync($"GetAppAsync({appName})", async () =>
@ -78,14 +89,7 @@ namespace Squidex.Domain.Apps.Entities
return null;
}
var app = await grainFactory.GetGrain<IAppGrain>(appId).GetStateAsync();
if (!IsExisting(app))
{
return null;
}
return app.Value;
return await GetAppByIdAsync(appId);
}
});
}
@ -184,6 +188,18 @@ namespace Squidex.Domain.Apps.Entities
});
}
private async Task<IAppEntity> GetAppByIdAsync(Guid appId)
{
var app = await grainFactory.GetGrain<IAppGrain>(appId).GetStateAsync();
if (!IsExisting(app))
{
return null;
}
return app.Value;
}
private async Task<List<Guid>> GetAppIdsByUserAsync(string userId)
{
using (Profiler.TraceMethod<AppProvider>())

3
src/Squidex.Domain.Apps.Entities/Apps/IAppEntity.cs

@ -6,6 +6,7 @@
// ==========================================================================
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Entities.Apps
{
@ -29,6 +30,8 @@ namespace Squidex.Domain.Apps.Entities.Apps
LanguagesConfig LanguagesConfig { get; }
Workflows Workflows { get; }
bool IsArchived { get; }
}
}

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

@ -7,6 +7,7 @@
using System.Runtime.Serialization;
using Squidex.Domain.Apps.Core.Apps;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure.Dispatching;
@ -42,6 +43,9 @@ namespace Squidex.Domain.Apps.Entities.Apps.State
[DataMember]
public LanguagesConfig LanguagesConfig { get; set; } = LanguagesConfig.English;
[DataMember]
public Workflows Workdlows { get; set; } = Workflows.Empty;
[DataMember]
public bool IsArchived { get; set; }

15
src/Squidex.Domain.Apps.Entities/Contents/ContentEnricher.cs

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure;
@ -67,26 +68,26 @@ namespace Squidex.Domain.Apps.Entities.Contents
private async Task ResolveNextsAsync(IContentEntity content, ContentEntity result)
{
result.Nexts = await contentWorkflow.GetNextsAsync(content);
result.Nexts = await contentWorkflow.GetNextsAsync(content, ClaimsPrincipal.Current);
}
private async Task ResolveColorAsync(IContentEntity content, ContentEntity result, Dictionary<(Guid, Status), StatusInfo> cache)
{
result.StatusColor = await GetColorAsync(content.SchemaId, content.Status, cache);
result.StatusColor = await GetColorAsync(content, cache);
}
private async Task<string> GetColorAsync(NamedId<Guid> schemaId, Status status, Dictionary<(Guid, Status), StatusInfo> cache)
private async Task<string> GetColorAsync(IContentEntity content, Dictionary<(Guid, Status), StatusInfo> cache)
{
if (!cache.TryGetValue((schemaId.Id, status), out var info))
if (!cache.TryGetValue((content.SchemaId.Id, content.Status), out var info))
{
info = await contentWorkflow.GetInfoAsync(status);
info = await contentWorkflow.GetInfoAsync(content);
if (info == null)
{
info = new StatusInfo(status, DefaultColor);
info = new StatusInfo(content.Status, DefaultColor);
}
cache[(schemaId.Id, status)] = info;
cache[(content.SchemaId.Id, content.Status)] = info;
}
return info.Color;

9
src/Squidex.Domain.Apps.Entities/Contents/DefaultContentWorkflow.cs

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
@ -53,7 +54,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
return Task.FromResult(result);
}
public Task<bool> CanMoveToAsync(IContentEntity content, Status next)
public Task<bool> CanMoveToAsync(IContentEntity content, Status next, ClaimsPrincipal user)
{
var result = Flow.TryGetValue(content.Status, out var step) && step.Transitions.Any(x => x.Status == next);
@ -67,14 +68,14 @@ namespace Squidex.Domain.Apps.Entities.Contents
return Task.FromResult(result);
}
public Task<StatusInfo> GetInfoAsync(Status status)
public Task<StatusInfo> GetInfoAsync(IContentEntity content)
{
var result = Flow[status].Info;
var result = Flow[content.Status].Info;
return Task.FromResult(result);
}
public Task<StatusInfo[]> GetNextsAsync(IContentEntity content)
public Task<StatusInfo[]> GetNextsAsync(IContentEntity content, ClaimsPrincipal user)
{
var result = Flow.TryGetValue(content.Status, out var step) ? step.Transitions : Array.Empty<StatusInfo>();

131
src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs

@ -0,0 +1,131 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Core.Scripting;
using Squidex.Domain.Apps.Entities.Schemas;
using Squidex.Infrastructure;
namespace Squidex.Domain.Apps.Entities.Contents
{
public sealed class DynamicContentWorkflow : IContentWorkflow
{
private readonly IScriptEngine scriptEngine;
private readonly IAppProvider appProvider;
public DynamicContentWorkflow(IScriptEngine scriptEngine, IAppProvider appProvider)
{
Guard.NotNull(scriptEngine, nameof(scriptEngine));
Guard.NotNull(appProvider, nameof(appProvider));
this.scriptEngine = scriptEngine;
this.appProvider = appProvider;
}
public async Task<StatusInfo[]> GetAllAsync(ISchemaEntity schema)
{
var workflow = await GetWorkflowAsync(schema.AppId.Id);
return workflow.Steps.Select(x => new StatusInfo(x.Key, GetColor(x.Value))).ToArray();
}
public async Task<bool> CanMoveToAsync(IContentEntity content, Status next, ClaimsPrincipal user)
{
var workflow = await GetWorkflowAsync(content.AppId.Id);
var transition = workflow.GetTransition(content.Status, next);
return transition != null && CanUse(transition, content, user);
}
public async Task<bool> CanUpdateAsync(IContentEntity content)
{
var workflow = await GetWorkflowAsync(content.AppId.Id);
if (workflow.TryGetStep(content.Status, out var step))
{
return !step.NoUpdate;
}
return true;
}
public async Task<StatusInfo> GetInfoAsync(IContentEntity content)
{
var workflow = await GetWorkflowAsync(content.AppId.Id);
if (workflow.TryGetStep(content.Status, out var step))
{
return new StatusInfo(content.Status, GetColor(step));
}
return new StatusInfo(content.Status, StatusColors.Draft);
}
public async Task<StatusInfo> GetInitialStatusAsync(ISchemaEntity schema)
{
var workflow = await GetWorkflowAsync(schema.AppId.Id);
var (status, step) = workflow.GetInitialStep();
return new StatusInfo(status, GetColor(step));
}
public async Task<StatusInfo[]> GetNextsAsync(IContentEntity content, ClaimsPrincipal user)
{
var result = new List<StatusInfo>();
var workflow = await GetWorkflowAsync(content.AppId.Id);
foreach (var (to, step, transition) in workflow.GetTransitions(content.Status))
{
if (CanUse(transition, content, user))
{
result.Add(new StatusInfo(to, GetColor(step)));
}
}
return result.ToArray();
}
private bool CanUse(WorkflowTransition transition, IContentEntity content, ClaimsPrincipal user)
{
if (!string.IsNullOrWhiteSpace(transition.Role))
{
if (!user.Claims.Any(x => x.Type == ClaimTypes.Role && x.Value == transition.Role))
{
return false;
}
}
if (!string.IsNullOrWhiteSpace(transition.Expression))
{
return scriptEngine.Evaluate("data", content.DataDraft, transition.Expression);
}
return true;
}
private async Task<Workflow> GetWorkflowAsync(Guid appId)
{
var app = await appProvider.GetAppAsync(appId);
return app?.Workflows.Values?.FirstOrDefault() ?? Workflow.Default;
}
private static string GetColor(WorkflowStep step)
{
return step.Color ?? StatusColors.Draft;
}
}
}

2
src/Squidex.Domain.Apps.Entities/Contents/Guards/GuardContent.cs

@ -76,7 +76,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guards
return Validate.It(() => "Cannot change status.", async e =>
{
if (!await contentWorkflow.CanMoveToAsync(content, command.Status))
if (!await contentWorkflow.CanMoveToAsync(content, command.Status, command.User))
{
if (content.Status == command.Status && content.Status == Status.Published)
{

7
src/Squidex.Domain.Apps.Entities/Contents/IContentWorkflow.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Security.Claims;
using System.Threading.Tasks;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Schemas;
@ -15,13 +16,13 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
Task<StatusInfo> GetInitialStatusAsync(ISchemaEntity schema);
Task<bool> CanMoveToAsync(IContentEntity content, Status next);
Task<bool> CanMoveToAsync(IContentEntity content, Status next, ClaimsPrincipal user);
Task<bool> CanUpdateAsync(IContentEntity content);
Task<StatusInfo> GetInfoAsync(Status status);
Task<StatusInfo> GetInfoAsync(IContentEntity content);
Task<StatusInfo[]> GetNextsAsync(IContentEntity content);
Task<StatusInfo[]> GetNextsAsync(IContentEntity content, ClaimsPrincipal user);
Task<StatusInfo[]> GetAllAsync(ISchemaEntity schema);
}

2
src/Squidex.Domain.Apps.Entities/IAppProvider.cs

@ -19,6 +19,8 @@ namespace Squidex.Domain.Apps.Entities
{
Task<(IAppEntity, ISchemaEntity)> GetAppWithSchemaAsync(Guid appId, Guid id);
Task<IAppEntity> GetAppAsync(Guid appId);
Task<IAppEntity> GetAppAsync(string appName);
Task<ISchemaEntity> GetSchemaAsync(Guid appId, Guid id, bool allowDeleted = false);

14
tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs

@ -80,7 +80,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact]
public async Task Create_should_create_events_and_update_state()
{
var command = new CreateApp { Name = AppName, Actor = User, AppId = AppId };
var command = new CreateApp { Name = AppName, Actor = Actor, AppId = AppId };
var result = await sut.ExecuteAsync(CreateCommand(command));
@ -91,7 +91,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new AppCreated { Name = AppName }),
CreateEvent(new AppContributorAssigned { ContributorId = User.Identifier, Role = Role.Owner }),
CreateEvent(new AppContributorAssigned { ContributorId = Actor.Identifier, Role = Role.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]*" })
@ -103,7 +103,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
var command = new ChangePlan { PlanId = planIdPaid };
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(Actor.Identifier, AppNamedId, planIdPaid))
.Returns(new PlanChangedResult());
await ExecuteCreateAsync();
@ -125,10 +125,10 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
var command = new ChangePlan { PlanId = planIdFree };
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(Actor.Identifier, AppNamedId, planIdPaid))
.Returns(new PlanChangedResult());
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdFree))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(Actor.Identifier, AppNamedId, planIdFree))
.Returns(new PlanResetResult());
await ExecuteCreateAsync();
@ -151,7 +151,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
{
var command = new ChangePlan { PlanId = planIdPaid };
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(Actor.Identifier, AppNamedId, planIdPaid))
.Returns(new RedirectToCheckoutResult(new Uri("http://squidex.io")));
await ExecuteCreateAsync();
@ -174,7 +174,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
result.ShouldBeEquivalent(new EntitySavedResult(5));
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(User.Identifier, AppNamedId, planIdPaid))
A.CallTo(() => appPlansBillingManager.ChangePlanAsync(Actor.Identifier, AppNamedId, planIdPaid))
.MustNotHaveHappened();
}

8
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentEnricherTests.cs

@ -30,7 +30,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
var source = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
A.CallTo(() => workflow.GetInfoAsync(Status.Published))
A.CallTo(() => workflow.GetInfoAsync(source))
.Returns(new StatusInfo(Status.Published, StatusColors.Published));
var result = await sut.EnrichAsync(source);
@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
var source = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
A.CallTo(() => workflow.GetInfoAsync(Status.Published))
A.CallTo(() => workflow.GetInfoAsync(source))
.Returns(Task.FromResult<StatusInfo>(null));
var result = await sut.EnrichAsync(source);
@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
var source1 = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
var source2 = new ContentEntity { Status = Status.Published, SchemaId = schemaId };
A.CallTo(() => workflow.GetInfoAsync(Status.Published))
A.CallTo(() => workflow.GetInfoAsync(source1))
.Returns(new StatusInfo(Status.Published, StatusColors.Published));
var result = await sut.EnrichAsync(new[] { source1, source2 });
@ -78,7 +78,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
Assert.Equal(StatusColors.Published, result[0].StatusColor);
Assert.Equal(StatusColors.Published, result[1].StatusColor);
A.CallTo(() => workflow.GetInfoAsync(Status.Published))
A.CallTo(() => workflow.GetInfoAsync(A<IContentEntity>.Ignored))
.MustHaveHappenedOnceExactly();
}
}

2
tests/Squidex.Domain.Apps.Entities.Tests/Contents/ContentGrainTests.cs

@ -448,7 +448,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
var command = new ChangeContentStatus { Status = Status.Published, JobId = sut.Snapshot.ScheduleJob.Id };
A.CallTo(() => contentWorkflow.CanMoveToAsync(A<IContentEntity>.Ignored, Status.Published))
A.CallTo(() => contentWorkflow.CanMoveToAsync(A<IContentEntity>.Ignored, Status.Published, User))
.Returns(false);
var result = await sut.ExecuteAsync(CreateContentCommand(command));

8
tests/Squidex.Domain.Apps.Entities.Tests/Contents/DefaultContentWorkflowTests.cs

@ -31,7 +31,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
var content = new ContentEntity { Status = Status.Published };
var result = await sut.CanMoveToAsync(content, Status.Draft);
var result = await sut.CanMoveToAsync(content, Status.Draft, null);
Assert.True(result);
}
@ -77,7 +77,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
new StatusInfo(Status.Published, StatusColors.Published)
};
var result = await sut.GetNextsAsync(content);
var result = await sut.GetNextsAsync(content, null);
result.Should().BeEquivalentTo(expected);
}
@ -92,7 +92,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
new StatusInfo(Status.Draft, StatusColors.Draft)
};
var result = await sut.GetNextsAsync(content);
var result = await sut.GetNextsAsync(content, null);
result.Should().BeEquivalentTo(expected);
}
@ -108,7 +108,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
new StatusInfo(Status.Draft, StatusColors.Draft)
};
var result = await sut.GetNextsAsync(content);
var result = await sut.GetNextsAsync(content, null);
result.Should().BeEquivalentTo(expected);
}

14
tests/Squidex.Domain.Apps.Entities.Tests/Contents/Guard/GuardContentTests.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Security.Claims;
using System.Threading.Tasks;
using FakeItEasy;
using NodaTime;
@ -23,6 +24,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
{
private readonly ISchemaEntity schema = A.Fake<ISchemaEntity>();
private readonly IContentWorkflow contentWorkflow = A.Fake<IContentWorkflow>();
private readonly ClaimsPrincipal user = new ClaimsPrincipal();
private readonly Instant dueTimeInPast = SystemClock.Instance.GetCurrentInstant().Minus(Duration.FromHours(1));
[Fact]
@ -180,9 +182,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
SetupSingleton(false);
var content = CreateContent(Status.Draft, false);
var command = new ChangeContentStatus { Status = Status.Published, DueTime = dueTimeInPast };
var command = new ChangeContentStatus { Status = Status.Published, DueTime = dueTimeInPast, User = user };
A.CallTo(() => contentWorkflow.CanMoveToAsync(content, command.Status))
A.CallTo(() => contentWorkflow.CanMoveToAsync(content, command.Status, user))
.Returns(true);
await ValidationAssert.ThrowsAsync(() => GuardContent.CanChangeStatus(schema, content, contentWorkflow, command),
@ -195,9 +197,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
SetupSingleton(false);
var content = CreateContent(Status.Draft, false);
var command = new ChangeContentStatus { Status = Status.Published };
var command = new ChangeContentStatus { Status = Status.Published, User = user };
A.CallTo(() => contentWorkflow.CanMoveToAsync(content, command.Status))
A.CallTo(() => contentWorkflow.CanMoveToAsync(content, command.Status, user))
.Returns(false);
await ValidationAssert.ThrowsAsync(() => GuardContent.CanChangeStatus(schema, content, contentWorkflow, command),
@ -210,9 +212,9 @@ namespace Squidex.Domain.Apps.Entities.Contents.Guard
SetupSingleton(false);
var content = CreateContent(Status.Draft, false);
var command = new ChangeContentStatus { Status = Status.Published };
var command = new ChangeContentStatus { Status = Status.Published, User = user };
A.CallTo(() => contentWorkflow.CanMoveToAsync(content, command.Status))
A.CallTo(() => contentWorkflow.CanMoveToAsync(content, command.Status, user))
.Returns(true);
await GuardContent.CanChangeStatus(schema, content, contentWorkflow, command);

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

@ -8,6 +8,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using FakeItEasy;
using Squidex.Domain.Apps.Events;
using Squidex.Infrastructure;
@ -26,7 +27,7 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
private readonly IPersistence<TState> persistence1 = A.Fake<IPersistence<TState>>();
private readonly IPersistence persistence2 = A.Fake<IPersistence>();
protected RefToken User { get; } = new RefToken(RefTokenType.Subject, Guid.NewGuid().ToString());
protected RefToken Actor { get; } = new RefToken(RefTokenType.Subject, Guid.NewGuid().ToString());
protected Guid AppId { get; } = Guid.NewGuid();
@ -36,6 +37,8 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
protected string SchemaName { get; } = "my-schema";
protected ClaimsPrincipal User { get; } = new ClaimsPrincipal();
protected NamedId<Guid> AppNamedId
{
get { return NamedId.Of(AppId, AppName); }
@ -87,7 +90,12 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
if (command.Actor == null)
{
command.Actor = User;
command.Actor = Actor;
}
if (command.User == null)
{
command.User = User;
}
if (command is IAppCommand appCommand && appCommand.AppId == null)
@ -110,7 +118,7 @@ namespace Squidex.Domain.Apps.Entities.TestHelpers
protected TEvent CreateEvent<TEvent>(TEvent @event) where TEvent : SquidexEvent
{
@event.Actor = User;
@event.Actor = Actor;
EnrichAppInfo(@event);
EnrichSchemaInfo(@event);

Loading…
Cancel
Save