Configure control flow activities

This commit is contained in:
Sipke Schoorstra 2021-04-04 15:19:16 +02:00
parent 299b871c50
commit cebb57caaa
9 changed files with 79 additions and 40 deletions

View file

@ -3,6 +3,8 @@ using System.Linq;
using System.Threading.Tasks;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Expressions;
using Elsa.Models;
using Elsa.Services;
using Elsa.Services.Models;
@ -17,25 +19,30 @@ namespace Elsa.Activities.ControlFlow
)]
public class Finish : Activity
{
[ActivityProperty(Hint = "The value to set as the workflow's output")]
[ActivityProperty(Hint = "The value to set as the workflow's output", SupportedSyntaxes = new[] { SyntaxNames.Literal, SyntaxNames.JavaScript, SyntaxNames.Liquid })]
public object? OutputValue { get; set; }
[ActivityProperty(Hint = "The outcomes to set on the container activity")]
[ActivityProperty(
Hint = "The outcomes to set on the container activity",
UIHint = ActivityPropertyUIHints.MultiText,
DefaultValue = Elsa.OutcomeNames.Done,
DefaultSyntax = SyntaxNames.Json,
SupportedSyntaxes = new[] { SyntaxNames.Json, SyntaxNames.JavaScript, SyntaxNames.Liquid })]
public IEnumerable<string> OutcomeNames { get; set; } = new[] { Elsa.OutcomeNames.Done };
protected override async ValueTask<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context)
{
var parentBlueprint = context.ActivityBlueprint.Parent;
// Remove any blocking activities within the scope of the composite activity.
var blockingActivities = context.WorkflowExecutionContext.WorkflowInstance.BlockingActivities;
var blockingActivityIds = blockingActivities.Select(x => x.ActivityId).ToList();
var containedBlockingActivityIds = parentBlueprint == null ? blockingActivityIds : parentBlueprint.Activities.Where(x => blockingActivityIds.Contains(x.Id)).Select(x => x.Id).ToList();
var containedBlockingActivities = blockingActivities.Where(x => containedBlockingActivityIds.Contains(x.ActivityId));
foreach (var blockingActivity in containedBlockingActivities)
foreach (var blockingActivity in containedBlockingActivities)
await context.WorkflowExecutionContext.RemoveBlockingActivityAsync(blockingActivity);
// Evict & remove any scope activities within the scope of the composite activity.
var scopes = context.WorkflowInstance.Scopes.Select(x => x).Reverse().ToList();
var scopeIds = scopes.Select(x => x.ActivityId).ToList();
@ -47,9 +54,9 @@ namespace Elsa.Activities.ControlFlow
await context.WorkflowExecutionContext.EvictScopeAsync(scopeActivity);
scopes.RemoveAll(x => x.ActivityId == scopeId);
}
context.WorkflowInstance.Scopes = new SimpleStack<ActivityScope>(scopes.AsEnumerable().Reverse());
// Return output.
var output = new FinishOutput(OutputValue, OutcomeNames);

View file

@ -1,6 +1,7 @@
using System;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Expressions;
using Elsa.Services;
using Elsa.Services.Models;
@ -14,16 +15,16 @@ namespace Elsa.Activities.ControlFlow
)]
public class For : Activity
{
[ActivityProperty(Hint = "The starting number.")]
[ActivityProperty(Hint = "The starting number.", SupportedSyntaxes = new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid })]
public long Start { get; set; }
[ActivityProperty(Hint = "The ending number.")]
[ActivityProperty(Hint = "The ending number.", SupportedSyntaxes = new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid })]
public long End { get; set; }
[ActivityProperty(Hint = "An expression that evaluates to the incrementing number on each step.")]
[ActivityProperty(Hint = "An expression that evaluates to the incrementing number on each step.", SupportedSyntaxes = new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid })]
public long Step { get; set; } = 1;
[ActivityProperty(Hint = "The operator to use when comparing the current value against the end value.")]
[ActivityProperty(Hint = "The operator to use when comparing the current value against the end value.", SupportedSyntaxes = new[] { SyntaxNames.Literal, SyntaxNames.JavaScript, SyntaxNames.Liquid })]
public Operator Operator { get; set; } = Operator.LessThan;
internal long? CurrentValue
@ -46,9 +47,9 @@ namespace Elsa.Activities.ControlFlow
Break = false;
return Done();
}
var currentValue = CurrentValue ?? Start;
var loop = Operator switch
{
Operator.LessThan => currentValue < End,
@ -62,7 +63,7 @@ namespace Elsa.Activities.ControlFlow
{
CurrentValue = currentValue + Step;
var scope = context.CreateScope();
scope.Variables.Set(nameof(CurrentValue), currentValue);
return Outcome(OutcomeNames.Iterate, currentValue);
}

View file

@ -3,6 +3,8 @@ using System.Collections.ObjectModel;
using System.Linq;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Expressions;
using Elsa.Services;
using Elsa.Services.Models;
@ -16,21 +18,26 @@ namespace Elsa.Activities.ControlFlow
)]
public class ForEach : Activity
{
[ActivityProperty(Hint = "A collection of items to iterate over.")]
[ActivityProperty(
Hint = "A collection of items to iterate over.",
UIHint = ActivityPropertyUIHints.MultiLine,
DefaultSyntax = SyntaxNames.Json,
SupportedSyntaxes = new[] { SyntaxNames.Json, SyntaxNames.JavaScript, SyntaxNames.Liquid }
)]
public ICollection<object> Items { get; set; } = new Collection<object>();
private int? CurrentIndex
{
get => GetState<int?>();
set => SetState(value);
}
private bool Break
{
get => GetState<bool>();
set => SetState(value);
}
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
if (Break)
@ -39,7 +46,7 @@ namespace Elsa.Activities.ControlFlow
Break = false;
return Done();
}
var collection = Items.ToList();
var currentIndex = CurrentIndex ?? 0;
@ -50,7 +57,7 @@ namespace Elsa.Activities.ControlFlow
scope.Variables.Set("CurrentIndex", currentIndex);
scope.Variables.Set("CurrentValue", currentValue);
CurrentIndex = currentIndex + 1;
return Combine(Outcome(OutcomeNames.Iterate, currentValue));
}

View file

@ -6,6 +6,7 @@ using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Events;
using Elsa.Expressions;
using Elsa.Models;
using Elsa.Services;
using Elsa.Services.Models;
@ -19,8 +20,11 @@ namespace Elsa.Activities.ControlFlow
public class Fork : Activity
{
[ActivityProperty(
Hint = "Enter one or more branch names.",
UIHint = ActivityPropertyUIHints.MultiText,
Hint = "Enter one or more branch names.")]
DefaultSyntax = SyntaxNames.Json,
SupportedSyntaxes = new[] { SyntaxNames.Json }
)]
public ISet<string> Branches { get; set; } = new HashSet<string>();
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context) => Outcomes(Branches);

View file

@ -4,6 +4,7 @@ using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Events;
using Elsa.Expressions;
using Elsa.Services;
using Elsa.Services.Models;
using MediatR;
@ -23,7 +24,7 @@ namespace Elsa.Activities.ControlFlow
public const string True = "True";
public const string False = "False";
[ActivityProperty(Hint = "The condition to evaluate.", UIHint = ActivityPropertyUIHints.SingleLine)]
[ActivityProperty(Hint = "The condition to evaluate.", UIHint = ActivityPropertyUIHints.SingleLine, SupportedSyntaxes = new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid })]
public bool Condition { get; set; }
public bool EnteredScope
@ -54,12 +55,12 @@ namespace Elsa.Activities.ControlFlow
public Task Handle(ScopeEvicted notification, CancellationToken cancellationToken)
{
if (notification.EvictedScope.Type != nameof(If))
if (notification.EvictedScope.Type != nameof(If))
return Task.CompletedTask;
var data = notification.WorkflowExecutionContext.WorkflowInstance.ActivityData.GetItem(notification.EvictedScope.Id, () => new JObject());
data.SetState(nameof(EnteredScope), false);
return Task.CompletedTask;
}
}

View file

@ -6,6 +6,7 @@ using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Events;
using Elsa.Expressions;
using Elsa.Models;
using Elsa.Services;
using Elsa.Services.Models;
@ -39,7 +40,8 @@ namespace Elsa.Activities.ControlFlow
[ActivityProperty(
UIHint = ActivityPropertyUIHints.Dropdown,
Hint = "WaitAll: wait for all incoming activities to have executed. WaitAny: continue execution as soon as any of the incoming activity has executed.",
Options = new[] { "WaitAll", "WaitAny" }
Options = new[] { "WaitAll", "WaitAny" },
SupportedSyntaxes = new[] { SyntaxNames.Literal, SyntaxNames.JavaScript, SyntaxNames.Liquid }
)]
public JoinMode Mode { get; set; }

View file

@ -3,6 +3,8 @@ using System.Collections.ObjectModel;
using System.Linq;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Expressions;
using Elsa.Services;
using Elsa.Services.Models;
@ -16,7 +18,12 @@ namespace Elsa.Activities.ControlFlow
)]
public class ParallelForEach : Activity
{
[ActivityProperty(Hint = "A collection of items to iterate over.")]
[ActivityProperty(
Hint = "A collection of items to iterate over.",
UIHint = ActivityPropertyUIHints.MultiLine,
DefaultSyntax = SyntaxNames.Json,
SupportedSyntaxes = new[] { SyntaxNames.Json, SyntaxNames.JavaScript, SyntaxNames.Liquid }
)]
public ICollection<object> Items { get; set; } = new Collection<object>();
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)

View file

@ -5,6 +5,7 @@ using System.Threading.Tasks;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Events;
using Elsa.Expressions;
using Elsa.Services;
using Elsa.Services.Models;
using MediatR;
@ -24,9 +25,13 @@ namespace Elsa.Activities.ControlFlow
[ActivityProperty(Hint = "The conditions to evaluate.")]
public ICollection<SwitchCase> Cases { get; set; } = new List<SwitchCase>();
[ActivityProperty(Hint = "The conditions to evaluate.")]
[ActivityProperty(
Hint = "The switch mode determines whether the first match should be scheduled, or all matches.",
DefaultValue = SwitchMode.MatchFirst,
SupportedSyntaxes = new[] { SyntaxNames.Literal, SyntaxNames.JavaScript, SyntaxNames.Liquid }
)]
public SwitchMode Mode { get; set; } = SwitchMode.MatchFirst;
public bool EnteredScope
{
get => GetState<bool>();
@ -48,23 +53,23 @@ namespace Elsa.Activities.ControlFlow
return Done();
}
}
var matches = Cases.Where(x => x.Condition).Select(x => x.Name).ToList();
var results = Mode == SwitchMode.MatchFirst ? matches.Any() ? new[] { matches.First() } : new string[0] : matches.ToArray();
var outcomes = results;
return Outcomes(outcomes);
}
Task INotificationHandler<ScopeEvicted>.Handle(ScopeEvicted notification, CancellationToken cancellationToken)
{
if (notification.EvictedScope.Type != nameof(Switch))
if (notification.EvictedScope.Type != nameof(Switch))
return Task.CompletedTask;
var data = notification.WorkflowExecutionContext.WorkflowInstance.ActivityData.GetItem(notification.EvictedScope.Id, () => new JObject());
data.SetState(nameof(EnteredScope), false);
data.SetState("Unwinding", false);
return Task.CompletedTask;
}
}

View file

@ -1,6 +1,7 @@
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Design;
using Elsa.Expressions;
using Elsa.Services;
using Elsa.Services.Models;
@ -14,9 +15,13 @@ namespace Elsa.Activities.ControlFlow
)]
public class While : Activity
{
[ActivityProperty(Hint = "The condition to evaluate.", UIHint = ActivityPropertyUIHints.SingleLine)]
[ActivityProperty(
Hint = "The condition to evaluate.",
UIHint = ActivityPropertyUIHints.SingleLine,
SupportedSyntaxes = new[] { SyntaxNames.JavaScript, SyntaxNames.Liquid }
)]
public bool Condition { get; set; }
private bool Break
{
get => GetState<bool>();
@ -30,12 +35,12 @@ namespace Elsa.Activities.ControlFlow
Break = false;
return Done();
}
var loop = Condition;
if (!loop)
if (!loop)
return Done();
context.CreateScope();
return Outcome(OutcomeNames.Iterate);
}