Files
patel-lyzr 1766bdf497 feat: Implement AI Gateway page with feature tiles and descriptions
feat: Update AppSidebar to include new Environments and Credentials sections

feat: Enhance node form with branch selection for triggers and deployment

feat: Create CredentialForm and CredentialRow components for managing credentials

feat: Add API endpoints for credential management

fix: Update node catalog defaults for deploy and promote nodes
2026-05-13 22:15:08 +05:30

120 lines
4.1 KiB
Go

package executors
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/lyzrai/flow/pkg/engine"
"github.com/lyzrai/flow/pkg/models"
)
// stubExecutor implements a no-op executor that:
// - logs the node's parameters under a distinct slog message
// - sleeps `sleep` to simulate work (interruptible by ctx cancel)
// - decorates each input item with `{stub: <stage>, status: success}` so
// downstream nodes have something to read.
//
// This is a deliberate placeholder for nodes whose real backing is still
// pending (Test, Eval, Policy, Deploy, Promote, Rollback). Swap individual
// stubs for real executors as they're built; the runtime contract stays
// the same.
type stubExecutor struct {
stage string // "test" | "eval" | "policy" | ...
logName string // slog message name
sleep time.Duration // simulated work
}
func (e *stubExecutor) Execute(ctx context.Context, node models.NodeDef, inputs [][]models.Item, _ *engine.ExecutionContext) (map[int][]models.Item, error) {
slog.InfoContext(ctx, e.logName,
slog.String("node", node.Name),
slog.Any("parameters", node.Parameters),
)
logger := engine.NodeLoggerFromContext(ctx)
logger.Log("[stub:" + e.stage + "] starting " + node.Name)
if len(node.Parameters) > 0 {
for k, v := range node.Parameters {
logger.Log(" param " + k + " = " + sprintAny(v))
}
}
if e.sleep > 0 {
t := time.NewTimer(e.sleep)
defer t.Stop()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-t.C:
}
}
var inputItems []models.Item
for _, in := range inputs {
inputItems = append(inputItems, in...)
}
if len(inputItems) == 0 {
// Trigger nodes typically pass {} as the single seed item; preserve
// that shape so a stub at the head of the pipeline still produces
// something downstream can consume.
inputItems = []models.Item{{}}
}
out := make([]models.Item, 0, len(inputItems))
for _, item := range inputItems {
ci := copyItem(item)
ci["__stub"] = e.stage
ci["__status"] = "success"
ci["__node"] = node.Name
out = append(out, ci)
}
logger.Log("[stub:" + e.stage + "] finished " + node.Name + " (" + strconvItoa(len(out)) + " items)")
return map[int][]models.Item{0: out}, nil
}
// strconvItoa wraps strconv.Itoa so we don't pull strconv into stubs.go just for one call.
func strconvItoa(n int) string { return fmt.Sprintf("%d", n) }
// sprintAny formats a parameter value for log lines, capping length so a
// huge JSON blob doesn't blow up the log channel.
func sprintAny(v any) string {
s := fmt.Sprintf("%v", v)
if len(s) > 200 {
return s[:200] + "…"
}
return s
}
// TestExecutor stubs the Test stage.
type TestExecutor struct{}
func (e *TestExecutor) Execute(ctx context.Context, node models.NodeDef, inputs [][]models.Item, ec *engine.ExecutionContext) (map[int][]models.Item, error) {
return (&stubExecutor{stage: "test", logName: "stub_test", sleep: 500 * time.Millisecond}).Execute(ctx, node, inputs, ec)
}
// EvalExecutor stubs the Eval stage.
type EvalExecutor struct{}
func (e *EvalExecutor) Execute(ctx context.Context, node models.NodeDef, inputs [][]models.Item, ec *engine.ExecutionContext) (map[int][]models.Item, error) {
return (&stubExecutor{stage: "eval", logName: "stub_eval", sleep: 750 * time.Millisecond}).Execute(ctx, node, inputs, ec)
}
// PolicyExecutor stubs the Policy stage.
type PolicyExecutor struct{}
func (e *PolicyExecutor) Execute(ctx context.Context, node models.NodeDef, inputs [][]models.Item, ec *engine.ExecutionContext) (map[int][]models.Item, error) {
return (&stubExecutor{stage: "policy", logName: "stub_policy", sleep: 200 * time.Millisecond}).Execute(ctx, node, inputs, ec)
}
// DeployExecutor lives in deploy.go (real impl) — was previously a stub.
// PromoteExecutor lives in promote.go (real impl) — was previously a stub.
// RollbackExecutor stubs the Rollback stage.
type RollbackExecutor struct{}
func (e *RollbackExecutor) Execute(ctx context.Context, node models.NodeDef, inputs [][]models.Item, ec *engine.ExecutionContext) (map[int][]models.Item, error) {
return (&stubExecutor{stage: "rollback", logName: "stub_rollback", sleep: 500 * time.Millisecond}).Execute(ctx, node, inputs, ec)
}