From cea4c8fa0a67d614cb9341471570cf5d6754aca3 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 6 Apr 2023 14:59:30 +0200 Subject: [PATCH] Add support for resuming workflows using activity instance ID and hash --- .../DispatchWorkflowRequestConsumer.cs | 20 ++++++--- .../MassTransitWorkflowDispatcher.cs | 3 ++ .../Messages/DispatchWorkflowInstance.cs | 3 ++ .../Elsa.ProtoActor/Grains/WorkflowGrain.cs | 14 ++++++- .../Elsa.ProtoActor/Protos/Messages.proto | 5 ++- .../Contracts/IWorkflowRunner.cs | 5 ++- .../WorkflowExecutionContextExtensions.cs | 10 +++++ .../Models/WorkflowExecutionContext.cs | 41 ++++++++++++++----- .../DefaultWorkflowExecutionContextFactory.cs | 6 ++- .../Elsa.Workflows.Core/Services/Hasher.cs | 2 +- .../Services/WorkflowRunner.cs | 27 ++++++++++-- .../DispatchWorkflowInstanceCommand.cs | 3 ++ .../Contracts/IWorkflowHost.cs | 17 +++++--- .../Contracts/IWorkflowRuntime.cs | 10 ++++- .../DispatchWorkflowRequestHandler.cs | 11 ++++- .../Models/DispatchWorkflowInstanceRequest.cs | 3 ++ .../Services/DefaultWorkflowRuntime.cs | 12 +++++- .../Services/TaskBasedWorkflowDispatcher.cs | 11 ++++- .../Services/WorkflowHost.cs | 19 +++++++-- 19 files changed, 185 insertions(+), 37 deletions(-) diff --git a/src/modules/Elsa.MassTransit/Consumers/DispatchWorkflowRequestConsumer.cs b/src/modules/Elsa.MassTransit/Consumers/DispatchWorkflowRequestConsumer.cs index 3361b4eb9..7592d9d21 100644 --- a/src/modules/Elsa.MassTransit/Consumers/DispatchWorkflowRequestConsumer.cs +++ b/src/modules/Elsa.MassTransit/Consumers/DispatchWorkflowRequestConsumer.cs @@ -7,9 +7,9 @@ namespace Elsa.MassTransit.Consumers; /// /// A consumer of various dispatch message types to asynchronously execute workflows. /// -public class DispatchWorkflowRequestConsumer : - IConsumer, - IConsumer, +public class DispatchWorkflowRequestConsumer : + IConsumer, + IConsumer, IConsumer, IConsumer { @@ -28,7 +28,7 @@ public class DispatchWorkflowRequestConsumer : { var message = context.Message; var options = new StartWorkflowRuntimeOptions(message.CorrelationId, message.Input, message.VersionOptions, InstanceId: message.InstanceId); - + await _workflowRuntime.StartWorkflowAsync(message.DefinitionId, options, context.CancellationToken); } @@ -36,7 +36,17 @@ public class DispatchWorkflowRequestConsumer : public async Task Consume(ConsumeContext context) { var message = context.Message; - var options = new ResumeWorkflowRuntimeOptions(message.CorrelationId, message.InstanceId, message.BookmarkId, message.ActivityId, message.Input); + + var options = new ResumeWorkflowRuntimeOptions( + message.CorrelationId, + message.InstanceId, + message.BookmarkId, + message.ActivityId, + message.ActivityNodeId, + message.ActivityInstanceId, + message.ActivityHash, + message.Input); + await _workflowRuntime.ResumeWorkflowAsync(message.InstanceId, options, context.CancellationToken); } diff --git a/src/modules/Elsa.MassTransit/Implementations/MassTransitWorkflowDispatcher.cs b/src/modules/Elsa.MassTransit/Implementations/MassTransitWorkflowDispatcher.cs index ff4db4690..413ba00ad 100644 --- a/src/modules/Elsa.MassTransit/Implementations/MassTransitWorkflowDispatcher.cs +++ b/src/modules/Elsa.MassTransit/Implementations/MassTransitWorkflowDispatcher.cs @@ -40,6 +40,9 @@ public class MassTransitWorkflowDispatcher : IWorkflowDispatcher request.InstanceId, request.BookmarkId, request.ActivityId, + request.ActivityNodeId, + request.ActivityInstanceId, + request.ActivityHash, request.Input, request.CorrelationId ), cancellationToken); diff --git a/src/modules/Elsa.MassTransit/Messages/DispatchWorkflowInstance.cs b/src/modules/Elsa.MassTransit/Messages/DispatchWorkflowInstance.cs index db67e0d52..5e83a0d84 100644 --- a/src/modules/Elsa.MassTransit/Messages/DispatchWorkflowInstance.cs +++ b/src/modules/Elsa.MassTransit/Messages/DispatchWorkflowInstance.cs @@ -5,6 +5,9 @@ public record DispatchWorkflowInstance string InstanceId, string? BookmarkId, string? ActivityId, + string? ActivityNodeId, + string? ActivityInstanceId, + string? ActivityHash, IDictionary? Input, string? CorrelationId ); \ No newline at end of file diff --git a/src/modules/Elsa.ProtoActor/Grains/WorkflowGrain.cs b/src/modules/Elsa.ProtoActor/Grains/WorkflowGrain.cs index f66cba85c..f6be977a9 100644 --- a/src/modules/Elsa.ProtoActor/Grains/WorkflowGrain.cs +++ b/src/modules/Elsa.ProtoActor/Grains/WorkflowGrain.cs @@ -146,8 +146,20 @@ public class WorkflowGrain : WorkflowGrainBase var correlationId = request.CorrelationId; var bookmarkId = request.BookmarkId.NullIfEmpty(); var activityId = request.ActivityId.NullIfEmpty(); + var activityNodeId = request.ActivityNodeId.NullIfEmpty(); + var activityInstanceId = request.ActivityInstanceId.NullIfEmpty(); + var activityHash = request.ActivityHash.NullIfEmpty(); var cancellationToken = Context.CancellationToken; - var resumeWorkflowHostOptions = new ResumeWorkflowHostOptions(correlationId, bookmarkId, activityId, _input); + + var resumeWorkflowHostOptions = new ResumeWorkflowHostOptions( + correlationId, + bookmarkId, + activityId, + activityNodeId, + activityInstanceId, + activityHash, + _input); + var definitionId = _definitionId; var versionOptions = VersionOptions.SpecificVersion(_version); diff --git a/src/modules/Elsa.ProtoActor/Protos/Messages.proto b/src/modules/Elsa.ProtoActor/Protos/Messages.proto index 06156ab3c..53e52efa3 100644 --- a/src/modules/Elsa.ProtoActor/Protos/Messages.proto +++ b/src/modules/Elsa.ProtoActor/Protos/Messages.proto @@ -37,7 +37,10 @@ message ResumeWorkflowRequest { optional string CorrelationId = 2; optional string BookmarkId = 3; optional string ActivityId = 4; - optional Input input = 5; + optional string ActivityNodeId = 5; + optional string ActivityInstanceId = 6; + optional string ActivityHash = 7; + optional Input input = 8; } message ResumeWorkflowResponse { diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs index 874dd78b4..60f2f4da7 100644 --- a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs +++ b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowRunner.cs @@ -23,6 +23,9 @@ public record RunWorkflowOptions( string? InstanceId = default, string? CorrelationId = default, string? BookmarkId = default, - string? ActivityNodeId = default, + string? ActivityId = default, + string? ActivityNodeId = default, + string? ActivityInstanceId = default, + string? ActivityHash = default, IDictionary? Input = default, string? TriggerActivityId = default); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs index de3bac037..7f480bb41 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs @@ -54,6 +54,16 @@ public static class WorkflowExecutionContextExtensions var workItem = new ActivityWorkItem(activity.Id, async () => await activityInvoker.InvokeAsync(workflowExecutionContext, activity)); workflowExecutionContext.Scheduler.Schedule(workItem); } + + /// + /// Schedules the specified activity execution context of the workflow. + /// + public static void ScheduleActivityExecutionContext(this WorkflowExecutionContext workflowExecutionContext, ActivityExecutionContext activityExecutionContext) + { + var activityInvoker = workflowExecutionContext.GetRequiredService(); + var workItem = new ActivityWorkItem(activityExecutionContext.Activity.Id, async () => await activityInvoker.InvokeAsync(activityExecutionContext)); + workflowExecutionContext.Scheduler.Schedule(workItem); + } /// /// Schedules the activity of the specified bookmark. diff --git a/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs b/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs index acc183596..5df4ea903 100644 --- a/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs +++ b/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs @@ -23,6 +23,7 @@ public class WorkflowExecutionContext : IExecutionContext { internal static ValueTask Complete(ActivityExecutionContext context) => context.CompleteActivityAsync(); private readonly IServiceProvider _serviceProvider; + private readonly IHasher _hasher; private readonly IActivityRegistry _activityRegistry; private readonly IList _nodes; private readonly IList _completionCallbackEntries = new List(); @@ -33,6 +34,7 @@ public class WorkflowExecutionContext : IExecutionContext /// public WorkflowExecutionContext( IServiceProvider serviceProvider, + IHasher hasher, string id, string? correlationId, Workflow workflow, @@ -47,6 +49,7 @@ public class WorkflowExecutionContext : IExecutionContext CancellationToken cancellationToken) { _serviceProvider = serviceProvider; + _hasher = hasher; _activityRegistry = activityRegistry; Workflow = workflow; Graph = graph; @@ -61,6 +64,7 @@ public class WorkflowExecutionContext : IExecutionContext TriggerActivityId = triggerActivityId; CancellationToken = cancellationToken; NodeIdLookup = _nodes.ToDictionary(x => x.NodeId); + NodeHashLookup = _nodes.ToDictionary(x => Hash(x.NodeId)); NodeActivityLookup = _nodes.ToDictionary(x => x.Activity); MemoryRegister = workflow.CreateRegister(); ExpressionExecutionContext = new ExpressionExecutionContext(serviceProvider, MemoryRegister, cancellationToken: cancellationToken); @@ -110,6 +114,11 @@ public class WorkflowExecutionContext : IExecutionContext /// A map between activity IDs and s in the workflow graph. /// public IDictionary NodeIdLookup { get; } + + /// + /// A map between hashed activity node IDs and s in the workflow graph. + /// + public IDictionary NodeHashLookup { get; } /// /// A map between s and s in the workflow graph. @@ -267,6 +276,13 @@ public class WorkflowExecutionContext : IExecutionContext /// Returns the with the specified activity ID from the workflow graph. /// public ActivityNode FindNodeById(string nodeId) => NodeIdLookup[nodeId]; + + /// + /// Returns the with the specified hash of the activity node ID from the workflow graph. + /// + /// The hash of the activity node ID. + /// The with the specified hash of the activity node ID. + public ActivityNode FindNodeByHash(string hash) => NodeHashLookup[hash]; /// /// Returns the containing the specified activity from the workflow graph. @@ -279,11 +295,16 @@ public class WorkflowExecutionContext : IExecutionContext public IActivity FindActivityByNodeId(string nodeId) => FindNodeById(nodeId).Activity; /// - /// + /// Returns the with the specified ID from the workflow graph. /// - /// - /// public IActivity FindActivityByActivityId(string activityId) => FindNodeById(NodeIdLookup.Single(n => n.Key.Contains(activityId)).Value.NodeId).Activity; + + /// + /// Returns the with the specified hash of the activity node ID from the workflow graph. + /// + /// The hash of the activity node ID. + /// The with the specified hash of the activity node ID. + public IActivity FindActivityByHash(string hash) => FindNodeByHash(hash).Activity; /// /// Returns a custom property with the specified key from the dictionary. @@ -311,12 +332,12 @@ public class WorkflowExecutionContext : IExecutionContext /// public bool HasProperty(string name) => Properties.ContainsKey(name); + internal bool CanTransitionTo(WorkflowSubStatus targetSubStatus) => ValidateStatusTransition(targetSubStatus); + internal void TransitionTo(WorkflowSubStatus subStatus) { - var targetStatus = GetMainStatus(subStatus); - - if (!ValidateStatusTransition(SubStatus, subStatus)) - throw new Exception($"Cannot transition from {Status} to {targetStatus}"); + if (!ValidateStatusTransition(SubStatus)) + throw new Exception($"Cannot transition from {SubStatus} to {subStatus}"); SubStatus = subStatus; } @@ -390,11 +411,11 @@ public class WorkflowExecutionContext : IExecutionContext _ => throw new ArgumentOutOfRangeException(nameof(subStatus), subStatus, null) }; - private bool ValidateStatusTransition(WorkflowSubStatus currentSubStatus, WorkflowSubStatus target) + private bool ValidateStatusTransition(WorkflowSubStatus targetSubStatus) { - var currentMainStatus = GetMainStatus(currentSubStatus); + var currentMainStatus = GetMainStatus(SubStatus); return currentMainStatus != WorkflowStatus.Finished; } - private IEnumerable GetMergedRegistersView() => new[] { MemoryRegister }.Concat(ActivityExecutionContexts.Select(x => x.ExpressionExecutionContext.Memory)).ToList(); + private string Hash(string nodeId) => _hasher.Hash(nodeId); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Services/DefaultWorkflowExecutionContextFactory.cs b/src/modules/Elsa.Workflows.Core/Services/DefaultWorkflowExecutionContextFactory.cs index 184f36783..19cb5be80 100644 --- a/src/modules/Elsa.Workflows.Core/Services/DefaultWorkflowExecutionContextFactory.cs +++ b/src/modules/Elsa.Workflows.Core/Services/DefaultWorkflowExecutionContextFactory.cs @@ -13,6 +13,7 @@ public class DefaultWorkflowExecutionContextFactory : IWorkflowExecutionContextF private readonly IActivitySchedulerFactory _schedulerFactory; private readonly IActivityRegistry _activityRegistry; private readonly IWorkflowStateSerializer _workflowStateSerializer; + private readonly IHasher _hasher; /// /// Constructor. @@ -22,13 +23,15 @@ public class DefaultWorkflowExecutionContextFactory : IWorkflowExecutionContextF IIdentityGraphService identityGraphService, IActivitySchedulerFactory schedulerFactory, IActivityRegistry activityRegistry, - IWorkflowStateSerializer workflowStateSerializer) + IWorkflowStateSerializer workflowStateSerializer, + IHasher hasher) { _activityVisitor = activityVisitor; _identityGraphService = identityGraphService; _schedulerFactory = schedulerFactory; _activityRegistry = activityRegistry; _workflowStateSerializer = workflowStateSerializer; + _hasher = hasher; } /// @@ -64,6 +67,7 @@ public class DefaultWorkflowExecutionContextFactory : IWorkflowExecutionContextF // Setup a workflow execution context. var workflowExecutionContext = new WorkflowExecutionContext( serviceProvider, + _hasher, instanceId, correlationId, workflow, diff --git a/src/modules/Elsa.Workflows.Core/Services/Hasher.cs b/src/modules/Elsa.Workflows.Core/Services/Hasher.cs index 14fd9e40b..7b3d01247 100644 --- a/src/modules/Elsa.Workflows.Core/Services/Hasher.cs +++ b/src/modules/Elsa.Workflows.Core/Services/Hasher.cs @@ -10,7 +10,7 @@ public class Hasher : IHasher /// public string Hash(string value) { - using var sha = HashAlgorithm.Create(HashAlgorithmName.SHA256.ToString())!; + using var sha = SHA256.Create(); return Hash(sha, value); } diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs index 516408732..fb9f63f08 100644 --- a/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs +++ b/src/modules/Elsa.Workflows.Core/Services/WorkflowRunner.cs @@ -114,7 +114,10 @@ public class WorkflowRunner : IWorkflowRunner var triggerActivityId = options?.TriggerActivityId; var workflowExecutionContext = await CreateWorkflowExecutionContextAsync(scope.ServiceProvider, workflow, workflowState.Id, correlationId, workflowState, input, default, triggerActivityId, cancellationToken); var bookmarkId = options?.BookmarkId; - var nodeId = options?.ActivityNodeId; + var activityNodeId = options?.ActivityNodeId; + var activityId = options?.ActivityId; + var activityInstanceId = options?.ActivityInstanceId; + var activityHash = options?.ActivityHash; if (bookmarkId != null) { @@ -124,12 +127,30 @@ public class WorkflowRunner : IWorkflowRunner if (bookmark != null) workflowExecutionContext.ScheduleBookmark(bookmark); } - else if (nodeId != null) + else if (activityNodeId != null) { // Schedule the activity. - var activity = workflowExecutionContext.FindActivityByNodeId(nodeId); + var activity = workflowExecutionContext.FindActivityByNodeId(activityNodeId); workflowExecutionContext.ScheduleActivity(activity); } + else if (activityHash != null) + { + // Schedule the activity. + var activity = workflowExecutionContext.FindActivityByHash(activityHash); + workflowExecutionContext.ScheduleActivity(activity); + } + else if (activityId != null) + { + // Schedule the activity. + var activity = workflowExecutionContext.FindActivityByActivityId(activityId); + workflowExecutionContext.ScheduleActivity(activity); + } + else if (activityInstanceId != null) + { + // Schedule the activity. + var activityExecutionContext = workflowExecutionContext.ActivityExecutionContexts.FirstOrDefault(x => x.Id == activityInstanceId) ?? throw new Exception("No activity execution context found with the specified ID."); + workflowExecutionContext.ScheduleActivityExecutionContext(activityExecutionContext); + } else { // Schedule the workflow itself. diff --git a/src/modules/Elsa.Workflows.Runtime/Commands/DispatchWorkflowInstanceCommand.cs b/src/modules/Elsa.Workflows.Runtime/Commands/DispatchWorkflowInstanceCommand.cs index c138cb105..d77543a81 100644 --- a/src/modules/Elsa.Workflows.Runtime/Commands/DispatchWorkflowInstanceCommand.cs +++ b/src/modules/Elsa.Workflows.Runtime/Commands/DispatchWorkflowInstanceCommand.cs @@ -8,5 +8,8 @@ public record DispatchWorkflowInstanceCommand( string InstanceId, string? BookmarkId = default, string? ActivityId = default, + string? ActivityNodeId = default, + string? ActivityInstanceId = default, + string? ActivityHash = default, IDictionary? Input = default, string? CorrelationId = default) : ICommand; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowHost.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowHost.cs index b54376422..f38e047d8 100644 --- a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowHost.cs +++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowHost.cs @@ -13,22 +13,22 @@ public interface IWorkflowHost /// The workflow definition. /// Workflow Workflow { get; set; } - + /// /// The workflow state. /// WorkflowState WorkflowState { get; set; } - + /// /// Returns a value indicating whether or not the specified workflow can start a new instance or not. /// Task CanStartWorkflowAsync(StartWorkflowHostOptions? options = default, CancellationToken cancellationToken = default); - + /// /// Start a new workflow instance and execute it. /// Task StartWorkflowAsync(StartWorkflowHostOptions? options = default, CancellationToken cancellationToken = default); - + /// /// Resume an existing workflow instance. /// @@ -37,7 +37,14 @@ public interface IWorkflowHost public record StartWorkflowHostOptions(string? InstanceId = default, string? CorrelationId = default, IDictionary? Input = default, string? TriggerActivityId = default); -public record ResumeWorkflowHostOptions(string? CorrelationId = default, string? BookmarkId = default, string? ActivityId = default, IDictionary? Input = default); +public record ResumeWorkflowHostOptions( + string? CorrelationId = default, + string? BookmarkId = default, + string? ActivityId = default, + string? ActivityNodeId = default, + string? ActivityInstanceId = default, + string? ActivityHash = default, + IDictionary? Input = default); public record StartWorkflowHostResult(Diff BookmarksDiff); diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowRuntime.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowRuntime.cs index f7f48a8c0..a361766db 100644 --- a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowRuntime.cs +++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowRuntime.cs @@ -92,7 +92,15 @@ public interface IWorkflowRuntime public record StartWorkflowRuntimeOptions(string? CorrelationId = default, IDictionary? Input = default, VersionOptions VersionOptions = default, string? TriggerActivityId = default, string? InstanceId = default); -public record ResumeWorkflowRuntimeOptions(string? CorrelationId = default, string? WorkflowInstanceId = default, string? BookmarkId = default, string? ActivityId = default, IDictionary? Input = default); +public record ResumeWorkflowRuntimeOptions( + string? CorrelationId = default, + string? WorkflowInstanceId = default, + string? BookmarkId = default, + string? ActivityId = default, + string? ActivityNodeId = default, + string? ActivityInstanceId = default, + string? ActivityHash = default, + IDictionary? Input = default); public record CanStartWorkflowResult(string? InstanceId, bool CanStart); diff --git a/src/modules/Elsa.Workflows.Runtime/Handlers/DispatchWorkflowRequestHandler.cs b/src/modules/Elsa.Workflows.Runtime/Handlers/DispatchWorkflowRequestHandler.cs index 1284b9be5..a6c499a6b 100644 --- a/src/modules/Elsa.Workflows.Runtime/Handlers/DispatchWorkflowRequestHandler.cs +++ b/src/modules/Elsa.Workflows.Runtime/Handlers/DispatchWorkflowRequestHandler.cs @@ -38,7 +38,16 @@ internal class DispatchWorkflowRequestHandler : public async Task HandleAsync(DispatchWorkflowInstanceCommand command, CancellationToken cancellationToken) { - var options = new ResumeWorkflowRuntimeOptions(command.CorrelationId, command.InstanceId, command.BookmarkId, command.ActivityId, command.Input); + var options = new ResumeWorkflowRuntimeOptions( + command.CorrelationId, + command.InstanceId, + command.BookmarkId, + command.ActivityId, + command.ActivityNodeId, + command.ActivityInstanceId, + command.ActivityHash, + command.Input); + await _workflowRuntime.ResumeWorkflowAsync(command.InstanceId, options, cancellationToken); return Unit.Instance; diff --git a/src/modules/Elsa.Workflows.Runtime/Models/DispatchWorkflowInstanceRequest.cs b/src/modules/Elsa.Workflows.Runtime/Models/DispatchWorkflowInstanceRequest.cs index 15c8ecdc6..113df5752 100644 --- a/src/modules/Elsa.Workflows.Runtime/Models/DispatchWorkflowInstanceRequest.cs +++ b/src/modules/Elsa.Workflows.Runtime/Models/DispatchWorkflowInstanceRequest.cs @@ -4,5 +4,8 @@ public record DispatchWorkflowInstanceRequest( string InstanceId, string? BookmarkId = default, string? ActivityId = default, + string? ActivityNodeId = default, + string? ActivityInstanceId = default, + string? ActivityHash = default, IDictionary? Input = default, string? CorrelationId = default); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowRuntime.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowRuntime.cs index 90ae9f720..b3c98b981 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowRuntime.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowRuntime.cs @@ -129,7 +129,15 @@ public class DefaultWorkflowRuntime : IWorkflowRuntime var workflow = await _workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken); var workflowHost = await _workflowHostFactory.CreateAsync(workflow, workflowState, cancellationToken); - var resumeWorkflowOptions = new ResumeWorkflowHostOptions(options.CorrelationId, options.BookmarkId, options.ActivityId, options.Input); + + var resumeWorkflowOptions = new ResumeWorkflowHostOptions( + options.CorrelationId, + options.BookmarkId, + options.ActivityId, + options.ActivityNodeId, + options.ActivityInstanceId, + options.ActivityHash, + options.Input); await workflowHost.ResumeWorkflowAsync(resumeWorkflowOptions, cancellationToken); @@ -296,7 +304,7 @@ public class DefaultWorkflowRuntime : IWorkflowRuntime var hash = _hasher.Hash(workflowsFilter.ActivityTypeName, workflowsFilter.BookmarkPayload); var correlationId = workflowsFilter.Options.CorrelationId; var workflowInstanceId = workflowsFilter.Options.WorkflowInstanceId; - var filter = new BookmarkFilter { Hash = hash, CorrelationId = correlationId, WorkflowInstanceId = workflowInstanceId}; + var filter = new BookmarkFilter { Hash = hash, CorrelationId = correlationId, WorkflowInstanceId = workflowInstanceId }; var bookmarks = await _bookmarkStore.FindManyAsync(filter, cancellationToken); var collectedWorkflows = bookmarks.Select(b => new ResumableWorkflowMatch(b.WorkflowInstanceId, default, correlationId, b.BookmarkId)).ToList(); return collectedWorkflows; diff --git a/src/modules/Elsa.Workflows.Runtime/Services/TaskBasedWorkflowDispatcher.cs b/src/modules/Elsa.Workflows.Runtime/Services/TaskBasedWorkflowDispatcher.cs index 49a7a70a0..7cd57b9bd 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/TaskBasedWorkflowDispatcher.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/TaskBasedWorkflowDispatcher.cs @@ -37,7 +37,16 @@ public class TaskBasedWorkflowDispatcher : IWorkflowDispatcher /// public async Task DispatchAsync(DispatchWorkflowInstanceRequest request, CancellationToken cancellationToken = default) { - var command = new DispatchWorkflowInstanceCommand(request.InstanceId, request.BookmarkId, request.ActivityId, request.Input, request.CorrelationId); + var command = new DispatchWorkflowInstanceCommand( + request.InstanceId, + request.BookmarkId, + request.ActivityId, + request.ActivityNodeId, + request.ActivityInstanceId, + request.ActivityHash, + request.Input, + request.CorrelationId); + await _backgroundCommandSender.SendAsync(command, cancellationToken); return new DispatchWorkflowInstanceResponse(); } diff --git a/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs b/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs index 163ac414c..2f9fd6f51 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/WorkflowHost.cs @@ -38,7 +38,7 @@ public class WorkflowHost : IWorkflowHost /// public Workflow Workflow { get; set; } - + /// public WorkflowState WorkflowState { get; set; } @@ -84,7 +84,7 @@ public class WorkflowHost : IWorkflowHost public async Task ResumeWorkflowAsync(ResumeWorkflowHostOptions? options = default, CancellationToken cancellationToken = default) { var originalBookmarks = WorkflowState.Bookmarks.ToList(); - + if (WorkflowState.Status != WorkflowStatus.Running) { _logger.LogWarning("Attempt to resume workflow {WorkflowInstanceId} that is not in the Running state. The actual state is {ActualWorkflowStatus}", WorkflowState.Id, WorkflowState.Status); @@ -93,11 +93,22 @@ public class WorkflowHost : IWorkflowHost var instanceId = WorkflowState.Id; var input = options?.Input; - var runOptions = new RunWorkflowOptions(instanceId, options?.CorrelationId, options?.BookmarkId, options?.ActivityId, input); + + var runOptions = new RunWorkflowOptions( + instanceId, + options?.CorrelationId, + options?.BookmarkId, + options?.ActivityId, + options?.ActivityNodeId, + options?.ActivityInstanceId, + options?.ActivityHash, + input + ); + var workflowResult = await _workflowRunner.RunAsync(Workflow, WorkflowState, runOptions, cancellationToken); WorkflowState = workflowResult.WorkflowState; - + var updatedBookmarks = WorkflowState.Bookmarks; return new ResumeWorkflowHostResult(Diff.For(originalBookmarks, updatedBookmarks)); }