@opengis/bi 1.0.14 → 1.0.15

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 (53) hide show
  1. package/README.md +50 -50
  2. package/config.js +12 -12
  3. package/dist/bi.js +1 -1
  4. package/dist/bi.umd.cjs +63 -63
  5. package/dist/{import-file-DUp3rsNI.js → import-file-CRC0sYYT.js} +8055 -7987
  6. package/dist/{map-component-mixin-CGM0P5ub.js → map-component-mixin-BCtWEvzv.js} +3795 -2116
  7. package/dist/style.css +1 -1
  8. package/dist/{vs-calendar-cOoinEwc.js → vs-calendar-5ot79n0N.js} +20 -9
  9. package/dist/{vs-funnel-bar-kLkPoIhJ.js → vs-funnel-bar-CLo6gXI_.js} +2 -2
  10. package/dist/{vs-heatmap-3XAVGTSo.js → vs-heatmap-DHGA8dRk.js} +3 -4
  11. package/dist/{vs-map-cluster-BWJPx7wE.js → vs-map-cluster-CNgX6JVF.js} +2 -2
  12. package/dist/{vs-map-B1tr6V5_.js → vs-map-pIn5wS4G.js} +2 -2
  13. package/dist/{vs-number-CrU7LmkV.js → vs-number-DYfok8VU.js} +19 -12
  14. package/dist/{vs-text-DRPx3aID.js → vs-text-Dckykz09.js} +19 -14
  15. package/package.json +107 -97
  16. package/plugin.js +14 -13
  17. package/server/migrations/bi.dataset.sql +26 -0
  18. package/server/migrations/bi.sql +93 -93
  19. package/server/plugins/docs.js +48 -48
  20. package/server/plugins/hook.js +89 -89
  21. package/server/plugins/vite.js +69 -69
  22. package/server/routes/dashboard/controllers/dashboard.delete.js +38 -37
  23. package/server/routes/dashboard/controllers/dashboard.js +118 -114
  24. package/server/routes/dashboard/controllers/dashboard.list.js +30 -36
  25. package/server/routes/dashboard/controllers/utils/yaml.js +11 -11
  26. package/server/routes/dashboard/index.mjs +25 -25
  27. package/server/routes/data/controllers/data.js +167 -156
  28. package/server/routes/data/controllers/util/chartSQL.js +42 -39
  29. package/server/routes/data/controllers/util/normalizeData.js +59 -56
  30. package/server/routes/data/index.mjs +29 -24
  31. package/server/routes/dataset/controllers/bi.dataset.demo.add.js +97 -0
  32. package/server/routes/dataset/controllers/bi.dataset.import.js +67 -0
  33. package/server/routes/dataset/controllers/util/create.table.js +22 -0
  34. package/server/routes/dataset/controllers/util/prepare.data.js +49 -0
  35. package/server/routes/dataset/index.mjs +19 -0
  36. package/server/routes/db/controllers/dbTablePreview.js +63 -63
  37. package/server/routes/db/controllers/dbTables.js +36 -36
  38. package/server/routes/db/index.mjs +17 -17
  39. package/server/routes/edit/controllers/dashboard.add.js +26 -24
  40. package/server/routes/edit/controllers/dashboard.edit.js +46 -44
  41. package/server/routes/edit/controllers/widget.add.js +75 -73
  42. package/server/routes/edit/controllers/widget.del.js +69 -70
  43. package/server/routes/edit/controllers/widget.edit.js +52 -103
  44. package/server/routes/edit/index.mjs +31 -31
  45. package/server/routes/map/controllers/cluster.js +109 -104
  46. package/server/routes/map/controllers/clusterVtile.js +166 -213
  47. package/server/routes/map/controllers/geojson.js +127 -127
  48. package/server/routes/map/controllers/map.js +60 -57
  49. package/server/routes/map/controllers/utils/downloadClusterData.js +43 -0
  50. package/server/routes/map/controllers/vtile.js +183 -182
  51. package/server/routes/map/index.mjs +25 -25
  52. package/server/utils/getWidget.js +85 -83
  53. package/utils.js +12 -12
@@ -1,182 +1,183 @@
1
- import Sphericalmercator from '@mapbox/sphericalmercator';
2
-
3
- import path from 'path';
4
- import { createHash } from 'crypto';
5
- import { writeFile, mkdir } from 'fs/promises';
6
- import { existsSync, /* readdirSync, */ readFileSync } from 'fs';
7
- import { getWidget } from '../../../../utils.js';
8
-
9
- import yaml from '../../dashboard/controllers/utils/yaml.js';
10
- import normalizeData from '../../data/controllers/util/normalizeData.js';
11
-
12
- const mercator = new Sphericalmercator({ size: 256 });
13
-
14
- const types = {
15
- point: 'ST_Point' /* ,ST_MultiPoint */,
16
- polygon: 'ST_Polygon,ST_MultiPolygon',
17
- };
18
-
19
- const area = {
20
- 1: 1000000,
21
- 2: 100000,
22
- 3: 100000,
23
- 4: 100000,
24
- 5: 100000,
25
- 6: 100000,
26
- 7: 100000,
27
- 8: 100000,
28
- 9: 100000,
29
- 10: 50000,
30
- 11: 40000,
31
- 12: 20000,
32
- 13: 20000,
33
- };
34
-
35
- export default async function vtile(req, reply) {
36
- const { pg, params = {}, query = {}, funcs = {}, log } = req;
37
-
38
- const {
39
- filter,
40
- widget,
41
- dashboard,
42
- sql,
43
- cluster,
44
- type,
45
- nocache,
46
- geom = 'geom',
47
- pointZoom = 0,
48
- } = query;
49
-
50
- if (!widget) {
51
- return { message: 'not enough params: widget', status: 400 };
52
- }
53
-
54
- const { y, z } = params;
55
- const x = params.x?.split('.')[0] - 0;
56
-
57
- if (!x || !y || !z) {
58
- return { message: 'not enough params: xyz', status: 400 };
59
- }
60
-
61
- const { data } = await getWidget({ widget, dashboard });
62
-
63
- const headers = {
64
- 'Content-Type': 'application/x-protobuf',
65
- 'Cache-Control': nocache || sql ? 'no-cache' : 'public, max-age=86400',
66
- };
67
-
68
- const hash = [pointZoom, filter].filter((el) => el).join();
69
-
70
- const root = funcs.getFolder(req);
71
- const file = path.join(
72
- root,
73
- `/map/vtile/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}${z}/${x}/${y}.mvt`
74
- );
75
-
76
- try {
77
- const pkey = pg.pk?.[data?.table];
78
- if (!pkey) {
79
- return {
80
- message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
81
- status: 400,
82
- };
83
- }
84
-
85
- // data param
86
- const {
87
- table,
88
- where = '1=1',
89
- xName = {},
90
- metric,
91
- } = normalizeData(data, query);
92
-
93
- // get sql
94
- const columns = data.columns?.map((el) => el.name || el)?.join(',') || '1';
95
- const filterQ = filter
96
- ? await funcs.getFilterSQL({ pg, table, filter })
97
- : undefined;
98
- const q = `select "${pkey}",
99
- ${data?.color ? `"${data?.color}"` : '0'} as x,
100
- ${data.metrics?.[0] ? `"${data.metrics[0]}"::float` : '0'} as metric,
101
- ${columns},
102
- ${geom} as geom
103
- from ${filterQ ? `(${filterQ})` : table} q where ${where}`;
104
-
105
- if (sql === '1') return q;
106
-
107
- const koef =
108
- {
109
- 10: 0.1,
110
- 11: 0.05,
111
- 12: 0.005,
112
- 13: 0.0002,
113
- 14: 0.00005,
114
- }[z] || 0.000001;
115
-
116
- const geomCol =
117
- parseInt(z, 10) < parseInt(pointZoom, 10) || true
118
- ? `ST_Centroid(${geom})`
119
- : geom;
120
-
121
- const bbox = mercator.bbox(+y, +x, +z, false /* , '900913' */);
122
- const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
123
-
124
- const areaZoom =
125
- area[z] && false
126
- ? ` and (st_area(st_transform(${geom},3857)))>'${area[z]}'`
127
- : '';
128
-
129
- const q1 =
130
- cluster > z
131
- ? `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
132
- FROM (
133
- SELECT floor(random() * 100000 + 1)::int + row_number() over() as row, count(*) as point_count,
134
- ST_AsMVTGeom(st_transform(st_centroid(ST_Union(geom)),3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) as geom
135
- FROM (
136
- SELECT geom, ST_ClusterDBSCAN(geom,${koef},1) OVER () AS cluster
137
- FROM (${q})q where ${geom} && ${bbox2d} ) j
138
- WHERE cluster IS NOT NULL
139
- GROUP BY cluster
140
- ORDER BY 1 DESC
141
- )q`
142
- : `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
143
- FROM (
144
- SELECT
145
- floor(random() * 100000 + 1)::int + row_number() over() as row,
146
-
147
- ${pkey} as id,
148
- x,
149
- metric,
150
- ${columns},
151
- ST_AsMVTGeom(st_transform(${geomCol}, 3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) as geom
152
-
153
- FROM (select * from (${q})q where ${geom} && ${bbox2d}
154
-
155
- and ${geom} is not null and st_srid(${geom}) >0
156
-
157
- ${areaZoom}
158
-
159
- ${types[type] ? ` and ST_GeometryType(geom) = any ('{ ${types[type]} }') ` : ''}
160
-
161
- limit 3000)q
162
- ) q`;
163
-
164
- if (sql === '2') return q1;
165
-
166
- const { rows = [] } = await pg.query(q1);
167
-
168
- if (sql === '3') return rows.map((el) => el.tile);
169
-
170
- const buffer = Buffer.concat(rows.map((el) => Buffer.from(el.tile)));
171
-
172
- if (!nocache) {
173
- await mkdir(path.dirname(file), { recursive: true });
174
- await writeFile(file, buffer, 'binary');
175
- }
176
-
177
- return reply.headers(headers).send(buffer);
178
- } catch (err) {
179
- log.error('bi/vtile', { error: err.toString(), query, params });
180
- return { error: err.toString(), status: 500 };
181
- }
182
- }
1
+ import Sphericalmercator from '@mapbox/sphericalmercator';
2
+
3
+ import path from 'path';
4
+ import { createHash } from 'crypto';
5
+ import { writeFile, mkdir } from 'fs/promises';
6
+ import { existsSync, /* readdirSync, */ readFileSync } from 'fs';
7
+ import { getWidget } from '../../../../utils.js';
8
+
9
+ import { getFolder, getFilterSQL } from '@opengis/fastify-table/utils.js';
10
+
11
+ import normalizeData from '../../data/controllers/util/normalizeData.js';
12
+
13
+ const mercator = new Sphericalmercator({ size: 256 });
14
+
15
+ const types = {
16
+ point: 'ST_Point' /* ,ST_MultiPoint */,
17
+ polygon: 'ST_Polygon,ST_MultiPolygon',
18
+ };
19
+
20
+ const area = {
21
+ 1: 1000000,
22
+ 2: 100000,
23
+ 3: 100000,
24
+ 4: 100000,
25
+ 5: 100000,
26
+ 6: 100000,
27
+ 7: 100000,
28
+ 8: 100000,
29
+ 9: 100000,
30
+ 10: 50000,
31
+ 11: 40000,
32
+ 12: 20000,
33
+ 13: 20000,
34
+ };
35
+
36
+ export default async function vtile(req, reply) {
37
+ const { pg, params = {}, query = {}, funcs = {}, log } = req;
38
+
39
+ const {
40
+ filter,
41
+ widget,
42
+ dashboard,
43
+ sql,
44
+ cluster,
45
+ type,
46
+ nocache,
47
+ geom = 'geom',
48
+ pointZoom = 0,
49
+ } = query;
50
+
51
+ if (!widget) {
52
+ return { message: 'not enough params: widget', status: 400 };
53
+ }
54
+
55
+ const { y, z } = params;
56
+ const x = params.x?.split('.')[0] - 0;
57
+
58
+ if (!x || !y || !z) {
59
+ return { message: 'not enough params: xyz', status: 400 };
60
+ }
61
+
62
+ const { data } = await getWidget({ widget, dashboard });
63
+
64
+ const headers = {
65
+ 'Content-Type': 'application/x-protobuf',
66
+ 'Cache-Control': nocache || sql ? 'no-cache' : 'public, max-age=86400',
67
+ };
68
+
69
+ const hash = [pointZoom, filter].filter((el) => el).join();
70
+
71
+ const root = getFolder(req);
72
+ const file = path.join(
73
+ root,
74
+ `/map/vtile/${widget}/${hash ? `${createHash('sha1').update(hash).digest('base64')}/` : ''}${z}/${x}/${y}.mvt`
75
+ );
76
+
77
+ try {
78
+ const pkey = pg.pk?.[data?.table];
79
+ if (!pkey) {
80
+ return {
81
+ message: `invalid ${widget ? 'widget' : 'dashboard'}: table pk not found (${data?.table})`,
82
+ status: 400,
83
+ };
84
+ }
85
+
86
+ // data param
87
+ const {
88
+ table,
89
+ where = '1=1',
90
+ xName = {},
91
+ metric,
92
+ } = normalizeData(data, query);
93
+
94
+ // get sql
95
+ const columns = data.columns?.map((el) => el.name || el)?.join(',') || '1';
96
+ const filterQ = filter
97
+ ? await getFilterSQL({ pg, table, filter })
98
+ : undefined;
99
+ const q = `select "${pkey}",
100
+ ${data?.color ? `"${data?.color}"` : '0'} as x,
101
+ ${data.metrics?.[0] ? `"${data.metrics[0]}"::float` : '0'} as metric,
102
+ ${columns},
103
+ ${geom} as geom
104
+ from ${filterQ ? `(${filterQ})` : table} q where ${where}`;
105
+
106
+ if (sql === '1') return q;
107
+
108
+ const koef =
109
+ {
110
+ 10: 0.1,
111
+ 11: 0.05,
112
+ 12: 0.005,
113
+ 13: 0.0002,
114
+ 14: 0.00005,
115
+ }[z] || 0.000001;
116
+
117
+ const geomCol =
118
+ parseInt(z, 10) < parseInt(pointZoom, 10) || true
119
+ ? `ST_Centroid(${geom})`
120
+ : geom;
121
+
122
+ const bbox = mercator.bbox(+y, +x, +z, false /* , '900913' */);
123
+ const bbox2d = `'BOX(${bbox[0]} ${bbox[1]},${bbox[2]} ${bbox[3]})'::box2d`;
124
+
125
+ const areaZoom =
126
+ area[z] && false
127
+ ? ` and (st_area(st_transform(${geom},3857)))>'${area[z]}'`
128
+ : '';
129
+
130
+ const q1 =
131
+ cluster > z
132
+ ? `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
133
+ FROM (
134
+ SELECT floor(random() * 100000 + 1)::int + row_number() over() as row, count(*) as point_count,
135
+ ST_AsMVTGeom(st_transform(st_centroid(ST_Union(geom)),3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) as geom
136
+ FROM (
137
+ SELECT geom, ST_ClusterDBSCAN(geom,${koef},1) OVER () AS cluster
138
+ FROM (${q})q where ${geom} && ${bbox2d} ) j
139
+ WHERE cluster IS NOT NULL
140
+ GROUP BY cluster
141
+ ORDER BY 1 DESC
142
+ )q`
143
+ : `SELECT ST_AsMVT(q, 'bi', 4096, 'geom','row') as tile
144
+ FROM (
145
+ SELECT
146
+ floor(random() * 100000 + 1)::int + row_number() over() as row,
147
+
148
+ ${pkey} as id,
149
+ x,
150
+ metric,
151
+ ${columns},
152
+ ST_AsMVTGeom(st_transform(${geomCol}, 3857),ST_TileEnvelope(${z},${y},${x})::box2d,4096,256,false) as geom
153
+
154
+ FROM (select * from (${q})q where ${geom} && ${bbox2d}
155
+
156
+ and ${geom} is not null and st_srid(${geom}) >0
157
+
158
+ ${areaZoom}
159
+
160
+ ${types[type] ? ` and ST_GeometryType(geom) = any ('{ ${types[type]} }') ` : ''}
161
+
162
+ limit 3000)q
163
+ ) q`;
164
+
165
+ if (sql === '2') return q1;
166
+
167
+ const { rows = [] } = await pg.query(q1);
168
+
169
+ if (sql === '3') return rows.map((el) => el.tile);
170
+
171
+ const buffer = Buffer.concat(rows.map((el) => Buffer.from(el.tile)));
172
+
173
+ if (!nocache) {
174
+ await mkdir(path.dirname(file), { recursive: true });
175
+ await writeFile(file, buffer, 'binary');
176
+ }
177
+
178
+ return reply.headers(headers).send(buffer);
179
+ } catch (err) {
180
+ log.error('bi/vtile', { error: err.toString(), query, params });
181
+ return { error: err.toString(), status: 500 };
182
+ }
183
+ }
@@ -1,25 +1,25 @@
1
- import map from './controllers/map.js';
2
- import geojson from './controllers/geojson.js';
3
- import vtile from './controllers/vtile.js';
4
-
5
- import cluster from './controllers/cluster.js';
6
- import clusterVtile from './controllers/clusterVtile.js';
7
-
8
- const biSchema = {
9
- querystring: {
10
- widget: { type: 'string', pattern: '^([\\d\\w]+)$' },
11
- dashboard: { type: 'string', pattern: '^([\\d\\w]+)$' },
12
- sql: { type: 'string', pattern: '^([\\d])$' },
13
- },
14
- params: {
15
- id: { type: 'string', pattern: '^([\\d\\w]+)$' },
16
- },
17
- };
18
-
19
- export default async function route(fastify, opts) {
20
- fastify.get('/bi-map', { schema: biSchema }, map);
21
- fastify.get('/bi-geojson', { schema: biSchema }, geojson);
22
- fastify.get('/bi-vtile/:z/:y/:x', { schema: biSchema }, vtile);
23
- fastify.get('/bi-cluster', { schema: biSchema }, cluster);
24
- fastify.get('/bi-cluster-vtile/:z/:y/:x', { schema: biSchema }, clusterVtile);
25
- }
1
+ import map from './controllers/map.js';
2
+ import geojson from './controllers/geojson.js';
3
+ import vtile from './controllers/vtile.js';
4
+
5
+ import cluster from './controllers/cluster.js';
6
+ import clusterVtile from './controllers/clusterVtile.js';
7
+
8
+ const biSchema = {
9
+ querystring: {
10
+ widget: { type: 'string', pattern: '^([\\d\\w]+)$' },
11
+ dashboard: { type: 'string', pattern: '^([\\d\\w]+)$' },
12
+ sql: { type: 'string', pattern: '^([\\d])$' },
13
+ },
14
+ params: {
15
+ id: { type: 'string', pattern: '^([\\d\\w]+)$' },
16
+ },
17
+ };
18
+
19
+ export default async function route(fastify, opts) {
20
+ fastify.get('/bi-map', { schema: biSchema }, map);
21
+ fastify.get('/bi-geojson', { schema: biSchema }, geojson);
22
+ fastify.get('/bi-vtile/:z/:y/:x', { schema: biSchema }, vtile);
23
+ fastify.get('/bi-cluster', { schema: biSchema }, cluster);
24
+ fastify.get('/bi-cluster-vtile/:z/:y/:x', { schema: biSchema }, clusterVtile);
25
+ }
@@ -1,83 +1,85 @@
1
- import { getTemplate } from '@opengis/fastify-table/utils.js';
2
- import pgClients from '@opengis/fastify-table/pg/pgClients.js';
3
-
4
- const pg = pgClients.client;
5
-
6
- async function getWidget({ dashboard, widget }) {
7
- const dashboardData = dashboard
8
- ? await getTemplate('dashboard', dashboard)
9
- : null;
10
-
11
- const { id, tableName } =
12
- !dashboardData && pg.pk['bi.dashboard'] && dashboard
13
- ? await pg
14
- .query(
15
- `select dashboard_id as id, table_name as "tableName" from bi.dashboard where $1 in (dashboard_id, name)`,
16
- [dashboard]
17
- )
18
- .then((res1) => res1.rows?.[0] || {})
19
- : {};
20
-
21
- if (!dashboardData && dashboard && !id) {
22
- return { message: `dashboard not found: ${dashboard}`, status: 404 };
23
- }
24
-
25
- dashboardData?.forEach((el) => {
26
- el[2] = el[0].split('.')[0];
27
- });
28
- const dashboardIndex = dashboardData?.find((el) => el[2] == 'index')?.[1];
29
-
30
- const widgetData = dashboard
31
- ? dashboardData?.find((el) => el[2] === (widget || 'index'))?.[1]
32
- : await getTemplate('widget', widget);
33
- if (typeof widgetData === 'string') {
34
- return { source: widgetData, status: 200 };
35
- }
36
- if (!id && !dashboardData && !widgetData) {
37
- return { message: `not found ${widget} ${dashboard}`, status: 404 };
38
- }
39
-
40
- const q = `select *, coalesce(data::jsonb, '{}'::jsonb) || jsonb_build_object('table', table_name) as data from bi.widget where dashboard_id=$1 and name=$2`;
41
-
42
- const {
43
- type,
44
- text,
45
- data = {},
46
- controls,
47
- style,
48
- options,
49
- yml
50
- } = widgetData ||
51
- (await pg
52
- .query(q, [id || dashboard, widget])
53
- .then((res1) => res1.rows?.[0] || {}));
54
-
55
- if (!type) {
56
- return { message: `widget not found: ${widget}`, status: 404 };
57
- }
58
-
59
- Object.assign(data, {
60
- table:
61
- data.table ||
62
- tableName ||
63
- widgetData?.table_name ||
64
- dashboardIndex?.table ||
65
- dashboardIndex?.table_name,
66
- db: dashboardIndex?.db || pgClients.client?.options?.database,
67
- });
68
- const main = { ...(dashboardIndex || {}), ...widgetData, ...data };
69
-
70
- if (!main?.table) {
71
- return {
72
- message: /* json.error || */ `invalid ${widget ? 'widget' : 'dashboard'}: 1`,
73
- status: 404,
74
- };
75
- }
76
-
77
- const tableSQL = main?.sql?.map(
78
- (el, i) => `left join lateral(${el})t${i + 1} on 1=1`
79
- );
80
-
81
- return { ...main, tableSQL, data, type, text, controls, style, options, yml };
82
- }
83
- export default getWidget;
1
+ import { getTemplate, pgClients } from '@opengis/fastify-table/utils.js';
2
+
3
+ const pg = pgClients.client;
4
+
5
+ async function getWidget({ dashboard, widget }) {
6
+ if (!dashboard && !widget) {
7
+ return { message: `not enough params: dashboard / widget`, status: 400 };
8
+ }
9
+ const dashboardData = dashboard
10
+ ? await getTemplate('dashboard', dashboard)
11
+ : null;
12
+
13
+ const { id, tableName } =
14
+ !dashboardData && pg.pk['bi.dashboard'] && dashboard
15
+ ? await pg
16
+ .query(
17
+ `select dashboard_id as id, table_name as "tableName" from bi.dashboard where $1 in (dashboard_id, name)`,
18
+ [dashboard]
19
+ )
20
+ .then((res1) => res1.rows?.[0] || {})
21
+ : {};
22
+
23
+ if (!dashboardData && dashboard && !id) {
24
+ return { message: `dashboard not found: ${dashboard}`, status: 404 };
25
+ }
26
+
27
+ dashboardData?.forEach((el) => {
28
+ el[2] = el[0].split('.')[0];
29
+ });
30
+ const dashboardIndex = dashboardData?.find((el) => el[2] == 'index')?.[1];
31
+
32
+ const widgetData = dashboard
33
+ ? dashboardData?.find((el) => el[2] === (widget || 'index'))?.[1]
34
+ : await getTemplate('widget', widget);
35
+ if (typeof widgetData === 'string') {
36
+ return { source: widgetData, status: 200 };
37
+ }
38
+ if (!id && !dashboardData && !widgetData) {
39
+ return { message: `not found ${widget} ${dashboard}`, status: 404 };
40
+ }
41
+
42
+ const q = `select *, coalesce(data::jsonb, '{}'::jsonb) || jsonb_build_object('table', table_name) as data from bi.widget where dashboard_id=$1 and name=$2`;
43
+
44
+ const {
45
+ type,
46
+ text,
47
+ data = {},
48
+ controls,
49
+ style,
50
+ options,
51
+ yml
52
+ } = widgetData ||
53
+ (await pg
54
+ .query(q, [id || dashboard, widget])
55
+ .then((res1) => res1.rows?.[0] || {}));
56
+
57
+ if (!type) {
58
+ return { message: `widget not found: ${widget}`, status: 404 };
59
+ }
60
+
61
+ Object.assign(data, {
62
+ table:
63
+ data.table ||
64
+ tableName ||
65
+ widgetData?.table_name ||
66
+ dashboardIndex?.table ||
67
+ dashboardIndex?.table_name,
68
+ db: dashboardIndex?.db || pgClients.client?.options?.database,
69
+ });
70
+ const main = { ...(dashboardIndex || {}), ...widgetData, ...data, ...data?.data || {} };
71
+
72
+ if (!main?.table) {
73
+ return {
74
+ message: /* json.error || */ `invalid ${widget ? 'widget' : 'dashboard'}: 1`,
75
+ status: 404,
76
+ };
77
+ }
78
+
79
+ const tableSQL = main?.sql?.map(
80
+ (el, i) => `left join lateral(${el})t${i + 1} on 1=1`
81
+ );
82
+
83
+ return { ...main, tableSQL, data, type, text, controls, style, options, yml };
84
+ }
85
+ export default getWidget;
package/utils.js CHANGED
@@ -1,12 +1,12 @@
1
- // This file contains code that we reuse
2
- // between our tests.
3
-
4
- // import getTemplatePath from '@opengis/fastify-table/table/controllers/utils/getTemplatePath.js';
5
- import getWidget from './server/utils/getWidget.js';
6
- import yamlSafe from './server/routes/dashboard/controllers/utils/yaml.js';
7
-
8
- export {
9
- // getTemplatePath,
10
- yamlSafe,
11
- getWidget,
12
- };
1
+ // This file contains code that we reuse
2
+ // between our tests.
3
+
4
+ // import getTemplatePath from '@opengis/fastify-table/table/controllers/utils/getTemplatePath.js';
5
+ import getWidget from './server/utils/getWidget.js';
6
+ import yamlSafe from './server/routes/dashboard/controllers/utils/yaml.js';
7
+
8
+ export {
9
+ // getTemplatePath,
10
+ yamlSafe,
11
+ getWidget,
12
+ };