From 18fe3e827a7be3ba1f59a57a0ee81495c074e703 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 17 Sep 2025 21:25:55 +0200 Subject: [PATCH] Add `WorkflowStateCommitted` notification support and update state handling logic - Introduced `WorkflowStateCommitted` notification to encapsulate workflow execution context, state, and instance details. - Updated `DefaultCommitStateHandler` to publish `WorkflowStateCommitted` via `IMediator`. - Adjusted `DispatchWorkflowExtensions` to use `WorkflowStateCommitted` for workflow completion. --- .../DispatchWorkflowExtensions.cs | 14 +++++++++----- .../Notifications/WorkflowStateCommitted.cs | 7 +++++++ .../Services/DefaultCommitStateHandler.cs | 6 +++++- 3 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowStateCommitted.cs diff --git a/src/common/Elsa.Testing.Shared.Integration/DispatchWorkflowExtensions.cs b/src/common/Elsa.Testing.Shared.Integration/DispatchWorkflowExtensions.cs index fcd9264a4..58487d5c6 100644 --- a/src/common/Elsa.Testing.Shared.Integration/DispatchWorkflowExtensions.cs +++ b/src/common/Elsa.Testing.Shared.Integration/DispatchWorkflowExtensions.cs @@ -5,6 +5,7 @@ using Elsa.Workflows; using Elsa.Workflows.Notifications; using Elsa.Workflows.Runtime; using Elsa.Workflows.Runtime.Contracts; +using Elsa.Workflows.Runtime.Notifications; using Elsa.Workflows.Runtime.Requests; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -13,7 +14,7 @@ namespace Elsa.Testing.Shared; public static class DispatchWorkflowExtensions { - public static async Task DispatchWorkflowAndRunToCompletion( + public static async Task DispatchWorkflowAndRunToCompletion( this IWorkflow workflowDefinition, Action? configureServices = null, Action? configureElsa = null, @@ -21,7 +22,7 @@ public static class DispatchWorkflowExtensions TimeSpan? timeout = null) { var semaphore = new SemaphoreSlim(0, 1); - WorkflowFinished? workflowFinishedRecord = null; + WorkflowStateCommitted? workflowFinishedRecord = null; var host = Host.CreateDefaultBuilder() .ConfigureServices(services => @@ -29,8 +30,11 @@ public static class DispatchWorkflowExtensions configureServices?.Invoke(services); // This notification handler will capture the WorkflowFinished record (to be returned) and release the semaphore. - services.AddNotificationHandler(sp => new(notification => + services.AddNotificationHandler(sp => new(notification => { + if (notification.WorkflowExecutionContext.Status != WorkflowStatus.Finished) + return; + workflowFinishedRecord = notification; semaphore.Release(); })); @@ -75,9 +79,9 @@ public static class DispatchWorkflowExtensions } } - class WorkflowFinishedAction(Action action) : INotificationHandler + class WorkflowFinishedAction(Action action) : INotificationHandler { - public Task HandleAsync(WorkflowFinished notification, CancellationToken cancellationToken) + public Task HandleAsync(WorkflowStateCommitted notification, CancellationToken cancellationToken) { action(notification); return Task.CompletedTask; diff --git a/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowStateCommitted.cs b/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowStateCommitted.cs new file mode 100644 index 000000000..dcad37410 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Notifications/WorkflowStateCommitted.cs @@ -0,0 +1,7 @@ +using Elsa.Mediator.Contracts; +using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.State; + +namespace Elsa.Workflows.Runtime.Notifications; + +public record WorkflowStateCommitted(WorkflowExecutionContext WorkflowExecutionContext, WorkflowState WorkflowState, WorkflowInstance WorkflowInstance) : INotification; \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultCommitStateHandler.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultCommitStateHandler.cs index 3df97e11c..768c9c373 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultCommitStateHandler.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultCommitStateHandler.cs @@ -1,6 +1,8 @@ +using Elsa.Mediator.Contracts; using Elsa.Workflows.CommitStates; using Elsa.Workflows.Management; using Elsa.Workflows.Runtime.Entities; +using Elsa.Workflows.Runtime.Notifications; using Elsa.Workflows.Runtime.Requests; using Elsa.Workflows.State; @@ -10,6 +12,7 @@ public class DefaultCommitStateHandler( IWorkflowInstanceManager workflowInstanceManager, IBookmarksPersister bookmarkPersister, IVariablePersistenceManager variablePersistenceManager, + IMediator mediator, ILogRecordSink activityExecutionLogRecordSink, ILogRecordSink workflowExecutionLogRecordSink) : ICommitStateHandler { @@ -26,9 +29,10 @@ public class DefaultCommitStateHandler( await activityExecutionLogRecordSink.PersistExecutionLogsAsync(workflowExecutionContext, cancellationToken); await workflowExecutionLogRecordSink.PersistExecutionLogsAsync(workflowExecutionContext, cancellationToken); await variablePersistenceManager.SaveVariablesAsync(workflowExecutionContext); - await workflowInstanceManager.SaveAsync(workflowState, cancellationToken); + var workflowInstance = await workflowInstanceManager.SaveAsync(workflowState, cancellationToken); workflowExecutionContext.ExecutionLog.Clear(); workflowExecutionContext.ClearCompletedActivityExecutionContexts(); await workflowExecutionContext.ExecuteDeferredTasksAsync(); + await mediator.SendAsync(new WorkflowStateCommitted(workflowExecutionContext, workflowState, workflowInstance), cancellationToken); } } \ No newline at end of file