Browse Source

Finalized workflow class.

pull/379/head
Sebastian Stehle 7 years ago
parent
commit
bb0931f8a6
  1. 37
      src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.cs
  2. 2
      src/Squidex.Domain.Apps.Core.Model/Contents/Status.cs
  3. 36
      src/Squidex.Domain.Apps.Core.Model/Contents/StatusConverter.cs
  4. 34
      src/Squidex.Domain.Apps.Core.Model/Contents/Workflow.cs
  5. 4
      src/Squidex.Domain.Apps.Core.Model/Contents/Workflows.cs
  6. 2
      src/Squidex.Domain.Apps.Entities/Apps/State/AppState.cs
  7. 4
      src/Squidex.Domain.Apps.Entities/Contents/DynamicContentWorkflow.cs
  8. 3
      src/Squidex/Config/Domain/SerializationServices.cs
  9. 2
      tests/RunCoverage.ps1
  10. 19
      tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/StatusTests.cs
  11. 36
      tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs
  12. 116
      tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowTests.cs
  13. 3
      tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs

37
src/Squidex.Domain.Apps.Core.Model/Contents/Json/WorkflowConverter.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.Linq;
using Newtonsoft.Json;
using Squidex.Infrastructure.Json.Newtonsoft;
namespace Squidex.Domain.Apps.Core.Contents.Json
{
public sealed class WorkflowConverter : JsonClassConverter<Workflows>
{
protected override void WriteValue(JsonWriter writer, Workflows value, JsonSerializer serializer)
{
var json = new Dictionary<Guid, Workflow>(value.Count);
foreach (var workflow in value)
{
json.Add(workflow.Key, workflow.Value);
}
serializer.Serialize(writer, json);
}
protected override Workflows ReadValue(JsonReader reader, Type objectType, JsonSerializer serializer)
{
var json = serializer.Deserialize<Dictionary<Guid, Workflow>>(reader);
return new Workflows(json.ToArray());
}
}
}

2
src/Squidex.Domain.Apps.Core.Model/Contents/Status.cs

@ -6,9 +6,11 @@
// ==========================================================================
using System;
using System.ComponentModel;
namespace Squidex.Domain.Apps.Core.Contents
{
[TypeConverter(typeof(StatusConverter))]
public struct Status : IEquatable<Status>
{
public static readonly Status Archived = new Status("Archived");

36
src/Squidex.Domain.Apps.Core.Model/Contents/StatusConverter.cs

@ -0,0 +1,36 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.ComponentModel;
using System.Globalization;
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class StatusConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return new Status(value?.ToString());
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
return value.ToString();
}
}
}

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

@ -5,7 +5,6 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System;
using System.Collections.Generic;
using Squidex.Infrastructure;
@ -54,29 +53,6 @@ namespace Squidex.Domain.Apps.Core.Contents
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))
@ -88,14 +64,16 @@ namespace Squidex.Domain.Apps.Core.Contents
}
}
public WorkflowTransition GetTransition(Status from, Status to)
public bool TryGetTransition(Status from, Status to, out WorkflowTransition transition)
{
if (TryGetStep(from, out var step) && step.Transitions.TryGetValue(to, out var transition))
if (TryGetStep(from, out var step) && step.Transitions.TryGetValue(to, out transition))
{
return transition;
return true;
}
return null;
transition = null;
return false;
}
public bool TryGetStep(Status status, out WorkflowStep step)

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

@ -13,7 +13,7 @@ using Squidex.Infrastructure.Collections;
namespace Squidex.Domain.Apps.Core.Contents
{
public sealed class Workflows : ArrayDictionary<Guid, Workflows>
public sealed class Workflows : ArrayDictionary<Guid, Workflow>
{
public static readonly Workflows Empty = new Workflows();
@ -21,7 +21,7 @@ namespace Squidex.Domain.Apps.Core.Contents
{
}
public Workflows(KeyValuePair<Guid, Workflows>[] items)
public Workflows(KeyValuePair<Guid, Workflow>[] items)
: base(items)
{
}

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

@ -44,7 +44,7 @@ namespace Squidex.Domain.Apps.Entities.Apps.State
public LanguagesConfig LanguagesConfig { get; set; } = LanguagesConfig.English;
[DataMember]
public Workflows Workdlows { get; set; } = Workflows.Empty;
public Workflows Workflows { get; set; } = Workflows.Empty;
[DataMember]
public bool IsArchived { get; set; }

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

@ -43,9 +43,7 @@ namespace Squidex.Domain.Apps.Entities.Contents
{
var workflow = await GetWorkflowAsync(content.AppId.Id);
var transition = workflow.GetTransition(content.Status, next);
return transition != null && CanUse(transition, content, user);
return workflow.TryGetTransition(content.Status, next, out var transition) && CanUse(transition, content, user);
}
public async Task<bool> CanUpdateAsync(IContentEntity content)

3
src/Squidex/Config/Domain/SerializationServices.cs

@ -46,7 +46,8 @@ namespace Squidex.Config.Domain
new RuleConverter(),
new SchemaConverter(),
new StatusConverter(),
new StringEnumConverter());
new StringEnumConverter(),
new WorkflowConverter());
settings.NullValueHandling = NullValueHandling.Ignore;

2
tests/RunCoverage.ps1

@ -76,6 +76,6 @@ if ($all -Or $web) {
-oldStyle
}
&"$folderHome\.nuget\packages\ReportGenerator\4.0.13.1\tools\net47\ReportGenerator.exe" `
&"$folderHome\.nuget\packages\ReportGenerator\4.1.6\tools\net47\ReportGenerator.exe" `
-reports:"$folderWorking\$folderReports\*.xml" `
-targetdir:"$folderWorking\$folderReports\Output"

19
tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/StatusTests.cs

@ -5,6 +5,7 @@
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.ComponentModel;
using Squidex.Domain.Apps.Core.Contents;
using Xunit;
@ -12,6 +13,24 @@ namespace Squidex.Domain.Apps.Core.Model.Contents
{
public class StatusTests
{
private readonly TypeConverter typeConverter = TypeDescriptor.GetConverter(typeof(Status));
[Fact]
public void Should_convert_from_string()
{
var result = typeConverter.ConvertFromString("Draft");
Assert.Equal(Status.Draft, result);
}
[Fact]
public void Should_convert_to_string()
{
var result = typeConverter.ConvertToString(Status.Draft);
Assert.Equal("Draft", result);
}
[Fact]
public void Should_initialize_default()
{

36
tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowJsonTests.cs

@ -0,0 +1,36 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using FluentAssertions;
using Squidex.Domain.Apps.Core.Contents;
using Xunit;
namespace Squidex.Domain.Apps.Core.Model.Contents
{
public class WorkflowJsonTests
{
[Fact]
public void Should_serialize_and_deserialize()
{
var workflow = Workflow.Default;
var serialized = workflow.SerializeAndDeserialize();
serialized.Should().BeEquivalentTo(workflow);
}
[Fact]
public void Should_serialize_and_deserialize_workflows()
{
var workflow = Workflows.Empty.Set(Workflow.Default);
var serialized = workflow.SerializeAndDeserialize();
serialized.Should().BeEquivalentTo(workflow);
}
}
}

116
tests/Squidex.Domain.Apps.Core.Tests/Model/Contents/WorkflowTests.cs

@ -0,0 +1,116 @@
// ==========================================================================
// Squidex Headless CMS
// ==========================================================================
// Copyright (c) Squidex UG (haftungsbeschraenkt)
// All rights reserved. Licensed under the MIT license.
// ==========================================================================
using System.Collections.Generic;
using System.Linq;
using Squidex.Domain.Apps.Core.Contents;
using Xunit;
namespace Squidex.Domain.Apps.Core.Model.Contents
{
public class WorkflowTests
{
private readonly Workflow workflow = new Workflow(
new Dictionary<Status, WorkflowStep>
{
[Status.Draft] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>
{
[Status.Archived] = new WorkflowTransition("ToArchivedExpr", "ToArchivedRole"),
[Status.Published] = new WorkflowTransition("ToPublishedExpr", "ToPublishedRole")
},
StatusColors.Draft),
[Status.Archived] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>(),
StatusColors.Archived, true),
[Status.Published] =
new WorkflowStep(
new Dictionary<Status, WorkflowTransition>(),
StatusColors.Archived)
}, Status.Draft);
[Fact]
public void Should_provide_initial_state()
{
var (status, step) = workflow.GetInitialStep();
Assert.Equal(Status.Draft, status);
Assert.Equal(StatusColors.Draft, step.Color);
Assert.Same(workflow.Steps[Status.Draft], step);
}
[Fact]
public void Should_provide_step()
{
var found = workflow.TryGetStep(Status.Draft, out var step);
Assert.True(found);
Assert.Same(workflow.Steps[Status.Draft], step);
}
[Fact]
public void Should_not_provide_unknown_step()
{
var found = workflow.TryGetStep(default, out var step);
Assert.False(found);
Assert.Null(step);
}
[Fact]
public void Should_provide_transition()
{
var found = workflow.TryGetTransition(Status.Draft, Status.Archived, out var transition);
Assert.True(found);
Assert.Equal("ToArchivedExpr", transition.Expression);
Assert.Equal("ToArchivedRole", transition.Role);
}
[Fact]
public void Should_not_provide_transition_from_unknown_step()
{
var found = workflow.TryGetTransition(default, Status.Archived, out var transition);
Assert.False(found);
Assert.Null(transition);
}
[Fact]
public void Should_not_provide_transition_to_unknown_step()
{
var found = workflow.TryGetTransition(Status.Draft, default, out var transition);
Assert.False(found);
Assert.Null(transition);
}
[Fact]
public void Should_provide_transitions()
{
var transitions = workflow.GetTransitions(Status.Draft).ToArray();
Assert.Equal(2, transitions.Length);
var (status1, step1, transition1) = transitions[0];
Assert.Equal(Status.Archived, status1);
Assert.Equal("ToArchivedExpr", transition1.Expression);
Assert.Equal("ToArchivedRole", transition1.Role);
Assert.Same(workflow.Steps[Status.Archived], step1);
var (status2, step2, transition2) = transitions[1];
Assert.Equal(Status.Published, status2);
Assert.Equal("ToPublishedExpr", transition2.Expression);
Assert.Equal("ToPublishedRole", transition2.Role);
Assert.Same(workflow.Steps[Status.Published], step2);
}
}
}

3
tests/Squidex.Domain.Apps.Core.Tests/TestUtils.cs

@ -58,7 +58,8 @@ namespace Squidex.Domain.Apps.Core
new RuleConverter(),
new SchemaConverter(),
new StatusConverter(),
new StringEnumConverter()),
new StringEnumConverter(),
new WorkflowConverter()),
TypeNameHandling = typeNameHandling
};

Loading…
Cancel
Save