@opengis/fastify-table 1.1.17 → 1.1.19

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.
package/Changelog.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # fastify-table
2
2
 
3
+ ## 1.1.19 - 01.10.2024
4
+
5
+ - custom hook support - addHook, applyHook
6
+
3
7
  ## 1.1.17 - 30.09.2024
4
8
 
5
9
  - add relkinds to pg
@@ -0,0 +1,8 @@
1
+ import hookList from './hookList.js';
2
+
3
+ export default function addHook(name, fn) {
4
+ if (!hookList[name]) {
5
+ hookList[name] = [];
6
+ }
7
+ hookList[name].push(fn);
8
+ }
@@ -0,0 +1,24 @@
1
+ import config from '../../config.js';
2
+ import hookList from './hookList.js';
3
+
4
+ export default async function applyHook(name, data) {
5
+ const debug = config.local || config.debug;
6
+ if (debug) console.log('applyHook', name);
7
+ if (!hookList[name]?.length) return null;
8
+ const result = {};
9
+ await Promise.all(hookList[name].map(async (hook) => {
10
+ const hookData = await hook({ ...data, config });
11
+ if (hookData) {
12
+ if (debug) console.log('applyHook', name, hookData);
13
+ Object.assign(result, hookData);
14
+ }
15
+ })).catch((err) => {
16
+ console.error('applyHook', name, err.toString());
17
+ });
18
+
19
+ if (Object.keys(result).length) {
20
+ return result;
21
+ }
22
+
23
+ return null;
24
+ }
@@ -0,0 +1 @@
1
+ export default {};
package/hook/index.js ADDED
@@ -0,0 +1,6 @@
1
+ import addHook from './funcs/addHook.js';
2
+ import applyHook from './funcs/applyHook.js';
3
+
4
+ export default async function plugin(fastify, opts) {
5
+ fastify.decorate('hook', { add: addHook, apply: applyHook });
6
+ }
package/index.js CHANGED
@@ -14,6 +14,7 @@ import crudPlugin from './crud/index.js';
14
14
  import policyPlugin from './policy/index.js';
15
15
  import utilPlugin from './util/index.js';
16
16
  import cronPlugin from './cron/index.js';
17
+ import hookPlugin from './hook/index.js';
17
18
 
18
19
  import pgClients from './pg/pgClients.js';
19
20
 
@@ -92,6 +93,7 @@ async function plugin(fastify, opt) {
92
93
  widgetPlugin(fastify, opt);
93
94
  utilPlugin(fastify, opt);
94
95
  cronPlugin(fastify, opt);
96
+ hookPlugin(fastify, opt);
95
97
  }
96
98
  export default fp(plugin);
97
99
  // export { rclient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.1.17",
3
+ "version": "1.1.19",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "main": "index.js",
@@ -6,16 +6,24 @@ import getAccess from '../../crud/funcs/getAccess.js';
6
6
  import setToken from '../../crud/funcs/setToken.js';
7
7
  import gisIRColumn from './utils/gisIRColumn.js';
8
8
 
9
+ import { applyHook } from '../../utils.js';
10
+
9
11
  const maxLimit = 100;
10
- export default async function dataAPI({
11
- pg, params, funcs = {}, query = {}, opt = {}, uid: uid1, req, session,
12
- }) {
12
+ export default async function dataAPI(req) {
13
+ const {
14
+ pg, params, funcs = {}, query = {}, opt = {}, uid: uid1, session,
15
+ } = req;
13
16
  const time = Date.now();
14
17
 
15
18
  const uid = session?.passport?.user?.uid || uid1 || query.uid || 0;
16
19
 
17
20
  const loadTable = await getTemplate('table', params.table);
18
21
 
22
+ const check = await applyHook('preData', { req });
23
+ if (check?.message && check?.status) {
24
+ return { message: check?.message, status: check?.status };
25
+ }
26
+
19
27
  if (!loadTable) { return { message: 'template not found', status: 404 }; }
20
28
 
21
29
  const {
@@ -104,5 +112,7 @@ export default async function dataAPI({
104
112
  });
105
113
  }
106
114
 
107
- return res;
115
+ const result = await applyHook('afterData', { req, table: loadTable.table, res });
116
+
117
+ return result || res;
108
118
  }
@@ -1,9 +1,11 @@
1
1
  import getTemplate from './utils/getTemplate.js';
2
2
  import getMeta from '../../pg/funcs/getMeta.js';
3
3
 
4
+ import { applyHook } from '../../utils.js';
5
+
4
6
  export default async function tableAPI(req) {
5
7
  const {
6
- pg, params = {}, query = {}, opt = {},
8
+ pg, params = {}, query = {}, opt = {}, funcs,
7
9
  } = req;
8
10
  if (!params.id) return { message: 'not enough params', status: 400 };
9
11
 
@@ -12,6 +14,11 @@ export default async function tableAPI(req) {
12
14
  if (!pg.pk?.[opt?.table || params.table]) { return { message: 'not found', status: 404 }; }
13
15
  }
14
16
 
17
+ const check = await applyHook('preTable', { req });
18
+ if (check?.message && check?.status) {
19
+ return { message: check?.message, status: check?.status };
20
+ }
21
+
15
22
  const {
16
23
  table, /* columns, */ form,
17
24
  } = loadTable;
@@ -48,5 +55,6 @@ export default async function tableAPI(req) {
48
55
  }));
49
56
  }
50
57
 
51
- return rows?.[0] || {};
58
+ const res = await applyHook('afterTable', { req, table: loadTable?.table, rows });
59
+ return res || rows?.[0] || {};
52
60
  }
@@ -0,0 +1,96 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import path from 'node:path';
4
+ import pgClients from '../../pg/pgClients.js';
5
+ import init from '../../pg/funcs/init.js';
6
+
7
+ import build from '../../helper.js';
8
+ import config from '../config.js';
9
+
10
+ import { addTemplateDir, addHook } from '../../utils.js';
11
+
12
+ const prefix = config.prefix || '/api';
13
+ const table = 'gis.dataset.table';
14
+
15
+ test('applyHook to API data/table', async (t) => {
16
+ const app = await build(t);
17
+ const cwd = process.cwd();
18
+ await init(pgClients.client);
19
+ addTemplateDir(path.join(cwd, 'test/templates'));
20
+
21
+ addHook('preData', async ({ req }) => {
22
+ if (req.params?.table === `${table}1`) {
23
+ return { message: 'access restricted by hook', status: 403 };
24
+ }
25
+ return null;
26
+ });
27
+ addHook('preTable', async ({ req }) => {
28
+ const { params = {} } = req;
29
+ if (params?.table === `${table}1`) {
30
+ return { message: 'access restricted by hook', status: 403 };
31
+ }
32
+ return null;
33
+ });
34
+
35
+ addHook('afterData', async ({ res }) => {
36
+ Object.assign(res, { test: '1' });
37
+ });
38
+ addHook('afterTable', async ({ rows = [] }) => {
39
+ Object.assign(rows[0], { count: 1 });
40
+ });
41
+
42
+ await t.test('GET /data (preData ok)', async () => {
43
+ const res = await app.inject({
44
+ method: 'GET',
45
+ url: `${prefix}/data/${table}`,
46
+ });
47
+ const json = res.json();
48
+ assert.ok(json?.time, 'api error');
49
+ });
50
+ await t.test('GET /data (preData message)', async () => {
51
+ const res = await app.inject({
52
+ method: 'GET',
53
+ url: `${prefix}/data/${table}1`,
54
+ });
55
+ const json = res.json();
56
+ assert.ok(json.message === 'access restricted by hook', 'preData hook error');
57
+ });
58
+
59
+ await t.test('GET /data (afterData)', async () => {
60
+ const res = await app.inject({
61
+ method: 'GET',
62
+ url: `${prefix}/data/${table}`,
63
+ });
64
+ const json = res.json();
65
+ assert.ok(json?.test === '1', 'afterData hook error');
66
+ });
67
+
68
+ const { id = '1' } = pgClients.client?.pk['gis.dataset']
69
+ ? await pgClients.client.query('select dataset_id as id from gis.dataset limit 1')
70
+ .then((res) => res.rows?.[0] || {}) : {};
71
+ await t.test('GET /table/:id (preTable ok)', async () => {
72
+ const res = await app.inject({
73
+ method: 'GET',
74
+ url: `${prefix}/table/${table}1/${id}`,
75
+ });
76
+ const json = res.json();
77
+ assert.ok(json.message === 'access restricted by hook', 'preTable hook error');
78
+ });
79
+ await t.test('GET /table/:id (preTable message)', async () => {
80
+ const res = await app.inject({
81
+ method: 'GET',
82
+ url: `${prefix}/table/${table}1/${id}`,
83
+ });
84
+ const json = res.json();
85
+ assert.ok(json.message === 'access restricted by hook', 'preTable hook error');
86
+ });
87
+
88
+ await t.test('GET /table/:id (afterTable)', async () => {
89
+ const res = await app.inject({
90
+ method: 'GET',
91
+ url: `${prefix}/table/${table}/${id}`,
92
+ });
93
+ const json = res.json();
94
+ assert.equal(json?.count, 1);
95
+ });
96
+ });
package/utils.js CHANGED
@@ -24,6 +24,8 @@ import gisIRColumn from './table/controllers/utils/gisIRColumn.js';
24
24
  import getMeta from './pg/funcs/getMeta.js';
25
25
  import getAccess from './crud/funcs/getAccess.js';
26
26
  import getSelectVal from './table/funcs/metaFormat/getSelectVal.js';
27
+ import applyHook from './hook/funcs/applyHook.js';
28
+ import addHook from './hook/funcs/addHook.js';
27
29
 
28
30
  export default null;
29
31
  export {
@@ -47,4 +49,6 @@ export {
47
49
  getMeta,
48
50
  getAccess,
49
51
  getSelectVal,
52
+ applyHook,
53
+ addHook,
50
54
  };
@@ -33,7 +33,11 @@ export default async function widgetGet({
33
33
  left join lateral(
34
34
  select change_data_id, entity_key, value_new, value_old from log.table_changes_data where change_id=a.change_id
35
35
  )b on 1=1
36
- where entity_id=$1 and b.change_data_id is not null order by cdate desc limit 100`,
36
+ where (entity_id=$1 or entity_id in (
37
+ select communication_id as comments from crm.communications where entity_id=$1
38
+ union all select file_id from crm.files where entity_id=$1
39
+ union all select checklist_id from crm.checklists where entity_id=$1)
40
+ ) and b.change_data_id is not null order by cdate desc limit 100`,
37
41
 
38
42
  checklist: pg.pk['admin.users']
39
43
  ? `SELECT checklist_id, entity_id, subject, is_done, done_date, c.uid, c.cdate, coalesce(user_name,' ')||' '||coalesce(sur_name,'') as username,