feat(server): step usage counts endpoint
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user