diff --git a/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs b/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs index fe08250ad..037086ff8 100644 --- a/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs +++ b/src/common/Elsa.Testing.Shared.Integration/ServiceProviderExtensions.cs @@ -153,7 +153,7 @@ public static class ServiceProviderExtensions public static async Task GetWorkflowDefinitionAsync(this IServiceProvider services, string workflowDefinitionId, VersionOptions versionOptions, CancellationToken cancellationToken = default) { var workflowDefinitionService = services.GetRequiredService(); - var workflowDefinition = await workflowDefinitionService.FindWorkflowDefinitionAsync(workflowDefinitionId, versionOptions); + var workflowDefinition = await workflowDefinitionService.FindWorkflowDefinitionAsync(workflowDefinitionId, versionOptions, cancellationToken); return workflowDefinition!; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Activities/Inline.cs b/src/modules/Elsa.Workflows.Core/Activities/Inline.cs index daea21359..ad209f296 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Inline.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Inline.cs @@ -14,26 +14,26 @@ namespace Elsa.Workflows.Activities; [PublicAPI] public class Inline : CodeActivity { - private readonly Func _activity = default!; + private readonly Func _activity = _ => default; /// - public Inline([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + public Inline([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line) { } /// - public Inline(Func activity, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + public Inline(Func activity, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line) { _activity = activity; } /// - public Inline(Func activity, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(_ => activity(), source, line) + public Inline(Func activity, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(_ => activity(), source, line) { } /// - public Inline(Action activity, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(c => + public Inline(Action activity, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(c => { activity(c); return new ValueTask(); @@ -42,7 +42,7 @@ public class Inline : CodeActivity } /// - public Inline(Action activity, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(c => + public Inline(Action activity, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(c => { activity(); return new ValueTask(); @@ -103,20 +103,20 @@ public class Inline : CodeActivity private readonly Func> _activity; /// - public Inline(Func> activity, MemoryBlockReference? output = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) + public Inline(Func> activity, MemoryBlockReference? output = null, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(output, source, line) { _activity = activity; } /// - public Inline(Func> activity, MemoryBlockReference? output = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) + public Inline(Func> activity, MemoryBlockReference? output = null, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(_ => activity(), output, source, line) { } /// - public Inline(Func activity, MemoryBlockReference? output = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) + public Inline(Func activity, MemoryBlockReference? output = null, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(c => { var result = activity(c); @@ -126,7 +126,7 @@ public class Inline : CodeActivity } /// - public Inline(Func activity, MemoryBlockReference? output = default, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) + public Inline(Func activity, MemoryBlockReference? output = null, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(c => { var result = activity(); diff --git a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs index 76f25d0c9..57c01a6e6 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs @@ -139,4 +139,10 @@ public static class WorkflowExecutionContextExtensions /// Returns true if all activities have completed or canceled, false otherwise. /// public static bool AllActivitiesCompleted(this WorkflowExecutionContext workflowExecutionContext) => workflowExecutionContext.ActivityExecutionContexts.All(x => x.IsCompleted); + + public static object? GetOutputByActivityId(this WorkflowExecutionContext workflowExecutionContext, string activityId, string? outputName = null) + { + var outputRegister = workflowExecutionContext.GetActivityOutputRegister(); + return outputRegister.FindOutputByActivityId(activityId, outputName); + } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionResultExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionResultExtensions.cs new file mode 100644 index 000000000..3103ed1a6 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionResultExtensions.cs @@ -0,0 +1,12 @@ +using Elsa.Extensions; +using Elsa.Workflows.Models; + +namespace Elsa.Workflows; + +public static class WorkflowExecutionResultExtensions +{ + public static T GetActivityOutput(this RunWorkflowResult result, IActivity activity, string? outputName = null) + { + return (T)result.WorkflowExecutionContext.GetOutputByActivityId(activity.Id, outputName)!; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Models/RunWorkflowResult.cs b/src/modules/Elsa.Workflows.Core/Models/RunWorkflowResult.cs index 422d6dd9a..7cb280819 100644 --- a/src/modules/Elsa.Workflows.Core/Models/RunWorkflowResult.cs +++ b/src/modules/Elsa.Workflows.Core/Models/RunWorkflowResult.cs @@ -6,9 +6,9 @@ namespace Elsa.Workflows.Models; /// /// Contains information about a workflow run, such as . /// -public record RunWorkflowResult(WorkflowState WorkflowState, Workflow Workflow, object? Result); +public record RunWorkflowResult(WorkflowExecutionContext WorkflowExecutionContext, WorkflowState WorkflowState, Workflow Workflow, object? Result); /// /// Contains information about a workflow run, such as . /// -public record RunWorkflowResult(WorkflowState WorkflowState, Workflow Workflow, TResult Result); \ No newline at end of file +public record RunWorkflowResult(WorkflowExecutionContext WorkflowExecutionContext, WorkflowState WorkflowState, Workflow Workflow, TResult Result); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs index 80c8be63d..f426d1aa9 100644 --- a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs +++ b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs @@ -45,7 +45,7 @@ public class WorkflowRunner( public async Task> RunAsync(WorkflowBase workflow, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default) { var result = await RunAsync((IWorkflow)workflow, options, cancellationToken); - return new(result.WorkflowState, result.Workflow, (TResult)result.Result!); + return new(result.WorkflowExecutionContext, result.WorkflowState, result.Workflow, (TResult)result.Result!); } /// @@ -219,6 +219,6 @@ public class WorkflowRunner( var result = workflow.ResultVariable?.Get(workflowExecutionContext.MemoryRegister); await notificationSender.SendAsync(new WorkflowExecuted(workflow, workflowState, workflowExecutionContext), cancellationToken); await commitStateHandler.CommitAsync(workflowExecutionContext, workflowState, cancellationToken); - return new(workflowState, workflowExecutionContext.Workflow, result); + return new(workflowExecutionContext, workflowState, workflowExecutionContext.Workflow, result); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Actors/WorkflowInstance.cs b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Actors/WorkflowInstance.cs index 2039857a7..6253ed724 100644 --- a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Actors/WorkflowInstance.cs +++ b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Actors/WorkflowInstance.cs @@ -200,7 +200,7 @@ internal class WorkflowInstance( if (_isRunning) { _queuedRunWorkflowOptions.Enqueue(runWorkflowOptions); - return new(null!, null!, null); + return new(null!, null!, null!, null); } _isRunning = true; diff --git a/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs b/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs index 1ae3cc4b9..01e816184 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs @@ -71,7 +71,7 @@ public class WorkflowHost : IWorkflowHost if (WorkflowState.Status != WorkflowStatus.Running) { _logger.LogWarning("Attempt to resume workflow {WorkflowInstanceId} that is not in the Running state. The actual state is {ActualWorkflowStatus}", WorkflowState.Id, WorkflowState.Status); - return new RunWorkflowResult(WorkflowState, Workflow, null); + return new RunWorkflowResult(null!, WorkflowState, Workflow, null); } var runOptions = new RunWorkflowOptions diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/ComplexIfWorkflow.cs b/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/ComplexIfWorkflow.cs deleted file mode 100644 index 893262273..000000000 --- a/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/ComplexIfWorkflow.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Elsa.Workflows.Activities; - -namespace Elsa.Workflows.IntegrationTests.Activities; - -public class ComplexIfWorkflow : WorkflowBase -{ - private readonly Func _condition; - - public ComplexIfWorkflow(Func condition) - { - _condition = condition; - } - - protected override void Build(IWorkflowBuilder workflow) - { - workflow.Root = new Sequence - { - Activities = - { - new WriteLine("Start"), - new If(_condition) - { - Then = new Sequence - { - Activities = - { - new WriteLine("Executing"), - new WriteLine("True!") - } - }, - Else = new Sequence - { - Activities = - { - new WriteLine("Executing"), - new WriteLine("False!") - } - } - }, - new WriteLine("End") - } - }; - } -} \ No newline at end of file diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/IfTests.cs b/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/IfTests.cs index 8d7ece3e1..6de0dfdd9 100644 --- a/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/IfTests.cs +++ b/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/IfTests.cs @@ -1,4 +1,5 @@ using Elsa.Testing.Shared; +using Elsa.Workflows.Activities; using Microsoft.Extensions.DependencyInjection; using Xunit.Abstractions; @@ -17,24 +18,42 @@ public class IfTests } [Theory(DisplayName = "The correct branch executes when condition is true")] - [InlineData(true, "True!")] - [InlineData(false, "False!")] - public async Task Test1(bool conditionResult, string expectedLine) + [InlineData(true)] + [InlineData(false)] + public async Task Test1(bool conditionResult) { - await _services.PopulateRegistriesAsync(); - await _workflowRunner.RunAsync(new IfThenWorkflow(() => conditionResult)); - var lines = _capturingTextWriter.Lines.ToList(); - Assert.Equal(new[] { expectedLine }, lines); + var result = default(bool?); + + var activity = new If(() => conditionResult) + { + Then = new Inline(() => result = true), + Else = new Inline(() => result = false) + }; + await _services.RunActivityAsync(activity); + Assert.Equal(conditionResult, result); } [Theory(DisplayName = "The If activity completes only after either one of its branches completed")] - [InlineData(true, new[] { "Start", "Executing", "True!", "End" })] - [InlineData(false, new[] { "Start", "Executing", "False!", "End" })] - public async Task Test2(bool conditionResult, string[] expectedLines) + [InlineData(true)] + [InlineData(false)] + public async Task Test2(bool conditionResult) { - await _services.PopulateRegistriesAsync(); - await _workflowRunner.RunAsync(new ComplexIfWorkflow(() => conditionResult)); - var lines = _capturingTextWriter.Lines.ToList(); - Assert.Equal(expectedLines, lines); + var activity = new If(() => conditionResult) + { + Then = new Inline(), + Else = new Inline() + }; + var result = await _services.RunActivityAsync(activity); + Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status); + } + + [Fact(DisplayName = "The If activity produces a result when one of its branches completes")] + public async Task Test3() + { + var activity = new If(() => true); + var result = await _services.RunActivityAsync(activity); + var activityResult = result.GetActivityOutput(activity); + + Assert.True(activityResult); } } \ No newline at end of file diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/IfThenWorkflow.cs b/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/IfThenWorkflow.cs deleted file mode 100644 index 0d69089ad..000000000 --- a/test/integration/Elsa.Workflows.IntegrationTests/Activities/If/IfThenWorkflow.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Elsa.Workflows.Activities; - -namespace Elsa.Workflows.IntegrationTests.Activities; - -public class IfThenWorkflow : WorkflowBase -{ - private readonly Func _condition; - - public IfThenWorkflow(Func condition) - { - _condition = condition; - } - - protected override void Build(IWorkflowBuilder workflow) - { - workflow.Root = new If(_condition) - { - Then = new WriteLine("True!"), - Else = new WriteLine("False!") - }; - } -} \ No newline at end of file