< 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: 47
Uncovered lines: 17
Coverable lines: 64
Total lines: 171
Line coverage: 73.4%
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>
 49826public 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    {
 332534        context.CancellationToken.ThrowIfCancellationRequested();
 35
 332536        var workflowExecutionContext = context.WorkflowExecutionContext;
 37
 38        // Evaluate input properties.
 332539        await EvaluateInputPropertiesAsync(context);
 40
 41        // Prevent the activity from being started if cancellation is requested.
 332542        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.
 332550        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.
 332558        using var executionState = context.EnterExecution();
 59
 60        // Conditionally commit the workflow state.
 332561        if (ShouldCommit(context, ActivityLifetimeEvent.ActivityExecuting))
 062            await context.WorkflowExecutionContext.CommitAsync();
 63
 332564        var previousActivityStatus = context.Status;
 332565        context.TransitionTo(ActivityStatus.Running);
 66
 67        // Execute activity.
 332568        await ExecuteActivityAsync(context);
 69
 331270        var currentActivityStatus = context.Status;
 331271        var activityDidComplete = previousActivityStatus != ActivityStatus.Completed && currentActivityStatus == Activit
 72
 73        // Reset execute delegate.
 331274        workflowExecutionContext.ExecuteDelegate = null;
 75
 76        // If a bookmark was used to resume, burn it if not burnt already by the activity.
 331277        var resumedBookmark = workflowExecutionContext.ResumedBookmarkContext?.Bookmark;
 78
 331279        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.
 331286        context.IncrementExecutionCount();
 87
 88        // Invoke next middleware.
 331289        await next(context);
 90
 91        // If the activity completed, send a notification.
 331292        if (activityDidComplete)
 93        {
 108994            var mediator = context.GetRequiredService<INotificationSender>();
 108995            await mediator.SendAsync(new Notifications.ActivityCompleted(context), context.CancellationToken);
 96        }
 97
 98        // Conditionally commit the workflow state.
 331299        if (ShouldCommit(context, ActivityLifetimeEvent.ActivityExecuted))
 0100            await context.WorkflowExecutionContext.CommitAsync();
 3312101    }
 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    {
 3325109        var executeDelegate = context.WorkflowExecutionContext.ExecuteDelegate ?? (ExecuteActivityDelegate)Delegate.Crea
 3325110        await executeDelegate(context);
 3312111    }
 112
 113    private async Task EvaluateInputPropertiesAsync(ActivityExecutionContext context)
 114    {
 115        // Evaluate containing composite input properties, if any.
 12938116        var compositeContainerContexts = context.GetAncestors().Where(x => x.Activity is Composite).ToList();
 117
 18824118        foreach (var activityExecutionContext in compositeContainerContexts)
 119        {
 6087120            if (!activityExecutionContext.GetHasEvaluatedProperties())
 39121                await activityExecutionContext.EvaluateInputPropertiesAsync();
 122        }
 123
 124        // Evaluate input properties.
 3325125        await context.EvaluateInputPropertiesAsync();
 3325126    }
 127
 128    private bool ShouldCommit(ActivityExecutionContext context, ActivityLifetimeEvent lifetimeEvent)
 129    {
 6637130        var strategyName = context.Activity.GetCommitStrategy();
 131
 6637132        IActivityCommitStrategy? strategy = !string.IsNullOrWhiteSpace(strategyName)
 6637133            ? commitStrategyRegistry.FindActivityStrategy(strategyName)
 6637134            : commitStateOptions.Value.DefaultActivityCommitStrategy;
 135
 6637136        var commitAction = CommitAction.Default;
 137
 6637138        if (strategy != null)
 139        {
 0140            var strategyContext = new ActivityCommitStateStrategyContext(context, lifetimeEvent);
 0141            commitAction = strategy.ShouldCommit(strategyContext);
 142        }
 143
 144        switch (commitAction)
 145        {
 146            case CommitAction.Skip:
 0147                return false;
 148            case CommitAction.Commit:
 0149                return true;
 150            case CommitAction.Default:
 151                {
 6637152                    var workflowStrategyName = context.WorkflowExecutionContext.Workflow.Options.CommitStrategyName;
 153
 6637154                    IWorkflowCommitStrategy? workflowStrategy = !string.IsNullOrWhiteSpace(workflowStrategyName)
 6637155                        ? commitStrategyRegistry.FindWorkflowStrategy(workflowStrategyName)
 6637156                        : commitStateOptions.Value.DefaultWorkflowCommitStrategy;
 157
 6637158                    if (workflowStrategy == null)
 6637159                        return false;
 160
 0161                    var workflowLifetimeEvent = lifetimeEvent == ActivityLifetimeEvent.ActivityExecuting ? WorkflowLifet
 0162                    var workflowCommitStateStrategyContext = new WorkflowCommitStateStrategyContext(context.WorkflowExec
 0163                    commitAction = workflowStrategy.ShouldCommit(workflowCommitStateStrategyContext);
 164
 0165                    return commitAction == CommitAction.Commit;
 166                }
 167            default:
 0168                throw new ArgumentOutOfRangeException(nameof(commitAction), commitAction, "Unknown commit action");
 169        }
 170    }
 171}