This repository has no description
Something went wrong. Try again.
3.5 kB · 116 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117// SPDX-License-Identifier: AGPL-3.0-or-later
import {upsertOne} from './CassandraQueryExecution';import type {ColumnName, DbOp, PatchObject, RowValue, Table} from './CassandraTypes';import {Db, nextVersion} from './CassandraTypes';
export async function executeVersionedUpdate< Row extends { version?: number | null; }, PK extends ColumnName<Row>, Patch extends PatchObject = PatchObject,>( fetchCurrent: () => Promise<Row | null>, buildPatch: (current: Row | null) => { pk: Record<string, unknown>; patch: Patch; }, table: Table<Row, PK>, opts?: { initialData?: Row | null; },): Promise<{ finalVersion: number | null; previousData: Row | null;}> { const current = opts?.initialData !== undefined ? opts.initialData : await fetchCurrent(); const currentVersion = current?.version ?? null; const newVersion = nextVersion(currentVersion); const {pk, patch} = buildPatch(current); await upsertOne( table.patchByPk( pk as Pick<Row, PK>, {...patch, version: Db.set(newVersion)} as Partial<{ [K in Exclude<ColumnName<Row>, PK>]: DbOp<RowValue<Row, K>>; }>, ), ); return {finalVersion: newVersion, previousData: current};}
export function applyPatchToRow<Row extends object>(current: Partial<Row> | null, patch: PatchObject): Partial<Row> { const next = {...(current ?? {})} as Record<string, unknown>; for (const [column, op] of Object.entries(patch)) { if (op.kind === 'clear') { next[column] = null; continue; } next[column] = op.value; } return next as Partial<Row>;}
function valuesEqual(a: unknown, b: unknown): boolean { if (a === b) return true; if (a == null || b == null) return false; if (a instanceof Date && b instanceof Date) { return a.getTime() === b.getTime(); } if (a instanceof Set && b instanceof Set) { if (a.size !== b.size) return false; for (const item of a) { if (!b.has(item)) return false; } return true; } if (a instanceof Map && b instanceof Map) { if (a.size !== b.size) return false; for (const [key, val] of a) { if (!b.has(key) || !valuesEqual(val, b.get(key))) return false; } return true; } if (Buffer.isBuffer(a) && Buffer.isBuffer(b)) { return a.equals(b); } if (typeof a === 'object' && typeof b === 'object') { const keysA = Object.keys(a as Record<string, unknown>); const keysB = Object.keys(b as Record<string, unknown>); if (keysA.length !== keysB.length) return false; for (const key of keysA) { if (!keysB.includes(key)) return false; if (!valuesEqual((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key])) { return false; } } return true; } return a === b;}
export function buildPatchFromData<Row extends object>( newData: Partial<Row>, oldData: Partial<Row> | null, columns: ReadonlyArray<keyof Row>, pkColumns: ReadonlyArray<keyof Row>,): Record<string, DbOp<unknown>> { const patch: Record<string, DbOp<unknown>> = {}; for (const col of columns) { if (pkColumns.includes(col)) continue; if (col === 'version') continue; const colName = col as string; const newVal = (newData as Record<string, unknown>)[colName]; if (newVal === undefined) continue; const oldVal = oldData ? (oldData as Record<string, unknown>)[colName] : undefined; if (valuesEqual(newVal, oldVal)) continue; if (newVal === null) { if (oldData !== null && oldVal !== null && oldVal !== undefined) { patch[colName] = Db.clear(); } } else { patch[colName] = Db.set(newVal); } } return patch;}