Task 4 report — Adapter interface + SyncHTTPAdapter
Status: DONE. Commit 7465a56 — feat(p2): Adapter interface + sync-http OCR adapter (branch feat/gateway-p2-jobs-ocr, off d71284a).
What shipped
internal/jobs/adapter.go—Adapterinterface,AdapterError,SyncHTTPAdapter.internal/jobs/adapter_test.go— httptest-stub tests, namespace-safe (t4-*ids/tenants/env).internal/jobs/job.go— addedContentType stringfield (jsoncontent_type,omitempty, documented), recorded at submit; empty ⇒ defaultapplication/json.
Signatures (Task 5/6/7 must match)
type Adapter interface {
Run(ctx context.Context, job *Job, payload []byte, endpoint string,
report func(stage string, progress float64)) (result json.RawMessage, err error)
}
type AdapterError struct {
Retryable bool
Code string
Status int // upstream HTTP status; 0 for transport/timeout
RetryAfter string // upstream Retry-After header, verbatim, when present
}
func (e *AdapterError) Error() string
type SyncHTTPAdapter struct {
Upstream *config.Upstream
Client *http.Client // injectable; nil ⇒ http.DefaultClient
}
Behavior
- POST
payloadtoendpoint + SubmitPath(default/ocrwhen unset) with the job'sContentType. - Calls
report("processing", 0)at start (nil-safe). Adapter never touches the store. - 2xx → body as
json.RawMessage. 429/503 →AdapterError{Retryable:true, Status, RetryAfter}. Other 4xx/5xx →AdapterError{Retryable:false, Code:"UPSTREAM_<n>", Status}. ctx deadline/cancel →AdapterError{Retryable:false, Code:"ADAPTER_TIMEOUT"}promptly; transport error →UPSTREAM_UNREACHABLE. - Server-side key injection: only if
Upstream.APIKeyEnvis set + env present (mirrorsproxy.forward); never forwards a client key (builds its own request). - Timeout enforced via ctx, not
Client.Timeout; response body closed on every path — no goroutine leak.
Verification
go build ./...clean.go vet ./...clean.go test -race ./internal/jobs/...→ ok (1.42s), 12 adapter tests + existing store tests pass.
Concerns / notes for Task 7
reportis called synchronously; manager must keep thereport→UpdateStatusmutation cheap (store holds its mutex during the callback). Documented in the interface doc comment.- Default
SubmitPathis/ocr; example config (Task 9) should set explicit paths (e.g. PaddleOCR/ocr/predict-by-doc). AzureDIAdapter(Task 5) andClassifyAdapter(Task 6) are not built here, per scope.