Skip to content

feat(transcode): complete google.api.http request/response mapping#23

Merged
polaz merged 3 commits into
mainfrom
feat/#22-http-mapping-completeness
Jun 19, 2026
Merged

feat(transcode): complete google.api.http request/response mapping#23
polaz merged 3 commits into
mainfrom
feat/#22-http-mapping-completeness

Conversation

@polaz

@polaz polaz commented Jun 19, 2026

Copy link
Copy Markdown
Member

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

  • Query-parameter binding — fields not bound by path or body are filled from the query string, coerced to each field's proto type:
    • scalars (bool / 32-bit ints → JSON number, 64-bit ints → string, floats → number)
    • repeated fields (?tag=a&tag=b → array)
    • dotted nested paths (?nested.city=x)
    • unknown keys dropped (lenient, like the gateways)
  • body mapping — 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::request module (build_request_json + coerce + extract_response_body), independent of axum/tonic, so it is fully unit-testable. The binding parser is split into collect_bindings for the same reason.

Testing

  • 11 new unit tests: body mapping (*/field/none), query coercion per kind, repeated + dotted query, precedence, int64-as-string, unknown-key drop, response_body extraction, and additional_bindings parsing.
  • Test descriptors are built programmatically via 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

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
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1eedb7c8-cd4b-41c7-a7e2-e14047b843b1

📥 Commits

Reviewing files that changed from the base of the PR and between 19bb811 and 0f0fdae.

📒 Files selected for processing (3)
  • README.md
  • src/transcode/mod.rs
  • src/transcode/request.rs

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Enhanced REST-to-gRPC transcoding with support for query parameters, multiple route mappings, and flexible response body extraction.
    • Added support for mapped response routes and additional HTTP bindings for complex API designs.
  • Documentation

    • Updated documentation to explain REST route generation, query parameter binding, and response mapping capabilities.
    • Added roadmap item for context propagation across REST↔gRPC boundaries.

Walkthrough

Adds complete google.api.http request/response mapping to the REST↔gRPC transcoder. A new request.rs module provides BodyMapping, build_request_json (body+path+query with proto-type coercion), and extract_response_body. mod.rs gains HttpBinding and multi-binding extraction, RouteEntry gains response_body, and transcode_handler integrates query-string parsing and conditional body handling. README is updated to reflect the completed feature set.

Changes

Complete google.api.http mapping

Layer / File(s) Summary
request.rs: BodyMapping, build_request_json, extract_response_body, helpers, and tests
src/transcode/request.rs
New module defining BodyMapping enum with parse, build_request_json assembling gRPC request JSON from body/path/query with precedence rules and proto-type coercion via coerce, extract_response_body walking dotted paths in response JSON, and group_query/set_field helpers. Unit tests cover all mapping variants, precedence, repeated/dotted fields, int64 coercion, unknown field dropping, and response extraction.
HttpBinding struct and multi-binding extraction helpers
src/transcode/mod.rs
Introduces HttpBinding and extract_http_bindings/collect_bindings/parse_http_rule to parse both the primary HTTP rule and additional_bindings from proto method options, replacing the prior single-rule extraction. Includes a test-only descriptor builder and a collect_bindings unit test.
RouteEntry extension, transcode_handler refactor, response extraction, and route registration
src/transcode/mod.rs
Extends RouteEntry with response_body. Updates main and alias handler closures to capture RawQuery. Refactors transcode_handler to conditionally parse the HTTP body, derive query pairs, build the gRPC request JSON via request::build_request_json, and deserialize into DynamicMessage. Adds extract_response_body call on unary response path. Refactors unary and streaming route registration to iterate extract_http_bindings and populate RouteEntry. Adds pub mod request and RawQuery import.
README feature list, roadmap, and How It Works updates
README.md
Expands Features bullets to describe completed query param, body/response_body, and additional_bindings support. Removes the transcoding completeness roadmap item, adds a context propagation item, and updates How It Works step 3 to mention query parameters.

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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(transcode): complete google.api.http request/response mapping' is clear, specific, and accurately describes the main change in implementing request/response mapping for google.api.http bindings across all modified files.
Description check ✅ Passed The PR description clearly relates to the changeset, explaining the four core features (query-parameter binding, body mapping, response_body, additional_bindings), design decisions, testing approach, and documentation updates matching the code changes.
Linked Issues check ✅ Passed The PR implements all four core requirements from issue #22: query-parameter binding with proto-type coercion, body mapping with * and field options, response_body extraction, and additional_bindings registration, validated by 11 new unit tests and passing test suite.
Out of Scope Changes check ✅ Passed All changes directly address the objectives in issue #22: README documentation updates reflect the new features, transcode/mod.rs refactors routing and binding parsing, and transcode/request.rs provides pure request/response helpers—no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#22-http-mapping-completeness

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown

Greptile Summary

This PR completes the google.api.http transcoding surface by adding query-parameter binding (with proto-type coercion, repeated/dotted-path support), body field mapping (* / named field / none), response_body subfield extraction, and additional_bindings route registration. Request construction is cleanly factored into the new transcode::request module, keeping it independent of axum and fully unit-testable.

  • src/transcode/request.rs (new): pure functions build_request_json, extract_response_body, coerce, set_field, group_query, and the BodyMapping enum, covered by 11 unit tests built from programmatic prost_types descriptors with no protoc dependency.
  • src/transcode/mod.rs: extract_http_rule replaced by collect_bindings/parse_http_rule to handle the primary rule plus additional_bindings; transcode_handler now reads RawQuery, parses query pairs, and delegates to build_request_json with the correct precedence (path > body > query).

Confidence Score: 5/5

The new transcoding logic is well-factored and the added unit tests cover the main paths; no new correctness issues were found in this review pass.

The request-construction module is a pure function with no side effects and is independently tested. Precedence rules (path > body > query) are correctly enforced, edge cases like empty bodies and repeated query keys are handled, and the additional_bindings parser correctly stops at one level of nesting as the proto spec requires.

No files require special attention.

Important Files Changed

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

Comment thread src/transcode/mod.rs Outdated
Comment thread src/transcode/request.rs Outdated
Comment thread src/transcode/mod.rs
polaz added 2 commits June 19, 2026 14:27
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(transcode): complete google.api.http mapping (query params, body, response_body, additional_bindings)

1 participant