@opengis/fastify-table 2.0.149 → 2.0.151

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import getRedis from "../../redis/funcs/getRedis.js";
3
3
  import pgClients from "../../pg/pgClients.js";
4
4
  import getTemplate from "../../table/funcs/getTemplate.js";
5
5
  import config from "../../../../config.js";
6
- // import logChanges from "./utils/logChanges.js";
6
+ import logChanges from "./utils/logChanges.js";
7
7
  import getInsertQuery from "./utils/getInsertQuery.js";
8
8
  import logger from "../../logger/getLogger.js";
9
9
  import extraData from "../../extra/extraData.js";
@@ -95,16 +95,16 @@ export default async function dataInsert({ id, table: table1, referer, data, pg:
95
95
  Object.assign(res.rows[0], { [key]: parentRows.filter(Boolean) });
96
96
  }));
97
97
  }
98
- // await logChanges({
99
- // pg: client,
100
- // table,
101
- // tokenData,
102
- // referer,
103
- // data,
104
- // id: id1,
105
- // uid,
106
- // type: "INSERT",
107
- // });
98
+ await logChanges({
99
+ pg: client,
100
+ table,
101
+ tokenData,
102
+ referer,
103
+ data,
104
+ id: id1,
105
+ uid,
106
+ type: "INSERT",
107
+ });
108
108
  if (config.redis && rclient?.status !== "end") {
109
109
  rclient.incr(`pg:${client.options?.database}:${table}:crud`);
110
110
  }
@@ -16,6 +16,7 @@ export default function logChanges({ pg, table: table1, tokenData, referer, id,
16
16
  change_type: string;
17
17
  old: any;
18
18
  new: any;
19
+ titles: Record<string, string>;
19
20
  error?: undefined;
20
21
  } | {
21
22
  error: any;
@@ -26,5 +27,6 @@ export default function logChanges({ pg, table: table1, tokenData, referer, id,
26
27
  change_id?: undefined;
27
28
  old?: undefined;
28
29
  new?: undefined;
30
+ titles?: undefined;
29
31
  } | null>;
30
32
  //# sourceMappingURL=logChanges.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logChanges.d.ts","sourceRoot":"","sources":["../../../../../../server/plugins/crud/funcs/utils/logChanges.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA4B5D,wBAA8B,UAAU,CAAC,EACvC,EAAE,EACF,KAAK,EAAE,MAAM,EACb,SAAS,EACT,OAAO,EACP,EAAE,EACF,IAAI,EACJ,GAAO,EACP,IAAI,GACL,EAAE;IACD,EAAE,EAAE,UAAU,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;;;;;;;;;;;;;;;;;;UA6JA"}
1
+ {"version":3,"file":"logChanges.d.ts","sourceRoot":"","sources":["../../../../../../server/plugins/crud/funcs/utils/logChanges.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AA8C5D,wBAA8B,UAAU,CAAC,EACvC,EAAE,EACF,KAAK,EAAE,MAAM,EACb,SAAS,EACT,OAAO,EACP,EAAE,EACF,IAAI,EACJ,GAAO,EACP,IAAI,GACL,EAAE;IACD,EAAE,EAAE,UAAU,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd;;;;;;;;;;;;;;;;;;;;UAiNA"}
@@ -1,6 +1,7 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import getTemplate from "../../../table/funcs/getTemplate.js";
3
3
  import metaFormat from "../../../table/funcs/metaFormat/index.js";
4
+ import getMeta from "../../../pg/funcs/getMeta.js";
4
5
  const defaultTitles = {
5
6
  editor_date: "Дата оновлення",
6
7
  editor_id: "Редактор",
@@ -14,15 +15,26 @@ const defaultTitles = {
14
15
  size: "Розмір файлу",
15
16
  ext: "Розширення файлу",
16
17
  };
17
- function getValue(val, tableName) {
18
+ // skip log of system columns changes made by CRUD functions - log only changes of data
19
+ const systemColumns = [
20
+ "cdate",
21
+ "editor_date",
22
+ "created_at",
23
+ "updated_at",
24
+ "editor_id",
25
+ "uid",
26
+ "updated_by",
27
+ "created_by",
28
+ ];
29
+ function getValue(val, tableName, columnTypes) {
30
+ if (typeof val === "boolean")
31
+ return val;
18
32
  if (!val)
19
33
  return null;
20
34
  if (["crm.files"].includes(tableName)) {
21
35
  return typeof val === "object" ? JSON.stringify(val) : val;
22
36
  }
23
- return typeof val === "object"
24
- ? JSON.stringify(val)?.substring?.(0, 30)
25
- : val?.toString?.()?.substring?.(0, 30);
37
+ return typeof val === "object" ? null : val?.toString?.()?.substring?.(0, 30);
26
38
  }
27
39
  // extract titles and cls from form schema
28
40
  // alt: extract table template from referer -> form
@@ -75,35 +87,74 @@ export default async function logChanges({ pg, table: table1, tokenData, referer
75
87
  const body = await getTemplate("form", tokenData?.form);
76
88
  const schema = body?.schema || body || {};
77
89
  const titles = Object.keys(schema).reduce((acc, curr) => Object.assign(acc, { [curr]: schema[curr].title || schema[curr].ua }), {});
90
+ Object.assign(titles, defaultTitles);
78
91
  const cls = Object.keys(schema)
79
92
  .filter((el) => schema[el]?.data)
80
93
  .reduce((acc, curr) => Object.assign(acc, { [curr]: schema[curr].data }), {});
81
- const data1 = data
82
- ? await metaFormat({
83
- rows: [data],
84
- cls,
85
- sufix: false,
86
- reassign: false,
87
- }, pg)
88
- : null;
89
- const newObj = Object.fromEntries(Object.entries(data1?.[0] || {}).map((el) => [
90
- [titles[el[0]] || defaultTitles[el[0]] || el[0]],
91
- el[1],
92
- ]));
93
- const changesData = Object.keys(newObj || {})
94
- .map((el) => ({
94
+ // const original = JSON.parse(JSON.stringify(data));
95
+ const row = await metaFormat({
96
+ rows: [data].filter(Boolean),
97
+ cls,
98
+ sufix: false,
99
+ reassign: false,
100
+ }, pg).then((el) => el[0]);
101
+ const newObj1 = {};
102
+ const meta = await getMeta({ pg, table });
103
+ const columnTypes = (meta?.columns || []).reduce((acc, curr) => ({
104
+ ...acc,
105
+ [curr.name]: pg?.pgType?.[curr.dataTypeID],
106
+ }), {});
107
+ if (type !== "DELETE") {
108
+ Object.keys(row || {})
109
+ .filter((key) => !systemColumns.includes(key))
110
+ .reduce((acc, curr) => {
111
+ Object.assign(newObj1, {
112
+ [titles[curr] || curr]: columnTypes[curr] === "geometry"
113
+ ? {}
114
+ : {
115
+ value: getValue(row[curr], table, columnTypes),
116
+ hash: typeof row[curr] === "boolean" || row[curr]
117
+ ? createHash("md5")
118
+ .update(JSON.stringify(row[curr]))
119
+ .digest("hex")
120
+ : null,
121
+ },
122
+ });
123
+ return acc;
124
+ });
125
+ }
126
+ await Promise.all(Object.keys(row)
127
+ .filter((key) => row[key] && columnTypes[key] === "geometry")
128
+ .map(async (key) => pg
129
+ .query(typeof row[key] === "string" // binary geometry as string via update?
130
+ ? "select st_asgeojson(st_pointonsurface($1))::json"
131
+ : "select st_asgeojson(st_pointonsurface(st_geomfromgeojson($1)))::json", [row[key]])
132
+ .then((el) => ({ key, geometry: el.rows[0].st_asgeojson })))).then((r) => r.forEach((curr) => {
133
+ const value = curr.geometry.coordinates.join(",");
134
+ const hash = createHash("md5")
135
+ .update(JSON.stringify(value))
136
+ .digest("hex");
137
+ if (titles[curr.key]) {
138
+ newObj1[titles[curr.key]] = { value, hash };
139
+ }
140
+ else {
141
+ newObj1[curr.key] = { value, hash };
142
+ }
143
+ }));
144
+ const changesData = Object.keys(newObj1 || {})
145
+ .filter((key) => typeof newObj1?.[key] === "boolean" || newObj1?.[key])
146
+ .filter((key) => old[key] ? old[key].hash !== newObj1[key]?.hash : newObj1[key]?.hash)
147
+ .map((key) => ({
95
148
  change_id: changeId,
96
- entity_key: el,
97
- value_old: getValue(old?.[el]?.value, table),
98
- value_new: type === "DELETE" ? null : getValue(newObj?.[el], table),
99
- value_hash: newObj?.[el]
100
- ? createHash("md5").update(JSON.stringify(newObj?.[el])).digest("hex")
101
- : null,
149
+ entity_key: key,
150
+ value_old: old?.[key]?.value,
151
+ value_new: newObj1?.[key]?.value,
152
+ value_hash: newObj1?.[key]?.hash,
102
153
  uid,
103
- }))
104
- .filter((el) => (newObj?.[el.entity_key] ||
105
- getValue(old?.[el.entity_key]?.value, table)) &&
106
- old?.[el.entity_key]?.hash !== el.value_hash);
154
+ }));
155
+ if (process.env.NODE_ENV === "test") {
156
+ console.log("changesData", changesData);
157
+ }
107
158
  const res = await Promise.all(changesData.map(async (el) => {
108
159
  const insertQuery = `insert into log.table_changes_data (${Object.entries(el)
109
160
  ?.map((key) => `"${key[0]}"`)
@@ -111,18 +162,18 @@ export default async function logChanges({ pg, table: table1, tokenData, referer
111
162
  values (${Object.entries(el)
112
163
  ?.map((key, i) => `$${i + 1}`)
113
164
  .join(",")}) returning *`;
114
- const { rows = [] } = (await pg.query(insertQuery, [
165
+ const row1 = await pg
166
+ .query(insertQuery, [
115
167
  ...Object.entries(el).map((el1) => el1[1] &&
116
168
  typeof el1[1] === "object" &&
117
169
  (!Array.isArray(el1[1]) || typeof el1[1]?.[0] === "object")
118
170
  ? JSON.stringify(el1[1])
119
171
  : el1[1]),
120
- ])) || {};
121
- return rows[0];
172
+ ])
173
+ .then((el) => el.rows?.[0] || {});
174
+ return row1;
122
175
  }));
123
- const newData = type === "DELETE"
124
- ? {}
125
- : (Array.isArray(res) ? res : [res]).reduce((acc, curr) => Object.assign(acc, { [curr.entity_key]: curr.value_new }), {});
176
+ const newData = (Array.isArray(res) ? res : [res]).reduce((acc, curr) => Object.assign(acc, { [curr.entity_key]: curr.value_new }), {});
126
177
  // console.log('logChanges OK', type);
127
178
  return {
128
179
  change_id: changeId,
@@ -132,6 +183,7 @@ export default async function logChanges({ pg, table: table1, tokenData, referer
132
183
  change_type: type,
133
184
  old,
134
185
  new: newData,
186
+ titles,
135
187
  };
136
188
  }
137
189
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "2.0.149",
3
+ "version": "2.0.151",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [