feat(gateway,adapters): /publish endpoint (ADR-068) + to_openapi 6-endpoint projection
gateway-publish:
- GatewayDispatch::invoke_sink (internal:false, forwarded_for:None)
- POST /publish: NDJSON body, first line {operation, chunk} (OQ-02
resolved: first-line convention; terminal errors = plain HTTP status
+ JSON body, not NDJSON lines); 404 internal/unknown, 401/403 ACL,
400 INVALID_OPERATION_TYPE for non-Pub
- ADR-068 + open-questions.md updated with the OQ-02 resolution
adapter-to-openapi:
- src/adapters/openapi_spec.rs: OpenAPISpec model (JSON/YAML/from_str
JSON-first per ADR-051, $ref resolution) shared by from/to_openapi
- src/adapters/to_openapi.rs: 6-endpoint projection, info.version
1.0.0 -> 1.1.0 (minor: /publish addition per ADR-045), /publish
NDJSON doc with 400 oneOf (INVALID_INPUT + INVALID_OPERATION_TYPE),
ADR-023 error fidelity (protocol statuses, HTTP_<status> passthrough,
internal-op exclusion)
- GET /openapi.json wired into HttpAdapter's router (bearer-auth layer)
Verified: cargo test (136 lib), test --all-features (136+10 WS),
clippy -D warnings (both), fmt. Doc validates against openapiv3.
This commit is contained in:
@@ -86,6 +86,36 @@ impl GatewayDispatch {
|
||||
.invoke_streaming(&operation_name, input, context)
|
||||
}
|
||||
|
||||
/// Dispatch a `Pub` operation (ADR-046, ADR-068): the
|
||||
/// `publish_stream` is the initiator's chunk stream (one
|
||||
/// `Ok(Value)` per published chunk); the handler's final
|
||||
/// `ResponseEnvelope` is the result. Pre-handler failures
|
||||
/// (not-found, forbidden, non-Pub op) surface as a single error
|
||||
/// envelope — the same envelope the `/publish` route maps to HTTP.
|
||||
pub async fn invoke_sink(
|
||||
&self,
|
||||
identity: Option<Identity>,
|
||||
op: &str,
|
||||
input: Value,
|
||||
publish_stream: alkcall::registry::registration::PublishStream,
|
||||
) -> ResponseEnvelope {
|
||||
let operation_name = strip_leading_slash(op).to_string();
|
||||
let request_id = uuid::Uuid::new_v4().to_string();
|
||||
let context = self.build_root_context_sink(&request_id, &operation_name, identity);
|
||||
self.registry
|
||||
.invoke_sink(&operation_name, input, publish_stream, context)
|
||||
.await
|
||||
}
|
||||
|
||||
fn build_root_context_sink(
|
||||
&self,
|
||||
request_id: &str,
|
||||
operation_name: &str,
|
||||
identity: Option<Identity>,
|
||||
) -> OperationContext {
|
||||
self.build_root_context_inner(request_id, operation_name, identity, false)
|
||||
}
|
||||
|
||||
fn build_root_context(
|
||||
&self,
|
||||
request_id: &str,
|
||||
|
||||
Reference in New Issue
Block a user