@opengis/fastify-table 1.4.3 → 1.4.5

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.
Files changed (26) hide show
  1. package/index.js +0 -2
  2. package/package.json +1 -1
  3. package/server/plugins/table/funcs/getData.js +1 -1
  4. package/server/plugins/table/funcs/getFilterSQL/index.js +5 -2
  5. package/server/plugins/table/funcs/getFilterSQL/util/formatValue.js +15 -4
  6. package/server/plugins/table/funcs/getFilterSQL/util/getFilterQuery.js +3 -1
  7. package/server/routes/{data → table}/controllers/cardTabData.js +1 -2
  8. package/server/routes/{data → table}/controllers/tableData.js +10 -12
  9. package/server/routes/table/{controllers/data.js → functions/getData.js} +2 -2
  10. package/server/routes/table/index.js +18 -3
  11. package/server/routes/table/schema.js +56 -1
  12. package/server/routes/data/controllers/funcs/getFilterSQL/index.js +0 -92
  13. package/server/routes/data/controllers/funcs/getFilterSQL/util/formatValue.js +0 -170
  14. package/server/routes/data/controllers/funcs/getFilterSQL/util/getCustomQuery.js +0 -13
  15. package/server/routes/data/controllers/funcs/getFilterSQL/util/getFilterQuery.js +0 -64
  16. package/server/routes/data/controllers/funcs/getFilterSQL/util/getOptimizedQuery.js +0 -12
  17. package/server/routes/data/controllers/funcs/getFilterSQL/util/getTableSql.js +0 -34
  18. package/server/routes/data/controllers/utils/assignTokens.js +0 -31
  19. package/server/routes/data/controllers/utils/conditions.js +0 -19
  20. package/server/routes/data/controllers/utils/getColumns.js +0 -9
  21. package/server/routes/data/index.mjs +0 -22
  22. package/server/routes/data/schema.js +0 -70
  23. /package/server/routes/{data → table}/controllers/cardData.js +0 -0
  24. /package/server/routes/{data → table}/controllers/tableFilter.js +0 -0
  25. /package/server/routes/{data → table}/controllers/tableInfo.js +0 -0
  26. /package/server/routes/{data → table}/controllers/tokenInfo.js +0 -0
package/index.js CHANGED
@@ -37,7 +37,6 @@ import pgClients from './server/plugins/pg/pgClients.js';
37
37
 
38
38
  import dblistRoutes from './server/routes/dblist/index.mjs';
39
39
 
40
- import dataRoutes from './server/routes/data/index.mjs';
41
40
  import menuRoutes from './server/routes/menu/index.mjs';
42
41
  import templatesRoutes from './server/routes/templates/index.mjs';
43
42
 
@@ -110,7 +109,6 @@ async function plugin(fastify, opt) {
110
109
  tableRoutes(fastify, opt);
111
110
  utilRoutes(fastify, opt);
112
111
 
113
- dataRoutes(fastify, opt);
114
112
  menuRoutes(fastify, opt);
115
113
  templatesRoutes(fastify, opt);
116
114
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.4.3",
3
+ "version": "1.4.5",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [
@@ -1,4 +1,4 @@
1
- import routeData from '../../../routes/table/controllers/data.js';
1
+ import routeData from '../../../routes/table/controllers/tableData.js';
2
2
 
3
3
  export default async function getData({ id, table, pg, filter, state, limit, page, search, user, order, sql, contextQuery, sufix }, reply, called) {
4
4
  const params = { table, id };
@@ -32,7 +32,7 @@ export default async function getFilterSQL({
32
32
  const extraDataTable = extra
33
33
  ? config.extraData?.[body.table]
34
34
  || config.extraData?.[body.table.split('.').shift()]
35
- || config.extraData?.['default']
35
+ || config.extraData?.default
36
36
  || config.extraData
37
37
  || defaultTable
38
38
  : undefined;
@@ -131,7 +131,10 @@ export default async function getFilterSQL({
131
131
 
132
132
  // table
133
133
 
134
- const obj = { body, extraSqlColumns, table, q };
134
+ const obj = {
135
+ body, extraSqlColumns, table, q,
136
+ };
137
+
135
138
  const optimizedSQL = `select * from ${getOptimizedQuery(obj)} `;
136
139
  const tableCount = getOptimizedQuery(obj, true);
137
140
 
@@ -1,18 +1,29 @@
1
- import applyHookSync from "../../../../hook/funcs/applyHookSync.js";
2
- import getRangeQuery from "./getRangeQuery.js";
1
+ import applyHookSync from '../../../../hook/funcs/applyHookSync.js';
2
+ import getRangeQuery from './getRangeQuery.js';
3
3
 
4
4
  export default function formatValue({
5
5
  pg, table, filter = {}, name, value, dataTypeID, uid = 1, optimize,
6
6
  }) {
7
- const { extra, sql, select, strict, options, default: defaultValue } = filter;
7
+ const {
8
+ extra, sql, select, strict, options, default: defaultValue,
9
+ } = filter;
10
+
8
11
  const pk = pg?.pk && table ? pg.pk[table] : undefined;
9
12
 
13
+ if (name === 'search' && value && filter?.id) {
14
+ const columns = filter.id.split(',');
15
+ const sval = `ilike '%${decodeURIComponent(value.replace(/%/g, '%25')).replace(/%/g, '\\%')}%'`;
16
+ const query = `${columns.map(el => `"${el}" ${sval}`).join(' or ')}`;
17
+ return { op: '~', query };
18
+ }
19
+
10
20
  const filterType = filter.type?.toLowerCase() || 'text';
11
21
  const fieldType = pg?.pgType?.[dataTypeID] || pg?.pgType?.[{ date: 1114 }[filterType] || 25];
12
22
 
13
23
  const hookData = applyHookSync('onFormatValue', {
14
24
  pg, pk, table, name, value, filterType, fieldType, filter, dataTypeID, uid, optimize,
15
25
  });
26
+
16
27
  if (hookData) {
17
28
  return hookData?.query ? hookData : { op: '=', query: hookData };
18
29
  }
@@ -21,7 +32,7 @@ export default function formatValue({
21
32
  || !value
22
33
  || (!dataTypeID && !extra && !sql && !options)
23
34
  || extra?.input?.toLowerCase?.() === 'datatable'
24
- || !['check', 'autocomplete', 'tags', 'avatar', 'radio'].includes(filterType) && options?.find?.(el => el?.sql)
35
+ || (!['check', 'autocomplete', 'tags', 'avatar', 'radio'].includes(filterType) && options?.find?.(el => el?.sql))
25
36
  ) {
26
37
  return {};
27
38
  }
@@ -35,7 +35,9 @@ function getFilterQuery({
35
35
  }
36
36
 
37
37
  // filter
38
- const filter = filterList?.find?.((el) => el.type && el.name === name) || { type: 'text' };
38
+ const filter = filterList?.find?.((el) => el.type === 'search' && el.id)
39
+ || filterList?.find?.((el) => el.type && el.name === name)
40
+ || { type: 'text' };
39
41
  const { strict, extra } = filter;
40
42
 
41
43
  // find all value
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable no-param-reassign */
2
2
  import {
3
- config, handlebars, getToken, getTemplate, getMeta,
4
- pgClients,
3
+ config, handlebars, getToken, getTemplate, getMeta, pgClients,
5
4
  } from '../../../../utils.js';
6
5
 
7
6
  import getTableData from './tableData.js';
@@ -1,15 +1,13 @@
1
1
  import {
2
- getOpt, getMeta, getTemplate, getData, pgClients, getToken,
2
+ getOpt, getMeta, getTemplate, pgClients, getToken,
3
3
  } from '../../../../utils.js';
4
4
 
5
- export default async function getTableData(req, reply) {
6
- const {
7
- user = {}, params = {}, query = {}, pg = pgClients.client,
8
- } = req;
5
+ import getData from '../functions/getData.js';
9
6
 
7
+ export default async function getTableData(req, reply, called) {
10
8
  const {
11
- filter, limit, page, search, sql, state, order,
12
- } = query || {};
9
+ user = {}, params = {}, query = {}, pg = pgClients.client, contextQuery: contextQuery1, sufix = true,
10
+ } = req;
13
11
 
14
12
  const { id } = params || {};
15
13
 
@@ -30,8 +28,8 @@ export default async function getTableData(req, reply) {
30
28
  }
31
29
 
32
30
  const resp = await getData({
33
- id, table: tokenData.table, pg, filter, state, limit, page, search, order, user, sql, contextQuery: tokenData.query,
34
- }, reply);
31
+ pg, params: { id, table: tokenData.table }, query, user, contextQuery: [contextQuery1, tokenData.query].filter(Boolean).join(' and '), sufix,
32
+ }, reply, called);
35
33
  if (resp?.addToken && tokenData.obj) { Object.assign(resp, { addToken: params.table }); }
36
34
  return resp;
37
35
  }
@@ -49,11 +47,11 @@ export default async function getTableData(req, reply) {
49
47
  const { rows = [] } = pg.pk?.['admin.rules'] ? await pg.query(q, [params.table, user.uid]) : {};
50
48
 
51
49
  const context = rows.filter(el => el.rule_values).map(el => `${el.attr} = any ('{${el.rule_values}}')`).join(' and ');
52
- const contextQuery = [interfaceQuery, context].filter(el => el).join(' and ') || ' 2=2 ';
50
+ const contextQuery = [contextQuery1, interfaceQuery, context].filter(Boolean).join(' and ') || ' 2=2 ';
53
51
 
54
52
  const res = await getData({
55
- id, table: params.table, pg, filter, state, limit, page, search, order, user, sql, contextQuery,
56
- }, reply);
53
+ pg, params, query, user, contextQuery, sufix,
54
+ }, reply, called);
57
55
 
58
56
  return res;
59
57
  }
@@ -4,9 +4,9 @@ import {
4
4
 
5
5
  import extraDataGet from '../../../plugins/extra/extraDataGet.js';
6
6
 
7
- import locales from './utils/locales.js';
7
+ import locales from '../controllers/utils/locales.js';
8
8
 
9
- import conditions from './utils/conditions.js';
9
+ import conditions from '../controllers/utils/conditions.js';
10
10
 
11
11
  const components = {
12
12
  'vs-widget-file': 'select \'vs-widget-file\' as component, count(*) from crm.files where entity_id=$1 and file_status<>3',
@@ -1,6 +1,11 @@
1
1
  import suggest from './controllers/suggest.js';
2
- import data from './controllers/data.js';
2
+ import data from './controllers/tableData.js';
3
+ import cardTabData from './controllers/cardTabData.js';
4
+ import cardData from './controllers/cardData.js';
3
5
  import dataInfo from './controllers/dataInfo.js';
6
+ import tableInfo from './controllers/tableInfo.js';
7
+ import tokenInfo from './controllers/tokenInfo.js';
8
+ import tableFilter from './controllers/tableFilter.js';
4
9
 
5
10
  import card from './controllers/card.js';
6
11
  import search from './controllers/search.js';
@@ -10,14 +15,24 @@ import form from './controllers/form.js';
10
15
  import loadTemplatePath from '../../plugins/table/funcs/loadTemplatePath.js';
11
16
 
12
17
  import {
13
- tableSchema, searchSchema, suggestSchema, formSchema, filterSchema,
18
+ tableDataSchema, tableSchema, searchSchema, suggestSchema, formSchema, filterSchema, cardTabDataSchema, tableDataIdSchema, tableFilterSchema,
14
19
  } from './schema.js';
15
20
 
21
+ const policy = ['public'];
22
+
16
23
  async function plugin(app, config = {}) {
17
24
  const { prefix = '/api' } = config;
18
- const policy = ['public'];
25
+
26
+ app.get(`${prefix}/token-info/:token`, { config: { policy: ['admin'] } }, tokenInfo);
27
+
28
+ app.get(`${prefix}/table-filter/:table`, { config: { policy }, schema: tableFilterSchema }, tableFilter);
29
+
30
+ app.get(`${prefix}/table-info/:table/:id?`, { config: { policy }, schema: tableDataSchema }, tableInfo);
19
31
  app.get(`${prefix}/suggest/:data`, { config: { policy }, schema: suggestSchema }, suggest);
20
32
  app.get(`${prefix}/data/:table/:id?`, { config: { policy: ['public', 'no-sql'] }, schema: tableSchema }, data);
33
+ app.get(`${prefix}/table-data/:table`, { config: { policy: ['user', 'no-sql'] }, schema: tableDataSchema }, data);
34
+ app.get(`${prefix}/table-data/:table/:id`, { config: { policy }, schema: tableDataIdSchema }, cardData);
35
+ app.get(`${prefix}/card-data/:token`, { config: { policy }, scheme: cardTabDataSchema }, cardTabData);
21
36
  app.get(`${prefix}/data-info/:id?`, { config: { policy: ['public', 'no-sql'] }, schema: tableSchema }, dataInfo);
22
37
 
23
38
  app.get(`${prefix}/card/:table/:id`, { config: { policy }, schema: tableSchema }, card);
@@ -1,3 +1,36 @@
1
+ const tableDataSchema = {
2
+ type: 'object',
3
+ properties: {
4
+ querystring: {
5
+ limit: { type: 'string', pattern: '^(\\d+)$' },
6
+ page: { type: 'string', pattern: '^(\\d+)$' },
7
+ // filter: { type: 'string', pattern: '^([\\w\\d_-]+)=([А-Яа-яҐґЄєІіЇї\\d\\w\\s\\/\\[\\]\\(\\)\\{\\}\\|,.!?;:—_=-@%#$&^*+=`~]+)$' },
8
+ // search: { type: 'string', pattern: '^([А-Яа-яҐґЄєІіЇї\\d\\w\\s\\/\\[\\]\\(\\)\\{\\}\\|,.!?;:—_=-@%#$&^*+=`~]+)$' },
9
+ order: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
10
+ desc: { type: 'string', pattern: '^(\\d+)$' },
11
+ state: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
12
+ custom: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
13
+ bbox: { type: 'string', pattern: '^([\\d\\s,.-]+)$' },
14
+ polyline: { type: 'string', pattern: '^([\\d\\w|@{}~_`]+)$' },
15
+ // key: { type: 'string', pattern: '^([\\d\\w_]+)$' },
16
+ sql: { type: 'string', pattern: '^(\\d)$' },
17
+ },
18
+ params: {
19
+ id: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
20
+ table: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
21
+ },
22
+ },
23
+ };
24
+
25
+ const tableFilterSchema = {
26
+ type: 'object',
27
+ properties: {
28
+ params: {
29
+ name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
30
+ },
31
+ },
32
+ };
33
+
1
34
  const tableSchema = {
2
35
  type: 'object',
3
36
  properties: {
@@ -75,6 +108,28 @@ const filterSchema = {
75
108
  },
76
109
  };
77
110
 
111
+ const cardTabDataSchema = {
112
+ type: 'object',
113
+ properties: {
114
+ querystring: {
115
+ sql: { type: 'string', pattern: '^(\\d)$' }
116
+ },
117
+ params: {
118
+ token: { type: 'string', pattern: '^([\\d\\w]+)$' },
119
+ },
120
+ },
121
+ };
122
+
123
+ const tableDataIdSchema = {
124
+ type: 'object',
125
+ properties: {
126
+ params: {
127
+ id: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
128
+ name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
129
+ },
130
+ },
131
+ };
132
+
78
133
  export {
79
- tableSchema, searchSchema, suggestSchema, formSchema, filterSchema,
134
+ tableDataSchema, tableDataIdSchema, tableSchema, searchSchema, suggestSchema, formSchema, filterSchema, cardTabDataSchema, tableFilterSchema,
80
135
  };
@@ -1,92 +0,0 @@
1
- import { getPG, getTemplate } from "@opengis/fastify-table/utils.js";
2
- // import pgClients from '@opengis/fastify-table/pg/funcs/pgClients.js';
3
-
4
- import getTableSql from './util/getTableSql.js';
5
- import getFilterQuery from './util/getFilterQuery.js';
6
- import getOptimizedQuery from './util/getOptimizedQuery.js';
7
-
8
- async function getFilterSQL({
9
- pg: pg1, table, filter, search, filterList, query, uid,
10
- }) {
11
- if (!table) return { error: 'param table is required', status: 400 };
12
-
13
- const pg = pg1 || getPG();
14
- const body = await getTemplate('table', table);
15
-
16
- const sqlList = body?.sql?.length
17
- ? body?.sql?.filter((el) => !el.disabled && el?.sql?.replace)
18
- .map((el, i) => {
19
- Object.assign(el, { name: el.name || `t${i + 1}` });
20
- return ` left join lateral (${el.filter ? el.sql.replace(/limit 1/ig, '') : el.sql}) as ${el.name} on 1=1 `;
21
- }).join(' ')
22
- : '';
23
- const fieldQuery = `select * from ${body?.table || table} ${sqlList ? ` t ${sqlList}` : ''} where 1=1 limit 0`;
24
- const { fields = [] } = await pg.query(fieldQuery);
25
-
26
- const { fields: fieldsModel } = pg.pk[body?.table] ? await pg.query(`select * from ${body.table} limit 0`) : {};
27
-
28
- const autoSearchColumn = fields?.filter((el) => pg.pgType?.[el.dataTypeID] === 'text')?.map((el) => el.name).join(',');
29
- const searchColumn = body?.search_column || autoSearchColumn;
30
- const fieldsList = (fieldsModel || fields)?.map((el) => el.name);
31
- try {
32
- const tableSQL = await getTableSql({
33
- pg, body, table, fields,
34
- });
35
- const sval = `ilike '%${decodeURIComponent(search).replace(/'/g, "''")}%'`;
36
- const searchQuery = search && searchColumn
37
- ? ` (${searchColumn.split(',')?.map((name) => {
38
- const { pk } = tableSQL.find((el) => el.name === name) || {};
39
- return pk && !fieldsList.includes(name) ? `${pk} in (select ${pk} from (${fieldQuery})q where ${name} ${sval})` : `${name} ${sval}`;
40
- }).join(' or ')} )` : '';
41
-
42
- // table_properties - user filter data
43
- const { rows: filterProperties = [] } = await pg.query(`select column_id, name, title, format, data from admin.custom_column where entity=$1 and uid=$2`, [table, uid]);
44
- const extraFilters = filterProperties.map((row) => ({ ua: row.title, name: row.name, type: row.format, data: row.data, extra: true }));
45
- const customFilters = [];
46
-
47
- const filterList1 = await Promise.all((filterList || (body?.filter_list || []).concat(body?.filterInline || []).concat(body?.filterCustom || []).concat(body?.filterState || []).concat(body?.filterList || []).concat(body?.filters || [])).concat(extraFilters || []).concat(customFilters || [])
48
-
49
- ?.map(async (el) => {
50
- if (!el?.data) return el;
51
- const cls = await getTemplate(['cls', 'select'], el.data);
52
- if (Array.isArray(cls) && cls?.length) {
53
- Object.assign(el, { options: cls });
54
- } else if (typeof (cls?.sql || cls) === 'string') {
55
- Object.assign(el, { sql: cls?.sql || cls });
56
- }
57
- return el;
58
- })
59
- );
60
- const filters = getFilterQuery({
61
- pg,
62
- filter,
63
- table: body?.table || table,
64
- tableSQL,
65
- fields,
66
- filterList: filterList1,
67
- });
68
-
69
- // filter
70
- const filterQuery = filters?.filter((el) => el.query)?.map((el) => `${el.query} `).join(' and ');
71
- const q = [body?.query, query, searchQuery, filterQuery].filter((el) => el).join(' and ');
72
-
73
- // table
74
- const modelQuery = body?.model || body?.table || table;
75
- const optimizedSQL = `select * from ${getOptimizedQuery({ body, table, q })} `;
76
- const tableCount = getOptimizedQuery({ body, table, q }, true);
77
- // console.log(optimizedSQL);
78
- return {
79
- filterList,
80
- q,
81
- optimizedSQL,
82
- tableCount,
83
- table: modelQuery,
84
- searchQuery,
85
- };
86
- }
87
- catch (err) {
88
- throw new Error(err.toString());
89
- }
90
- }
91
-
92
- export default getFilterSQL;
@@ -1,170 +0,0 @@
1
- const dateTypeList = ['date', 'timestamp', 'timestamp without time zone'];
2
- const numberTypeList = ['float8', 'int4', 'int8', 'numeric', 'double precision', 'integer'];
3
-
4
- function dt(y, m, d) {
5
- return new Date(Date.UTC(y, m, d)).toISOString().slice(0, 10);
6
- }
7
- const dp = {
8
- d: new Date().getDate(),
9
- w: new Date().getDate() - (new Date().getDay() || 7) + 1,
10
- m: new Date().getMonth(),
11
- q: (new Date().getMonth() / 4).toFixed() * 3,
12
- y: new Date().getFullYear(),
13
- };
14
-
15
- function formatDateISOString(date) {
16
- if (!date?.includes('.')) return date;
17
- const [day, month, year] = date.split('.');
18
- return `${year}-${month}-${day}`;
19
- }
20
-
21
- function formatValue({
22
- pg, table, filter = {}, name, value, operator = '=', dataTypeID, uid = 1, optimize,
23
- }) {
24
- const { data, sql, extra } = filter;
25
- const pk = pg?.pk && table ? pg.pk[table] : undefined;
26
-
27
- if (!dataTypeID && !extra) return {};
28
- const fieldType = extra ? pg.pgType?.[{ 'Date': 1114 }[filter?.type] || 25] : pg.pgType?.[dataTypeID];
29
- if (!name || !value || !fieldType) return {};
30
- const filterType = filter.type?.toLowerCase();
31
-
32
- // current day, week, month, year etc.
33
- if (dateTypeList.includes(fieldType) && !value?.includes('_') && ['cd', 'cw', 'cm', 'cq', 'cy'].includes(value)) {
34
- const query = {
35
- cd: `${name}::date = '${dt(dp.y, dp.m, dp.d)}'::date`,
36
- cw: `${name}::date >= '${dt(dp.y, dp.m, dp.w)}'::date and ${name} <= '${dt(dp.y, dp.m, dp.w + 6)}'::date`,
37
- cm: `${name}::date >= '${dt(dp.y, dp.m, 1)}'::date and ${name} <= '${dt(dp.y, dp.m + 1, 0)}'::date`,
38
- cq: `${name}::date >= '${dt(dp.y, dp.q, 1)}'::date and ${name} <= '${dt(dp.y, dp.q + 3, 0)}'::date`,
39
- cy: `${name}::date >= '${dt(dp.y, 0, 1)}'::date and ${name}::date <= '${dt(dp.y, 11, 31)}'::date`,
40
- }[value];
41
- return { op: '=', query, extra };
42
- }
43
-
44
- // date range
45
- if (dateTypeList.includes(fieldType) && value?.includes('_')) {
46
- const [min, max] = value.split('_');
47
- const query = `${name} >= '${min}'::date and ${name} <= '${max}'::date`;
48
- return { op: 'between', query, extra };
49
- }
50
-
51
- // v3 filter date range, example - "01.01.2024-31.12.2024"
52
- if (dateTypeList.includes(fieldType) && value?.includes('.') && value?.indexOf('-') === 10 && value?.length === 21) {
53
- const [startDate, endDate] = value.split('-');
54
- const min = formatDateISOString(startDate);
55
- const max = formatDateISOString(endDate);
56
- const query = extra && pk
57
- ? `${pk} in (select object_id from crn.extra_data where property_key='${name}' and value_date::date >= '${min}'::date and value_date::date <= '${max}'::date)`
58
- : `${name}::date >= '${min}'::date and ${name}::date <= '${max}'::date`;
59
- return { op: 'between', query, extra };
60
- }
61
-
62
- // my rows
63
- if (value === 'me' && uid && fieldType === 'text') {
64
- return { op: '=', query: extra ? `uid = '${uid}'` : `${name}::text = '${uid}'`, extra };
65
- }
66
-
67
- const formatType = {
68
- float8: 'numeric',
69
- int4: 'numeric',
70
- int8: 'numeric',
71
- varchar: 'text',
72
- bool: 'boolean',
73
- geometry: 'geom',
74
- }[fieldType] || 'text';
75
-
76
- if (optimize && optimize.name !== optimize.pk) {
77
- const val = filterType === 'text' ? `ilike '%${value}%'` : `= any('{${value}}')`;
78
- return {
79
- op: '~',
80
- query: fieldType?.includes('[]')
81
- ? `${optimize.pk} && (select array_agg(${optimize.pk}) from ${optimize.table} where ${name} ${val} )`
82
- : `${optimize.pk} in (select ${optimize.pk} from ${optimize.table} where ${name} ${val} )`,
83
- extra,
84
- };
85
- }
86
-
87
- if (fieldType?.includes('[]')) {
88
- return { op: 'in', query: `'{${value}}'::text[] && ${name}::text[]`, extra };
89
- }
90
-
91
- // multiple items of 1 param
92
- if (value?.indexOf(',') !== -1) {
93
- const values = value.split(',').filter((el) => el !== 'null');
94
- if (extra && pk) {
95
- const query = value?.indexOf('null') !== -1
96
- ? `${pk} in (select object_id from crm.extra_data where property_key='${name}' and ( value_text is null or value_text in (${values?.map((el) => `'"${el}"'`).join(',')}) ) )`
97
- : `${pk} in (select object_id from crm.extra_data where property_key='${name}' and value_text in (${values?.map((el) => `'"${el}"'`).join(',')}) )`;
98
- return { op: 'in', query, extra };
99
- }
100
- const query = value?.indexOf('null') !== -1
101
- ? `( ${name} is null or ${name}::text in (${values?.map((el) => `'${el}'`).join(',')}) )`
102
- : `${name}::text in (${value.split(',')?.map((el) => `'${el}'`).join(',')})`;
103
- return { op: 'in', query, extra };
104
- }
105
-
106
- // v3 filter number range, example - "100_500"
107
- if (numberTypeList.includes(fieldType) && value?.indexOf('_') !== -1) {
108
- const [min, max] = value.split('_');
109
- const query = (max === 'max' ? `${name} > ${min}` : null) || (min === 'min' ? `${name} < ${max}` : null) || `${name} between ${min} and ${max}`;
110
- return { op: 'between', query, extra };
111
- }
112
-
113
- // number range
114
- if (numberTypeList.includes(fieldType) && value?.indexOf('-') !== -1) {
115
- const [min, max] = value.split('-');
116
- if (min === 'min' && max === 'max') return {};
117
- const query = (max === 'max' ? `${name} > ${min}` : null) || (min === 'min' ? `${name} < ${max}` : null) || `${name} between ${min} and ${max}`;
118
- return { op: 'between', query, extra };
119
- }
120
-
121
- if (['<', '>'].includes(operator)) {
122
- const query = `${name} ${operator} '${value}'::${formatType}`;
123
- return { op: operator, query, extra };
124
- }
125
-
126
- if (operator === '=' && filterType !== 'text' && !filter?.data) {
127
- const query = {
128
- null: `${name} is null`,
129
- notnull: `${name} is not null`,
130
- }[value] || `${name}::${formatType}='${value}'::${formatType}`;
131
- return { op: '=', query, extra };
132
- }
133
-
134
- if (['~', '='].includes(operator)) {
135
- const operator1 = (filterType === 'text' && (filter?.id || filter?.name) && operator === '=' ? '~' : operator);
136
- const match = operator1 === '=' ? `='${value}'` : `ilike '%${value}%'`;
137
- if (extra && pk) {
138
- const query = data && sql
139
- ? `${pk} in (select object_id from crm.extra_data where property_key='${name}' and value_text in ( ( with q(id,name) as (${sql}) select id from q where name ${match})))`
140
- : `${pk} in (select object_id from crm.extra_data where property_key='${name}' and value_text ${match})`
141
- return { op: 'ilike', query, extra };
142
- }
143
-
144
- const query = filter?.data && filter?.sql
145
- ? `${filter?.name || filter?.id} in ( ( with q(id,name) as (${filter?.sql}) select id from q where name::text ${match}) )` // filter with cls
146
- : `${name}::text ${match}`; // simple filter
147
- // console.log(query);
148
- return { op: 'ilike', query };
149
- }
150
-
151
- // json
152
- if (name.includes('.')) {
153
- const [col, prop] = name.split('.');
154
- const query = ` ${col}->>'${prop}' in ('${value.join("','")}')`;
155
- return { op: 'in', query, extra };
156
- }
157
-
158
- // geometry
159
- if (['geometry'].includes(fieldType)) {
160
- const bbox = value[0].split('_');
161
-
162
- if (bbox?.length === 4) {
163
- const query = ` ${name} && 'box(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d `;
164
- return { op: '&&', query, extra };
165
- }
166
- }
167
- return {};
168
- }
169
-
170
- export default formatValue;
@@ -1,13 +0,0 @@
1
- async function getCustomQuery({
2
- pg, table, customFilter,
3
- }) {
4
- if (!customFilter) return null;
5
- const customFilterList = customFilter?.split(',')?.map((el) => el?.split('_').pop());
6
- const { property_json: customFilterSQL } = await pg.one(`select json_agg(json_build_object('id',property_id,'name',property_key,'query',property_text)
7
- ) as property_json from admin.properties where property_key is not null and property_entity='customQuery' and object_id=$1`, [table]);
8
- const data = customFilterSQL?.length ? customFilterSQL.filter((el) => customFilterList.includes(el.id)) || [] : [];
9
- const customQuery = data?.map((el) => el.query).join(' and ');
10
- return `${customQuery}`;
11
- }
12
-
13
- export default getCustomQuery;
@@ -1,64 +0,0 @@
1
- /* eslint-disable no-continue */
2
-
3
- /**
4
- * @param {Number} opt.json - (1|0) 1 - Результат - Object, 0 - String
5
- * @param {String} opt.query - запит до таблиці
6
- * @param {String} opt.hash - інформація з хешу по запиту
7
- */
8
-
9
- import formatValue from './formatValue.js';
10
-
11
- function getQuery({
12
- pg, filter: filterStr, table, tableSQL, fields, filterList,
13
- }) {
14
- if (!filterStr) return null; // filter list API
15
-
16
- const mainOperators = ['=', '~', '>', '<'];
17
-
18
- const filterQueryArray = decodeURI(filterStr?.replace(/(^,)|(,$)/g, '')).replace(/'/g, '').split(/[;|]/);
19
-
20
- const resultList = [];
21
-
22
- for (let i = 0; i < filterQueryArray.length; i += 1) {
23
- const item = filterQueryArray[i];
24
- const operator = mainOperators?.find((el) => item.indexOf(el) !== -1) || '=';
25
- const [name] = item.split(operator);
26
-
27
- // skip already added filter
28
- if (resultList.find((el) => el.name === name)) {
29
- continue;
30
- }
31
-
32
- // filter
33
- const filter = filterList?.find((el) => [el.id, el.name].includes(name)) || { type: 'text' };
34
-
35
- // find all value
36
- const value = filterQueryArray.filter((el) => el.startsWith(name)).map((el) => el.substring(name.length + 1)).join(',');
37
-
38
- const optimize = fields?.find((el) => el.name === name) ? null : tableSQL.find((el) => el.name === name);
39
-
40
- // find field and skip not exists
41
- const { dataTypeID } = fields?.find((el) => el.name === name) || fields?.find((el) => el.name === optimize?.pk) || {};
42
-
43
- // format query
44
- const { op, query, filterType, fieldType } = formatValue({
45
- pg,
46
- table,
47
- filter,
48
- optimize,
49
- name,
50
- value: decodeURIComponent(value),
51
- operator,
52
- dataTypeID,
53
- }) || {};
54
- if (!query) continue;
55
-
56
- resultList.push({
57
- name, value, query, operator: op, filterType, type: fieldType,
58
- });
59
- }
60
-
61
- return resultList;
62
- }
63
-
64
- export default getQuery;
@@ -1,12 +0,0 @@
1
- function getOptimizedQuery({ body, table, q }, count) {
2
- const order = body?.orderby || body?.order ? `order by ${body?.orderby || body?.order}` : '';
3
-
4
- const tableName = body?.table || body?.model || table;
5
-
6
- const sqlList = body?.sql?.filter((el) => !el.disabled && el?.sql?.replace && (count ? el.count !== false : true))
7
- .map((el) => ` left join lateral (${el.filter ? el.sql.replace(/limit 1/ig, '') : el.sql}) as ${el.name} on 1=1 `).join(' ');
8
-
9
- return `(select * from ${tableName} ${sqlList ? ` t ${sqlList}` : ''} where 1=1 and ${q?.replace('q.', 't.') || '1=1'} ${order})q`;
10
- }
11
-
12
- export default getOptimizedQuery;
@@ -1,34 +0,0 @@
1
- function getTable(table) {
2
- const result = table?.toLowerCase()?.replace(/[\n\r]+/g, ' ')?.split(' from ')?.filter((el) => /^[a-z0-9_]+\.[a-z0-9_]+/.test(el))
3
- ?.map((el) => el.split(/[ )]/)[0]);
4
- return result;
5
- }
6
-
7
- /**
8
- * @param {Number} opt.json - (1|0) 1 - Результат - Object, 0 - String
9
- * @param {String} opt.query - запит до таблиці
10
- * @param {String} opt.hash - інформація з хешу по запиту
11
- */
12
- const tableSql = {};
13
- async function getTableSql({
14
- pg, body, table, fields,
15
- }) {
16
- if (tableSql[table]) return tableSql[table];
17
-
18
- const fieldList = fields.map((el) => el.name);
19
-
20
- const tableList = body?.sql?.map((el) => getTable(el.sql)).reduce((acc, el) => acc.concat(el), []).filter((el) => fieldList.includes(pg.pk[el]));
21
-
22
- if (!tableList) { tableSql[table] = []; return []; }
23
-
24
- const data = await Promise.all(tableList?.map(async (tableEl) => {
25
- const { fields: fieldsEl } = await pg.query(`select * from ${tableEl} limit 0`);
26
- return fieldsEl.map((el) => ({ name: el.name, table: tableEl, pk: pg.pk[tableEl] }));
27
- }));
28
-
29
- tableSql[table] = data.reduce((acc, el) => acc.concat(el), []);
30
-
31
- return tableSql[table];
32
- }
33
-
34
- export default getTableSql;
@@ -1,31 +0,0 @@
1
- import { config, setToken } from "@opengis/fastify-table/utils.js";
2
-
3
- export default function assignTokens({
4
- rows = [], ispublic, uid, loadTable = {},
5
- }) {
6
- if (config?.security?.disableToken) return;
7
-
8
- if (!config?.auth?.disable && !ispublic && !uid) throw new Error('empty user');
9
- if (!loadTable?.table || !(loadTable?.form || loadTable?.add_form)) return null;
10
-
11
- const form = loadTable?.form || loadTable?.add_form;
12
- const addTokens = setToken({
13
- ids: [JSON.stringify({ add: loadTable.table, form })],
14
- mode: 'a',
15
- uid: config?.auth?.disable || ispublic ? '1' : uid,
16
- array: 1,
17
- });
18
- if (!rows.length) return addTokens[0];
19
-
20
- rows.forEach((row) => {
21
- const editTokens = setToken({
22
- ids: [JSON.stringify({ id: row.id, table: loadTable.table, form })],
23
- mode: 'w',
24
- uid: config?.auth?.disable || ispublic ? '1' : uid,
25
- array: 1,
26
- });
27
- Object.assign(row, { token: editTokens[0] });
28
- });
29
- return addTokens[0];
30
-
31
- }
@@ -1,19 +0,0 @@
1
- function onCheck(rule, data) {
2
- const val = data[rule[0]];
3
- if (rule[1] === '==') return val === rule[2];
4
- if (rule[1] === '!=') return val !== rule[2];
5
- if (rule[1] === 'in' && rule[2].split) return rule[2].split(',').includes(val);
6
- if (rule[1] === 'in' && rule[2].includes) return rule[2].includes(val);
7
-
8
- if (rule[1] === 'not in' && rule[2].split) return !rule[2].split(',').includes(val);
9
- if (rule[1] === 'not in' && rule[2].includes) return !rule[2].includes(val);
10
-
11
- if (rule[1] === '>') return val > rule[2];
12
- if (rule[1] === '<') return val < rule[2];
13
- return false;
14
- }
15
- export default function conditions(rules, data) {
16
- if (!rules?.length) return true;
17
- const result = Array.isArray(rules[0]) ? !rules.filter(el => !onCheck(el, data)).length : onCheck(rules, data);
18
- return result;
19
- }
@@ -1,9 +0,0 @@
1
- import { getTemplate } from "@opengis/fastify-table/utils.js";
2
-
3
- export default async function getColumns({
4
- columns = [], params = {}, opt = {}, loadTable = {}, form, table, dbColumns = [], mode = 'table',
5
- }) {
6
- const columnList = dbColumns.map((el) => el.name || el).join(',');
7
- const cols = columns.filter((el) => columnList.includes(el?.name) && el?.name !== 'geom').map((el) => el?.name || el).join(',');
8
- return { cols, columnList };
9
- }
@@ -1,22 +0,0 @@
1
- import tableData from './controllers/tableData.js';
2
- import tokenInfo from './controllers/tokenInfo.js';
3
- import cardTabData from './controllers/cardTabData.js';
4
- import cardData from './controllers/cardData.js';
5
- import tableFilter from './controllers/tableFilter.js';
6
- import tableInfo from './controllers/tableInfo.js';
7
-
8
- import {
9
- tableDataSchema, tableDataIdSchema, tableFilterSchema, cardTabDataSchema,
10
- } from './schema.js';
11
-
12
- const policy = ['user'];
13
-
14
- export default async function route(app, config = {}) {
15
- const { prefix = '/api' } = config;
16
- app.get(`${prefix}/table-data/:table`, { config: { policy: ['user', 'no-sql'] }, schema: tableDataSchema }, tableData);
17
- app.get(`${prefix}/token-info/:token`, { config: { policy: ['admin'] } }, tokenInfo);
18
- app.get(`${prefix}/card-data/:token`, { config: { policy }, scheme: cardTabDataSchema }, cardTabData);
19
- app.get(`${prefix}/table-data/:table/:id`, { config: { policy }, schema: tableDataIdSchema }, cardData);
20
- app.get(`${prefix}/table-filter/:table`, { config: { policy }, schema: tableFilterSchema }, tableFilter);
21
- app.get(`${prefix}/table-info/:table/:id?`, { config: { policy }, schema: tableDataSchema }, tableInfo);
22
- }
@@ -1,70 +0,0 @@
1
- const tableDataSchema = {
2
- type: 'object',
3
- properties: {
4
- querystring: {
5
- limit: { type: 'string', pattern: '^(\\d+)$' },
6
- page: { type: 'string', pattern: '^(\\d+)$' },
7
- // filter: { type: 'string', pattern: '^([\\w\\d_-]+)=([А-Яа-яҐґЄєІіЇї\\d\\w\\s\\/\\[\\]\\(\\)\\{\\}\\|,.!?;:—_=-@%#$&^*+=`~]+)$' },
8
- // search: { type: 'string', pattern: '^([А-Яа-яҐґЄєІіЇї\\d\\w\\s\\/\\[\\]\\(\\)\\{\\}\\|,.!?;:—_=-@%#$&^*+=`~]+)$' },
9
- order: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
10
- desc: { type: 'string', pattern: '^(\\d+)$' },
11
- state: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
12
- custom: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
13
- bbox: { type: 'string', pattern: '^([\\d\\s,.-]+)$' },
14
- polyline: { type: 'string', pattern: '^([\\d\\w|@{}~_`]+)$' },
15
- // key: { type: 'string', pattern: '^([\\d\\w_]+)$' },
16
- sql: { type: 'string', pattern: '^(\\d)$' },
17
- },
18
- params: {
19
- id: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
20
- table: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
21
- },
22
- },
23
- };
24
-
25
- const tableDataIdSchema = {
26
- type: 'object',
27
- properties: {
28
- params: {
29
- id: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
30
- name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
31
- },
32
- },
33
- };
34
-
35
- const tableFilterSchema = {
36
- type: 'object',
37
- properties: {
38
- params: {
39
- name: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
40
- },
41
- },
42
- };
43
-
44
- const cardDataSchema = {
45
- type: 'object',
46
- properties: {
47
- params: {
48
- id: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
49
- table: { type: 'string', pattern: '^([\\d\\w._-]+)$' },
50
- },
51
- },
52
- };
53
-
54
- const cardTabDataSchema = {
55
- type: 'object',
56
- properties: {
57
- querystring: {
58
- sql: { type: 'string', pattern: '^(\\d)$' }
59
- },
60
- params: {
61
- token: { type: 'string', pattern: '^([\\d\\w]+)$' },
62
- },
63
- },
64
- };
65
- export {
66
- tableDataSchema, tableDataIdSchema, tableFilterSchema,
67
- cardDataSchema, cardTabDataSchema,
68
- };
69
-
70
- export default null;