Add server-side solver service module with REST API endpoints, database
schema, job definitions, and runner result caching.
New files:
- migrations/021_solver_results.sql: solver_results table with upsert constraint
- internal/db/solver_results.go: SolverResultRepository (Upsert, GetByItem, GetByItemRevision)
- internal/api/solver_handlers.go: solver API handlers and maybeCacheSolverResult hook
- jobdefs/assembly-solve.yaml: manual solve job definition
- jobdefs/assembly-validate.yaml: auto-validate on revision creation
- jobdefs/assembly-kinematic.yaml: manual kinematic simulation job
Modified:
- internal/config/config.go: SolverConfig struct with max_context_size_mb, default_timeout
- internal/modules/modules.go, loader.go: register solver module (depends on jobs)
- internal/db/jobs.go: ListSolverJobs helper with definition_name prefix filter
- internal/api/handlers.go: wire SolverResultRepository into Server
- internal/api/routes.go: /api/solver/* routes + /api/items/{partNumber}/solver/results
- internal/api/runner_handlers.go: async result cache hook on job completion
API endpoints:
- POST /api/solver/jobs — submit solver job (editor)
- GET /api/solver/jobs — list solver jobs with filters
- GET /api/solver/jobs/{id} — get solver job status
- POST /api/solver/jobs/{id}/cancel — cancel solver job (editor)
- GET /api/solver/solvers — registry of available solvers
- GET /api/items/{pn}/solver/results — cached results for item
Also fixes pre-existing test compilation errors (missing workflows param
in NewServer calls across 6 test files).
86 lines
2.5 KiB
Go
86 lines
2.5 KiB
Go
package modules
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
"github.com/kindredsystems/silo/internal/config"
|
|
)
|
|
|
|
// LoadState applies module state from config YAML and database overrides.
|
|
//
|
|
// Precedence (highest wins):
|
|
// 1. Database module_state table
|
|
// 2. YAML modules.* toggles
|
|
// 3. Backward-compat YAML fields (auth.enabled, odoo.enabled)
|
|
// 4. Module defaults (set by NewRegistry)
|
|
func LoadState(r *Registry, cfg *config.Config, pool *pgxpool.Pool) error {
|
|
// Step 1: Apply backward-compat top-level YAML fields.
|
|
// auth.enabled and odoo.enabled existed before the modules section.
|
|
// Only apply if the new modules.* section doesn't override them.
|
|
if cfg.Modules.Auth == nil {
|
|
r.setEnabledUnchecked(Auth, cfg.Auth.Enabled)
|
|
}
|
|
if cfg.Modules.Odoo == nil {
|
|
r.setEnabledUnchecked(Odoo, cfg.Odoo.Enabled)
|
|
}
|
|
|
|
// Step 2: Apply explicit modules.* YAML toggles (override defaults + compat).
|
|
applyToggle(r, Auth, cfg.Modules.Auth)
|
|
applyToggle(r, Projects, cfg.Modules.Projects)
|
|
applyToggle(r, Audit, cfg.Modules.Audit)
|
|
applyToggle(r, Odoo, cfg.Modules.Odoo)
|
|
applyToggle(r, FreeCAD, cfg.Modules.FreeCAD)
|
|
applyToggle(r, Jobs, cfg.Modules.Jobs)
|
|
applyToggle(r, DAG, cfg.Modules.DAG)
|
|
applyToggle(r, Solver, cfg.Modules.Solver)
|
|
|
|
// Step 3: Apply database overrides (highest precedence).
|
|
if pool != nil {
|
|
if err := loadFromDB(r, pool); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Step 4: Validate the final state.
|
|
return r.ValidateDependencies()
|
|
}
|
|
|
|
// applyToggle sets a module's state from a YAML ModuleToggle if present.
|
|
func applyToggle(r *Registry, id string, toggle *config.ModuleToggle) {
|
|
if toggle == nil || toggle.Enabled == nil {
|
|
return
|
|
}
|
|
r.setEnabledUnchecked(id, *toggle.Enabled)
|
|
}
|
|
|
|
// setEnabledUnchecked sets module state without dependency validation.
|
|
// Used during loading when the full state is being assembled incrementally.
|
|
func (r *Registry) setEnabledUnchecked(id string, enabled bool) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if m, ok := r.modules[id]; ok && !m.Required {
|
|
m.enabled = enabled
|
|
}
|
|
}
|
|
|
|
// loadFromDB reads module_state rows and applies them to the registry.
|
|
func loadFromDB(r *Registry, pool *pgxpool.Pool) error {
|
|
rows, err := pool.Query(context.Background(),
|
|
`SELECT module_id, enabled FROM module_state`)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var id string
|
|
var enabled bool
|
|
if err := rows.Scan(&id, &enabled); err != nil {
|
|
return err
|
|
}
|
|
r.setEnabledUnchecked(id, enabled)
|
|
}
|
|
return rows.Err()
|
|
}
|