Browse Source

Workflow management started.

pull/382/head
Sebastian Stehle 7 years ago
parent
commit
d903691702
  1. 42
      src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs
  2. 14
      src/Squidex.Domain.Apps.Entities/Apps/Commands/AddWorkflow.cs
  3. 16
      src/Squidex.Domain.Apps.Entities/Apps/Commands/DeleteWorkflow.cs
  4. 5
      src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateWorkflow.cs
  5. 13
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppWorkflows.cs
  6. 17
      src/Squidex.Domain.Apps.Events/Apps/AppWorkflowAdded.cs
  7. 18
      src/Squidex.Domain.Apps.Events/Apps/AppWorkflowDeleted.cs
  8. 7
      src/Squidex.Domain.Apps.Events/Apps/AppWorkflowUpdated.cs
  9. 4
      src/Squidex/Areas/Api/Controllers/Apps/Models/UpsertWorkflowDto.cs
  10. 2
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs
  11. 32
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppWorkflowTests.cs
  12. 29
      tools/Migrate_01/OldEvents/AppWorkflowConfigured.cs

42
src/Squidex.Domain.Apps.Entities/Apps/AppGrain.cs

@ -119,12 +119,32 @@ namespace Squidex.Domain.Apps.Entities.Apps
return Snapshot; return Snapshot;
}); });
case ConfigureWorkflow configureWorkflow: case AddWorkflow addWorkflow:
return UpdateReturn(configureWorkflow, c => return UpdateReturn(addWorkflow, c =>
{ {
GuardAppWorkflows.CanConfigure(c); GuardAppWorkflows.CanAdd(c);
ConfigureWorkflow(c); AddWorkflow(c);
return Snapshot;
});
case UpdateWorkflow updateWorkflow:
return UpdateReturn(updateWorkflow, c =>
{
GuardAppWorkflows.CanUpdate(Snapshot.Workflows, c);
UpdateWorkflow(c);
return Snapshot;
});
case DeleteWorkflow deleteWorkflow:
return UpdateReturn(deleteWorkflow, c =>
{
GuardAppWorkflows.CanDelete(Snapshot.Workflows, c);
DeleteWorkflow(c);
return Snapshot; return Snapshot;
}); });
@ -329,9 +349,19 @@ namespace Squidex.Domain.Apps.Entities.Apps
RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked())); RaiseEvent(SimpleMapper.Map(command, new AppClientRevoked()));
} }
public void ConfigureWorkflow(ConfigureWorkflow command) public void AddWorkflow(AddWorkflow command)
{
RaiseEvent(SimpleMapper.Map(command, new AppWorkflowAdded()));
}
public void UpdateWorkflow(UpdateWorkflow command)
{
RaiseEvent(SimpleMapper.Map(command, new AppWorkflowUpdated()));
}
public void DeleteWorkflow(DeleteWorkflow command)
{ {
RaiseEvent(SimpleMapper.Map(command, new AppWorkflowConfigured())); RaiseEvent(SimpleMapper.Map(command, new AppWorkflowDeleted()));
} }
public void AddLanguage(AddLanguage command) public void AddLanguage(AddLanguage command)

14
src/Squidex.Domain.Apps.Entities/Apps/Commands/AddWorkflow.cs

@ -0,0 +1,14 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class AddWorkflow : AppCommand
{
public string Name { get; set; }
}
}

16
src/Squidex.Domain.Apps.Entities/Apps/Commands/DeleteWorkflow.cs

@ -0,0 +1,16 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class DeleteWorkflow : AppCommand
{
public Guid WorkflowId { get; set; }
}
}

5
src/Squidex.Domain.Apps.Entities/Apps/Commands/ConfigureWorkflow.cs → src/Squidex.Domain.Apps.Entities/Apps/Commands/UpdateWorkflow.cs

@ -5,12 +5,15 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
namespace Squidex.Domain.Apps.Entities.Apps.Commands namespace Squidex.Domain.Apps.Entities.Apps.Commands
{ {
public sealed class ConfigureWorkflow : AppCommand public sealed class UpdateWorkflow : AppCommand
{ {
public Guid WorkflowId { get; set; }
public Workflow Workflow { get; set; } public Workflow Workflow { get; set; }
} }
} }

13
src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppWorkflows.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Apps.Commands; using Squidex.Domain.Apps.Entities.Apps.Commands;
using Squidex.Infrastructure; using Squidex.Infrastructure;
@ -13,7 +14,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{ {
public static class GuardAppWorkflows public static class GuardAppWorkflows
{ {
public static void CanConfigure(ConfigureWorkflow command) public static void CanUpdate(Workflows workflows, UpdateWorkflow command)
{ {
Guard.NotNull(command, nameof(command)); Guard.NotNull(command, nameof(command));
@ -72,5 +73,15 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
} }
}); });
} }
internal static void CanAdd(AddWorkflow c)
{
throw new NotImplementedException();
}
internal static void CanDelete(Workflows workflows, DeleteWorkflow c)
{
throw new NotImplementedException();
}
} }
} }

17
src/Squidex.Domain.Apps.Events/Apps/AppWorkflowAdded.cs

@ -0,0 +1,17 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Apps
{
[EventType(nameof(AppWorkflowAdded))]
public sealed class AppWorkflowAdded : AppEvent
{
public string Name { get; set; }
}
}

18
src/Squidex.Domain.Apps.Events/Apps/AppWorkflowDeleted.cs

@ -0,0 +1,18 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Apps
{
[EventType(nameof(AppWorkflowDeleted))]
public sealed class AppWorkflowDeleted : AppEvent
{
public Guid WorkflowId { get; set; }
}
}

7
src/Squidex.Domain.Apps.Events/Apps/AppWorkflowConfigured.cs → src/Squidex.Domain.Apps.Events/Apps/AppWorkflowUpdated.cs

@ -5,14 +5,17 @@
// All rights reserved. Licensed under the MIT license. // All rights reserved. Licensed under the MIT license.
// ========================================================================== // ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Contents; using Squidex.Domain.Apps.Core.Contents;
using Squidex.Infrastructure.EventSourcing; using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Apps namespace Squidex.Domain.Apps.Events.Apps
{ {
[EventType(nameof(AppWorkflowConfigured))] [EventType(nameof(AppWorkflowUpdated))]
public sealed class AppWorkflowConfigured : AppEvent public sealed class AppWorkflowUpdated : AppEvent
{ {
public Guid WorkflowId { get; set; }
public Workflow Workflow { get; set; } public Workflow Workflow { get; set; }
} }
} }

4
src/Squidex/Areas/Api/Controllers/Apps/Models/UpsertWorkflowDto.cs

@ -26,7 +26,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
/// </summary> /// </summary>
public Status Initial { get; set; } public Status Initial { get; set; }
public ConfigureWorkflow ToCommand() public UpdateWorkflow ToCommand()
{ {
var workflow = new Workflow( var workflow = new Workflow(
Steps?.ToDictionary( Steps?.ToDictionary(
@ -39,7 +39,7 @@ namespace Squidex.Areas.Api.Controllers.Apps.Models
x.Value.NoUpdate)), x.Value.NoUpdate)),
Initial); Initial);
return new ConfigureWorkflow { Workflow = workflow }; return new UpdateWorkflow { Workflow = workflow };
} }
} }
} }

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

@ -301,7 +301,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
[Fact] [Fact]
public async Task ConfigureWorkflow_should_create_events_and_update_state() public async Task ConfigureWorkflow_should_create_events_and_update_state()
{ {
var command = new ConfigureWorkflow { Workflow = Workflow.Default }; var command = new UpdateWorkflow { Workflow = Workflow.Default };
await ExecuteCreateAsync(); await ExecuteCreateAsync();

32
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppWorkflowTests.cs

@ -19,16 +19,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
[Fact] [Fact]
public void CanConfigure_should_throw_exception_if_workflow_is_not_defined() public void CanConfigure_should_throw_exception_if_workflow_is_not_defined()
{ {
var command = new ConfigureWorkflow(); var command = new UpdateWorkflow();
ValidationAssert.Throws(() => GuardAppWorkflows.CanConfigure(command), ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
new ValidationError("Workflow is required.", "Workflow")); new ValidationError("Workflow is required.", "Workflow"));
} }
[Fact] [Fact]
public void CanConfigure_should_throw_exception_if_workflow_has_no_initial_step() public void CanConfigure_should_throw_exception_if_workflow_has_no_initial_step()
{ {
var command = new ConfigureWorkflow var command = new UpdateWorkflow
{ {
Workflow = new Workflow( Workflow = new Workflow(
new Dictionary<Status, WorkflowStep> new Dictionary<Status, WorkflowStep>
@ -38,14 +38,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
default) default)
}; };
ValidationAssert.Throws(() => GuardAppWorkflows.CanConfigure(command), ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
new ValidationError("Initial step is required.", "Workflow.Initial")); new ValidationError("Initial step is required.", "Workflow.Initial"));
} }
[Fact] [Fact]
public void CanConfigure_should_throw_exception_if_initial_step_is_published() public void CanConfigure_should_throw_exception_if_initial_step_is_published()
{ {
var command = new ConfigureWorkflow var command = new UpdateWorkflow
{ {
Workflow = new Workflow( Workflow = new Workflow(
new Dictionary<Status, WorkflowStep> new Dictionary<Status, WorkflowStep>
@ -55,14 +55,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
Status.Published) Status.Published)
}; };
ValidationAssert.Throws(() => GuardAppWorkflows.CanConfigure(command), ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
new ValidationError("Initial step cannot be published step.", "Workflow.Initial")); new ValidationError("Initial step cannot be published step.", "Workflow.Initial"));
} }
[Fact] [Fact]
public void CanConfigure_should_throw_exception_if_workflow_does_not_have_published_state() public void CanConfigure_should_throw_exception_if_workflow_does_not_have_published_state()
{ {
var command = new ConfigureWorkflow var command = new UpdateWorkflow
{ {
Workflow = new Workflow( Workflow = new Workflow(
new Dictionary<Status, WorkflowStep> new Dictionary<Status, WorkflowStep>
@ -72,14 +72,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
Status.Draft) Status.Draft)
}; };
ValidationAssert.Throws(() => GuardAppWorkflows.CanConfigure(command), ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
new ValidationError("Workflow must have a published step.", "Workflow.Steps")); new ValidationError("Workflow must have a published step.", "Workflow.Steps"));
} }
[Fact] [Fact]
public void CanConfigure_should_throw_exception_if_workflow_step_is_not_defined() public void CanConfigure_should_throw_exception_if_workflow_step_is_not_defined()
{ {
var command = new ConfigureWorkflow var command = new UpdateWorkflow
{ {
Workflow = new Workflow( Workflow = new Workflow(
new Dictionary<Status, WorkflowStep> new Dictionary<Status, WorkflowStep>
@ -90,14 +90,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
Status.Draft) Status.Draft)
}; };
ValidationAssert.Throws(() => GuardAppWorkflows.CanConfigure(command), ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
new ValidationError("Step is required.", "Workflow.Steps.Published")); new ValidationError("Step is required.", "Workflow.Steps.Published"));
} }
[Fact] [Fact]
public void CanConfigure_should_throw_exception_if_workflow_transition_is_invalid() public void CanConfigure_should_throw_exception_if_workflow_transition_is_invalid()
{ {
var command = new ConfigureWorkflow var command = new UpdateWorkflow
{ {
Workflow = new Workflow( Workflow = new Workflow(
new Dictionary<Status, WorkflowStep> new Dictionary<Status, WorkflowStep>
@ -113,14 +113,14 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
Status.Draft) Status.Draft)
}; };
ValidationAssert.Throws(() => GuardAppWorkflows.CanConfigure(command), ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
new ValidationError("Transition has an invalid target.", "Workflow.Steps.Published.Transitions.Archived")); new ValidationError("Transition has an invalid target.", "Workflow.Steps.Published.Transitions.Archived"));
} }
[Fact] [Fact]
public void CanConfigure_should_throw_exception_if_workflow_transition_is_not_defined() public void CanConfigure_should_throw_exception_if_workflow_transition_is_not_defined()
{ {
var command = new ConfigureWorkflow var command = new UpdateWorkflow
{ {
Workflow = new Workflow( Workflow = new Workflow(
new Dictionary<Status, WorkflowStep> new Dictionary<Status, WorkflowStep>
@ -137,16 +137,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
Status.Draft) Status.Draft)
}; };
ValidationAssert.Throws(() => GuardAppWorkflows.CanConfigure(command), ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
new ValidationError("Transition is required.", "Workflow.Steps.Published.Transitions.Draft")); new ValidationError("Transition is required.", "Workflow.Steps.Published.Transitions.Draft"));
} }
[Fact] [Fact]
public void CanConfigure_should_not_throw_exception_if_workflow_is_valid() public void CanConfigure_should_not_throw_exception_if_workflow_is_valid()
{ {
var command = new ConfigureWorkflow { Workflow = Workflow.Default }; var command = new UpdateWorkflow { Workflow = Workflow.Default };
GuardAppWorkflows.CanConfigure(command); GuardAppWorkflows.CanUpdate(command);
} }
} }
} }

29
tools/Migrate_01/OldEvents/AppWorkflowConfigured.cs

@ -0,0 +1,29 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Events;
using Squidex.Domain.Apps.Events.Apps;
using Squidex.Infrastructure;
using Squidex.Infrastructure.EventSourcing;
using Squidex.Infrastructure.Reflection;
namespace Migrate_01.OldEvents
{
[EventType(nameof(AppWorkflowConfigured))]
[Obsolete]
public sealed class AppWorkflowConfigured : AppEvent, IMigrated<IEvent>
{
public Workflow Workflow { get; set; }
public IEvent Migrate()
{
return SimpleMapper.Map(this, new AppWorkflowUpdated());
}
}
}
Loading…
Cancel
Save