Merge remote-tracking branch 'origin/bug/activity-execution-record-state' into develop/3.6.0
This commit is contained in:
commit
6d38ebbc97
|
|
@ -1,6 +1,7 @@
|
|||
using Elsa.Mediator.Contracts;
|
||||
using Elsa.Workflows.Signals;
|
||||
using Elsa.Workflows.Notifications;
|
||||
using JetBrains.Annotations;
|
||||
using ActivityCompleted = Elsa.Workflows.Signals.ActivityCompleted;
|
||||
|
||||
namespace Elsa.Workflows.Behaviors;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Mediator.Contracts;
|
||||
using Elsa.Workflows.Activities.Flowchart.Models;
|
||||
using Elsa.Workflows.Signals;
|
||||
|
||||
|
|
@ -62,6 +63,9 @@ public partial class ActivityExecutionContext
|
|||
|
||||
// Update the completed at timestamp.
|
||||
CompletedAt = WorkflowExecutionContext.SystemClock.UtcNow;
|
||||
|
||||
var mediator = GetRequiredService<INotificationSender>();
|
||||
await mediator.SendAsync(new Notifications.ActivityCompleted(this), CancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ namespace Elsa.Workflows.Signals;
|
|||
/// Signaled when an activity has completed.
|
||||
/// </summary>
|
||||
/// <param name="Result">An optional result.</param>
|
||||
public record ActivityCompleted(object? Result = default);
|
||||
public record ActivityCompleted(object? Result = null);
|
||||
|
|
@ -33,7 +33,6 @@ public class DistributedRuntimeFeature : FeatureBase
|
|||
{
|
||||
Services
|
||||
.AddScoped<DistributedWorkflowRuntime>()
|
||||
.AddScoped<DistributedBookmarkQueueWorker>()
|
||||
.AddCommandHandler<CancelWorkflowsCommandHandler>();
|
||||
.AddScoped<DistributedBookmarkQueueWorker>();
|
||||
}
|
||||
}
|
||||
|
|
@ -8,34 +8,32 @@ namespace Elsa.Extensions;
|
|||
public static class ActivityExecutionContextRecordExtensions
|
||||
{
|
||||
private const string ActivityExecutionRecordKey = "CapturedActivityExecutionRecord";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Captures the activity execution record for the provided <see cref="ActivityExecutionContext"/> and stores it in the context's transient properties.
|
||||
/// </summary>
|
||||
public static async Task CaptureActivityExecutionRecordAsync(this ActivityExecutionContext context)
|
||||
{
|
||||
var mapper = context.GetRequiredService<IActivityExecutionMapper>();
|
||||
var record = await mapper.MapAsync(context);
|
||||
context.TransientProperties[ActivityExecutionRecordKey] = record;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the captured activity execution record from the transient properties of the provided <see cref="ActivityExecutionContext"/>.
|
||||
/// If the record is not found, it maps and returns a new activity execution record using the <see cref="IActivityExecutionMapper"/> service.
|
||||
/// </summary>
|
||||
public static async Task<ActivityExecutionRecord> GetOrMapCapturedActivityExecutionRecordAsync(this ActivityExecutionContext context)
|
||||
{
|
||||
// If the record is already captured in the transient properties, return it, as it will contain the serialized state of the activity execution at the time of capture, rather than the current state.
|
||||
// This is useful for scenarios where the activity execution state may change after the record is captured, such as referenced workflow variables.
|
||||
if (context.TransientProperties.TryGetValue(ActivityExecutionRecordKey, out var capturedRecord))
|
||||
return (ActivityExecutionRecord)capturedRecord;
|
||||
|
||||
// If the record is not captured, map a new activity execution record using the mapper.
|
||||
var mapper = context.GetRequiredService<IActivityExecutionMapper>();
|
||||
var record = await mapper.MapAsync(context);
|
||||
|
||||
if (context.TransientProperties.TryGetValue(ActivityExecutionRecordKey, out var capturedRecord))
|
||||
{
|
||||
var serializedSnapshot = ((ActivityExecutionRecord)capturedRecord).SerializedSnapshot!;
|
||||
|
||||
// Take the existing serialized snapshot.
|
||||
record.SerializedSnapshot = serializedSnapshot;
|
||||
|
||||
// Update the serialized snapshot with the current record's properties.
|
||||
// This will reflect the latest state of the activity execution context without losing the existing serialized snapshot representing e.g., variable values at the time of the record capture.
|
||||
serializedSnapshot.HasBookmarks = record.HasBookmarks;
|
||||
serializedSnapshot.Status = record.Status;
|
||||
serializedSnapshot.AggregateFaultCount = record.AggregateFaultCount;
|
||||
serializedSnapshot.CompletedAt = record.CompletedAt;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,6 @@ public static class PipelineWorkflowsFeatureExtensions
|
|||
.UseExecutionLogging()
|
||||
.UseNotifications()
|
||||
.UseLogPersistenceModeEvaluation()
|
||||
.UseActivityExecutionLogCapturing()
|
||||
.UseBackgroundActivityInvoker();
|
||||
|
||||
configurePipeline?.Invoke(pipeline);
|
||||
|
|
|
|||
|
|
@ -345,6 +345,7 @@ public class WorkflowRuntimeFeature(IModule module) : FeatureBase(module)
|
|||
.AddNotificationHandler<RefreshActivityRegistry>()
|
||||
.AddNotificationHandler<SignalBookmarkQueueWorker>()
|
||||
.AddNotificationHandler<EvaluateParentLogPersistenceModes>()
|
||||
.AddNotificationHandler<CaptureActivityExecutionState>()
|
||||
.AddNotificationHandler<ValidateWorkflowRequestHandler>()
|
||||
|
||||
// Workflow activation strategies.
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
using Elsa.Mediator.Contracts;
|
||||
using Elsa.Mediator.Models;
|
||||
using Elsa.Workflows.Runtime.Commands;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Handles the <see cref="CancelWorkflowsCommand"/>.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class CancelWorkflowsCommandHandler(IWorkflowRuntime workflowRuntime) : ICommandHandler<CancelWorkflowsCommand>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Mediator.Contracts;
|
||||
using Elsa.Workflows.Notifications;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Captures the execution state of an activity when it completes.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class CaptureActivityExecutionState : INotificationHandler<ActivityCompleted>
|
||||
{
|
||||
public async Task HandleAsync(ActivityCompleted notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var context = notification.ActivityExecutionContext;
|
||||
await context.CaptureActivityExecutionRecordAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -20,12 +20,4 @@ public static class ActivityExecutionPipelineBuilderExtensions
|
|||
/// Installs the <see cref="EvaluateLogPersistenceModesMiddleware"/> which evaluates log persistence modes during activity execution.
|
||||
/// </summary>
|
||||
public static IActivityExecutionPipelineBuilder UseLogPersistenceModeEvaluation(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.UseMiddleware<EvaluateLogPersistenceModesMiddleware>();
|
||||
|
||||
/// <summary>
|
||||
/// Installs the <see cref="CaptureActivityExecutionRecordMiddleware"/> into the activity execution pipeline to capture and map activity execution details.
|
||||
/// </summary>
|
||||
public static IActivityExecutionPipelineBuilder UseActivityExecutionLogCapturing(this IActivityExecutionPipelineBuilder pipelineBuilder)
|
||||
{
|
||||
return pipelineBuilder.UseMiddleware<CaptureActivityExecutionRecordMiddleware>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Workflows.Pipelines.ActivityExecution;
|
||||
|
||||
namespace Elsa.Workflows.Runtime.Middleware.Activities;
|
||||
|
||||
public class CaptureActivityExecutionRecordMiddleware(ActivityMiddlewareDelegate next) : IActivityExecutionMiddleware
|
||||
{
|
||||
public async ValueTask InvokeAsync(ActivityExecutionContext context)
|
||||
{
|
||||
await next(context);
|
||||
await context.CaptureActivityExecutionRecordAsync();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue