@opengis/fastify-table 1.0.81 → 1.0.83

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,9 +1,17 @@
1
1
  # fastify-table
2
2
 
3
+ ## 1.0.83 - 20.08.2024
4
+
5
+ - code optimization
6
+
3
7
  ## 1.0.81 - 15.08.2024
4
8
 
5
9
  - refactor /table API
6
10
 
11
+ ## 1.0.78 - 14.08.2024
12
+
13
+ - add sqlColumns to /api/data/:table/?:id
14
+
7
15
  ## 1.0.77 - 13.08.2024
8
16
 
9
17
  - add statusMonitor API
package/index.js CHANGED
@@ -1,97 +1,97 @@
1
- import path from 'path';
2
- import { existsSync, readdirSync, readFileSync } from 'fs';
3
-
4
- import fp from 'fastify-plugin';
5
- import config from './config.js';
6
- // import rclient from './redis/client.js';
7
-
8
- import redisPlugin from './redis/index.js';
9
- import pgPlugin from './pg/index.js';
10
- import tablePlugin from './table/index.js';
11
- import notificationPlugin from './notification/index.js';
12
- import widgetPlugin from './widget/index.js';
13
- import crudPlugin from './crud/index.js';
14
- import policyPlugin from './policy/index.js';
15
- import utilPlugin from './util/index.js';
16
- import cronPlugin from './cron/index.js';
17
-
18
- import pgClients from './pg/pgClients.js';
19
-
20
- import execMigrations from './migration/exec.migrations.js';
21
-
22
- async function plugin(fastify, opt) {
23
- // console.log(opt);
24
- config.pg = opt.pg;
25
- config.redis = opt.redis;
26
- config.root = opt.root;
27
- config.mapServerRoot = opt.mapServerRoot;
28
-
29
- // independent npm start / unit test
30
- if (!fastify.config) {
31
- fastify.decorate('config', config);
32
- }
33
-
34
- fastify.register(import('@fastify/sensible'), {
35
- errorHandler: false,
36
- });
37
-
38
- fastify.register(import('@fastify/url-data'), {
39
- errorHandler: false,
40
- });
41
-
42
- fastify.register(import('@opengis/fastify-hb'));
43
- fastify.decorate('getFolder', (req, type = 'server') => {
44
- if (!['server', 'local'].includes(type)) throw new Error('params type is invalid');
45
- const types = { local: req.root, server: req.mapServerRoot };
46
- const filepath = path.posix.join(types[type] || '/data/local', req.folder || config.folder || '');
47
- return filepath;
48
- });
49
-
50
- fastify.addHook('onListen', async () => {
51
- const { client } = pgClients;
52
- if (client?.pk?.['crm.cls']) {
53
- const clsDir = path.join(process.cwd(), 'server/templates/cls');
54
- const files = existsSync(clsDir) ? readdirSync(clsDir) : [];
55
- if (files.length) {
56
- const res = await Promise.all(files.map(async (filename) => {
57
- const filepath = path.join(clsDir, filename);
58
- const data = JSON.parse(readFileSync(filepath));
59
- return { name: path.parse(filename).name, data };
60
- }));
61
- await client.query('truncate table crm.cls');
62
- const { rows } = await client.query(`insert into crm.cls(name, type)
63
- select value->>'name', 'json' from json_array_elements($1) returning cls_id as id, name`, [JSON.stringify(res).replace(/'/g, "''")]);
64
- rows.forEach((row) => Object.assign(row, { data: res.find((cls) => row.name === cls.name)?.data }));
65
- const sql = `insert into crm.cls(code, name, parent)
66
- select json_array_elements(value->'data')->>'id', json_array_elements(value->'data')->>'text', value->>'name' from json_array_elements($1)`;
67
- await client.query(sql, [JSON.stringify(rows).replace(/'/g, "''")]);
68
- }
69
- }
70
- // call from another repo / project
71
- fastify.execMigrations = execMigrations;
72
- // execute core migrations
73
- await fastify.execMigrations();
74
- });
75
- if (!fastify.funcs) {
76
- fastify.addHook('onRequest', async (req) => {
77
- req.funcs = fastify;
78
- if (!req.user && req.session?.passport?.user) {
79
- const { user } = req.session?.passport || {};
80
- req.user = user;
81
- }
82
- });
83
- // fastify.decorateRequest('funcs', fastify);
84
- }
85
-
86
- policyPlugin(fastify);
87
- redisPlugin(fastify);
88
- await pgPlugin(fastify, opt);
89
- tablePlugin(fastify, opt);
90
- crudPlugin(fastify, opt);
91
- notificationPlugin(fastify, opt);
92
- widgetPlugin(fastify, opt);
93
- utilPlugin(fastify, opt);
94
- cronPlugin(fastify, opt);
95
- }
96
- export default fp(plugin);
97
- // export { rclient };
1
+ import path from 'path';
2
+ import { existsSync, readdirSync, readFileSync } from 'fs';
3
+
4
+ import fp from 'fastify-plugin';
5
+ import config from './config.js';
6
+ // import rclient from './redis/client.js';
7
+
8
+ import redisPlugin from './redis/index.js';
9
+ import pgPlugin from './pg/index.js';
10
+ import tablePlugin from './table/index.js';
11
+ import notificationPlugin from './notification/index.js';
12
+ import widgetPlugin from './widget/index.js';
13
+ import crudPlugin from './crud/index.js';
14
+ import policyPlugin from './policy/index.js';
15
+ import utilPlugin from './util/index.js';
16
+ import cronPlugin from './cron/index.js';
17
+
18
+ import pgClients from './pg/pgClients.js';
19
+
20
+ import execMigrations from './migration/exec.migrations.js';
21
+
22
+ async function plugin(fastify, opt) {
23
+ // console.log(opt);
24
+ config.pg = opt.pg;
25
+ config.redis = opt.redis;
26
+ config.root = opt.root;
27
+ config.mapServerRoot = opt.mapServerRoot;
28
+
29
+ // independent npm start / unit test
30
+ if (!fastify.config) {
31
+ fastify.decorate('config', config);
32
+ }
33
+
34
+ fastify.register(import('@fastify/sensible'), {
35
+ errorHandler: false,
36
+ });
37
+
38
+ fastify.register(import('@fastify/url-data'), {
39
+ errorHandler: false,
40
+ });
41
+
42
+ fastify.register(import('@opengis/fastify-hb'));
43
+ fastify.decorate('getFolder', (req, type = 'server') => {
44
+ if (!['server', 'local'].includes(type)) throw new Error('params type is invalid');
45
+ const types = { local: req.root || config.root, server: req.mapServerRoot || config.mapServerRoot };
46
+ const filepath = path.posix.join(types[type] || '/data/local', req.folder || config.folder || '');
47
+ return filepath;
48
+ });
49
+
50
+ fastify.addHook('onListen', async () => {
51
+ const { client } = pgClients;
52
+ if (client?.pk?.['crm.cls']) {
53
+ const clsDir = path.join(process.cwd(), 'server/templates/cls');
54
+ const files = existsSync(clsDir) ? readdirSync(clsDir) : [];
55
+ if (files.length) {
56
+ const res = await Promise.all(files.map(async (filename) => {
57
+ const filepath = path.join(clsDir, filename);
58
+ const data = JSON.parse(readFileSync(filepath));
59
+ return { name: path.parse(filename).name, data };
60
+ }));
61
+ await client.query('truncate table crm.cls');
62
+ const { rows } = await client.query(`insert into crm.cls(name, type)
63
+ select value->>'name', 'json' from json_array_elements($1) returning cls_id as id, name`, [JSON.stringify(res).replace(/'/g, "''")]);
64
+ rows.forEach((row) => Object.assign(row, { data: res.find((cls) => row.name === cls.name)?.data }));
65
+ const sql = `insert into crm.cls(code, name, parent)
66
+ select json_array_elements(value->'data')->>'id', json_array_elements(value->'data')->>'text', value->>'name' from json_array_elements($1)`;
67
+ await client.query(sql, [JSON.stringify(rows).replace(/'/g, "''")]);
68
+ }
69
+ }
70
+ // call from another repo / project
71
+ fastify.execMigrations = execMigrations;
72
+ // execute core migrations
73
+ await fastify.execMigrations();
74
+ });
75
+ if (!fastify.funcs) {
76
+ fastify.addHook('onRequest', async (req) => {
77
+ req.funcs = fastify;
78
+ if (!req.user && req.session?.passport?.user) {
79
+ const { user } = req.session?.passport || {};
80
+ req.user = user;
81
+ }
82
+ });
83
+ // fastify.decorateRequest('funcs', fastify);
84
+ }
85
+
86
+ policyPlugin(fastify);
87
+ redisPlugin(fastify);
88
+ await pgPlugin(fastify, opt);
89
+ tablePlugin(fastify, opt);
90
+ crudPlugin(fastify, opt);
91
+ notificationPlugin(fastify, opt);
92
+ widgetPlugin(fastify, opt);
93
+ utilPlugin(fastify, opt);
94
+ cronPlugin(fastify, opt);
95
+ }
96
+ export default fp(plugin);
97
+ // export { rclient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.0.81",
3
+ "version": "1.0.83",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "main": "index.js",
@@ -59,6 +59,20 @@ if (_returnType != 'text') then
59
59
 
60
60
  else
61
61
  raise notice 'skip default reassign';
62
+
63
+ CREATE EXTENSION if not exists "uuid-ossp";
64
+
65
+ CREATE OR REPLACE FUNCTION next_id()
66
+ RETURNS text AS
67
+ $BODY$
68
+ DECLARE
69
+
70
+ BEGIN
71
+ return replace(uuid_generate_v4()::text, '-', '');
72
+ END;
73
+ $BODY$
74
+ LANGUAGE plpgsql VOLATILE
75
+ COST 100;
62
76
  end if;
63
77
 
64
78
  end $$
@@ -1,3 +1,4 @@
1
- {
2
- "key": "dataset_id"
1
+ {
2
+ "key": "dataset_id",
3
+ "searchColumn": "table_name"
3
4
  }
@@ -1 +1 @@
1
- select dataset_id,dataset_name from gis.dataset
1
+ select dataset_id, dataset_name, table_name from gis.dataset
@@ -0,0 +1,3 @@
1
+ {
2
+ "db": "mbk_state_old"
3
+ }
@@ -0,0 +1,26 @@
1
+ select
2
+ codifier,
3
+ case
4
+ when object_type = 'A' then name_ua
5
+ else case
6
+ when object_type in ('P', 'H', 'O', 'B') then name_ua || ' ' || prefix_ua
7
+ else prefix_ua || ' ' || name_ua
8
+ end
9
+ end as title
10
+ from
11
+ ato_new.ato_new_all
12
+ left join ato_new.ato_settings_city_and_terrytory on ato_type = object_type
13
+ where
14
+ codifier is not null
15
+ union
16
+ all
17
+ select
18
+ country_id,
19
+ name_ua as title
20
+ from
21
+ ato_new.country
22
+ where
23
+ country_id = '2845832997045798794'
24
+ union
25
+ all
26
+ select 'UA00000000000000000', 'Україна'
@@ -0,0 +1,5 @@
1
+ {
2
+ "db": "mbk_poltava",
3
+ "key": "dataset_id",
4
+ "searchColumn": "table_name"
5
+ }
@@ -0,0 +1 @@
1
+ select dataset_id, dataset_name, table_name from gis.dataset
@@ -0,0 +1,3 @@
1
+ select dataset_id, dataset_name, table_name from gis.dataset
2
+ where
3
+ dataset_id = '{{parent}}'
@@ -1,96 +1,97 @@
1
- import getTemplate from './utils/getTemplate.js';
2
- import getFilterSQL from '../funcs/getFilterSQL/index.js';
3
- import getMeta from '../../pg/funcs/getMeta.js';
4
- import metaFormat from '../funcs/metaFormat/index.js';
5
- import getAccess from '../../crud/funcs/getAccess.js';
6
- import setToken from '../../crud/funcs/setToken.js';
7
- import gisIRColumn from './utils/gisIRColumn.js';
8
-
9
- const maxLimit = 100;
10
- export default async function dataAPI(req) {
11
- const time = Date.now();
12
- const {
13
- pg, params, funcs = {}, query = {}, opt = {}, uid,
14
- } = req;
15
-
16
- const loadTable = await getTemplate('table', params.table);
17
-
18
- if (!loadTable) { return { message: 'template not found', status: 404 }; }
19
-
20
- const {
21
- table, columns, sql, cardSql, filters, form, meta, sqlColumns, ispublic,
22
- } = loadTable;
23
- const { pk, columns: dbColumns = [] } = await getMeta(table);
24
-
25
- if (!pk) return { message: `table not found: ${table}`, status: 404 };
26
-
27
- const cols = columns.filter((el) => el.name !== 'geom').map((el) => el.name || el).join(',');
28
- const columnList = dbColumns.map((el) => el.name || el).join(',');
29
- const sqlTable = sql?.filter?.((el) => !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
30
- const cardSqlFiltered = opt?.id || params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
31
- const cardSqlTable = cardSqlFiltered.length ? cardSqlFiltered.map((el, i) => ` left join lateral (select json_agg(row_to_json(q)) as ${el.name} from (${el.sql})q) ct${i} on 1=1 `).join('') || '' : '';
32
-
33
- if (params.id && columnList.includes(params.id)) {
34
- return gisIRColumn({
35
- pg, funcs, layer: params.table, column: params.id, sql: query.sql,
36
- });
37
- }
38
-
39
- const fData = query.filter || query.search ? await getFilterSQL({
40
- filter: query.filter,
41
- search: query.search,
42
- table: params.table,
43
- json: 1,
44
- }) : {};
45
-
46
- const keyQuery = query.key && loadTable.key && !(opt?.id || params.id) ? `${loadTable.key}=$1` : null;
47
-
48
- const limit = Math.min(maxLimit, +(query.limit || 20));
49
-
50
- const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
51
- // id, query, filter
52
- const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
53
-
54
- const order = columnList.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
55
- const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
56
- const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
57
- const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
58
-
59
- const access = await getAccess(req, params.table);
60
- const where = [(opt?.id || params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search, access?.query || '1=1'].filter((el) => el);
61
- const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
62
- const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''} ${query.id || query.key ? '*' : sqlColumns || cols || '*'} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
63
-
64
- if (query.sql === '1') { return q; }
65
-
66
- const { rows } = await pg.query(q, (opt?.id || params.id ? [opt?.id || params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
67
-
68
- const total = keyQuery || opt?.id || params.id ? rows.length : await pg.queryCache(`select count(*) from ${table} t ${sqlTable} where ${where.join(' and ') || 'true'}`).then((el) => el?.rows[0]?.count);
69
-
70
- await metaFormat({ rows, table: params.table });
71
- const res = {
72
- time: Date.now() - time, card: loadTable.card, actions: loadTable.actions, access, total, count: rows.length, pk, form, rows, meta, columns, filters,
73
- };
74
-
75
- if (!funcs.config?.security?.disableToken) {
76
- const addTokens = setToken({
77
- ids: [JSON.stringify({ add: loadTable.table, form: loadTable.form })],
78
- mode: 'a',
79
- uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
80
- array: 1,
81
- });
82
- Object.assign(res, { addToken: addTokens[0] });
83
-
84
- rows.forEach((row) => {
85
- const editTokens = setToken({
86
- ids: [JSON.stringify({ id: row.id, table: loadTable.table, form: loadTable.form })],
87
- mode: 'w',
88
- uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
89
- array: 1,
90
- });
91
- Object.assign(row, { token: editTokens[0] });
92
- });
93
- }
94
-
95
- return res;
96
- }
1
+ import getTemplate from './utils/getTemplate.js';
2
+ import getFilterSQL from '../funcs/getFilterSQL/index.js';
3
+ import getMeta from '../../pg/funcs/getMeta.js';
4
+ import metaFormat from '../funcs/metaFormat/index.js';
5
+ import getAccess from '../../crud/funcs/getAccess.js';
6
+ import setToken from '../../crud/funcs/setToken.js';
7
+ import gisIRColumn from './utils/gisIRColumn.js';
8
+
9
+ const maxLimit = 100;
10
+ export default async function dataAPI({
11
+ pg, params, funcs = {}, query = {}, opt = {}, uid: uid1, req, session,
12
+ }) {
13
+ const time = Date.now();
14
+
15
+ const uid = session?.passport?.user?.uid || uid1 || query.uid || 0;
16
+
17
+ const loadTable = await getTemplate('table', params.table);
18
+
19
+ if (!loadTable) { return { message: 'template not found', status: 404 }; }
20
+
21
+ const {
22
+ table, columns, sql, cardSql, filters, form, meta, sqlColumns, ispublic,
23
+ } = loadTable;
24
+ const { pk, columns: dbColumns = [] } = await getMeta(table);
25
+
26
+ if (!pk) return { message: `table not found: ${table}`, status: 404 };
27
+
28
+ const cols = columns.filter((el) => el.name !== 'geom').map((el) => el.name || el).join(',');
29
+ const columnList = dbColumns.map((el) => el.name || el).join(',');
30
+ const sqlTable = sql?.filter?.((el) => !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', uid)}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
31
+ const cardSqlFiltered = opt?.id || params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
32
+ const cardSqlTable = cardSqlFiltered.length ? cardSqlFiltered.map((el, i) => ` left join lateral (select json_agg(row_to_json(q)) as ${el.name} from (${el.sql})q) ct${i} on 1=1 `).join('') || '' : '';
33
+
34
+ if (params.id && columnList.includes(params.id)) {
35
+ return gisIRColumn({
36
+ pg, funcs, layer: params.table, column: params.id, sql: query.sql,
37
+ });
38
+ }
39
+
40
+ const fData = query.filter || query.search ? await getFilterSQL({
41
+ filter: query.filter,
42
+ search: query.search,
43
+ table: params.table,
44
+ json: 1,
45
+ }) : {};
46
+
47
+ const keyQuery = query.key && loadTable.key && !(opt?.id || params.id) ? `${loadTable.key}=$1` : null;
48
+
49
+ const limit = Math.min(maxLimit, +(query.limit || 20));
50
+
51
+ const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
52
+ // id, query, filter
53
+ const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
54
+
55
+ const order = columnList.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
56
+ const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
57
+ const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
58
+ const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
59
+
60
+ const access = await getAccess(req, params.table);
61
+ const where = [(opt?.id || params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search, access?.query || '1=1'].filter((el) => el);
62
+ const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
63
+ const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''} ${query.id || query.key ? '*' : sqlColumns || cols || '*'} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
64
+
65
+ if (query.sql === '1') { return q; }
66
+
67
+ const { rows } = await pg.query(q, (opt?.id || params.id ? [opt?.id || params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
68
+
69
+ const total = keyQuery || opt?.id || params.id ? rows.length : await pg.queryCache(`select count(*) from ${table} t ${sqlTable} where ${where.join(' and ') || 'true'}`).then((el) => el?.rows[0]?.count);
70
+
71
+ await metaFormat({ rows, table: params.table });
72
+ const res = {
73
+ time: Date.now() - time, card: loadTable.card, actions: loadTable.actions, access, total, count: rows.length, pk, form, rows, meta, columns, filters,
74
+ };
75
+
76
+ if (!funcs.config?.security?.disableToken) {
77
+ const addTokens = setToken({
78
+ ids: [JSON.stringify({ add: loadTable.table, form: loadTable.form })],
79
+ mode: 'a',
80
+ uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
81
+ array: 1,
82
+ });
83
+ Object.assign(res, { addToken: addTokens[0] });
84
+
85
+ rows.forEach((row) => {
86
+ const editTokens = setToken({
87
+ ids: [JSON.stringify({ id: row.id, table: loadTable.table, form: loadTable.form })],
88
+ mode: 'w',
89
+ uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
90
+ array: 1,
91
+ });
92
+ Object.assign(row, { token: editTokens[0] });
93
+ });
94
+ }
95
+
96
+ return res;
97
+ }
@@ -1,20 +1,20 @@
1
- import getTemplate from './getTemplate.js';
2
-
3
- const loadCls = {};
4
-
5
- export default async function getTable(name) {
6
- if (loadCls[name]) return loadCls[name];
7
-
8
- const clsData = await getTemplate('cls', name);
9
-
10
- if (clsData) {
11
- loadCls[name] = { arr: clsData };
12
- return loadCls[name];
13
- }
14
-
15
- const selectData = await getTemplate('select', name);
16
- if (!selectData) { return null; }
17
-
18
- loadCls[name] = selectData;
19
- return loadCls[name];
20
- }
1
+ import getTemplate from './getTemplate.js';
2
+
3
+ const loadCls = {};
4
+
5
+ export default async function getSelect(name) {
6
+ if (loadCls[name]) return loadCls[name];
7
+
8
+ const clsData = await getTemplate('cls', name);
9
+
10
+ if (clsData) {
11
+ loadCls[name] = { arr: clsData };
12
+ return loadCls[name];
13
+ }
14
+
15
+ const selectData = await getTemplate('select', name);
16
+ if (!selectData) { return null; }
17
+
18
+ loadCls[name] = selectData;
19
+ return loadCls[name];
20
+ }
package/table/index.js CHANGED
@@ -1,78 +1,80 @@
1
- import suggest from './controllers/suggest.js';
2
- import data from './controllers/data.js';
3
- import table from './controllers/table.js';
4
- import card from './controllers/card.js';
5
- import search from './controllers/search.js';
6
- import filter from './controllers/filter.js';
7
- import form from './controllers/form.js';
8
- import metaFormat from './funcs/metaFormat/index.js';
9
- import getFilterSQL from './funcs/getFilterSQL/index.js';
10
- import getTemplate from './controllers/utils/getTemplate.js';
11
-
12
- const tableSchema = {
13
- querystring: {
14
- page: { type: 'string', pattern: '^(\\d+)$' },
15
- order: { type: 'string', pattern: '^(\\d+)$' },
16
- filter: { type: 'string', pattern: '^([\\w\\d_-]+)=([\\w\\d_-]+)$' },
17
- },
18
- params: {
19
- id: { type: 'string', pattern: '^([\\d\\w]+)$' },
20
- table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
21
- },
22
- };
23
-
24
- const searchSchema = {
25
- querystring: {
26
- page: { type: 'string', pattern: '^(\\d+)$' },
27
- limit: { type: 'string', pattern: '^(\\d+)$' },
28
- order: { type: 'string', pattern: '^([\\w_.]+)$' },
29
- desc: { type: 'string', pattern: '^(desc)|(asc)$' },
30
- key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
31
- table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
32
- sql: { type: 'string', pattern: '^(\\d)$' },
33
- },
34
- };
35
-
36
- const suggestSchema = {
37
- querystring: {
38
- lang: { type: 'string', pattern: '^([\\w.]+)$' },
39
- // parent: { type: 'string', pattern: '^([\\w,./]+)$' },
40
- sel: { type: 'string', pattern: '^([\\w,./]+)$' },
41
- name: { type: 'string', pattern: '^([\\w,./]+)$' },
42
- // key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
43
- // val: { type: 'string', pattern: '^([\\w.,]+)$' },
44
- sql: { type: 'string', pattern: '^(\\d)$' },
45
- },
46
- params: {
47
- id: { type: 'string', pattern: '^([\\d\\w]+)$' },
48
- },
49
- };
50
-
51
- const formSchema = {
52
- params: {
53
- form: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
54
- },
55
- };
56
-
57
- const filterSchema = {
58
- params: {
59
- table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
60
- },
61
- };
62
-
63
- async function plugin(fastify, config = {}) {
64
- const prefix = config.prefix || '/api';
65
- fastify.decorate('metaFormat', metaFormat);
66
- fastify.decorate('getFilterSQL', getFilterSQL);
67
- fastify.decorate('getTemplate', getTemplate);
68
-
69
- fastify.get(`${prefix}/suggest/:data`, { schema: suggestSchema }, suggest);
70
- fastify.get(`${prefix}/data/:table/:id?`, { schema: tableSchema }, data); // vs.crm.data.api с node
71
- fastify.get(`${prefix}/table/:table/:id`, { schema: tableSchema }, table);
72
- fastify.get(`${prefix}/card/:table/:id`, { schema: tableSchema }, card);
73
- fastify.get(`${prefix}/search`, { schema: searchSchema }, search);
74
- fastify.get(`${prefix}/filter/:table`, { schema: filterSchema }, filter);
75
- fastify.get(`${prefix}/form/:form`, { schema: formSchema }, form);
76
- }
77
-
78
- export default plugin;
1
+ import suggest from './controllers/suggest.js';
2
+ import data from './controllers/data.js';
3
+ import table from './controllers/table.js';
4
+ import card from './controllers/card.js';
5
+ import search from './controllers/search.js';
6
+ import filter from './controllers/filter.js';
7
+ import form from './controllers/form.js';
8
+ import metaFormat from './funcs/metaFormat/index.js';
9
+ import getFilterSQL from './funcs/getFilterSQL/index.js';
10
+ import getTemplate from './controllers/utils/getTemplate.js';
11
+ import getSelect from './controllers/utils/getSelect.js';
12
+
13
+ const tableSchema = {
14
+ querystring: {
15
+ page: { type: 'string', pattern: '^(\\d+)$' },
16
+ order: { type: 'string', pattern: '^(\\d+)$' },
17
+ filter: { type: 'string', pattern: '^([\\w\\d_-]+)=([\\w\\d_-]+)$' },
18
+ },
19
+ params: {
20
+ id: { type: 'string', pattern: '^([\\d\\w]+)$' },
21
+ table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
22
+ },
23
+ };
24
+
25
+ const searchSchema = {
26
+ querystring: {
27
+ page: { type: 'string', pattern: '^(\\d+)$' },
28
+ limit: { type: 'string', pattern: '^(\\d+)$' },
29
+ order: { type: 'string', pattern: '^([\\w_.]+)$' },
30
+ desc: { type: 'string', pattern: '^(desc)|(asc)$' },
31
+ key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
32
+ table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
33
+ sql: { type: 'string', pattern: '^(\\d)$' },
34
+ },
35
+ };
36
+
37
+ const suggestSchema = {
38
+ querystring: {
39
+ lang: { type: 'string', pattern: '^([\\w.]+)$' },
40
+ // parent: { type: 'string', pattern: '^([\\w,./]+)$' },
41
+ sel: { type: 'string', pattern: '^([\\w,./]+)$' },
42
+ name: { type: 'string', pattern: '^([\\w,./]+)$' },
43
+ // key: { type: 'string', pattern: '^([\\w\\d_]+)$' },
44
+ // val: { type: 'string', pattern: '^([\\w.,]+)$' },
45
+ sql: { type: 'string', pattern: '^(\\d)$' },
46
+ },
47
+ params: {
48
+ id: { type: 'string', pattern: '^([\\d\\w]+)$' },
49
+ },
50
+ };
51
+
52
+ const formSchema = {
53
+ params: {
54
+ form: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
55
+ },
56
+ };
57
+
58
+ const filterSchema = {
59
+ params: {
60
+ table: { type: 'string', pattern: '^([\\w\\d_.]+)$' },
61
+ },
62
+ };
63
+
64
+ async function plugin(fastify, config = {}) {
65
+ const prefix = config.prefix || '/api';
66
+ fastify.decorate('metaFormat', metaFormat);
67
+ fastify.decorate('getFilterSQL', getFilterSQL);
68
+ fastify.decorate('getTemplate', getTemplate);
69
+ fastify.decorate('getSelect', getSelect);
70
+
71
+ fastify.get(`${prefix}/suggest/:data`, { schema: suggestSchema }, suggest);
72
+ fastify.get(`${prefix}/data/:table/:id?`, { schema: tableSchema }, data); // vs.crm.data.api с node
73
+ fastify.get(`${prefix}/table/:table/:id`, { schema: tableSchema }, table);
74
+ fastify.get(`${prefix}/card/:table/:id`, { schema: tableSchema }, card);
75
+ fastify.get(`${prefix}/search`, { schema: searchSchema }, search);
76
+ fastify.get(`${prefix}/filter/:table`, { schema: filterSchema }, filter);
77
+ fastify.get(`${prefix}/form/:form`, { schema: formSchema }, form);
78
+ }
79
+
80
+ export default plugin;
@@ -0,0 +1,66 @@
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import build from '../../helper.js';
5
+
6
+ test('api suggest', async (t) => {
7
+ const app = await build(t);
8
+
9
+ await t.test('GET /suggest', async () => {
10
+ const res = await app.inject({
11
+ method: 'GET',
12
+ url: `/api/suggest/test.storage.data`,
13
+ });
14
+ const rep = JSON.parse(res?.body);
15
+ // console.log(rep);
16
+ assert.equal(res?.statusCode, 200);
17
+ assert.ok(rep?.count);
18
+ });
19
+
20
+ await t.test('GET /suggest key query', async () => {
21
+ const key = 'Новокиївка';
22
+ const res = await app.inject({
23
+ method: 'GET',
24
+ url: `/api/suggest/test.suggest.ato_new?key=${key}`,
25
+ });
26
+ const rep = JSON.parse(res?.body);
27
+ // console.log(rep);
28
+ assert.equal(res?.statusCode, 200);
29
+ assert.ok(rep?.count);
30
+ });
31
+
32
+ await t.test('GET /suggest key searchColumn', async () => {
33
+ const key = 'data_address.addr_city';
34
+ const res = await app.inject({
35
+ method: 'GET',
36
+ url: `/api/suggest/test.storage.data?key=${key}`,
37
+ });
38
+ const rep = JSON.parse(res?.body);
39
+ // console.log(rep);
40
+ assert.equal(res?.statusCode, 200);
41
+ assert.ok(rep?.count);
42
+ });
43
+
44
+ await t.test('GET /suggest інша db', async () => {
45
+ const res = await app.inject({
46
+ method: 'GET',
47
+ url: `/api/suggest/test.suggest.data`,
48
+ });
49
+ const rep = JSON.parse(res?.body);
50
+ // console.log(rep);
51
+ assert.equal(res?.statusCode, 200);
52
+ assert.ok(rep?.count);
53
+ });
54
+
55
+ await t.test('GET /suggest parent', async () => {
56
+ const parent = '3206158274160231699';
57
+ const res = await app.inject({
58
+ method: 'GET',
59
+ url: `/api/suggest/test.suggest.parent?parent=${parent}`,
60
+ });
61
+ const rep = JSON.parse(res?.body);
62
+ // console.log(rep);
63
+ assert.equal(res?.statusCode, 200);
64
+ assert.ok(rep?.count);
65
+ });
66
+ })
@@ -1,7 +1,7 @@
1
1
  import config from '../config.js';
2
2
 
3
3
  Object.assign(config, {
4
- templateDir: 'test/templates',
4
+ folder: 'test/templates',
5
5
  pg: {
6
6
  host: '192.168.3.160',
7
7
  port: 5434,