feat(transcode): complete google.api.http request/response mapping#23
Conversation
The transcoder previously treated the whole request body as the message and overlaid path params, ignoring several core google.api.http rules. Close those gaps for drop-in parity with grpc-gateway / Envoy: - Query-parameter binding: fields not bound by path or body are filled from the query string, coerced to each field's proto type (scalars, repeated → array, dotted nested paths). Unknown keys are dropped. - `body` mapping: honor `*` (whole body → message root), a named field (body → that subfield), or empty (no body; fields from path + query). - `response_body`: return just that response subfield as the HTTP body. - `additional_bindings`: register every extra (method, path, body) binding for the same RPC. Request construction is factored into a pure, unit-tested `request` module (body mapping + path + query overlay with type coercion + response_body extraction). Precedence: path > body > query. Closes #22
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughSummary by CodeRabbit
WalkthroughAdds complete ChangesComplete google.api.http mapping
Sequence Diagram(s)sequenceDiagram
participant Client as HTTP Client
participant AxumRouter as Axum Router
participant transcode_handler
participant build_request_json
participant gRPCChannel as gRPC Channel
participant extract_response_body
Client->>AxumRouter: HTTP request (path params, query string, body)
AxumRouter->>transcode_handler: path_params, raw_query, body_bytes, RouteEntry(body_mapping, response_body)
transcode_handler->>transcode_handler: parse body_bytes conditionally on body_mapping
transcode_handler->>build_request_json: body_json, path_params, query_pairs, MessageDescriptor
build_request_json-->>transcode_handler: merged request JSON (body → path → query precedence)
transcode_handler->>gRPCChannel: DynamicMessage from request JSON
gRPCChannel-->>transcode_handler: gRPC response message
transcode_handler->>extract_response_body: serialized response JSON, response_body dotted path
extract_response_body-->>transcode_handler: subfield Value (or full JSON if no response_body)
transcode_handler-->>Client: HTTP response body
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
| Filename | Overview |
|---|---|
| src/transcode/request.rs | New module implementing build_request_json, extract_response_body, BodyMapping, and helpers; well-structured with 11 unit tests covering the main code paths |
| src/transcode/mod.rs | Wires in query-param binding, body/response_body mapping, and additional_bindings; extract_http_rule replaced with collect_bindings/parse_http_rule |
| README.md | Feature list updated to reflect completed transcoding; transcoding-completeness roadmap item removed |
Reviews (2): Last reviewed commit: "fix(transcode): tighten query coercion a..." | Re-trigger Greptile
coerce() routes Uint32/Fixed32 through i64, so "-1" and values above u32::MAX coerce to a JSON number instead of falling back to a string. This test pins the expected behavior (out-of-range unsigned → string); the fix follows. Part of #22
- coerce: route unsigned 32-bit fields (Uint32/Fixed32) through u32 and signed (Int32/Sint32/Sfixed32) through i32, so out-of-range values fall back to a string that prost-reflect rejects precisely instead of passing a wrong JSON number. - query parsing: replace the silent unwrap_or_default() with parse_query returning a Result; a malformed query now returns 400 rather than dropping every query-bound field. - response_body: extract_response_body returns Option; a path that does not exist in the response logs a warning (misconfiguration) before falling back to null, instead of silently returning null with 200. Part of #22
Summary
Closes the core transcoding gaps so the proxy is a drop-in for grpc-gateway / Envoy on the request/response mapping surface. Previously the handler treated the whole body as the message and only overlaid path params.
What's added
?tag=a&tag=b→ array)?nested.city=x)bodymapping — honors*(whole body → message root), a named field (body → that subfield), or empty (no body; fields from path + query, e.g. GET/DELETE).response_body— returns just that response subfield as the HTTP body.additional_bindings— registers every extra (method, path, body) binding for the same RPC.Precedence: path > body > query (query fills only fields not already set).
Design
Request construction is factored into a pure
transcode::requestmodule (build_request_json+coerce+extract_response_body), independent of axum/tonic, so it is fully unit-testable. The binding parser is split intocollect_bindingsfor the same reason.Testing
*/field/none), query coercion per kind, repeated + dotted query, precedence, int64-as-string, unknown-key drop, response_body extraction, andadditional_bindingsparsing.prost_types(no protoc needed).cargo nextest run: 48 passed.cargo clippy --all-targets+cargo fmt --check: clean.Docs
README Features updated (path + query + body, response_body, additional_bindings); the "Transcoding completeness" roadmap item is removed (now done).
Closes #22