diff --git a/Elsa.sln b/Elsa.sln
index 34a01ba3a..f1c26d414 100644
--- a/Elsa.sln
+++ b/Elsa.sln
@@ -64,6 +64,9 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "unit", "unit", "{18453B51-25EB-4317-A4B3-B10518252E92}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "integration", "integration", "{1B8D5897-902E-4632-8698-E89CAF3DDF54}"
+ ProjectSection(SolutionItems) = preProject
+ test\integration\Elsa.Logging.Core.LoggerSinkTests.cs = test\integration\Elsa.Logging.Core.LoggerSinkTests.cs
+ EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "component", "component", "{08B41FFA-CEE3-46A7-B5C0-3EB65D37A16C}"
ProjectSection(SolutionItems) = preProject
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/EndpointBase.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/EndpointBase.cs
deleted file mode 100644
index 70432fd97..000000000
--- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/EndpointBase.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using System.Net.Mime;
-using Elsa.Abstractions;
-using Elsa.Common.Models;
-using Elsa.Workflows.Management;
-using Elsa.Workflows.Runtime;
-using Elsa.Workflows.State;
-using Microsoft.AspNetCore.Http;
-
-namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Execute;
-
-///
-/// 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.
-///
-internal abstract class EndpointBase(
- IWorkflowDefinitionService workflowDefinitionService,
- IWorkflowRuntime workflowRuntime,
- IWorkflowStarter workflowStarter,
- IApiSerializer apiSerializer)
- : ElsaEndpoint where T : IExecutionRequest, new()
-{
- ///
- public override void Configure()
- {
- Routes("/workflow-definitions/{definitionId}/execute");
- ConfigurePermissions("exec:workflow-definitions");
- }
-
- ///
- public override async Task HandleAsync(T request, CancellationToken cancellationToken)
- {
- var definitionId = request.DefinitionId;
- var versionOptions = request.VersionOptions ?? VersionOptions.Published;
- var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(definitionId, versionOptions, cancellationToken);
-
- if (workflowGraph == null)
- {
- await Send.NotFoundAsync(cancellationToken);
- return;
- }
-
- var startRequest = new StartWorkflowRequest
- {
- Workflow = workflowGraph.Workflow,
- CorrelationId = request.CorrelationId,
- Name = request.Name,
- Input = request.GetInputAsDictionary(),
- Variables = request.GetVariablesAsDictionary(),
- TriggerActivityId = request.TriggerActivityId,
- ActivityHandle = request.ActivityHandle
- };
-
- var startResponse = await workflowStarter.StartWorkflowAsync(startRequest, cancellationToken);
-
- if(!HttpContext.Response.HasStarted)
- HttpContext.Response.Headers.Append("x-elsa-workflow-cannot-start", startResponse.CannotStart.ToString());
-
- if (startResponse.CannotStart)
- {
- await Send.OkAsync(cancellationToken);
- return;
- }
-
- var instanceId = startResponse.WorkflowInstanceId!;
-
- // Write the workflow instance ID to the response header.
- // This allows clients to read the header even if the workflow writes a response body
- // (in which case, we can't transmit a JSON body that includes the instance ID).
- if(!HttpContext.Response.HasStarted)
- HttpContext.Response.Headers.Append("x-elsa-workflow-instance-id", instanceId);
-
- var workflowClient = await workflowRuntime.CreateClientAsync(instanceId, cancellationToken);
-
- // If a workflow fault occurred, respond appropriately with a 500 internal server error.
- if (startResponse.SubStatus == WorkflowSubStatus.Faulted)
- {
- var workflowState = await workflowClient.ExportStateAsync(cancellationToken);
- await HandleFaultAsync(workflowState, cancellationToken);
- }
- else
- {
- if (!HttpContext.Response.HasStarted)
- {
- // Write a response header to indicate that the response is a workflow state response.
- // This is used by tools like Elsa Studio to determine if the response is in response to a workflow execution manually triggered by the user.
- HttpContext.Response.Headers.Append("x-elsa-response", "true");
-
- // Only write a response if the workflow didn't change the HTTP status code.
- if (HttpContext.Response.StatusCode == StatusCodes.Status200OK)
- {
- var workflowState = await workflowClient.ExportStateAsync(cancellationToken);
- await Send.OkAsync(new(workflowState), cancellationToken);
- }
- }
- }
- }
-
- private async Task HandleFaultAsync(WorkflowState workflowState, CancellationToken cancellationToken)
- {
- var faultedResponse = apiSerializer.Serialize(new Response(workflowState));
-
- HttpContext.Response.ContentType = MediaTypeNames.Application.Json;
- HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
- await HttpContext.Response.WriteAsync(faultedResponse, cancellationToken);
- }
-}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs
index 24d6168e3..6148de9e6 100644
--- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs
@@ -24,7 +24,6 @@ internal class PostEndpoint(
ConfigurePermissions("exec:workflow-definitions");
Verbs(FastEndpoints.Http.POST);
}
-
///
public override async Task HandleAsync(CancellationToken cancellationToken)
{
diff --git a/src/modules/Elsa.Workflows.Core/Builders/WorkflowBuilder.cs b/src/modules/Elsa.Workflows.Core/Builders/WorkflowBuilder.cs
index fb48a93b8..b30d7669e 100644
--- a/src/modules/Elsa.Workflows.Core/Builders/WorkflowBuilder.cs
+++ b/src/modules/Elsa.Workflows.Core/Builders/WorkflowBuilder.cs
@@ -63,6 +63,12 @@ public class WorkflowBuilder(IActivityVisitor activityVisitor, IIdentityGraphSer
DefinitionId = definitionId;
return this;
}
+
+ public IWorkflowBuilder WithId(string id)
+ {
+ Id = id;
+ return this;
+ }
///
public IWorkflowBuilder WithTenantId(string tenantId)
diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilder.cs b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilder.cs
index 363fcbf11..fc9a78cdb 100644
--- a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilder.cs
+++ b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilder.cs
@@ -96,6 +96,13 @@ public interface IWorkflowBuilder
/// The definition ID to use for the workflow being built.
IWorkflowBuilder WithDefinitionId(string definitionId);
+ ///
+ /// A fluent method for setting the property.
+ ///
+ /// The unique identifier to use for the workflow being built.
+ /// The current instance for method chaining.
+ IWorkflowBuilder WithId(string id);
+
///
/// A fluent method for setting the property.
///
diff --git a/src/modules/Elsa.Workflows.Core/Models/InputDescriptor.cs b/src/modules/Elsa.Workflows.Core/Models/InputDescriptor.cs
index 79a1c65ee..726ea230a 100644
--- a/src/modules/Elsa.Workflows.Core/Models/InputDescriptor.cs
+++ b/src/modules/Elsa.Workflows.Core/Models/InputDescriptor.cs
@@ -106,6 +106,12 @@ public class InputDescriptor : PropertyDescriptor
/// True if the expression should be evaluated automatically, false otherwise. Defaults to true.
///
public bool AutoEvaluate { get; set; } = true;
+
+ ///
+ /// Specifies the type of a custom evaluator to use for evaluating the input property value.
+ /// The evaluator type determines how the value for the property is resolved at runtime.
+ ///
+ public Type? EvaluatorType { get; set; }
///
/// Specifies the type of a custom evaluator to use for evaluating the input property value.
diff --git a/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs b/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs
index 024e5c492..bbcd5f0ef 100644
--- a/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Activities/Event.cs
@@ -15,7 +15,7 @@ namespace Elsa.Workflows.Runtime.Activities;
[UsedImplicitly]
public class Event : Trigger