using System.Globalization; using System.Text.Json.Nodes; using w4c_workflows.Models.Nodes; namespace w4c_workflows.Services.Nodes.Executors; /// /// Wait node. Pauses for the configured duration (per item, since it runs in /// each-item mode) and then passes the item through unchanged. The delay is /// capped to protect the in-process run loop from a runaway configuration. /// public sealed class WaitNodeExecutor : INodeExecutor { /// Upper bound on a single wait, so one node cannot stall the run loop indefinitely. public static readonly TimeSpan MaxDelay = TimeSpan.FromMinutes(5); public string Type => "core.wait"; public async Task RunAsync(NodeExecutionContext context, CancellationToken ct) { var delay = ResolveDelay(context); if (delay > TimeSpan.Zero) await Task.Delay(delay, ct); return NodeExecutionOutcome.Single(context.Input(0).ToList()); } private static TimeSpan ResolveDelay(NodeExecutionContext context) { var amount = ReadDouble(context, "amount", 1); if (amount <= 0) return TimeSpan.Zero; var unit = context.Parameters["unit"] is JsonValue value && value.TryGetValue(out var text) ? text : "seconds"; var delay = unit switch { "milliseconds" => TimeSpan.FromMilliseconds(amount), "minutes" => TimeSpan.FromMinutes(amount), _ => TimeSpan.FromSeconds(amount), }; return delay > MaxDelay ? MaxDelay : delay; } private static double ReadDouble(NodeExecutionContext context, string name, double defaultValue) { if (context.Parameters[name] is not JsonValue value) return defaultValue; if (value.TryGetValue(out var doubleValue)) return doubleValue; if (value.TryGetValue(out var decimalValue)) return (double)decimalValue; if (value.TryGetValue(out var longValue)) return longValue; if (value.TryGetValue(out var text) && double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed)) return parsed; return defaultValue; } }