From b6ac5133c31f70960bfb7fe12beb3abd4148e5a8 Mon Sep 17 00:00:00 2001 From: Forbes Date: Sat, 14 Feb 2026 13:20:15 -0600 Subject: [PATCH] feat: add auto-trigger hooks for revision and BOM changes --- internal/api/bom_handlers.go | 4 +++ internal/api/handlers.go | 3 ++ internal/api/job_handlers.go | 55 ++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/internal/api/bom_handlers.go b/internal/api/bom_handlers.go index dbd1a81..f93d52e 100644 --- a/internal/api/bom_handlers.go +++ b/internal/api/bom_handlers.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/csv" "encoding/json" "fmt" @@ -1219,6 +1220,9 @@ func (s *Server) HandleMergeBOM(w http.ResponseWriter, r *http.Request) { "unreferenced": len(diff.Removed), })) + // Trigger auto-jobs (e.g. assembly validation) + go s.triggerJobs(context.Background(), "bom_changed", parent.ID, parent) + writeJSON(w, http.StatusOK, resp) } diff --git a/internal/api/handlers.go b/internal/api/handlers.go index c1fd419..3b3493f 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -1489,6 +1489,9 @@ func (s *Server) HandleCreateRevision(w http.ResponseWriter, r *http.Request) { "part_number": partNumber, "revision_number": rev.RevisionNumber, })) + + // Trigger auto-jobs (e.g. validation, export) + go s.triggerJobs(context.Background(), "revision_created", item.ID, item) } // HandleUploadFile uploads a file and creates a new revision. diff --git a/internal/api/job_handlers.go b/internal/api/job_handlers.go index e48a41d..ebeb01e 100644 --- a/internal/api/job_handlers.go +++ b/internal/api/job_handlers.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/json" "net/http" "strconv" @@ -321,3 +322,57 @@ func (s *Server) HandleDeleteRunner(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } + +// triggerJobs creates jobs for all enabled definitions matching the trigger type. +// It applies trigger filters (e.g. item_type) before creating each job. +func (s *Server) triggerJobs(ctx context.Context, triggerType string, itemID string, item *db.Item) { + defs, err := s.jobs.GetDefinitionsByTrigger(ctx, triggerType) + if err != nil { + s.logger.Error().Err(err).Str("trigger", triggerType).Msg("failed to get job definitions for trigger") + return + } + + for _, def := range defs { + // Apply trigger filter (e.g. item_type == "assembly") + if def.Definition != nil { + if triggerCfg, ok := def.Definition["trigger"].(map[string]any); ok { + if filterCfg, ok := triggerCfg["filter"].(map[string]any); ok { + if reqType, ok := filterCfg["item_type"].(string); ok && item != nil { + if item.ItemType != reqType { + continue + } + } + } + } + } + + job := &db.Job{ + JobDefinitionID: &def.ID, + DefinitionName: def.Name, + Priority: def.Priority, + ItemID: &itemID, + RunnerTags: def.RunnerTags, + TimeoutSeconds: def.TimeoutSeconds, + MaxRetries: def.MaxRetries, + } + + if err := s.jobs.CreateJob(ctx, job); err != nil { + s.logger.Error().Err(err).Str("definition", def.Name).Msg("failed to create triggered job") + continue + } + + s.broker.Publish("job.created", mustMarshal(map[string]any{ + "job_id": job.ID, + "definition_name": def.Name, + "trigger": triggerType, + "item_id": itemID, + })) + + s.logger.Info(). + Str("job_id", job.ID). + Str("definition", def.Name). + Str("trigger", triggerType). + Str("item_id", itemID). + Msg("triggered job") + } +}