Add support for providing workflow input via dispatch endpoint

This commit is contained in:
Sipke Schoorstra 2023-03-13 12:31:23 +01:00
parent 2f8dde1f99
commit d8b95db04e
7 changed files with 46 additions and 21 deletions

View file

@ -3,6 +3,7 @@ using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.JavaScript.Notifications;
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Core.Models;
using Elsa.Workflows.Management.Activities;
using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity;
@ -38,27 +39,41 @@ public class WorkflowDefinitionActivityJavaScriptHandler : INotificationHandler<
var engine = notification.Engine;
var context = notification.Context;
// If we are already evaluating inputs, then we're in a circular evaluation loop. In this case, we should not attempt to evaluate the inputs.
if(context.TransientProperties.TryGetValue("EvaluatingInputs", out var evaluatingInputs) && (bool)evaluatingInputs)
return;
// Always create workflow input accessors.
CreateWorkflowInputAccessors(engine, context);
// If we are already evaluating inputs, then we're in a circular evaluation loop. In this case, we should not attempt to evaluate the inputs.
if (context.TransientProperties.TryGetValue("EvaluatingInputs", out var evaluatingInputs) && (bool)evaluatingInputs)
return;
// To prevent a circular evaluation loop, set a flag on the context to indicate that we're currently evaluating the inputs.
context.TransientProperties["EvaluatingInputs"] = true;
// Create input getters.
await CreateInputAccessorsAsync(engine, context);
}
private void CreateWorkflowInputAccessors(Engine engine, ExpressionExecutionContext context)
{
var input = context.GetWorkflowExecutionContext().Input;
foreach (var inputEntry in input)
{
var inputPascalName = inputEntry.Key.Pascalize();
var inputValue = inputEntry.Value;
engine.SetValue($"get{inputPascalName}", (Func<object?>)(() => inputValue));
}
}
private async Task CreateInputAccessorsAsync(Engine engine, ExpressionExecutionContext context)
{
var workflowDefinitionActivity = GetFirstWorkflowDefinitionActivity(context);
var workflowDefinitionActivity = context.GetActivityExecutionContext().GetFirstWorkflowDefinitionActivity();
if (workflowDefinitionActivity == null)
return;
var descriptor = _activityRegistry.Find(workflowDefinitionActivity.Type, workflowDefinitionActivity.Version)!;
var inputDefinitions = descriptor.Inputs;
var workflowDefinitionActivityDescriptor = _activityRegistry.Find(workflowDefinitionActivity.Type, workflowDefinitionActivity.Version);
var inputDefinitions = workflowDefinitionActivityDescriptor?.Inputs ?? Enumerable.Empty<InputDescriptor>();
foreach (var inputDefinition in inputDefinitions)
{
var inputPascalName = inputDefinition.Name.Pascalize();
@ -68,7 +83,4 @@ public class WorkflowDefinitionActivityJavaScriptHandler : INotificationHandler<
engine.SetValue($"get{inputPascalName}", (Func<object?>)(() => evaluatedExpression));
}
}
private static WorkflowDefinitionActivity? GetFirstWorkflowDefinitionActivity(ExpressionExecutionContext context) =>
context.GetActivityExecutionContext().GetFirstWorkflowDefinitionActivity();
}

View file

@ -70,6 +70,7 @@ public class JintJavaScriptEvaluator : IJavaScriptEvaluator
engine.SetValue("setCorrelationId", (Action<string?>)(value => context.GetActivityExecutionContext().WorkflowExecutionContext.CorrelationId = value));
engine.SetValue("setVariable", (Action<string, object>)((name, value) => context.SetVariable(name, value)));
engine.SetValue("getVariable", (Func<string, object?>)(name => context.GetVariable(name)));
engine.SetValue("getInput", (Func<string, object?>)(name => context.GetWorkflowExecutionContext().Input.GetValue(name)));
// Create variable & input setters and getters for each variable.
CreateMemoryBlockAccessors(engine, context);

View file

@ -42,6 +42,11 @@ internal class CommonFunctionsDefinitionProvider : FunctionDefinitionProvider
.Parameter("name", "string")
.ReturnType("any"));
yield return CreateFunctionDefinition(builder => builder
.Name("getInput")
.Parameter("name", "string")
.ReturnType("any"));
yield return CreateFunctionDefinition(builder => builder
.Name("isNullOrWhiteSpace")
.Parameter("value", "string")

View file

@ -34,11 +34,15 @@ internal class Endpoint : ElsaEndpoint<Request, Response>
await SendNotFoundAsync(cancellationToken);
return;
}
var correlationId = request.CorrelationId;
var input = (IDictionary<string, object>?)request.Input;
var result = await _workflowDispatcher.DispatchAsync(new DispatchWorkflowDefinitionRequest(
await _workflowDispatcher.DispatchAsync(new DispatchWorkflowDefinitionRequest(
request.DefinitionId,
VersionOptions.Published,
CorrelationId: request.CorrelationId),
input,
correlationId),
cancellationToken);
await SendOkAsync(new Response(), cancellationToken);

View file

@ -1,11 +1,17 @@
using System.Text.Json.Serialization;
using Elsa.Workflows.Core.Serialization.Converters;
namespace Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Dispatch;
public class Request
internal class Request
{
public string DefinitionId { get; set; } = default!;
public string? CorrelationId { get; set; }
[JsonConverter(typeof(ExpandoObjectConverter))]
public object? Input { get; set; }
}
public class Response
internal class Response
{
}

View file

@ -2,9 +2,8 @@ using Elsa.Mediator.Contracts;
using Elsa.Workflows.Management.Contracts;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Management.Notifications;
using Elsa.Workflows.Management.Providers;
namespace Elsa.Workflows.Management.Handlers;
namespace Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity;
/// <summary>
/// Refreshes the <see cref="IActivityRegistry"/> for the <see cref="WorkflowDefinitionActivityProvider"/> provider whenever an <see cref="WorkflowDefinition"/> is published, retracted or deleted.

View file

@ -1,12 +1,10 @@
using Elsa.Common.Models;
using Elsa.Workflows.Core.Models;
using Elsa.Workflows.Management.Activities;
using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity;
using Elsa.Workflows.Management.Contracts;
using Elsa.Workflows.Management.Entities;
using Humanizer;
namespace Elsa.Workflows.Management.Providers;
namespace Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity;
/// <summary>
/// Provides activity descriptors based on <see cref="WorkflowDefinition"/>s stored in the database.