fix(adapters): to_openapi runtime-truthful projection (PRJ-01..05, PRJ-11..15)
- /search, /schema: document the envelope wrapper and the real item/spec fields (PRJ-01/02); /search drops unreachable 401/403, documents 404 (PRJ-15) - error statuses: 422 for dispatch-path INVALID_INPUT / INVALID_OPERATION_TYPE; extractor 400s documented as the plain-text gap they are (PRJ-03); operation-declared errors projected by http_status with x-runtime-behavior: 500 on non-HTTP_* codes (PRJ-04 project-honest decision) — no runtime changes - /subscribe: 200+SSE only; event:error terminal contract documented (PRJ-05, GW-12) - components for requests/responses; CallRequest no longer inlined per-path (PRJ-14); all library expect() paths removed (PRJ-11) - error projections folded into BTreeMaps: same registry => byte-identical doc, sorted enums (PRJ-12) - components.securitySchemes.bearerAuth + top-level security (PRJ-15) - info.version 1.1.0 -> 1.2.0 (ADR-045 minor: additive documentation of the settled runtime contract) - 31 unit tests incl. golden print-level assertions mirroring the routes tests' actual bodies and a determinism test Verification: cargo test (288), clippy --all-targets -D warnings, fmt --check, cargo doc --no-deps, cargo test --all-features (all green in a clean worktree at HEAD; shared tree carries parallel agents' edits).
This commit is contained in:
@@ -395,7 +395,7 @@ surface problem).
|
||||
|
||||
| OpenAPI path | Call protocol | HTTP method | Purpose |
|
||||
|--------------|--------------|-------------|---------|
|
||||
| `/search` | `services/list` | `GET` | List/search operations (AccessControl-filtered). Names + descriptions. |
|
||||
| `/search` | `services/list` | `GET` | List the caller's callable operations (AccessControl-filtered). Items: `name`, `namespace`, `op_type`. |
|
||||
| `/schema` | `services/schema` | `GET` | Get an operation's full `OperationSpec`. |
|
||||
| `/call` | `call.requested` (Query/Mutation) | `POST` | Invoke an operation. Flat JSON body `{ operation, input }`. |
|
||||
| `/batch` | multiple `call.requested` | `POST` | Invoke multiple operations. Array of `{ operation, input }`. |
|
||||
@@ -473,27 +473,31 @@ number:
|
||||
```
|
||||
|
||||
`to_openapi` projects `error_schemas` to the gateway endpoint's
|
||||
response definitions. The `/call` endpoint's responses include the
|
||||
operation-level errors (mapped by `http_status`), plus the protocol-
|
||||
level errors:
|
||||
response definitions. The `/call` endpoint's responses are the shared
|
||||
components-referenced status map: the protocol-level errors, plus any
|
||||
operation-level errors (keyed by the registry's declared `http_status`;
|
||||
entries whose codes lack the `HTTP_<status>` prefix carry
|
||||
`x-runtime-behavior: 500` — the runtime mapper is purely code-driven,
|
||||
see review-001 PRJ-04):
|
||||
|
||||
```yaml
|
||||
# /call endpoint responses
|
||||
responses:
|
||||
'200': { schema: <output_schema for the called operation> }
|
||||
'400': { schema: <INVALID_INPUT error> }
|
||||
'401': { schema: <no bearer token> }
|
||||
'403': { schema: <FORBIDDEN — insufficient scopes> }
|
||||
'404': { schema: <NOT_FOUND — operation not registered or Internal> }
|
||||
'422': { schema: <operation-level error with http_status=422> }
|
||||
'429': { schema: <operation-level error with http_status=429> }
|
||||
'500': { schema: <INTERNAL> }
|
||||
'504': { schema: <TIMEOUT> }
|
||||
'200': { $ref: '#/components/schemas/CallOk' } # envelope { request_id, result, output }
|
||||
'400': { description: plain-text extractor rejection (framework body) }
|
||||
'401': { $ref: '#/components/responses/Unauthorized' }
|
||||
'403': { $ref: '#/components/responses/Forbidden' }
|
||||
'404': { $ref: '#/components/responses/NotFound' }
|
||||
'422': { oneOf: [CallErrorInvalidInput, CallErrorInvalidOperationType] }
|
||||
'429': { ... } # only when ops declare errors at 429; x-runtime-behavior: 500 for non-HTTP_* codes
|
||||
'500': { oneOf: [CallErrorInternal, CallFailure] } # INTERNAL + non-HTTP_* operation codes
|
||||
'503': { ... } # only when ops declare errors at 503
|
||||
'504': { $ref: '#/components/responses/Timeout' }
|
||||
```
|
||||
|
||||
The operation-level errors (with `http_status`) are surfaced on the
|
||||
`/call` endpoint's response — the gateway propagates the called
|
||||
operation's `error_schemas` as response definitions. This makes the
|
||||
The operation-declared errors are surfaced on the `/call` and
|
||||
`/publish` endpoints' responses — the gateway projects the registered
|
||||
operations' `error_schemas` as response definitions. This makes the
|
||||
adapter contract from alkcall ADR-022 faithful on the error axis — no
|
||||
silent dropping of error contracts. See alkcall ADR-016.
|
||||
|
||||
|
||||
+1144
-686
File diff suppressed because it is too large
Load Diff
@@ -764,7 +764,7 @@ mod tests {
|
||||
assert!(text.contains("application/json"), "got: {text}");
|
||||
// The 6-endpoint gateway doc with the version from the /publish addition.
|
||||
assert!(text.contains("\"/publish\""), "publish path in doc");
|
||||
assert!(text.contains("1.1.0"), "info.version 1.1.0 in doc");
|
||||
assert!(text.contains("1.2.0"), "info.version 1.2.0 in doc");
|
||||
assert!(text.contains("gatewayPublish"), "publish operationId");
|
||||
|
||||
let _ = server_task.await;
|
||||
|
||||
@@ -265,7 +265,7 @@ async fn full_surface_gateway_over_http() {
|
||||
.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
let body: serde_json::Value = resp.json().await.unwrap();
|
||||
assert_eq!(body["info"]["version"], "1.1.0");
|
||||
assert_eq!(body["info"]["version"], "1.2.0");
|
||||
assert!(body["paths"].get("/publish").is_some());
|
||||
assert!(body["paths"].get("/call").is_some());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user