homelabby/internal/inventory/catalog_updater.go
Mikkel Georgsen 4fc9362519 feat(02-03): POST /api/intake handler with orchestrator and NetBox wiring
- IntakeHandler with IntakeOrchestrator/IntakeNetBoxClient/IntakeCatalogUpdater/IntakeWAQ interfaces
- Validates 1-3 photos, base64-encodes, calls Analyze, allocates HW-ID
- Quick-add mode: confidence >= threshold skips review, creates NetBox record immediately
- WAQ enqueue on NetBox failure returns 202 with queued=true
- nil WAQ + NetBox down returns 503
- Six unit tests: reject-0, reject-4, high-confidence, low-confidence, quick-add, netbox-down
- [Rule 1 - Bug] PatchCustomFields signature changed int -> int64 to match NetBoxOpsClient interface
- [Rule 1 - Bug] UpdateCatalogStatus signature changed int -> int64 for consistency with CreateDevice return type
2026-04-10 05:54:33 +00:00

38 lines
1.2 KiB
Go

package inventory
import (
"context"
"fmt"
"git.georgsen.dk/hwlab/internal/netbox"
)
// CatalogUpdater persists quality gate transitions to NetBox.
type CatalogUpdater struct {
client *netbox.Client
}
// NewCatalogUpdater creates a CatalogUpdater backed by the given NetBox client.
func NewCatalogUpdater(client *netbox.Client) *CatalogUpdater {
return &CatalogUpdater{client: client}
}
// UpdateCatalogStatus validates the transition from current to next status
// and persists the result to NetBox via PatchCustomFields.
// Returns the new status on success.
//
// All catalog_status writes MUST go through this method to ensure T-04-01 mitigation:
// the quality gate transition is always validated before any NetBox PATCH.
func (u *CatalogUpdater) UpdateCatalogStatus(ctx context.Context, deviceID int64, current, next CatalogStatus) (CatalogStatus, error) {
newStatus, err := Transition(current, next)
if err != nil {
return "", err
}
patch := map[string]interface{}{
"catalog_status": string(newStatus),
}
if err := u.client.PatchCustomFields(ctx, deviceID, patch); err != nil {
return "", fmt.Errorf("persist catalog_status to NetBox: %w", err)
}
return newStatus, nil
}