@opengis/fastify-table 1.2.69 → 1.2.71

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "1.2.69",
3
+ "version": "1.2.71",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [
@@ -70,7 +70,7 @@ async function init(client) {
70
70
 
71
71
  async function queryCache(q, param = {}) {
72
72
  const { table, args = [], time = 15 } = param;
73
- const seconds = typeof time !== 'number' || time < 0 ? 15 : (config.local ? 0 : time * 60);
73
+ const seconds = typeof time !== 'number' || time < 0 ? 0 : time * 60;
74
74
 
75
75
  // CRUD table state
76
76
  const keyCacheTable = `pg:${table}:crud`;
@@ -92,6 +92,7 @@ async function init(client) {
92
92
  if (seconds > 0) {
93
93
  rclient.set(keyCache, JSON.stringify(data), 'EX', seconds);
94
94
  }
95
+
95
96
  // console.log('no cache', table, crudInc, query);
96
97
  return data;
97
98
  }
@@ -44,16 +44,17 @@ export default async function getFilterSQL({
44
44
  // console.log('extra getFilterSQL', extraDataTable, pg.pk?.[extraDataTable]);
45
45
 
46
46
  // check sql inline fields count
47
- if (!checkInline[body.table] && body.sql?.length && body.table) {
48
- const filterSql = body.sql.filter(el => el.inline ?? true);
49
- const d = await Promise.all(filterSql.map(el => pg.query(`select q.* from(select * from ${body.table})t left join lateral(${el.sql})q on 1=1 limit 0`).then(el => el.fields)))
47
+ if (!checkInline[body?.table] && body?.sql?.length && body.table) {
48
+ const filterSql = body.sql.filter(el => !el?.disabled && (el.inline ?? true));
49
+ const sqlTable = filterSql.map((el, i) => ` left join lateral (${el.sql}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
50
+ const d = await Promise.all(filterSql.map((el, i) => pg.query(`select ${el.name || `t${i}`}.* from(select * from ${body.table})t ${sqlTable} limit 0`).then(el => el.fields)))
50
51
  d.forEach((el, i) => {
51
52
  filterSql[i].inline = el.length == 1 ? true : false;
52
53
  filterSql[i].fields = el.map(f => f.name);
53
54
  });
54
- checkInline[table] = body.sql;
55
- } else if (checkInline[table]) {
56
- body.sql = checkInline[table]
55
+ checkInline[body?.table] = body.sql;
56
+ } else if (checkInline[body?.table]) {
57
+ body.sql = checkInline[body?.table]
57
58
  }
58
59
 
59
60
  const sqlTable = body?.sql?.length
@@ -30,7 +30,9 @@ export default async function getSelectVal({
30
30
  const filteredValues = values.filter(el => !cache[el]);
31
31
 
32
32
  // query select
33
- const q = `with c(id,text) as (select * from (${cls.sql})q where ${id} = any('{${filteredValues}}')) select * from c`;
33
+
34
+ const q = `with c(id,text) as (select * from (${cls.sql})q where ${id} = any('{${filteredValues.join(',').replace(/\"/g, '\\"')}}')) select * from c`;
35
+
34
36
  const data = filteredValues.length ? await pg.query(q).then(el => el.rows) : [];
35
37
 
36
38
  const clsObj = { ...cache, ...data.reduce((p, el) => ({ ...p, [el.id.toString()]: el.color ? el : el.text }), {}) };
@@ -1,92 +1,92 @@
1
- import {
2
- config, getAccess, getTemplate, getMeta, setToken, applyHook, getToken, pgClients,
3
- } from '../../../../utils.js';
4
-
5
- export default async function tableAPI(req) {
6
- const {
7
- pg = pgClients.client, params, user = {}, query = {},
8
- } = req;
9
- const tokenData = await getToken({ token: params?.table, uid: user.uid, json: 1 }) || {};
10
-
11
- const hookData = await applyHook('preTable', {
12
- pg, table: params?.table, id: params?.id, ...tokenData || {}, user,
13
- });
14
-
15
- if (hookData?.message && hookData?.status) {
16
- return { message: hookData?.message, status: hookData?.status };
17
- }
18
- const tableName1 = hookData?.table || tokenData.table || params.table;
19
-
20
- const loadTable = await getTemplate('table', tableName1) || {};
21
- if (!loadTable && !pg.pk?.[tokenData.table]) {
22
- return { message: 'not found', status: 404 };
23
- }
24
-
25
- const { table, /* columns, */ form } = loadTable;
26
-
27
- const templateName = hookData?.table || tokenData.table || params.table;
28
- const tableName = table || templateName;
29
-
30
- const id = hookData?.id || tokenData.id || params.id;
31
-
32
- if (tokenData && !id) return { message: {} };
33
- if (!tableName && !id) {
34
- return { message: 'not enough params', status: 400 };
35
- }
36
-
37
- const { actions = [], query: accessQuery } = await getAccess({ table: templateName, id, user }, pg) || {};
38
-
39
- if (!actions.includes('edit') && !config?.local && !tokenData) {
40
- return { message: 'access restricted', status: 403 };
41
- }
42
-
43
- const { pk, columns: dbColumns = [] } = await getMeta({ pg, table: tableName });
44
- if (!pk) return { message: `table not found: ${table}`, status: 404 };
45
-
46
- // const cols = columns.map((el) => el.name || el).join(',');
47
- const formName = hookData?.form || tokenData?.form || form;
48
- const formData = await getTemplate('form', formName) || {};
49
- const schema = formData?.schema || formData || {};
50
- // skip DataTable from another table
51
- const extraKeys = Object.keys(schema).filter((key) => schema[key]?.type === 'DataTable' && schema[key]?.table && schema[key]?.parent_id && schema[key]?.colModel?.length);
52
- // skip non-existing columns
53
- const columnList = dbColumns.map((el) => el.name || el).join(',');
54
-
55
- const { fields = [] } = !loadTable?.table ? await pg.query(`select * from ${tableName} limit 0`) : {};
56
- const cols = loadTable?.table
57
- ? Object.keys(schema || {}).filter((col) => columnList.includes(col) && !extraKeys.includes(col))?.map((col) => (col?.includes('geom') && schema?.[col]?.type === 'Geom' ? `st_asgeojson(${col})::json as "${col}"` : `"${col}"`))?.join(',')
58
- : fields.map((el) => (el?.name?.includes('geom') && pg.pgType[el?.dataTypeID] === 'geometry' ? `st_asgeojson(${el.name})::json as "${el.name}"` : `"${el?.name}"`)).join(',');
59
- const where = [`"${pk}" = $1`, loadTable.query, accessQuery].filter((el) => el);
60
- const geom = dbColumns.find((el) => el.name === 'geom' && pg.pgType[el.dataTypeID] === 'geometry') ? ',st_asgeojson(geom)::json as geom' : '';
61
- const q = `select "${pk}" as id, ${cols || '*'} ${geom} from ${tableName} t where ${where.join(' and ') || 'true'} limit 1`;
62
-
63
- if (query?.sql === '1') return q;
64
-
65
- const data = await pg.query(q, [id]).then(el => el.rows[0]);
66
- if (!data) return { message: 'not found', status: 404 };
67
-
68
- Object.keys(schema).filter(key => schema[key]?.type === 'DataTable').forEach(key => {
69
- if (data[key] && !Array.isArray(data[key])) { data[key] = null };
70
- });
71
-
72
- if (extraKeys?.length) {
73
- await Promise.all(extraKeys?.map(async (key) => {
74
- const { colModel, table: extraTable, parent_id: parentId } = schema[key];
75
- const q1 = `select ${parentId} as parent, ${colModel.map((col) => col.name || col.key).join(',')} from ${extraTable} a where ${parentId}=$1`;
76
- // console.log(tableName, formName, q1);
77
- const { rows: extraRows } = await pg.query(q1, [hookData?.id || tokenData?.id || params?.id]);
78
- Object.assign(data, { [key]: extraRows });
79
- }));
80
- }
81
- if (user?.uid && actions?.includes?.('edit')) {
82
- data.token = tokenData?.table ? params.table : setToken({
83
- ids: [JSON.stringify({ id, table: tableName, form: loadTable.form })],
84
- uid: user.uid,
85
- array: 1,
86
- })[0];
87
- }
88
- const res = await applyHook('afterTable', {
89
- pg, table: tableName, payload: [data], user,
90
- });
91
- return res || data || {};
92
- }
1
+ import {
2
+ config, getAccess, getTemplate, getMeta, setToken, applyHook, getToken, pgClients,
3
+ } from '../../../../utils.js';
4
+
5
+ export default async function tableAPI(req) {
6
+ const {
7
+ pg = pgClients.client, params, user = {}, query = {},
8
+ } = req;
9
+ const tokenData = await getToken({ token: params?.table, uid: user.uid, json: 1 }) || {};
10
+
11
+ const hookData = await applyHook('preTable', {
12
+ pg, table: params?.table, id: params?.id, ...tokenData || {}, user,
13
+ });
14
+
15
+ if (hookData?.message && hookData?.status) {
16
+ return { message: hookData?.message, status: hookData?.status };
17
+ }
18
+ const tableName1 = hookData?.table || tokenData.table || params.table;
19
+
20
+ const loadTable = await getTemplate('table', tableName1) || {};
21
+ if (!loadTable && !pg.pk?.[tokenData.table]) {
22
+ return { message: 'not found', status: 404 };
23
+ }
24
+
25
+ const { table, /* columns, */ form } = loadTable;
26
+
27
+ const templateName = hookData?.table || tokenData.table || params.table;
28
+ const tableName = table || templateName;
29
+
30
+ const id = hookData?.id || tokenData.id || params.id;
31
+
32
+ if (tokenData && !id) return { message: {} };
33
+ if (!tableName && !id) {
34
+ return { message: 'not enough params', status: 400 };
35
+ }
36
+
37
+ const { actions = [], query: accessQuery } = await getAccess({ table: templateName, id, user }, pg) || {};
38
+
39
+ if (!actions.includes('edit') && !config?.local && !tokenData) {
40
+ return { message: 'access restricted', status: 403 };
41
+ }
42
+
43
+ const { pk, columns: dbColumns = [] } = await getMeta({ pg, table: tableName });
44
+ if (!pk) return { message: `table not found: ${table}`, status: 404 };
45
+
46
+ // const cols = columns.map((el) => el.name || el).join(',');
47
+ const formName = hookData?.form || tokenData?.form || form;
48
+ const formData = await getTemplate('form', formName) || {};
49
+ const schema = formData?.schema || formData || {};
50
+ // skip DataTable from another table
51
+ const extraKeys = Object.keys(schema).filter((key) => schema[key]?.type === 'DataTable' && schema[key]?.table && schema[key]?.parent_id && schema[key]?.colModel?.length);
52
+ // skip non-existing columns
53
+ const columnList = dbColumns.map((el) => el.name || el).join(',');
54
+
55
+ const { fields = [] } = !loadTable?.table ? await pg.query(`select * from ${tableName} limit 0`) : {};
56
+ const cols = loadTable?.table
57
+ ? Object.keys(schema || {}).filter((col) => columnList.includes(col) && !extraKeys.includes(col))?.map((col) => (col?.includes('geom') && schema?.[col]?.type === 'Geom' ? `st_asgeojson(${col})::json as "${col}"` : `"${col}"`))?.join(',')
58
+ : fields.map((el) => (el?.name?.includes('geom') && pg.pgType[el?.dataTypeID] === 'geometry' ? `st_asgeojson(${el.name})::json as "${el.name}"` : `"${el?.name}"`)).join(',');
59
+ const where = [`"${pk}" = $1`, loadTable.query, accessQuery].filter((el) => el);
60
+ const geom = dbColumns.find((el) => el.name === 'geom' && pg.pgType[el.dataTypeID] === 'geometry') ? ',st_asgeojson(geom)::json as geom' : '';
61
+ const q = `select "${pk}" as id, ${cols || '*'} ${geom} from ${tableName} t where ${where.join(' and ') || 'true'} limit 1`;
62
+
63
+ if (query?.sql === '1') return q;
64
+
65
+ const data = await pg.query(q, [id]).then(el => el.rows[0]);
66
+ if (!data) return { message: 'not found', status: 404 };
67
+
68
+ Object.keys(schema).filter(key => schema[key]?.type === 'DataTable').forEach(key => {
69
+ if (data[key] && !Array.isArray(data[key])) { data[key] = null };
70
+ });
71
+
72
+ if (extraKeys?.length) {
73
+ await Promise.all(extraKeys?.map(async (key) => {
74
+ const { colModel, table: extraTable, parent_id: parentId } = schema[key];
75
+ const q1 = `select ${parentId} as parent, ${colModel.map((col) => col.name || col.key).join(',')} from ${extraTable} a where ${parentId}=$1`;
76
+ // console.log(tableName, formName, q1);
77
+ const { rows: extraRows } = await pg.query(q1, [hookData?.id || tokenData?.id || params?.id]);
78
+ Object.assign(data, { [key]: extraRows });
79
+ }));
80
+ }
81
+ if (user?.uid && actions?.includes?.('edit')) {
82
+ data.token = tokenData?.table ? params.table : setToken({
83
+ ids: [JSON.stringify({ id, table: tableName, form: loadTable.form })],
84
+ uid: user.uid,
85
+ array: 1,
86
+ })[0];
87
+ }
88
+ const res = await applyHook('afterTable', {
89
+ pg, table: tableName, payload: [data], user,
90
+ });
91
+ return res || data || {};
92
+ }
@@ -19,7 +19,7 @@ export default async function dataAPI(req, reply, called) {
19
19
  } = req;
20
20
 
21
21
  const time = Date.now();
22
-
22
+ const timeArr = [Date.now()];
23
23
  const { uid } = user;
24
24
 
25
25
  const hookData = await applyHook('preData', {
@@ -63,7 +63,7 @@ export default async function dataAPI(req, reply, called) {
63
63
  } = loadTable || tokenData;
64
64
 
65
65
  const tableMeta = await getMeta({ pg, table });
66
-
66
+ timeArr.push(Date.now())
67
67
  if (tableMeta?.view) {
68
68
  if (!loadTable?.key && !tokenData?.key) return { message: `key not found: ${table}`, status: 404 };
69
69
  Object.assign(tableMeta, { pk: loadTable?.key || tokenData?.key });
@@ -78,7 +78,8 @@ export default async function dataAPI(req, reply, called) {
78
78
  const cardSqlFiltered = hookData?.id || params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
79
79
  const cardSqlTable = cardSqlFiltered.length ? cardSqlFiltered.map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', uid)}) ct${i} on 1=1 `).join('\n') || '' : '';
80
80
 
81
- const { fields = [] } = pg.queryCache ? await pg.queryCache(`select * from ${table} t ${sqlTable} ${cardSqlTable} limit 0`) : {};
81
+ const sqlInline = loadTable.sql?.filter?.(el => el.inline).map(el => `,(${el.sql})`).join('');
82
+ const { fields = [] } = pg.queryCache ? await pg.queryCache(`select * ${sqlInline} from ${table} t ${sqlTable} ${cardSqlTable} limit 0`) : {};
82
83
  const dbColumnsTable = fields.map(el => el.name);
83
84
  const cols = columns.filter((el) => el.name !== 'geom' && dbColumnsTable.includes(el.name)).map((el) => el.name || el).join(',');
84
85
  const metaCols = Object.keys(loadTable?.meta?.cls || {}).filter((el) => !cols.includes(el)).length
@@ -109,10 +110,10 @@ export default async function dataAPI(req, reply, called) {
109
110
  uid,
110
111
  json: 1,
111
112
  }) : {};
112
-
113
+ timeArr.push(Date.now())
113
114
  const keyQuery = query.key && (loadTable?.key || tokenData?.key) && !(hookData?.id || tokenData?.id || params.id) ? `${loadTable?.key || tokenData?.key}=$1` : null;
114
115
 
115
- const limit = Math.min(maxLimit, +(query.limit || 20));
116
+ const limit = called ? (query.limit || 20) : Math.min(maxLimit, +(query.limit || 20));
116
117
 
117
118
  const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
118
119
  // id, query, filter
@@ -135,11 +136,9 @@ export default async function dataAPI(req, reply, called) {
135
136
  const q = `select ${pk ? `"${pk}" as id,` : ''}
136
137
  ${params.id || query.key ? '*' : sqlColumns || cols || '*'}
137
138
  ${metaCols}
138
- ${sql?.filter?.(el => el.inline && el.fields?.length)?.length
139
- ? `,${sql?.filter(el => el.inline && el.fields).map(el => el.fields).join(',')}` || ''
140
- : ''}
139
+
141
140
  ${dbColumns.find((el) => el.name === 'geom' && pg.pgType?.[el.dataTypeID] === 'geometry') ? ',st_asgeojson(geom)::json as geom' : ''}
142
- from (select * ${sql?.filter(el => el.inline).map(el => `,(${el.sql})`).join('') || ''} from ${table} t where ${sqlTable ? 'true' : (where.join(' and ') || 'true')} ) t
141
+ from (select * ${sql?.filter(el => el.inline).map(el => `,(${el.sql})`).join('') || ''} from ${table} t ) t
143
142
  ${sqlTable}
144
143
  ${params.id ? cardSqlTable : ''}
145
144
  where ${where.join(' and ') || 'true'}
@@ -152,6 +151,8 @@ export default async function dataAPI(req, reply, called) {
152
151
 
153
152
  const { rows = [] } = await pg.query(q, (tokenData?.id || hookData?.id || params.id ? [tokenData?.id || hookData?.id || params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
154
153
 
154
+ timeArr.push(Date.now())
155
+
155
156
  if (uid && rows.length && editable) {
156
157
  rows.forEach(row => {
157
158
  row.token = setToken({
@@ -178,15 +179,16 @@ export default async function dataAPI(req, reply, called) {
178
179
 
179
180
  const counts = keyQuery || tokenData?.id || hookData?.id || params.id
180
181
  ? { total: rows.length, filtered: rows.length }
181
- : await pg.queryCache?.(qCount, { table: loadTable?.table || tokenData?.table, time: 5 }).then(el => el?.rows[0] || {});
182
+ : await pg.queryCache?.(qCount, { table: loadTable?.table || tokenData?.table, time: 5 * 60 }).then(el => el?.rows[0] || {});
182
183
 
184
+ timeArr.push(Date.now())
183
185
  const { total, filtered } = counts || {};
184
186
  const agg = Object.keys(counts).filter(el => !['total', 'filtered'].includes(el)).reduce((acc, el) => ({ ...acc, [el]: counts[el] }), {});
185
187
 
186
188
  await extraDataGet({ rows, table: loadTable?.table, form }, pg);
187
189
 
188
190
  await metaFormat({ rows, table: tokenData?.table || hookData?.table || params.table }, pg);
189
-
191
+ timeArr.push(Date.now())
190
192
  const status = [];
191
193
  if (loadTable?.meta?.status) {
192
194
  const statusColumn = loadTable.meta?.cls?.[loadTable.meta?.status]
@@ -273,7 +275,15 @@ export default async function dataAPI(req, reply, called) {
273
275
  }
274
276
 
275
277
  const res = {
276
- time: Date.now() - time,
278
+ time: {
279
+ total: Date.now() - time,
280
+ init: timeArr[1] - timeArr[0],
281
+ filter: timeArr[2] - timeArr[1],
282
+ data: timeArr[3] - timeArr[2],
283
+ count: timeArr[4] - timeArr[3],
284
+ format: timeArr[5] - timeArr[4],
285
+ },
286
+
277
287
  public: ispublic,
278
288
  tokens,
279
289
  card: loadTable?.card,
@@ -1,7 +1,5 @@
1
1
  import { logger, autoIndex, getSelect, getFilterSQL, getTemplate, getSelectVal, pgClients } from '../../../../utils.js';
2
2
 
3
- const checkInline = {};
4
-
5
3
  export default async function filterAPI(req) {
6
4
  const time = Date.now();
7
5
 
@@ -12,38 +10,23 @@ export default async function filterAPI(req) {
12
10
  const loadTable = await getTemplate('table', params.table);
13
11
  if (!loadTable) { return { status: 404, message: 'not found' }; }
14
12
 
15
- // check sql inline fields count
16
- if (!checkInline[params.table] && loadTable.sql?.length && loadTable.table) {
17
- const filterSql = loadTable.sql.filter(el => el.inline ?? true);
18
- const d = await Promise.all(filterSql.map(el => pg.query(`select q.* from(select * from ${loadTable.table})t left join lateral(${el.sql})q on 1=1 limit 0`).then(el => el.fields)))
19
- d.forEach((el, i) => {
20
- filterSql[i].inline = el.length == 1 ? true : false;
21
- filterSql[i].fields = el.map(f => f.name);
22
- });
23
- checkInline[params?.table] = loadTable.sql;
24
- } else if (checkInline[params?.table]) {
25
- loadTable.sql = checkInline[params?.table]
26
- }
27
-
28
- const sqlTable = false ? loadTable.sql?.filter?.((el) => !el.inline && !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', user?.uid)}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '' : '';
29
- const sqlInline = loadTable.sql?.filter?.(el => el.inline).map(el => `,(${el.sql})`).join('');
30
- const { fields: columns = [] } = await pg.query(`select q.* from (select * ${sqlInline || ''} from ${loadTable.table} t ${sqlTable} limit 0)q`);
13
+ const sqlTable = loadTable.sql?.filter?.((el) => !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', user?.uid)}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
14
+ const { fields: columns = [] } = await pg.query(`select * from ${loadTable.table} t ${sqlTable} limit 0`);
31
15
  const { fields = [] } = await pg.query(`select * from ${loadTable.table} t limit 0`);
32
16
 
33
17
  const {
34
18
  filter, custom, state, search,
35
19
  } = query;
36
20
 
37
- const { extra } = loadTable?.form ? await getTemplate('form', loadTable?.form) || {} : {};
21
+ // const { extra } = loadTable?.form ? await getTemplate('form', loadTable?.form) || {} : {};
38
22
 
39
- const { optimizedSQL = `select * ${sqlInline || ''} from ${loadTable.table}` } = loadTable?.sql || filter || custom || state || search || extra ? await getFilterSQL({
23
+ const { optimizedSQL = `select * from ${loadTable.table}` } = false ? await getFilterSQL({
40
24
  pg,
41
25
  table: params.table,
42
26
  filter,
43
27
  custom,
44
28
  state,
45
29
  search,
46
- uid: user?.uid,
47
30
  }) : {};
48
31
 
49
32
  const filters = (loadTable?.filter_list || loadTable?.filters || loadTable?.filterList || []).concat(loadTable?.filterSql || []);
@@ -64,8 +47,8 @@ export default async function filterAPI(req) {
64
47
  await Promise.all(filters.filter((el) => el.data && el.id && el.type !== 'Autocomplete').map(async (el) => {
65
48
  const cls = await getSelect(el.data, pg);
66
49
 
50
+ if (!cls || !loadTable.table) return;
67
51
  const { dataTypeID } = columns.find((item) => item.name === el.id) || {};
68
- if (!cls || !loadTable.table || !dataTypeID) return;
69
52
 
70
53
  if (el.extra && el.type === 'select' && Array.isArray(cls)) {
71
54
  const countArr = await pg.query(`select value_text as id, count(*) from crm.extra_data where property_key=$1 and property_entity=$2 group by value_text`, [el.id, params.table]);
@@ -159,4 +142,4 @@ export default async function filterAPI(req) {
159
142
  inline: loadTable?.filterInline,
160
143
  state: loadTable?.filterState,
161
144
  };
162
- }
145
+ }