@opengis/fastify-table
Version:
core-plugins
180 lines (155 loc) • 6.68 kB
JavaScript
import getPG from '../../pg/funcs/getPG.js';
import getMeta from '../../pg/funcs/getMeta.js';
import getRedis from '../../redis/funcs/getRedis.js';
import pgClients from '../../pg/pgClients.js';
import getTemplate from '../../table/funcs/getTemplate.js';
import config from '../../../../config.js';
import extraData from '../../extra/extraData.js';
import logChanges from './utils/logChanges.js';
import logger from '../../logger/getLogger.js';
import getInsertQuery from './utils/getInsertQuery.js';
const rclient = getRedis();
const srids = {};
function assignValue(key, i, srid = 4326, columnType = 'text') {
if (key === 'geom' && columnType === 'geometry') {
return `"${key}"=st_setsrid(st_geomfromgeojson($${i + 2}::json),4326)`;
}
if (key?.includes('geom') && columnType === 'geometry') {
return `"${key}"=st_setsrid(st_geomfromgeojson($${i + 2}::json),${srid})`;
}
return `"${key}"=$${i + 2}`;
}
export default async function dataUpdate({
table, tokenData, referer, id, data, pg: pg1, uid,
}) {
if (!data || !table || !id) return null;
const pg = pg1 || getPG({ name: 'client' });
if (!pg) return null;
// pg client single transaction support
if (!pg?.pk && config.pg) {
pg.options = pgClients.client?.options;
pg.tlist = pgClients.client?.tlist;
pg.pgType = pgClients.client?.pgType;
pg.relkinds = pgClients.client?.relkinds;
pg.pk = pgClients.client?.pk;
}
const { columns, pk } = await getMeta({ pg, table });
if (!columns) return null;
const names = columns.map((el) => el.name);
const types = columns.reduce((acc, { name, dataTypeID }) => ({ ...acc, [name]: pg.pgType?.[dataTypeID] }), {});
const filterData = Object.keys(data)
.filter((el) => (/* typeof data[el] === 'boolean' ? true : data[el] && */ names?.includes(el) && !['editor_date', 'editor_id', 'updated_by', 'updated_at'].includes(el)));
const systemColumns = [['editor_date', 'now()'], ['updated_at', 'now()'], uid ? ['editor_id', `'${uid.replace(/'/g, "''")}'`] : null, uid ? ['updated_by', `'${uid.replace(/'/g, "''")}'`] : null].filter((el) => el && names.includes(el[0])).map((el) => `${el[0]} = ${el[1]}`).join(',');
const filterValue = filterData.map((el) => {
const { dataTypeID = 25 } = columns.find((col) => col?.name === el) || {};
if (pg.pgType[dataTypeID]?.endsWith('[]') && ['string', 'number'].includes(typeof data[el])) {
Object.assign(data, { [el]: data[el].split(',') });
}
return [el, data[el]];
}).map((el) => (typeof el[1] === 'object' && types[el[0]]?.includes?.('json') && el[1] ? JSON.stringify(el[1]) : el[1]));
// update geometry with srid
if (!srids[table] && pg.tlist?.includes('public.geometry_columns')) {
const { srids1 } = await pg.query(`select json_object_agg(_table,rel) as srids1 from (
select f_table_schema||'.'||f_table_name as _table,
json_object_agg(f_geometry_column, case when srid = 0 then 4326 else srid end) as rel
from public.geometry_columns group by f_table_schema||'.'||f_table_name
)q`).then((res1) => res1.rows?.[0] || {});
Object.assign(srids, srids1);
}
const updateQuery = `UPDATE ${table} SET ${systemColumns ? `${systemColumns}${filterData?.length ? ',' : ''}` : ''}
${filterData?.map((key, i) => assignValue(key, i, srids[table]?.[key] || 4326, pg.pgType?.[columns.find(col => col.name === key)?.dataTypeID || '']))?.join(',')}
WHERE ${pk}::text = $1::text returning *`;
// console.log(updateQuery, filterValue);
// for transactions
const isClient = typeof pg.query === 'function' && typeof pg.release === 'function';
const client = isClient ? pg : await pg.connect();
if (isClient || !client.pk) {
client.options = pg.options;
client.tlist = pg.tlist;
client.pgType = pg.pgType;
client.relkinds = pg.relkinds;
client.pk = pg.pk;
}
try {
if (!isClient) {
await client.query('begin;');
}
const res = await client.query(updateQuery, [id, ...filterValue])
.catch(err => {
logger.file('crud/update', {
error: err.toString(),
stack: err.stack,
table,
id,
referer,
uid,
data,
q: updateQuery,
});
throw err;
})
.then(el => el?.rows?.[0]) || {};
await extraData({
table,
form: tokenData?.form,
id,
data,
uid,
row: res,
}, client);
// foreign key dataTable (table + parent_id)
const formData = tokenData?.form ? (await getTemplate('form', tokenData.form) || {}) : {};
const schema = formData?.schema || formData;
const parentKeys = Object.keys(schema || {})?.filter((key) => Array.isArray(data[key]) && schema?.[key]?.table && schema?.[key]?.parent_id /* && body[key].length */);
if (parentKeys?.length) {
await Promise.all(parentKeys?.map(async (key) => {
const objId = data[schema[key].parent_id] || data?.id || res?.[schema[key]?.parent_id] || res?.[pg.pk?.[table] || ''];
// delete old extra data
await client.query(`delete from ${schema[key].table} where ${schema[key].parent_id}=$1`, [objId]); // rewrite?
// insert new extra data
if (Array.isArray(data[key]) && data[key]?.length) {
const parentKey = schema[key].parent_id;
const extraRows = await Promise.all(data[key]?.map?.(async (row) => {
Object.assign(row, { [parentKey]: objId });
const parentRes = await getInsertQuery({
pg: client, table: schema[key].table, data: row, uid,
});
if (!parentRes?.insertQuery || !parentRes?.args?.length) return null;
const { rows = [] } = await client.query(parentRes.insertQuery, parentRes.args);
return rows[0];
}));
Object.assign(res, { [key]: extraRows.filter(Boolean) });
}
}));
}
await logChanges({
pg,
table,
tokenData,
referer,
data,
id,
uid,
type: 'UPDATE',
});
if (config.redis) { rclient.incr(`pg:${table}:crud`); }
if (!isClient) {
await client.query('commit;');
}
return res || {};
}
catch (err) {
logger.file('crud/update', {
error: err.toString(), stack: err.stack, table, id, referer, uid, form: tokenData?.form,
});
if (!isClient) {
await client.query('rollback;');
}
throw err;
}
finally {
if (!isClient) {
client.release();
}
}
}