Complete the integrations migration: OpenAPI bindings to D1 tables, OAuth app UI, legacy value cleanup (#980) - #985
Conversation
Move _openapi:<name> snapshots out of value_entries into user_openapi_bindings + user_openapi_binding_operations. Per-operation child rows replace whole-blob rewrites on refresh; migration 0102 backfills and deletes the legacy value rows. Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Add /account/integrations/apps/:appSlug showing app config, the connections sharing it, and a rotate-credentials form that lists affected connections and requires confirmation. Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
…iene migration Remove the _integration:/_openapi: reserved-name machinery now that no platform data lives in value_entries. Migration 0103 deletes leftover _integration:* rows, clears refresh-token secret names that were never written, and normalizes URL-shaped required hosts. Connect flow now persists refreshTokenSecretName only when the secret exists and normalizes URL-shaped required hosts at save time. Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
Migration 0102 now requires non-empty unique operation slugs so bad rows abort via the assertion trap instead of an opaque constraint error. Credential rotation merges the client-secret's existing allowed hosts instead of replacing them. Drop leftover account-values name indirection and document the openapi account-deletion order. Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
|
Warning Review limit reached
Next review available in: 4 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (57)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🔎 Preview deployed: https://kody-pr-985.kody-a99.workers.dev Worker: Mocks:
|
Closes #980.
Finishes the migration #979 started: after this PR the values store holds no platform-owned data, and the only evidence of the old approach left in the codebase is the migration SQL (and the migration tests that seed legacy fixtures).
OpenAPI binding storage (migration
0102)_openapi:<name>value blobs move to first-class tables:user_openapi_bindings(config: spec/api URLs, auth, selection, flags) +user_openapi_binding_operations(one row per resolved operation). Per-operation child rows replace rewriting an up-to-900 KB JSON blob on everyopenapi_binding_refresh; staying in D1 (vs KV/R2) keeps the data move a pure SQL migration and keeps per-user isolation structural via composite keys, matching the 0101 design. The migration backfills, asserts counts (including non-empty unique operation slugs — a bad row aborts loudly via the assertion trap rather than an opaque constraint error), and deletes the legacy value rows. Both production rows (canva,sentry) were validated against the migratable predicate and the full zod schema before this PR was opened, and were backed up.OAuth app management UI
New
/account/integrations/apps/:appSlugpage: app config (secret names only, never values), the connections sharing the app, and a rotate-credentials form that lists affected connections and requires explicit confirmation. Rotation reuses theintegration_oauth_app_rotate_credentialsservice path and merges the client-secret's existing allowed hosts instead of replacing them. Sidebar app group headers and connection detail pages link to it.Legacy machinery removal + data hygiene (migration
0103)value-name-guards.tsand all reserved-prefix special-casing deleted fromvalue_list/value_set, search, and the account values UI —_integration:*/_openapi:*are ordinary value names now.0103deletes the 3 leftover_integration:*rows (abandoned connect-flow attempts, never completed a token exchange; backed up before deploy), NULLsrefresh_token_secret_namewhere the named secret was never written (5 production rows — prevents a confusing missing-secret error on any 401-triggered refresh), and normalizes URL-shapedrequired_hosts_jsonentries to bare hosts (1 production row: linkedin).refreshTokenSecretNamewhen the secret actually exists, and required hosts are normalized from URLs to hostnames at save time.Testing
npm run validategreen (format, lint, typecheck, 1459 unit tests, 20 Playwright e2e, MCP e2e, migrations + primitives checks).openapi/migration.node.test.ts(backfill field-for-field, abort on non-migratable/missing/duplicate slug),integrations/hygiene-migration.node.test.ts(all three hygiene behaviors).Production data preflight (already done)
_openapi:*rows (both schema-valid), 3 leftover_integration:*rows, 5 orphaned refresh-token references, 1 URL-shaped host. Backup artifact captured before deploy.<provider>-client-idvalues (bundle-grep audit in flight) and collapsegithub/github-kentinto one OAuth app (both client secrets verified valid against GitHub's check-token endpoint — same GitHub app, two secrets).System recap — extends primitives (medium risk)
Mode: recap · Base:
main@e508c8f3· Head:a9fe6b0bClassification: extends — no new primitives; OpenAPI bindings storage and the values contract change shape, integrations gain an account-UI management surface.
Primitives touched
openapi-bindingsuser_openapi_bindings+user_openapi_binding_operationsd1-app-dbvaluesintegrationsapp-ui/account/integrationsmcp-servercapability-registrySystem map
OpenAPI capabilities and the registry now read bindings from dedicated D1 tables; the account UI gains an OAuth app page whose rotation writes through the integrations service; the values store loses its platform-reserved filtering.
Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).
Before / after
value_entriesrow_openapi:<name>, whole ≤900 KB blob rewritten per refreshuser_openapi_bindingsrow + oneuser_openapi_binding_operationsrow per operation_integration:/_openapi:hidden from list/search/UI, blocked invalue_setInvariants
Per-user isolation preserved structurally: both new tables use composite
(user_id, …)primary/foreign keys; every new query path is userId-scoped.