| | | 1 | | using Elsa.Extensions; |
| | | 2 | | using Elsa.Workflows.Activities.Flowchart.Models; |
| | | 3 | | |
| | | 4 | | namespace Elsa.Workflows.Activities.Flowchart.Extensions; |
| | | 5 | | |
| | | 6 | | public static class ActivityExecutionContextExtensions |
| | | 7 | | { |
| | | 8 | | private const string GraphTransientProperty = "FlowGraph"; |
| | | 9 | | |
| | | 10 | | extension(Activities.Flowchart flowchart) |
| | | 11 | | { |
| | | 12 | | public IActivity? GetStartActivity(string? triggerActivityId) |
| | | 13 | | { |
| | 1829 | 14 | | return flowchart.GetTriggerActivity(triggerActivityId) |
| | 1829 | 15 | | ?? flowchart.Start |
| | 1829 | 16 | | ?? flowchart.GetExplicitStartActivity() |
| | 1829 | 17 | | ?? flowchart.GetCanStartWorkflowActivity() |
| | 1829 | 18 | | ?? flowchart.GetRootActivity() |
| | 1829 | 19 | | ?? flowchart.Activities.FirstOrDefault(); |
| | | 20 | | } |
| | | 21 | | |
| | | 22 | | /// <summary> |
| | | 23 | | /// Gets the activity that was triggered by the specified trigger activity ID. |
| | | 24 | | /// </summary> |
| | | 25 | | public IActivity? GetTriggerActivity(string? triggerActivityId) |
| | | 26 | | { |
| | 1829 | 27 | | return triggerActivityId == null |
| | 1829 | 28 | | ? null : |
| | 3252 | 29 | | flowchart.Activities.FirstOrDefault(x => x.Id == triggerActivityId); |
| | | 30 | | } |
| | | 31 | | |
| | | 32 | | /// <summary> |
| | | 33 | | /// Gets the explicit Start activity from the flowchart. |
| | | 34 | | /// </summary> |
| | | 35 | | public IActivity? GetExplicitStartActivity() |
| | | 36 | | { |
| | 1743 | 37 | | return flowchart.Activities.FirstOrDefault(x => x is Start); |
| | | 38 | | } |
| | | 39 | | |
| | | 40 | | /// <summary> |
| | | 41 | | /// Gets the first activity marked as "Can Start Workflow". |
| | | 42 | | /// </summary> |
| | | 43 | | public IActivity? GetCanStartWorkflowActivity() |
| | | 44 | | { |
| | 1735 | 45 | | return flowchart.Activities.FirstOrDefault(x => x.GetCanStartWorkflow()); |
| | | 46 | | } |
| | | 47 | | } |
| | | 48 | | |
| | | 49 | | extension(ActivityExecutionContext context) |
| | | 50 | | { |
| | | 51 | | /// <summary> |
| | | 52 | | /// Checks if there is any pending work for the flowchart. |
| | | 53 | | /// </summary> |
| | | 54 | | public bool HasPendingWork() |
| | | 55 | | { |
| | 1013 | 56 | | return context.HasRunningActivityInstances() |
| | 1013 | 57 | | || context.HasScheduledWork() |
| | 1013 | 58 | | || context.HasUnconsumedTokens() |
| | 1013 | 59 | | || context.HasFaultedChildren(); |
| | | 60 | | } |
| | | 61 | | |
| | | 62 | | public FlowGraph GetFlowGraph() |
| | | 63 | | { |
| | | 64 | | // Store in TransientProperties so FlowChart is not persisted in WorkflowState |
| | 1132 | 65 | | var flowchart = (Activities.Flowchart)context.Activity; |
| | 1132 | 66 | | var startActivity = flowchart.GetStartActivity(context.WorkflowExecutionContext.TriggerActivityId); |
| | 1818 | 67 | | return context.TransientProperties.GetOrAdd(GraphTransientProperty, () => new FlowGraph(flowchart.Connection |
| | | 68 | | } |
| | | 69 | | |
| | | 70 | | public async Task CancelInboundAncestorsAsync(IActivity activity) |
| | | 71 | | { |
| | 22 | 72 | | if(context.Activity is not Activities.Flowchart) |
| | 0 | 73 | | throw new InvalidOperationException("Activity context is not a flowchart."); |
| | | 74 | | |
| | 22 | 75 | | var flowGraph = context.GetFlowGraph(); |
| | 22 | 76 | | var ancestorActivities = flowGraph.GetAncestorActivities(activity); |
| | 172 | 77 | | var inboundActivityExecutionContexts = context.WorkflowExecutionContext.ActivityExecutionContexts.Where(x => |
| | | 78 | | |
| | | 79 | | // Cancel each ancestor activity. |
| | 230 | 80 | | foreach (var activityExecutionContext in inboundActivityExecutionContexts) |
| | | 81 | | { |
| | 93 | 82 | | await activityExecutionContext.CancelActivityAsync(); |
| | | 83 | | } |
| | 22 | 84 | | } |
| | | 85 | | |
| | | 86 | | /// <summary> |
| | | 87 | | /// Checks if the flowchart has any unconsumed tokens. |
| | | 88 | | /// </summary> |
| | | 89 | | public bool HasUnconsumedTokens() |
| | | 90 | | { |
| | 685 | 91 | | var flowchart = (Activities.Flowchart)context.Activity; |
| | 928 | 92 | | return flowchart.GetTokenList(context).Any(x => x is { Consumed: false, Blocked: false }); |
| | | 93 | | } |
| | | 94 | | |
| | | 95 | | /// <summary> |
| | | 96 | | /// Checks if the flowchart has any running activity instances. |
| | | 97 | | /// </summary> |
| | | 98 | | public bool HasRunningActivityInstances() |
| | | 99 | | { |
| | 1013 | 100 | | var flowchart = (Activities.Flowchart)context.Activity; |
| | 3306 | 101 | | var activityIds = flowchart.Activities.Select(x => x.Id).ToList(); |
| | 1013 | 102 | | var children = context.Children; |
| | 4327 | 103 | | return children.Where(x => activityIds.Contains(x.Activity.Id)).Any(x => x.Status == ActivityStatus.Running) |
| | | 104 | | } |
| | | 105 | | |
| | | 106 | | /// <summary> |
| | | 107 | | /// Checks if the flowchart has any scheduled work items. |
| | | 108 | | /// </summary> |
| | | 109 | | public bool HasScheduledWork() |
| | | 110 | | { |
| | 1013 | 111 | | var workflowExecutionContext = context.WorkflowExecutionContext; |
| | | 112 | | |
| | 1013 | 113 | | return workflowExecutionContext.Scheduler.List().Any(workItem => |
| | 1013 | 114 | | { |
| | 328 | 115 | | var ownerInstanceId = workItem.Owner?.Id; |
| | 1013 | 116 | | |
| | 328 | 117 | | if (ownerInstanceId == null) |
| | 0 | 118 | | return false; |
| | 1013 | 119 | | |
| | 328 | 120 | | if (ownerInstanceId == context.Id) |
| | 328 | 121 | | return true; |
| | 1013 | 122 | | |
| | 0 | 123 | | var ownerContext = context.WorkflowExecutionContext.ActivityExecutionContexts.First(x => x.Id == ownerIn |
| | 0 | 124 | | var ancestors = ownerContext.GetAncestors().ToList(); |
| | 1013 | 125 | | |
| | 0 | 126 | | return ancestors.Any(x => x == context); |
| | 1013 | 127 | | }); |
| | | 128 | | } |
| | | 129 | | |
| | | 130 | | public bool HasFaultedChildren() |
| | | 131 | | { |
| | 1784 | 132 | | return context.Children.Any(x => x.Status == ActivityStatus.Faulted); |
| | | 133 | | } |
| | | 134 | | } |
| | | 135 | | } |