73 lines
3.1 KiB
C#
73 lines
3.1 KiB
C#
using System.Text.Json.Nodes;
|
|
using w4c_workflows.Models.Nodes;
|
|
|
|
namespace w4c_workflows.Services.Nodes.Executors;
|
|
|
|
/// <summary>
|
|
/// Runs another workflow in-process and turns its output into this node's items
|
|
/// (the <c>core.executeWorkflow</c> step). The node itself only reads its
|
|
/// parameters and shapes the result: resolving the child definition, the
|
|
/// recursion/depth guard and child-run history live behind
|
|
/// <see cref="ISubWorkflowInvoker"/>.
|
|
///
|
|
/// The blueprint runs <c>allItems</c>, so the node always sees the whole input
|
|
/// batch and chooses per-item or single-run semantics itself.
|
|
/// </summary>
|
|
public sealed class ExecuteWorkflowNodeExecutor : INodeExecutor
|
|
{
|
|
public string Type => "core.executeWorkflow";
|
|
|
|
public async Task<NodeExecutionOutcome> RunAsync(NodeExecutionContext context, CancellationToken ct)
|
|
{
|
|
var invoker = context.SubWorkflows;
|
|
if (invoker == null)
|
|
{
|
|
return NodeExecutionOutcome.Failed(
|
|
"sub-workflow execution is not available on this runtime", "subworkflow_unavailable");
|
|
}
|
|
|
|
var workflow = ReadString(context, "workflow");
|
|
if (string.IsNullOrWhiteSpace(workflow))
|
|
return NodeExecutionOutcome.Failed(
|
|
"core.executeWorkflow requires a 'workflow' parameter", "invalid_parameter");
|
|
|
|
var mode = ReadString(context, "mode") ?? SubWorkflowMode.AllItems;
|
|
if (!string.Equals(mode, SubWorkflowMode.AllItems, StringComparison.OrdinalIgnoreCase) &&
|
|
!string.Equals(mode, SubWorkflowMode.EachItem, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return NodeExecutionOutcome.Failed(
|
|
$"core.executeWorkflow mode '{mode}' is invalid", "invalid_parameter");
|
|
}
|
|
|
|
var invocation = new SubWorkflowInvocation
|
|
{
|
|
Workflow = workflow,
|
|
Input = context.Input(0).ToList(),
|
|
Mode = mode,
|
|
RecordHistory = ReadBool(context, "recordHistory", defaultValue: true),
|
|
WaitForCompletion = ReadBool(context, "waitForCompletion", defaultValue: true),
|
|
ParentNodeId = context.TaskId,
|
|
WorkingDirectory = context.WorkingDirectory,
|
|
};
|
|
|
|
var result = await invoker.InvokeAsync(invocation, ct);
|
|
if (!result.Succeeded)
|
|
{
|
|
return NodeExecutionOutcome.Failed(
|
|
result.Failure!.Message, result.Failure.Code, result.Failure.Description);
|
|
}
|
|
|
|
// With waiting disabled the child still runs, but its items are not handed
|
|
// downstream — a fire-and-forget call.
|
|
return invocation.WaitForCompletion
|
|
? NodeExecutionOutcome.Single(result.Items)
|
|
: NodeExecutionOutcome.Empty;
|
|
}
|
|
|
|
private static string? ReadString(NodeExecutionContext context, string name)
|
|
=> context.Parameters[name] is JsonValue value && value.TryGetValue<string>(out var text) ? text : null;
|
|
|
|
private static bool ReadBool(NodeExecutionContext context, string name, bool defaultValue)
|
|
=> context.Parameters[name] is JsonValue value && value.TryGetValue<bool>(out var flag) ? flag : defaultValue;
|
|
}
|