Adds activity test-run endpoint

Adds an API endpoint to facilitate testing of individual activities within a workflow.

The endpoint allows developers to execute a specific activity within a given workflow definition.

Also ensures latest workflow definition is marked when publishing.
This commit is contained in:
Sipke Schoorstra 2025-06-08 20:11:58 +02:00
parent a18dbdcb80
commit e7fcf94b66
No known key found for this signature in database
GPG key ID: 5C10502B28A4268F
3 changed files with 57 additions and 8 deletions

View file

@ -0,0 +1,56 @@
using Elsa.Abstractions;
using Elsa.Workflows.Management;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.Api.Endpoints.Activities.TestRun;
/// <summary>
/// This abstract class provides the necessary infrastructure to handle the execution of workflows, including setup of routes, permissions,
/// and processing of HTTP requests to execute workflows.
/// </summary>
internal class Endpoint(
IWorkflowDefinitionService workflowDefinitionService,
IActivityInvoker activityInvoker,
IIdentityGenerator identityGenerator,
IServiceProvider serviceProvider)
: ElsaEndpoint<Request>
{
/// <inheritdoc />
public override void Configure()
{
Post("/activities/test-run");
ConfigurePermissions("exec:activities");
}
/// <inheritdoc />
public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
{
var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(request.WorkflowDefinitionHandle, cancellationToken);
if (workflowGraph == null)
{
AddError("Workflow definition not found.");
await SendErrorsAsync(cancellation: cancellationToken);
return;
}
var workflowInstanceId = identityGenerator.GenerateId();
var workflowExecutionContext = await WorkflowExecutionContext.CreateAsync(serviceProvider, workflowGraph, workflowInstanceId, cancellationToken: cancellationToken);
var activity = workflowExecutionContext.FindActivity(request.ActivityHandle);
if (activity == null)
{
AddError("Activity not found.");
await SendErrorsAsync(cancellation: cancellationToken);
return;
}
await activityInvoker.InvokeAsync(workflowExecutionContext, activity);
}
}
public class Request
{
public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; set; } = null!;
public ActivityHandle ActivityHandle { get; set; } = null!;
}

View file

@ -82,14 +82,6 @@ public class DefaultActivityInvokerMiddleware(ActivityMiddlewareDelegate next, I
// Invoke next middleware.
await next(context);
// // If the activity created any bookmarks, copy them into the workflow execution context.
// if (context.Bookmarks.Any())
// {
// // Store bookmarks.
// workflowExecutionContext.Bookmarks.AddRange(context.Bookmarks);
// logger.LogDebug("Added {BookmarkCount} bookmarks to the workflow execution context", context.Bookmarks.Count);
// }
// Conditionally commit the workflow state.
if (ShouldCommit(context, ActivityLifetimeEvent.ActivityExecuted))
await context.WorkflowExecutionContext.CommitAsync();

View file

@ -113,6 +113,7 @@ public class WorkflowDefinitionPublisher(
// Save the newly published definition.
definition.IsPublished = true;
definition.IsLatest = true;
definition = Initialize(definition);
await workflowDefinitionStore.SaveAsync(definition, cancellationToken);