97 lines
3.7 KiB
C#
97 lines
3.7 KiB
C#
using System.Text.Json.Nodes;
|
|
using w4c_workflows.Models.Nodes;
|
|
|
|
namespace w4c_workflows.Services.Nodes;
|
|
|
|
/// <summary>
|
|
/// Runs one node type. Implementations are registered in the node catalog and
|
|
/// resolved by the executor registry; the run engine feeds them resolved
|
|
/// parameters and input items and routes the returned outputs by port index.
|
|
/// </summary>
|
|
public interface INodeExecutor
|
|
{
|
|
/// <summary>Blueprint type this executor implements, e.g. "core.set".</summary>
|
|
string Type { get; }
|
|
|
|
Task<NodeExecutionOutcome> RunAsync(NodeExecutionContext context, CancellationToken ct);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Everything a node needs for one invocation. Parameters are already resolved
|
|
/// (interpolation applied) for the target item; the raw scope stays available so
|
|
/// a node can re-evaluate a parameter per item if it needs to.
|
|
/// </summary>
|
|
public sealed record NodeExecutionContext
|
|
{
|
|
public required NodeBlueprint Blueprint { get; init; }
|
|
|
|
/// <summary>Resolved parameters keyed by parameter name.</summary>
|
|
public required JsonObject Parameters { get; init; }
|
|
|
|
/// <summary>Input items per input port index.</summary>
|
|
public required IReadOnlyList<IReadOnlyList<FlowItem>> Inputs { get; init; }
|
|
|
|
/// <summary>Decrypted credential data keyed by credential alias.</summary>
|
|
public IReadOnlyDictionary<string, JsonObject> Credentials { get; init; } =
|
|
new Dictionary<string, JsonObject>();
|
|
|
|
/// <summary>Workflow-level environment variables.</summary>
|
|
public JsonObject Environment { get; init; } = new();
|
|
|
|
public string TenantId { get; init; } = string.Empty;
|
|
public string RunId { get; init; } = string.Empty;
|
|
public string TaskId { get; init; } = string.Empty;
|
|
public string? NodeName { get; init; }
|
|
|
|
/// <summary>Index of the item this invocation targets (0 for all-items mode).</summary>
|
|
public int ItemIndex { get; init; }
|
|
|
|
/// <summary>Run iteration index for loops (0 on the first pass).</summary>
|
|
public int RunIndex { get; init; }
|
|
|
|
public IServiceProvider? Services { get; init; }
|
|
|
|
/// <summary>Items from the given input port (empty when the port is absent).</summary>
|
|
public IReadOnlyList<FlowItem> Input(int portIndex = 0)
|
|
=> portIndex >= 0 && portIndex < Inputs.Count ? Inputs[portIndex] : Array.Empty<FlowItem>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Result of one node invocation. <see cref="Outputs"/> is indexed by output
|
|
/// port; a missing port means "no items on that port".
|
|
/// </summary>
|
|
public sealed record NodeExecutionOutcome
|
|
{
|
|
public required IReadOnlyList<IReadOnlyList<FlowItem>> Outputs { get; init; }
|
|
|
|
/// <summary>Set when the node failed but the engine may route to the error port.</summary>
|
|
public NodeFailure? Failure { get; init; }
|
|
|
|
public bool Succeeded => Failure == null;
|
|
|
|
/// <summary>Single-output convenience wrapper.</summary>
|
|
public static NodeExecutionOutcome Single(IReadOnlyList<FlowItem> items)
|
|
=> new() { Outputs = new[] { items } };
|
|
|
|
/// <summary>No items on any port (used by control nodes that consume input).</summary>
|
|
public static readonly NodeExecutionOutcome Empty = new()
|
|
{
|
|
Outputs = Array.Empty<IReadOnlyList<FlowItem>>(),
|
|
};
|
|
|
|
/// <summary>Failure on the error port.</summary>
|
|
public static NodeExecutionOutcome Failed(string message, string? code = null, string? description = null)
|
|
=> new()
|
|
{
|
|
Outputs = Array.Empty<IReadOnlyList<FlowItem>>(),
|
|
Failure = new NodeFailure(message, code, description),
|
|
};
|
|
}
|
|
|
|
/// <summary>A node-level failure, optionally mapped to a user-facing message.</summary>
|
|
public sealed record NodeFailure(
|
|
string Message,
|
|
string? Code = null,
|
|
string? Description = null,
|
|
int? HttpStatus = null);
|