@opengis/fastify-table 1.3.72 → 1.3.74

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.3.72",
3
+ "version": "1.3.74",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [
@@ -0,0 +1,16 @@
1
+ import { basename } from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+
4
+ import menuDirs from './menuDirs.js';
5
+
6
+ export default function addMenu(filepath) {
7
+ if (basename(filepath) !== 'menu.json') {
8
+ throw new Error('addMenu: filepath must be a menu.json file');
9
+ }
10
+
11
+ if (filepath && !menuDirs.includes(filepath) && existsSync(filepath)) {
12
+ menuDirs.push(filepath);
13
+ }
14
+
15
+ return menuDirs;
16
+ }
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
 
3
3
  import userTemplateDir from './userTemplateDir.js';
4
4
  import customTokens from './customTokens.js';
5
- import yml2json from '../../yml/funcs/yml2json.js'
5
+ import yml2json from '../../yml/funcs/yml2json.js';
6
6
 
7
7
  export default function addTemplateDir(dir) {
8
8
  if (dir && !userTemplateDir.includes(dir)) {
@@ -0,0 +1 @@
1
+ export default [];
@@ -5,7 +5,7 @@ import { join } from 'node:path';
5
5
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
6
6
 
7
7
  import {
8
- userTemplateDir, pgClients, applyHook, config,
8
+ menuDirs, pgClients, applyHook, config,
9
9
  } from '../../../../utils.js';
10
10
 
11
11
  const menuCache = [];
@@ -13,17 +13,16 @@ const menuCache = [];
13
13
  // check module dir
14
14
  const moduleDir = join(process.cwd(), 'module');
15
15
  const dirs = existsSync(moduleDir)
16
- ? readdirSync(moduleDir).map(el => join(moduleDir, el))
16
+ ? readdirSync(moduleDir).map(el => join(moduleDir, el, 'menu.json'))
17
17
  : [];
18
18
 
19
19
  const isProduction = process.env.NODE_ENV === 'production';
20
20
  // readMenu();
21
21
 
22
22
  async function readMenu() {
23
- const menuList = dirs.concat(userTemplateDir)
24
- .map(el => join(el, 'menu.json'))
23
+ const menuList = dirs.concat(menuDirs)
25
24
  .filter(el => existsSync(el));
26
- // const list = menuList.map(el => readFileSync(el, 'utf-8')); // sync
25
+
27
26
  const list = menuList.filter((el, idx, arr) => el && arr.indexOf(el) === idx).map(el => readFileSync(el, 'utf-8')); // sync
28
27
  const menus = list.reduce((p, el) => p.concat(JSON.parse(el)), [])
29
28
  .map(el => ({ order: 0, ...el }))
@@ -32,11 +31,12 @@ async function readMenu() {
32
31
  if (!menuCache.length) {
33
32
  menus.forEach(el => menuCache.push(el));
34
33
  }
35
-
36
34
  return menus;
37
35
  }
38
36
 
39
- export default async function adminMenu({ user = {}, session, pg = pgClients.client }, reply) {
37
+ export default async function adminMenu({
38
+ user = {}, session, pg = pgClients.client,
39
+ }, reply) {
40
40
  const time = Date.now();
41
41
 
42
42
  if (!user.uid) {
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
3
 
4
4
  import {
5
- config, getPG, getTemplate, getSelectMeta, getMeta, applyHook, getSelectVal, logger,
5
+ config, getPG, getTemplate, getSelectMeta, getMeta, applyHook, getSelectVal, logger, getSelect,
6
6
  } from '../../../../utils.js';
7
7
 
8
8
  const limit = 50;
@@ -12,12 +12,19 @@ const headers = {
12
12
  'Cache-Control': 'no-cache',
13
13
  };
14
14
 
15
- function getTableColumnMeta(table, column, filtered) {
15
+ async function getTableColumnMeta(tableName, column, filtered, pg) {
16
+ if (!tableName || !column) { return null; }
17
+
18
+ const loadTable = await getTemplate('table', tableName);
19
+ const { data: clsName } = loadTable?.columns?.find?.(el => el.name === column) || {};
20
+ const { arr } = await getSelect(clsName, pg) || {};
21
+
16
22
  const original = {
17
- true: `with c(id,text) as (select ${column}, ${column} from ${table} group by ${column}) select id, text from c`,
18
- false: `with c(id,text) as (select ${column}, ${column}, count(*) from ${table} group by ${column} limit ${limit}) select * from c`,
23
+ true: `with c(id,text) as (select ${column} as id, ${column} as text from ${loadTable?.table || tableName} group by ${column}) select id, text from c`,
24
+ false: `with c(id,text) as (select ${column} as id, ${column} as text, count(*) from ${loadTable?.table || tableName} group by ${column} limit ${limit}) select * from c`,
19
25
  }[!!filtered];
20
26
  return {
27
+ arr,
21
28
  original,
22
29
  searchQuery: '(lower("text") ~ $1 )',
23
30
  pk: 'id',
@@ -41,13 +48,13 @@ export default async function suggest(req) {
41
48
  }
42
49
  }
43
50
 
44
- const [table, column] = params.data?.includes(':') ? params.data.split(':') : [];
51
+ const [table, column] = params.data?.includes(':') ? params.data.split(':') : [query.token, query.column];
45
52
  const selectName = query.sel || query.name || params.data;
46
53
 
47
54
  if (!selectName) return { status: 400, message: 'name is required' };
48
55
 
49
56
  const { body: hookBody } = table || query.token ? await applyHook('preSuggest', { pg: pg1, table: table || query.token }) || {} : {};
50
- const body = await getTemplate('table', table);
57
+ const body = await getTemplate('table', table || query.token);
51
58
  const tableName = hookBody?.table || body?.table || table || query.token;
52
59
 
53
60
  if (table && !pg1.pk?.[tableName]) {
@@ -55,7 +62,7 @@ export default async function suggest(req) {
55
62
  }
56
63
 
57
64
  const tableMeta = await getMeta({ pg: pg1, table: tableName });
58
- const columnExists = (hookBody?.columns || body?.columns || tableMeta?.columns)?.find((col) => col?.name === column);
65
+ const columnExists = (hookBody?.columns || body?.columns || tableMeta?.columns)?.find(col => col?.name === (column || query.column));
59
66
 
60
67
  if (table && (!column || !columnExists)) {
61
68
  return { status: 400, message: 'param name is invalid: 2' };
@@ -65,8 +72,8 @@ export default async function suggest(req) {
65
72
  return { status: 400, message: 'param limit is invalid' };
66
73
  }
67
74
 
68
- const meta = table && column
69
- ? getTableColumnMeta(table, column, query?.key || query?.val)
75
+ const meta = tableName && column
76
+ ? await getTableColumnMeta(table, column, query?.key || query?.val, pg1)
70
77
  : await getSelectMeta({
71
78
  pg: pg1, name: selectName, nocache: query?.nocache, parent,
72
79
  });
@@ -95,17 +102,19 @@ export default async function suggest(req) {
95
102
  const column1 = columns.find(el => el.name === query.column);
96
103
  const args = { table: tableName };
97
104
  if (!column1) return [];
98
- const sqlCls = `select array_agg(distinct value)::text[] from (select ${pg.pgType?.[column1.dataTypeID]?.includes('[]') ? `unnest(${column1.name})` : `${column1.name}`} as value from ${(loadTable?.table || tableName).replace(/'/g, "''")} where ${hookBody?.query || loadTable?.query || '1=1'})q`;
105
+ const sqlCls = query.count
106
+ ? `select value, count(*) from (select ${pg.pgType?.[column1.dataTypeID]?.includes('[]') ? `unnest(${column1.name})` : `${column1.name}`} as value from ${(loadTable?.table || tableName).replace(/'/g, "''")} where ${hookBody?.query || loadTable?.query || '1=1'})q group by value`
107
+ : `select array_agg(distinct value)::text[] from (select ${pg.pgType?.[column1.dataTypeID]?.includes('[]') ? `unnest(${column1.name})` : `${column1.name}`} as value from ${(loadTable?.table || tableName).replace(/'/g, "''")} where ${hookBody?.query || loadTable?.query || '1=1'})q`;
99
108
 
100
109
  if (query.sql && (config.local || user?.user_type?.includes?.('admin'))) {
101
110
  return sqlCls;
102
111
  }
103
112
 
104
- if (pg.pk?.[loadTable?.table || tableName] && column?.name) {
105
- const vals = await pg.queryCache(sqlCls, args).then(el => el.rows?.[0]?.array_agg || []);
106
- // console.log(vals);
113
+ if (pg.pk?.[loadTable?.table || tableName] && column1?.name) {
114
+ const qRes = await pg.queryCache(sqlCls, args);
115
+ const vals = (query.count ? qRes.rows?.map?.(el => el.value?.toString?.()) : qRes.rows?.[0]?.array_agg) || [];
107
116
 
108
- const lower = query.key?.toLowerCase();
117
+ const lower = query.key?.toLowerCase?.();
109
118
  const data1 = query.key || query.val
110
119
  ? arr?.filter((el) => !lower || (el[lang] || el.text)?.toLowerCase()?.indexOf(lower) !== -1)?.filter((el) => !query.val || el.id === query.val)
111
120
  : arr;
@@ -113,6 +122,11 @@ export default async function suggest(req) {
113
122
  const data2 = data1.filter((el) => el.id && vals.includes(el.id.toString()));
114
123
  const data = data2.slice(0, Math.min(query.limit || limit, limit));
115
124
 
125
+ if (data.length && query.count) {
126
+ const counts = qRes.rows?.reduce?.((acc, curr) => ({ ...acc, [curr.value]: curr.count }), {});
127
+ data.forEach(el => Object.assign(el, { count: counts?.[el.id] || 0 }));
128
+ }
129
+
116
130
  if (config.debug) {
117
131
  logger.file('suggest/debug', {
118
132
  type: 1,
@@ -41,6 +41,7 @@ const suggestSchema = {
41
41
  // key: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
42
42
  val: { type: 'string', pattern: '([\\d\\w]+)$' },
43
43
  sql: { type: 'string', pattern: '^(\\d)$' },
44
+ count: { type: 'string', pattern: '^(\\d)$' },
44
45
  },
45
46
  params: {
46
47
  // data: { type: 'string', pattern: '^([\\d\\w]+)$' },
package/utils.js CHANGED
@@ -23,6 +23,8 @@ import getTemplateSync from './server/plugins/table/funcs/getTemplateSync.js';
23
23
  import getTemplates from './server/plugins/table/funcs/getTemplates.js';
24
24
  import getTemplatePath from './server/plugins/table/funcs/getTemplatePath.js';
25
25
  import addTemplateDir from './server/plugins/table/funcs/addTemplateDir.js';
26
+ import addMenu from './server/plugins/table/funcs/addMenu.js';
27
+ import menuDirs from './server/plugins/table/funcs/menuDirs.js';
26
28
  import userTemplateDir from './server/plugins/table/funcs/userTemplateDir.js';
27
29
  import customTokens from './server/plugins/table/funcs/customTokens.js';
28
30
  import userTokens from './server/plugins/table/funcs/userTokens.js';
@@ -36,6 +38,7 @@ import getSelect from './server/plugins/table/funcs/getSelect.js';
36
38
  import getSelectMeta from './server/plugins/table/funcs/getSelectMeta.js';
37
39
  import gisIRColumn from './server/plugins/table/funcs/gisIRColumn.js';
38
40
  import getData from './server/plugins/table/funcs/getData.js';
41
+ import getMenu from './server/routes/menu/controllers/getMenu.js';
39
42
  import getFilter from './server/plugins/table/funcs/getFilter.js';
40
43
 
41
44
  // crud
@@ -109,6 +112,8 @@ export {
109
112
  getTemplates,
110
113
  getTemplatePath,
111
114
  addTemplateDir,
115
+ menuDirs,
116
+ addMenu,
112
117
  userTemplateDir,
113
118
  customTokens,
114
119
  userTokens,
@@ -134,6 +139,7 @@ export {
134
139
  gisIRColumn,
135
140
  getData,
136
141
  getFilter,
142
+ getMenu,
137
143
 
138
144
  // pg
139
145
  initPG,