Improve interaction between Finish and composite activities
This update enabled activities within composite activity containers to control the outcome of the container
This commit is contained in:
parent
7c2e9f9342
commit
f14c22d5d4
4
src/core/Elsa.Abstractions/Models/FinishOutput.cs
Normal file
4
src/core/Elsa.Abstractions/Models/FinishOutput.cs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
namespace Elsa.Models
|
||||
{
|
||||
public record FinishOutput(object? Output, string? Outcome);
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
using Elsa.ActivityResults;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Services
|
||||
|
|
@ -18,16 +21,51 @@ namespace Elsa.Services
|
|||
set => SetState(value);
|
||||
}
|
||||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
protected override async ValueTask<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
if (!IsScheduled)
|
||||
{
|
||||
IsScheduled = true;
|
||||
await OnEnterAsync(context);
|
||||
return Outcome(Enter);
|
||||
}
|
||||
|
||||
IsScheduled = false;
|
||||
return Done();
|
||||
await OnExitAsync(context);
|
||||
|
||||
var finishOutput = context.GetInput<FinishOutput>();
|
||||
var outcomes = new List<string> { OutcomeNames.Done };
|
||||
var output = default(object?);
|
||||
|
||||
if (finishOutput != null)
|
||||
{
|
||||
if(!string.IsNullOrWhiteSpace(finishOutput.Outcome))
|
||||
outcomes.Add(finishOutput.Outcome!);
|
||||
|
||||
output = finishOutput.Output;
|
||||
}
|
||||
|
||||
return Combine(Outcomes(outcomes), Output(output));
|
||||
}
|
||||
|
||||
protected virtual ValueTask OnEnterAsync(ActivityExecutionContext context)
|
||||
{
|
||||
OnEnter(context);
|
||||
return new();
|
||||
}
|
||||
|
||||
protected virtual ValueTask OnExitAsync(ActivityExecutionContext context)
|
||||
{
|
||||
OnExit(context);
|
||||
return new();
|
||||
}
|
||||
|
||||
protected virtual void OnEnter(ActivityExecutionContext context)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnExit(ActivityExecutionContext context)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
using System.Linq;
|
||||
using Elsa.ActivityResults;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
|
|
@ -13,8 +14,11 @@ 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")]
|
||||
public object? OutputValue { get; set; }
|
||||
|
||||
[ActivityProperty(Hint = "The outcome to set on the container activity")]
|
||||
public string? Outcome { get; set; }
|
||||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
|
|
@ -24,8 +28,9 @@ namespace Elsa.Activities.ControlFlow
|
|||
var containedBlockingActivityIds = parentBlueprint == null ? blockingActivityIds : parentBlueprint.Activities.Where(x => blockingActivityIds.Contains(x.Id)).Select(x => x.Id).ToList();
|
||||
|
||||
blockingActivities.RemoveWhere(x => containedBlockingActivityIds.Contains(x.ActivityId));
|
||||
|
||||
return Done(OutputValue);
|
||||
var output = new FinishOutput(OutputValue, Outcome);
|
||||
context.WorkflowExecutionContext.WorkflowInstance.Output = output;
|
||||
return Done(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
|
|
@ -8,8 +9,13 @@ namespace Elsa.Activities.ControlFlow
|
|||
public static class FinishBuilderExtensions
|
||||
{
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Action<ISetupActivity<Finish>>? setup = default) => builder.Then(setup);
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<ActivityExecutionContext, object?> output) => builder.Finish(activity => activity.Set(x => x.OutputValue, output));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<object?> output) => builder.Finish(activity => activity.Set(x => x.OutputValue, output));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, object? output) => builder.Finish(activity => activity.Set(x => x.OutputValue, output));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<object?>> output) => builder.Finish(activity => activity.WithOutput(output));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<ActivityExecutionContext, object?> output) => builder.Finish(activity => activity.WithOutput(output));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<object?> output) => builder.Finish(activity => activity.WithOutput(output));
|
||||
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<string?>> outcome) => builder.Finish(activity => activity.WithOutcome(outcome));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<ActivityExecutionContext, string?> outcome) => builder.Finish(activity => activity.WithOutcome(outcome));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, Func<string?> outcome) => builder.Finish(activity => activity.WithOutcome(outcome));
|
||||
public static IActivityBuilder Finish(this IBuilder builder, string? outcome) => builder.Finish(activity => activity.WithOutcome(outcome));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Activities.ControlFlow
|
||||
{
|
||||
public static class FinishExtensions
|
||||
{
|
||||
public static ISetupActivity<Finish> WithOutput(this ISetupActivity<Finish> activity, Func<ActivityExecutionContext, ValueTask<object?>> value) => activity.Set(x => x.OutputValue, value);
|
||||
public static ISetupActivity<Finish> WithOutput(this ISetupActivity<Finish> activity, Func<ActivityExecutionContext, object?> value) => activity.Set(x => x.OutputValue, value);
|
||||
public static ISetupActivity<Finish> WithOutput(this ISetupActivity<Finish> activity, Func<object?> value) => activity.Set(x => x.OutputValue, value);
|
||||
public static ISetupActivity<Finish> WithOutput(this ISetupActivity<Finish> activity, object? value) => activity.Set(x => x.OutputValue, value);
|
||||
|
||||
public static ISetupActivity<Finish> WithOutcome(this ISetupActivity<Finish> activity, Func<ActivityExecutionContext, ValueTask<string?>> value) => activity.Set(x => x.Outcome, value);
|
||||
public static ISetupActivity<Finish> WithOutcome(this ISetupActivity<Finish> activity, Func<ActivityExecutionContext, string?> value) => activity.Set(x => x.Outcome, value);
|
||||
public static ISetupActivity<Finish> WithOutcome(this ISetupActivity<Finish> activity, Func<string?> value) => activity.Set(x => x.Outcome, value);
|
||||
public static ISetupActivity<Finish> WithOutcome(this ISetupActivity<Finish> activity, string? value) => activity.Set(x => x.Outcome, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Ccondition/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cfinish/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cfor/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cifthen/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cprimitives_005Csetname/@EntryIndexedValue">True</s:Boolean>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.ControlFlow;
|
||||
using Elsa.Events;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace Elsa.Handlers
|
||||
|
|
@ -58,8 +60,11 @@ namespace Elsa.Handlers
|
|||
// Re-schedule the parent activity, if any.
|
||||
if (activityBlueprint.Parent != null && workflowBlueprint.GetActivity(activityBlueprint.Parent.Id) != null)
|
||||
{
|
||||
workflowExecutionContext.ScheduleActivity(activityBlueprint.Parent.Id);
|
||||
var output = GetFinishOutput(workflowExecutionContext);
|
||||
workflowExecutionContext.ScheduleActivity(activityBlueprint.Parent.Id, output);
|
||||
}
|
||||
}
|
||||
|
||||
private static FinishOutput? GetFinishOutput(WorkflowExecutionContext workflowExecutionContext) => workflowExecutionContext.WorkflowInstance.Output as FinishOutput;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
using System;
|
||||
using Elsa.Activities.Console;
|
||||
using Elsa.Activities.Console;
|
||||
using Elsa.Activities.ControlFlow;
|
||||
using Elsa.Attributes;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using NetBox.Extensions;
|
||||
|
||||
namespace Elsa.Samples.ProgrammaticCompositeActivitiesConsole.Activities
|
||||
{
|
||||
|
|
@ -20,11 +20,7 @@ namespace Elsa.Samples.ProgrammaticCompositeActivitiesConsole.Activities
|
|||
.StartWith(GetInstructions)
|
||||
.WriteLine(context => (string)context.Input)
|
||||
.ReadLine()
|
||||
.IfElse(context => string.Equals(context.GetInput<string>(), "left", StringComparison.CurrentCultureIgnoreCase), ifElse =>
|
||||
{
|
||||
ifElse.When(IfElse.True).WriteLine("We're going left");
|
||||
ifElse.When(IfElse.False).WriteLine("We're going right");
|
||||
});
|
||||
.Finish(context => context.GetInput<string>().Capitalize());
|
||||
}
|
||||
|
||||
private static void GetInstructions(ActivityExecutionContext context) => context.Output = "Turn left or right?";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,11 @@ namespace Elsa.Samples.ProgrammaticCompositeActivitiesConsole.Workflows
|
|||
.WriteLine("Welcome to the Composite Activities demo workflow!")
|
||||
|
||||
// A custom, composite activity
|
||||
.Then<CountDownActivity>()
|
||||
.Then<CountDownActivity>(countDown =>
|
||||
{
|
||||
countDown.When("Left").WriteLine("Where going left!");
|
||||
countDown.When("Right").WriteLine("Where going right!");
|
||||
})
|
||||
.WriteLine("Done")
|
||||
;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue