@opengis/fastify-table 1.1.37 → 1.1.39

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 (43) hide show
  1. package/Changelog.md +1 -1
  2. package/README.md +26 -26
  3. package/config.js +10 -10
  4. package/cron/controllers/cronApi.js +22 -22
  5. package/cron/controllers/utils/cronList.js +1 -1
  6. package/cron/funcs/addCron.js +131 -131
  7. package/cron/index.js +10 -10
  8. package/crud/controllers/utils/checkXSS.js +45 -45
  9. package/crud/controllers/utils/xssInjection.js +72 -72
  10. package/crud/funcs/getToken.js +27 -27
  11. package/crud/funcs/isFileExists.js +13 -13
  12. package/crud/funcs/setToken.js +53 -53
  13. package/helper.js +4 -2
  14. package/module/test/cls/itree.composition.json +26 -0
  15. package/module/test/table/test.rest_zone.table.json +265 -0
  16. package/notification/controllers/testEmail.js +49 -49
  17. package/notification/funcs/utils/sendEmail.js +39 -39
  18. package/package.json +1 -1
  19. package/pg/funcs/getPG.js +30 -30
  20. package/redis/funcs/getRedis.js +23 -23
  21. package/server/migrations/log.sql +80 -80
  22. package/server/migrations/properties.sql +1 -1
  23. package/table/controllers/card.js +44 -44
  24. package/table/controllers/data.js +6 -5
  25. package/table/controllers/form.js +28 -28
  26. package/table/controllers/table.js +4 -4
  27. package/table/funcs/getFilterSQL/index.js +25 -13
  28. package/table/funcs/getFilterSQL/util/formatValue.js +44 -19
  29. package/table/funcs/getFilterSQL/util/getFilterQuery.js +14 -21
  30. package/test/api/crud.xss.test.js +72 -72
  31. package/test/api/table.test.js +46 -8
  32. package/test/config.example +18 -18
  33. package/test/funcs/pg.test.js +34 -34
  34. package/test/funcs/redis.test.js +19 -19
  35. package/test/templates/cls/test.json +9 -9
  36. package/test/templates/form/cp_building.form.json +32 -32
  37. package/test/templates/select/account_id.json +3 -3
  38. package/test/templates/select/storage.data.json +2 -2
  39. package/test/templates/table/gis.dataset.table.json +20 -20
  40. package/util/controllers/next.id.js +4 -4
  41. package/util/controllers/properties.get.js +19 -19
  42. package/util/index.js +23 -23
  43. package/utils.js +1 -0
@@ -19,9 +19,15 @@ function formatDateISOString(date) {
19
19
  }
20
20
 
21
21
  function formatValue({
22
- filterType, filter, name, value, operator = '=', fieldType = 'text', uid = 1, optimize,
22
+ pg, table, filter = {}, name, value, operator = '=', dataTypeID, uid = 1, optimize,
23
23
  }) {
24
- if (!name || !value) return {};
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();
25
31
 
26
32
  // current day, week, month, year etc.
27
33
  if (dateTypeList.includes(fieldType) && !value?.includes('_') && ['cd', 'cw', 'cm', 'cq', 'cy'].includes(value)) {
@@ -32,26 +38,32 @@ function formatValue({
32
38
  cq: `${name}::date >= '${dt(dp.y, dp.q, 1)}'::date and ${name} <= '${dt(dp.y, dp.q + 3, 0)}'::date`,
33
39
  cy: `${name}::date >= '${dt(dp.y, 0, 1)}'::date and ${name}::date <= '${dt(dp.y, 11, 31)}'::date`,
34
40
  }[value];
35
- return { op: '=', query };
41
+ return { op: '=', query, extra };
36
42
  }
43
+
37
44
  // date range
38
45
  if (dateTypeList.includes(fieldType) && value?.includes('_')) {
39
46
  const [min, max] = value.split('_');
40
47
  const query = `${name} >= '${min}'::date and ${name} <= '${max}'::date`;
41
- return { op: 'between', query };
48
+ return { op: 'between', query, extra };
42
49
  }
50
+
43
51
  // v3 filter date range, example - "01.01.2024-31.12.2024"
44
52
  if (dateTypeList.includes(fieldType) && value?.includes('.') && value?.indexOf('-') === 10 && value?.length === 21) {
45
53
  const [startDate, endDate] = value.split('-');
46
54
  const min = formatDateISOString(startDate);
47
55
  const max = formatDateISOString(endDate);
48
- const query = `${name}::date >= '${min}'::date and ${name}::date <= '${max}'::date`;
49
- return { op: 'between', query };
56
+ const query = extra && pk
57
+ ? `${pk} in (select object_id from crm.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 };
50
60
  }
61
+
51
62
  // my rows
52
63
  if (value === 'me' && uid && fieldType === 'text') {
53
- return { op: '=', query: `${name}::text = '${uid}'` };
64
+ return { op: '=', query: extra ? `uid = '${uid}'` : `${name}::text = '${uid}'`, extra };
54
65
  }
66
+
55
67
  const formatType = {
56
68
  float8: 'numeric',
57
69
  int4: 'numeric',
@@ -68,41 +80,47 @@ function formatValue({
68
80
  query: fieldType?.includes('[]')
69
81
  ? `${optimize.pk} && (select array_agg(${optimize.pk}) from ${optimize.table} where ${name} ${val} )`
70
82
  : `${optimize.pk} in (select ${optimize.pk} from ${optimize.table} where ${name} ${val} )`,
83
+ extra,
71
84
  };
72
85
  }
73
86
 
74
- if (filter.sql) {
75
- return { op: '~', query: filter.sql.replace('{val}', value) };
76
- }
77
87
  if (fieldType?.includes('[]')) {
78
- return { op: 'in', query: `'{${value}}'::text[] && ${name}::text[]` };
88
+ return { op: 'in', query: `'{${value}}'::text[] && ${name}::text[]`, extra };
79
89
  }
90
+
80
91
  // multiple items of 1 param
81
92
  if (value?.indexOf(',') !== -1) {
82
93
  const values = value.split(',').filter((el) => el !== 'null');
83
- const query = value?.indexOf('null' !== -1)
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
84
101
  ? `( ${name} is null or ${name}::text in (${values?.map((el) => `'${el}'`).join(',')}) )`
85
102
  : `${name}::text in (${value.split(',')?.map((el) => `'${el}'`).join(',')})`;
86
- return { op: 'in', query };
103
+ return { op: 'in', query, extra };
87
104
  }
88
105
 
89
106
  // v3 filter number range, example - "100_500"
90
107
  if (numberTypeList.includes(fieldType) && value?.indexOf('_') !== -1) {
91
108
  const [min, max] = value.split('_');
92
109
  const query = (max === 'max' ? `${name} > ${min}` : null) || (min === 'min' ? `${name} < ${max}` : null) || `${name} between ${min} and ${max}`;
93
- return { op: 'between', query };
110
+ return { op: 'between', query, extra };
94
111
  }
112
+
95
113
  // number range
96
114
  if (numberTypeList.includes(fieldType) && value?.indexOf('-') !== -1) {
97
115
  const [min, max] = value.split('-');
98
116
  if (min === 'min' && max === 'max') return {};
99
117
  const query = (max === 'max' ? `${name} > ${min}` : null) || (min === 'min' ? `${name} < ${max}` : null) || `${name} between ${min} and ${max}`;
100
- return { op: 'between', query };
118
+ return { op: 'between', query, extra };
101
119
  }
102
120
 
103
121
  if (['<', '>'].includes(operator)) {
104
122
  const query = `${name} ${operator} '${value}'::${formatType}`;
105
- return { op: operator, query };
123
+ return { op: operator, query, extra };
106
124
  }
107
125
 
108
126
  if (operator === '=' && filterType !== 'text' && !filter?.data) {
@@ -110,12 +128,19 @@ function formatValue({
110
128
  null: `${name} is null`,
111
129
  notnull: `${name} is not null`,
112
130
  }[value] || `${name}::${formatType}='${value}'::${formatType}`;
113
- return { op: '=', query };
131
+ return { op: '=', query, extra };
114
132
  }
115
133
 
116
134
  if (['~', '='].includes(operator)) {
117
135
  const operator1 = (filterType === 'text' && (filter?.id || filter?.name) && operator === '=' ? '~' : operator);
118
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
+
119
144
  const query = filter?.data && filter?.sql
120
145
  ? `${filter?.name || filter?.id} in ( ( with q(id,name) as (${filter?.sql}) select id from q where name::text ${match}) )` // filter with cls
121
146
  : `${name}::text ${match}`; // simple filter
@@ -127,7 +152,7 @@ function formatValue({
127
152
  if (name.includes('.')) {
128
153
  const [col, prop] = name.split('.');
129
154
  const query = ` ${col}->>'${prop}' in ('${value.join("','")}')`;
130
- return { op: 'in', query };
155
+ return { op: 'in', query, extra };
131
156
  }
132
157
 
133
158
  // geometry
@@ -136,7 +161,7 @@ function formatValue({
136
161
 
137
162
  if (bbox?.length === 4) {
138
163
  const query = ` ${name} && 'box(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d `;
139
- return { op: '&&', query };
164
+ return { op: '&&', query, extra };
140
165
  }
141
166
  }
142
167
  return {};
@@ -1,4 +1,5 @@
1
1
  /* eslint-disable no-continue */
2
+
2
3
  /**
3
4
  * @param {Number} opt.json - (1|0) 1 - Результат - Object, 0 - String
4
5
  * @param {String} opt.query - запит до таблиці
@@ -8,18 +9,13 @@
8
9
  import formatValue from './formatValue.js';
9
10
 
10
11
  function getQuery({
11
- filter: filterStr, tableSQL, fields, filterList, config = {}, pg,
12
+ pg, filter: filterStr, table, tableSQL, fields, filterList,
12
13
  }) {
13
14
  if (!filterStr) return null; // filter list API
14
15
 
15
16
  const mainOperators = ['=', '~', '>', '<'];
16
17
 
17
- // v3 filter
18
- const { allTemplates } = config;
19
-
20
- const filterQueryArray = config.v3?.filter
21
- ? decodeURI(filterStr?.replace(/(^,)|(,$)/g, '')).replace(/'/g, '').split(/[;|]/)
22
- : decodeURI(filterStr?.replace(/(^,)|(,$)/g, '')?.replace(/,null/g, '')).replace(/'/g, '').split(/[;|]/);
18
+ const filterQueryArray = decodeURI(filterStr?.replace(/(^,)|(,$)/g, '')).replace(/'/g, '').split(/[;|]/);
23
19
 
24
20
  const resultList = [];
25
21
 
@@ -33,6 +29,9 @@ function getQuery({
33
29
  continue;
34
30
  }
35
31
 
32
+ // filter
33
+ const filter = filterList?.find((el) => [el.id, el.name].includes(name)) || { type: 'text' };
34
+
36
35
  // find all value
37
36
  const value = filterQueryArray.filter((el) => el.startsWith(name)).map((el) => el.substring(name.length + 1)).join(',');
38
37
 
@@ -40,30 +39,24 @@ function getQuery({
40
39
 
41
40
  // find field and skip not exists
42
41
  const { dataTypeID } = fields?.find((el) => el.name === name) || fields?.find((el) => el.name === optimize?.pk) || {};
43
- if (!dataTypeID) continue;
44
42
 
45
- const type = pg.pgType?.[dataTypeID];
46
-
47
- // filter
48
- const filter = filterList?.find((el) => el.id === name) || { type: 'text' };
49
- const filterType = filter.type?.toLowerCase();
50
43
  // format query
51
-
52
- const { op, query } = formatValue({
53
- clsList: allTemplates?.cls || [],
44
+ const {
45
+ op, query, filterType, fieldType,
46
+ } = formatValue({
47
+ pg,
48
+ table,
54
49
  filter,
55
50
  optimize,
56
- filterType,
57
51
  name,
58
- value: decodeURIComponent(value), // decodeURIComponent(value)?.replace(new RegExp(String.raw`\b${name}=\b`, 'g'), '') for checkboxes?
52
+ value: decodeURIComponent(value),
59
53
  operator,
60
- fieldType: type || 'text',
54
+ dataTypeID,
61
55
  }) || {};
62
- // console.log({ query, value });
63
56
  if (!query) continue;
64
57
 
65
58
  resultList.push({
66
- name, value, query, operator: op, filterType, type,
59
+ name, value, query, operator: op, filterType, type: fieldType,
67
60
  });
68
61
  }
69
62
 
@@ -1,72 +1,72 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert';
3
-
4
- import build from '../../helper.js';
5
-
6
- import setToken from '../../crud/funcs/setToken.js';
7
- import config from '../config.js';
8
-
9
- test('api crud xss', async (t) => {
10
- const app = await build(t);
11
- const session = { passport: { user: { uid: '1' } } };
12
- app.addHook('onRequest', async (req) => {
13
- req.session = session;
14
- });
15
- // app.decorateRequest('session', session);
16
-
17
- const prefix = config.prefix || '/api';
18
-
19
- let addTokens;
20
- let editTokens;
21
-
22
- // before
23
- t.test('setToken', async () => {
24
- addTokens = setToken({
25
- ids: [JSON.stringify({ add: 'gis.dataset', form: 'test.dataset.form' })],
26
- mode: 'a',
27
- uid: 1,
28
- array: 1,
29
- });
30
- editTokens = setToken({
31
- ids: [JSON.stringify({ id: '5400000', table: 'gis.dataset', form: 'test.dataset.form' })],
32
- mode: 'w',
33
- uid: 1,
34
- array: 1,
35
- });
36
- assert.ok(addTokens.length === 1 && editTokens.length === 1, 'invalid token');
37
- });
38
-
39
- await t.test('POST /insert', async () => {
40
- const res = await app.inject({
41
- method: 'POST',
42
- url: `${prefix}/table/${addTokens[0]}`,
43
- body: { dataset_name: '<a onClick="alert("XSS Injection")">xss injection</a>', dataset_id: '5400000' },
44
- });
45
-
46
- const rep = JSON.parse(res?.body);
47
- console.log(rep)
48
- assert.ok(rep.status, 409);
49
- });
50
-
51
- await t.test('PUT /update', async () => {
52
- const res = await app.inject({
53
- method: 'PUT',
54
- url: `${prefix}/table/${editTokens[0]}/${editTokens[0]}`,
55
- body: { editor_id: '11', dataset_name: '<a onClick="alert("XSS Injection")">xss injection</a>' },
56
- });
57
-
58
- const rep = JSON.parse(res?.body);
59
- console.log(rep)
60
- assert.equal(rep.status, 409);
61
- });
62
- await t.test('DELETE /delete', async () => {
63
- const res = await app.inject({
64
- method: 'DELETE',
65
- url: `${prefix}/table/gis.dataset/5400000`,
66
- });
67
-
68
- const rep = JSON.parse(res?.body);
69
- console.log(rep)
70
- assert.ok(rep);
71
- });
72
- });
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import build from '../../helper.js';
5
+
6
+ import setToken from '../../crud/funcs/setToken.js';
7
+ import config from '../config.js';
8
+
9
+ test('api crud xss', async (t) => {
10
+ const app = await build(t);
11
+ const session = { passport: { user: { uid: '1' } } };
12
+ app.addHook('onRequest', async (req) => {
13
+ req.session = session;
14
+ });
15
+ // app.decorateRequest('session', session);
16
+
17
+ const prefix = config.prefix || '/api';
18
+
19
+ let addTokens;
20
+ let editTokens;
21
+
22
+ // before
23
+ t.test('setToken', async () => {
24
+ addTokens = setToken({
25
+ ids: [JSON.stringify({ add: 'gis.dataset', form: 'test.dataset.form' })],
26
+ mode: 'a',
27
+ uid: 1,
28
+ array: 1,
29
+ });
30
+ editTokens = setToken({
31
+ ids: [JSON.stringify({ id: '5400000', table: 'gis.dataset', form: 'test.dataset.form' })],
32
+ mode: 'w',
33
+ uid: 1,
34
+ array: 1,
35
+ });
36
+ assert.ok(addTokens.length === 1 && editTokens.length === 1, 'invalid token');
37
+ });
38
+
39
+ await t.test('POST /insert', async () => {
40
+ const res = await app.inject({
41
+ method: 'POST',
42
+ url: `${prefix}/table/${addTokens[0]}`,
43
+ body: { dataset_name: '<a onClick="alert("XSS Injection")">xss injection</a>', dataset_id: '5400000' },
44
+ });
45
+
46
+ const rep = JSON.parse(res?.body);
47
+ console.log(rep)
48
+ assert.ok(rep.status, 409);
49
+ });
50
+
51
+ await t.test('PUT /update', async () => {
52
+ const res = await app.inject({
53
+ method: 'PUT',
54
+ url: `${prefix}/table/${editTokens[0]}/${editTokens[0]}`,
55
+ body: { editor_id: '11', dataset_name: '<a onClick="alert("XSS Injection")">xss injection</a>' },
56
+ });
57
+
58
+ const rep = JSON.parse(res?.body);
59
+ console.log(rep)
60
+ assert.equal(rep.status, 409);
61
+ });
62
+ await t.test('DELETE /delete', async () => {
63
+ const res = await app.inject({
64
+ method: 'DELETE',
65
+ url: `${prefix}/table/gis.dataset/5400000`,
66
+ });
67
+
68
+ const rep = JSON.parse(res?.body);
69
+ console.log(rep)
70
+ assert.ok(rep);
71
+ });
72
+ });
@@ -5,34 +5,72 @@ import init from '../../pg/funcs/init.js';
5
5
 
6
6
  import build from '../../helper.js';
7
7
  import config from '../config.js';
8
+ import { getTemplate } from '../../utils.js';
9
+
10
+ const table = 'test.rest_zone.table';
8
11
 
9
12
  test('api table', async (t) => {
10
13
  const app = await build(t);
11
14
  await init(pgClients.client);
12
15
 
16
+ const body = await getTemplate('table', table);
17
+ assert.equal(body?.table, 'itree.rest_zones');
18
+ assert.ok(pgClients.client?.pk?.[body?.table], 'invalid db - skip filter unit test');
19
+
20
+ if (pgClients.client?.pk?.[body?.table]) {
21
+ const custom = body?.filterCustom?.[0] || {};
22
+ assert.ok(custom.name && custom.sql, 'invalid test template - empty filterCustom');
23
+ const state = body?.filterState?.[0] || {};
24
+ assert.ok(state.name && state.sql, 'invalid test template - empty filterState');
25
+
26
+ await t.test('GET /data (filter + custom + state + sql)', async () => {
27
+ const res = await app.inject({
28
+ method: 'GET',
29
+ url: `${config.prefix || '/api'}/data/${table}`,
30
+ query: {
31
+ custom: custom.name,
32
+ state: state.name,
33
+ filter: 'balancer~1;composition=1',
34
+ sql: 1,
35
+ },
36
+ });
37
+ assert.ok(res.body?.includes(custom.sql), 'filterCustom not ok');
38
+ assert.ok(res.body?.includes(state.sql), 'filterState not ok');
39
+ assert.ok(res.body?.includes('balancer::text ilike \'%1%\' and \'{1}\'::text[] && composition::text[]'), 'filter not ok');
40
+ });
41
+ }
42
+
13
43
  const bbox = '20.276526313524393 40.6651677831094,35.27752631352439 50.66616778310941';
14
- const { count = 0 } = await pgClients.client.query(`select count(*) from gis.dataset
15
- where geom && 'box(${bbox})'::box2d`)
16
- .then((res) => res.rows[0] || {});
44
+ const { count = 0 } = pgClients.client?.pk[body?.table] ? await pgClients.client.query(`select count(*) from itree.rest_zones
45
+ where verif and '{1}'::text[] && composition::text[] and geom && 'box(${bbox})'::box2d`).then((res) => res.rows[0] || {}) : {};
17
46
 
18
47
  await t.test('GET /data (meta bbox / cls)', async () => {
19
48
  const res = await app.inject({
20
49
  method: 'GET',
21
- url: `${config.prefix || '/api'}/data/test.dataset.table?bbox=${bbox}`,
50
+ url: `${config.prefix || '/api'}/data/${table}`,
51
+ query: { bbox, filter: 'composition=1' },
22
52
  });
23
53
  const json = res.json();
24
- assert.ok(json?.rows?.length === +count, 'meta bbox - not ok');
25
- assert.ok(json?.rows?.length ? json?.rows?.[0]?.dataset_id_text : true, 'meta cls - not ok');
54
+ const res1 = await app.inject({
55
+ method: 'GET',
56
+ url: `${config.prefix || '/api'}/data/${table}`,
57
+ query: { bbox, sql: 1 },
58
+ });
59
+ const { body: sql } = res1;
60
+ assert.ok(sql?.includes(bbox), 'meta bbox sql - not ok');
61
+ assert.ok(json?.rows?.length && +count >= json?.rows?.length, 'meta bbox - not ok');
62
+ assert.ok(json?.rows?.length ? json?.rows?.[0]?.composition_text : true, 'meta cls - not ok');
26
63
  });
27
64
 
28
65
  const polyline = 'wfvkH_jsvCoKvj@oKfiB?fw@?nd@?nK?nK~WgEfE?wQfw@gEfE?vQwQvj@wQvcAoKvQoKnKgEnd@_cBgw@wj@fEwj@nKg^vQg^oK?vQgEnKgpAvj@?vcAfEf^fEf^?vj@vQvj@vQgE~Wvj@?~p@gE~iAfEnK?~iA_XnvA?nd@fEf^~p@~bBvQ?nd@wQ?vQfEf^fEnK?vQ?vj@oKfEoKfEvQnd@nKvj@?vQfEvQgEf^gEf^?vQfEvQfEvQfE~W?fE?fE?~W?f^?nd@?nK?nKfEfEg^oKwj@nK_X~W~p@~WvQnKoKn}@wQvj@oK~Wg^vcAgEnK?~Wfw@vgD~p@oKfpAvQnd@nKoKvQoKvQ?fE?fEnKvj@wQvQ?fEod@fw@~Wn}@fEn}@_Xv|A_jA~fEwQwQ_XoKwQoKoKoKoKoKg^gE_XgEoKg^gEgEgE?gEfEgEfEgEf^_XoKwQ?gEgEoK?wQ?gE~WoKoKoKwQfEgEnK_X?gEgE?oKfEoKoKgE_XgEgEwQfEoKod@_Xgw@gEoKgE_Xg^wQoKg^_XnvAgE?gEvQgE?od@fpA?vQ?f^oKvcAwj@oKg^?wQn}@gEf^oK?w|AnKgE??nKwj@gEgEg^oKwQoKoK?gEwQoaD?oKod@oKod@gEod@wQo}@oKfEf^gEfEgpAnd@_XfEgE?wQnKo}@_q@?gEgEwj@?oK?oK?_X?oKfEoK?oKfEoK?oK?oK?gEfEoKwQfEoK?gEwQwj@nKo}@nKwQ~Wod@nKoKfEgw@ovAgEwQgE??oKoKgEoKgEgEwQ?oKfEoKnKwQfEwQoK?g^fE?wQfEg^fEod@gE??fEoKvQ?nKoKnKgEfEgEgE?g^nK_q@nKod@?oKoKoK~Wgw@fEoKnd@fE?_XfEoKfEwQfEoKfEwQfEoKvQ?fEfE?vj@gE~W~WvQvQwj@vQvQf^o}@nKvQ?_XgEg^gEg^nKod@?wQgEgEgE?oK_XgEoK?oKgEoKgE_XoKg^oKg^oKod@?gEfEoKvQgEnK?nKfE?_Xnd@gw@fEwQf^wj@fEwQnd@w|A?gEgEgEgEwQfEoKfEgEnKoKnKwQfEgE?oKfEoKnKgEnKwQ_XwQnKwcAod@wQvQod@~Wgw@nKgw@oKg^f^ovAgEwQ?_X?od@gEwj@gEwQvQg^fEoKnKoKvQnK~WfEnKvQ~Wod@f^wj@~WoKnKg^~W?vQgE~WoKnKoK~WoKfEwQvQwQfEgEnKwQnKwQvQwQnKoKnKgEnKgEnKgEvQnKnK?fE?fE?~WwQnKgEnKgEfE?fE?nK?fE?fE?vQgEfE?nKfEfE?fE?vQ?~W?~WgEfEgEnKgEnKwQfEnKnKfE?wj@?o}@g^o}@_Xod@?gEf^_X?o}@?wQ~W?~WgE~WgEfE?f^gEf^oK~Wg^nKgE?oK~WwQ?od@nK_XfEwQnKoKfEgEfE~W?f^f^fEfEfEnKfEnK?gEf^vQfEnK?~WnKvQnK?vQf^nKfEfEgE~WnKfEnK?fEgEfE?nK?fEfEfEgEnK?nKgEnK?nKfEf^?vQgEvQ?nd@gEnKo}@vQgE~WgEf^gE'; // UA26040270000047749
29
66
 
30
- const { count1 } = await pgClients.client.query('select count(*) as count1 from gis.dataset where ST_Contains(ST_MakePolygon(ST_LineFromEncodedPolyline($1)),geom)', [polyline])
67
+ const { count1 } = await pgClients.client.query('select count(*) as count1 from itree.rest_zones where verif and ST_Contains(ST_MakePolygon(ST_LineFromEncodedPolyline($1)),geom)', [polyline])
31
68
  .then((res) => res.rows[0] || {});
32
69
  await t.test('GET /data (meta polyline)', async () => {
33
70
  const res = await app.inject({
34
71
  method: 'GET',
35
- url: `${config.prefix || '/api'}/data/test.dataset.table?polyline=${polyline}`,
72
+ url: `${config.prefix || '/api'}/data/${table}`,
73
+ query: { polyline },
36
74
  });
37
75
  const json = res.json();
38
76
  assert.ok(json?.rows?.length === +count1, 'meta bbox (polyline) - not ok');
@@ -1,18 +1,18 @@
1
- import config from '../config.js';
2
-
3
- Object.assign(config, {
4
- folder: 'test/templates',
5
- pg: {
6
- host: '192.168.3.160',
7
- port: 5434,
8
- database: 'mbk_rivne_dma',
9
- user: 'postgres',
10
- password: 'postgres',
11
- },
12
- redis: {
13
- host: '192.168.3.160',
14
- port: 6379,
15
- family: 4,
16
- },
17
- });
18
- export default config;
1
+ import config from '../config.js';
2
+
3
+ Object.assign(config, {
4
+ folder: 'test/templates',
5
+ pg: {
6
+ host: '192.168.3.160',
7
+ port: 5434,
8
+ database: 'mbk_rivne_dma',
9
+ user: 'postgres',
10
+ password: 'postgres',
11
+ },
12
+ redis: {
13
+ host: '192.168.3.160',
14
+ port: 6379,
15
+ family: 4,
16
+ },
17
+ });
18
+ export default config;
@@ -1,34 +1,34 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert';
3
-
4
- import '../config.js';
5
-
6
- import getMeta from '../../pg/funcs/getMeta.js';
7
- import autoIndex from '../../pg/funcs/autoIndex.js';
8
- import pgClients from '../../pg/pgClients.js';
9
- import rclient from '../../redis/client.js';
10
- // import pgClients from '../../pg/funcs/pgClients.js';
11
-
12
- test('funcs pg', async (t) => {
13
- await pgClients.client.init();
14
- await t.test('getMeta', async () => {
15
- const { columns } = await getMeta({ table: 'gis.dataset' });
16
- // console.log(columns)
17
- assert.ok(columns);
18
- });
19
-
20
- /* await t.test('getPG', async (t) => {
21
- const data = await getPG({});
22
- assert.ok(data);
23
- }); */
24
-
25
- await t.test('autoIndex', async () => {
26
- await autoIndex({ table: 'gis.dataset', columns: ['service_type'] });
27
- assert.ok(1);
28
- });
29
- t.after(() => {
30
- pgClients.client.end();
31
-
32
- rclient.quit();
33
- });
34
- });
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import '../config.js';
5
+
6
+ import getMeta from '../../pg/funcs/getMeta.js';
7
+ import autoIndex from '../../pg/funcs/autoIndex.js';
8
+ import pgClients from '../../pg/pgClients.js';
9
+ import rclient from '../../redis/client.js';
10
+ // import pgClients from '../../pg/funcs/pgClients.js';
11
+
12
+ test('funcs pg', async (t) => {
13
+ await pgClients.client.init();
14
+ await t.test('getMeta', async () => {
15
+ const { columns } = await getMeta({ table: 'gis.dataset' });
16
+ // console.log(columns)
17
+ assert.ok(columns);
18
+ });
19
+
20
+ /* await t.test('getPG', async (t) => {
21
+ const data = await getPG({});
22
+ assert.ok(data);
23
+ }); */
24
+
25
+ await t.test('autoIndex', async () => {
26
+ await autoIndex({ table: 'gis.dataset', columns: ['service_type'] });
27
+ assert.ok(1);
28
+ });
29
+ t.after(() => {
30
+ pgClients.client.end();
31
+
32
+ rclient.quit();
33
+ });
34
+ });
@@ -1,19 +1,19 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert';
3
-
4
- import '../config.js';
5
-
6
- import rclient from '../../redis/client.js';
7
-
8
- test('funcs redis', async (t) => {
9
- await t.test('get/set', async () => {
10
- await rclient.set('test', '1');
11
- const d = await rclient.get('test');
12
- // console.log(columns)
13
- assert.equal(d, '1');
14
- });
15
-
16
- t.after(() => {
17
- rclient.quit();
18
- });
19
- });
1
+ import { test } from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import '../config.js';
5
+
6
+ import rclient from '../../redis/client.js';
7
+
8
+ test('funcs redis', async (t) => {
9
+ await t.test('get/set', async () => {
10
+ await rclient.set('test', '1');
11
+ const d = await rclient.get('test');
12
+ // console.log(columns)
13
+ assert.equal(d, '1');
14
+ });
15
+
16
+ t.after(() => {
17
+ rclient.quit();
18
+ });
19
+ });
@@ -1,10 +1,10 @@
1
- [
2
- {
3
- "id": 1,
4
- "text": "test"
5
- },
6
- {
7
- "id": 2,
8
- "text": "test2"
9
- }
1
+ [
2
+ {
3
+ "id": 1,
4
+ "text": "test"
5
+ },
6
+ {
7
+ "id": 2,
8
+ "text": "test2"
9
+ }
10
10
  ]