diff --git a/src/modules/Elsa.ProtoActor/Mappers/BookmarkMapper.cs b/src/modules/Elsa.ProtoActor/Mappers/BookmarkMapper.cs index b97d99d0a..1be685e7b 100644 --- a/src/modules/Elsa.ProtoActor/Mappers/BookmarkMapper.cs +++ b/src/modules/Elsa.ProtoActor/Mappers/BookmarkMapper.cs @@ -34,6 +34,7 @@ internal class BookmarkMapper ActivityInstanceId = bookmark.ActivityInstanceId, AutoBurn = bookmark.AutoBurn, CallbackMethodName = bookmark.CallbackMethodName.EmptyIfNull(), + AutoComplete = bookmark.AutoComplete, CreatedAt = bookmark.CreatedAt.ToString("O"), Metadata = { bookmark.Metadata ?? new Dictionary() } }); @@ -51,5 +52,6 @@ internal class BookmarkMapper DateTimeOffset.Parse(bookmark.CreatedAt), bookmark.AutoBurn, bookmark.CallbackMethodName.NullIfEmpty(), + bookmark.AutoComplete, bookmark.Metadata.ToDictionary(x => x.Key, x => x.Value))); } \ No newline at end of file diff --git a/src/modules/Elsa.ProtoActor/Proto/WorkflowInstance.Messages.proto b/src/modules/Elsa.ProtoActor/Proto/WorkflowInstance.Messages.proto index d9d83a88d..e6090c311 100644 --- a/src/modules/Elsa.ProtoActor/Proto/WorkflowInstance.Messages.proto +++ b/src/modules/Elsa.ProtoActor/Proto/WorkflowInstance.Messages.proto @@ -98,5 +98,6 @@ message Bookmark { map Metadata = 8; optional bool AutoBurn = 9; optional string CallbackMethodName = 10; - string CreatedAt = 11; // ISO 8601 + optional bool AutoComplete = 11; + string CreatedAt = 12; // ISO 8601 } \ 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 77b31b1b1..942c7da4c 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs @@ -77,7 +77,12 @@ public static class WorkflowExecutionContextExtensions workflowExecutionContext.Scheduler.Schedule(workItem); // If no resumption point was specified, use a "noop" to prevent the regular "ExecuteAsync" method to be invoked and instead complete the activity. - workflowExecutionContext.ExecuteDelegate = bookmark.CallbackMethodName != null ? bookmarkedActivity.GetResumeActivityDelegate(bookmark.CallbackMethodName) : WorkflowExecutionContext.Noop; + // Unless the bookmark is configured to auto-complete, in which case we'll just complete the activity. + workflowExecutionContext.ExecuteDelegate = bookmark.CallbackMethodName != null + ? bookmarkedActivity.GetResumeActivityDelegate(bookmark.CallbackMethodName) + : bookmark.AutoComplete + ? WorkflowExecutionContext.Complete + : WorkflowExecutionContext.Noop; // Store the bookmark to resume in the context. workflowExecutionContext.ResumedBookmarkContext = new ResumedBookmarkContext(bookmark); diff --git a/src/modules/Elsa.Workflows.Core/Models/Bookmark.cs b/src/modules/Elsa.Workflows.Core/Models/Bookmark.cs index 6f3d8110b..bb20d4c55 100644 --- a/src/modules/Elsa.Workflows.Core/Models/Bookmark.cs +++ b/src/modules/Elsa.Workflows.Core/Models/Bookmark.cs @@ -27,6 +27,7 @@ public record Bookmark( DateTimeOffset CreatedAt, bool AutoBurn = true, string? CallbackMethodName = default, + bool AutoComplete = true, IDictionary? Metadata = default) { /// diff --git a/src/modules/Elsa.Workflows.Core/Models/CreateBookmarkArgs.cs b/src/modules/Elsa.Workflows.Core/Models/CreateBookmarkArgs.cs index 06dcc3a8b..fd652ab08 100644 --- a/src/modules/Elsa.Workflows.Core/Models/CreateBookmarkArgs.cs +++ b/src/modules/Elsa.Workflows.Core/Models/CreateBookmarkArgs.cs @@ -5,15 +5,30 @@ namespace Elsa.Workflows.Core.Models; /// /// Provides bookmark creation options. /// -/// An optional payload to associate with the bookmark. -/// An optional callback to invoke when the bookmark is triggered. -/// An optional name to associate with the bookmark. -/// Whether or not the bookmark should be automatically burned when triggered. -/// Whether or not the activity instance ID should be included in the bookmark payload. -public record CreateBookmarkArgs( - object? Payload = default, - ExecuteActivityDelegate? Callback = default, - string? BookmarkName = default, - bool AutoBurn = true, - bool IncludeActivityInstanceId = true, - IDictionary? Metadata = default); \ No newline at end of file +public class CreateBookmarkArgs +{ + /// An optional payload to associate with the bookmark. + public object? Payload { get; set; } + + /// An optional callback to invoke when the bookmark is triggered. + public ExecuteActivityDelegate? Callback { get; set; } + + /// An optional name to associate with the bookmark. + public string? BookmarkName { get; set; } + + /// Whether or not the bookmark should be automatically burned when triggered. + public bool AutoBurn { get; set; } = true; + + /// Whether or not the activity instance ID should be included in the bookmark payload. + public bool IncludeActivityInstanceId { get; set; } + + /// + /// Whether or not the activity being resumed should be automatically completed if CallBack is not specified. + /// + public bool AutoComplete { get; set; } = true; + + /// + /// An optional dictionary of metadata to associate with the bookmark. + /// + public IDictionary? Metadata { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Activities/BulkDispatchWorkflows.cs b/src/modules/Elsa.Workflows.Runtime/Activities/BulkDispatchWorkflows.cs index 0ff9cd7fd..d3f579292 100644 --- a/src/modules/Elsa.Workflows.Runtime/Activities/BulkDispatchWorkflows.cs +++ b/src/modules/Elsa.Workflows.Runtime/Activities/BulkDispatchWorkflows.cs @@ -133,7 +133,7 @@ public class BulkDispatchWorkflows : Activity ScheduledInstanceIdsCount = dispatchedInstancesCount }, IncludeActivityInstanceId = false, - AutoBurn = false + AutoBurn = false, }; context.CreateBookmark(bookmarkOptions); } diff --git a/src/modules/Elsa.Workflows.Runtime/Middleware/Activities/BackgroundActivityInvokerMiddleware.cs b/src/modules/Elsa.Workflows.Runtime/Middleware/Activities/BackgroundActivityInvokerMiddleware.cs index d618a85ec..22db0fa84 100644 --- a/src/modules/Elsa.Workflows.Runtime/Middleware/Activities/BackgroundActivityInvokerMiddleware.cs +++ b/src/modules/Elsa.Workflows.Runtime/Middleware/Activities/BackgroundActivityInvokerMiddleware.cs @@ -19,7 +19,7 @@ public class BackgroundActivityCollectorMiddleware : DefaultActivityInvokerMiddl /// A key into the activity execution context's transient properties that indicates whether the current activity is being executed in the background. /// public static readonly object IsBackgroundExecution = new(); - + internal static string GetBackgroundActivityOutputKey(string activityId) => $"__BackgroundActivityOutput:{activityId}"; internal static readonly object BackgroundActivitySchedulesKey = new(); internal const string BackgroundActivityBookmarkName = "BackgroundActivity"; @@ -66,7 +66,7 @@ public class BackgroundActivityCollectorMiddleware : DefaultActivityInvokerMiddl var workflowInstanceId = context.WorkflowExecutionContext.Id; var activityNodeId = context.NodeId; var bookmarkPayload = new BackgroundActivityBookmark(); - var bookmarkOptions = new CreateBookmarkArgs { BookmarkName = BackgroundActivityBookmarkName, Payload = bookmarkPayload }; + var bookmarkOptions = new CreateBookmarkArgs { BookmarkName = BackgroundActivityBookmarkName, Payload = bookmarkPayload, AutoComplete = false }; var bookmark = context.CreateBookmark(bookmarkOptions); scheduledBackgroundActivities.Add(new ScheduledBackgroundActivity(workflowInstanceId, activityNodeId, bookmark.Id)); } diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs index 8a0476ac0..2e45cde58 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowInbox.cs @@ -104,17 +104,20 @@ public class DefaultWorkflowInbox : IWorkflowInbox var activityInstanceId = message.ActivityInstanceId; var bookmarkPayload = message.BookmarkPayload; var input = message.Input; - + if (workflowInstanceId != null) { - await _workflowDispatcher.DispatchAsync(new DispatchTriggerWorkflowsRequest(activityTypeName, bookmarkPayload) - { - CorrelationId = correlationId, - WorkflowInstanceId = workflowInstanceId, - ActivityInstanceId = activityInstanceId, - Input = input - }, cancellationToken); + await ResumeWorkflowsAsync(message, cancellationToken); + return; } + + await _workflowDispatcher.DispatchAsync(new DispatchTriggerWorkflowsRequest(activityTypeName, bookmarkPayload) + { + CorrelationId = correlationId, + WorkflowInstanceId = workflowInstanceId, + ActivityInstanceId = activityInstanceId, + Input = input + }, cancellationToken); } private async Task ResumeWorkflowsAsync(WorkflowInboxMessage message, CancellationToken cancellationToken = default)