< Summary

Information
Class: Elsa.Workflows.Runtime.WorkflowCancellationService
Assembly: Elsa.Workflows.Runtime
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Runtime/Services/WorkflowCancellationService.cs
Line coverage
7%
Covered lines: 4
Uncovered lines: 53
Coverable lines: 57
Total lines: 104
Line coverage: 7%
Branch coverage
0%
Covered branches: 0
Total branches: 8
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
CancelWorkflowAsync()0%620%
CancelWorkflowsAsync()100%210%
CancelWorkflowByDefinitionVersionAsync()100%210%
CancelWorkflowByDefinitionAsync()0%620%
CancelWorkflows()100%210%
CancelChildWorkflowInstances()0%2040%

File(s)

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

#LineLine coverage
 1using Elsa.Common.Models;
 2using Elsa.Workflows.Management;
 3using Elsa.Workflows.Management.Entities;
 4using Elsa.Workflows.Management.Filters;
 5using Elsa.Workflows.Runtime.Requests;
 6
 7namespace Elsa.Workflows.Runtime;
 8
 9/// <inheritdoc />
 110public class WorkflowCancellationService(
 111    IWorkflowDefinitionService workflowDefinitionService,
 112    IWorkflowInstanceStore workflowInstanceStore,
 113    IWorkflowCancellationDispatcher dispatcher)
 14    : IWorkflowCancellationService
 15{
 16    /// <inheritdoc />
 17    public async Task<bool> CancelWorkflowAsync(string workflowInstanceId, CancellationToken cancellationToken = default
 18    {
 019        var filter = new WorkflowInstanceFilter
 020        {
 021            Id = workflowInstanceId
 022        };
 023        var instance = await workflowInstanceStore.FindAsync(filter, cancellationToken);
 24
 025        if(instance == null)
 026            return false;
 27
 028        await CancelWorkflows([instance], cancellationToken);
 029        return true;
 030    }
 31
 32    /// <inheritdoc />
 33    public async Task<int> CancelWorkflowsAsync(IEnumerable<string> workflowInstanceIds, CancellationToken cancellationT
 34    {
 035        var filter = new WorkflowInstanceFilter
 036        {
 037            Ids = workflowInstanceIds.ToList()
 038        };
 039        var instances = await workflowInstanceStore.FindManyAsync(filter, cancellationToken);
 040        return await CancelWorkflows(instances.ToList(), cancellationToken);
 041    }
 42
 43    /// <inheritdoc />
 44    public async Task<int> CancelWorkflowByDefinitionVersionAsync(string definitionVersionId, CancellationToken cancella
 45    {
 046        var filter = new WorkflowInstanceFilter
 047        {
 048            DefinitionVersionId = definitionVersionId,
 049            WorkflowStatus = WorkflowStatus.Running
 050        };
 051        var instances = (await workflowInstanceStore.FindManyAsync(filter, cancellationToken)).ToList();
 052        var instanceIds = instances.Select(i => i.Id).ToList();
 53
 054        return await CancelWorkflowsAsync(instanceIds, cancellationToken);
 055    }
 56
 57    /// <inheritdoc />
 58    public async Task<int> CancelWorkflowByDefinitionAsync(string definitionId, VersionOptions versionOptions, Cancellat
 59    {
 60        // Shouldn't we get possible multiple definitions here?
 061        var workflowDefinition = await workflowDefinitionService.FindWorkflowDefinitionAsync(definitionId, versionOption
 062        if (workflowDefinition is null)
 063            return 0;
 64
 065        return await CancelWorkflowByDefinitionVersionAsync(workflowDefinition.Id, cancellationToken);
 066    }
 67
 68    private async Task<int> CancelWorkflows(IList<WorkflowInstance> workflowInstances, CancellationToken cancellationTok
 69    {
 070        var tasks = workflowInstances.Where(i => i.Status != WorkflowStatus.Finished)
 071            .Select(i => dispatcher.DispatchAsync(new DispatchCancelWorkflowRequest
 072            {
 073                WorkflowInstanceId = i.Id
 074            }, cancellationToken)).ToList();
 75
 076        var instanceIds = workflowInstances.Select(i => i.Id).ToList();
 077        await CancelChildWorkflowInstances(instanceIds, cancellationToken);
 078        await Task.WhenAll(tasks);
 79
 080        return tasks.Count;
 081    }
 82
 83    private async Task CancelChildWorkflowInstances(IEnumerable<string> workflowInstanceIds, CancellationToken cancellat
 84    {
 085        var tasks = new List<Task<int>>();
 086        var workflowInstanceIdBatches = workflowInstanceIds.Chunk(50);
 87
 088        foreach (var workflowInstanceIdBatch in workflowInstanceIdBatches)
 89        {
 90            // Find child instances for the current workflow instance ID and cancel them.
 91            // Do not check on status as their children might still be running and need to be canceled.
 092            WorkflowInstanceFilter filter = new()
 093            {
 094                ParentWorkflowInstanceIds = workflowInstanceIdBatch.ToList()
 095            };
 096            var childInstances = (await workflowInstanceStore.FindManyAsync(filter, cancellationToken)).ToList();
 97
 098            if (childInstances.Any())
 099                tasks.Add(CancelWorkflows(childInstances, cancellationToken));
 100        }
 101
 0102        await Task.WhenAll(tasks);
 0103    }
 104}