@nocobase/plugin-data-visualization 0.13.0-alpha.9 → 0.14.0-alpha.2

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.
@@ -0,0 +1,20 @@
1
+ module.exports = {
2
+ "@ant-design/icons": "5.1.4",
3
+ "@formily/antd-v5": "1.1.0",
4
+ "@nocobase/client": "0.14.0-alpha.2",
5
+ "antd": "5.8.6",
6
+ "react": "18.2.0",
7
+ "@formily/react": "2.2.27",
8
+ "@formily/shared": "2.2.27",
9
+ "@nocobase/cache": "0.14.0-alpha.2",
10
+ "@nocobase/server": "0.14.0-alpha.2",
11
+ "dayjs": "1.11.9",
12
+ "@formily/core": "2.2.27",
13
+ "lodash": "4.17.21",
14
+ "react-i18next": "11.18.6",
15
+ "ahooks": "3.7.8",
16
+ "@emotion/css": "11.11.2",
17
+ "@nocobase/actions": "0.14.0-alpha.2",
18
+ "@nocobase/database": "0.14.0-alpha.2",
19
+ "@nocobase/utils": "0.14.0-alpha.2"
20
+ };
@@ -0,0 +1 @@
1
+ (function(){"use strict";var e={979:function(e){e.exports=compose;function compose(e){if(!Array.isArray(e))throw new TypeError("Middleware stack must be an array!");for(const r of e){if(typeof r!=="function")throw new TypeError("Middleware must be composed of functions!")}return function(r,t){let i=-1;return dispatch(0);function dispatch(n){if(n<=i)return Promise.reject(new Error("next() called multiple times"));i=n;let o=e[n];if(n===e.length)o=t;if(!o)return Promise.resolve();try{return Promise.resolve(o(r,dispatch.bind(null,n+1)))}catch(e){return Promise.reject(e)}}}}}};var r={};function __nccwpck_require__(t){var i=r[t];if(i!==undefined){return i.exports}var n=r[t]={exports:{}};var o=true;try{e[t](n,n.exports,__nccwpck_require__);o=false}finally{if(o)delete r[t]}return n.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var t=__nccwpck_require__(979);module.exports=t})();
@@ -0,0 +1 @@
1
+ {"name":"koa-compose","description":"compose Koa middleware","repository":"koajs/compose","version":"4.1.0","keywords":["koa","middleware","compose"],"files":["index.js"],"dependencies":{},"devDependencies":{"codecov":"^3.0.0","jest":"^21.0.0","matcha":"^0.7.0","standard":"^10.0.3"},"scripts":{"bench":"matcha bench/bench.js","lint":"standard --fix .","test":"jest --forceExit --coverage"},"jest":{"testEnvironment":"node"},"license":"MIT","_lastModified":"2023-09-12T20:05:15.672Z"}
@@ -1,86 +1,8 @@
1
1
  import { Context, Next } from '@nocobase/actions';
2
- import { Cache } from '@nocobase/cache';
3
- type MeasureProps = {
4
- field: string | string[];
5
- type?: string;
6
- aggregation?: string;
7
- alias?: string;
8
- };
9
- type DimensionProps = {
10
- field: string | string[];
11
- type?: string;
12
- alias?: string;
13
- format?: string;
14
- };
15
- type OrderProps = {
16
- field: string | string[];
17
- alias?: string;
18
- order?: 'asc' | 'desc';
19
- };
20
- type QueryParams = Partial<{
21
- uid: string;
22
- collection: string;
23
- measures: MeasureProps[];
24
- dimensions: DimensionProps[];
25
- orders: OrderProps[];
26
- filter: any;
27
- limit: number;
28
- sql: {
29
- fields?: string;
30
- clauses?: string;
31
- };
32
- cache: {
33
- enabled: boolean;
34
- ttl: number;
35
- };
36
- refresh: boolean;
37
- }>;
38
- export declare const parseFieldAndAssociations: (ctx: Context, params: QueryParams) => {
39
- where: any;
40
- measures: {
41
- field: any;
42
- name: string;
43
- type: any;
44
- alias: string;
45
- }[];
46
- dimensions: {
47
- field: any;
48
- name: string;
49
- type: any;
50
- alias: string;
51
- }[];
52
- orders: {
53
- field: any;
54
- name: string;
55
- type: any;
56
- alias: string;
57
- }[];
58
- include: any[];
59
- };
60
- export declare const parseBuilder: (ctx: Context, builder: QueryParams) => {
61
- queryParams: {
62
- where: any;
63
- attributes: any[];
64
- include: any[];
65
- group: any[];
66
- order: any[];
67
- limit: number;
68
- raw: boolean;
69
- };
70
- fieldMap: {};
71
- };
72
- export declare const processData: (ctx: Context, data: any[], fieldMap: {
73
- [source: string]: {
74
- type?: string;
75
- };
76
- }) => any[];
77
- export declare const queryData: (ctx: Context, builder: QueryParams) => Promise<any[]>;
78
- export declare const cacheWrap: (cache: Cache, options: {
79
- func: () => Promise<any>;
80
- key: string;
81
- ttl?: number;
82
- useCache?: boolean;
83
- refresh?: boolean;
84
- }) => Promise<any>;
2
+ export declare const postProcess: (ctx: Context, next: Next) => Promise<void>;
3
+ export declare const queryData: (ctx: Context, next: Next) => Promise<void>;
4
+ export declare const parseBuilder: (ctx: Context, next: Next) => Promise<void>;
5
+ export declare const parseFieldAndAssociations: (ctx: Context, next: Next) => Promise<void>;
6
+ export declare const parseVariables: (ctx: Context, next: Next) => Promise<any>;
7
+ export declare const cacheMiddleware: (ctx: Context, next: Next) => Promise<void>;
85
8
  export declare const query: (ctx: Context, next: Next) => Promise<void>;
86
- export {};
@@ -1,6 +1,8 @@
1
+ var __create = Object.create;
1
2
  var __defProp = Object.defineProperty;
2
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
4
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
7
  var __export = (target, all) => {
6
8
  for (var name in all)
@@ -14,21 +16,132 @@ var __copyProps = (to, from, except, desc) => {
14
16
  }
15
17
  return to;
16
18
  };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
17
27
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
28
  var query_exports = {};
19
29
  __export(query_exports, {
20
- cacheWrap: () => cacheWrap,
30
+ cacheMiddleware: () => cacheMiddleware,
21
31
  parseBuilder: () => parseBuilder,
22
32
  parseFieldAndAssociations: () => parseFieldAndAssociations,
23
- processData: () => processData,
33
+ parseVariables: () => parseVariables,
34
+ postProcess: () => postProcess,
24
35
  query: () => query,
25
36
  queryData: () => queryData
26
37
  });
27
38
  module.exports = __toCommonJS(query_exports);
28
39
  var import_database = require("@nocobase/database");
29
40
  var import_formatter = require("./formatter");
30
- const parseFieldAndAssociations = (ctx, params) => {
31
- const { collection: collectionName, measures, dimensions, orders, filter } = params;
41
+ var import_koa_compose = __toESM(require("koa-compose"));
42
+ var import_utils = require("@nocobase/utils");
43
+ const postProcess = async (ctx, next) => {
44
+ const { sequelize } = ctx.db;
45
+ const dialect = sequelize.getDialect();
46
+ const { data, fieldMap } = ctx.action.params.values;
47
+ switch (dialect) {
48
+ case "postgres":
49
+ ctx.body = data.map((record) => {
50
+ const result = {};
51
+ Object.entries(record).forEach(([key, value]) => {
52
+ const { type } = fieldMap[key] || {};
53
+ switch (type) {
54
+ case "bigInt":
55
+ case "integer":
56
+ case "float":
57
+ case "double":
58
+ value = Number(value);
59
+ break;
60
+ }
61
+ result[key] = value;
62
+ });
63
+ return result;
64
+ });
65
+ break;
66
+ default:
67
+ ctx.body = data;
68
+ }
69
+ await next();
70
+ };
71
+ const queryData = async (ctx, next) => {
72
+ const { collection, queryParams, fieldMap } = ctx.action.params.values;
73
+ const model = ctx.db.getModel(collection);
74
+ const data = await model.findAll(queryParams);
75
+ ctx.action.params.values = {
76
+ data,
77
+ fieldMap
78
+ };
79
+ await next();
80
+ };
81
+ const parseBuilder = async (ctx, next) => {
82
+ const { sequelize } = ctx.db;
83
+ const { measures, dimensions, orders, include, where, limit } = ctx.action.params.values;
84
+ const attributes = [];
85
+ const group = [];
86
+ const order = [];
87
+ const fieldMap = {};
88
+ let hasAgg = false;
89
+ measures.forEach((measure) => {
90
+ const { field, aggregation, alias } = measure;
91
+ const attribute = [];
92
+ const col = sequelize.col(field);
93
+ if (aggregation) {
94
+ hasAgg = true;
95
+ attribute.push(sequelize.fn(aggregation, col));
96
+ } else {
97
+ attribute.push(col);
98
+ }
99
+ if (alias) {
100
+ attribute.push(alias);
101
+ }
102
+ attributes.push(attribute.length > 1 ? attribute : attribute[0]);
103
+ fieldMap[alias || field] = measure;
104
+ });
105
+ dimensions.forEach((dimension) => {
106
+ const { field, format, alias, type } = dimension;
107
+ const attribute = [];
108
+ const col = sequelize.col(field);
109
+ if (format) {
110
+ attribute.push((0, import_formatter.formatter)(sequelize, type, field, format));
111
+ } else {
112
+ attribute.push(col);
113
+ }
114
+ if (alias) {
115
+ attribute.push(alias);
116
+ }
117
+ attributes.push(attribute.length > 1 ? attribute : attribute[0]);
118
+ if (hasAgg) {
119
+ group.push(attribute[0]);
120
+ }
121
+ fieldMap[alias || field] = dimension;
122
+ });
123
+ orders.forEach((item) => {
124
+ const alias = sequelize.getQueryInterface().quoteIdentifier(item.alias);
125
+ const name = hasAgg ? sequelize.literal(alias) : sequelize.col(item.field);
126
+ order.push([name, item.order || "ASC"]);
127
+ });
128
+ ctx.action.params.values = {
129
+ ...ctx.action.params.values,
130
+ queryParams: {
131
+ where,
132
+ attributes,
133
+ include,
134
+ group,
135
+ order,
136
+ limit: limit > 2e3 ? 2e3 : limit,
137
+ raw: true
138
+ },
139
+ fieldMap
140
+ };
141
+ await next();
142
+ };
143
+ const parseFieldAndAssociations = async (ctx, next) => {
144
+ const { collection: collectionName, measures, dimensions, orders, filter } = ctx.action.params.values;
32
145
  const collection = ctx.db.getCollection(collectionName);
33
146
  const fields = collection.fields;
34
147
  const underscored = ctx.db.options.underscored;
@@ -87,149 +200,103 @@ const parseFieldAndAssociations = (ctx, params) => {
87
200
  }
88
201
  return item;
89
202
  });
90
- return {
203
+ ctx.action.params.values = {
204
+ ...ctx.action.params.values,
91
205
  where,
92
206
  measures: parsedMeasures,
93
207
  dimensions: parsedDimensions,
94
208
  orders: parsedOrders,
95
209
  include: [...include, ...parsedFilterInclude || []]
96
210
  };
211
+ await next();
97
212
  };
98
- const parseBuilder = (ctx, builder) => {
99
- const { sequelize } = ctx.db;
100
- const { limit } = builder;
101
- const { measures, dimensions, orders, include, where } = parseFieldAndAssociations(ctx, builder);
102
- const attributes = [];
103
- const group = [];
104
- const order = [];
105
- const fieldMap = {};
106
- let hasAgg = false;
107
- measures.forEach((measure) => {
108
- const { field, aggregation, alias } = measure;
109
- const attribute = [];
110
- const col = sequelize.col(field);
111
- if (aggregation) {
112
- hasAgg = true;
113
- attribute.push(sequelize.fn(aggregation, col));
114
- } else {
115
- attribute.push(col);
116
- }
117
- if (alias) {
118
- attribute.push(alias);
119
- }
120
- attributes.push(attribute.length > 1 ? attribute : attribute[0]);
121
- fieldMap[alias || field] = measure;
122
- });
123
- dimensions.forEach((dimension) => {
124
- const { field, format, alias, type } = dimension;
125
- const attribute = [];
126
- const col = sequelize.col(field);
127
- if (format) {
128
- attribute.push((0, import_formatter.formatter)(sequelize, type, field, format));
129
- } else {
130
- attribute.push(col);
131
- }
132
- if (alias) {
133
- attribute.push(alias);
134
- }
135
- attributes.push(attribute.length > 1 ? attribute : attribute[0]);
136
- if (hasAgg) {
137
- group.push(attribute[0]);
138
- }
139
- fieldMap[alias || field] = dimension;
140
- });
141
- orders.forEach((item) => {
142
- const alias = sequelize.getQueryInterface().quoteIdentifier(item.alias);
143
- const name = hasAgg ? sequelize.literal(alias) : sequelize.col(item.field);
144
- order.push([name, item.order || "ASC"]);
145
- });
146
- return {
147
- queryParams: {
148
- where,
149
- attributes,
150
- include,
151
- group,
152
- order,
153
- limit: limit > 2e3 ? 2e3 : limit,
154
- raw: true
155
- },
156
- fieldMap
213
+ const parseVariables = async (ctx, next) => {
214
+ const { filter } = ctx.action.params.values;
215
+ if (!filter) {
216
+ return next();
217
+ }
218
+ const isNumeric = (str) => {
219
+ if (typeof str === "number")
220
+ return true;
221
+ if (typeof str != "string")
222
+ return false;
223
+ return !isNaN(str) && !isNaN(parseFloat(str));
157
224
  };
158
- };
159
- const processData = (ctx, data, fieldMap) => {
160
- const { sequelize } = ctx.db;
161
- const dialect = sequelize.getDialect();
162
- switch (dialect) {
163
- case "postgres":
164
- return data.map((record) => {
165
- const result = {};
166
- Object.entries(record).forEach(([key, value]) => {
167
- const { type } = fieldMap[key] || {};
168
- switch (type) {
169
- case "bigInt":
170
- case "integer":
171
- case "float":
172
- case "double":
173
- value = Number(value);
174
- break;
175
- }
176
- result[key] = value;
177
- });
178
- return result;
225
+ const getUser = () => {
226
+ return async ({ fields }) => {
227
+ var _a, _b;
228
+ const userFields = fields.filter((f) => f && ctx.db.getFieldByPath("users." + f));
229
+ (_a = ctx.logger) == null ? void 0 : _a.info("filter-parse: ", { userFields });
230
+ if (!ctx.state.currentUser) {
231
+ return;
232
+ }
233
+ if (!userFields.length) {
234
+ return;
235
+ }
236
+ const user = await ctx.db.getRepository("users").findOne({
237
+ filterByTk: ctx.state.currentUser.id,
238
+ fields: userFields
179
239
  });
180
- default:
181
- return data;
182
- }
183
- };
184
- const queryData = async (ctx, builder) => {
185
- const { collection, measures, dimensions, orders, filter, limit, sql } = builder;
186
- const model = ctx.db.getModel(collection);
187
- const { queryParams, fieldMap } = parseBuilder(ctx, { collection, measures, dimensions, orders, filter, limit });
188
- const data = await model.findAll(queryParams);
189
- return processData(ctx, data, fieldMap);
240
+ (_b = ctx.logger) == null ? void 0 : _b.info("filter-parse: ", {
241
+ $user: user == null ? void 0 : user.toJSON()
242
+ });
243
+ return user;
244
+ };
245
+ };
246
+ ctx.action.params.values.filter = await (0, import_utils.parseFilter)(filter, {
247
+ timezone: ctx.get("x-timezone"),
248
+ now: (/* @__PURE__ */ new Date()).toISOString(),
249
+ getField: (path) => {
250
+ const fieldPath = path.split(".").filter((p) => !p.startsWith("$") && !isNumeric(p)).join(".");
251
+ const { resourceName } = ctx.action;
252
+ return ctx.db.getFieldByPath(`${resourceName}.${fieldPath}`);
253
+ },
254
+ vars: {
255
+ $nDate: (0, import_utils.getDateVars)(),
256
+ $user: getUser()
257
+ }
258
+ });
259
+ await next();
190
260
  };
191
- const cacheWrap = async (cache, options) => {
192
- const { func, key, ttl, useCache, refresh } = options;
261
+ const cacheMiddleware = async (ctx, next) => {
262
+ const { uid, cache: cacheConfig, refresh } = ctx.action.params.values;
263
+ const plugin = ctx.app.getPlugin("data-visualization");
264
+ const cache = plugin.cache;
265
+ const useCache = (cacheConfig == null ? void 0 : cacheConfig.enabled) && uid;
193
266
  if (useCache && !refresh) {
194
- const data2 = await cache.get(key);
195
- if (data2) {
196
- return data2;
267
+ const data = await cache.get(uid);
268
+ if (data) {
269
+ ctx.body = data;
270
+ return;
197
271
  }
198
272
  }
199
- const data = await func();
273
+ await next();
200
274
  if (useCache) {
201
- await cache.set(key, data, ttl);
275
+ console.log(uid, ctx.body);
276
+ await cache.set(uid, ctx.body, (cacheConfig == null ? void 0 : cacheConfig.ttl) || 30);
277
+ console.log(cache.get(uid));
202
278
  }
203
- return data;
204
279
  };
205
- const query = async (ctx, next) => {
206
- const {
207
- uid,
208
- collection,
209
- measures,
210
- dimensions,
211
- orders,
212
- filter,
213
- limit,
214
- sql,
215
- cache: cacheConfig,
216
- refresh
217
- } = ctx.action.params.values;
280
+ const checkPermission = (ctx, next) => {
281
+ const { collection } = ctx.action.params.values;
218
282
  const roleName = ctx.state.currentRole || "anonymous";
219
283
  const can = ctx.app.acl.can({ role: roleName, resource: collection, action: "list" });
220
284
  if (!can && roleName !== "root") {
221
285
  ctx.throw(403, "No permissions");
222
286
  }
223
- const plugin = ctx.app.getPlugin("data-visualization");
224
- const cache = plugin.cache;
225
- const useCache = (cacheConfig == null ? void 0 : cacheConfig.enabled) && uid;
287
+ return next();
288
+ };
289
+ const query = async (ctx, next) => {
226
290
  try {
227
- ctx.body = await cacheWrap(cache, {
228
- func: async () => await queryData(ctx, { collection, measures, dimensions, orders, filter, limit, sql }),
229
- key: uid,
230
- ttl: (cacheConfig == null ? void 0 : cacheConfig.ttl) || 30,
231
- useCache: useCache ? true : false,
232
- refresh
291
+ await (0, import_koa_compose.default)([
292
+ checkPermission,
293
+ cacheMiddleware,
294
+ parseVariables,
295
+ parseFieldAndAssociations,
296
+ parseBuilder,
297
+ queryData,
298
+ postProcess
299
+ ])(ctx, async () => {
233
300
  });
234
301
  } catch (err) {
235
302
  ctx.app.logger.error("charts query: ", err);
@@ -239,10 +306,11 @@ const query = async (ctx, next) => {
239
306
  };
240
307
  // Annotate the CommonJS export names for ESM import in node:
241
308
  0 && (module.exports = {
242
- cacheWrap,
309
+ cacheMiddleware,
243
310
  parseBuilder,
244
311
  parseFieldAndAssociations,
245
- processData,
312
+ parseVariables,
313
+ postProcess,
246
314
  query,
247
315
  queryData
248
316
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/plugin-data-visualization",
3
- "version": "0.13.0-alpha.9",
3
+ "version": "0.14.0-alpha.2",
4
4
  "displayName": "Data Visualization",
5
5
  "displayName.zh-CN": "数据可视化",
6
6
  "description": "Provides business intelligence and data visualization features",
@@ -17,6 +17,7 @@
17
17
  "@testing-library/react": "^14.0.0",
18
18
  "antd": "5.x",
19
19
  "classnames": "^2.3.1",
20
+ "koa-compose": "^4.1.0",
20
21
  "lodash": "^4.17.21",
21
22
  "react": "^18.2.0",
22
23
  "react-error-boundary": "^4.0.10",
@@ -32,5 +33,5 @@
32
33
  "@nocobase/test": "0.x",
33
34
  "@nocobase/utils": "0.x"
34
35
  },
35
- "gitHead": "ee5825377d8bec05f968a8365a0cdcd49878ada6"
36
+ "gitHead": "ea841d4a1e8c37aa77b4fbae6a6a4937e2aa3c0a"
36
37
  }
package/server.d.ts CHANGED
File without changes
package/server.js CHANGED
File without changes