feat(paddle_fdw): rich query pushdown, more entities, richer import schema#616
Conversation
…hema Improve the Paddle Wasm FDW (0.2.0 -> 0.3.0): - Fix `subscriptions` id pushdown: `where id = '...'` now does a single-object GET instead of a full paginated scan. - Add a multi-column + date-range qual pushdown framework (modeled on orb_fdw). Date-range operators ([LT]/[LTE]/[GT]/[GTE]) are transactions-only per the Paddle API; bounds use superset rounding so pushdown never drops rows. `IN (...)` lists are pushed as Paddle comma-separated values. - Percent-encode pushed filter values and the id path segment. - Fully wire `adjustments` (refunds/chargebacks); its `id` is a list filter since Paddle has no single-object adjustments endpoint. - Expand `import foreign schema` to 7 richer, typed tables (customers, products, prices, subscriptions, transactions, discounts, adjustments) and honor `limit to` / `except`. - Treat a column missing from the API response as SQL NULL rather than erroring. - Update docs/catalog/paddle.md and add mock-backed tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thanks for the PR @heymcgovern 👍! Can we add |
…ptions import The subscriptions generated import schema listed `price_id` and `scheduled_change_action` as pushed-down filters but didn't create the columns, so they weren't usable from the imported table. Add both (filter-oriented — the underlying values are nested, so they read as NULL and the actual data is in `attrs`). Addresses review feedback on supabase#616. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Done in 7bc90cc — added 🤖 Addressed by Claude Code |
|
Just pulling back to draft for the mo. Claude was a bit keen here and I need to dig into this a bit more. I'm not sure |
The previous commit added `price_id` and `scheduled_change_action` as subscriptions columns, but both read NULL (the values are nested in the API response). Because the wrapper's pushed-down quals are re-checked locally by Postgres, filtering a NULL column silently returns zero rows. - Populate `scheduled_change_action` from the nested `scheduled_change.action` (defaulting to 'none' when there's no pending change, matching the filter enum), so the filter works end-to-end. Gated to the subscriptions object. - Remove `price_id` from the filter list, generated column, and docs: a subscription can have multiple item prices and Paddle's filter is an any-item match, so no scalar column can represent it under recheck. Prices remain available via `attrs->'items'`. - Don't comma-join IN (...) for single-value params (collection_mode, type, mode), which Paddle would reject. - Add a pushdown test for scheduled_change_action. Addresses review feedback on supabase#616. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Ok thanks for your review here, and cheers for your patience while I looked into this. A couple of these params are new to me so I wanted to dig into the API properly.
Tested and verified this all against my sandbox account. Thanks again 😄 |
burmecia
left a comment
There was a problem hiding this comment.
That looks great now! Thanks @heymcgovern !
Hey team 👋 I'm a dev advocate at Paddle and a big fan of Supabase. Love that there's a Paddle wrapper 💛 - cheers for building this! Was digging into it and spotted a few places where we could improve things.
Once this lands, I'll get out some docs on our side + a developer changelog entry, and I'll include in our next roundup email newsletter (similar to your monthly "Supa Update" one).
What kind of change does this PR introduce?
Feature + docs, with a small bug fix.
Everything is contained in the Paddle Wasm FDW (
wasm-wrappers/fdw/paddle_fdw), version bumped to 0.3.0.What is the current behavior?
The Paddle wrapper only pushes down a single
idequality. Any otherWHEREclause falls back to a full paginated scan of the object.import foreign schemacreates just three tables (customers, products, subscriptions) with minimal cols, so most fields have to be pulled out ofattrs.Two specific gaps:
subscriptionsis missing from the id-pushdown allowlist, sowhere id = 'sub_...'does a full scan instead of a single-object GETadjustments(refunds, credits, chargebacks) is half-wired. It has page-size tuning but no pushdown, import, ordocs).What is the new behavior?
subscriptionsid lookups now do a single-object GET.customer_id,subscription_id,status,product_id— full list per entity in the docs),IN (...)pushed as Paddle comma-separated lists, and date-range pushdown for transactions (created_at/updated_at/billed_at). Filter values and the id path segment are percent-encoded.adjustmentsis fully wired - this is esp. useful for chargebacks reporting.import foreign schemanow generates 7 typed tables and honors thelimit to/exceptclauses.docs/catalog/paddle.mdupdated to match. I also made a small tweak to our intro - we don't generally lead with being payments infrastructure as we're not a PSP.Auth and the pinned
Paddle-Versionheader are unchanged.Additional context
Testing
cargo pgrx test --features "wasm_fdw pg15") cover the single-object id GET, multi-column + date-range pushdown,IN (...), and the adjustments list-filter path.cargo pgrx run, withapi_urlpointed at a small logging proxy to confirm the pushed-down URLs actually reach Paddle, e.g.GET /transactions?per_page=30&customer_id=...&created_at[GTE]=...&created_at[LT]=....cargo fmt,cargo clippy -D warnings, andcargo component buildare clean.Everything went through Fable 5 adversarial review, too.
Other
0.3.0checksum in the docs is a placeholder for now. I understand the real one comes from the release build.customer_idin the path. I'm going to take this back to the team and think about the pattern some more.deletesupport, since we typically only offer archiving via a status update (we're an MoR).