From a9fbb57a094d722b3b67533b1a6bd2be030ded8e Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 9 Mar 2022 13:53:34 +0100 Subject: [PATCH] Various API enhancements --- .../Elsa.Core/Activities/Console/ReadLine.cs | 8 +- .../Elsa.Core/Activities/ControlFlow/For.cs | 2 +- .../Activities/ControlFlow/ForEach.cs | 2 +- .../Elsa.Core/Activities/ControlFlow/If.cs | 10 ++- .../Activities/ControlFlow/ParallelForEach.cs | 2 +- .../Activities/ControlFlow/Switch.cs | 4 +- .../Elsa.Core/Activities/ControlFlow/While.cs | 4 +- .../Elsa.Core/Activities/Primitives/Inline.cs | 85 +++++++++++++++++++ .../Activities/Workflows/Composite.cs | 62 ++++++++++++++ .../Activities/Workflows/Container.cs | 7 ++ .../Activities/Workflows/Flowchart.cs | 2 +- .../Activities/Workflows/Sequence.cs | 6 +- .../ActivityExecutionContextExtensions.cs | 3 + .../Models/ActivityExecutionContext.cs | 6 +- src/core/Elsa.Core/Models/Composite.cs | 17 ---- .../Activities/Delay.cs | 9 ++ .../Workflows/VariablesWorkflow.cs | 2 +- .../console/Elsa.Samples.Console2/Program.cs | 2 +- 18 files changed, 196 insertions(+), 37 deletions(-) create mode 100644 src/core/Elsa.Core/Activities/Primitives/Inline.cs create mode 100644 src/core/Elsa.Core/Activities/Workflows/Composite.cs delete mode 100644 src/core/Elsa.Core/Models/Composite.cs diff --git a/src/core/Elsa.Core/Activities/Console/ReadLine.cs b/src/core/Elsa.Core/Activities/Console/ReadLine.cs index 4ee322a6d..3d4e53b8f 100644 --- a/src/core/Elsa.Core/Activities/Console/ReadLine.cs +++ b/src/core/Elsa.Core/Activities/Console/ReadLine.cs @@ -3,19 +3,17 @@ using Elsa.Models; namespace Elsa.Activities.Console; -public class ReadLine : Activity +public class ReadLine : Activity { public ReadLine() { } - public ReadLine(Variable variable, Func? valueConverter = default) => Output = new Output(variable, valueConverter); - - [Output] public Output? Output { get; set; } + public ReadLine(Variable variable, Func? valueConverter = default) => Result = new Output(variable, valueConverter); protected override void Execute(ActivityExecutionContext context) { var text = System.Console.ReadLine(); - context.Set(Output, text); + context.Set(Result, text); } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/For.cs b/src/core/Elsa.Core/Activities/ControlFlow/For.cs index 1ced6704f..dc6f29b3e 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/For.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/For.cs @@ -55,7 +55,7 @@ public class For : Activity if (loop) { - context.ScheduleActivity(iterateNode, OnChildComplete); + context.SubmitActivity(iterateNode, OnChildComplete); // Update loop variable. CurrentValue.Set(context.ExpressionExecutionContext, currentValue); diff --git a/src/core/Elsa.Core/Activities/ControlFlow/ForEach.cs b/src/core/Elsa.Core/Activities/ControlFlow/ForEach.cs index bfc6373ac..4ee6ae804 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/ForEach.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/ForEach.cs @@ -32,7 +32,7 @@ public class ForEach : Activity CurrentValue.Set(context, currentItem); if (Body != null) - context.ScheduleActivity(Body, OnChildCompleted); + context.SubmitActivity(Body, OnChildCompleted); // Increment index. context.UpdateProperty(CurrentIndexProperty, x => x + 1); diff --git a/src/core/Elsa.Core/Activities/ControlFlow/If.cs b/src/core/Elsa.Core/Activities/ControlFlow/If.cs index 69beb05b1..75f996e5e 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/If.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/If.cs @@ -6,6 +6,14 @@ namespace Elsa.Activities.ControlFlow; public class If : Activity { + public If() + { + } + + public If(Input condition) => Condition = condition; + public If(Func condition) => Condition = new Input(condition); + public If(Func condition) => Condition = new Input(condition); + [Input] public Input Condition { get; set; } = new(new Literal(false)); [Outbound] public IActivity? Then { get; set; } [Outbound] public IActivity? Else { get; set; } @@ -16,7 +24,7 @@ public class If : Activity var nextNode = result ? Then : Else; if (nextNode != null) - context.ScheduleActivity(nextNode, OnChildCompletedAsync); + context.SubmitActivity(nextNode, OnChildCompletedAsync); } private ValueTask OnChildCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) diff --git a/src/core/Elsa.Core/Activities/ControlFlow/ParallelForEach.cs b/src/core/Elsa.Core/Activities/ControlFlow/ParallelForEach.cs index 0bcf4e78c..e6399eae9 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/ParallelForEach.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/ParallelForEach.cs @@ -26,7 +26,7 @@ public class ParallelForEach : Activity }; // Schedule a body of work for each item. - context.ScheduleActivity(Body, OnChildCompleted, new[]{localVariable}); + context.SubmitActivity(Body, OnChildCompleted, new[]{localVariable}); } } diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs b/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs index 7bbd60a6e..dcbacfdd1 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs @@ -21,12 +21,12 @@ public class Switch : Activity if (matchingCase != null) { if (matchingCase.Activity != null) - context.ScheduleActivity(matchingCase.Activity); + context.SubmitActivity(matchingCase.Activity); return; } if (Default != null) - context.ScheduleActivity(Default); + context.SubmitActivity(Default); } private async Task FindMatchingCaseAsync(ExpressionExecutionContext context) diff --git a/src/core/Elsa.Core/Activities/ControlFlow/While.cs b/src/core/Elsa.Core/Activities/ControlFlow/While.cs index 8e19fa3fc..883323cda 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/While.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/While.cs @@ -14,7 +14,7 @@ public class While : Activity var loop = context.Get(Condition); if(loop) - context.ScheduleActivity(Body, OnBodyCompleted); + context.SubmitActivity(Body, OnBodyCompleted); } private async ValueTask OnBodyCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext) @@ -22,6 +22,6 @@ public class While : Activity var loop = await context.EvaluateAsync(Condition); if(loop) - context.ScheduleActivity(Body, OnBodyCompleted); + context.SubmitActivity(Body, OnBodyCompleted); } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Primitives/Inline.cs b/src/core/Elsa.Core/Activities/Primitives/Inline.cs new file mode 100644 index 000000000..5880e0d5f --- /dev/null +++ b/src/core/Elsa.Core/Activities/Primitives/Inline.cs @@ -0,0 +1,85 @@ +using Elsa.Models; + +namespace Elsa.Activities.Primitives; + +/// +/// Represents an inline code activity that can be used to execute arbitrary .NET code from a workflow. +/// +public class Inline : Activity +{ + private readonly Func _activity; + + public Inline(Func activity) => _activity = activity; + + public Inline(Func activity) : this(_ => activity()) + { + } + + public Inline(Action activity) : this(c => + { + activity(c); + return new ValueTask(); + }) + { + } + + public Inline(Action activity) : this(c => + { + activity(); + return new ValueTask(); + }) + { + } + + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) => await _activity(context); + + public static Inline From(Func activity) => new(activity); + public static Inline From(Func activity) => new(activity); + public static Inline From(Action activity) => new(activity); + public static Inline From(Action activity) => new(activity); + + public static Inline From(Func> activity) => new(activity); + public static Inline From(Func> activity) => new(activity); + public static Inline From(Func activity) => new(activity); + public static Inline From(Func activity) => new(activity); +} + +/// +/// Represents an inline code activity that can be used to execute arbitrary .NET code from a workflow and return a value. +/// +public class Inline : Activity +{ + private readonly Func> _activity; + + public Inline(Func> activity, RegisterLocationReference? output = default) + { + _activity = activity; + if (output != null) Result = new Output(output); + } + + public Inline(Func> activity, RegisterLocationReference? output = default) : this(_ => activity(), output) + { + } + + public Inline(Func activity, RegisterLocationReference? output = default) : this(c => + { + var result = activity(c); + return new ValueTask(result); + }, output) + { + } + + public Inline(Func activity, RegisterLocationReference? output = default) : this(c => + { + var result = activity(); + return new ValueTask(result); + }, output) + { + } + + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + { + var result = await _activity(context); + context.Set(Result, result); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Workflows/Composite.cs b/src/core/Elsa.Core/Activities/Workflows/Composite.cs new file mode 100644 index 000000000..dad9e8088 --- /dev/null +++ b/src/core/Elsa.Core/Activities/Workflows/Composite.cs @@ -0,0 +1,62 @@ +using Elsa.Activities.Primitives; +using Elsa.Attributes; +using Elsa.Contracts; +using Elsa.Models; + +namespace Elsa.Activities.Workflows; + +/// +/// Represents a composite activity that has a single activity. +/// +public class Composite : Activity +{ + /// + /// The activity to schedule when this activity executes. + /// + [Outbound] + public IActivity Root { get; protected set; } = new Sequence(); + + protected override void Execute(ActivityExecutionContext context) + { + context.SubmitActivity(Root, OnCompletedAsync); + } + + protected virtual ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => ValueTask.CompletedTask; + + protected static Inline From(Func activity) => new(activity); + protected static Inline From(Func activity) => new(activity); + protected static Inline From(Action activity) => new(activity); + protected static Inline From(Action activity) => new(activity); + protected static Inline From(Func> activity, RegisterLocationReference? output = default) => new(activity, output); + protected static Inline From(Func> activity, RegisterLocationReference? output = default) => new(activity, output); + protected static Inline From(Func activity, RegisterLocationReference? output = default) => new(activity, output); + protected static Inline From(Func activity, RegisterLocationReference? output = default) => new(activity, output); +} + +/// +/// Represents a composite activity that has a single activity and returns a result. +/// +public class Composite : Activity +{ + /// + /// The activity to schedule when this activity executes. + /// + [Outbound] + public IActivity Root { get; protected set; } = new Sequence(); + + protected override void Execute(ActivityExecutionContext context) + { + context.SubmitActivity(Root, OnCompletedAsync); + } + + protected virtual ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => ValueTask.CompletedTask; + + protected static Inline From(Func activity) => new(activity); + protected static Inline From(Func activity) => new(activity); + protected static Inline From(Action activity) => new(activity); + protected static Inline From(Action activity) => new(activity); + protected static Inline From(Func> activity, RegisterLocationReference? output = default) => new(activity, output); + protected static Inline From(Func> activity, RegisterLocationReference? output = default) => new(activity, output); + protected static Inline From(Func activity, RegisterLocationReference? output = default) => new(activity, output); + protected static Inline From(Func activity, RegisterLocationReference? output = default) => new(activity, output); +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Workflows/Container.cs b/src/core/Elsa.Core/Activities/Workflows/Container.cs index d1bd27fd0..3a54814de 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Container.cs +++ b/src/core/Elsa.Core/Activities/Workflows/Container.cs @@ -12,6 +12,13 @@ public abstract class Container : Activity, IContainer } protected Container(params IActivity[] activities) => Activities = activities; + + protected Container(ICollection variables, params IActivity[] activities) + { + Variables = variables; + Activities = activities; + } + [Outbound] public ICollection Activities { get; set; } = new List(); public ICollection Variables { get; set; } = new Collection(); diff --git a/src/core/Elsa.Core/Activities/Workflows/Flowchart.cs b/src/core/Elsa.Core/Activities/Workflows/Flowchart.cs index dd65da4d5..105fdc0cf 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Flowchart.cs +++ b/src/core/Elsa.Core/Activities/Workflows/Flowchart.cs @@ -16,7 +16,7 @@ public class Flowchart : Container if (Start == null!) return; - context.ScheduleActivity(Start, OnChildCompleted); + context.SubmitActivity(Start, OnChildCompleted); } private ValueTask OnChildCompleted(ActivityExecutionContext context, ActivityExecutionContext childContext) diff --git a/src/core/Elsa.Core/Activities/Workflows/Sequence.cs b/src/core/Elsa.Core/Activities/Workflows/Sequence.cs index 1ef5dc161..54a94cc24 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Sequence.cs +++ b/src/core/Elsa.Core/Activities/Workflows/Sequence.cs @@ -16,6 +16,10 @@ public class Sequence : Container public Sequence(params IActivity[] activities) : base(activities) { } + + public Sequence(ICollection variables, params IActivity[] activities) : base(variables, activities) + { + } protected override void ScheduleChildren(ActivityExecutionContext context) { @@ -31,7 +35,7 @@ public class Sequence : Container return; var nextActivity = childActivities.ElementAt(currentIndex); - context.ScheduleActivity(nextActivity, OnChildCompleted); + context.SubmitActivity(nextActivity, OnChildCompleted); context.UpdateProperty(CurrentIndexProperty, x => x + 1); } diff --git a/src/core/Elsa.Core/Extensions/ActivityExecutionContextExtensions.cs b/src/core/Elsa.Core/Extensions/ActivityExecutionContextExtensions.cs index 59b8f2c46..2b4161daa 100644 --- a/src/core/Elsa.Core/Extensions/ActivityExecutionContextExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ActivityExecutionContextExtensions.cs @@ -21,4 +21,7 @@ public static class ActivityExecutionContextExtensions workflowExecutionContext.ExecutionLog.Add(logEntry); return logEntry; } + + public static Variable SetVariable(this ActivityExecutionContext context, string name, object? value) => context.WorkflowExecutionContext.SetVariable(name, value); + public static T? GetVariable(this ActivityExecutionContext context, string name) => context.WorkflowExecutionContext.GetVariable(name); } \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/ActivityExecutionContext.cs b/src/core/Elsa.Core/Models/ActivityExecutionContext.cs index db89525ae..ebd713145 100644 --- a/src/core/Elsa.Core/Models/ActivityExecutionContext.cs +++ b/src/core/Elsa.Core/Models/ActivityExecutionContext.cs @@ -68,7 +68,7 @@ public class ActivityExecutionContext /// public IDictionary JournalData { get; private set; } = new Dictionary(); - public void ScheduleActivity(IActivity? activity, ActivityCompletionCallback? completionCallback = default, IEnumerable? locationReferences = default, object? tag = default) + public void SubmitActivity(IActivity? activity, ActivityCompletionCallback? completionCallback = default, IEnumerable? locationReferences = default, object? tag = default) { if (activity == null) return; @@ -76,7 +76,7 @@ public class ActivityExecutionContext WorkflowExecutionContext.Schedule(activity, this, completionCallback, locationReferences, tag); } - public void ScheduleActivity(IActivity? activity, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, IEnumerable? locationReferences = default, object? tag = default) + public void SubmitActivity(IActivity? activity, ActivityExecutionContext owner, ActivityCompletionCallback? completionCallback = default, IEnumerable? locationReferences = default, object? tag = default) { if (activity == null) return; @@ -89,7 +89,7 @@ public class ActivityExecutionContext public void ScheduleActivities(IEnumerable activities, ActivityCompletionCallback? completionCallback = default) { foreach (var activity in activities) - ScheduleActivity(activity, completionCallback); + SubmitActivity(activity, completionCallback); } public void CreateBookmarks(IEnumerable bookmarkData, ExecuteActivityDelegate? callback = default) diff --git a/src/core/Elsa.Core/Models/Composite.cs b/src/core/Elsa.Core/Models/Composite.cs deleted file mode 100644 index 1b9144bbf..000000000 --- a/src/core/Elsa.Core/Models/Composite.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Elsa.Activities.Workflows; -using Elsa.Attributes; -using Elsa.Contracts; - -namespace Elsa.Models; - -public abstract class Composite : Activity -{ - [Outbound] public IActivity Root { get; protected set; } = new Sequence(); - - protected override void Execute(ActivityExecutionContext context) - { - context.ScheduleActivity(Root, OnCompletedAsync); - } - - protected virtual ValueTask OnCompletedAsync(ActivityExecutionContext context, ActivityExecutionContext childContext) => ValueTask.CompletedTask; -} \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Scheduling/Activities/Delay.cs b/src/modules/Elsa.Modules.Scheduling/Activities/Delay.cs index f07aac73b..eb53d1578 100644 --- a/src/modules/Elsa.Modules.Scheduling/Activities/Delay.cs +++ b/src/modules/Elsa.Modules.Scheduling/Activities/Delay.cs @@ -5,8 +5,17 @@ using Elsa.Models; namespace Elsa.Modules.Scheduling.Activities; +[Activity("Scheduling", "Delays execution for the specified amount of time.")] public class Delay : Activity { + public Delay() + { + } + + public Delay(Input timeSpan) => TimeSpan = timeSpan; + public Delay(TimeSpan timeSpan) => TimeSpan = new Input(timeSpan); + public Delay(Variable timeSpan) => TimeSpan = new Input(timeSpan); + [Input] public Input TimeSpan { get; set; } = default!; protected override void Execute(ActivityExecutionContext context) diff --git a/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs b/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs index 6933c1925..d75ca234f 100644 --- a/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs +++ b/src/samples/console/Elsa.Samples.Console1/Workflows/VariablesWorkflow.cs @@ -20,7 +20,7 @@ public static class VariablesWorkflow new WriteLine(context => greeting.Get(context)), new ReadLine { - Output = new Output(name) + Result = new Output(name) }, new WriteLine(new DelegateReference(context => $"Nice to meet you, {name.Get(context)}!")), } diff --git a/src/samples/console/Elsa.Samples.Console2/Program.cs b/src/samples/console/Elsa.Samples.Console2/Program.cs index 0397e9005..4770ccdfd 100644 --- a/src/samples/console/Elsa.Samples.Console2/Program.cs +++ b/src/samples/console/Elsa.Samples.Console2/Program.cs @@ -32,7 +32,7 @@ typeSystem.Register(); typeSystem.Register(); functionActivityRegistry.RegisterFunction("print", nameof(WriteLine), new[] { nameof(WriteLine.Text) }); -functionActivityRegistry.RegisterFunction("read", nameof(ReadLine), new[] { nameof(ReadLine.Output) }); +functionActivityRegistry.RegisterFunction("read", nameof(ReadLine), new[] { nameof(ReadLine.Result) }); var assembly = Assembly.GetExecutingAssembly(); var resource = assembly.GetManifestResourceStream("Elsa.Samples.Console2.Sample1.elsa");