@opengis/fastify-table
Version:
core-plugins
202 lines (200 loc) • 7.76 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] === "" ? null : data[el]]; // ensure '' on number does not throw
})
.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((el) => el.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?.status !== "end") {
rclient.incr(`pg:${table}:crud`);
}
if (!isClient) {
await client.query("commit;");
}
return { ...(res || {}), id };
}
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();
}
}
}