< Summary

Information
Class: Elsa.Workflows.Runtime.Activities.DispatchWorkflow
Assembly: Elsa.Workflows.Runtime
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/Activities/DispatchWorkflow.cs
Line coverage
100%
Covered lines: 60
Uncovered lines: 0
Coverable lines: 60
Total lines: 155
Line coverage: 100%
Branch coverage
90%
Covered branches: 9
Total branches: 10
Branch coverage: 90%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_WorkflowDefinitionId()100%11100%
get_CorrelationId()100%11100%
get_Input()100%11100%
get_WaitForCompletion()100%11100%
get_StartNewTrace()100%11100%
get_ChannelName()100%11100%
ExecuteAsync()100%22100%
DispatchChildWorkflowAsync()87.5%88100%
OnChildWorkflowCompletedAsync()100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/Activities/DispatchWorkflow.cs

#LineLine coverage
 1using System.Runtime.CompilerServices;
 2using Elsa.Common.Models;
 3using Elsa.Extensions;
 4using Elsa.Workflows.Attributes;
 5using Elsa.Workflows.Management;
 6using Elsa.Workflows.Models;
 7using Elsa.Workflows.Runtime.Requests;
 8using Elsa.Workflows.Runtime.Stimuli;
 9using Elsa.Workflows.Runtime.UIHints;
 10using Elsa.Workflows.UIHints;
 11using JetBrains.Annotations;
 12
 13namespace Elsa.Workflows.Runtime.Activities;
 14
 15/// <summary>
 16/// Creates a new workflow instance of the specified workflow and dispatches it for execution.
 17/// </summary>
 18[Activity("Elsa", "Composition", "Create a new workflow instance of the specified workflow and dispatch it for execution
 19[UsedImplicitly]
 20public class DispatchWorkflow : Activity<object>
 21{
 22    /// <inheritdoc />
 9023    public DispatchWorkflow([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, 
 24    {
 9025    }
 26
 27    /// <summary>
 28    /// The definition ID of the workflow to dispatch.
 29    /// </summary>
 30    [Input(
 31        DisplayName = "Workflow Definition",
 32        Description = "The definition ID of the workflow to dispatch.",
 33        UIHint = InputUIHints.WorkflowDefinitionPicker
 34    )]
 32335    public Input<string> WorkflowDefinitionId { get; set; } = null!;
 36
 37    /// <summary>
 38    /// The correlation ID to associate the workflow with.
 39    /// </summary>
 40    [Input(
 41        DisplayName = "Correlation ID",
 42        Description = "The correlation ID to associate the workflow with."
 43    )]
 25044    public Input<string?> CorrelationId { get; set; } = null!;
 45
 46    /// <summary>
 47    /// The input to send to the workflow.
 48    /// </summary>
 49    [Input(Description = "The input to send to the workflow.")]
 25050    public Input<IDictionary<string, object>?> Input { get; set; } = null!;
 51
 52    /// <summary>
 53    /// True to wait for the child workflow to complete before completing this activity, false to "fire and forget".
 54    /// </summary>
 55    [Input(Description = "Wait for the child workflow to complete before completing this activity.")]
 32356    public Input<bool> WaitForCompletion { get; set; } = null!;
 57
 58    /// <summary>
 59    /// Indicates whether a new trace context should be started for the workflow execution.
 60    /// </summary>
 61    [Input(Description = "Start a new trace context when using Open Telemetry.", Category = "Open Telemetry")]
 23262    public Input<bool> StartNewTrace { get; set; } = null!;
 63
 64    /// <summary>
 65    /// The channel to dispatch the workflow to.
 66    /// </summary>
 67    [Input(
 68        DisplayName = "Channel",
 69        Description = "The channel to dispatch the workflow to.",
 70        UIHint = InputUIHints.DropDown,
 71        UIHandler = typeof(DispatcherChannelOptionsProvider)
 72    )]
 23273    public Input<string?> ChannelName { get; set; } = null!;
 74
 75    /// <inheritdoc />
 76    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
 77    {
 578        var waitForCompletion = WaitForCompletion.GetOrDefault(context);
 79
 80        // Dispatch the child workflow.
 581        var instanceId = await DispatchChildWorkflowAsync(context, waitForCompletion);
 82
 83        // If we need to wait for the child workflow to complete, create a bookmark.
 484        if (waitForCompletion)
 85        {
 386            var bookmarkOptions = new CreateBookmarkArgs
 387            {
 388                Callback = OnChildWorkflowCompletedAsync,
 389                Stimulus = new DispatchWorkflowStimulus(instanceId),
 390                IncludeActivityInstanceId = false
 391            };
 392            context.CreateBookmark(bookmarkOptions);
 93        }
 94        else
 95        {
 96            // Otherwise, we can complete immediately.
 197            await context.CompleteActivityAsync();
 98        }
 499    }
 100
 101    private async ValueTask<string> DispatchChildWorkflowAsync(ActivityExecutionContext context, bool waitForCompletion)
 102    {
 5103        var workflowDefinitionId = WorkflowDefinitionId.Get(context);
 5104        var workflowDefinitionService = context.GetRequiredService<IWorkflowDefinitionService>();
 5105        var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(workflowDefinitionId, VersionOptions.
 106
 5107        if (workflowGraph == null)
 1108            throw new($"No published version of workflow definition with ID {workflowDefinitionId} found.");
 109
 4110        var input = Input.GetOrDefault(context) ?? new Dictionary<string, object>();
 4111        var channelName = ChannelName.GetOrDefault(context);
 4112        var startNewTrace = StartNewTrace.GetOrDefault(context);
 4113        var parentInstanceId = context.WorkflowExecutionContext.Id;
 4114        var properties = new Dictionary<string, object>
 4115        {
 4116            ["ParentInstanceId"] = parentInstanceId,
 4117        };
 118
 119        // If we need to wait for the child workflow to complete, set the property. This will be used by the ResumeDispa
 7120        if (waitForCompletion) properties["WaitForCompletion"] = true;
 4121        if (startNewTrace) properties["StartNewTrace"] = true;
 122
 4123        input["ParentInstanceId"] = parentInstanceId;
 124
 4125        var correlationId = CorrelationId.GetOrDefault(context);
 4126        var workflowDispatcher = context.GetRequiredService<IWorkflowDispatcher>();
 4127        var identityGenerator = context.GetRequiredService<IIdentityGenerator>();
 4128        var instanceId = identityGenerator.GenerateId();
 4129        var request = new DispatchWorkflowDefinitionRequest(workflowGraph.Workflow.Identity.Id)
 4130        {
 4131            ParentWorkflowInstanceId = parentInstanceId,
 4132            Input = input,
 4133            Properties = properties,
 4134            CorrelationId = correlationId,
 4135            InstanceId = instanceId,
 4136        };
 4137        var options = new DispatchWorkflowOptions
 4138        {
 4139            Channel = channelName
 4140        };
 141
 142        // Dispatch the child workflow.
 4143        var dispatchResponse = await workflowDispatcher.DispatchAsync(request, options, context.CancellationToken);
 4144        dispatchResponse.ThrowIfFailed();
 145
 4146        return instanceId;
 4147    }
 148
 149    private async ValueTask OnChildWorkflowCompletedAsync(ActivityExecutionContext context)
 150    {
 3151        var input = context.WorkflowInput;
 3152        context.Set(Result, input);
 3153        await context.CompleteActivityAsync();
 3154    }
 155}