Migrate billing invoices to GraphQL and decouple from the Billing store#2012
Open
GregorShear wants to merge 3 commits into
Open
Migrate billing invoices to GraphQL and decouple from the Billing store#2012GregorShear wants to merge 3 commits into
GregorShear wants to merge 3 commits into
Conversation
Billing invoices are now fetched through a tenant-keyed SWR hook (useBillingInvoices) instead of an imperative fetch into the Billing store. Because the SWR key derives from the selected tenant and the rolling six-month window, switching orgs re-fetches automatically and never shows the previous tenant's data — so the store no longer needs to be manually reset when the tenant changes. Removes the useTenantChangeReset hook, the unmount reset in AdminBilling, and the StoreWithHydration machinery (invoices, active/hydrated/ networkFailed/hydrationErrorsExist, resetState) from the Billing store, which is now just the invoice selection and paymentMethodExists. The selected invoice resolves to the stored selection or falls back to the newest invoice, so a stale selection from another tenant self-corrects. All invoice/loading/error reads across the billing page, history table, line-items table, usage graphs, and graph-state wrapper now come from the hook. SWR dedupes the shared key to a single request, and revisiting a tenant is served instantly from cache.
Swap the transport behind useBillingInvoices from the PostgREST invoices_ext view to the GraphQL tenant(name).billing.invoices connection. The hook's public shape is unchanged, so no consumer is touched — the invoice list, history table, line-items table, usage graphs, and graph-state wrapper all keep reading through the hook. The GQL invoice node is camelCased, returns invoiceType as an enum, and omits billed_prefix (the tenant is the query parent), so the hook maps each node back to the existing Invoice shape (billed_prefix = the queried tenant, lowercased invoice_type, JSON line items/extra cast to the established types). Money fields stay integer cents, matching the existing /100 display. GraphQL's InvoiceFilter is narrower than the old PostgREST predicate (no "window OR manual" clause, only gt/lt date bounds), so the six-month window + manual-invoice filter and the newest-first sort are reproduced client-side over a generous fetch limit. urql keys its cache on the query variables, preserving the tenant-keyed refetch and instant cached revisits. Removes the now-unused getInvoicesBetween PostgREST query. Note: the line-item/extra JSON field shapes are assumed identical to the PostgREST payload (same data source); pending runtime verification.
The TenantBillingInvoices query selects Tenant, TenantBilling, and Invoice, none of which expose an id/_id, so graphcache logged 'Invalid key' warnings and fell back to embedding them on the parent. Add the three types to the cacheExchange keys config returning null (the same convention already used for Alert/LiveSpecRef/etc.), making the embed-on-parent behavior explicit and silencing the warnings. Read-only billing data fetched per tenant, so normalization isn't needed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Two changes to the billing admin page, kept tightly scoped:
invoices_extview via PostgREST (getInvoicesBetween). It now comes from thetenant(name).billing.invoicesGraphQL connection. This was billing's only direct PostgREST read.useBillingInvoices), so switching orgs re-fetches automatically and never shows the previous tenant's data.The second change is what unblocks the tenant/prefixSelector refactor:
TenantOptionsno longer needs to passupdateStoreStateto reset the store on tenant switch, so it drops to a bare<TenantSelector />. Billing was the only caller passing that prop, so it's now dead — the refactor can remove it as its first step. (Left in place here to keep this PR purely about billing.)How it works
useBillingInvoicesqueries invoices via urql, keyed on the selected tenant. urql caches on the query variables, so revisiting a tenant is instant and the previous tenant's data is never shown.InvoiceFilteris narrower than the old PostgREST predicate (no "window OR manual" clause), so the six-month-window + manual-invoice filter and the newest-first sort are reproduced client-side over a generous fetch limit.invoiceTypeas an enum, and omitsbilled_prefix(the tenant is the query parent). The hook maps each node back to the existingInvoiceshape, so no consumer of the hook changed behavior — the history table, line-items table, usage graphs, and graph-state wrapper all keep reading through it.Tenant,TenantBilling, andInvoiceare registered as keyless in the urql cache (they have no stable id).Out of scope (deferred)
This branch is the narrow slice of a larger billing branch. Deliberately excluded, to be split into follow-ups:
getTenantInvoiceedge function, unchanged)Verification
npm run typecheck— cleannpm run lint— cleangetInvoicesBetween/invoices_extreferences remain in billingNeeds a runtime check before merge
Confirm the GraphQL
billing.invoicesconnection returns the same set as the oldinvoices_extview — specifically the in-progress preview invoice and manual invoices, which the usage graphs and current-month line items depend on. Worth checking against a tenant that has a current open invoice plus a historical manual one.