Repository navigation
feat(gax): add resolveResponsePayload helper for response.payload - #9745
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the resolveResponsePayload function to serialize response payloads as JSON (or return them as-is if they are already strings), preferring the raw response when available. Unit tests were also added to verify its behavior. The reviewer suggested improving the handling of Uint8Array or Buffer payloads by decoding them to a UTF-8 string first, preventing inefficient serialization and unreadable logs.
002dd72 to
98244b3
Compare
98244b3 to
7c3861d
Compare
5c23589 to
b22791a
Compare
quirogas
left a comment
There was a problem hiding this comment.
LGTM! Approved with 2 minor non-blocking nits so this PR is unblocked.
| // Encode binary data as base64. Buffer#toJSON runs before this replacer and | ||
| // turns a Buffer into {type: 'Buffer', data: [...]}, so in that case the | ||
| // original Buffer is read back from the holder. | ||
| const bytes = toBytes( | ||
| (val as {type?: unknown}).type === 'Buffer' | ||
| ? (this as Record<string, unknown>)[key] | ||
| : val, | ||
| ); | ||
| if (bytes) { | ||
| return encodeBase64(bytes); | ||
| } |
There was a problem hiding this comment.
Nit: Now that safeJsonStringify (lines 840-850) automatically base64-encodes Buffer, Uint8Array, and ArrayBuffer values in its replacer, isBuffer (line 778) and the 20-line cleanMap Buffer-encoding loop in resolveServerExceptionDetails in the same file are redundant; resolveServerExceptionDetails can pass map directly to safeJsonStringify(map).
| export function resolveResponsePayload( | ||
| response: unknown, | ||
| rawResponse?: unknown, | ||
| ): string | undefined { | ||
| const payload = rawResponse ?? response; | ||
| if (payload === undefined || payload === null) { | ||
| return undefined; | ||
| } | ||
| // TODO(security): The payload is returned in full because `response.payload` is defined | ||
| // as the raw response, so it can contain sensitive data (e.g. secret values or access | ||
| // tokens). Only emit it when logging is explicitly enabled (see `checkLoggingEnabled`), | ||
| // and consider redacting known sensitive fields. | ||
| if (typeof payload === 'string') { | ||
| return payload; | ||
| } | ||
| // Binary payloads are base64 encoded and, like strings, not wrapped in JSON quotes. | ||
| const bytes = toBytes(payload); | ||
| return bytes ? encodeBase64(bytes) : safeJsonStringify(payload); | ||
| } |
There was a problem hiding this comment.
Nit: Consider removing the unused rawResponse?: unknown parameter on resolveResponsePayload (response.payload is an L4 error-only attribute in the Milestone 2 Design Doc and never receives paginated/LRO rawResponse), avoiding allocating intermediate Uint8Array and Buffer views in toBytes / encodeBase64 when value is already a Buffer or Uint8Array, and checking Array.isArray((val as {data?: unknown}).data) alongside (val as {type?: unknown}).type === 'Buffer' in safeJsonStringify (line 844) before re-reading this[key].
b22791a to
36bbb9b
Compare
36bbb9b to
db3ba99
Compare
1f82ff2 to
790f978
Compare
14c124f to
1fad4ea
Compare
1a1433d to
ac7adf4
Compare
ac7adf4 to
0570a43
Compare
Add resolveResponsePayload to errorUtil so LoggerHelper can populate response.payload with the full response serialized as JSON (string payloads are returned as-is). It prefers the raw response gax provides for paginated and long-running calls over the processed response, and returns undefined when there is no response. Payloads are not redacted, so a TODO(security) notes they must only be emitted when logging is explicitly enabled.
Buffers and other binary data (Uint8Array, ArrayBuffer) were serialized one JSON entry per byte, e.g. {"type":"Buffer","data":[...]} or {"0":123,...}, bloating logs. Binary payloads are now base64 encoded, as are binary values nested in a response such as bytes fields. Base64 is how the protobuf JSON mapping represents bytes and matches what REST responses already emit. Uses Buffer when available and protobufjs's pure JavaScript encoder otherwise (e.g. in browsers).
…Stringify tests resolveResponsePayload passes non-string, non-binary payloads to safeJsonStringify, whose own tests cover circular references and BigInt values. Also drop assertions that repeat the JSON path ([]) or combine two existing cases ((null, null)).
0570a43 to
36e6a90
Compare
🤖 I have created a release *beep* *boop* --- <details><summary>google-gax: 6.15.0</summary> ## [6.15.0](google-gax-v6.14.0...google-gax-v6.15.0) (2026-11-02) ### Features * **gax:** Add error attributes for error logging ([#9743](#9743)) ([9ad830f](9ad830f)) * **gax:** Add error logging for client requests ([#9756](#9756)) ([b0f3dad](b0f3dad)) * **gax:** Add resolveExceptionStacktrace helper for exception.stacktrace ([#9744](#9744)) ([baed8c4](baed8c4)) * **gax:** Add resolveResponsePayload helper for response.payload ([#9745](#9745)) ([f4da96e](f4da96e)) * **gax:** Support cancelling API calls via CallOptions.signal ([#9462](#9462)) ([4e1f88f](4e1f88f)) </details> <details><summary>gaxios: 8.3.3</summary> ## [8.3.3](gaxios-v8.3.2...gaxios-v8.3.3) (2026-11-02) ### Bug Fixes * **gaxios:** Enforce maxContentLength on headerless responses in Bun ([#9773](#9773)) ([1426695](1426695)) </details> <details><summary>gapic-tools: 2.2.2</summary> ## [2.2.2](gapic-tools-v2.2.1...gapic-tools-v2.2.2) (2026-11-02) ### Bug Fixes * **tools:** Remove duplicate compileProtos invocation in ESM mode ([#9764](#9764)) ([6f475fb](6f475fb)) </details> <details><summary>pubsub: 6.4.0</summary> ## [6.4.0](pubsub-v6.3.2...pubsub-v6.4.0) (2026-11-02) ### Features * **pubsub:** Add CancellationSharer and HedgingScheduler ([#9464](#9464)) ([43ef4dd](43ef4dd)) * **pubsub:** Add enabled option to HedgingOptions (post-merge adjustments) ([#9654](#9654)) ([cdb23f5](cdb23f5)) * **pubsub:** Add HedgingOptions validation and token bucket ([#9463](#9463)) ([764347a](764347a)) * **pubsub:** Add OpenTelemetry events and logging for hedged publish ([#9465](#9465)) ([8636dba](8636dba)) * **pubsub:** Integrate publish hedging into MessageQueue ([#9466](#9466)) ([89fd786](89fd786)) </details> <details><summary>chat: 0.36.0</summary> ## [0.36.0](chat-v0.35.0...chat-v0.36.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>ces: 0.11.0</summary> ## [0.11.0](ces-v0.10.0...ces-v0.11.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>designcenter: 0.1.0</summary> ## 0.1.0 (2026-11-02) ### Features * **google/cloud/designcenter/v1:** Add google-cloud-designcenter ([#9767](#9767)) ([28f6a4b](28f6a4b)) * Update API sources and regenerate ([#9554](#9554)) ([8b6837a](8b6837a)) </details> <details><summary>documentai: 10.3.0</summary> ## [10.3.0](documentai-v10.2.0...documentai-v10.3.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>geminidataanalytics: 0.15.0</summary> ## [0.15.0](geminidataanalytics-v0.14.0...geminidataanalytics-v0.15.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>memorystore: 0.13.0</summary> ## [0.13.0](memorystore-v0.12.0...memorystore-v0.13.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>modelarmor: 0.12.0</summary> ## [0.12.0](modelarmor-v0.11.0...modelarmor-v0.12.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>network-management: 6.3.0</summary> ## [6.3.0](network-management-v6.2.0...network-management-v6.3.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>observability: 0.1.1</summary> ## [0.1.1](observability-v0.1.0...observability-v0.1.1) (2026-11-02) ### Bug Fixes * **observability:** Update package.json scripts ([#9770](#9770)) ([dcaea88](dcaea88)) </details> <details><summary>oracledatabase: 0.13.0</summary> ## [0.13.0](oracledatabase-v0.12.0...oracledatabase-v0.13.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>redis-cluster: 0.19.0</summary> ## [0.19.0](redis-cluster-v0.18.0...redis-cluster-v0.19.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>sql: 0.31.0</summary> ## [0.31.0](sql-v0.30.0...sql-v0.31.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>video-transcoder: 5.3.0</summary> ## [5.3.0](video-transcoder-v5.2.0...video-transcoder-v5.3.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> <details><summary>quota: 0.12.0</summary> ## [0.12.0](quota-v0.11.0...quota-v0.12.0) (2026-11-02) ### Features * Update API sources and regenerate ([#9769](#9769)) ([5437a0b](5437a0b)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
Add resolveResponsePayload to errorUtil so LoggerHelper can populate response.payload with the full response serialized as JSON (string payloads are returned as-is). It prefers the raw response gax provides for paginated and long-running calls over the processed response, and returns undefined when there is no response.