80 lines
3.2 KiB
C#
80 lines
3.2 KiB
C#
using w4c_workflows.Models.Nodes;
|
|
|
|
namespace w4c_workflows.Services.Nodes;
|
|
|
|
/// <summary>
|
|
/// Runs another workflow from a <c>core.executeWorkflow</c> node and maps the
|
|
/// child's terminal output back to items. The implementation resolves the child
|
|
/// definition in the caller's tenant, enforces the recursion/depth guard and
|
|
/// (optionally) persists a parent-linked child run for history; the node
|
|
/// executor only shapes parameters and output.
|
|
/// </summary>
|
|
public interface ISubWorkflowInvoker
|
|
{
|
|
/// <summary>Maximum allowed nesting depth (a top-level run is depth 0).</summary>
|
|
int MaxDepth { get; }
|
|
|
|
Task<SubWorkflowRunResult> InvokeAsync(SubWorkflowInvocation invocation, CancellationToken ct);
|
|
}
|
|
|
|
/// <summary>How a sub-workflow node hands its input to the child run.</summary>
|
|
public static class SubWorkflowMode
|
|
{
|
|
/// <summary>One child run receives all input items at once (the default).</summary>
|
|
public const string AllItems = "allItems";
|
|
|
|
/// <summary>One child run per input item; their output items are concatenated.</summary>
|
|
public const string EachItem = "eachItem";
|
|
}
|
|
|
|
/// <summary>
|
|
/// A request to run a child workflow: which workflow, with what input items, and
|
|
/// whether the child run is recorded. The parent run/task context is bound to
|
|
/// the invoker instance by the run that created it.
|
|
/// </summary>
|
|
public sealed record SubWorkflowInvocation
|
|
{
|
|
/// <summary>Target workflow id (Guid string) or unique name within the tenant.</summary>
|
|
public required string Workflow { get; init; }
|
|
|
|
/// <summary>Items handed to the child as its seed input.</summary>
|
|
public required IReadOnlyList<FlowItem> Input { get; init; }
|
|
|
|
/// <summary>One of <see cref="SubWorkflowMode"/>.</summary>
|
|
public string Mode { get; init; } = SubWorkflowMode.AllItems;
|
|
|
|
/// <summary>Persist a parent-linked child <c>WorkflowRun</c> (and its task rows).</summary>
|
|
public bool RecordHistory { get; init; } = true;
|
|
|
|
/// <summary>
|
|
/// When true the child's output items become this node's output; when false
|
|
/// the child still runs but its output is dropped (fire-and-forget semantics).
|
|
/// </summary>
|
|
public bool WaitForCompletion { get; init; } = true;
|
|
|
|
/// <summary>The calling node's id, used to stamp the child run's parent task.</summary>
|
|
public string? ParentNodeId { get; init; }
|
|
|
|
/// <summary>Directory the child's code nodes resolve their entry files from.</summary>
|
|
public string? WorkingDirectory { get; init; }
|
|
}
|
|
|
|
/// <summary>Outcome of a sub-workflow invocation.</summary>
|
|
public sealed record SubWorkflowRunResult
|
|
{
|
|
public required IReadOnlyList<FlowItem> Items { get; init; }
|
|
|
|
public NodeFailure? Failure { get; init; }
|
|
|
|
/// <summary>Id of the child run that produced the items (null when none was recorded).</summary>
|
|
public Guid? RunId { get; init; }
|
|
|
|
public bool Succeeded => Failure == null;
|
|
|
|
public static SubWorkflowRunResult Ok(IReadOnlyList<FlowItem> items, Guid? runId = null)
|
|
=> new() { Items = items, RunId = runId };
|
|
|
|
public static SubWorkflowRunResult Failed(string message, string? code = null, Guid? runId = null)
|
|
=> new() { Items = Array.Empty<FlowItem>(), Failure = new NodeFailure(message, code), RunId = runId };
|
|
}
|