Browse Source

App grain stuff.

pull/382/head
Sebastian Stehle 7 years ago
parent
commit
d7624866b9
  1. 9
      src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs
  2. 9
      src/Squidex.Domain.Apps.Entities/Apps/Commands/AddWorkflow.cs
  3. 32
      src/Squidex.Domain.Apps.Entities/Apps/Guards/GuardAppWorkflows.cs
  4. 14
      src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs
  5. 3
      src/Squidex.Domain.Apps.Events/Apps/AppWorkflowAdded.cs
  6. 2
      tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs
  7. 2
      tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPatternsTests.cs
  8. 2
      tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs
  9. 27
      tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsTests.cs
  10. 52
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/AppGrainTests.cs
  11. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs
  12. 4
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppRolesTests.cs
  13. 108
      tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppWorkflowTests.cs

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

@ -34,11 +34,11 @@ namespace Squidex.Domain.Apps.Core.Contents
}
[Pure]
public Workflows Add(string name)
public Workflows Add(Guid workflowId, string name)
{
Guard.NotNullOrEmpty(name, nameof(name));
return new Workflows(With(Guid.NewGuid(), Workflow.CreateDefault(name)));
return new Workflows(With(workflowId, Workflow.CreateDefault(name)));
}
[Pure]
@ -54,6 +54,11 @@ namespace Squidex.Domain.Apps.Core.Contents
{
Guard.NotNull(workflow, nameof(workflow));
if (id == Guid.Empty)
{
return Set(workflow);
}
if (!ContainsKey(id))
{
return this;

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

@ -5,10 +5,19 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
namespace Squidex.Domain.Apps.Entities.Apps.Commands
{
public sealed class AddWorkflow : AppCommand
{
public Guid WorkflowId { get; set; }
public string Name { get; set; }
public AddWorkflow()
{
WorkflowId = Guid.NewGuid();
}
}
}

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

@ -14,11 +14,26 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
public static class GuardAppWorkflows
{
public static void CanAdd(AddWorkflow command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot add workflow.", e =>
{
if (string.IsNullOrWhiteSpace(command.Name))
{
e(Not.Defined("Name"), nameof(command.Name));
}
});
}
public static void CanUpdate(Workflows workflows, UpdateWorkflow command)
{
Guard.NotNull(command, nameof(command));
Validate.It(() => "Cannot configure workflow.", e =>
GetWorkflowOrThrow(workflows, command.WorkflowId);
Validate.It(() => "Cannot update workflow.", e =>
{
if (command.Workflow == null)
{
@ -74,14 +89,21 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
});
}
internal static void CanAdd(AddWorkflow c)
public static void CanDelete(Workflows workflows, DeleteWorkflow command)
{
throw new NotImplementedException();
Guard.NotNull(command, nameof(command));
GetWorkflowOrThrow(workflows, command.WorkflowId);
}
internal static void CanDelete(Workflows workflows, DeleteWorkflow c)
private static Workflow GetWorkflowOrThrow(Workflows workflows, Guid id)
{
throw new NotImplementedException();
if (!workflows.TryGetValue(id, out var workflow))
{
throw new DomainObjectNotFoundException(id.ToString(), "Workflows", typeof(IAppEntity));
}
return workflow;
}
}
}

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

@ -96,9 +96,19 @@ namespace Squidex.Domain.Apps.Entities.Apps.State
Clients = Clients.Revoke(@event.Id);
}
protected void On(AppWorkflowConfigured @event)
protected void On(AppWorkflowAdded @event)
{
Workflows = Workflows.Set(@event.Workflow);
Workflows = Workflows.Add(@event.WorkflowId, @event.Name);
}
protected void On(AppWorkflowUpdated @event)
{
Workflows = Workflows.Update(@event.WorkflowId, @event.Workflow);
}
protected void On(AppWorkflowDeleted @event)
{
Workflows = Workflows.Remove(@event.WorkflowId);
}
protected void On(AppPatternAdded @event)

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

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using Squidex.Infrastructure.EventSourcing;
namespace Squidex.Domain.Apps.Events.Apps
@ -12,6 +13,8 @@ namespace Squidex.Domain.Apps.Events.Apps
[EventType(nameof(AppWorkflowAdded))]
public sealed class AppWorkflowAdded : AppEvent
{
public Guid WorkflowId { get; set; }
public string Name { get; set; }
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppClientsTests.cs

@ -95,7 +95,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps
{
var clients_1 = clients_0.Revoke("2");
Assert.Empty(clients_1);
Assert.NotEmpty(clients_1);
}
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/AppPatternsTests.cs

@ -70,7 +70,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps
{
var patterns_1 = patterns_0.Remove(id);
Assert.Empty(patterns_1);
Assert.NotEmpty(patterns_1);
}
}
}

2
tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/RolesTests.cs

@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Core.Model.Apps
{
var roles_1 = roles_0.Remove(role);
Assert.Empty(roles_1);
Assert.NotEmpty(roles_1);
}
[Fact]

27
tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowsTests.cs

@ -6,7 +6,6 @@
// ==========================================================================
using System;
using System.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Xunit;
@ -38,20 +37,32 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
[Fact]
public void Should_add_new_workflow_with_default_states()
{
var workflows_1 = workflows_0.Add("1");
var id = Guid.NewGuid();
Assert.Equal(workflows_1.GetFirst().Steps.Keys, new[] { Status.Archived, Status.Draft, Status.Published });
var workflows_1 = workflows_0.Add(id, "1");
Assert.Equal(workflows_1[id].Steps.Keys, new[] { Status.Archived, Status.Draft, Status.Published });
}
[Fact]
public void Should_update_workflow()
{
var workflows_1 = workflows_0.Add("1");
var workflows_2 = workflows_1.Update(workflows_1.Keys.First(), Workflow.Empty);
var id = Guid.NewGuid();
var workflows_1 = workflows_0.Add(id, "1");
var workflows_2 = workflows_1.Update(id, Workflow.Empty);
Assert.Empty(workflows_2.GetFirst().Steps.Keys);
}
[Fact]
public void Should_update_workflow_with_default_guid()
{
var workflows_1 = workflows_0.Update(Guid.Empty, Workflow.Empty);
Assert.NotEmpty(workflows_1);
}
[Fact]
public void Should_do_nothing_if_workflow_to_update_not_found()
{
@ -63,8 +74,10 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
[Fact]
public void Should_remove_workflow()
{
var workflows_1 = workflows_0.Add("1");
var workflows_2 = workflows_1.Remove(workflows_1.Keys.First());
var id = Guid.NewGuid();
var workflows_1 = workflows_0.Add(id, "1");
var workflows_2 = workflows_1.Remove(id);
Assert.Empty(workflows_2);
}

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

@ -38,6 +38,7 @@ namespace Squidex.Domain.Apps.Entities.Apps
private readonly string planIdPaid = "premium";
private readonly string planIdFree = "free";
private readonly AppGrain sut;
private readonly Guid workflowId = Guid.NewGuid();
private readonly Guid patternId1 = Guid.NewGuid();
private readonly Guid patternId2 = Guid.NewGuid();
private readonly Guid patternId3 = Guid.NewGuid();
@ -299,9 +300,9 @@ namespace Squidex.Domain.Apps.Entities.Apps
}
[Fact]
public async Task ConfigureWorkflow_should_create_events_and_update_state()
public async Task AddWorkflow_should_create_events_and_update_state()
{
var command = new UpdateWorkflow { Workflow = Workflow.Default };
var command = new AddWorkflow { WorkflowId = workflowId, Name = "my-workflow" };
await ExecuteCreateAsync();
@ -313,7 +314,47 @@ namespace Squidex.Domain.Apps.Entities.Apps
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new AppWorkflowConfigured { Workflow = Workflow.Default })
CreateEvent(new AppWorkflowAdded { WorkflowId = workflowId, Name = "my-workflow" })
);
}
[Fact]
public async Task UpdateWorkflow_should_create_events_and_update_state()
{
var command = new UpdateWorkflow { WorkflowId = workflowId, Workflow = Workflow.Default };
await ExecuteCreateAsync();
await ExecuteAddWorkflowAsync();
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(sut.Snapshot);
Assert.NotEmpty(sut.Snapshot.Workflows);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new AppWorkflowUpdated { WorkflowId = workflowId, Workflow = Workflow.Default })
);
}
[Fact]
public async Task DeleteWorkflow_should_create_events_and_update_state()
{
var command = new DeleteWorkflow { WorkflowId = workflowId };
await ExecuteCreateAsync();
await ExecuteAddWorkflowAsync();
var result = await sut.ExecuteAsync(CreateCommand(command));
result.ShouldBeEquivalent(sut.Snapshot);
Assert.Empty(sut.Snapshot.Workflows);
LastEvents
.ShouldHaveSameEvents(
CreateEvent(new AppWorkflowDeleted { WorkflowId = workflowId })
);
}
@ -540,6 +581,11 @@ namespace Squidex.Domain.Apps.Entities.Apps
return sut.ExecuteAsync(CreateCommand(new AddLanguage { Language = language }));
}
private Task ExecuteAddWorkflowAsync()
{
return sut.ExecuteAsync(CreateCommand(new AddWorkflow { WorkflowId = workflowId, Name = "my-workflow" }));
}
private Task ExecuteChangePlanAsync()
{
return sut.ExecuteAsync(CreateCommand(new ChangePlan { PlanId = planIdPaid }));

4
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppPatternsTests.cs

@ -71,7 +71,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
}
[Fact]
public void CanAdd_should_not_throw_exception_if_success()
public void CanAdd_should_not_throw_exception_if_command_is_valid()
{
var command = new AddPattern { PatternId = patternId, Name = "any", Pattern = ".*" };
@ -87,7 +87,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
}
[Fact]
public void CanDelete_should_not_throw_exception_if_success()
public void CanDelete_should_not_throw_exception_if_command_is_valid()
{
var patterns_1 = patterns_0.Add(patternId, "any", ".*", "Message");

4
tests/Squidex.Domain.Apps.Entities.Tests/Apps/Guards/GuardAppRolesTests.cs

@ -43,7 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
}
[Fact]
public void CanAdd_should_not_throw_exception_if_success()
public void CanAdd_should_not_throw_exception_if_command_is_valid()
{
var command = new AddRole { Name = roleName };
@ -101,7 +101,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
}
[Fact]
public void CanDelete_should_not_throw_exception_if_success()
public void CanDelete_should_not_throw_exception_if_command_is_valid()
{
var roles_1 = roles_0.Add(roleName);

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

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Domain.Apps.Core.Contents;
using Squidex.Domain.Apps.Entities.Apps.Commands;
@ -16,17 +17,54 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
public class GuardAppWorkflowTests
{
private readonly Guid workflowId = Guid.NewGuid();
private readonly Workflows workflows;
public GuardAppWorkflowTests()
{
workflows = Workflows.Empty.Add(workflowId, "name");
}
[Fact]
public void CanAdd_should_throw_exception_if_name_is_not_defined()
{
var command = new AddWorkflow();
ValidationAssert.Throws(() => GuardAppWorkflows.CanAdd(command),
new ValidationError("Name is required.", "Name"));
}
[Fact]
public void CanAdd_should_not_throw_exception_if_command_is_valid()
{
var command = new AddWorkflow { Name = "my-workflow" };
GuardAppWorkflows.CanAdd(command);
}
[Fact]
public void CanUpdate_should_throw_exception_if_workflow_not_found()
{
var command = new UpdateWorkflow
{
Workflow = Workflow.Empty,
WorkflowId = Guid.NewGuid()
};
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppWorkflows.CanUpdate(workflows, command));
}
[Fact]
public void CanConfigure_should_throw_exception_if_workflow_is_not_defined()
public void CanUpdate_should_throw_exception_if_workflow_is_not_defined()
{
var command = new UpdateWorkflow();
var command = new UpdateWorkflow { WorkflowId = workflowId };
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(workflows, command),
new ValidationError("Workflow is required.", "Workflow"));
}
[Fact]
public void CanConfigure_should_throw_exception_if_workflow_has_no_initial_step()
public void CanUpdate_should_throw_exception_if_workflow_has_no_initial_step()
{
var command = new UpdateWorkflow
{
@ -35,15 +73,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
[Status.Published] = new WorkflowStep()
},
default)
default),
WorkflowId = workflowId
};
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(workflows, command),
new ValidationError("Initial step is required.", "Workflow.Initial"));
}
[Fact]
public void CanConfigure_should_throw_exception_if_initial_step_is_published()
public void CanUpdate_should_throw_exception_if_initial_step_is_published()
{
var command = new UpdateWorkflow
{
@ -52,15 +91,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
[Status.Published] = new WorkflowStep()
},
Status.Published)
Status.Published),
WorkflowId = workflowId
};
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(workflows, command),
new ValidationError("Initial step cannot be published step.", "Workflow.Initial"));
}
[Fact]
public void CanConfigure_should_throw_exception_if_workflow_does_not_have_published_state()
public void CanUpdate_should_throw_exception_if_workflow_does_not_have_published_state()
{
var command = new UpdateWorkflow
{
@ -69,15 +109,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
{
[Status.Draft] = new WorkflowStep()
},
Status.Draft)
Status.Draft),
WorkflowId = workflowId
};
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(workflows, command),
new ValidationError("Workflow must have a published step.", "Workflow.Steps"));
}
[Fact]
public void CanConfigure_should_throw_exception_if_workflow_step_is_not_defined()
public void CanUpdate_should_throw_exception_if_workflow_step_is_not_defined()
{
var command = new UpdateWorkflow
{
@ -87,15 +128,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
[Status.Published] = null,
[Status.Draft] = new WorkflowStep()
},
Status.Draft)
Status.Draft),
WorkflowId = workflowId
};
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(workflows, command),
new ValidationError("Step is required.", "Workflow.Steps.Published"));
}
[Fact]
public void CanConfigure_should_throw_exception_if_workflow_transition_is_invalid()
public void CanUpdate_should_throw_exception_if_workflow_transition_is_invalid()
{
var command = new UpdateWorkflow
{
@ -110,15 +152,16 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
}),
[Status.Draft] = new WorkflowStep()
},
Status.Draft)
Status.Draft),
WorkflowId = workflowId
};
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(workflows, command),
new ValidationError("Transition has an invalid target.", "Workflow.Steps.Published.Transitions.Archived"));
}
[Fact]
public void CanConfigure_should_throw_exception_if_workflow_transition_is_not_defined()
public void CanUpdate_should_throw_exception_if_workflow_transition_is_not_defined()
{
var command = new UpdateWorkflow
{
@ -134,19 +177,36 @@ namespace Squidex.Domain.Apps.Entities.Apps.Guards
[Status.Draft] = null
})
},
Status.Draft)
Status.Draft),
WorkflowId = workflowId
};
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(command),
ValidationAssert.Throws(() => GuardAppWorkflows.CanUpdate(workflows, command),
new ValidationError("Transition is required.", "Workflow.Steps.Published.Transitions.Draft"));
}
[Fact]
public void CanConfigure_should_not_throw_exception_if_workflow_is_valid()
public void CanUpdate_should_not_throw_exception_if_workflow_is_valid()
{
var command = new UpdateWorkflow { Workflow = Workflow.Default, WorkflowId = workflowId };
GuardAppWorkflows.CanUpdate(workflows, command);
}
[Fact]
public void CanDelete_should_throw_exception_if_workflow_not_found()
{
var command = new DeleteWorkflow { WorkflowId = Guid.NewGuid() };
Assert.Throws<DomainObjectNotFoundException>(() => GuardAppWorkflows.CanDelete(workflows, command));
}
[Fact]
public void CanDelete_should_not_throw_exception_if_workflow_is_found()
{
var command = new UpdateWorkflow { Workflow = Workflow.Default };
var command = new DeleteWorkflow { WorkflowId = workflowId };
GuardAppWorkflows.CanUpdate(command);
GuardAppWorkflows.CanDelete(workflows, command);
}
}
}

Loading…
Cancel
Save