Update activity completion API

This commit is contained in:
Sipke Schoorstra 2022-04-20 11:14:20 +02:00
parent beedc471dc
commit 4def8b8a10
6 changed files with 63 additions and 19 deletions

View file

@ -36,6 +36,9 @@ public abstract class Container : Activity, IContainer
// Schedule children.
await ScheduleChildrenAsync(context);
// Because we override ExecuteAsync, this activity will not automatically complete.
// Instead, it's up to the derived classes to explicitly complete this activity.
}
protected virtual async ValueTask OnChildActivityCompletedAsync(ActivityCompleted signal, SignalContext context)

View file

@ -2,7 +2,6 @@ using System.ComponentModel;
using Elsa.Attributes;
using Elsa.Contracts;
using Elsa.Models;
using Elsa.Signals;
namespace Elsa.Activities;
@ -36,7 +35,7 @@ public class Sequence : Container
if (currentIndex >= childActivities.Count)
{
await context.SignalAsync(new ActivityCompleted());
await context.CompleteActivityAsync();
return;
}

View file

@ -20,13 +20,7 @@ public abstract class Activity : ISignalHandler
protected virtual async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
Execute(context);
await OnExecutedAsync(context);
}
protected virtual async ValueTask OnExecutedAsync(ActivityExecutionContext context)
{
// By default, signal that the activity is completed.
await context.SignalAsync(new ActivityCompleted());
await CompleteAsync(context);
}
protected virtual ValueTask OnSignalReceivedAsync(object signal, SignalContext context)
@ -43,9 +37,17 @@ public abstract class Activity : ISignalHandler
{
}
/// <summary>
/// Notify the sytem that this activity completed.
/// </summary>
protected async ValueTask CompleteAsync(ActivityExecutionContext context)
{
await context.CompleteActivityAsync();
}
protected void OnSignalReceived(Type signalType, Func<object, SignalContext, ValueTask> handler) => _signalHandlers.Add(new SignalHandlerRegistration(signalType, handler));
protected void OnSignalReceived<T>(Func<T, SignalContext, ValueTask> handler) => OnSignalReceived(typeof(T), (signal, context) => handler((T)signal, context));
protected void OnSignalReceived<T>(Action<T, SignalContext> handler)
{
OnSignalReceived<T>((signal, context) =>
@ -55,8 +57,11 @@ public abstract class Activity : ISignalHandler
});
}
ValueTask IActivity.ExecuteAsync(ActivityExecutionContext context) => ExecuteAsync(context);
async ValueTask IActivity.ExecuteAsync(ActivityExecutionContext context)
{
await ExecuteAsync(context);
}
async ValueTask ISignalHandler.HandleSignalAsync(object signal, SignalContext context)
{
// Give derived activity a chance to do something with the signal.

View file

@ -2,6 +2,7 @@ using System.Collections.ObjectModel;
using System.Reflection;
using Elsa.Activities;
using Elsa.Contracts;
using Elsa.Signals;
namespace Elsa.Models;
@ -190,6 +191,14 @@ public class ActivityExecutionContext
return;
}
}
/// <summary>
/// Explicitly complete the current activity. This should only be called by activities that explicitly suppress automatic-completion.
/// </summary>
public async ValueTask CompleteActivityAsync()
{
await SignalAsync(new ActivityCompleted());
}
/// <summary>
/// Returns a flattened list of the current context's ancestors.

View file

@ -55,7 +55,11 @@ public class ForEachWorkflowTests
{
Items = new Input<ICollection<string>>(_items),
CurrentValue = currentItem,
Body = new WriteLine(context => currentItem.Get(context))
Body = new If(() => true)
{
Then = new Break(),
Else = new WriteLine()
}
},
}
});

View file

@ -16,24 +16,48 @@ public class SequentialWorkflowTests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly Workflow _workflow;
public SequentialWorkflowTests(ITestOutputHelper testOutputHelper)
{
var services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = services.GetRequiredService<IWorkflowRunner>();
_workflow = new WorkflowDefinitionBuilder().BuildWorkflow(new SequentialWorkflow());
}
[Fact(DisplayName = "Sequence completes only after its child activities complete")]
[Fact(DisplayName = "Sequence completes after its child activities complete")]
public async Task Test1()
{
await _workflowRunner.RunAsync(_workflow);
var workflow = new WorkflowDefinitionBuilder().BuildWorkflow(new SequentialWorkflow());
await _workflowRunner.RunAsync(workflow);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Line 1", "Line 2", "Line 3" }, lines);
}
[Fact(DisplayName = "Sequence completes after its child sequence activity complete")]
public async Task Test2()
{
var workflow = new WorkflowDefinitionBuilder().BuildWorkflow(new NestedSequentialWorkflow());
await _workflowRunner.RunAsync(workflow);
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[] { "Start", "Line 1", "Line 2", "Line 3", "End" }, lines);
}
private class SequentialWorkflow : IWorkflow
{
public void Build(IWorkflowDefinitionBuilder workflow)
{
workflow.WithRoot(new Sequence
{
Activities =
{
new WriteLine("Line 1"),
new WriteLine("Line 2"),
new WriteLine("Line 3")
}
});
}
}
private class NestedSequentialWorkflow : IWorkflow
{
public void Build(IWorkflowDefinitionBuilder workflow)
{