@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.
- package/Changelog.md +1 -1
- package/README.md +26 -26
- package/config.js +10 -10
- package/cron/controllers/cronApi.js +22 -22
- package/cron/controllers/utils/cronList.js +1 -1
- package/cron/funcs/addCron.js +131 -131
- package/cron/index.js +10 -10
- package/crud/controllers/utils/checkXSS.js +45 -45
- package/crud/controllers/utils/xssInjection.js +72 -72
- package/crud/funcs/getToken.js +27 -27
- package/crud/funcs/isFileExists.js +13 -13
- package/crud/funcs/setToken.js +53 -53
- package/helper.js +4 -2
- package/module/test/cls/itree.composition.json +26 -0
- package/module/test/table/test.rest_zone.table.json +265 -0
- package/notification/controllers/testEmail.js +49 -49
- package/notification/funcs/utils/sendEmail.js +39 -39
- package/package.json +1 -1
- package/pg/funcs/getPG.js +30 -30
- package/redis/funcs/getRedis.js +23 -23
- package/server/migrations/log.sql +80 -80
- package/server/migrations/properties.sql +1 -1
- package/table/controllers/card.js +44 -44
- package/table/controllers/data.js +6 -5
- package/table/controllers/form.js +28 -28
- package/table/controllers/table.js +4 -4
- package/table/funcs/getFilterSQL/index.js +25 -13
- package/table/funcs/getFilterSQL/util/formatValue.js +44 -19
- package/table/funcs/getFilterSQL/util/getFilterQuery.js +14 -21
- package/test/api/crud.xss.test.js +72 -72
- package/test/api/table.test.js +46 -8
- package/test/config.example +18 -18
- package/test/funcs/pg.test.js +34 -34
- package/test/funcs/redis.test.js +19 -19
- package/test/templates/cls/test.json +9 -9
- package/test/templates/form/cp_building.form.json +32 -32
- package/test/templates/select/account_id.json +3 -3
- package/test/templates/select/storage.data.json +2 -2
- package/test/templates/table/gis.dataset.table.json +20 -20
- package/util/controllers/next.id.js +4 -4
- package/util/controllers/properties.get.js +19 -19
- package/util/index.js +23 -23
- package/utils.js +1 -0
|
@@ -19,9 +19,15 @@ function formatDateISOString(date) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
function formatValue({
|
|
22
|
-
|
|
22
|
+
pg, table, filter = {}, name, value, operator = '=', dataTypeID, uid = 1, optimize,
|
|
23
23
|
}) {
|
|
24
|
-
|
|
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 =
|
|
49
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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),
|
|
52
|
+
value: decodeURIComponent(value),
|
|
59
53
|
operator,
|
|
60
|
-
|
|
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
|
+
});
|
package/test/api/table.test.js
CHANGED
|
@@ -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
|
|
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
|
|
50
|
+
url: `${config.prefix || '/api'}/data/${table}`,
|
|
51
|
+
query: { bbox, filter: 'composition=1' },
|
|
22
52
|
});
|
|
23
53
|
const json = res.json();
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
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
|
|
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');
|
package/test/config.example
CHANGED
|
@@ -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;
|
package/test/funcs/pg.test.js
CHANGED
|
@@ -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
|
+
});
|
package/test/funcs/redis.test.js
CHANGED
|
@@ -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
|
]
|