@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.
- package/config.js +12 -12
- package/dist/bi.js +1 -1
- package/dist/bi.umd.cjs +89 -76
- package/dist/{import-file-BYmPXlRW.js → import-file-P2S3-jSU.js} +9920 -9875
- package/dist/{map-component-mixin-PRMJIS1C.js → map-component-mixin-D156eanj.js} +1549 -1546
- package/dist/style.css +1 -1
- package/dist/{vs-donut-CqOel5Tz.js → vs-donut-BU63imSx.js} +7 -7
- package/dist/{vs-funnel-bar-DlkEo68l.js → vs-funnel-bar-DYNnxgrn.js} +5 -5
- package/dist/{vs-map-B6x6GV6M.js → vs-map-BpeilpO5.js} +3 -3
- package/dist/{vs-map-cluster-Dz5xmauq.js → vs-map-cluster-B2LSExjW.js} +3 -3
- package/dist/{vs-number-DMpjdJl7.js → vs-number-UWj-bst_.js} +3 -3
- package/dist/{vs-table-W-7bQhfI.js → vs-table-DGuWaiP9.js} +6 -6
- package/dist/{vs-text-BS_wjmtb.js → vs-text-FouEorvE.js} +39 -32
- package/package.json +1 -1
- package/server/plugins/docs.js +48 -48
- package/server/plugins/hook.js +89 -89
- package/server/plugins/vite.js +69 -69
- package/server/routes/dashboard/controllers/utils/yaml.js +11 -11
- package/server/routes/data/controllers/data.js +9 -3
- package/server/routes/data/controllers/util/normalizeData.js +61 -61
- package/server/routes/map/controllers/cluster.js +110 -110
- package/server/routes/map/controllers/clusterVtile.js +166 -166
- package/server/routes/map/controllers/geojson.js +127 -127
- package/server/routes/map/controllers/map.js +61 -61
- package/server/routes/map/controllers/utils/downloadClusterData.js +44 -44
- package/server/routes/map/controllers/vtile.js +183 -183
- package/server/utils/getWidget.js +2 -2
- package/utils.js +12 -12
package/server/plugins/hook.js
CHANGED
|
@@ -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);
|
package/server/plugins/vite.js
CHANGED
|
@@ -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
|
|
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 ? '
|
|
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;
|