feat(server): step usage counts endpoint

This commit is contained in:
2026-07-21 11:31:05 +01:00
parent d0442291f5
commit 93423e32e6
3 changed files with 97 additions and 0 deletions
+10
View File
@@ -23,6 +23,7 @@ func registerWorkflowRoutes(g *gin.RouterGroup) {
g.GET("/steps/:id/export", exportStep)
g.POST("/steps/import", importStep)
g.POST("/steps/seed-defaults", seedDefaults)
g.GET("/steps/usage", stepUsage)
g.POST("/steps/parse", parseStep)
g.GET("/workflows", listWorkflows)
@@ -154,6 +155,15 @@ func listSteps(c *gin.Context) {
c.JSON(http.StatusOK, steps)
}
func stepUsage(c *gin.Context) {
counts, err := services.StepUsageCounts()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, counts)
}
func createStep(c *gin.Context) {
var s models.WorkflowStep
if err := c.ShouldBindJSON(&s); err != nil {
+28
View File
@@ -61,6 +61,34 @@ func ListSteps() ([]models.WorkflowStep, error) {
return steps, nil
}
// StepUsageCounts returns, per library step_id, the number of distinct
// workflows that reference it. Inline steps have no step_id and are ignored.
func StepUsageCounts() (map[string]int, error) {
ctx, cancel := wfCtx()
defer cancel()
cur, err := db.Col("workflows").Find(ctx, bson.M{})
if err != nil {
return nil, err
}
defer cur.Close(ctx)
var wfs []models.Workflow
if err := cur.All(ctx, &wfs); err != nil {
return nil, err
}
counts := map[string]int{}
for _, w := range wfs {
seen := map[string]bool{}
for _, ref := range w.Steps {
if ref.StepID == "" || seen[ref.StepID] {
continue
}
seen[ref.StepID] = true
counts[ref.StepID]++
}
}
return counts, nil
}
func CreateStep(s models.WorkflowStep) (*models.WorkflowStep, error) {
ctx, cancel := wfCtx()
defer cancel()
@@ -0,0 +1,59 @@
package services
import (
"os"
"testing"
"github.com/mrhid6/vantage/server/internal/db"
"github.com/mrhid6/vantage/server/internal/models"
)
func TestMain(m *testing.M) {
uri := os.Getenv("VANTAGE_TEST_MONGO_URI")
if uri == "" {
uri = "mongodb://localhost:27117"
}
if err := db.Connect(uri, "vantage_test"); err != nil {
// No MongoDB available in this environment; skip DB-backed tests.
os.Exit(0)
}
code := m.Run()
os.Exit(code)
}
func mkUsageStep(name string) models.WorkflowStep {
return models.WorkflowStep{Name: name, Interpreter: "bash", Script: "echo hi"}
}
func mkWorkflowWithStep(name, stepID string) models.Workflow {
return models.Workflow{Name: name, Steps: []models.WorkflowStepRef{{StepID: stepID, Order: 0, OnFailure: "stop"}}}
}
func TestStepUsageCounts(t *testing.T) {
// A step used by two workflows, a step used by none.
used, err := CreateStep(mkUsageStep("used-step"))
if err != nil {
t.Fatal(err)
}
unused, err := CreateStep(mkUsageStep("unused-step"))
if err != nil {
t.Fatal(err)
}
if _, err := CreateWorkflow(mkWorkflowWithStep("wf-a", used.StepID)); err != nil {
t.Fatal(err)
}
if _, err := CreateWorkflow(mkWorkflowWithStep("wf-b", used.StepID)); err != nil {
t.Fatal(err)
}
counts, err := StepUsageCounts()
if err != nil {
t.Fatal(err)
}
if counts[used.StepID] != 2 {
t.Fatalf("used step: want 2, got %d", counts[used.StepID])
}
if counts[unused.StepID] != 0 {
t.Fatalf("unused step: want 0, got %d", counts[unused.StepID])
}
}