feat(server): auto-derive outputs on save + resolve inline steps
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mrhid6/vantage/server/internal/models"
|
||||
)
|
||||
|
||||
func TestResolveInlineStep(t *testing.T) {
|
||||
ref := models.WorkflowStepRef{
|
||||
Order: 2,
|
||||
OnFailure: "",
|
||||
Inline: &models.WorkflowStep{
|
||||
Name: "adhoc",
|
||||
Interpreter: "bash",
|
||||
Script: "echo hi",
|
||||
SecretRefs: []string{"TOKEN"},
|
||||
DeclaredInputs: []models.InputParam{
|
||||
{Name: "REGION", Default: "eu"},
|
||||
},
|
||||
},
|
||||
Inputs: map[string]string{"REGION": "us"},
|
||||
}
|
||||
rs := resolveInlineStep(ref)
|
||||
if rs.Name != "adhoc" || rs.Script != "echo hi" || rs.Order != 2 {
|
||||
t.Fatalf("bad resolve: %+v", rs)
|
||||
}
|
||||
if rs.OnFailure != "stop" {
|
||||
t.Fatalf("want default on_failure=stop, got %q", rs.OnFailure)
|
||||
}
|
||||
if rs.Inputs["REGION"] != "us" {
|
||||
t.Fatalf("want input override us, got %q", rs.Inputs["REGION"])
|
||||
}
|
||||
if len(rs.SecretRefs) != 1 || rs.SecretRefs[0] != "TOKEN" {
|
||||
t.Fatalf("bad secret refs: %v", rs.SecretRefs)
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,10 @@ func resolveSteps(wf *models.Workflow) ([]models.ResolvedStep, error) {
|
||||
defer cancel()
|
||||
out := make([]models.ResolvedStep, 0, len(wf.Steps))
|
||||
for _, ref := range wf.Steps {
|
||||
if ref.Inline != nil {
|
||||
out = append(out, resolveInlineStep(ref))
|
||||
continue
|
||||
}
|
||||
lib, err := getStep(ctx, ref.StepID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -123,6 +127,35 @@ func resolveSteps(wf *models.Workflow) ([]models.ResolvedStep, error) {
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// resolveInlineStep freezes an ad-hoc (inline) step ref into a ResolvedStep.
|
||||
func resolveInlineStep(ref models.WorkflowStepRef) models.ResolvedStep {
|
||||
in := ref.Inline
|
||||
inputs := map[string]string{}
|
||||
for _, p := range in.DeclaredInputs {
|
||||
if ref.Inputs != nil {
|
||||
if v, ok := ref.Inputs[p.Name]; ok {
|
||||
inputs[p.Name] = v
|
||||
continue
|
||||
}
|
||||
}
|
||||
inputs[p.Name] = p.Default
|
||||
}
|
||||
onFailure := ref.OnFailure
|
||||
if onFailure == "" {
|
||||
onFailure = "stop"
|
||||
}
|
||||
return models.ResolvedStep{
|
||||
Order: ref.Order,
|
||||
Name: in.Name,
|
||||
Interpreter: in.Interpreter,
|
||||
Script: in.Script,
|
||||
SecretRefs: in.SecretRefs,
|
||||
OnFailure: onFailure,
|
||||
MaxRetries: ref.MaxRetries,
|
||||
Inputs: inputs,
|
||||
}
|
||||
}
|
||||
|
||||
// executeRun fans out one goroutine per server run and waits for all to finish.
|
||||
func executeRun(runID string) {
|
||||
run, err := GetRun(runID)
|
||||
|
||||
@@ -60,8 +60,9 @@ func CreateStep(s models.WorkflowStep) (*models.WorkflowStep, error) {
|
||||
s.StepID = uuid.New().String()
|
||||
s.CreatedAt = time.Now()
|
||||
s.UpdatedAt = s.CreatedAt
|
||||
if s.DeclaredOutputs == nil {
|
||||
s.DeclaredOutputs = []string{}
|
||||
s.DeclaredOutputs = DeriveOutputs(s.Script)
|
||||
if s.Source == "" {
|
||||
s.Source = "user"
|
||||
}
|
||||
if s.SecretRefs == nil {
|
||||
s.SecretRefs = []string{}
|
||||
@@ -83,7 +84,7 @@ func UpdateStep(stepID string, s models.WorkflowStep) error {
|
||||
"description": s.Description,
|
||||
"interpreter": s.Interpreter,
|
||||
"script": s.Script,
|
||||
"declared_outputs": s.DeclaredOutputs,
|
||||
"declared_outputs": DeriveOutputs(s.Script),
|
||||
"declared_inputs": s.DeclaredInputs,
|
||||
"secret_refs": s.SecretRefs,
|
||||
"updated_at": time.Now(),
|
||||
@@ -178,6 +179,10 @@ func CreateWorkflow(w models.Workflow) (*models.Workflow, error) {
|
||||
if w.Steps == nil {
|
||||
w.Steps = []models.WorkflowStepRef{}
|
||||
}
|
||||
if err := ValidateWorkflow(w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
normalizeInlineSteps(&w)
|
||||
if _, err := db.Col("workflows").InsertOne(ctx, w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -187,6 +192,10 @@ func CreateWorkflow(w models.Workflow) (*models.Workflow, error) {
|
||||
func UpdateWorkflow(id string, w models.Workflow) error {
|
||||
ctx, cancel := wfCtx()
|
||||
defer cancel()
|
||||
if err := ValidateWorkflow(w); err != nil {
|
||||
return err
|
||||
}
|
||||
normalizeInlineSteps(&w)
|
||||
_, err := db.Col("workflows").UpdateOne(ctx, bson.M{"workflow_id": id}, bson.M{"$set": bson.M{
|
||||
"name": w.Name,
|
||||
"target_server_ids": w.TargetServerIDs,
|
||||
@@ -196,6 +205,29 @@ func UpdateWorkflow(id string, w models.Workflow) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// normalizeInlineSteps derives outputs for inline steps and strips fields that
|
||||
// only belong to library steps.
|
||||
func normalizeInlineSteps(w *models.Workflow) {
|
||||
for i := range w.Steps {
|
||||
in := w.Steps[i].Inline
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
in.DeclaredOutputs = DeriveOutputs(in.Script)
|
||||
in.StepID = ""
|
||||
in.Slug = ""
|
||||
in.Source = ""
|
||||
in.CreatedAt = time.Time{}
|
||||
in.UpdatedAt = time.Time{}
|
||||
if in.SecretRefs == nil {
|
||||
in.SecretRefs = []string{}
|
||||
}
|
||||
if in.DeclaredInputs == nil {
|
||||
in.DeclaredInputs = []models.InputParam{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteWorkflow(id string) error {
|
||||
ctx, cancel := wfCtx()
|
||||
defer cancel()
|
||||
|
||||
Reference in New Issue
Block a user