UNPKG

@clickup/ent-framework

Version:

A PostgreSQL graph-database-alike library with microsharding and row-level security

170 lines 6.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Triggers = void 0; exports.buildUpdateNewRow = buildUpdateNewRow; const deepEqual_1 = require("../internal/deepEqual"); const misc_1 = require("../internal/misc"); class Triggers { constructor(beforeInsert, beforeUpdate, beforeDelete, beforeMutation, afterInsert, afterUpdate, afterDelete, afterMutation) { this.beforeInsert = beforeInsert; this.beforeUpdate = beforeUpdate; this.beforeDelete = beforeDelete; this.beforeMutation = beforeMutation; this.afterInsert = afterInsert; this.afterUpdate = afterUpdate; this.afterDelete = afterDelete; this.afterMutation = afterMutation; } hasInsertTriggers() { return (this.beforeInsert.length > 0 || this.beforeMutation.length > 0 || this.afterInsert.length > 0 || this.afterMutation.length > 0); } hasUpdateTriggers() { return (this.beforeUpdate.length > 0 || this.beforeMutation.length > 0 || this.afterUpdate.length > 0 || this.afterMutation.length > 0); } async wrapInsert(func, vc, input) { if (!this.hasInsertTriggers()) { return func(input); } for (const triggerBeforeInsert of this.beforeInsert) { // We clone the input to make different triggers calls independent: if the // trigger e.g. stores input somewhere by reference, we don't want the // next trigger to affect that place. input = { ...input }; await triggerBeforeInsert(vc, { input }); } for (const [_, triggerBeforeMutation] of this.beforeMutation) { input = { ...input }; await triggerBeforeMutation(vc, { op: "INSERT", newOrOldRow: input, input, }); } const output = await func(input); if (!output) { // Insert failed (unique key constraint failed); don't run after-triggers. return output; } for (const triggerAfterInsert of this.afterInsert) { await triggerAfterInsert(vc, { input }); } for (const [_, triggerAfterMutation] of this.afterMutation) { await triggerAfterMutation(vc, { op: "INSERT", newOrOldRow: input }); } return output; } async wrapUpdate(func, vc, oldRow, input) { if (!this.hasUpdateTriggers()) { return func(input); } let newRow = buildUpdateNewRow(oldRow, input); for (const [depsBuilder, triggerBeforeUpdate] of this.beforeUpdate) { if (await depsBuilderApproves(depsBuilder, vc, oldRow, newRow)) { await triggerBeforeUpdate(vc, { newRow, oldRow, input }); // Each call to triggerBefore() may potentially change the input, so we // need to rebuild newRow each time to feed it to the next call of // triggerBefore() and to the rest of triggerAfter. newRow = buildUpdateNewRow(oldRow, input); } } for (const [depsBuilder, triggerBeforeMutation] of this.beforeMutation) { if (await depsBuilderApproves(depsBuilder, vc, oldRow, newRow)) { await triggerBeforeMutation(vc, { op: "UPDATE", newOrOldRow: newRow, input, }); newRow = buildUpdateNewRow(oldRow, input); } } const output = await func(input); if (!output) { // Update failed (no row with such ID); don't call after-triggers. return output; } for (const [depsBuilder, triggerAfterUpdate] of this.afterUpdate) { if (await depsBuilderApproves(depsBuilder, vc, oldRow, newRow)) { await triggerAfterUpdate(vc, { newRow, oldRow: oldRow, }); } } for (const [depsBuilder, triggerAfterMutation] of this.afterMutation) { if (await depsBuilderApproves(depsBuilder, vc, oldRow, newRow)) { await triggerAfterMutation(vc, { op: "UPDATE", newOrOldRow: newRow, }); } } return output; } async wrapDelete(func, vc, oldRow) { for (const triggerBeforeDelete of this.beforeDelete) { await triggerBeforeDelete(vc, { oldRow }); } for (const [_, triggerBeforeMutation] of this.beforeMutation) { await triggerBeforeMutation(vc, { op: "DELETE", newOrOldRow: oldRow, input: oldRow, }); } const output = await func(); if (!output) { // Delete failed (no row with such ID); don't call after-triggers. return output; } for (const triggerAfterDelete of this.afterDelete) { await triggerAfterDelete(vc, { oldRow }); } for (const [_, triggerAfterMutation] of this.afterMutation) { await triggerAfterMutation(vc, { op: "DELETE", newOrOldRow: oldRow, }); } return output; } } exports.Triggers = Triggers; /** * Simulates an update for a row, as if it's applied to the Ent. * @ignore */ function buildUpdateNewRow(oldRow, input) { const newRow = { ...oldRow }; for (const k of Object.getOwnPropertyNames(input)) { if (input[k] !== undefined) { newRow[k] = input[k]; } } for (const k of Object.getOwnPropertySymbols(input)) { if (input[k] !== undefined) { newRow[k] = input[k]; } } return newRow; } /** * Returns true if depsBuilder approves running the trigger (i.e. the deps are * changed, or no depsBuilder is presented at all). */ async function depsBuilderApproves(depsBuilder, vc, oldRow, newRow) { if (!depsBuilder) { return true; } const [depsOld, depsNew] = await (0, misc_1.join)([ depsBuilder(vc, oldRow), depsBuilder(vc, newRow), ]); return !(0, deepEqual_1.deepEqual)(depsOld, depsNew); } //# sourceMappingURL=Triggers.js.map