- CatalogStatus type with forward-only state machine (draft→indexed→…→complete) - Transition() enforces valid transitions, returns error with 'invalid transition' message - ParseCatalogStatus() validates known status strings - HardwareRecord domain type composing netbox.Device with quality gate state - CatalogUpdater.UpdateCatalogStatus() validates transition then PatchCustomFields - SyncTags() normalizes tags (slug form) and ensures they exist in NetBox - normalizeTags deduplicates across case/whitespace/space-vs-hyphen variants - ensureTag uses go-netbox v4 NewTagRequest(name, slug) / ExtrasTagsCreate - All 12 state machine table-driven tests pass
38 lines
1.2 KiB
Go
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 int, 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
|
|
}
|