< Summary

Information
Class: Elsa.Workflows.Runtime.LocalWorkflowClient
Assembly: Elsa.Workflows.Runtime
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/Services/LocalWorkflowClient.cs
Line coverage
93%
Covered lines: 118
Uncovered lines: 8
Coverable lines: 126
Total lines: 216
Line coverage: 93.6%
Branch coverage
71%
Covered branches: 10
Total branches: 14
Branch coverage: 71.4%
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_WorkflowInstanceId()100%11100%
CreateInstanceAsync()100%11100%
RunInstanceAsync()100%11100%
CreateAndRunInstanceAsync()100%11100%
CancelAsync()100%11100%
CancelAsync()100%22100%
ExportStateAsync()100%11100%
ImportStateAsync()100%11100%
InstanceExistsAsync(...)100%210%
DeleteAsync()100%22100%
RunInstanceAsync()50%4477.41%
CreateInstanceInternalAsync()100%11100%
GetWorkflowInstanceAsync()50%22100%
TryGetWorkflowInstanceAsync(...)100%11100%
GetWorkflowGraphAsync()100%11100%
GetWorkflowGraphAsync()75%44100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/Services/LocalWorkflowClient.cs

#LineLine coverage
 1using Elsa.Workflows.Management;
 2using Elsa.Workflows.Management.Entities;
 3using Elsa.Workflows.Management.Exceptions;
 4using Elsa.Workflows.Management.Filters;
 5using Elsa.Workflows.Management.Mappers;
 6using Elsa.Workflows.Management.Options;
 7using Elsa.Workflows.Models;
 8using Elsa.Workflows.Options;
 9using Elsa.Workflows.Runtime.Exceptions;
 10using Elsa.Workflows.Runtime.Messages;
 11using Elsa.Workflows.State;
 12using Microsoft.Extensions.Logging;
 13
 14namespace Elsa.Workflows.Runtime;
 15
 16/// <summary>
 17/// Represents a client for executing and managing local workflows.
 18/// </summary>
 18819public class LocalWorkflowClient(
 18820    string workflowInstanceId,
 18821    IWorkflowInstanceManager workflowInstanceManager,
 18822    IWorkflowDefinitionService workflowDefinitionService,
 18823    IWorkflowRunner workflowRunner,
 18824    IWorkflowCanceler workflowCanceler,
 18825    WorkflowStateMapper workflowStateMapper,
 18826    ILogger<LocalWorkflowClient> logger) : IWorkflowClient
 27{
 28    /// <inheritdoc />
 44829    public string WorkflowInstanceId => workflowInstanceId;
 30
 31    /// <inheritdoc />
 32    public async Task<CreateWorkflowInstanceResponse> CreateInstanceAsync(CreateWorkflowInstanceRequest request, Cancell
 33    {
 3634        var workflowDefinitionHandle = request.WorkflowDefinitionHandle;
 3635        var workflowGraph = await GetWorkflowGraphAsync(workflowDefinitionHandle, cancellationToken);
 36
 3437        var options = new WorkflowInstanceOptions
 3438        {
 3439            WorkflowInstanceId = WorkflowInstanceId,
 3440            CorrelationId = request.CorrelationId,
 3441            Name = request.Name,
 3442            ParentWorkflowInstanceId = request.ParentId,
 3443            Input = request.Input,
 3444            Properties = request.Properties
 3445        };
 46
 3447        await workflowInstanceManager.CreateAndCommitWorkflowInstanceAsync(workflowGraph.Workflow, options, cancellation
 3448        return new();
 3449    }
 50
 51    /// <inheritdoc />
 52    public async Task<RunWorkflowInstanceResponse> RunInstanceAsync(RunWorkflowInstanceRequest request, CancellationToke
 53    {
 7854        var workflowInstance = await GetWorkflowInstanceAsync(cancellationToken);
 7855        return await RunInstanceAsync(workflowInstance, request, cancellationToken);
 7856    }
 57
 58    /// <inheritdoc />
 59    public async Task<RunWorkflowInstanceResponse> CreateAndRunInstanceAsync(CreateAndRunWorkflowInstanceRequest request
 60    {
 4661        var createRequest = new CreateWorkflowInstanceRequest
 4662        {
 4663            Properties = request.Properties,
 4664            CorrelationId = request.CorrelationId,
 4665            Name = request.Name,
 4666            Input = request.Input,
 4667            WorkflowDefinitionHandle = request.WorkflowDefinitionHandle,
 4668            ParentId = request.ParentId
 4669        };
 4670        var workflowInstance = await CreateInstanceInternalAsync(createRequest, cancellationToken);
 4471        return await RunInstanceAsync(workflowInstance, new()
 4472        {
 4473            Input = request.Input,
 4474            Variables = request.Variables,
 4475            Properties = request.Properties,
 4476            TriggerActivityId = request.TriggerActivityId,
 4477            ActivityHandle = request.ActivityHandle,
 4478            IncludeWorkflowOutput = request.IncludeWorkflowOutput
 4479        }, cancellationToken);
 4480    }
 81
 82    /// <inheritdoc />
 83    public async Task CancelAsync(CancellationToken cancellationToken = default)
 84    {
 185        var workflowInstance = await GetWorkflowInstanceAsync(cancellationToken);
 186        await CancelAsync(workflowInstance, cancellationToken);
 187    }
 88
 89    private async Task CancelAsync(WorkflowInstance workflowInstance, CancellationToken cancellationToken)
 90    {
 1191        if (workflowInstance.Status != WorkflowStatus.Running) return;
 592        var workflowGraph = await GetWorkflowGraphAsync(workflowInstance, cancellationToken);
 593        var workflowState = await workflowCanceler.CancelWorkflowAsync(workflowGraph, workflowInstance.WorkflowState, ca
 594        await workflowInstanceManager.SaveAsync(workflowState, cancellationToken);
 895    }
 96
 97    /// <inheritdoc />
 98    public async Task<WorkflowState> ExportStateAsync(CancellationToken cancellationToken = default)
 99    {
 55100        var workflowInstance = await GetWorkflowInstanceAsync(cancellationToken);
 55101        return workflowInstance.WorkflowState;
 55102    }
 103
 104    /// <inheritdoc />
 105    public async Task ImportStateAsync(WorkflowState workflowState, CancellationToken cancellationToken = default)
 106    {
 1107        var workflowInstance = workflowStateMapper.Map(workflowState)!;
 1108        await workflowInstanceManager.SaveAsync(workflowInstance, cancellationToken);
 1109    }
 110
 111    public Task<bool> InstanceExistsAsync(CancellationToken cancellationToken = default)
 112    {
 0113        return workflowInstanceManager.ExistsAsync(workflowInstanceId, cancellationToken);
 114    }
 115
 116    /// <inheritdoc />
 117    public async Task<bool> DeleteAsync(CancellationToken cancellationToken = default)
 118    {
 119        // Load the workflow instance (single DB call)
 8120        var workflowInstance = await TryGetWorkflowInstanceAsync(cancellationToken);
 8121        if (workflowInstance == null)
 1122            return false;
 123
 7124        await CancelAsync(workflowInstance, cancellationToken);
 125
 126        // Delete the workflow instance
 7127        var filter = new WorkflowInstanceFilter { Id = workflowInstanceId };
 7128        await workflowInstanceManager.DeleteAsync(filter, cancellationToken);
 7129        return true;
 8130    }
 131
 132    public async Task<RunWorkflowInstanceResponse> RunInstanceAsync(WorkflowInstance workflowInstance, RunWorkflowInstan
 133    {
 175134        var workflowState = workflowInstance.WorkflowState;
 135
 175136        if (workflowInstance.Status != WorkflowStatus.Running)
 137        {
 0138            logger.LogWarning("Attempt to resume workflow {WorkflowInstanceId} that is not in the Running state. The act
 0139            return new()
 0140            {
 0141                WorkflowInstanceId = WorkflowInstanceId,
 0142                Status = workflowInstance.Status,
 0143                SubStatus = workflowInstance.SubStatus
 0144            };
 145        }
 146
 175147        var runWorkflowOptions = new RunWorkflowOptions
 175148        {
 175149            Input = request.Input,
 175150            Variables = request.Variables,
 175151            Properties = request.Properties,
 175152            BookmarkId = request.BookmarkId,
 175153            TriggerActivityId = request.TriggerActivityId,
 175154            ActivityHandle = request.ActivityHandle,
 175155        };
 156
 175157        var workflowGraph = await GetWorkflowGraphAsync(workflowInstance, cancellationToken);
 175158        var workflowResult = await workflowRunner.RunAsync(workflowGraph, workflowState, runWorkflowOptions, cancellatio
 159
 175160        workflowState = workflowResult.WorkflowState;
 161
 175162        return new()
 175163        {
 175164            WorkflowInstanceId = WorkflowInstanceId,
 175165            Status = workflowState.Status,
 175166            SubStatus = workflowState.SubStatus,
 175167            Incidents = workflowState.Incidents,
 175168            Output = request.IncludeWorkflowOutput ? new Dictionary<string, object>(workflowState.Output) : null,
 175169            Bookmarks = workflowState.Bookmarks
 175170        };
 175171    }
 172
 173    public async Task<WorkflowInstance> CreateInstanceInternalAsync(CreateWorkflowInstanceRequest request, CancellationT
 174    {
 99175        var workflowDefinitionHandle = request.WorkflowDefinitionHandle;
 99176        var workflowGraph = await GetWorkflowGraphAsync(workflowDefinitionHandle, cancellationToken);
 177
 97178        var options = new WorkflowInstanceOptions
 97179        {
 97180            WorkflowInstanceId = WorkflowInstanceId,
 97181            CorrelationId = request.CorrelationId,
 97182            Name = request.Name,
 97183            ParentWorkflowInstanceId = request.ParentId,
 97184            Input = request.Input,
 97185            Properties = request.Properties
 97186        };
 187
 97188        return workflowInstanceManager.CreateWorkflowInstance(workflowGraph.Workflow, options);
 97189    }
 190
 191    private async Task<WorkflowInstance> GetWorkflowInstanceAsync(CancellationToken cancellationToken)
 192    {
 134193        var workflowInstance = await TryGetWorkflowInstanceAsync(cancellationToken);
 134194        if (workflowInstance == null) throw new WorkflowInstanceNotFoundException("Workflow instance not found.", Workfl
 134195        return workflowInstance;
 134196    }
 197
 198    private Task<WorkflowInstance?> TryGetWorkflowInstanceAsync(CancellationToken cancellationToken)
 199    {
 142200        return workflowInstanceManager.FindByIdAsync(WorkflowInstanceId, cancellationToken);
 201    }
 202
 203    private async Task<WorkflowGraph> GetWorkflowGraphAsync(WorkflowInstance workflowInstance, CancellationToken cancell
 204    {
 180205        var handle = WorkflowDefinitionHandle.ByDefinitionVersionId(workflowInstance.DefinitionVersionId);
 180206        return await GetWorkflowGraphAsync(handle, cancellationToken);
 180207    }
 208
 209    private async Task<WorkflowGraph> GetWorkflowGraphAsync(WorkflowDefinitionHandle definitionHandle, CancellationToken
 210    {
 315211        var result = await workflowDefinitionService.TryFindWorkflowGraphAsync(definitionHandle, cancellationToken);
 317212        if (!result.WorkflowDefinitionExists) throw new WorkflowDefinitionNotFoundException("Workflow definition not fou
 315213        if (!result.WorkflowGraphExists) throw new WorkflowMaterializerNotFoundException(result.WorkflowDefinition!.Mate
 311214        return result.WorkflowGraph!;
 311215    }
 216}