Files
iotlcm/worker-svr/internal/fullsync/httpapi/README.md

5.0 KiB

Full-sync HTTP control API

The handler imports no queue or storage implementations. Inject the five contract interfaces in Dependencies: RunLister, RunReader, FailurePager, ReplayRequester, and ActionReader. The replay service owns durable idempotency and execution; the HTTP layer does not cache requests or launch work.

Use NewRunReader(live, archive, rounds) to compose LiveRunReader, ArchivedRunReader, and RoundStatsReader into a RunReader. Live metadata wins and returns source: "live"; only ErrNotFound from live metadata permits archive fallback, returning source: "archive". Live storage errors and round lookup errors propagate. Archive and round readers may be nil to disable those lookups. A failure listing always calls FailurePager directly, never archives.

Routes and responses

All routes require Authorization: Bearer <configured token>. Missing, wrong, or ambiguous credentials return 401 with a WWW-Authenticate challenge. An empty configured token is a startup configuration error. Responses use JSON and Cache-Control: no-store.

Method Route Success body
GET /internal/v1/full-sync-runs?syncKind=&limit= 200 contract.RunList
GET /internal/v1/full-sync-runs/{runId} 200 contract.RunView
GET /internal/v1/full-sync-runs/{runId}/failures?cursor=&limit= 200 contract.FailurePage
POST /internal/v1/full-sync-runs/{runId}/replay 202 contract.ReplayAccepted
GET /internal/v1/full-sync-actions/{actionId} 200 contract.ReplayAction

Run list results merge active Redis metadata with flat run-<runId>.json archives under the configured archive root. Active Redis metadata wins when an ID exists in both sources. syncKind accepts traffic_usage or sim_status; limit defaults to 50 and is capped at 100.

Failure limits use the contract rules: absent, empty, or zero selects 100; positive values up to 500 are accepted; other values are rejected. Repeated cursor/limit parameters and malformed query encoding are rejected. Cursors are opaque and passed unchanged after URL decoding. The pager validates cursor contents and run binding. Empty pages serialize items: [] and preserve nextCursor; only an empty next cursor signals completion.

Replay requires a single Idempotency-Key header and a JSON body such as {"requestedBy":"operator"}. The body is limited to 64 KiB, must contain one JSON object with no unknown fields, and must satisfy ReplayRequest.Validate. If supplied, Content-Type must be application/json (parameters are allowed). The exact requestedBy value is preserved for fingerprinting. Repeated accepted requests return the service's existing action/target and current status with 202. The adapter constructs statusUrl and the Location header from actionId.

All application errors use this envelope, without storage error details:

{"error":{"code":"invalid_argument","message":"Invalid request"}}
Condition HTTP status Error code
Invalid path, query, header, body, or ErrInvalidArgument 400 invalid_argument
Missing/wrong bearer credentials 401 unauthorized
Unknown route/resource or ErrNotFound 404 not_found
Unsupported route method 405 method_not_allowed
ErrConflict (including changed idempotent request), ErrLeaseLost 409 conflict
ErrFailureDetailsUnavailable 410 failure_details_unavailable
Oversized replay body 413 body_too_large
ErrBusy 503 busy
Other service errors or unencodable service output 500 internal_error

Lifecycle

NewHandler(token, deps) can be mounted by an existing HTTP server. For an independent listener, construct NewServer(Config{Addr: address, BearerToken: token}, deps) and call Start(workerCtx). Start binds synchronously and returns address-in-use and other startup errors directly. Addr() exposes the bound address, including an OS-assigned port when configured with port zero.

Call Wait() after Start, normally in the worker's supervision goroutine. It returns serving or shutdown failures; it also returns startup failures if Start failed. The server is single-use and rejects a second Start. Wait can have multiple callers but must not be called before Start.

Canceling workerCtx closes the listener and drains active HTTP requests. Active request contexts stay usable during the drain. ShutdownTimeout defaults to ten seconds; if it expires, active connections are closed and Wait returns an error wrapping context.DeadlineExceeded. Normal cancellation with a successful drain returns nil. No worker wiring or configuration-file changes are part of this package.

Verification

gofmt -l internal/fullsync/httpapi
go test ./internal/fullsync/...
go test ./internal/fullsync/httpapi -cover
go vet ./internal/fullsync/...

Tests use contract mocks and local HTTP listeners, requiring no Redis or archive files. go test -race ./internal/fullsync/... additionally requires a supported C compiler and CGO enabled.