< Summary

Information
Class: Elsa.Workflows.Middleware.Activities.DefaultActivityInvokerMiddleware
Assembly: Elsa.Workflows.Core
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Middleware/Activities/DefaultActivityInvokerMiddleware.cs
Line coverage
73%
Covered lines: 48
Uncovered lines: 17
Coverable lines: 65
Total lines: 173
Line coverage: 73.8%
Branch coverage
63%
Covered branches: 24
Total branches: 38
Branch coverage: 63.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.cctor()100%11100%
InvokeAsync()72.22%231875%
ExecuteActivityAsync()100%22100%
EvaluateInputPropertiesAsync()100%44100%
ShouldCommit(...)35.71%291457.14%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Middleware/Activities/DefaultActivityInvokerMiddleware.cs

#LineLine coverage
 1using System.Reflection;
 2using Elsa.Extensions;
 3using Elsa.Mediator.Contracts;
 4using Elsa.Workflows.Activities;
 5using Elsa.Workflows.CommitStates;
 6using Elsa.Workflows.Pipelines.ActivityExecution;
 7using Microsoft.Extensions.Logging;
 8using Microsoft.Extensions.Options;
 9
 10namespace Elsa.Workflows.Middleware.Activities;
 11
 12/// <summary>
 13/// Provides extension methods to <see cref="IActivityExecutionPipelineBuilder"/>.
 14/// </summary>
 15public static class ActivityInvokerMiddlewareExtensions
 16{
 17    /// <summary>
 18    /// Adds the <see cref="DefaultActivityInvokerMiddleware"/> component to the pipeline.
 19    /// </summary>
 20    public static IActivityExecutionPipelineBuilder UseDefaultActivityInvoker(this IActivityExecutionPipelineBuilder pip
 21}
 22
 23/// <summary>
 24/// A default activity execution middleware component that evaluates the current activity's properties, executes the act
 25/// </summary>
 50126public class DefaultActivityInvokerMiddleware(ActivityMiddlewareDelegate next, ICommitStrategyRegistry commitStrategyReg
 27    : IActivityExecutionMiddleware
 28{
 329    private static readonly MethodInfo ExecuteAsyncMethodInfo = typeof(IActivity).GetMethod(nameof(IActivity.ExecuteAsyn
 30
 31    /// <inheritdoc />
 32    public async ValueTask InvokeAsync(ActivityExecutionContext context)
 33    {
 331634        context.CancellationToken.ThrowIfCancellationRequested();
 35
 331636        var workflowExecutionContext = context.WorkflowExecutionContext;
 37
 38        // Evaluate input properties.
 331639        await EvaluateInputPropertiesAsync(context);
 40
 41        // Prevent the activity from being started if cancellation is requested.
 331642        if (context.CancellationToken.IsCancellationRequested)
 43        {
 044            context.TransitionTo(ActivityStatus.Canceled);
 045            context.AddExecutionLogEntry("Activity cancelled");
 046            return;
 47        }
 48
 49        // Check if the activity can be executed.
 331650        if (!await context.Activity.CanExecuteAsync(context))
 51        {
 052            context.TransitionTo(ActivityStatus.Pending);
 053            context.AddExecutionLogEntry("Precondition Failed", "Cannot execute at this time");
 054            return;
 55        }
 56
 57        // Mark workflow and activity as executing.
 331658        using var executionState = context.EnterExecution();
 59
 60        // Conditionally commit the workflow state.
 331661        if (ShouldCommit(context, ActivityLifetimeEvent.ActivityExecuting))
 062            await context.WorkflowExecutionContext.CommitAsync();
 63
 331664        var previousActivityStatus = context.Status;
 331665        context.TransitionTo(ActivityStatus.Running);
 66
 67        // Execute activity.
 331668        await ExecuteActivityAsync(context);
 69
 330270        var currentActivityStatus = context.Status;
 330271        var activityDidComplete = previousActivityStatus != ActivityStatus.Completed && currentActivityStatus == Activit
 72
 73        // Reset execute delegate.
 330274        workflowExecutionContext.ExecuteDelegate = null;
 75
 76        // If a bookmark was used to resume, burn it if not burnt already by the activity.
 330277        var resumedBookmark = workflowExecutionContext.ResumedBookmarkContext?.Bookmark;
 78
 330279        if (resumedBookmark is { AutoBurn: true })
 80        {
 6181            logger.LogDebug("Auto-burning bookmark {BookmarkId}", resumedBookmark.Id);
 6182            workflowExecutionContext.Bookmarks.Remove(resumedBookmark);
 83        }
 84
 85        // Update execution count.
 330286        context.IncrementExecutionCount();
 87
 88        // Invoke next middleware.
 330289        await next(context);
 90
 91        // If the activity completed, send a notification.
 330292        if (activityDidComplete)
 93        {
 108394            var mediator = context.GetRequiredService<INotificationSender>();
 108395            await mediator.SendAsync(new Notifications.ActivityCompleted(context), context.CancellationToken);
 96        }
 97
 98        // Conditionally commit the workflow state.
 330299        if (ShouldCommit(context, ActivityLifetimeEvent.ActivityExecuted))
 0100            await context.WorkflowExecutionContext.CommitAsync();
 3302101    }
 102
 103    /// <summary>
 104    /// Executes the activity using the specified context.
 105    /// This method is virtual so that modules might override this implementation to do things like e.g. asynchronous pr
 106    /// </summary>
 107    protected virtual async ValueTask ExecuteActivityAsync(ActivityExecutionContext context)
 108    {
 3316109        var executeDelegate = context.WorkflowExecutionContext.ExecuteDelegate
 3316110                              ?? (ExecuteActivityDelegate)Delegate.CreateDelegate(typeof(ExecuteActivityDelegate), conte
 111
 3316112        await executeDelegate(context);
 3302113    }
 114
 115    private async Task EvaluateInputPropertiesAsync(ActivityExecutionContext context)
 116    {
 117        // Evaluate containing composite input properties, if any.
 12914118        var compositeContainerContexts = context.GetAncestors().Where(x => x.Activity is Composite).ToList();
 119
 18790120        foreach (var activityExecutionContext in compositeContainerContexts)
 121        {
 6079122            if (!activityExecutionContext.GetHasEvaluatedProperties())
 39123                await activityExecutionContext.EvaluateInputPropertiesAsync();
 124        }
 125
 126        // Evaluate input properties.
 3316127        await context.EvaluateInputPropertiesAsync();
 3316128    }
 129
 130    private bool ShouldCommit(ActivityExecutionContext context, ActivityLifetimeEvent lifetimeEvent)
 131    {
 6618132        var strategyName = context.Activity.GetCommitStrategy();
 133
 6618134        IActivityCommitStrategy? strategy = !string.IsNullOrWhiteSpace(strategyName)
 6618135            ? commitStrategyRegistry.FindActivityStrategy(strategyName)
 6618136            : commitStateOptions.Value.DefaultActivityCommitStrategy;
 137
 6618138        var commitAction = CommitAction.Default;
 139
 6618140        if (strategy != null)
 141        {
 0142            var strategyContext = new ActivityCommitStateStrategyContext(context, lifetimeEvent);
 0143            commitAction = strategy.ShouldCommit(strategyContext);
 144        }
 145
 146        switch (commitAction)
 147        {
 148            case CommitAction.Skip:
 0149                return false;
 150            case CommitAction.Commit:
 0151                return true;
 152            case CommitAction.Default:
 153                {
 6618154                    var workflowStrategyName = context.WorkflowExecutionContext.Workflow.Options.CommitStrategyName;
 155
 6618156                    IWorkflowCommitStrategy? workflowStrategy = !string.IsNullOrWhiteSpace(workflowStrategyName)
 6618157                        ? commitStrategyRegistry.FindWorkflowStrategy(workflowStrategyName)
 6618158                        : commitStateOptions.Value.DefaultWorkflowCommitStrategy;
 159
 6618160                    if (workflowStrategy == null)
 6618161                        return false;
 162
 0163                    var workflowLifetimeEvent = lifetimeEvent == ActivityLifetimeEvent.ActivityExecuting ? WorkflowLifet
 0164                    var workflowCommitStateStrategyContext = new WorkflowCommitStateStrategyContext(context.WorkflowExec
 0165                    commitAction = workflowStrategy.ShouldCommit(workflowCommitStateStrategyContext);
 166
 0167                    return commitAction == CommitAction.Commit;
 168                }
 169            default:
 0170                throw new ArgumentOutOfRangeException(nameof(commitAction), commitAction, "Unknown commit action");
 171        }
 172    }
 173}