import type { Prisma } from '../../../generated/prisma/client'; import type { TbankSyncAttempt } from './tbank-access.service'; const NO_UPDATED_ROWS = 0; // Shared owners for the tbank-sync DB claim/lock protocol. // Both helpers are CAS fences: they run inside the caller's transaction and // keep the exact where/data shapes they replaced. Callers keep deciding // success from the claim count exactly as before. // Claims the active TbankSyncWork row: only the still-running attempt of a // valid connection may transition. `data` is the state transition to apply. export type ClaimOptions = { // Overrides the default 'running' fence (e.g. an initiator row also // accepts 'complete'). state: Prisma.TbankSyncWorkWhereInput['state'] | undefined; // true drops the `connection: { invalidToken: false }` guard for fences // that must also fire on an invalidated connection. withoutConnectionFence: boolean | undefined; }; export async function claimSyncWork( tx: Prisma.TransactionClient, attempt: TbankSyncAttempt, data: Prisma.TbankSyncWorkUpdateManyMutationInput, options: Partial = {}, ): Promise { const result = await tx.tbankSyncWork.updateMany({ where: { ...attempt, state: options.state ?? 'running', ...(options.withoutConnectionFence ? {} : { connection: { invalidToken: false } }), }, data, }); return result.count; } // Locks the connection generation before reserving or publishing work: the // row must still be the current revision with a valid token. `extra` // (e.g. account membership) is merged into the where clause. export async function lockConnectionGeneration( tx: Prisma.TransactionClient, owner: { revision: string; userId: string }, extra: Prisma.TbankConnectionWhereInput = {}, ): Promise { const result = await tx.tbankConnection.updateMany({ where: { ...owner, ...extra, invalidToken: false }, data: { membershipRevision: { increment: 0 } }, }); return result.count > NO_UPDATED_ROWS; }