Fix DX-5356 + DX-5421, classify 5 deferred bugs as server-side#22
Conversation
DX-5356 — streams testFilter: filter_function is now required (String)
instead of Option<String> in TestFilterParams. The API rejects requests
without it, so the type now matches the API contract. Mirrored across
the Python pyo3 signature, Ruby hash validator, RBS sig, examples, and
existing test fixtures.
Adds 5 wiremock regression tests that capture the outgoing request body
for the 4 silent-no-op / ignored-field bugs and confirm the SDK is
sending the correct wire format. All 5 pass, meaning the failures are
server-side (or upstream contract issues), not SDK bugs:
- DX-5341: bulk_sets sends `add_sets` correctly
- DX-5342: bulk_sets sends `delete_sets` correctly
- DX-5343: update_list sends `add_items` and `remove_items` correctly
- DX-5347: update_webhook_template sends `name` correctly
- DX-5352: update_team_endpoints sends `endpoint_ids: []` (empty
array) correctly when supplied with an empty Vec
The wire-inspection tests are kept in the suite as durable regressions
so future serde changes that drop or rename these fields fail loudly.
DX-5356 SDK Bug 0017 — streams: testFilter rejects requests without `filterFunction`/`addressBookConfig` despite optional typing
Symptom
…or, with Reproductionimport { QuicknodeSdk } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
await qn.streams.testFilter({
network: "ethereum-mainnet",
dataset: "Block",
block: "17811625",
// no filterFunction
// no addressBookConfig
});ObservedA subsequent call with
A call with a valid base64-encoded JS function succeeds. ExpectedEither:
The first error message ("must be base64 encoded") is also misleading when the field is simply absent — the field hasn't been encoded incorrectly, it just hasn't been provided. Suspected causeSame shape as bugs 0006, 0007, 0015: SDK type optional, upstream API treats it as required. NotesA trivial identity filter Discovered by the sdk-smoke-tester e2e suite. Bug file: DX-5421 SDK Bug 0022 — admin: getAccountMetrics fails to decode `tag` field (server returns tuple, SDK expects string)
Symptom
Reproductionimport { QuicknodeSdk } from "@quicknode/sdk";
const qn = QuicknodeSdk.fromEnv();
await qn.admin.getAccountMetrics({ period: "day", metric: "credits_over_time" });
// throws DecodeErrorObservedRaw response body: {
"data": [
{ "tag": ["network", "arbitrum-mainnet"], "data": [[1779109200, 40]] },
{ "tag": ["network", "mainnet"], "data": [[1779116400, 40]] }
],
"error": null
}The SDK's Rust core can't deserialise ExpectedEither:
Suspected causeMismatch between the Rust core's Notes
Discovered by the sdk-smoke-tester e2e suite. Bug file: |
The metrics endpoints return `tag` as either a plain string (single-axis series like `"total"` or `"p95"`) or a `[key, value]` tuple (multi-axis series like `["network", "arbitrum-mainnet"]`). The SDK declared `tag: String`, so multi-axis responses failed with DecodeError before returning to the caller. Change the field to `Vec<String>` with a custom `tag_as_vec` deserializer that accepts both wire shapes (plain string normalises to a 1-element vec, tuple collects into N elements). Mirrors the `null_as_default` precedent in kvstore. Adds 5 unit tests for the deserializer plus a wiremock regression that replays the exact body shape from the bug report. Updates examples and READMEs across all four languages to reflect the array shape. Also adds a CLAUDE.md rule to keep Linear/PR identifiers out of code comments, and removes the 4 pre-existing DX-#### references that had crept into wire-inspection regression comments.
Summary
Second pass on the DX-5040 SDK bug sweep. Fixes two more SDK-side bugs and classifies five "silent no-op" / "ignored field" bugs as server-side via durable wire-inspection regression tests.
SDK fixes
TestFilterParams.filter_functionis nowString(required) instead ofOption<String>. The API rejects requests without it, so the type now matches the API contract. Mirrored across the Python pyo3 signature, Ruby hash validator + RBS sig, the Rust example, and existing test fixtures.EndpointMetric.tagis nowVec<String>with a customtag_as_vecdeserializer that accepts both wire shapes. The metrics endpoints returntagas either a plain string for single-axis series ("total","p95") or a[key, value]tuple for multi-axis series (["network", "arbitrum-mainnet"]); the previoustag: Stringdeclaration failed every multi-axis response withDecodeError. Surfaces asArray<string>(TS),list[str](Python), andArray<String>(Ruby) — bindings regenerated automatically. Includes 5 unit tests plus a wiremock regression that replays the exact bug-report body.Bugs classified as server-side
Added 5 wiremock regression tests that assert the outgoing request body. All 5 pass, confirming the SDK sends the correct wire format — so the failures are server-side, not serde misconfigurations:
bulk_setssendsadd_setsbulk_setssendsdelete_setsupdate_listsendsadd_items+remove_itemsupdate_webhook_templatesendsnameupdate_team_endpointssendsendpoint_ids: []when given an empty VecThe tests stay in the suite as durable regressions so future serde changes that rename or drop these fields fail loudly.
Housekeeping
CLAUDE.mdrule: no Linear IDs, PR numbers, or other internal tracker references in code comments. The "why" should be self-contained; tracker context belongs in commit messages or PRs.DX-####references from the wire-inspection regression comments added by the earlier commit in this branch (kvstore × 2, admin × 1, webhooks × 1).Recommended Linear action
For DX-5341, DX-5342, DX-5343, DX-5347, DX-5352: leave them open but reassign / re-team to the backend owner with a comment pointing at the wire-inspection test that proves the SDK side is correct.
Test plan
cargo check --workspacecleanjust lint(clippy-D warnings) cleancargo test -p quicknode-sdk --lib— 190 Rust tests pass (184 from sweep Beta #1 commit + 6 new from the DX-5421 fix: 5 unit + 1 wiremock regression)just node-build(build + napi test) cleanjust ruby-buildcleanjust python-build(stubs regenerated) clean@quicknode/sdkdep at this branchLinear
Fixes DX-5356
Fixes DX-5421