Skip to content

fix(node): preserve camelCase keys for webhook template args#37

Merged
johnpmitsch merged 1 commit into
mainfrom
dx-5346-fix-node-evm-contract-events-camelcase
Jun 2, 2026
Merged

fix(node): preserve camelCase keys for webhook template args#37
johnpmitsch merged 1 commit into
mainfrom
dx-5346-fix-node-evm-contract-events-camelcase

Conversation

@johnpmitsch

Copy link
Copy Markdown
Collaborator

Summary

  • The Node binding's node_ta_to_core was snake-casing the inner args object before deserializing into core::webhooks::TemplateArgs. Core's EvmContractEventsTemplate carries #[serde(rename_all = "camelCase")] and expects eventHashes, so the snake-cased event_hashes was silently dropped during deserialization. The outbound serializer then omitted the now-None field (skip_serializing_if), and the API rejected the request with HTTP 500: "Expected array for arg eventHashes" — independent of whether the caller actually supplied the field.
  • Webhook template inner structs in core are modeled around the API wire format, so they don't need the per-field case conversion that stream destinations require. All other template structs only have single-word fields where camelCase and snake_case coincide, so dropping the conversion is safe across templates.
  • Adds a regression test in crates/node/src/webhooks_template.rs covering the evmContractEvents + eventHashes path (deserialize and re-serialize), plus end-to-end coverage of EvmContractEvents in the webhooks_e2e examples for all four languages.
  • Documents in CLAUDE.md not to silence clippy::panic/clippy::unreachable in tests by default — prefer assert!(matches!(...)) or let ... else { unreachable!() }.

Test plan

  • cargo test -p sdk-node --lib — new regression test passes
  • cargo test -p quicknode-sdk --lib — 203 core tests still pass
  • just lint — clippy + tests, -D warnings
  • cargo fmt --check
  • just node-build — Node binding compiles and smoke test runs
  • Live API e2e against evm-contract-events (requires QN_API_KEY; manual)

The Node binding's node_ta_to_core was snake-casing the inner `args`
object before deserializing into core::webhooks::TemplateArgs. Core's
EvmContractEventsTemplate carries #[serde(rename_all = "camelCase")]
and expects `eventHashes` on input, so the snake-cased `event_hashes`
was treated as an unknown key and silently dropped. With the field
gone, the outbound serializer omitted it (skip_serializing_if =
"Option::is_none"), and the API rejected the request with HTTP 500
("Expected array for arg eventHashes") whether or not the caller
supplied the field.

Webhook template inner structs are modeled around the API wire format
and don't need per-field case conversion (unlike streams destinations,
whose core structs use plain Rust snake_case). All other template
structs only have single-word fields where the two cases are
identical, so removing the conversion is safe across templates.

Adds a regression test in the Node crate that exercises
node_ta_to_core directly with eventHashes input and asserts the
outbound JSON contains "eventHashes" with the expected value, plus
end-to-end coverage of TemplateArgs::EvmContractEvents in the
webhooks examples for all four languages.

Also adds a CLAUDE.md note discouraging blanket clippy::panic /
clippy::unreachable allowances in tests; prefer matches! /
let-else-unreachable.
@johnpmitsch johnpmitsch merged commit a99409c into main Jun 2, 2026
4 checks passed
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.

2 participants