3.2 KiB
3.2 KiB
8.3 — Request/response schema validation, KV+type DSL (GREEN)
Phase: 8 — ServiceAdapter CRD rollout
Stage: RED
Depends on: 8.1 (CRD types carry requestSchema/responseSchema), 8.2 (dispatcher calls this per request)
Design contract: API_ROUTING_HYBRID_DESIGN.md §1 "Request/response validation" subsection — the full DSL including the array/nullable extension. Read that subsection in full before implementing; do not reinvent the shape from the checklist below alone.
internal/serviceadapter/validate.go(new, no external dependency — not a JSON Schema library): validates a parsed body (map[string]interface{}or[]interface{}for array-typed schemas) against aFieldSchema- Object schema:
requiredfields present, each present field infieldsmatches its declared Go runtime type (string→string,number→float64,boolean→bool,array→[]interface{},object→map[string]interface{}) nullable: trueon a field accepts JSONnullregardless of declared type; anullon a non-nullable field is atype_mismatch; barefield: stringshorthand means{type: string, nullable: false}strict: truerejects body keys not listed infields; defaultfalseis permissive- Array schema (
type: array):items: stringvalidates every element is that scalar type;items: { fields: {...} }validates every element as an object schema, one level deep inside each item — no further nesting - Compiled once per CR add/update in 8.1's informer callback, not per-request —
the parsed
FieldSchemastored in the same registry entry as the route - Request-side violation → 400, RFC 9457
problem+json,{type, title, detail, errors: [{field, reason}]},reasonone ofmissing,type_mismatch: want X got Y,unknown_field - Response-side violation does not block the response — forwarded unchanged,
emits
serviceadapter_response_schema_mismatch{service,resource}metric + log line - GET requests with no body skip request-schema validation entirely (query-param validation is out of scope for this task — schemas in this repo's adapters only apply to POST/PATCH bodies per the CRs in §1/§3/§6)
Verify
# missing required field
curl -s -X POST https://api.riotpiao.com/ \
-H 'Authorization: Bearer <jwt>' -H 'X-Service: postgres' -H 'X-Resource: query' \
-d '{"params": []}'
# expected: 400, errors: [{"field":"sql","reason":"missing"}]
# type mismatch
curl -s -X POST https://api.riotpiao.com/ \
-H 'Authorization: Bearer <jwt>' -H 'X-Service: postgres' -H 'X-Resource: query' \
-d '{"sql": 42}'
# expected: 400, errors: [{"field":"sql","reason":"type_mismatch: want string got number"}]
# nullable field accepted
curl -s -o /dev/null -w '%{http_code}\n' https://api.riotpiao.com/ \
-H 'Authorization: Bearer <jwt>' -H 'X-Service: memory' -H 'X-Resource: skill'
# expected: 200 even though generated_from is null in the response body — response
# validation must not reject/replace the body
# unit test, not curl:
go test ./internal/serviceadapter/... -run TestValidate -v
# expected: covers array-of-scalar, array-of-object, strict rejection, nullable