< Summary

Line coverage
88%
Covered lines: 268
Uncovered lines: 34
Coverable lines: 302
Total lines: 763
Line coverage: 88.7%
Branch coverage
68%
Covered branches: 64
Total branches: 93
Branch coverage: 68.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
File 1: .ctor(...)100%11100%
File 1: Cancel()100%22100%
File 1: CancelWorkflow()75%4488.88%
File 2: .cctor()100%11100%
File 2: Complete(...)100%11100%
File 2: Noop(...)100%210%
File 2: .ctor(...)100%44100%
File 2: CreateAsync()100%11100%
File 2: CreateAsync()100%11100%
File 2: CreateAsync()100%11100%
File 2: CreateAsync()100%11100%
File 2: SetWorkflowGraphAsync()50%2283.33%
File 2: get_ServiceProvider()100%11100%
File 2: get_ActivityRegistry()100%11100%
File 2: get_ActivityRegistryLookup()100%11100%
File 2: get_WorkflowGraph()100%11100%
File 2: get_Workflow()100%11100%
File 2: get_Graph()100%210%
File 2: get_Status()100%11100%
File 2: get_SubStatus()100%11100%
File 2: get_IsExecuting()100%11100%
File 2: get_MemoryRegister()100%11100%
File 2: get_Id()100%11100%
File 2: get_Activity()100%210%
File 2: get_CorrelationId()100%11100%
File 2: get_Name()100%11100%
File 2: get_ParentWorkflowInstanceId()100%11100%
File 2: get_CreatedAt()100%11100%
File 2: get_UpdatedAt()100%11100%
File 2: get_FinishedAt()100%11100%
File 2: get_SystemClock()100%11100%
File 2: get_Nodes()100%11100%
File 2: get_NodeIdLookup()100%11100%
File 2: get_NodeHashLookup()100%210%
File 2: get_NodeActivityLookup()100%11100%
File 2: get_Scheduler()100%11100%
File 2: get_IdentityGenerator()100%11100%
File 2: get_OriginalBookmarks()100%11100%
File 2: get_Bookmarks()100%11100%
File 2: get_BookmarksDiff()100%11100%
File 2: get_Input()100%11100%
File 2: get_Output()100%11100%
File 2: get_Properties()100%11100%
File 2: get_TransientProperties()100%11100%
File 2: get_Incidents()100%11100%
File 2: get_ExecuteDelegate()100%11100%
File 2: get_ResumedBookmarkContext()100%11100%
File 2: get_TriggerActivityId()100%11100%
File 2: get_CancellationToken()100%11100%
File 2: get_CompletionCallbacks()100%11100%
File 2: get_ActivityExecutionContexts()100%11100%
File 2: set_ActivityExecutionContexts(...)100%11100%
File 2: get_ExecutionLogSequence()100%11100%
File 2: get_ExecutionLog()100%11100%
File 2: get_ExpressionExecutionContext()100%11100%
File 2: get_Variables()100%210%
File 2: GetRequiredService()100%11100%
File 2: GetRequiredService(...)100%11100%
File 2: GetOrCreateService()100%210%
File 2: GetOrCreateService(...)100%210%
File 2: GetService()100%11100%
File 2: GetService(...)100%210%
File 2: GetServices()100%11100%
File 2: AddCompletionCallback(...)100%11100%
File 2: PopCompletionCallback(...)100%44100%
File 2: RemoveCompletionCallback(...)100%11100%
File 2: RemoveCompletionCallbacks(...)100%22100%
File 2: ClearCompletionCallbacks()100%11100%
File 2: FindActivity(...)0%7280%
File 2: FindNodeById(...)50%22100%
File 2: FindNodeByHash(...)0%620%
File 2: FindNodeByActivity(...)50%22100%
File 2: FindNodeByActivityId(...)100%210%
File 2: FindActivityByNodeId(...)50%22100%
File 2: FindActivityById(...)0%620%
File 2: FindActivityByHash(...)0%620%
File 2: FindActivityByInstanceId(...)0%620%
File 2: GetProperty(...)50%22100%
File 2: SetProperty(...)100%210%
File 2: UpdateProperty(...)100%210%
File 2: HasProperty(...)100%210%
File 2: CanTransitionTo(...)100%11100%
File 2: TransitionTo(...)90%101090%
File 2: CreateActivityExecutionContextAsync()90%2020100%
File 2: GetActivityOutputRegister()100%11100%
File 2: GetLastActivityResult()0%620%
File 2: AddActivityExecutionContext(...)100%11100%
File 2: RemoveActivityExecutionContext(...)50%22100%
File 2: RemoveActivityExecutionContexts(...)100%22100%
File 2: ClearCompletedActivityExecutionContexts()100%44100%
File 2: GetActiveActivityExecutionContexts()100%22100%
File 2: RecordActivityOutput(...)100%22100%
File 2: GetMainStatus(...)85.71%7790%
File 2: ValidateStatusTransition()100%11100%
File 2: CommitAsync()100%210%
File 3: AddExecutionLogEntry(...)100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.Cancel.cs

#LineLine coverage
 1namespace Elsa.Workflows;
 2
 3/// <summary>
 4/// Provides context to the currently executing workflow.
 5/// </summary>
 6public partial class WorkflowExecutionContext
 7{
 9298    private ICollection<CancellationTokenSource> _cancellationTokenSources = new List<CancellationTokenSource>();
 9299    private ICollection<CancellationTokenRegistration> _cancellationRegistrations = new List<CancellationTokenRegistrati
 10
 11    /// <summary>
 12    /// Cancels the workflow and all it's children.
 13    /// </summary>
 14    public void Cancel()
 15    {
 1616        foreach (var source in _cancellationTokenSources)
 417            source.Cancel();
 18
 419        _cancellationTokenSources.Clear();
 420    }
 21
 22    private void CancelWorkflow()
 23    {
 424        Bookmarks.Clear();
 425        _completionCallbackEntries.Clear();
 26
 427        if (!CanTransitionTo(WorkflowSubStatus.Cancelled))
 028            return;
 29
 430        AddExecutionLogEntry("Workflow cancelled");
 31
 432        TransitionTo(WorkflowSubStatus.Cancelled);
 33
 1634        foreach (var registration in _cancellationRegistrations)
 435            registration.Dispose();
 436    }
 37}

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs

#LineLine coverage
 1using System.Collections.ObjectModel;
 2using Elsa.Common;
 3using Elsa.Expressions.Helpers;
 4using Elsa.Expressions.Models;
 5using Elsa.Extensions;
 6using Elsa.Workflows.Activities;
 7using Elsa.Workflows.CommitStates;
 8using Elsa.Workflows.Exceptions;
 9using Elsa.Workflows.Helpers;
 10using Elsa.Workflows.Memory;
 11using Elsa.Workflows.Models;
 12using Elsa.Workflows.Options;
 13using Elsa.Workflows.State;
 14using JetBrains.Annotations;
 15using Microsoft.Extensions.DependencyInjection;
 16
 17namespace Elsa.Workflows;
 18
 19/// <summary>
 20/// A delegate entry that is used by activities to be notified when the activities they scheduled are completed.
 21/// </summary>
 22/// <param name="Owner">The activity scheduling the <see cref="Child"/> activity.</param>
 23/// <param name="Child">The child <see cref="IActivity"/> being scheduled.</param>
 24/// <param name="CompletionCallback">The <see cref="ActivityCompletionCallback"/> delegate to invoke when the scheduled 
 25/// <param name="Tag">An optional tag.</param>
 26public record ActivityCompletionCallbackEntry(ActivityExecutionContext Owner, ActivityNode Child, ActivityCompletionCall
 27
 28/// <summary>
 29/// Provides context to the currently executing workflow.
 30/// </summary>
 31[PublicAPI]
 32public partial class WorkflowExecutionContext : IExecutionContext
 33{
 534    private static readonly object ActivityOutputRegistryKey = new();
 535    private static readonly object LastActivityResultKey = new();
 1636    internal static ValueTask Complete(ActivityExecutionContext context) => context.CompleteActivityAsync();
 037    internal static ValueTask Noop(ActivityExecutionContext context) => default;
 92938    private readonly IList<ActivityCompletionCallbackEntry> _completionCallbackEntries = new List<ActivityCompletionCall
 39    private IList<ActivityExecutionContext> _activityExecutionContexts;
 40    private readonly IHasher _hasher;
 41    private readonly ICommitStateHandler _commitStateHandler;
 42
 43    /// <summary>
 44    /// Initializes a new instance of <see cref="WorkflowExecutionContext"/>.
 45    /// </summary>
 92946    private WorkflowExecutionContext(
 92947        IServiceProvider serviceProvider,
 92948        WorkflowGraph workflowGraph,
 92949        string id,
 92950        string? correlationId,
 92951        string? parentWorkflowInstanceId,
 92952        IDictionary<string, object>? input,
 92953        IDictionary<string, object>? properties,
 92954        ExecuteActivityDelegate? executeDelegate,
 92955        string? triggerActivityId,
 92956        IEnumerable<ActivityIncident> incidents,
 92957        IEnumerable<Bookmark> originalBookmarks,
 92958        DateTimeOffset createdAt,
 92959        CancellationToken cancellationToken)
 60    {
 92961        ServiceProvider = serviceProvider;
 92962        SystemClock = serviceProvider.GetRequiredService<ISystemClock>();
 92963        ActivityRegistry = serviceProvider.GetRequiredService<IActivityRegistry>();
 92964        ActivityRegistryLookup = serviceProvider.GetRequiredService<IActivityRegistryLookupService>();
 92965        _hasher = serviceProvider.GetRequiredService<IHasher>();
 92966        _commitStateHandler = serviceProvider.GetRequiredService<ICommitStateHandler>();
 92967        SubStatus = WorkflowSubStatus.Pending;
 92968        Id = id;
 92969        CorrelationId = correlationId;
 92970        ParentWorkflowInstanceId = parentWorkflowInstanceId;
 92971        _activityExecutionContexts = new List<ActivityExecutionContext>();
 92972        Scheduler = serviceProvider.GetRequiredService<IActivitySchedulerFactory>().CreateScheduler();
 92973        IdentityGenerator = serviceProvider.GetRequiredService<IIdentityGenerator>();
 92974        Input = input != null ? new(input, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComp
 92975        Properties = properties != null ? new(properties, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, obj
 92976        ExecuteDelegate = executeDelegate;
 92977        TriggerActivityId = triggerActivityId;
 92978        CreatedAt = createdAt;
 92979        UpdatedAt = createdAt;
 92980        CancellationToken = cancellationToken;
 92981        Incidents = incidents.ToList();
 92982        OriginalBookmarks = originalBookmarks.ToList();
 92983        WorkflowGraph = workflowGraph;
 92984        var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
 92985        _cancellationTokenSources.Add(linkedCancellationTokenSource);
 92986        _cancellationRegistrations.Add(linkedCancellationTokenSource.Token.Register(CancelWorkflow));
 92987    }
 88
 89    /// <summary>
 90    /// Creates a new <see cref="WorkflowExecutionContext"/> for the specified workflow.
 91    /// </summary>
 92    public static async Task<WorkflowExecutionContext> CreateAsync(
 93        IServiceProvider serviceProvider,
 94        WorkflowGraph workflowGraph,
 95        string id,
 96        CancellationToken cancellationToken = default)
 97    {
 41598        var systemClock = serviceProvider.GetRequiredService<ISystemClock>();
 99
 415100        return await CreateAsync(
 415101            serviceProvider,
 415102            workflowGraph,
 415103            id,
 415104            new List<ActivityIncident>(),
 415105            new List<Bookmark>(),
 415106            systemClock.UtcNow,
 415107            cancellationToken: cancellationToken
 415108        );
 415109    }
 110
 111    /// <summary>
 112    /// Creates a new <see cref="WorkflowExecutionContext"/> for the specified workflow.
 113    /// </summary>
 114    public static async Task<WorkflowExecutionContext> CreateAsync(
 115        IServiceProvider serviceProvider,
 116        WorkflowGraph workflowGraph,
 117        string id,
 118        string? correlationId,
 119        string? parentWorkflowInstanceId = null,
 120        IDictionary<string, object>? input = null,
 121        IDictionary<string, object>? properties = null,
 122        ExecuteActivityDelegate? executeDelegate = null,
 123        string? triggerActivityId = null,
 124        CancellationToken cancellationToken = default)
 125    {
 376126        var systemClock = serviceProvider.GetRequiredService<ISystemClock>();
 127
 376128        return await CreateAsync(
 376129            serviceProvider,
 376130            workflowGraph,
 376131            id,
 376132            new List<ActivityIncident>(),
 376133            new List<Bookmark>(),
 376134            systemClock.UtcNow,
 376135            correlationId,
 376136            parentWorkflowInstanceId,
 376137            input,
 376138            properties,
 376139            executeDelegate,
 376140            triggerActivityId,
 376141            cancellationToken
 376142        );
 376143    }
 144
 145    /// <summary>
 146    /// Creates a new <see cref="WorkflowExecutionContext"/> for the specified workflow.
 147    /// </summary>
 148    public static async Task<WorkflowExecutionContext> CreateAsync(
 149        IServiceProvider serviceProvider,
 150        WorkflowGraph workflowGraph,
 151        WorkflowState workflowState,
 152        string? correlationId = null,
 153        string? parentWorkflowInstanceId = null,
 154        IDictionary<string, object>? input = null,
 155        IDictionary<string, object>? properties = null,
 156        ExecuteActivityDelegate? executeDelegate = null,
 157        string? triggerActivityId = null,
 158        CancellationToken cancellationToken = default)
 159    {
 138160        var workflowExecutionContext = await CreateAsync(
 138161            serviceProvider,
 138162            workflowGraph,
 138163            workflowState.Id,
 138164            workflowState.Incidents,
 138165            workflowState.Bookmarks,
 138166            workflowState.CreatedAt,
 138167            correlationId,
 138168            parentWorkflowInstanceId,
 138169            input,
 138170            properties,
 138171            executeDelegate,
 138172            triggerActivityId,
 138173            cancellationToken);
 174
 138175        var workflowStateExtractor = serviceProvider.GetRequiredService<IWorkflowStateExtractor>();
 138176        await workflowStateExtractor.ApplyAsync(workflowExecutionContext, workflowState);
 177
 138178        return workflowExecutionContext;
 138179    }
 180
 181    /// <summary>
 182    /// Creates a new <see cref="WorkflowExecutionContext"/> for the specified workflow.
 183    /// </summary>
 184    public static async Task<WorkflowExecutionContext> CreateAsync(
 185        IServiceProvider serviceProvider,
 186        WorkflowGraph workflowGraph,
 187        string id,
 188        IEnumerable<ActivityIncident> incidents,
 189        IEnumerable<Bookmark> originalBookmarks,
 190        DateTimeOffset createdAt,
 191        string? correlationId = null,
 192        string? parentWorkflowInstanceId = null,
 193        IDictionary<string, object>? input = null,
 194        IDictionary<string, object>? properties = null,
 195        ExecuteActivityDelegate? executeDelegate = null,
 196        string? triggerActivityId = null,
 197        CancellationToken cancellationToken = default)
 198    {
 199        // Set up a workflow execution context.
 929200        var workflowExecutionContext = new WorkflowExecutionContext(
 929201            serviceProvider,
 929202            workflowGraph,
 929203            id,
 929204            correlationId,
 929205            parentWorkflowInstanceId,
 929206            input,
 929207            properties,
 929208            executeDelegate,
 929209            triggerActivityId,
 929210            incidents,
 929211            originalBookmarks,
 929212            createdAt,
 929213            cancellationToken)
 929214        {
 929215            MemoryRegister = workflowGraph.Workflow.CreateRegister()
 929216        };
 217
 929218        workflowExecutionContext.ExpressionExecutionContext = new(serviceProvider, workflowExecutionContext.MemoryRegist
 219
 929220        await workflowExecutionContext.SetWorkflowGraphAsync(workflowGraph);
 929221        return workflowExecutionContext;
 929222    }
 223
 224    /// <summary>
 225    /// Assigns the specified workflow to this workflow execution context.
 226    /// </summary>
 227    /// <param name="workflowGraph">The workflow graph to assign.</param>
 228    public async Task SetWorkflowGraphAsync(WorkflowGraph workflowGraph)
 229    {
 929230        WorkflowGraph = workflowGraph;
 929231        var nodes = workflowGraph.Nodes;
 232
 233        // Register activity types.
 234        var activityTypes = nodes.Select(x => x.Activity.GetType()).Distinct().ToList();
 929235        await ActivityRegistry.RegisterAsync(activityTypes, CancellationToken);
 236
 237        // Update the activity execution contexts with the actual activity instances.
 1858238        foreach (var activityExecutionContext in ActivityExecutionContexts)
 0239            activityExecutionContext.Activity = workflowGraph.NodeIdLookup[activityExecutionContext.Activity.NodeId].Act
 929240    }
 241
 242    /// Gets the <see cref="IServiceProvider"/>.
 45001243    public IServiceProvider ServiceProvider { get; }
 244
 245    /// Gets the <see cref="IActivityRegistry"/>.
 929246    public IActivityRegistry ActivityRegistry { get; }
 247
 248    /// Gets the <see cref="IActivityRegistryLookupService"/>.
 3879249    public IActivityRegistryLookupService ActivityRegistryLookup { get; }
 250
 251    /// Gets the workflow graph.
 75143252    public WorkflowGraph WorkflowGraph { get; private set; }
 253
 254    /// The <see cref="Workflow"/> associated with the execution context.
 35485255    public Workflow Workflow => WorkflowGraph.Workflow;
 256
 257    /// A graph of the workflow structure.
 0258    public ActivityNode Graph => WorkflowGraph.Root;
 259
 260    /// The current status of the workflow.
 4109261    public WorkflowStatus Status => GetMainStatus(SubStatus);
 262
 263    /// The current sub status of the workflow.
 10253264    public WorkflowSubStatus SubStatus { get; internal set; }
 265
 266    /// <summary>
 267    /// Gets or sets a value indicating whether the workflow instance is actively executing.
 268    /// </summary>
 269    /// <remarks>
 270    /// This flag is set to <c>true</c> immediately before the workflow begins execution
 271    /// and is set to <c>false</c> once the execution is completed.
 272    /// It can be used to determine if a workflow instance was in-progress in case of unexpected
 273    /// application termination, allowing the system to retry execution upon restarting.
 274    /// </remarks>
 7283275    public bool IsExecuting { get; set; }
 276
 277    /// The root <see cref="MemoryRegister"/> associated with the execution context.
 2190278    public MemoryRegister MemoryRegister { get; private set; } = null!;
 279
 280    /// A unique ID of the execution context.
 13553281    public string Id { get; set; }
 282
 283    /// <inheritdoc />
 0284    public IActivity Activity => Workflow;
 285
 286    /// An application-specific identifier associated with the execution context.
 2148287    public string? CorrelationId { get; set; }
 288
 289    /// Gets or sets the name of the workflow instance.
 670290    public string? Name { get; set; }
 291
 292    /// The ID of the workflow instance that triggered this instance.
 1580293    public string? ParentWorkflowInstanceId { get; set; }
 294
 295    /// The date and time the workflow execution context was created.
 1580296    public DateTimeOffset CreatedAt { get; set; }
 297
 298    /// The date and time the workflow execution context was last updated.
 3551299    public DateTimeOffset UpdatedAt { get; set; }
 300
 301    /// The date and time the workflow execution context has finished.
 1118302    public DateTimeOffset? FinishedAt { get; set; }
 303
 304    /// Gets the clock used to determine the current time.
 12657305    public ISystemClock SystemClock { get; }
 306
 307    /// A flattened list of <see cref="ActivityNode"/>s from the <see cref="Graph"/>.
 9308    public IReadOnlyCollection<ActivityNode> Nodes => WorkflowGraph.Nodes.ToList();
 309
 310    /// A map between activity IDs and <see cref="ActivityNode"/>s in the workflow graph.
 254311    public IDictionary<string, ActivityNode> NodeIdLookup => WorkflowGraph.NodeIdLookup;
 312
 313    /// A map between hashed activity node IDs and <see cref="ActivityNode"/>s in the workflow graph.
 0314    public IDictionary<string, ActivityNode> NodeHashLookup => WorkflowGraph.NodeHashLookup;
 315
 316    /// A map between <see cref="IActivity"/>s and <see cref="ActivityNode"/>s in the workflow graph.
 37537317    public IDictionary<IActivity, ActivityNode> NodeActivityLookup => WorkflowGraph.NodeActivityLookup;
 318
 319    /// The <see cref="IActivityScheduler"/> for the execution context.
 5772320    public IActivityScheduler Scheduler { get; }
 321
 322    /// Gets the <see cref="IIdentityGenerator"/>.
 3879323    public IIdentityGenerator IdentityGenerator { get; }
 324
 325    /// Gets the collection of original bookmarks associated with the workflow execution context.
 1438326    public ICollection<Bookmark> OriginalBookmarks { get; set; }
 327
 328    /// A collection of collected bookmarks during workflow execution.
 18064329    public ICollection<Bookmark> Bookmarks { get; set; } = new List<Bookmark>();
 330
 331    /// A diff between the original bookmarks and the current bookmarks.
 509332    public Diff<Bookmark> BookmarksDiff => Diff.For(OriginalBookmarks, Bookmarks);
 333
 334    /// <summary>
 335    /// A dictionary of inputs provided at the start of the current workflow execution.
 336    /// </summary>
 5686337    public IDictionary<string, object> Input { get; set; }
 338
 339    /// <summary>
 340    /// A dictionary of outputs provided by the current workflow execution.
 341    /// </summary>
 1614342    public IDictionary<string, object> Output { get; set; } = new Dictionary<string, object>();
 343
 344    /// <inheritdoc />
 2683345    public IDictionary<string, object> Properties { get; set; }
 346
 347    /// <summary>
 348    /// A dictionary that can be used by application code and middleware to store information and even services. Values 
 349    /// All data will be gone once workflow execution completes.
 350    /// </summary>
 6157351    public IDictionary<object, object> TransientProperties { get; set; } = new Dictionary<object, object>();
 352
 353    /// <summary>
 354    /// A collection of incidents that may have occurred during execution.
 355    /// </summary>
 1458356    public ICollection<ActivityIncident> Incidents { get; set; }
 357
 358    /// <summary>
 359    /// The current <see cref="ExecuteActivityDelegate"/> delegate to invoke when executing the next activity.
 360    /// </summary>
 10902361    public ExecuteActivityDelegate? ExecuteDelegate { get; set; }
 362
 363    /// <summary>
 364    /// Provides context about the bookmark that was used to resume workflow execution, if any.
 365    /// </summary>
 3341366    public ResumedBookmarkContext? ResumedBookmarkContext { get; set; }
 367
 368    /// <summary>
 369    /// The ID of the activity associated with the trigger that caused this workflow execution, if any.
 370    /// </summary>
 2985371    public string? TriggerActivityId { get; set; }
 372
 373    /// <summary>
 374    /// A set of cancellation tokens that can be used to cancel the workflow execution without cancelling system-level o
 375    /// </summary>
 10675376    public CancellationToken CancellationToken { get; }
 377
 378    /// <summary>
 379    /// A list of <see cref="ActivityCompletionCallbackEntry"/> callbacks that are invoked when the associated child act
 380    /// </summary>
 4435381    public ICollection<ActivityCompletionCallbackEntry> CompletionCallbacks => new ReadOnlyCollection<ActivityCompletion
 382
 383    /// <summary>
 384    /// A list of <see cref="ActivityExecutionContext"/>s that are currently active.
 385    /// </summary>
 386    public IReadOnlyCollection<ActivityExecutionContext> ActivityExecutionContexts
 387    {
 8344388        get => _activityExecutionContexts.AsReadOnly();
 138389        internal set => _activityExecutionContexts = value.ToList();
 390    }
 391
 392    /// <summary>
 393    /// The last execution log sequence number. This number is incremented every time a new entry is added to the execut
 394    /// </summary>
 14245395    public long ExecutionLogSequence { get; set; }
 396
 397    /// <summary>
 398    /// A collection of execution log entries. This collection is flushed when the workflow execution context ends.
 399    /// </summary>
 9253400    public ICollection<WorkflowExecutionLogEntry> ExecutionLog { get; } = new List<WorkflowExecutionLogEntry>();
 401
 402    /// <summary>
 403    /// The expression execution context for the current workflow execution.
 404    /// </summary>
 2001405    public ExpressionExecutionContext ExpressionExecutionContext { get; private set; } = null!;
 406
 407    /// <inheritdoc />
 0408    public IEnumerable<Variable> Variables => Workflow.Variables;
 409
 410    /// <summary>
 411    /// Resolves the specified service type from the service provider.
 412    /// </summary>
 36580413    public T GetRequiredService<T>() where T : notnull => ServiceProvider.GetRequiredService<T>();
 414
 415    /// <summary>
 416    /// Resolves the specified service type from the service provider.
 417    /// </summary>
 3048418    public object GetRequiredService(Type serviceType) => ServiceProvider.GetRequiredService(serviceType);
 419
 420    /// <summary>
 421    /// Resolves the specified service type from the service provider, or creates a new instance if the service type was
 422    /// </summary>
 0423    public T GetOrCreateService<T>() where T : notnull => ActivatorUtilities.GetServiceOrCreateInstance<T>(ServiceProvid
 424
 425    /// <summary>
 426    /// Resolves the specified service type from the service provider, or creates a new instance if the service type was
 427    /// </summary>
 0428    public object GetOrCreateService(Type serviceType) => ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider,
 429
 430    /// <summary>
 431    /// Resolves the specified service type from the service provider.
 432    /// </summary>
 586433    public T? GetService<T>() where T : notnull => ServiceProvider.GetService<T>();
 434
 435    /// <summary>
 436    /// Resolves the specified service type from the service provider.
 437    /// </summary>
 0438    public object? GetService(Type serviceType) => ServiceProvider.GetService(serviceType);
 439
 440    /// <summary>
 441    /// Resolves multiple implementations of the specified service type from the service provider.
 442    /// </summary>
 266443    public IEnumerable<T> GetServices<T>() where T : notnull => ServiceProvider.GetServices<T>();
 444
 445    /// <summary>
 446    /// Registers a completion callback for the specified activity.
 447    /// </summary>
 448    public void AddCompletionCallback(ActivityExecutionContext owner, ActivityNode child, ActivityCompletionCallback? co
 449    {
 2913450        var entry = new ActivityCompletionCallbackEntry(owner, child, completionCallback, tag);
 2913451        _completionCallbackEntries.Add(entry);
 2913452    }
 453
 454    /// <summary>
 455    /// Unregisters the completion callback for the specified owner and child activity.
 456    /// </summary>
 457    public ActivityCompletionCallbackEntry? PopCompletionCallback(ActivityExecutionContext owner, ActivityNode child)
 458    {
 33641459        var entry = _completionCallbackEntries.FirstOrDefault(x => x.Owner == owner && x.Child == child);
 460
 12813461        if (entry == null)
 10071462            return null;
 463
 2742464        RemoveCompletionCallback(entry);
 2742465        return entry;
 466    }
 467
 2742468    public void RemoveCompletionCallback(ActivityCompletionCallbackEntry entry) => _completionCallbackEntries.Remove(ent
 469
 470    public void RemoveCompletionCallbacks(IEnumerable<ActivityCompletionCallbackEntry> entries)
 471    {
 6822472        foreach (var entry in entries.ToList())
 6473            _completionCallbackEntries.Remove(entry);
 3405474    }
 475
 476    /// <summary>
 477    /// Clears all activity completion callback entries from the workflow execution context.
 478    /// </summary>
 479    public void ClearCompletionCallbacks()
 480    {
 9481        _completionCallbackEntries.Clear();
 9482    }
 483
 484    /// <summary>
 485    /// Finds the activity based on the provided <paramref name="handle"/>.
 486    /// </summary>
 487    /// <param name="handle">The handle containing the identification parameters for the activity.</param>
 488    /// <returns>The activity found based on the handle, or null if no activity is found.</returns>
 489    public IActivity? FindActivity(ActivityHandle handle)
 490    {
 0491        return handle.ActivityId != null
 0492            ? FindActivityById(handle.ActivityId)
 0493            : handle.ActivityNodeId != null
 0494                ? FindActivityByNodeId(handle.ActivityNodeId)
 0495                : handle.ActivityInstanceId != null
 0496                    ? FindActivityByInstanceId(handle.ActivityInstanceId)
 0497                    : handle.ActivityHash != null
 0498                        ? FindActivityByHash(handle.ActivityHash)
 0499                        : null;
 500    }
 501
 502    /// <summary>
 503    /// Returns the <see cref="ActivityNode"/> with the specified activity ID from the workflow graph.
 504    /// </summary>
 254505    public ActivityNode? FindNodeById(string nodeId) => NodeIdLookup.TryGetValue(nodeId, out var node) ? node : null;
 506
 507    /// <summary>
 508    /// Returns the <see cref="ActivityNode"/> with the specified hash of the activity node ID from the workflow graph.
 509    /// </summary>
 510    /// <param name="hash">The hash of the activity node ID.</param>
 511    /// <returns>The <see cref="ActivityNode"/> with the specified hash of the activity node ID.</returns>
 0512    public ActivityNode? FindNodeByHash(string hash) => NodeHashLookup.TryGetValue(hash, out var node) ? node : null;
 513
 514    /// Returns the <see cref="ActivityNode"/> containing the specified activity from the workflow graph.
 515    public ActivityNode? FindNodeByActivity(IActivity activity)
 516    {
 34729517        return NodeActivityLookup.TryGetValue(activity, out var node) ? node : null;
 518    }
 519
 520    /// Returns the <see cref="ActivityNode"/> associated with the specified activity ID.
 0521    public ActivityNode? FindNodeByActivityId(string activityId) => Nodes.FirstOrDefault(x => x.Activity.Id == activityI
 522
 523    /// Returns the <see cref="IActivity"/> with the specified ID from the workflow graph.
 149524    public IActivity? FindActivityByNodeId(string nodeId) => FindNodeById(nodeId)?.Activity;
 525
 526    /// Returns the <see cref="IActivity"/> with the specified ID from the workflow graph.
 0527    public IActivity? FindActivityById(string activityId) => FindNodeById(NodeIdLookup.SingleOrDefault(n => n.Key.EndsWi
 528
 529    /// Returns the <see cref="IActivity"/> with the specified hash of the activity node ID from the workflow graph.
 530    /// <param name="hash">The hash of the activity node ID.</param>
 531    /// <returns>The <see cref="IActivity"/> with the specified hash of the activity node ID.</returns>
 0532    public IActivity? FindActivityByHash(string hash) => FindNodeByHash(hash)?.Activity;
 533
 534    /// Returns the <see cref="ActivityExecutionContext"/> with the specified activity instance ID.
 0535    public IActivity? FindActivityByInstanceId(string activityInstanceId) => ActivityExecutionContexts.FirstOrDefault(x 
 536
 537    /// Returns a custom property with the specified key from the <see cref="Properties"/> dictionary.
 203538    public T? GetProperty<T>(string key) => Properties.TryGetValue(key, out var value) ? value.ConvertTo<T>() : default;
 539
 540    /// Sets a custom property with the specified key on the <see cref="Properties"/> dictionary.
 0541    public void SetProperty<T>(string key, T value) => Properties[key] = value!;
 542
 543    /// Updates a custom property with the specified key on the <see cref="Properties"/> dictionary.
 544    public T UpdateProperty<T>(string key, Func<T?, T> updater)
 545    {
 0546        var value = GetProperty<T?>(key);
 0547        value = updater(value);
 0548        Properties[key] = value!;
 0549        return value;
 550    }
 551
 552    /// Returns true if the <see cref="Properties"/> dictionary contains the specified key.
 0553    public bool HasProperty(string name) => Properties.ContainsKey(name);
 554
 17555    internal bool CanTransitionTo(WorkflowSubStatus targetSubStatus) => ValidateStatusTransition();
 556
 557    internal void TransitionTo(WorkflowSubStatus subStatus)
 558    {
 1494559        if (!ValidateStatusTransition())
 0560            throw new($"Cannot transition from {SubStatus} to {subStatus}");
 561
 1494562        SubStatus = subStatus;
 1494563        UpdatedAt = SystemClock.UtcNow;
 564
 1494565        if (Status == WorkflowStatus.Finished)
 467566            FinishedAt = UpdatedAt;
 567
 1494568        if (Status == WorkflowStatus.Finished || SubStatus == WorkflowSubStatus.Suspended)
 569        {
 2060570            foreach (var registration in _cancellationRegistrations)
 515571                registration.Dispose();
 572        }
 1494573    }
 574
 575    /// Creates a new <see cref="ActivityExecutionContext"/> for the specified activity.
 576    public async Task<ActivityExecutionContext> CreateActivityExecutionContextAsync(IActivity activity, ActivityInvocati
 577    {
 3879578        var activityDescriptor = await ActivityRegistryLookup.FindAsync(activity) ?? throw new ActivityNotFoundException
 3879579        var tag = options?.Tag;
 3879580        var parentContext = options?.Owner;
 3879581        var now = SystemClock.UtcNow;
 3879582        var id = IdentityGenerator.GenerateId();
 3879583        var activityExecutionContext = new ActivityExecutionContext(id, this, parentContext, activity, activityDescripto
 3879584        var variablesToDeclare = options?.Variables ?? [];
 3879585        var variableContainer = new[]
 3879586        {
 3879587            activityExecutionContext.ActivityNode
 588        }.Concat(activityExecutionContext.ActivityNode.Ancestors()).FirstOrDefault(x => x.Activity is IVariableContainer
 3879589        activityExecutionContext.ExpressionExecutionContext.TransientProperties[ExpressionExecutionContextExtensions.Act
 590
 3879591        if (variableContainer != null)
 592        {
 7932593            foreach (var variable in variablesToDeclare)
 594            {
 595                // Declare a dynamic variable on the activity execution context.
 129596                activityExecutionContext.DynamicVariables.RemoveWhere(x => x.Name == variable.Name);
 87597                activityExecutionContext.DynamicVariables.Add(variable);
 598
 599                // Assign the variable to the expression execution context.
 87600                activityExecutionContext.ExpressionExecutionContext.CreateVariable(variable.Name, variable.Value);
 601            }
 602        }
 603
 3879604        var activityInput = options?.Input ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
 3879605        activityExecutionContext.ActivityInput.Merge(activityInput);
 606
 3879607        return activityExecutionContext;
 3879608    }
 609
 610    /// Returns a register of recorded activity output.
 4538611    public ActivityOutputRegister GetActivityOutputRegister() => TransientProperties.GetOrAdd(ActivityOutputRegistryKey,
 612
 613    /// Returns the last activity result.
 0614    public object? GetLastActivityResult() => TransientProperties.TryGetValue(LastActivityResultKey, out var value) ? va
 615
 616    /// Adds the specified <see cref="ActivityExecutionContext"/> to the workflow execution context.
 3277617    public void AddActivityExecutionContext(ActivityExecutionContext context) => _activityExecutionContexts.Add(context)
 618
 619    /// Removes the specified <see cref="ActivityExecutionContext"/> from the workflow execution context.
 620    public void RemoveActivityExecutionContext(ActivityExecutionContext context)
 621    {
 2755622        _activityExecutionContexts.Remove(context);
 2755623        context.ParentActivityExecutionContext?.Children.Remove(context);
 2755624    }
 625
 626    /// Removes the specified <see cref="ActivityExecutionContext"/> from the workflow execution context.
 627    /// <param name="predicate">The predicate used to filter the activity execution contexts to remove.</param>
 628    public void RemoveActivityExecutionContexts(Func<ActivityExecutionContext, bool> predicate)
 629    {
 509630        var itemsToRemove = _activityExecutionContexts.Where(predicate).ToList();
 6528631        foreach (var item in itemsToRemove)
 2755632            RemoveActivityExecutionContext(item);
 509633    }
 634
 635    /// <summary>
 636    /// Removes all completed activity execution contexts that have a parent activity execution context.
 637    /// </summary>
 638    public void ClearCompletedActivityExecutionContexts()
 639    {
 3922640        RemoveActivityExecutionContexts(x => x is { IsCompleted: true, ParentActivityExecutionContext: not null });
 509641    }
 642
 643    public IEnumerable<ActivityExecutionContext> GetActiveActivityExecutionContexts()
 644    {
 645        // Filter out completed activity execution contexts, except for the root Workflow activity context, which stores
 646        // This will currently break scripts accessing activity output directly, but there's a workaround for that via v
 647        // We may ultimately restore direct output access, but differently.
 7876648        return ActivityExecutionContexts.Where(x => !x.IsCompleted || x.ParentActivityExecutionContext == null);
 649    }
 650
 651    /// <summary>
 652    /// Records the output of the specified activity into the current workflow execution context.
 653    /// </summary>
 654    /// <param name="activityExecutionContext">The <see cref="ActivityExecutionContext"/> of the activity.</param>
 655    /// <param name="outputName">The name of the output.</param>
 656    /// <param name="value">The value of the output.</param>
 657    internal void RecordActivityOutput(ActivityExecutionContext activityExecutionContext, string? outputName, object? va
 658    {
 1722659        var register = GetActivityOutputRegister();
 1722660        register.Record(activityExecutionContext, outputName, value);
 661
 662        // If the output name is the default output name, record the value as the last activity result.
 1722663        if (outputName == ActivityOutputRegister.DefaultOutputName)
 388664            TransientProperties[LastActivityResultKey] = value!;
 1722665    }
 666
 667    private WorkflowStatus GetMainStatus(WorkflowSubStatus subStatus) =>
 5620668        subStatus switch
 5620669        {
 472670            WorkflowSubStatus.Pending => WorkflowStatus.Running,
 12671            WorkflowSubStatus.Cancelled => WorkflowStatus.Finished,
 3439672            WorkflowSubStatus.Executing => WorkflowStatus.Running,
 57673            WorkflowSubStatus.Faulted => WorkflowStatus.Finished,
 1414674            WorkflowSubStatus.Finished => WorkflowStatus.Finished,
 226675            WorkflowSubStatus.Suspended => WorkflowStatus.Running,
 0676            _ => throw new ArgumentOutOfRangeException(nameof(subStatus), subStatus, null)
 5620677        };
 678
 679    // TODO: Check if we should not use the target subStatus here instead.
 680    private bool ValidateStatusTransition()
 681    {
 1511682        var currentMainStatus = GetMainStatus(SubStatus);
 1511683        return currentMainStatus != WorkflowStatus.Finished;
 684    }
 685
 686    public Task CommitAsync()
 687    {
 0688        return _commitStateHandler.CommitAsync(this, CancellationToken);
 689    }
 690}

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.ExecutionLogEntry.cs

#LineLine coverage
 1using Elsa.Extensions;
 2using Elsa.Workflows.Models;
 3
 4namespace Elsa.Workflows;
 5
 6public partial class WorkflowExecutionContext
 7{
 8    /// <summary>
 9    /// Adds a new <see cref="WorkflowExecutionLogEntry"/> to the execution log of the current <see cref="Workflows.Work
 10    /// </summary>
 11    /// <param name="eventName">The name of the event.</param>
 12    /// <param name="message">The message of the event.</param>
 13    /// <param name="payload">Any contextual data related to this event.</param>
 14    /// <returns>Returns the created <see cref="WorkflowExecutionLogEntry"/>.</returns>
 15    public WorkflowExecutionLogEntry AddExecutionLogEntry(string eventName, string? message = null, object? payload = nu
 16    {
 417        var logEntry = new WorkflowExecutionLogEntry(
 418            Id,
 419            null,
 420            Workflow.Id,
 421            Workflow.Type,
 422            Workflow.Identity.Version,
 423            Workflow.Name,
 424            Workflow.Identity.Id,
 425            null,
 426            SystemClock.UtcNow,
 427            ExecutionLogSequence++,
 428            eventName,
 429            message,
 430            Workflow.GetSource(),
 431            payload);
 32
 433        ExecutionLog.Add(logEntry);
 434        return logEntry;
 35    }
 36}

Methods/Properties

.ctor(System.IServiceProvider,Elsa.Workflows.Models.WorkflowGraph,System.String,System.String,System.String,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,Elsa.Workflows.ExecuteActivityDelegate,System.String,System.Collections.Generic.IEnumerable`1<Elsa.Workflows.Models.ActivityIncident>,System.Collections.Generic.IEnumerable`1<Elsa.Workflows.Models.Bookmark>,System.DateTimeOffset,System.Threading.CancellationToken)
Cancel()
CancelWorkflow()
.cctor()
Complete(Elsa.Workflows.ActivityExecutionContext)
Noop(Elsa.Workflows.ActivityExecutionContext)
.ctor(System.IServiceProvider,Elsa.Workflows.Models.WorkflowGraph,System.String,System.String,System.String,System.Collections.Generic.IDictionary`2<System.String,System.Object>,System.Collections.Generic.IDictionary`2<System.String,System.Object>,Elsa.Workflows.ExecuteActivityDelegate,System.String,System.Collections.Generic.IEnumerable`1<Elsa.Workflows.Models.ActivityIncident>,System.Collections.Generic.IEnumerable`1<Elsa.Workflows.Models.Bookmark>,System.DateTimeOffset,System.Threading.CancellationToken)
CreateAsync()
CreateAsync()
CreateAsync()
CreateAsync()
SetWorkflowGraphAsync()
get_ServiceProvider()
get_ActivityRegistry()
get_ActivityRegistryLookup()
get_WorkflowGraph()
get_Workflow()
get_Graph()
get_Status()
get_SubStatus()
get_IsExecuting()
get_MemoryRegister()
get_Id()
get_Activity()
get_CorrelationId()
get_Name()
get_ParentWorkflowInstanceId()
get_CreatedAt()
get_UpdatedAt()
get_FinishedAt()
get_SystemClock()
get_Nodes()
get_NodeIdLookup()
get_NodeHashLookup()
get_NodeActivityLookup()
get_Scheduler()
get_IdentityGenerator()
get_OriginalBookmarks()
get_Bookmarks()
get_BookmarksDiff()
get_Input()
get_Output()
get_Properties()
get_TransientProperties()
get_Incidents()
get_ExecuteDelegate()
get_ResumedBookmarkContext()
get_TriggerActivityId()
get_CancellationToken()
get_CompletionCallbacks()
get_ActivityExecutionContexts()
set_ActivityExecutionContexts(System.Collections.Generic.IReadOnlyCollection`1<Elsa.Workflows.ActivityExecutionContext>)
get_ExecutionLogSequence()
get_ExecutionLog()
get_ExpressionExecutionContext()
get_Variables()
GetRequiredService()
GetRequiredService(System.Type)
GetOrCreateService()
GetOrCreateService(System.Type)
GetService()
GetService(System.Type)
GetServices()
AddCompletionCallback(Elsa.Workflows.ActivityExecutionContext,Elsa.Workflows.Models.ActivityNode,Elsa.Workflows.ActivityCompletionCallback,System.Object)
PopCompletionCallback(Elsa.Workflows.ActivityExecutionContext,Elsa.Workflows.Models.ActivityNode)
RemoveCompletionCallback(Elsa.Workflows.ActivityCompletionCallbackEntry)
RemoveCompletionCallbacks(System.Collections.Generic.IEnumerable`1<Elsa.Workflows.ActivityCompletionCallbackEntry>)
ClearCompletionCallbacks()
FindActivity(Elsa.Workflows.Models.ActivityHandle)
FindNodeById(System.String)
FindNodeByHash(System.String)
FindNodeByActivity(Elsa.Workflows.IActivity)
FindNodeByActivityId(System.String)
FindActivityByNodeId(System.String)
FindActivityById(System.String)
FindActivityByHash(System.String)
FindActivityByInstanceId(System.String)
GetProperty(System.String)
SetProperty(System.String,T)
UpdateProperty(System.String,System.Func`2<T,T>)
HasProperty(System.String)
CanTransitionTo(Elsa.Workflows.WorkflowSubStatus)
TransitionTo(Elsa.Workflows.WorkflowSubStatus)
CreateActivityExecutionContextAsync()
GetActivityOutputRegister()
GetLastActivityResult()
AddActivityExecutionContext(Elsa.Workflows.ActivityExecutionContext)
RemoveActivityExecutionContext(Elsa.Workflows.ActivityExecutionContext)
RemoveActivityExecutionContexts(System.Func`2<Elsa.Workflows.ActivityExecutionContext,System.Boolean>)
ClearCompletedActivityExecutionContexts()
GetActiveActivityExecutionContexts()
RecordActivityOutput(Elsa.Workflows.ActivityExecutionContext,System.String,System.Object)
GetMainStatus(Elsa.Workflows.WorkflowSubStatus)
ValidateStatusTransition()
CommitAsync()
AddExecutionLogEntry(System.String,System.String,System.Object)