MCPcopy Create free account
hub / github.com/UsefulSoftwareCo/executor / writeMembership

Function writeMembership

apps/cloud/src/auth/workos-mirror-store.ts:606–664  ·  view source on GitHub ↗
(membership: WorkOsMirrorMembership)

Source from the content-addressed store, hash-verified

604 // The membership upsert under the row guard (`membershipAcceptsPayload`)
605 // and the account tombstone, never the organization mark.
606 const writeMembership = async (membership: WorkOsMirrorMembership): Promise<boolean> => {
607 await ensureAccount(membership.accountId);
608 // Never for a DELETED user. The row guard below orders a payload against
609 // the membership row it would overwrite; a membership the mirror has not
610 // seen yet has no row, so the guard cannot refuse the INSERT — and a
611 // feeder that fetched the membership before the user was deleted and
612 // writes it after (a stalled login list, the backfill's older listing)
613 // would insert it live. The account tombstone is the one row a deleted
614 // user always leaves behind, so it is consulted first, by identity:
615 // WorkOS never reuses a user id, so no payload naming a tombstoned
616 // account is ever current.
617 //
618 // Read FOR SHARE, held until the transaction `db` belongs to commits: a
619 // `deleteUser` applying the deletion at this moment holds the row FOR NO
620 // KEY UPDATE until ITS commit, so this read waits for it and sees the
621 // tombstone — and a deletion that arrives after this read waits for this
622 // transaction, then tombstones the membership it inserted. Unlocked, a
623 // deletion could land between this read and the insert below and leave
624 // a live membership for a deleted user.
625 const locked = await db
626 .select({ email: accounts.email, workosUpdatedAt: accounts.workosUpdatedAt })
627 .from(accounts)
628 .where(eq(accounts.id, membership.accountId))
629 .for("share");
630 const account = locked[0];
631 // `ensureAccount` guarantees the row; accounts are tombstoned, never
632 // deleted, so a missing one is refused like a tombstone, not written for.
633 if (account === undefined || isAccountTombstone(account)) return false;
634 // Never under a DELETED membership id. The row guard below can only
635 // refuse against the id the row holds; a delete of THIS id that found
636 // the row under another id (see the header) left only the ledger entry
637 // behind, and that is what refuses the payload here.
638 if (await membershipIdDeleted(db, membership.id)) return false;
639 const written = await db
640 .insert(memberships)
641 .values({
642 accountId: membership.accountId,
643 organizationId: membership.organizationId,
644 membershipId: membership.id,
645 role: membership.role,
646 status: membership.status,
647 workosUpdatedAt: membership.updatedAt,
648 })
649 .onConflictDoUpdate({
650 target: [memberships.accountId, memberships.organizationId],
651 set: {
652 membershipId: membership.id,
653 role: membership.role,
654 status: membership.status,
655 workosUpdatedAt: membership.updatedAt,
656 // A replacement taking over a tombstone is live again; a row that
657 // was not tombstoned had nothing here.
658 deletedAt: null,
659 },
660 setWhere: membershipAcceptsPayload(membership.id, membership.updatedAt),
661 })
662 .returning({ accountId: memberships.accountId });
663 return written.length > 0;

Callers 1

makeWritesFunction · 0.85

Calls 5

ensureAccountFunction · 0.85
isAccountTombstoneFunction · 0.85
membershipIdDeletedFunction · 0.85
membershipAcceptsPayloadFunction · 0.85
valuesMethod · 0.80

Tested by

no test coverage detected