auth configs / projects
Stack: Batch Revoke — Apollo side · PR 2 of 4
Towards DASH-814
We're adding batch-revoke support to the platform: the ability to revoke every connected account in a scope (org / project / auth config / connected account) asynchronously, plus opt-in revoke-on-delete on the public DELETE endpoints. This PR stack is the Apollo side over the Thermos workflow machinery (separate stack).
Apollo-side stack (a DAG — merge bottom-up):
apollo-jobs-table — jobs ownership table + oj_/pj_ ids (base: master)apollo-lib — shared, auth-agnostic revoke-job lib ← this PR (base: 1)
apollo-endpoints — dashboard + platform job endpoints (base: 2)apollo-delete-cascade — opt-in revoke-on-delete on DELETE handlers (base: 2)Base:
apollo-jobs-table— review/merge that first. PRs 3 and 4 both build on this PR.
The async revoke-job feature — scoped/bulk revocation of connected accounts plus opt-in
revoke-on-delete — needs an Apollo-side core shared by the create/poll/retry endpoints (PR 3)
and the DELETE-cascade path (PR 4). This adds that core as an auth- and scope-agnostic
library (src/lib/jobs/revoke) so each consumer stays a thin shell — the load-bearing work
of this Apollo stack.
What:
scope.ts): the closed RevokeScope union (org / project / auth_config /
connected_account), the job class, and a deterministic concurrent-run dedup key (the Temporal
workflow id) hashed from the scope.enumerate.ts): scope → live connection-id snapshot. Soft-delete does not
cascade, so liveness is asserted at every level (connection, auth config, project, org); a
connection orphaned by a deleted auth config is excluded.enqueue.ts, poll.ts, retry.ts): orchestration over Thermos.
enqueue records ownership in the jobs table and encodes {scope, trigger} into the row
metadata so retry rebuilds its inputs from the Job alone, even after the scope is gone.
Status is Thermos's verbatim running/completed/failed.common/lib/external/thermos.ts) with the
jobs/revoke enqueue + poll calls.Note on
generated/thermos/types.gen.ts: the wholesrc/generated/thermos/directory is git-ignored and regenerated bypnpm generate:client, but a staletypes.gen.tswas still committed tomaster. This PR drops that one committed artifact so the directory is consistently generated, not tracked. No CI changes are required — every workflow that builds, typechecks, or lints Apollo already runsgenerate:client: either via a Turbo task that declares it as a dependency (build/lint/check-types/build:openapi) or via an explicit step (the vitest integration / e2e / self-hosted suites). The Docker images (self-host.Dockerfile,docker/CI.dockerfile) and the Vercel production deploy all compile Apollo withturbo build, which regenerates the client first.
apps/apollo/src/lib/jobs/revoke/enumerate.test.ts (7 cases,
seeded DB): live-only enumeration across all four scopes; a connection under a soft-deleted
auth config is excluded (orphaned, not live); a soft-deleted auth config / project / connection
yields nothing (no cascade); no scheme pre-filter (every live connection in scope, revokable
or not).apps/apollo/src/lib/jobs/revoke/retry.test.ts (7 cases):
collectFailedConnectionIds polls only the failed rows, accumulates across pages following
next_cursor to null, skips items missing a connected_account_id, returns empty when nothing
failed, rejects a not-yet-finished job without paging further, and propagates a Thermos poll
error.pnpm test:unit in apps/apollo.auth configs / projects