@opengis/bi 1.0.35 → 1.0.36

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 (28) hide show
  1. package/config.js +12 -12
  2. package/dist/bi.js +1 -1
  3. package/dist/bi.umd.cjs +89 -76
  4. package/dist/{import-file-BYmPXlRW.js → import-file-P2S3-jSU.js} +9920 -9875
  5. package/dist/{map-component-mixin-PRMJIS1C.js → map-component-mixin-D156eanj.js} +1549 -1546
  6. package/dist/style.css +1 -1
  7. package/dist/{vs-donut-CqOel5Tz.js → vs-donut-BU63imSx.js} +7 -7
  8. package/dist/{vs-funnel-bar-DlkEo68l.js → vs-funnel-bar-DYNnxgrn.js} +5 -5
  9. package/dist/{vs-map-B6x6GV6M.js → vs-map-BpeilpO5.js} +3 -3
  10. package/dist/{vs-map-cluster-Dz5xmauq.js → vs-map-cluster-B2LSExjW.js} +3 -3
  11. package/dist/{vs-number-DMpjdJl7.js → vs-number-UWj-bst_.js} +3 -3
  12. package/dist/{vs-table-W-7bQhfI.js → vs-table-DGuWaiP9.js} +6 -6
  13. package/dist/{vs-text-BS_wjmtb.js → vs-text-FouEorvE.js} +39 -32
  14. package/package.json +1 -1
  15. package/server/plugins/docs.js +48 -48
  16. package/server/plugins/hook.js +89 -89
  17. package/server/plugins/vite.js +69 -69
  18. package/server/routes/dashboard/controllers/utils/yaml.js +11 -11
  19. package/server/routes/data/controllers/data.js +9 -3
  20. package/server/routes/data/controllers/util/normalizeData.js +61 -61
  21. package/server/routes/map/controllers/cluster.js +110 -110
  22. package/server/routes/map/controllers/clusterVtile.js +166 -166
  23. package/server/routes/map/controllers/geojson.js +127 -127
  24. package/server/routes/map/controllers/map.js +61 -61
  25. package/server/routes/map/controllers/utils/downloadClusterData.js +44 -44
  26. package/server/routes/map/controllers/vtile.js +183 -183
  27. package/server/utils/getWidget.js +2 -2
  28. package/utils.js +12 -12
@@ -1,89 +1,89 @@
1
- import fp from 'fastify-plugin';
2
- import fs from 'fs';
3
-
4
- // the use of fastify-plugin is required to be able
5
- // to export the decorators to the outer scope
6
-
7
- async function plugin(fastify) {
8
- // preSerialization
9
- fastify.addHook('preSerialization', async (req, reply, payload) => {
10
- if (!req.session?.passport?.user?.uid) {
11
- // return reply.redirect('/login');
12
- }
13
- if (req.url.includes('/suggest/') && !req.query.json) {
14
- return payload?.data;
15
- }
16
- if (payload.redirect) {
17
- return reply.redirect(payload.redirect);
18
- }
19
- if (reply.sent) {
20
- return null;
21
- }
22
-
23
- if ([200, 400, 403, 409, 404, 500].includes(payload.status)) {
24
- reply.status(payload.status);
25
- }
26
- /* if (payload.headers) {
27
- reply.headers(payload.headers);
28
- } */
29
- if (payload.buffer) {
30
- return payload.buffer;
31
- }
32
- if (payload.file) {
33
- // const buffer = await readFile(payload.file);
34
- // return reply.send(buffer);
35
- const stream = fs.createReadStream(payload.file);
36
- return stream;
37
- // return reply.send(stream);
38
- }
39
-
40
- if (payload.message) {
41
- return payload.message;
42
- }
43
- return payload;
44
- });
45
-
46
- // preValidation
47
- fastify.addHook('preValidation', async (req) => {
48
- const parseRawBody =
49
- ['POST', 'PUT'].includes(req.method) &&
50
- req.body &&
51
- typeof req.body === 'string' &&
52
- req.body.trim(/\r\n/g).startsWith('{') &&
53
- req.body.trim(/\r\n/g).endsWith('}');
54
- if (parseRawBody) {
55
- try {
56
- req.body = JSON.parse(req.body || '{}');
57
- } catch (err) {
58
- // throw new Error('invalid body');
59
- // return { error: 'invalid body', status: 400 };
60
- }
61
- }
62
- });
63
-
64
- // allow upload file
65
- const kIsMultipart = Symbol.for('[FastifyMultipart.isMultipart]');
66
- fastify.addContentTypeParser('multipart', (request, _, done) => {
67
- request[kIsMultipart] = true;
68
- done(null);
69
- });
70
-
71
- // parse Body
72
- function contentParser(req, body, done) {
73
- const parseBody = decodeURIComponent(body.toString())
74
- .split('&')
75
- .reduce((acc, el) => {
76
- const [key, val] = el.split('=');
77
- return { ...acc, [key]: val };
78
- }, {});
79
- done(null, parseBody);
80
- }
81
-
82
- fastify.addContentTypeParser(
83
- 'application/x-www-form-urlencoded',
84
- { parseAs: 'buffer' },
85
- contentParser
86
- );
87
- }
88
-
89
- export default fp(plugin);
1
+ import fp from 'fastify-plugin';
2
+ import fs from 'fs';
3
+
4
+ // the use of fastify-plugin is required to be able
5
+ // to export the decorators to the outer scope
6
+
7
+ async function plugin(fastify) {
8
+ // preSerialization
9
+ fastify.addHook('preSerialization', async (req, reply, payload) => {
10
+ if (!req.session?.passport?.user?.uid) {
11
+ // return reply.redirect('/login');
12
+ }
13
+ if (req.url.includes('/suggest/') && !req.query.json) {
14
+ return payload?.data;
15
+ }
16
+ if (payload.redirect) {
17
+ return reply.redirect(payload.redirect);
18
+ }
19
+ if (reply.sent) {
20
+ return null;
21
+ }
22
+
23
+ if ([200, 400, 403, 409, 404, 500].includes(payload.status)) {
24
+ reply.status(payload.status);
25
+ }
26
+ /* if (payload.headers) {
27
+ reply.headers(payload.headers);
28
+ } */
29
+ if (payload.buffer) {
30
+ return payload.buffer;
31
+ }
32
+ if (payload.file) {
33
+ // const buffer = await readFile(payload.file);
34
+ // return reply.send(buffer);
35
+ const stream = fs.createReadStream(payload.file);
36
+ return stream;
37
+ // return reply.send(stream);
38
+ }
39
+
40
+ if (payload.message) {
41
+ return payload.message;
42
+ }
43
+ return payload;
44
+ });
45
+
46
+ // preValidation
47
+ fastify.addHook('preValidation', async (req) => {
48
+ const parseRawBody =
49
+ ['POST', 'PUT'].includes(req.method) &&
50
+ req.body &&
51
+ typeof req.body === 'string' &&
52
+ req.body.trim(/\r\n/g).startsWith('{') &&
53
+ req.body.trim(/\r\n/g).endsWith('}');
54
+ if (parseRawBody) {
55
+ try {
56
+ req.body = JSON.parse(req.body || '{}');
57
+ } catch (err) {
58
+ // throw new Error('invalid body');
59
+ // return { error: 'invalid body', status: 400 };
60
+ }
61
+ }
62
+ });
63
+
64
+ // allow upload file
65
+ const kIsMultipart = Symbol.for('[FastifyMultipart.isMultipart]');
66
+ fastify.addContentTypeParser('multipart', (request, _, done) => {
67
+ request[kIsMultipart] = true;
68
+ done(null);
69
+ });
70
+
71
+ // parse Body
72
+ function contentParser(req, body, done) {
73
+ const parseBody = decodeURIComponent(body.toString())
74
+ .split('&')
75
+ .reduce((acc, el) => {
76
+ const [key, val] = el.split('=');
77
+ return { ...acc, [key]: val };
78
+ }, {});
79
+ done(null, parseBody);
80
+ }
81
+
82
+ fastify.addContentTypeParser(
83
+ 'application/x-www-form-urlencoded',
84
+ { parseAs: 'buffer' },
85
+ contentParser
86
+ );
87
+ }
88
+
89
+ export default fp(plugin);
@@ -1,69 +1,69 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import config from '../../config.js';
4
-
5
- const { disableAuth } = config;
6
- const isProduction = process.env.NODE_ENV === 'production';
7
-
8
- async function plugin(fastify) {
9
- // vite server
10
- if (!isProduction) {
11
- const vite = await import('vite');
12
-
13
- const viteServer = await vite.createServer({
14
- server: {
15
- middlewareMode: true,
16
- },
17
- });
18
- // hot reload
19
- viteServer.watcher.on('all', (d, t) => {
20
- if (!t.includes('module') && !t.includes('templates')) return;
21
- // console.log(d, t);
22
- viteServer.ws.send({ type: 'full-reload' });
23
- });
24
-
25
- // this is middleware for vite's dev servert
26
- fastify.addHook('onRequest', async (req, reply) => {
27
- // const { user } = req.session?.passport || {};
28
- const next = () => new Promise((resolve) => {
29
- viteServer.middlewares(req.raw, reply.raw, () => resolve());
30
- });
31
- await next();
32
- });
33
- fastify.get('*', async () => {});
34
- return;
35
- }
36
-
37
- // From Build
38
- fastify.get('*', async (req, reply) => {
39
- // console.log(disableAuth)
40
- if (!req.user && !disableAuth) return reply.redirect('/login');
41
- const stream = fs.createReadStream('dist/index.html');
42
- return reply
43
- .headers({ 'Cache-Control': 'public, no-cache' })
44
- .type('text/html')
45
- .send(stream);
46
- });
47
- fastify.get('/assets/:file', async (req, reply) => {
48
- const stream = fs.createReadStream(`dist/assets/${req.params.file}`);
49
- const ext = path.extname(req.params.file);
50
- const mime = {
51
- '.js': 'text/javascript',
52
- '.css': 'text/css',
53
- '.woff2': 'application/font-woff',
54
- '.png': 'image/png',
55
- }[ext];
56
- // reply.cacheControl('max-age', '1d');
57
- return mime
58
- ? reply
59
- .headers({
60
- 'Cache-Control': 'public, max-age=3600',
61
- 'Content-Encoding': 'identity',
62
- })
63
- .type(mime)
64
- .send(stream)
65
- : stream;
66
- });
67
- }
68
-
69
- export default plugin;
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import config from '../../config.js';
4
+
5
+ const { disableAuth } = config;
6
+ const isProduction = process.env.NODE_ENV === 'production';
7
+
8
+ async function plugin(fastify) {
9
+ // vite server
10
+ if (!isProduction) {
11
+ const vite = await import('vite');
12
+
13
+ const viteServer = await vite.createServer({
14
+ server: {
15
+ middlewareMode: true,
16
+ },
17
+ });
18
+ // hot reload
19
+ viteServer.watcher.on('all', (d, t) => {
20
+ if (!t.includes('module') && !t.includes('templates')) return;
21
+ // console.log(d, t);
22
+ viteServer.ws.send({ type: 'full-reload' });
23
+ });
24
+
25
+ // this is middleware for vite's dev servert
26
+ fastify.addHook('onRequest', async (req, reply) => {
27
+ // const { user } = req.session?.passport || {};
28
+ const next = () => new Promise((resolve) => {
29
+ viteServer.middlewares(req.raw, reply.raw, () => resolve());
30
+ });
31
+ await next();
32
+ });
33
+ fastify.get('*', async () => {});
34
+ return;
35
+ }
36
+
37
+ // From Build
38
+ fastify.get('*', async (req, reply) => {
39
+ // console.log(disableAuth)
40
+ if (!req.user && !disableAuth) return reply.redirect('/login');
41
+ const stream = fs.createReadStream('dist/index.html');
42
+ return reply
43
+ .headers({ 'Cache-Control': 'public, no-cache' })
44
+ .type('text/html')
45
+ .send(stream);
46
+ });
47
+ fastify.get('/assets/:file', async (req, reply) => {
48
+ const stream = fs.createReadStream(`dist/assets/${req.params.file}`);
49
+ const ext = path.extname(req.params.file);
50
+ const mime = {
51
+ '.js': 'text/javascript',
52
+ '.css': 'text/css',
53
+ '.woff2': 'application/font-woff',
54
+ '.png': 'image/png',
55
+ }[ext];
56
+ // reply.cacheControl('max-age', '1d');
57
+ return mime
58
+ ? reply
59
+ .headers({
60
+ 'Cache-Control': 'public, max-age=3600',
61
+ 'Content-Encoding': 'identity',
62
+ })
63
+ .type(mime)
64
+ .send(stream)
65
+ : stream;
66
+ });
67
+ }
68
+
69
+ export default plugin;
@@ -1,11 +1,11 @@
1
- import yaml from 'js-yaml';
2
-
3
- yaml.loadSafe = (yml) => {
4
- try {
5
- return yaml.load(yml);
6
- } catch (err) {
7
- return { error: err.toString() };
8
- }
9
- };
10
-
11
- export default yaml;
1
+ import yaml from 'js-yaml';
2
+
3
+ yaml.loadSafe = (yml) => {
4
+ try {
5
+ return yaml.load(yml);
6
+ } catch (err) {
7
+ return { error: err.toString() };
8
+ }
9
+ };
10
+
11
+ export default yaml;
@@ -14,6 +14,8 @@ import normalizeData from './util/normalizeData.js';
14
14
 
15
15
  import { getWidget } from '../../../../utils.js';
16
16
 
17
+ const maxLimit = 100;
18
+
17
19
  export default async function dataAPI(req, reply) {
18
20
  const time = Date.now();
19
21
 
@@ -83,7 +85,7 @@ export default async function dataAPI(req, reply) {
83
85
 
84
86
  const order = data.order || (type === 'listbar' ? 'metric desc' : null);
85
87
 
86
- const { optimizedSQL = `select * from ${tableSQL || table}` } =
88
+ const fData =
87
89
  filter || search
88
90
  ? await getFilterSQL({
89
91
  pg,
@@ -94,16 +96,20 @@ export default async function dataAPI(req, reply) {
94
96
  })
95
97
  : {};
96
98
 
99
+ const optimizedSQL = widgetData?.sql
100
+ ? `${widgetData.sql} ${fData?.q && false ? fData?.q : ''} limit ${Math.min(query.limit || widgetData.limit || maxLimit, maxLimit)}`
101
+ : (fData?.optimizedSQL || `select * from ${tableSQL || table}`);
102
+
97
103
  if (type?.includes('bar') && !metric?.length) {
98
104
  return { message: 'empty widget params: metrics', status: 400 };
99
105
  }
100
106
 
101
- const sql = (chartSQL[type] || chartSQL.chart)({
107
+ const sql = widgetData.sql ? optimizedSQL : (chartSQL[type] || chartSQL.chart)({
102
108
  where,
103
109
  metric,
104
110
  yType, // metric type
105
111
  columns: widgetData.columns,
106
- table: `(${optimizedSQL} ${samples ? 'limit 10' : ''})q`,
112
+ table: `(${optimizedSQL} limit ${samples ? '10' : Math.min(query.limit || maxLimit, maxLimit)})q`,
107
113
  x,
108
114
  groupData,
109
115
  groupby,
@@ -1,61 +1,61 @@
1
- function normalizeData(data, query = {}, columnTypes = []) {
2
- const skip = [];
3
- ['x', 'groupby', 'granularity'].forEach((el) => {
4
- // console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
5
- if (!columnTypes.find((col) => col.name == query[el])) {
6
- if (query[el] && query[el] !== 'null') skip.push(`column not found: ${query[el]}`);
7
- if (!(el === 'groupby' && query[el] === 'null')) delete query[el];
8
- }
9
- });
10
-
11
- if (
12
- !columnTypes.find(
13
- (col) => col.type === 'numeric' && col.name == query.metric
14
- )
15
- ) {
16
- delete query.metric;
17
- }
18
-
19
- const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
20
- const xType = columnTypes.find((el) => el.name == xName)?.type;
21
-
22
- const granularity =
23
- xType === 'date' || xType?.includes('timestamp')
24
- ? query.granularity || data.granularity || 'year'
25
- : null;
26
-
27
- const x =
28
- (granularity
29
- ? `date_trunc('${granularity}',${xName})::date::text`
30
- : null) || xName;
31
-
32
- const metrics = Array.isArray(data.metrics || data.metric) ? (data.metrics || data.metric) : [data.metrics || data.metric];
33
- const metric =
34
- (query.metric ? `sum(${query.metric})` : null) ||
35
- (metrics.length
36
- ? (metrics
37
- ?.filter((el) => el && columnTypes.find((col) => col.name == (el?.name || el)))
38
- ?.map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)?.join(',') || 'count(*)')
39
- : 'count(*)');
40
-
41
- const yName = metrics?.[0]?.name || metrics?.[0];
42
- const yType = columnTypes.find((el) => el.name == yName)?.type;
43
-
44
- const { cls, table, filterCustom } = data;
45
- const groupby = (query.groupby || data.groupby) === 'null' ? null : (query.groupby || data.groupby);
46
- // const orderby = query.orderby || data.orderby || 'count(*)';
47
-
48
- const custom = query?.filterCustom
49
- ?.split(',')
50
- ?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
51
- ?.filter((el) => el)
52
- ?.join(' and ');
53
- const where = `${data.query || '1=1'} and ${custom || 'true'}`;
54
-
55
- const tableSQL = data.tableSQL?.length
56
- ? `(select * from ${data?.table} t ${data.tableSQL.join(' \n ')} where ${where})q`
57
- : undefined;
58
-
59
- return { x, cls, metric, table, where, tableSQL, groupby, xName, xType, yName, yType, error: skip.length ? skip.join(',') : undefined };
60
- }
61
- export default normalizeData;
1
+ function normalizeData(data, query = {}, columnTypes = []) {
2
+ const skip = [];
3
+ ['x', 'groupby', 'granularity'].forEach((el) => {
4
+ // console.log(el, query[el], columnTypes.find(col => col.name == query[el]))
5
+ if (!columnTypes.find((col) => col.name == query[el])) {
6
+ if (query[el] && query[el] !== 'null') skip.push(`column not found: ${query[el]}`);
7
+ if (!(el === 'groupby' && query[el] === 'null')) delete query[el];
8
+ }
9
+ });
10
+
11
+ if (
12
+ !columnTypes.find(
13
+ (col) => col.type === 'numeric' && col.name == query.metric
14
+ )
15
+ ) {
16
+ delete query.metric;
17
+ }
18
+
19
+ const xName = query.x || (Array.isArray(data.x) ? data.x[0] : data.x);
20
+ const xType = columnTypes.find((el) => el.name == xName)?.type;
21
+
22
+ const granularity =
23
+ xType === 'date' || xType?.includes('timestamp')
24
+ ? query.granularity || data.granularity || 'year'
25
+ : null;
26
+
27
+ const x =
28
+ (granularity
29
+ ? `date_trunc('${granularity}',${xName})::date::text`
30
+ : null) || xName;
31
+
32
+ const metrics = Array.isArray(data.metrics || data.metric) ? (data.metrics || data.metric) : [data.metrics || data.metric];
33
+ const metric =
34
+ (query.metric ? `sum(${query.metric})` : null) ||
35
+ (metrics.length
36
+ ? (metrics
37
+ ?.filter((el) => el && columnTypes.find((col) => col.name == (el?.name || el)))
38
+ ?.map((el) => el.fx || `${el.operator || 'sum'}(${el.name || el})`)?.join(',') || 'count(*)')
39
+ : 'count(*)');
40
+
41
+ const yName = metrics?.[0]?.name || metrics?.[0];
42
+ const yType = columnTypes.find((el) => el.name == yName)?.type;
43
+
44
+ const { cls, table, filterCustom } = data;
45
+ const groupby = (query.groupby || data.groupby) === 'null' ? null : (query.groupby || data.groupby);
46
+ // const orderby = query.orderby || data.orderby || 'count(*)';
47
+
48
+ const custom = query?.filterCustom
49
+ ?.split(',')
50
+ ?.map((el) => filterCustom?.find((item) => item?.name === el)?.sql)
51
+ ?.filter((el) => el)
52
+ ?.join(' and ');
53
+ const where = `${data.query || '1=1'} and ${custom || 'true'}`;
54
+
55
+ const tableSQL = data.tableSQL?.length
56
+ ? `(select * from ${data?.table} t ${data.tableSQL.join(' \n ')} where ${where})q`
57
+ : undefined;
58
+
59
+ return { x, cls, metric, table, where, tableSQL, groupby, xName, xType, yName, yType, error: skip.length ? skip.join(',') : undefined };
60
+ }
61
+ export default normalizeData;