w4c-workflows-api/Services/Nodes/ISubWorkflowInvoker.cs
2026-09-12 01:02:46 +03:00

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 };
}