UNPKG

@deep-foundation/deeplinks

Version:

[![npm](https://img.shields.io/npm/v/@deep-foundation/deeplinks.svg)](https://www.npmjs.com/package/@deep-foundation/deeplinks) [![Gitpod](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/deep-fo

834 lines (811 loc) 55.6 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z; import Debug from 'debug'; import { generateApolloClient } from '@deep-foundation/hasura/client.js'; import { DeepClient } from '../imports/client.js'; ; import { api, TABLE_NAME as LINKS_TABLE_NAME } from '../migrations/1616701513782-links.js'; import { sql } from '@deep-foundation/hasura/sql.js'; import { _ids } from '../imports/client.js'; const debug = Debug('deeplinks:migrations:plv8'); const log = debug.extend('log'); const error = debug.extend('error'); const client = generateApolloClient({ path: `${process.env.MIGRATIONS_HASURA_PATH}/v1/graphql`, ssl: !!+process.env.MIGRATIONS_HASURA_SSL, secret: process.env.MIGRATIONS_HASURA_SECRET, }); const deep = new DeepClient({ apolloClient: client, }); const handleInsertId = (_a = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _a === void 0 ? void 0 : _a.HandleInsert; const handleUpdateId = (_b = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _b === void 0 ? void 0 : _b.HandleUpdate; const handleDeleteId = (_c = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _c === void 0 ? void 0 : _c.HandleDelete; const userTypeId = (_d = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _d === void 0 ? void 0 : _d.User; const anyTypeId = (_e = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _e === void 0 ? void 0 : _e.Any; const thenTypeId = (_f = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _f === void 0 ? void 0 : _f.Then; const promiseTypeId = (_g = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _g === void 0 ? void 0 : _g.Promise; const resolvedTypeId = (_h = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _h === void 0 ? void 0 : _h.Resolved; const rejectedTypeId = (_j = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _j === void 0 ? void 0 : _j.Rejected; const promiseResultTypeId = (_k = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _k === void 0 ? void 0 : _k.PromiseResult; const packageTypeId = (_l = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _l === void 0 ? void 0 : _l.Package; const containTypeId = (_m = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _m === void 0 ? void 0 : _m.Contain; const plv8SupportsJsTypeId = (_o = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _o === void 0 ? void 0 : _o.plv8SupportsJs; const HandlerTypeId = (_p = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _p === void 0 ? void 0 : _p.Handler; const SelectorTypeId = (_q = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _q === void 0 ? void 0 : _q.Selector; const AllowSelectTypeId = (_r = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _r === void 0 ? void 0 : _r.AllowSelectType; const AllowSelectId = (_s = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _s === void 0 ? void 0 : _s.AllowSelect; const AllowAdminId = (_t = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _t === void 0 ? void 0 : _t.AllowAdmin; const AllowInsertTypeId = (_u = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _u === void 0 ? void 0 : _u.AllowInsertType; const AllowUpdateTypeId = (_v = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _v === void 0 ? void 0 : _v.AllowUpdateType; const AllowDeleteTypeId = (_w = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _w === void 0 ? void 0 : _w.AllowDeleteType; const AllowInsertId = (_x = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _x === void 0 ? void 0 : _x.AllowInsert; const AllowDeleteId = (_y = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _y === void 0 ? void 0 : _y.AllowDelete; const AllowUpdateId = (_z = _ids === null || _ids === void 0 ? void 0 : _ids['@deep-foundation/core']) === null || _z === void 0 ? void 0 : _z.AllowUpdate; const decodeBase64urlCode = `select decode(rpad(translate($1, '-_', '+/'),4*((length($1)+3)/4),'='),'base64');`; const parseJwtCode = sql ` DECLARE parts varchar array := string_to_array($1, '.'); BEGIN RETURN concat( '{ "headers":', convert_from(${LINKS_TABLE_NAME}__decode__base64url(parts[1]), 'utf-8'), ', "payload":', convert_from(${LINKS_TABLE_NAME}__decode__base64url(parts[2]), 'utf-8'), '}' ); END;`.replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const newSelectCode = '`'.concat(sql ` SELECT links.id as id, links.to_id as to_id FROM links, strings WHERE links.type_id=${containTypeId} AND strings.link_id=links.id AND strings.value='\${item}' AND links.from_id=\${query_id} `, '`').replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const insertLinkStringCode = `\`INSERT INTO links (type_id\${id ? ', id' : ''}\${from_id ? ', from_id' : ''}\${to_id ? ', to_id' : ''}) \``; const insertValueStringCode = `\`INSERT INTO \${checkedNumber ? 'number' : checkedString ? 'string' : checkedObject ? 'object' : ''}s ( link_id, value ) VALUES (\$1 , \$2) RETURNING ID\``; const updateValueStringCode = `\`UPDATE \${table} SET \${set} WHERE \${where} RETURNING id;\``; const deleteStringCode = `\`DELETE FROM links WHERE id=$1::bigint RETURNING ID\``; const deleteStringTableCode = `\`DELETE FROM \${table} WHERE link_id=$1::bigint RETURNING ID\``; const typeHandlersCode = '`'.concat(sql ` SELECT strings.value AS value, handler.id::int AS id FROM links AS codeLinks left join strings ON strings.link_id = codeLinks.id left join links AS handler ON handler.to_id = codeLinks.id WHERE EXISTS ( SELECT 1 FROM public.links AS handlers WHERE handlers.to_id = codeLinks.id AND handlers.type_id = ${HandlerTypeId} AND EXISTS ( SELECT 1 FROM public.links AS supports WHERE handlers.from_id = supports.id AND supports.id = ${plv8SupportsJsTypeId} ) AND EXISTS ( SELECT 1 FROM public.links AS HandlerOperation WHERE HandlerOperation.to_id = handlers.id AND EXISTS ( SELECT 1 FROM public.links AS HandleTypeLink WHERE HandlerOperation.from_id = HandleTypeLink.id AND HandlerOperation.type_id = $2::bigint AND ( HandleTypeLink.id = $1::bigint OR HandleTypeLink.id = ${anyTypeId} ) ) ) ) AND handler.type_id = ${HandlerTypeId}`, '`').replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const selectorHandlersCode = '`'.concat(sql ` SELECT strings.value AS value, handler.id::int AS id FROM links AS codeLinks left join strings ON strings.link_id = codeLinks.id left join links AS handler ON handler.to_id = codeLinks.id WHERE EXISTS ( SELECT 1 FROM public.links AS handlers WHERE handlers.to_id = codeLinks.id AND handlers.type_id = ${HandlerTypeId} AND EXISTS ( SELECT 1 FROM public.links AS supports WHERE handlers.from_id = supports.id AND supports.id = ${plv8SupportsJsTypeId} ) AND EXISTS ( SELECT 1 FROM public.links AS HandlerOperation WHERE HandlerOperation.to_id = handlers.id AND EXISTS ( SELECT 1 FROM public.links AS selector WHERE HandlerOperation.from_id = selector.id AND HandlerOperation.type_id = $2::bigint AND selector.type_id = ${SelectorTypeId}::bigint AND selector.id = ANY($1 :: bigint array) ) ) ) AND handler.type_id = ${HandlerTypeId}`, '`').replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const pckgCode = `typeof(start) === 'string' ? \`SELECT links.id as id FROM links, strings WHERE links.type_id=${packageTypeId} AND strings.link_id=links.id AND strings.value='\${start}'\` : \`SELECT links.id as id FROM WHERE links.id=\${start}\``; const selectWithPermissions = '`'.concat(sql ` SELECT main.* FROM links AS main\${valueTableString} WHERE ( EXISTS ( SELECT 1 FROM links AS type WHERE type.id = main.type_id AND EXISTS ( SELECT 1 FROM can AS canAllowSelectType WHERE canAllowSelectType.object_id = type.id AND canAllowSelectType.action_id = ${AllowSelectTypeId} AND canAllowSelectType.subject_id = $1 :: bigint ) ) OR EXISTS ( SELECT 1 FROM can AS canAllowSelect WHERE canAllowSelect.object_id = main.id AND canAllowSelect.action_id = ${AllowSelectId} AND canAllowSelect.subject_id = $1 :: bigint ) OR EXISTS ( SELECT 1 FROM can AS canAllowAdmin WHERE canAllowAdmin.object_id = $1 :: bigint AND canAllowAdmin.action_id = ${AllowAdminId} AND canAllowAdmin.subject_id = $1 :: bigint ) ) AND (\${where})`, '`').replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const selectTreeWithPermissions = '`'.concat(sql ` SELECT main.* FROM tree AS main WHERE ( EXISTS( SELECT 1 FROM links AS main1 WHERE ( main1.id = main.parent_id AND EXISTS( SELECT 1 FROM links AS type WHERE type.id = main1.type_id AND EXISTS ( SELECT 1 FROM can AS canAllowSelectType WHERE canAllowSelectType.object_id = type.id AND canAllowSelectType.action_id = ${AllowSelectTypeId} AND canAllowSelectType.subject_id = $1 :: bigint ) ) OR EXISTS ( SELECT 1 FROM can AS canAllowSelect WHERE canAllowSelect.object_id = main1.id AND canAllowSelect.action_id = ${AllowSelectId} AND canAllowSelect.subject_id = $1 :: bigint ) OR EXISTS ( SELECT 1 FROM can AS canAllowAdmin WHERE canAllowAdmin.object_id = $1 :: bigint AND canAllowAdmin.action_id = ${AllowAdminId} AND canAllowAdmin.subject_id = $1 :: bigint ) ) ) AND EXISTS ( SELECT 1 FROM links AS main2 WHERE main2.id = main.link_id AND EXISTS( SELECT 1 FROM links AS type2 WHERE type2.id = main2.type_id AND EXISTS ( SELECT 1 FROM can AS canAllowSelectType2 WHERE canAllowSelectType2.object_id = type2.id AND canAllowSelectType2.action_id = ${AllowSelectTypeId} AND canAllowSelectType2.subject_id = $1 :: bigint ) ) OR EXISTS ( SELECT 1 FROM can AS canAllowSelect2 WHERE canAllowSelect2.object_id = main2.id AND canAllowSelect2.action_id = ${AllowSelectId} AND canAllowSelect2.subject_id = $1 :: bigint ) OR EXISTS ( SELECT 1 FROM can AS canAllowAdmin2 WHERE canAllowAdmin2.object_id = $1 :: bigint AND canAllowAdmin2.action_id = ${AllowAdminId} AND canAllowAdmin2.subject_id = $1 :: bigint ) ) ) AND \${where} `, '`').replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const selectCan = sql `\`SELECT * FROM can AS "main" WHERE \${where} \``; const selectSelectors = sql `\`SELECT * FROM selectors AS "main" WHERE \${where} \``; const mpUpCode = '`'.concat(sql ` SELECT json_agg( jsonb_build_object( 'position_id', mp.position_id, 'id', mp.id, 'path_item', jsonb_build_object('id', links1.id, 'type_id', links1.type_id, 'value', strings.value) ) ) as root FROM mp LEFT OUTER JOIN links AS links1 ON mp.path_item_id = links1.id LEFT JOIN strings ON links1.id = strings.link_id WHERE ( mp.item_id = $1 :: bigint AND EXISTS ( SELECT 1 FROM links WHERE links.id = mp.path_item_id AND links.type_id = ANY((ARRAY [$2, $3]):: bigint array) ) ) `, '`').replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const mpMeCode = '`'.concat(sql ` SELECT json_agg(row_to_json(mp.*)) as "root" FROM mp WHERE mp.item_id = $1 :: bigint AND mp.path_item_id = $1 :: bigint `, '`').replace(/[\r\n]+/gm, " ").replace(/\s\s+/g, ' '); const checkAdmin = sql `\`SELECT exists(SELECT 1 FROM "public"."can" WHERE "action_id" = ${AllowAdminId}::bigint AND "subject_id" = $1::bigint )\``; const checkInsert = sql `\`SELECT exists(SELECT "linkForCheck"."id" FROM "public"."can" AS "can", "public"."links" AS "linkForCheck", "public"."links" AS "typeLink" WHERE ("can"."action_id") = (${AllowInsertId} :: bigint) AND ("can"."subject_id") = ($2 :: bigint) AND ("can"."object_id") = ("typeLink"."id") AND ("typeLink"."id") = ("linkForCheck"."type_id") AND ("linkForCheck"."id") = ($1 :: bigint))\``; const checkUpdate = sql `\`SELECT exists( SELECT "linkForCheck"."id" FROM "public"."can" AS "can", "public"."links" AS "linkForCheck", "public"."links" AS "typeLink" WHERE ( ("can"."action_id") = (${AllowUpdateId} :: bigint) ) AND ("can"."subject_id") = ($2 :: bigint) AND ("can"."object_id") = ("typeLink"."id") AND ("typeLink"."id") = ("linkForCheck"."type_id") AND ("linkForCheck"."id") = ($1 :: bigint))\``; const checkDelete = sql `\`SELECT exists( SELECT "linkForCheck"."id" FROM "public"."can" AS "can", "public"."links" AS "linkForCheck", "public"."links" AS "typeLink" WHERE ( ("can"."action_id") = (${AllowDeleteId} :: bigint) ) AND ("can"."subject_id") = ($2 :: bigint) AND ("can"."object_id") = ("typeLink"."id") AND ("typeLink"."id") = ("linkForCheck"."type_id") AND ("linkForCheck"."id") = ($1 :: bigint))\``; const checkInserted = `\`SELECT id from links where id = \${linkid}\``; const checkInsertPermissionCode = `(linkid, userId) => { if (!Number(plv8.execute(${checkInserted})?.[0]?.id))plv8.elog(ERROR, 'Inserted by sql not found'); if (plv8.execute(${checkAdmin}, [ userId ])?.[0]?.exists) return true; const result = plv8.execute(${checkInsert}, [ linkid, userId ]); return !!result[0]?.exists; }`; const checkUpdatePermissionCode = `(linkid, userId) => { if (plv8.execute(${checkAdmin}, [ userId ])?.[0]?.exists) return true; const result = plv8.execute(${checkUpdate}, [ linkid, userId ]); return !!result[0]?.exists; }`; const checkDeleteLinkPermissionCode = `(linkid, userId) => { if (plv8.execute(${checkAdmin}, [ userId ])?.[0]?.exists) return true; const result = plv8.execute(${checkDelete}, [ linkid, userId ]); return !!result[0]?.exists; }`; const prepareFunction = ` const getOwner = (handlerId) => { const mpUp = plv8.execute(${mpUpCode}, [ handlerId, ${userTypeId}, ${packageTypeId} ])[0]?.root; const mpMe = plv8.execute(${mpMeCode}, [ handlerId ])[0]?.root; const possibleOwners = mpMe.map((me) => { const getDepthDifference = (depth) => me.path_item_depth - depth; const up = mpUp.filter((up) => up.position_id == me.position_id); const closestUp = up.sort((a, b) => getDepthDifference(a.path_item_depth) - getDepthDifference(b.path_item_depth))[0];; return closestUp?.path_item; }).filter(r => !!r); const ownerPackage = possibleOwners.find(r => r.type_id == ${packageTypeId}); const ownerUser = possibleOwners.find(r => r.type_id == ${userTypeId}); const ownerId = ownerPackage ? ownerPackage?.id : ownerUser ? ownerUser?.id : null; return ownerId; }; const typeHandlers = plv8.execute(${typeHandlersCode}, [ link.type_id, handletypeid ]); for (let i = 0; i < typeHandlers.length; i++){ typeHandlers[i].owner = Number(getOwner(typeHandlers[i].id)); } const selectors = plv8.execute( 'SELECT s.selector_id, h.id as handle_operation_id, s.query_id FROM selectors s, links h WHERE s.item_id = $1 AND s.selector_id = h.from_id AND h.type_id = $2', [ link.id, handletypeid ] ); const testedSelectors = []; for (let i = 0; i < selectors.length; i++){ if (!selectors[i].query_id || plv8.execute('bool_exp_execute($1,$2,$3)', [link.id, selectors[i].query_id, user_id])) testedSelectors.push(selectors[i].selector_id); } const selectorHandlers = plv8.execute(${selectorHandlersCode}, [ testedSelectors, handletypeid ]); for (let i = 0; i < selectorHandlers.length; i++){ selectorHandlers[i].owner = Number(getOwner(selectorHandlers[i].id)); } const handlers = typeHandlers.concat(selectorHandlers); const sortById = ( a, b ) => { if ( a.id < b.id ){ return -1; } if ( a.id > b.id ){ return 1; } return 0; } return handlers.sort(sortById); `; const selectValueTable = `\`SELECT * FROM \${table} WHERE link_id = \${linkId}\``; const selectLinkByValue = `\`SELECT link_id as id FROM \${table} WHERE value = '\${value}'::\${table==='strings' ? 'text' : table==='objects' ? 'jsonb' : 'bigint'}\``; const generateSelectWhereCode = `(_where, shift = 0) => { const where = []; let values = []; let valueTable; const keys = Object.keys(_where); const pushToWhere = (value, tableKey, where, values, whereKey) => { if ( !value['_in'] ) { if (whereKey !== 'value'){ where.push(whereKey.concat('s.link_id = "main".id')); where.push('"'.concat(whereKey.concat('s".'), tableKey, ' = $', values.length+1+shift)); values.push(value); } else { const valuesPart = 's".'.concat(tableKey, ' = $', values.length+1+shift); let checkJson; try { typeof value === 'object' || JSON.parse(value) ? checkJson = true : null; } catch (e) { checkJson = false; } where.push('(strings.link_id = "main".id AND "string'.concat(valuesPart, '::text', !isNaN(value) ? ' OR numbers.link_id = "main".id'.concat(' AND "number', valuesPart) : '', checkJson ? ' OR objects.link_id = "main".id'.concat(' AND "object', valuesPart, '::text::jsonb') : '',' )' )); values.push(typeof value === 'object' ? JSON.stringify(value) : value); } } else { const inLength = value['_in'].length; const valuesLength = values.length; if (whereKey !== 'value'){ let inValues = '$'.concat(values.length+1+shift); for (let l = values.length+2+shift; l < inLength+values.length+1+shift; l++ ) { inValues = inValues.concat(',$',l); } where.push(whereKey.concat('s.link_id = "main".id')); where.push('"'.concat(whereKey.concat('s".')).concat(tableKey, ' IN ( ', inValues ,' )')); for (let i = 0; i<value['_in'].length; i++){ values.push(value['_in'][i]); } } else { const tables = {numbers: [], objects: [], strings: []}; for (let i = valuesLength+1+shift; i<inLength+valuesLength+1+shift; i++){ const valuesPart = 's".'.concat(tableKey, '=' ); tables.strings.push('"string'.concat(valuesPart, '$',i, '::text')); if (!isNaN(value['_in'][i-(valuesLength+1+shift)])) { tables.numbers.push('"number'.concat(valuesPart, '$',i,'::int')); } let checkJson; try { typeof value['_in'][i-(valuesLength+1+shift)] === 'object' || JSON.parse(value['_in'][i-(valuesLength+1+shift)]) ? checkJson = true : null; tables.objects.push('"object'.concat(valuesPart, '$',i, '::text::jsonb')); } catch (e) { checkJson = false; } values.push(typeof value['_in'][i-(valuesLength+1+shift)] === 'object' ? JSON.stringify(value['_in'][i-(valuesLength+1+shift)]) : value['_in'][i-(valuesLength+1+shift)]); } where.push('( '.concat(tables.strings.join(' OR '), tables.numbers.length ? ' OR '.concat(tables.numbers.join(' OR ')) : '', tables.objects.length ? ' OR '.concat(tables.objects.join(' OR ')) : '', ')')); } } } for (let i = 0; i < keys.length; i++ ){ if ( keys[i] === 'object' || keys[i] === 'string' || keys[i] === 'number' || keys[i] === 'value' ) valueTable = keys[i].concat('s'); if (_where[keys[i]] !== undefined) { if ( !_where[keys[i]]['_in'] ) { if (keys[i] !== 'object' && keys[i] !== 'string' && keys[i] !== 'number' && keys[i] !== 'value') { where.push('"main".'.concat(keys[i], ' = $',values.length+1+shift)); values.push(_where[keys[i]]); } else { const valueKeys = Object.keys(_where[keys[i]]); if (typeof _where[keys[i]] !== 'object' && keys[i] !== 'object') { pushToWhere(_where[keys[i]], 'value', where, values, keys[i]); } else { const simpleSyntax = !_where[keys[i]]['value'] && !_where[keys[i]]['link_id'] && !_where[keys[i]]['id']; for (let j = 0; j < valueKeys.length; j++ ){ pushToWhere(simpleSyntax ? _where[keys[i]] : _where[keys[i]][valueKeys[j]], simpleSyntax ? 'value' : valueKeys[j], where, values, keys[i]); } } } } else { pushToWhere(_where[keys[i]], 'value', where, values, keys[i]); } } } return { where: where.join(' AND '), values, valueTable }; }`; const fillValueByLinksCode = `(links) => { let table; let linkId; if (!links.length) return links; for (let i = 0; i < links.length; i++){ linkId = Number(links[i].id); if (!links[i].value){ table = 'strings'; const stringValue = plv8.execute(${selectValueTable})?.[0]; table = 'objects'; const objectValue = plv8.execute(${selectValueTable})?.[0]; table = 'numbers'; let numberValue = plv8.execute(${selectValueTable})?.[0]; const value = stringValue || objectValue || numberValue; if (value) links[i].value = { id: Number(value?.id), link_id: Number(value?.link_id), value: numberValue ? Number(value.value) : value.value}; } } }`; const isDeepEqualCode = `(object1, object2) => { const objKeys1 = Object.keys(object1); const objKeys2 = Object.keys(object2); if (objKeys1.length !== objKeys2.length) return false; for (var key of objKeys1) { const value1 = object1[key]; const value2 = object2[key]; const isObjects = value1 != null && typeof value1 === "object" && value2 != null && typeof value2 === "object" if ((isObjects && !isDeepEqual(value1, value2)) || (!isObjects && value1 !== value2) ) { return false; } } return true; }`; const findLinkIdByValueCode = `({ string, object, number, value }) => { let table; if (string) { table = 'strings'; return { id: plv8.execute(${selectLinkByValue}) }; } if (number) { if (isNaN(number)) plv8.elog(ERROR, 'number is NaN '.concat(number)); table = 'numbers'; return { id: plv8.execute(${selectLinkByValue}) }; } if (object) { table = 'objects'; try { JSON.parse(object); const idByObject = plv8.execute(${selectLinkByValue}); } catch (e) { plv8.elog(ERROR, 'Error by parsing json object '.concat(object)); } return { id: plv8.execute(${selectLinkByValue}) }; } if (value) { table = 'strings'; const idByString = plv8.execute(${selectLinkByValue})?.[0]?.id; table = 'numbers'; let idByNumber; if (!isNaN(number)) idByNumber = plv8.execute(${selectLinkByValue})?.[0]?.id; table = 'objects'; let idByObject; try { JSON.parse(value); const idByObject = plv8.execute(${selectLinkByValue})?.[0]?.id; } catch (e) { } return idByString || idByNumber || idByObject; } }`; const objectSet = `\`update objects set value = jsonb_set(value, $2, $3, true) where link_id = $1\``; const objectGet = `\`select value\${pathStr} as value from objects where link_id = $1\``; const deepFabric = `(ownerId, hasura_session) => { hasura_session['x-hasura-role'] = 'link'; const unsafe = { plv8 }; const deep = { linkId: Number(ownerId), id: (start, ...path) => { plv8.execute('SELECT set_config($1, $2, $3)', [ 'hasura.user', JSON.stringify({...hasura_session, 'x-hasura-user-id': this.linkId}), true]); hasura_session['x-hasura-user-id'] = this.linkId; try { const pathToWhere = (start, path) => { const pckg = ${pckgCode}; let query_id = plv8.execute(pckg)[0].id; for (let p = 0; p < path.length; p++) { const item = path[p] if (typeof(item) !== 'boolean') { const newSelect = plv8.execute(${newSelectCode})[0]; query_id = p === path.length-1 ? newSelect.to_id : newSelect.id; if (!query_id) return undefined; } } return Number(query_id); } const result = pathToWhere(start, path); if (!result && path[path.length - 1] !== true) { plv8.elog(ERROR, 'Id not found by '.concat(start, ' -> ', path.join(' -> '))); } return Number(result); } catch (error) { plv8.elog(ERROR, 'Id not found by '.concat(start, ' -> ', path.join(' -> '))); } }, objectSet: function(link_id, path, value) { plv8.execute('SELECT set_config($1, $2, $3)', [ 'hasura.user', JSON.stringify({...hasura_session, 'x-hasura-user-id': this.linkId}), true]); hasura_session['x-hasura-user-id'] = this.linkId; const linkCheck = checkUpdatePermission(link_id, this.linkId); if (!linkCheck) plv8.elog(ERROR, 'Update not permitted'); plv8.execute(${objectSet}, [link_id, path, value]); }, objectGet: function(link_id, path) { plv8.execute('SELECT set_config($1, $2, $3)', [ 'hasura.user', JSON.stringify({...hasura_session, 'x-hasura-user-id': this.linkId}), true]); hasura_session['x-hasura-user-id'] = this.linkId; const where = '"main".id = '.concat(link_id); const pathStr = path.length ? "->'".concat(path.join("'->'"),"'") : ''; valueTableString = ''; const link = plv8.execute(${selectWithPermissions}, [ this.linkId ]); let returning; if (link[0]) returning = plv8.execute(${objectGet}, [link_id])?.[0]?.value; return { data: returning[0] }; }, select: function(_where, options) { if (options?.table && !['links', 'tree', 'can', 'selectors'].includes(options?.table)) plv8.elog(ERROR, 'select not from "links" not permitted'); plv8.execute('SELECT set_config($1, $2, $3)', [ 'hasura.user', JSON.stringify({...hasura_session, 'x-hasura-user-id': this.linkId}), true]); hasura_session['x-hasura-user-id'] = this.linkId; if (!options?.table || options?.table === 'links'){ const { id, type_id, from_id, to_id, number, string, object, value } = _where; const generateSelectWhere = ${generateSelectWhereCode}; const findLinkIdByValue = ${findLinkIdByValueCode}; const fillValueByLinks = ${fillValueByLinksCode}; const isDeepEqual = ${isDeepEqualCode}; let generated = generateSelectWhere(_where, 1); const where = generated.where; let links = []; const valueTableString = generated.valueTable ? generated.valueTable === 'values' ? ' left join "strings" on "strings".link_id = "main".id left join "objects" on "objects".link_id = "main".id left join "numbers" on "numbers".link_id = "main".id' : ' left join "'.concat(generated.valueTable, '" on "',generated.valueTable,'".link_id = "main".id') : ''; if (options?.returning) return { data: links.map(link=>link[options?.returning]) }; if (where) links = plv8.execute(${selectWithPermissions}, [ this.linkId, ...generated.values ]); fillValueByLinks(links); return { data: links }; } if (options?.table === 'tree'){ const { id, link_id, parent_id, depth, root_id, position_id, tree_id } = _where; const generateSelectWhere = ${generateSelectWhereCode}; let generated = generateSelectWhere(_where, 1); const where = generated.where; let links = []; if (where) links = plv8.execute(${selectTreeWithPermissions}, [ this.linkId, ...generated.values ]); if (options?.returning) return { data: links.map(link=>link[options?.returning]) }; return { data: links }; } if (options?.table === 'can'){ const { rule_id, subject_id, object_id, action_id } = _where; const generateSelectWhere = ${generateSelectWhereCode}; let generated = generateSelectWhere(_where); const where = generated.where; let links = []; if (where) links = plv8.execute(${selectCan}, generated.values); if (options?.returning) return { data: links.map(link=>link[options?.returning]) }; return { data: links }; } if (options?.table === 'selectors'){ const { item_id, selector_id, selector_include_id, query_id } = _where; const generateSelectWhere = ${generateSelectWhereCode}; let generated = generateSelectWhere(_where); const where = generated.where; let links = []; if (where) links = plv8.execute(${selectSelectors}, generated.values); if (options?.returning) return { data: links.map(link=>link[options?.returning]) }; return { data: links }; } }, insert: function(exp, options) { const { id, type_id, from_id, to_id, number, string, object } = exp; if (options?.table && !['links', 'strings', 'numbers', 'objects'].includes(options?.table)) plv8.elog(ERROR, 'insert to '.concat(options?.table, ' not permitted')); if ( number && typeof number !== 'number' || number?.data?.value && typeof number?.data?.value !== 'number' || string && typeof string !== 'string' || string?.data?.value && typeof string?.data?.value !== 'string' || object && typeof object !== 'object' || object?.data?.value && typeof object?.data?.value !== 'object' ) plv8.elog(ERROR, 'value type error'); plv8.execute('SELECT set_config($1, $2, $3)', [ 'hasura.user', JSON.stringify({...hasura_session, 'x-hasura-user-id': this.linkId}), true]); hasura_session['x-hasura-user-id'] = this.linkId; const ids = {}; const checkedNumber = number?.data?.value ? number?.data?.value : number; const checkedString = string?.data?.value ? string?.data?.value : string; const checkedObject = object?.data?.value ? object?.data?.value : object; let valueIterator = 2; let insertLinkString = ${insertLinkStringCode}; let keysValue = 'VALUES ($1'; const keys = [type_id]; if (id) { keysValue = keysValue.concat(', $', valueIterator++); keys.push(id); } if (from_id) { keysValue = keysValue.concat(', $', valueIterator++); keys.push(from_id); } if (to_id) { keysValue = keysValue.concat(', $', valueIterator++); keys.push(to_id); } keysValue = keysValue.concat(') RETURNING id'); const linkid = plv8.execute(insertLinkString.concat(keysValue), keys)[0]?.id; const linkCheck = checkInsertPermission(linkid, this.linkId); if (!linkCheck) plv8.elog(ERROR, 'Insert not permitted'); const value = checkedNumber || checkedString || checkedObject; if (!value) return { data: [{ id: Number(linkid) }]}; const insertValueString = ${insertValueStringCode}; const valueId = plv8.execute(insertValueString, [ linkid, value ])[0]?.id; return { data: [{ id: Number(linkid) }]}; }, update: function(criteria, _set, options) { const exp = typeof criteria === 'object' ? criteria : typeof criteria === 'number' || typeof criteria === 'bigint' ? { id: criteria } : null; const { id, link_id, value } = criteria || {}; if (options?.table && !['strings', 'numbers', 'objects'].includes(options?.table)) plv8.elog(ERROR, 'update '.concat(options?.table, ' not permitted')); const { table } = options || {}; plv8.execute('SELECT set_config($1, $2, $3)', [ 'hasura.user', JSON.stringify({...hasura_session, 'x-hasura-user-id': this.linkId}), true]); hasura_session['x-hasura-user-id'] = this.linkId; const linkCheck = checkUpdatePermission(link_id, this.linkId); if (!linkCheck) plv8.elog(ERROR, 'Update not permitted'); const whereArr = []; const setArr = []; const whereFileds = Object.keys(exp).filter(key=>exp[key]); const variables = []; let counter = 1; for (let i = 0; i < whereFileds.length; i++, counter++ ){ whereArr.push(whereFileds[i].concat(' = $', counter)); variables.push(exp[whereFileds[i]]); } const setFileds = Object.keys(_set).filter(key=>_set[key]); for (let i = 0; i < setFileds.length; i++, counter++ ){ setArr.push(setFileds[i].concat(' = $', counter)); variables.push(_set[setFileds[i]]); } const where = whereArr.join(', '); const set = setArr.join(', '); const updateValueString = ${updateValueStringCode}; const links = plv8.execute(updateValueString, variables); return { data: links.map(id => Number(id))}; }, delete: function(criteria, options) { const _where = typeof criteria === 'object' ? criteria : typeof criteria === 'number' || typeof criteria === 'bigint' ? { id: criteria } : null; const { id } = _where || {}; if (!id) throw new Error('No valid id to delete'); const { table } = options || {}; if (table && !['links', 'strings', 'numbers', 'objects'].includes(table)) plv8.elog(ERROR, 'delete from '.concat(table, ' not permitted')); plv8.execute('SELECT set_config($1, $2, $3)', [ 'hasura.user', JSON.stringify({...hasura_session, 'x-hasura-user-id': this.linkId}), true]); hasura_session['x-hasura-user-id'] = this.linkId; const linkCheck = checkDeleteLinkPermission(id, this.linkId, table); if (!linkCheck) plv8.elog(ERROR, 'Delete not permitted'); const deleteString = ${deleteStringCode}; let linkid; if (table) { const deleteStringTable = ${deleteStringTableCode}; linkid = plv8.execute(deleteStringTable, [ id ])[0].id; } else { linkid = plv8.execute(deleteString, [ id ])[0].id; } return { data: [{ id: Number(linkid) }]}; }, login: function(options) { let { token, linkId } = options; if (!token && !linkId) plv8.elog(ERROR, 'No token and no linkId provided'); if (token && typeof token !== 'string' || linkId && typeof linkId !== 'number') plv8.elog(ERROR, 'Options validation failed'); if (token && !linkId) linkId = plv8.execute('SELECT ${LINKS_TABLE_NAME}__parse__jwt($1)', [token])[0]?.links__parse__jwt?.payload?.['https://hasura.io/jwt/claims']?.['x-hasura-user-id']; if (linkId) { this.linkId = Number(linkId); hasura_session['x-hasura-user-id'] = linkId; return ({ linkId, token }) } return ({ error: 'no link found'}); }, new: function(options) { let { token, linkId } = options; if (!token && !linkId) plv8.elog(ERROR, 'No token and no linkId provided'); if ( token && typeof token !== 'string' || linkId && typeof linkId !== 'number') plv8.elog(ERROR, 'Options validation failed'); if (token && !linkId) linkId = plv8.execute('SELECT ${LINKS_TABLE_NAME}__parse__jwt($1)', [token])[0]?.links__parse__jwt?.payload?.['https://hasura.io/jwt/claims']?.['x-hasura-user-id']; return deepFabric(linkId, hasura_session); }, can: function(objectIds, subjectIds, actionIds, userIds = this.linkId) { const where = {}; if (objectIds) where.object_id = typeof(objectIds) === 'number' ? +objectIds : { in: objectIds }; if (subjectIds) where.subject_id = typeof(subjectIds) === 'number' ? +subjectIds : { in: subjectIds }; if (actionIds) where.action_id = typeof(actionIds) === 'number' ? +actionIds : { in: actionIds }; const result = this.select(where, { table: 'can', returning: 'rule_id' }); return !!result?.data?.length; } }; // if (deep.can(deep.linkId, deep.linkId, deep.id('@deep-foundation/unsafe', 'AllowUnsafe'))) { deep.unsafe = unsafe; // } return deep; }`; const triggerFunctionFabric = (handleOperationTypeId, valueTrigger) => ` const checkInsertPermission = ${checkInsertPermissionCode}; const checkUpdatePermission = ${checkUpdatePermissionCode}; const checkDeleteLinkPermission = ${checkDeleteLinkPermissionCode}; const fillValueByLinks = ${fillValueByLinksCode}; const deepFabric = ${deepFabric}; const prepare = plv8.find_function("${LINKS_TABLE_NAME}__sync__handlers__prepare__function"); let data; let prepared; const hasura_session = JSON.parse(plv8.execute("select current_setting('hasura.user', 't')")[0].current_setting); const default_role = hasura_session['x-hasura-role']; const default_user_id = hasura_session['x-hasura-user-id']; if (${valueTrigger}){ const linkId = NEW?.link_id || OLD?.link_id; const link = plv8.execute("select * from links where id = $1", [ linkId ])[0]; prepared = link ? prepare(link, ${handleOperationTypeId}) : []; data = { oldLink: { id: Number(link?.id), from_id: Number(link?.from_id), to_id: Number(link?.to_id), type_id: Number(link?.type_id), value: OLD ? { id: Number(OLD.id), link_id: Number(OLD.link_id), value: typeof OLD.value === 'number' ? Number(OLD.value) : OLD.value } : undefined }, newLink: { id: Number(link?.id), from_id: Number(link?.from_id), to_id: Number(link?.to_id), type_id: Number(link?.type_id), value: NEW ? { id: Number(NEW.id), link_id: Number(NEW.link_id), value: typeof NEW.value === 'number' ? Number(NEW.value) : NEW.value } : undefined }, }; if (default_user_id) data.triggeredByLinkId = Number(default_user_id); } else { const link = { id: NEW?.id || OLD?.id }; fillValueByLinks([link]); prepared = prepare(${handleOperationTypeId === handleDeleteId ? 'OLD' : 'NEW'}, ${handleOperationTypeId}); data = { oldLink: OLD ? { id: Number(OLD?.id), from_id: Number(OLD?.from_id), to_id: Number(OLD?.to_id), type_id: Number(OLD?.type_id), value: link?.value } : undefined, newLink: NEW ? { id: Number(NEW?.id), from_id: Number(NEW?.from_id), to_id: Number(NEW?.to_id), type_id: Number(NEW?.type_id), value: link?.value } : undefined, }; if (default_user_id) data.triggeredByLinkId = Number(default_user_id); } const require = (package) => { const packageFabric = plv8.find_function("${LINKS_TABLE_NAME}__sync__handlers__".concat(package, '__package')); return packageFabric(); } for (let i = 0; i < prepared.length; i++) { (()=>{ const deep = deepFabric(prepared[i].owner, hasura_session); const prepare = undefined; const fillValueByLinks = undefined; const checkSelectPermission = undefined; const checkInsertPermission = undefined; const checkUpdatePermission = undefined; const checkDeleteLinkPermission = undefined; const default_role = undefined; const default_user_id = undefined; const func = eval(prepared[i].value); func({ deep, require, data }); })() }; if (hasura_session['x-hasura-role'] !== default_role || hasura_session['x-hasura-user-id'] !== default_user_id){ if (default_role) hasura_session['x-hasura-role'] = default_role; if (default_user_id) hasura_session['x-hasura-user-id'] = default_user_id; plv8.execute('SELECT set_config($1, $2, $3)', ['hasura.user', JSON.stringify(hasura_session), true]); } return NEW; `; const deepClientFunction = ` const checkInsertPermission = ${checkInsertPermissionCode}; const checkUpdatePermission = ${checkUpdatePermissionCode}; const checkDeleteLinkPermission = ${checkDeleteLinkPermissionCode}; const hasura_session = JSON.parse(plv8.execute("select current_setting('hasura.user', 't')")[0].current_setting); const default_role = hasura_session['x-hasura-role']; const default_user_id = hasura_session['x-hasura-user-id']; const deep = (${deepFabric})(Number(clientlinkid), hasura_session); const result = operation === 'id' || operation === 'update' || operation === 'objectSet' || operation === 'objectGet' ? deep[operation](...args) : operation === 'unsafe' ? deep[operation].plv8.execute(...args) : deep[operation](args, options); if (hasura_session['x-hasura-role'] !== default_role || hasura_session['x-hasura-user-id'] !== default_user_id){ if (default_role) hasura_session['x-hasura-role'] = default_role; if (default_user_id) hasura_session['x-hasura-user-id'] = default_user_id; plv8.execute('SELECT set_config($1, $2, $3)', ['hasura.user', JSON.stringify(hasura_session), true]); } return result;`; export const createPrepareFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__prepare__function(link jsonb, handletypeid bigint) RETURNS jsonb AS $$ ${prepareFunction} $$ LANGUAGE plv8;`; export const dropPrepareFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__prepare__function CASCADE;`; export const createDeepClientFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__deep__client(clientLinkId bigint, operation text, args jsonb, options jsonb) RETURNS jsonb AS $$ ${deepClientFunction} $$ LANGUAGE plv8;`; export const dropDeepClientFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__deep__client CASCADE;`; export const createDecodeBase64urlFunction = sql `CREATE OR REPLACE function ${LINKS_TABLE_NAME}__decode__base64url(text) returns bytea as $$ ${decodeBase64urlCode} $$ language sql strict immutable;`; export const dropDecodeBase64urlFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__decode__base64url CASCADE;`; export const createParseJwtFunction = sql `CREATE OR REPLACE function ${LINKS_TABLE_NAME}__parse__jwt(text) returns JSONB as $$ ${parseJwtCode} $$language plpgsql;`; export const dropParseJwtFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__parse__jwt CASCADE;`; export const createSyncInsertTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__insert__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleInsertId, false)} $$ LANGUAGE plv8;`; export const createSyncInsertTrigger = sql `CREATE TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__insert__trigger AFTER INSERT ON "links" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__insert__trigger__function();`; export const dropSyncInsertTrigger = sql `DROP TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__insert__trigger ON "${LINKS_TABLE_NAME}";`; export const dropSyncInsertTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__insert__trigger__function CASCADE;`; export const createSyncDeleteTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__delete__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleDeleteId, false)} $$ LANGUAGE plv8;`; export const createSyncDeleteTrigger = sql `CREATE TRIGGER a_${LINKS_TABLE_NAME}__sync__handlers__delete__trigger AFTER DELETE ON "links" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__delete__trigger__function();`; export const dropSyncDeleteTrigger = sql `DROP TRIGGER a_${LINKS_TABLE_NAME}__sync__handlers__delete__trigger ON "${LINKS_TABLE_NAME}";`; export const dropSyncDeleteTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__delete__trigger__function CASCADE;`; export const createSyncUpdateTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__update__handler__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleUpdateId, false)} $$ LANGUAGE plv8;`; export const createSyncUpdateTrigger = sql `CREATE TRIGGER a_${LINKS_TABLE_NAME}__sync__update__handler__trigger AFTER UPDATE ON "links" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__update__handler__function();`; export const dropSyncUpdateTrigger = sql `DROP TRIGGER a_${LINKS_TABLE_NAME}__sync__update__handler__trigger ON "${LINKS_TABLE_NAME}";`; export const dropSyncUpdateTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__update__handler__function CASCADE;`; export const createSyncInsertStringsTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__insert__strings__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleUpdateId, true)} $$ LANGUAGE plv8;`; export const createSyncInsertStringsTrigger = sql `CREATE TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__insert__strings__trigger AFTER INSERT ON "strings" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__insert__strings__trigger__function();`; export const dropSyncInsertStringsTrigger = sql `DROP TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__insert__strings__trigger ON "strings";`; export const dropSyncInsertStringsTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__insert__strings__trigger__function CASCADE;`; export const createSyncUpdateStringsTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__update__strings__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleUpdateId, true)} $$ LANGUAGE plv8;`; export const createSyncUpdateStringsTrigger = sql `CREATE TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__update__strings__trigger AFTER UPDATE ON "strings" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__update__strings__trigger__function();`; export const dropSyncUpdateStringsTrigger = sql `DROP TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__update__strings__trigger ON "strings";`; export const dropSyncUpdateStringsTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__update__strings__trigger__function CASCADE;`; export const createSyncDeleteStringsTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__delete__strings__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleUpdateId, true)} $$ LANGUAGE plv8;`; export const createSyncDeleteStringsTrigger = sql `CREATE TRIGGER a_${LINKS_TABLE_NAME}__sync__handlers__delete__strings__trigger AFTER DELETE ON "strings" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__delete__strings__trigger__function();`; export const dropSyncDeleteStringsTrigger = sql `DROP TRIGGER a_${LINKS_TABLE_NAME}__sync__handlers__delete__strings__trigger ON "strings";`; export const dropSyncDeleteStringsTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__delete__strings__trigger__function CASCADE;`; export const createSyncInsertNumbersTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__insert__numbers__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleUpdateId, true)} $$ LANGUAGE plv8;`; export const createSyncInsertNumbersTrigger = sql `CREATE TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__insert__numbers__trigger AFTER INSERT ON "numbers" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__insert__numbers__trigger__function();`; export const dropSyncInsertNumbersTrigger = sql `DROP TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__insert__numbers__trigger ON "numbers";`; export const dropSyncInsertNumbersTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__insert__numbers__trigger__function CASCADE;`; export const createSyncUpdateNumbersTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__update__numbers__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleUpdateId, true)} $$ LANGUAGE plv8;`; export const createSyncUpdateNumbersTrigger = sql `CREATE TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__update__numbers__trigger AFTER UPDATE ON "numbers" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__update__numbers__trigger__function();`; export const dropSyncUpdateNumbersTrigger = sql `DROP TRIGGER z_${LINKS_TABLE_NAME}__sync__handlers__update__numbers__trigger ON "numbers";`; export const dropSyncUpdateNumbersTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__update__numbers__trigger__function CASCADE;`; export const createSyncDeleteNumbersTriggerFunction = sql `CREATE OR REPLACE FUNCTION ${LINKS_TABLE_NAME}__sync__handlers__delete__numbers__trigger__function() RETURNS TRIGGER AS $$ ${triggerFunctionFabric(handleUpdateId, true)} $$ LANGUAGE plv8;`; export const createSyncDeleteNumbersTrigger = sql `CREATE TRIGGER a_${LINKS_TABLE_NAME}__sync__handlers__delete__numbers__trigger AFTER DELETE ON "numbers" FOR EACH ROW EXECUTE PROCEDURE ${LINKS_TABLE_NAME}__sync__handlers__delete__numbers__trigger__function();`; export const dropSyncDeleteNumbersTrigger = sql `DROP TRIGGER a_${LINKS_TABLE_NAME}__sync__handlers__delete__numbers__trigger ON "numbers";`; export const dropSyncDeleteNumbersTriggerFunction = sql `DROP FUNCTION IF EXISTS ${LINKS_TABLE_NAME}__sync__handlers__delete__numbers__trigger__function CASCADE;`; export const createSyncInsertObjectsTrigg