auth configs / projects
Stack: Batch Revoke — Apollo side · PR 1 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 — the jobs ownership table, the shared revoke-job lib, the two endpoint surfaces, and the DELETE cascade — over the Thermos workflow machinery that landed in a separate stack.
Apollo-side stack (a DAG — merge bottom-up):
apollo-jobs-table — jobs ownership table + oj_/pj_ ids ← this PR (base: master)apollo-lib — shared, auth-agnostic revoke-job lib (base: 1)
apollo-endpoints — dashboard + platform job endpoints (base: 2)apollo-delete-cascade — opt-in revoke-on-delete on DELETE handlers (base: 2)PRs 3 and 4 are siblings, both based on PR 2 (the shared lib).
Batch-revoke runs scoped revocation as a Temporal workflow on Thermos, where the
per-connection work list and results live. Apollo still needs a thin, Apollo-side anchor to
authorize polling/retry of a job (and later, to list jobs). This is the jobs
ownership/addressing row, which unblocks the job endpoints (PR 3). No status is stored here;
it is derived in Thermos from the work rows.
What:
Job Prisma model + jobs table (Liquibase migration v105): job_id (public
alias, PK), job_class, org_id, project_id, job_type, thermos_job_id (UNIQUE),
created_at.oj_ / pj_ branded nano ids in @composio/types; the prefix encodes job_class (org
vs project) and is enforced (greenfield, no legacy rows).JobError namespace and a single DB_JOB dbUtils class (upsertByThermosJobId,
byJobIdOrgIdProjectId).How / key decisions:
thermos_job_id UNIQUE is the concurrent-run dedup anchor: the create path upserts on it
(insert ⇒ mint a fresh job_id ⇒ 202; conflict ⇒ echo the existing job_id ⇒ 409). The
upsert is lookup-first, so re-posting a running scope does no wasted INSERT and logs no
unique-violation; a rare insert race is caught by re-reading.job_class / job_type are plain TEXT validated at the app layer (Zod), not pg enums, so
adding a class/type needs no migration.org_id / project_id FK orgs/projects by nanoId (ON DELETE CASCADE), so the authz getter
can join deleted = false and never leak jobs of a deleted scope; the getter is scoped by
(job_id, org_id, project_id?) so any ownership mismatch reads as null (⇒ handler 404).created_at is timestamptz.Migrations in Apollo are Liquibase-driven; schema.prisma is edited only for types and the
generated client (no prisma/migrations entry).
changelog.json + up.ts + down.ts, registered in changelog-root.json) apply against
a local DB; jobs table present with the UNIQUE thermos_job_id constraint and the
org_id/project_id FKs.DB_JOB.upsertByThermosJobId: first upsert for a thermos_job_id
mints a fresh oj_/pj_ id (→ 202 shape); a second upsert with the same thermos_job_id
echoes the existing id (→ 409 shape), with no wasted INSERT.oj_/pj_ schemas parse/serialize via the established
@composio/types brand.ts pattern; the prefix is enforced against job_class.This is a low-surface, self-contained data-model change; the load-bearing behavior it enables (the create-path upsert → 202/409) is exercised end-to-end by the endpoint PR (PR 3).
auth configs / projects