< 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: 68
Uncovered lines: 0
Coverable lines: 68
Total lines: 171
Line coverage: 100%
Branch coverage
91%
Covered branches: 11
Total branches: 12
Branch coverage: 91.6%
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()90%1010100%
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.Helpers;
 6using Elsa.Workflows.Management;
 7using Elsa.Workflows.Models;
 8using Elsa.Workflows.Runtime.Notifications;
 9using Elsa.Workflows.Runtime.Requests;
 10using Elsa.Workflows.Runtime.Stimuli;
 11using Elsa.Workflows.Runtime.UIHints;
 12using Elsa.Workflows.UIHints;
 13using JetBrains.Annotations;
 14
 15namespace Elsa.Workflows.Runtime.Activities;
 16
 17/// <summary>
 18/// Creates a new workflow instance of the specified workflow and dispatches it for execution.
 19/// When <see cref="WaitForCompletion"/> is enabled and activation is denied, the activity completes with a result dicti
 20/// <c>CannotStart</c> set to <c>true</c> and the requested <c>WorkflowInstanceId</c>.
 21/// </summary>
 22[Activity("Elsa", "Composition", "Create a new workflow instance of the specified workflow and dispatch it for execution
 23[UsedImplicitly]
 24public class DispatchWorkflow : Activity<object>
 25{
 26    /// <inheritdoc />
 32627    public DispatchWorkflow([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, 
 28    {
 32629    }
 30
 31    /// <summary>
 32    /// The definition ID of the workflow to dispatch.
 33    /// </summary>
 34    [Input(
 35        DisplayName = "Workflow Definition",
 36        Description = "The definition ID of the workflow to dispatch.",
 37        UIHint = InputUIHints.WorkflowDefinitionPicker
 38    )]
 120239    public Input<string> WorkflowDefinitionId { get; set; } = null!;
 40
 41    /// <summary>
 42    /// The correlation ID to associate the workflow with.
 43    /// </summary>
 44    [Input(
 45        DisplayName = "Correlation ID",
 46        Description = "The correlation ID to associate the workflow with."
 47    )]
 97748    public Input<string?> CorrelationId { get; set; } = null!;
 49
 50    /// <summary>
 51    /// The input to send to the workflow.
 52    /// </summary>
 53    [Input(Description = "The input to send to the workflow.")]
 93154    public Input<IDictionary<string, object>?> Input { get; set; } = null!;
 55
 56    /// <summary>
 57    /// True to wait for the child workflow to complete before completing this activity, false to "fire and forget".
 58    /// </summary>
 59    [Input(Description = "Wait for the child workflow to complete before completing this activity.")]
 120260    public Input<bool> WaitForCompletion { get; set; } = null!;
 61
 62    /// <summary>
 63    /// Indicates whether a new trace context should be started for the workflow execution.
 64    /// </summary>
 65    [Input(Description = "Start a new trace context when using Open Telemetry.", Category = "Open Telemetry")]
 87566    public Input<bool> StartNewTrace { get; set; } = null!;
 67
 68    /// <summary>
 69    /// The channel to dispatch the workflow to.
 70    /// </summary>
 71    [Input(
 72        DisplayName = "Channel",
 73        Description = "The channel to dispatch the workflow to.",
 74        UIHint = InputUIHints.DropDown,
 75        UIHandler = typeof(DispatcherChannelOptionsProvider)
 76    )]
 87577    public Input<string?> ChannelName { get; set; } = null!;
 78
 79    /// <inheritdoc />
 80    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
 81    {
 682        var waitForCompletion = WaitForCompletion.GetOrDefault(context);
 83
 84        // Dispatch the child workflow.
 685        var instanceId = await DispatchChildWorkflowAsync(context, waitForCompletion);
 86
 87        // If we need to wait for the child workflow to complete, create a bookmark.
 588        if (waitForCompletion)
 89        {
 490            var bookmarkOptions = new CreateBookmarkArgs
 491            {
 492                Callback = OnChildWorkflowCompletedAsync,
 493                Stimulus = new DispatchWorkflowStimulus(instanceId),
 494                IncludeActivityInstanceId = false
 495            };
 496            context.CreateBookmark(bookmarkOptions);
 97        }
 98        else
 99        {
 100            // Otherwise, we can complete immediately.
 1101            await context.CompleteActivityAsync();
 102        }
 5103    }
 104
 105    private async ValueTask<string> DispatchChildWorkflowAsync(ActivityExecutionContext context, bool waitForCompletion)
 106    {
 6107        var workflowDefinitionId = WorkflowDefinitionId.Get(context);
 6108        var workflowDefinitionService = context.GetRequiredService<IWorkflowDefinitionService>();
 6109        var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(workflowDefinitionId, VersionOptions.
 110
 6111        if (workflowGraph == null)
 1112            throw new($"No published version of workflow definition with ID {workflowDefinitionId} found.");
 113
 5114        var input = Input.GetOrDefault(context) ?? new Dictionary<string, object>();
 5115        var channelName = ChannelName.GetOrDefault(context);
 5116        var startNewTrace = StartNewTrace.GetOrDefault(context);
 5117        var parentInstanceId = context.WorkflowExecutionContext.Id;
 5118        var properties = new Dictionary<string, object>
 5119        {
 5120            ["ParentInstanceId"] = parentInstanceId,
 5121        };
 122
 123        // If we need to wait for the child workflow to complete, set the property. This will be used by the ResumeDispa
 9124        if (waitForCompletion) properties["WaitForCompletion"] = true;
 5125        if (startNewTrace) properties["StartNewTrace"] = true;
 126
 5127        input["ParentInstanceId"] = parentInstanceId;
 128
 5129        var correlationId = CorrelationId.GetOrDefault(context);
 5130        var workflowDispatcher = context.GetRequiredService<IWorkflowDispatcher>();
 5131        var identityGenerator = context.GetRequiredService<IIdentityGenerator>();
 5132        var instanceId = identityGenerator.GenerateId();
 133
 5134        if (waitForCompletion)
 135        {
 4136            var activityTypeName = ActivityTypeNameHelper.GenerateTypeName<DispatchWorkflow>();
 4137            var stimulus = new DispatchWorkflowStimulus(instanceId);
 4138            var stimulusHash = context.GetRequiredService<IStimulusHasher>().Hash(activityTypeName, stimulus);
 4139            DispatchWorkflowActivationDeniedRoute.Add(properties, activityTypeName, stimulusHash);
 140        }
 141
 5142        var request = new DispatchWorkflowDefinitionRequest(workflowGraph.Workflow.Identity.Id)
 5143        {
 5144            ParentWorkflowInstanceId = parentInstanceId,
 5145            Input = input,
 5146            Properties = properties,
 5147            CorrelationId = correlationId,
 5148            InstanceId = instanceId,
 5149            SchedulingActivityExecutionId = context.Id,
 5150            SchedulingWorkflowInstanceId = parentInstanceId,
 5151            SchedulingCallStackDepth = context.CallStackDepth
 5152        };
 5153        var options = new DispatchWorkflowOptions
 5154        {
 5155            Channel = channelName
 5156        };
 157
 158        // Dispatch the child workflow.
 5159        var dispatchResponse = await workflowDispatcher.DispatchAsync(request, options, context.CancellationToken);
 5160        dispatchResponse.ThrowIfFailed();
 161
 5162        return instanceId;
 5163    }
 164
 165    private async ValueTask OnChildWorkflowCompletedAsync(ActivityExecutionContext context)
 166    {
 4167        var input = context.WorkflowInput;
 4168        context.Set(Result, input);
 4169        await context.CompleteActivityAsync();
 4170    }
 171}