- NewNetBoxOpHandler routes create_device → CreateDevice, patch_custom_fields → PatchCustomFields - NetBoxOpsClient interface enables test injection without importing netbox package - Unknown op types return error (re-queued by worker, not silently dropped — T-02-08) - JSON payloads decoded into typed structs (T-02-07 tampering mitigation) - 6 handler tests all passing (TDD green); NoOpHandler untouched in worker.go
66 lines
2.3 KiB
Go
66 lines
2.3 KiB
Go
package queue
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
)
|
|
|
|
// Op type string constants for WAQ operations.
|
|
const (
|
|
OpNetBoxCreateDevice = "netbox.create_device"
|
|
OpNetBoxPatchCustomFields = "netbox.patch_custom_fields"
|
|
)
|
|
|
|
// CreateDevicePayload is the JSON payload for OpNetBoxCreateDevice ops.
|
|
type CreateDevicePayload struct {
|
|
Name string `json:"name"`
|
|
AssetTag string `json:"asset_tag"`
|
|
DeviceTypeID int32 `json:"device_type_id"`
|
|
RoleID int32 `json:"role_id"`
|
|
SiteID int32 `json:"site_id"`
|
|
}
|
|
|
|
// PatchCustomFieldsPayload is the JSON payload for OpNetBoxPatchCustomFields ops.
|
|
type PatchCustomFieldsPayload struct {
|
|
DeviceID int64 `json:"device_id"`
|
|
Patch map[string]interface{} `json:"patch"`
|
|
}
|
|
|
|
// NetBoxOpsClient is the subset of netbox.Client that the WAQ handler needs.
|
|
// Using an interface here allows tests to inject a mock without importing netbox.
|
|
type NetBoxOpsClient interface {
|
|
CreateDevice(ctx context.Context, name, assetTag string, deviceTypeID, roleID, siteID int32) (int64, error)
|
|
PatchCustomFields(ctx context.Context, deviceID int64, patch map[string]interface{}) error
|
|
}
|
|
|
|
// NewNetBoxOpHandler returns an OpHandler that processes netbox WAQ operations.
|
|
// Pass a *netbox.Client as the client argument (it satisfies NetBoxOpsClient).
|
|
//
|
|
// Routing:
|
|
// - OpNetBoxCreateDevice → client.CreateDevice
|
|
// - OpNetBoxPatchCustomFields → client.PatchCustomFields
|
|
// - Unknown op type → error (op will be re-queued, not silently dropped)
|
|
func NewNetBoxOpHandler(client NetBoxOpsClient) OpHandler {
|
|
return func(ctx context.Context, op PendingOp) error {
|
|
switch op.Type {
|
|
case OpNetBoxCreateDevice:
|
|
var p CreateDevicePayload
|
|
if err := json.Unmarshal(op.Payload, &p); err != nil {
|
|
return fmt.Errorf("decode create_device payload: %w", err)
|
|
}
|
|
_, err := client.CreateDevice(ctx, p.Name, p.AssetTag, p.DeviceTypeID, p.RoleID, p.SiteID)
|
|
return err
|
|
|
|
case OpNetBoxPatchCustomFields:
|
|
var p PatchCustomFieldsPayload
|
|
if err := json.Unmarshal(op.Payload, &p); err != nil {
|
|
return fmt.Errorf("decode patch_custom_fields payload: %w", err)
|
|
}
|
|
return client.PatchCustomFields(ctx, p.DeviceID, p.Patch)
|
|
|
|
default:
|
|
return fmt.Errorf("unknown op type: %q", op.Type)
|
|
}
|
|
}
|
|
}
|