Fix output accessors and add tests

This commit is contained in:
Sipke Schoorstra 2023-09-06 22:57:01 +02:00
parent e3d8624e66
commit 003bf4e335
6 changed files with 166 additions and 25 deletions

View file

@ -66,7 +66,7 @@ public static class ActivityExecutionContextExtensions
public static void SetResult(this ActivityExecutionContext context, object? value)
{
var activity = context.Activity as IActivityWithResult ?? throw new Exception($"Cannot set result on activity {context.Activity.Id} because it does not implement {nameof(IActivityWithResult)}.");
context.Set(activity.Result, value);
context.Set(activity.Result, value, "Result");
}
/// <summary>
@ -311,15 +311,6 @@ public static class ActivityExecutionContextExtensions
public static IEnumerable<ActivityExecutionContext> GetChildren(this ActivityExecutionContext context) =>
context.WorkflowExecutionContext.ActiveActivityExecutionContexts.Where(x => x.ParentActivityExecutionContext == context);
// /// <summary>
// /// Removes all child <see cref="ActivityExecutionContext"/> objects.
// /// </summary>
// public static async Task RemoveChildrenAsync(this ActivityExecutionContext context)
// {
// // Detach child activity execution contexts.
// await context.WorkflowExecutionContext.RemoveActivityExecutionContextsAsync(context.GetChildren());
// }
/// <summary>
/// Send a signal up the current hierarchy of ancestors.
/// </summary>

View file

@ -2,6 +2,7 @@ using System.Linq.Expressions;
using System.Reflection;
using Elsa.Expressions.Helpers;
using Elsa.Expressions.Models;
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Core.Models;
using Elsa.Workflows.Core.Services;
@ -61,16 +62,40 @@ public static class ActivityExtensions
/// <summary>
/// Gets the output with the specified name.
/// </summary>
/// <param name="activity">The activity.</param>
/// <param name="context">The context.</param>
/// <param name="activity">The activity to get the output from.</param>
/// <param name="context">The workflow execution context.</param>
/// <param name="outputName">Name of the output.</param>
/// <returns>The output value.</returns>
public static object? GetOutput(this IActivity activity, WorkflowExecutionContext context, string? outputName = default)
{
var workflowExecutionContext = context;
var outputRegister = workflowExecutionContext.GetActivityOutputRegister();
var output = outputRegister.FindOutputByActivityId(activity.Id, outputName);
return output;
}
/// <summary>
/// Gets the output with the specified name.
/// </summary>
/// <param name="activity">The activity to get the output from.</param>
/// <param name="context">The activity execution context.</param>
/// <param name="outputName">Name of the output.</param>
/// <returns>The output value.</returns>
public static object? GetOutput(this IActivity activity, ActivityExecutionContext context, string? outputName = default)
{
return activity.GetOutput(context.WorkflowExecutionContext, outputName);
}
/// <summary>
/// Gets the output with the specified name.
/// </summary>
/// <param name="activity">The activity to get the output from.</param>
/// <param name="context">The expression execution context.</param>
/// <param name="outputName">Name of the output.</param>
/// <returns>The output value.</returns>
public static object? GetOutput(this IActivity activity, ExpressionExecutionContext context, string? outputName = default)
{
var workflowExecutionContext = context.GetWorkflowExecutionContext();
var outputRegister = workflowExecutionContext.GetActivityOutputRegister();
var output = outputRegister.FindOutputByActivityInstanceId(context.GetActivityExecutionContext().Id, outputName);
return output;
return activity.GetOutput(context.GetWorkflowExecutionContext(), outputName);
}
/// <summary>
@ -81,11 +106,24 @@ public static class ActivityExtensions
/// <param name="outputName">Name of the output.</param>
/// <typeparam name="T">The type of the output.</typeparam>
/// <returns>The output value.</returns>
public static T? GetOutput<T>(this IActivity activity, ExpressionExecutionContext context, string outputName)
public static T? GetOutput<T>(this IActivity activity, ActivityExecutionContext context, string outputName)
{
var outputValue = activity.GetOutput(context, outputName);
return outputValue == null ? default! : outputValue.ConvertTo<T>();
}
/// <summary>
/// Gets the output with the specified name.
/// </summary>
/// <param name="activity">The activity.</param>
/// <param name="context">The context.</param>
/// <param name="outputName">Name of the output.</param>
/// <typeparam name="T">The type of the output.</typeparam>
/// <returns>The output value.</returns>
public static T? GetOutput<T>(this IActivity activity, ExpressionExecutionContext context, string outputName)
{
return activity.GetOutput<T>(context.GetActivityExecutionContext(), outputName);
}
/// <summary>
/// Gets the output with the specified name.
@ -96,7 +134,7 @@ public static class ActivityExtensions
/// <typeparam name="TActivity">The type of the activity.</typeparam>
/// <typeparam name="T">The type of the output.</typeparam>
/// <returns>The output value.</returns>
public static T? GetOutput<TActivity, T>(this TActivity activity, ExpressionExecutionContext context, Expression<Func<TActivity, object?>> outputExpression)
public static T? GetOutput<TActivity, T>(this TActivity activity, ActivityExecutionContext context, Expression<Func<TActivity, object?>> outputExpression)
{
var outputName = outputExpression.GetPropertyName();
return ((IActivity)activity!).GetOutput<T>(context, outputName);
@ -114,7 +152,7 @@ public static class ActivityExtensions
var value = GetResult(activity, context);
return value == null ? default! : value.ConvertTo<T>();
}
/// <summary>
/// Gets the Result output of the specified activity.
/// </summary>
@ -122,6 +160,17 @@ public static class ActivityExtensions
/// <param name="context">The context.</param>
/// <returns>The output value.</returns>
public static object? GetResult(this IActivity activity, ExpressionExecutionContext context)
{
return activity.GetResult(context.GetActivityExecutionContext());
}
/// <summary>
/// Gets the Result output of the specified activity.
/// </summary>
/// <param name="activity">The activity.</param>
/// <param name="context">The context.</param>
/// <returns>The output value.</returns>
public static object? GetResult(this IActivity activity, ActivityExecutionContext context)
{
return activity.GetOutput(context, ActivityOutputRegister.DefaultOutputName);
}
@ -132,7 +181,7 @@ public static class ActivityExtensions
/// <param name="context">The context.</param>
/// <param name="activity">The activity.</param>
/// <returns>The output value.</returns>
public static object? GetResult(this ExpressionExecutionContext context, IActivity activity)
public static object? GetResult(this ActivityExecutionContext context, IActivity activity)
{
return activity.GetOutput(context, ActivityOutputRegister.DefaultOutputName);
}
@ -143,7 +192,7 @@ public static class ActivityExtensions
/// <param name="context">The context.</param>
public static T? GetLastResult<T>(this ExpressionExecutionContext context)
{
var value = GetLastResult(context);
var value = GetLastResult(context.GetWorkflowExecutionContext());
return value == null ? default! : value.ConvertTo<T>();
}
@ -151,10 +200,18 @@ public static class ActivityExtensions
/// Gets the result of the last activity.
/// </summary>
/// <param name="context">The context.</param>
public static object? GetLastResult(this ExpressionExecutionContext context)
public static object? GetLastResult(this ActivityExecutionContext context)
{
var workflowExecutionContext = context.GetWorkflowExecutionContext();
return workflowExecutionContext.GetLastActivityResult();
return context.WorkflowExecutionContext.GetLastResult();
}
/// <summary>
/// Gets the result of the last activity.
/// </summary>
/// <param name="context">The context.</param>
public static object? GetLastResult(this WorkflowExecutionContext context)
{
return context.GetLastActivityResult();
}
/// <summary>

View file

@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using Elsa.Expressions.Models;
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Memory;
@ -19,7 +20,7 @@ public static class OutputExtensions
/// <summary>
/// Sets the output to the specified value.
/// </summary>
public static void Set<T>(this Output<T>? output, ActivityExecutionContext context, T? value) => context.Set(output, value);
public static void Set<T>(this Output<T>? output, ActivityExecutionContext context, T? value, [CallerArgumentExpression("output")] string? outputName = default) => context.Set(output, value, outputName);
/// <summary>
/// Sets the output to the specified value.

View file

@ -0,0 +1,28 @@
using Elsa.Extensions;
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Memory;
using Elsa.Workflows.Core.Models;
namespace Elsa.IntegrationTests.Scenarios.ActivityOutputs;
public class SumActivity : CodeActivity<int>
{
public SumActivity(Variable<int> a, Variable<int> b)
{
A = new(a);
B = new(b);
}
public Input<int> A { get; set; }
public Input<int> B { get; set; }
protected override void Execute(ActivityExecutionContext context)
{
var input1 = A.Get(context);
var input2 = B.Get(context);
var result = input1 + input2;
// All three work.
Result.Set(context, result);
}
}

View file

@ -0,0 +1,31 @@
using Elsa.Extensions;
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Activities;
using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Core.Memory;
namespace Elsa.IntegrationTests.Scenarios.ActivityOutputs;
public class SumWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder workflow)
{
var a = new Variable<int>();
var b = new Variable<int>();
var sumActivity = new SumActivity(a, b);
workflow.Root = new Sequence
{
Variables = { a, b },
Activities =
{
new Inline(context => a.Set(context, 4)),
new Inline(context => b.Set(context, 6)),
sumActivity,
new WriteLine(context => $"The result of {a.Get(context)} and {b.Get(context)} is {sumActivity.GetResult<int>(context)}."),
new WriteLine(context => $"The last result is {context.GetLastResult<int>()}."),
}
};
}
}

View file

@ -0,0 +1,33 @@
using System.Linq;
using System.Threading.Tasks;
using Elsa.Testing.Shared;
using Elsa.Workflows.Core.Contracts;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
using Xunit.Abstractions;
namespace Elsa.IntegrationTests.Scenarios.ActivityOutputs;
public class Tests
{
private readonly IWorkflowRunner _workflowRunner;
private readonly CapturingTextWriter _capturingTextWriter = new();
public Tests(ITestOutputHelper testOutputHelper)
{
var services = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
_workflowRunner = services.GetRequiredService<IWorkflowRunner>();
}
[Fact(DisplayName = "Activity outputs can be accessed from other activities.")]
public async Task Test1()
{
await _workflowRunner.RunAsync<SumWorkflow>();
var lines = _capturingTextWriter.Lines.ToList();
Assert.Equal(new[]
{
"The result of 4 and 6 is 10.",
"The last result is 10."
}, lines);
}
}