@webtypen/webframez-core 0.2.8 → 0.2.10

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.
@@ -15,6 +15,7 @@ const Config_1 = require("./Config");
15
15
  const BuildFinishCommand_1 = require("./Commands/BuildFinishCommand");
16
16
  const QueueJobsRegisty_1 = require("./Queue/QueueJobsRegisty");
17
17
  const QueueRunCommand_1 = require("./Commands/QueueRunCommand");
18
+ const DatatableRegistry_1 = require("./Datatable/DatatableRegistry");
18
19
  class ConsoleApplication {
19
20
  /**
20
21
  * Init the routes and start the http-server
@@ -62,6 +63,9 @@ class ConsoleApplication {
62
63
  program.addCommand(consoleCommand);
63
64
  }
64
65
  }
66
+ if (options && options.datatables) {
67
+ DatatableRegistry_1.DatatableRegistry.registerMany(options.datatables);
68
+ }
65
69
  if (options.jobs && options.jobs.length > 0) {
66
70
  QueueJobsRegisty_1.QueueJobsRegisty.registerJob(options.jobs);
67
71
  }
@@ -0,0 +1,37 @@
1
+ import { Request } from "../Router/Request";
2
+ export declare class Datatable {
3
+ collection: string | Function;
4
+ aggregation: {
5
+ [key: string]: any;
6
+ } | Function | null;
7
+ subAggregation: {
8
+ [key: string]: any;
9
+ } | Function | null;
10
+ filter: {
11
+ [key: string]: any;
12
+ } | Function | null;
13
+ columns: {
14
+ [key: string]: any;
15
+ } | Function | null;
16
+ onPressLink: string | Function | null;
17
+ exports: any | null;
18
+ perPage: number;
19
+ onRow?: Function;
20
+ onAttributes?: Function;
21
+ getInit(req: Request): Promise<any>;
22
+ getAggregation(req: Request): Promise<any>;
23
+ getSubAggregation(req: Request): Promise<any>;
24
+ getCollection(req: Request): Promise<any>;
25
+ getData(req: Request): Promise<{
26
+ page: number;
27
+ max_entries: any;
28
+ total_pages: number;
29
+ entries: any;
30
+ sums: any;
31
+ }>;
32
+ getTotalData(req: Request): Promise<any>;
33
+ getFilter(req: Request): Promise<any>;
34
+ getColumns(req: Request): Promise<any>;
35
+ getOnPressLink(req: Request): Promise<any>;
36
+ getFilterMatch(req: Request, searchOrFilter: any): Promise<any>;
37
+ }
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.Datatable = void 0;
13
+ const DBConnection_1 = require("../Database/DBConnection");
14
+ const NumericFunctions_1 = require("../Functions/NumericFunctions");
15
+ class Datatable {
16
+ constructor() {
17
+ this.collection = "";
18
+ this.aggregation = null;
19
+ this.subAggregation = null;
20
+ this.filter = null;
21
+ this.columns = null;
22
+ this.onPressLink = null;
23
+ this.exports = null;
24
+ this.perPage = 25;
25
+ }
26
+ getInit(req) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ const out = { table: req.body._table };
29
+ const promises = [];
30
+ promises.push(new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
31
+ out.filter = yield this.getFilter(req);
32
+ resolve(true);
33
+ })));
34
+ promises.push(new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
35
+ const cols = [];
36
+ const colsData = yield this.getColumns(req);
37
+ if (colsData && Object.keys(colsData).length > 0) {
38
+ for (let i in colsData) {
39
+ cols.push(Object.assign(Object.assign({}, colsData[i]), { key: i }));
40
+ }
41
+ }
42
+ out.columns = cols;
43
+ resolve(true);
44
+ })));
45
+ promises.push(new Promise((resolve) => __awaiter(this, void 0, void 0, function* () {
46
+ out.onPressLink = yield this.getOnPressLink(req);
47
+ resolve(true);
48
+ })));
49
+ if (this.exports) {
50
+ out.export_definitions = [];
51
+ for (let key in this.exports) {
52
+ out.export_definitions.push({
53
+ key: key,
54
+ title: this.exports[key].title,
55
+ contentType: this.exports[key].contentType,
56
+ });
57
+ }
58
+ }
59
+ yield Promise.all(promises);
60
+ return out;
61
+ });
62
+ }
63
+ getAggregation(req) {
64
+ return __awaiter(this, void 0, void 0, function* () {
65
+ return typeof this.aggregation === "function" ? yield this.aggregation(req) : this.aggregation;
66
+ });
67
+ }
68
+ getSubAggregation(req) {
69
+ return __awaiter(this, void 0, void 0, function* () {
70
+ return typeof this.subAggregation === "function" ? yield this.subAggregation(req) : this.subAggregation;
71
+ });
72
+ }
73
+ getCollection(req) {
74
+ return __awaiter(this, void 0, void 0, function* () {
75
+ if (this.collection && typeof this.collection === "function") {
76
+ return yield this.collection(req);
77
+ }
78
+ return this.collection;
79
+ });
80
+ }
81
+ getData(req) {
82
+ return __awaiter(this, void 0, void 0, function* () {
83
+ const page = req.body.page && parseInt(req.body.page) > 0 ? parseInt(req.body.page) : 0;
84
+ const aggregation = [];
85
+ const aggregationStats = [];
86
+ const aggregationDef = yield this.getAggregation(req);
87
+ const subAggregation = [];
88
+ const subAggregationDef = yield this.getSubAggregation(req);
89
+ let hasSkip = false;
90
+ let hasLimit = false;
91
+ if (aggregationDef && aggregationDef.length > 0) {
92
+ for (let entry of aggregationDef) {
93
+ if (entry.$skip !== undefined) {
94
+ hasSkip = true;
95
+ }
96
+ if (entry.$limit !== undefined) {
97
+ hasLimit = true;
98
+ }
99
+ if (entry.skipStats) {
100
+ delete entry.skipStats;
101
+ aggregation.push(entry);
102
+ }
103
+ else {
104
+ aggregation.push(entry);
105
+ aggregationStats.push(entry);
106
+ }
107
+ }
108
+ }
109
+ if (subAggregationDef && subAggregationDef.length > 0) {
110
+ for (let entry of subAggregationDef) {
111
+ if (entry.$skip !== undefined) {
112
+ hasSkip = true;
113
+ }
114
+ if (entry.$limit !== undefined) {
115
+ hasLimit = true;
116
+ }
117
+ if (entry.skipStats) {
118
+ delete entry.skipStats;
119
+ subAggregation.push(entry);
120
+ }
121
+ else {
122
+ subAggregation.push(entry);
123
+ aggregationStats.push(entry);
124
+ }
125
+ }
126
+ }
127
+ const connection = yield DBConnection_1.DBConnection.getConnection();
128
+ const results = yield connection.client
129
+ .db(null)
130
+ .collection(yield this.getCollection(req))
131
+ .aggregate([
132
+ ...(aggregation ? aggregation : []),
133
+ ...(!hasSkip ? [{ $skip: page ? page * this.perPage : 0 }] : []),
134
+ ...(!hasLimit ? [{ $limit: this.perPage }] : []),
135
+ ...(subAggregation ? subAggregation : []),
136
+ ], {
137
+ collation: {
138
+ locale: "de",
139
+ strength: 2,
140
+ },
141
+ })
142
+ .toArray();
143
+ const statsGroup = { $group: { _id: null, count: { $sum: 1 } } };
144
+ if (req.body.sums && Object.keys(req.body.sums).length > 0) {
145
+ for (let key in req.body.sums) {
146
+ if (req.body.sums[key] && req.body.sums[key].mapping) {
147
+ statsGroup.$group["sum_" + req.body.sums[key].mapping] = { $sum: "$" + req.body.sums[key].mapping };
148
+ }
149
+ }
150
+ }
151
+ const stats = yield connection.client
152
+ .db(null)
153
+ .collection(yield this.getCollection(req))
154
+ .aggregate([...(aggregationStats ? aggregationStats : []), statsGroup])
155
+ .toArray();
156
+ if (this.onRow || this.onAttributes) {
157
+ for (let i in results) {
158
+ if (this.onRow) {
159
+ results[i] = yield this.onRow(results[i]);
160
+ }
161
+ if (this.onAttributes) {
162
+ results[i].__cellattr = yield this.onAttributes(results[i]);
163
+ }
164
+ }
165
+ }
166
+ const sums = {};
167
+ if (req.body.sums && Object.keys(req.body.sums).length > 0) {
168
+ for (let key in req.body.sums) {
169
+ if (req.body.sums[key] && req.body.sums[key].mapping) {
170
+ sums[req.body.sums[key].mapping] = Object.assign(Object.assign({}, req.body.sums[key]), { value: stats &&
171
+ stats[0] &&
172
+ stats[0]["sum_" + req.body.sums[key].mapping] !== undefined &&
173
+ stats[0]["sum_" + req.body.sums[key].mapping] !== null
174
+ ? NumericFunctions_1.NumericFunctions.numberFormat(stats[0]["sum_" + req.body.sums[key].mapping]) +
175
+ (req.body.sums[key].suffix ? req.body.sums[key].suffix : "")
176
+ : undefined });
177
+ }
178
+ }
179
+ }
180
+ return {
181
+ page: page,
182
+ max_entries: stats && stats[0] && stats[0].count ? stats[0].count : 0,
183
+ total_pages: stats && stats[0] && stats[0].count > 0 ? Math.ceil(stats[0].count / this.perPage) : 0,
184
+ entries: results,
185
+ sums: sums,
186
+ };
187
+ });
188
+ }
189
+ getTotalData(req) {
190
+ return __awaiter(this, void 0, void 0, function* () {
191
+ const aggregation = [];
192
+ const aggregationDef = yield this.getAggregation(req);
193
+ if (aggregationDef && aggregationDef.length > 0) {
194
+ for (let entry of aggregationDef) {
195
+ if (entry.skipStats) {
196
+ delete entry.skipStats;
197
+ aggregation.push(entry);
198
+ }
199
+ }
200
+ }
201
+ const connection = yield DBConnection_1.DBConnection.getConnection();
202
+ return yield connection.client
203
+ .db(null)
204
+ .collection(yield this.getCollection(req))
205
+ .aggregate([...(aggregation ? aggregation : [])])
206
+ .toArray();
207
+ });
208
+ }
209
+ getFilter(req) {
210
+ return __awaiter(this, void 0, void 0, function* () {
211
+ if (!this.filter) {
212
+ return null;
213
+ }
214
+ if (typeof this.filter === "function") {
215
+ return yield this.filter(req);
216
+ }
217
+ return this.filter;
218
+ });
219
+ }
220
+ getColumns(req) {
221
+ return __awaiter(this, void 0, void 0, function* () {
222
+ if (!this.columns) {
223
+ return null;
224
+ }
225
+ if (typeof this.columns === "function") {
226
+ return yield this.columns(req);
227
+ }
228
+ return this.columns;
229
+ });
230
+ }
231
+ getOnPressLink(req) {
232
+ return __awaiter(this, void 0, void 0, function* () {
233
+ if (!this.onPressLink) {
234
+ return null;
235
+ }
236
+ if (typeof this.onPressLink === "function") {
237
+ return yield this.onPressLink(req);
238
+ }
239
+ return this.onPressLink;
240
+ });
241
+ }
242
+ getFilterMatch(req, searchOrFilter) {
243
+ return __awaiter(this, void 0, void 0, function* () {
244
+ if (!searchOrFilter || Object.keys(searchOrFilter).length < 1) {
245
+ return {};
246
+ }
247
+ const filterDef = yield this.getFilter(req);
248
+ const out = {};
249
+ for (let key in searchOrFilter) {
250
+ let value = undefined;
251
+ const entry = searchOrFilter[key];
252
+ const valueClean = entry.value !== undefined && entry.value !== "" ? entry.value : undefined;
253
+ const operator = entry.operator && entry.operator.trim() !== ""
254
+ ? entry.operator
255
+ : entry.mode && entry.mode.trim() !== ""
256
+ ? entry.mode
257
+ : "==";
258
+ const filterEl = filterDef && filterDef[key] ? filterDef[key] : null;
259
+ const entryType = entry.type && entry.type.trim() !== "" ? entry.type : filterEl && filterEl.type ? filterEl.type : null;
260
+ switch (operator) {
261
+ case "empty":
262
+ value = null;
263
+ break;
264
+ case "not_empty":
265
+ value = { $ne: null };
266
+ break;
267
+ case "==":
268
+ value = valueClean;
269
+ break;
270
+ case "!=":
271
+ if (valueClean !== undefined) {
272
+ value = { $ne: valueClean };
273
+ }
274
+ break;
275
+ case "$gt":
276
+ case "$gte":
277
+ case "$lt":
278
+ case "$lte":
279
+ if (valueClean !== undefined) {
280
+ value = {
281
+ [operator]: entryType === "number" || entryType === "numeric" ? parseFloat(valueClean) : valueClean,
282
+ };
283
+ }
284
+ break;
285
+ default:
286
+ if (valueClean !== undefined) {
287
+ if (entryType === "number" || entryType === "numeric" || entryType === "currency") {
288
+ value = parseFloat(valueClean.toString().replace(",", "."));
289
+ }
290
+ else if (entryType === "boolean") {
291
+ if (entry.value === "true") {
292
+ value = true;
293
+ }
294
+ else if (entry.value === "false") {
295
+ value = false;
296
+ }
297
+ }
298
+ else {
299
+ value = new RegExp(valueClean, "i");
300
+ }
301
+ }
302
+ break;
303
+ }
304
+ if (value !== undefined) {
305
+ out[key] = value;
306
+ }
307
+ }
308
+ return out;
309
+ });
310
+ }
311
+ }
312
+ exports.Datatable = Datatable;
@@ -0,0 +1,7 @@
1
+ import { Controller } from "../Controller/Controller";
2
+ import { Request } from "../Router/Request";
3
+ import { Response } from "../Router/Response";
4
+ export declare class DatatableController extends Controller {
5
+ restApi(req: Request, res: Response): Promise<Response>;
6
+ tableExport(req: Request, res: Response): Promise<Response>;
7
+ }
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.DatatableController = void 0;
13
+ const Controller_1 = require("../Controller/Controller");
14
+ const DatatableRegistry_1 = require("./DatatableRegistry");
15
+ class DatatableController extends Controller_1.Controller {
16
+ restApi(req, res) {
17
+ return __awaiter(this, void 0, void 0, function* () {
18
+ if (!req.body._table || req.body._table.trim() === "") {
19
+ return res.status(404).send({
20
+ status: "error",
21
+ message: "Missing table-key ...",
22
+ });
23
+ }
24
+ const tableClass = DatatableRegistry_1.DatatableRegistry.getTable(req.body._table);
25
+ if (!tableClass) {
26
+ return res.status(404).send({
27
+ status: "error",
28
+ message: "Table '" + req.body._table + "' not found ...",
29
+ });
30
+ }
31
+ if (req.body.init_request) {
32
+ const table = new tableClass();
33
+ return res.send({
34
+ status: "success",
35
+ data: yield table.getInit(req),
36
+ });
37
+ }
38
+ if (!req.body._table || req.body._table.trim() === "") {
39
+ return res.status(404).send({
40
+ status: "error",
41
+ message: "Missing table-key ...",
42
+ });
43
+ }
44
+ const table = new tableClass();
45
+ return res.send({
46
+ status: "success",
47
+ data: yield table.getData(req),
48
+ });
49
+ });
50
+ }
51
+ tableExport(req, res) {
52
+ return __awaiter(this, void 0, void 0, function* () {
53
+ if (!req.body._table || req.body._table.trim() === "") {
54
+ return res.status(404).send({
55
+ status: "error",
56
+ message: "Missing table-key ...",
57
+ });
58
+ }
59
+ const tableClass = DatatableRegistry_1.DatatableRegistry.getTable(req.body._table);
60
+ if (!tableClass) {
61
+ return res.status(404).send({
62
+ status: "error",
63
+ message: "Table '" + req.body._table + "' not found ...",
64
+ });
65
+ }
66
+ const table = new tableClass();
67
+ if (!req.body.export || !table.exports || (table.exports && !table.exports[req.body.export])) {
68
+ return res.status(404).send({
69
+ status: "error",
70
+ message: "Unknown export-type ...",
71
+ });
72
+ }
73
+ const exp = table.exports[req.body.export];
74
+ const cols = [];
75
+ const colsData = yield table.getColumns(req);
76
+ if (colsData && Object.keys(colsData).length > 0) {
77
+ for (let i in colsData) {
78
+ cols.push(Object.assign(Object.assign({}, colsData[i]), { key: i }));
79
+ }
80
+ }
81
+ const entries = yield table.getTotalData(req);
82
+ const exportData = yield exp.generate(entries, cols, req);
83
+ const base64 = Buffer.from("\uFEFF" + exportData).toString("base64");
84
+ return res.send({
85
+ status: "success",
86
+ data: {
87
+ filename: exp.filename ? (typeof exp.filename === "function" ? yield exp.filename(req) : exp.filename) : "Export",
88
+ base64: (exp.contentType ? exp.contentType + ";base64," : "") + base64,
89
+ },
90
+ });
91
+ });
92
+ }
93
+ }
94
+ exports.DatatableController = DatatableController;
@@ -0,0 +1,8 @@
1
+ declare class DatatableRegistryWrapper {
2
+ tables: any;
3
+ register(key: string, tableClass: any): this;
4
+ registerMany(data: any): this;
5
+ getTable(key: string): any;
6
+ }
7
+ export declare const DatatableRegistry: DatatableRegistryWrapper;
8
+ export {};
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DatatableRegistry = void 0;
4
+ class DatatableRegistryWrapper {
5
+ constructor() {
6
+ this.tables = {};
7
+ }
8
+ register(key, tableClass) {
9
+ this.tables[key] = tableClass;
10
+ return this;
11
+ }
12
+ registerMany(data) {
13
+ if (typeof data === "object") {
14
+ for (let key in data) {
15
+ this.register(key, data[key]);
16
+ }
17
+ }
18
+ return this;
19
+ }
20
+ getTable(key) {
21
+ return this.tables[key] ? this.tables[key] : null;
22
+ }
23
+ }
24
+ exports.DatatableRegistry = new DatatableRegistryWrapper();
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LambdaApplication = void 0;
4
4
  const Config_1 = require("./Config");
5
+ const DatatableRegistry_1 = require("./Datatable/DatatableRegistry");
5
6
  const QueueJobsRegisty_1 = require("./Queue/QueueJobsRegisty");
6
7
  const Router_1 = require("./Router/Router");
7
8
  class LambdaApplication {
@@ -21,6 +22,9 @@ class LambdaApplication {
21
22
  routesFunction: options && options.routesFunction ? options.routesFunction : null,
22
23
  tempDir: options && options.tempDir ? options.tempDir : null,
23
24
  });
25
+ if (options && options.datatables) {
26
+ DatatableRegistry_1.DatatableRegistry.registerMany(options.datatables);
27
+ }
24
28
  if (options.jobs && options.jobs.length > 0) {
25
29
  QueueJobsRegisty_1.QueueJobsRegisty.registerJob(options.jobs);
26
30
  }
@@ -8,6 +8,7 @@ const http_1 = __importDefault(require("http"));
8
8
  const Config_1 = require("./Config");
9
9
  const Router_1 = require("./Router/Router");
10
10
  const QueueJobsRegisty_1 = require("./Queue/QueueJobsRegisty");
11
+ const DatatableRegistry_1 = require("./Datatable/DatatableRegistry");
11
12
  class WebApplication {
12
13
  /**
13
14
  * Init the routes and start the http-server
@@ -25,6 +26,9 @@ class WebApplication {
25
26
  routesFunction: options && options.routesFunction ? options.routesFunction : null,
26
27
  tempDir: options && options.tempDir ? options.tempDir : null,
27
28
  });
29
+ if (options && options.datatables) {
30
+ DatatableRegistry_1.DatatableRegistry.registerMany(options.datatables);
31
+ }
28
32
  if (options.jobs && options.jobs.length > 0) {
29
33
  QueueJobsRegisty_1.QueueJobsRegisty.registerJob(options.jobs);
30
34
  }
package/dist/index.d.ts CHANGED
@@ -22,6 +22,9 @@ export * from "./Facades/StorageDrivers/BaseStorageDriver";
22
22
  export * from "./Facades/Storage";
23
23
  export * from "./DataBuilder/DataBuilder";
24
24
  export * from "./DataBuilder/DataBuilderController";
25
+ export * from "./Datatable/Datatable";
26
+ export * from "./Datatable/DatatableRegistry";
27
+ export * from "./Datatable/DatatableController";
25
28
  export * from "./Queue/BaseQueueJob";
26
29
  export * from "./Queue/QueueJob";
27
30
  export * from "./Queue/QueueJobsRegisty";
package/dist/index.js CHANGED
@@ -38,6 +38,9 @@ __exportStar(require("./Facades/StorageDrivers/BaseStorageDriver"), exports);
38
38
  __exportStar(require("./Facades/Storage"), exports);
39
39
  __exportStar(require("./DataBuilder/DataBuilder"), exports);
40
40
  __exportStar(require("./DataBuilder/DataBuilderController"), exports);
41
+ __exportStar(require("./Datatable/Datatable"), exports);
42
+ __exportStar(require("./Datatable/DatatableRegistry"), exports);
43
+ __exportStar(require("./Datatable/DatatableController"), exports);
41
44
  __exportStar(require("./Queue/BaseQueueJob"), exports);
42
45
  __exportStar(require("./Queue/QueueJob"), exports);
43
46
  __exportStar(require("./Queue/QueueJobsRegisty"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webtypen/webframez-core",
3
- "version": "0.2.8",
3
+ "version": "0.2.10",
4
4
  "description": "The core package of the typescript based webframez (web-framework) by webtypen.de",
5
5
  "homepage": "https://webtypen.de/",
6
6
  "license": "MIT",
@@ -3,6 +3,7 @@ import { Config } from "./Config";
3
3
  import { BuildFinishCommand } from "./Commands/BuildFinishCommand";
4
4
  import { QueueJobsRegisty } from "./Queue/QueueJobsRegisty";
5
5
  import { QueueRunCommand } from "./Commands/QueueRunCommand";
6
+ import { DatatableRegistry } from "./Datatable/DatatableRegistry";
6
7
 
7
8
  export class ConsoleApplication {
8
9
  /**
@@ -58,6 +59,10 @@ export class ConsoleApplication {
58
59
  }
59
60
  }
60
61
 
62
+ if (options && options.datatables) {
63
+ DatatableRegistry.registerMany(options.datatables);
64
+ }
65
+
61
66
  if (options.jobs && options.jobs.length > 0) {
62
67
  QueueJobsRegisty.registerJob(options.jobs);
63
68
  }
@@ -0,0 +1,322 @@
1
+ import { DBConnection } from "../Database/DBConnection";
2
+ import { NumericFunctions } from "../Functions/NumericFunctions";
3
+ import { Request } from "../Router/Request";
4
+
5
+ export class Datatable {
6
+ collection: string | Function = "";
7
+ aggregation: { [key: string]: any } | Function | null = null;
8
+ subAggregation: { [key: string]: any } | Function | null = null;
9
+ filter: { [key: string]: any } | Function | null = null;
10
+ columns: { [key: string]: any } | Function | null = null;
11
+ onPressLink: string | Function | null = null;
12
+ exports: any | null = null;
13
+ perPage: number = 25;
14
+ onRow?: Function;
15
+ onAttributes?: Function;
16
+
17
+ async getInit(req: Request) {
18
+ const out: any = { table: req.body._table };
19
+ const promises = [];
20
+ promises.push(
21
+ new Promise(async (resolve) => {
22
+ out.filter = await this.getFilter(req);
23
+ resolve(true);
24
+ })
25
+ );
26
+ promises.push(
27
+ new Promise(async (resolve) => {
28
+ const cols: any = [];
29
+ const colsData = await this.getColumns(req);
30
+ if (colsData && Object.keys(colsData).length > 0) {
31
+ for (let i in colsData) {
32
+ cols.push({ ...colsData[i], key: i });
33
+ }
34
+ }
35
+ out.columns = cols;
36
+ resolve(true);
37
+ })
38
+ );
39
+ promises.push(
40
+ new Promise(async (resolve) => {
41
+ out.onPressLink = await this.getOnPressLink(req);
42
+ resolve(true);
43
+ })
44
+ );
45
+
46
+ if (this.exports) {
47
+ out.export_definitions = [];
48
+
49
+ for (let key in this.exports) {
50
+ out.export_definitions.push({
51
+ key: key,
52
+ title: this.exports[key].title,
53
+ contentType: this.exports[key].contentType,
54
+ });
55
+ }
56
+ }
57
+
58
+ await Promise.all(promises);
59
+ return out;
60
+ }
61
+
62
+ async getAggregation(req: Request) {
63
+ return typeof this.aggregation === "function" ? await this.aggregation(req) : this.aggregation;
64
+ }
65
+
66
+ async getSubAggregation(req: Request) {
67
+ return typeof this.subAggregation === "function" ? await this.subAggregation(req) : this.subAggregation;
68
+ }
69
+
70
+ async getCollection(req: Request) {
71
+ if (this.collection && typeof this.collection === "function") {
72
+ return await this.collection(req);
73
+ }
74
+ return this.collection;
75
+ }
76
+
77
+ async getData(req: Request) {
78
+ const page = req.body.page && parseInt(req.body.page) > 0 ? parseInt(req.body.page) : 0;
79
+ const aggregation: any = [];
80
+ const aggregationStats: any = [];
81
+ const aggregationDef = await this.getAggregation(req);
82
+ const subAggregation: any = [];
83
+ const subAggregationDef = await this.getSubAggregation(req);
84
+ let hasSkip = false;
85
+ let hasLimit = false;
86
+
87
+ if (aggregationDef && aggregationDef.length > 0) {
88
+ for (let entry of aggregationDef) {
89
+ if (entry.$skip !== undefined) {
90
+ hasSkip = true;
91
+ }
92
+ if (entry.$limit !== undefined) {
93
+ hasLimit = true;
94
+ }
95
+
96
+ if (entry.skipStats) {
97
+ delete entry.skipStats;
98
+ aggregation.push(entry);
99
+ } else {
100
+ aggregation.push(entry);
101
+ aggregationStats.push(entry);
102
+ }
103
+ }
104
+ }
105
+
106
+ if (subAggregationDef && subAggregationDef.length > 0) {
107
+ for (let entry of subAggregationDef) {
108
+ if (entry.$skip !== undefined) {
109
+ hasSkip = true;
110
+ }
111
+ if (entry.$limit !== undefined) {
112
+ hasLimit = true;
113
+ }
114
+
115
+ if (entry.skipStats) {
116
+ delete entry.skipStats;
117
+ subAggregation.push(entry);
118
+ } else {
119
+ subAggregation.push(entry);
120
+ aggregationStats.push(entry);
121
+ }
122
+ }
123
+ }
124
+
125
+ const connection = await DBConnection.getConnection();
126
+ const results = await connection.client
127
+ .db(null)
128
+ .collection(await this.getCollection(req))
129
+ .aggregate(
130
+ [
131
+ ...(aggregation ? aggregation : []),
132
+ ...(!hasSkip ? [{ $skip: page ? page * this.perPage : 0 }] : []),
133
+ ...(!hasLimit ? [{ $limit: this.perPage }] : []),
134
+ ...(subAggregation ? subAggregation : []),
135
+ ],
136
+ {
137
+ collation: {
138
+ locale: "de",
139
+ strength: 2,
140
+ },
141
+ }
142
+ )
143
+ .toArray();
144
+
145
+ const statsGroup: any = { $group: { _id: null, count: { $sum: 1 } } };
146
+ if (req.body.sums && Object.keys(req.body.sums).length > 0) {
147
+ for (let key in req.body.sums) {
148
+ if (req.body.sums[key] && req.body.sums[key].mapping) {
149
+ statsGroup.$group["sum_" + req.body.sums[key].mapping] = { $sum: "$" + req.body.sums[key].mapping };
150
+ }
151
+ }
152
+ }
153
+
154
+ const stats = await connection.client
155
+ .db(null)
156
+ .collection(await this.getCollection(req))
157
+ .aggregate([...(aggregationStats ? aggregationStats : []), statsGroup])
158
+ .toArray();
159
+
160
+ if (this.onRow || this.onAttributes) {
161
+ for (let i in results) {
162
+ if (this.onRow) {
163
+ results[i] = await this.onRow(results[i]);
164
+ }
165
+
166
+ if (this.onAttributes) {
167
+ results[i].__cellattr = await this.onAttributes(results[i]);
168
+ }
169
+ }
170
+ }
171
+
172
+ const sums: any = {};
173
+ if (req.body.sums && Object.keys(req.body.sums).length > 0) {
174
+ for (let key in req.body.sums) {
175
+ if (req.body.sums[key] && req.body.sums[key].mapping) {
176
+ sums[req.body.sums[key].mapping] = {
177
+ ...req.body.sums[key],
178
+ value:
179
+ stats &&
180
+ stats[0] &&
181
+ stats[0]["sum_" + req.body.sums[key].mapping] !== undefined &&
182
+ stats[0]["sum_" + req.body.sums[key].mapping] !== null
183
+ ? NumericFunctions.numberFormat(stats[0]["sum_" + req.body.sums[key].mapping]) +
184
+ (req.body.sums[key].suffix ? req.body.sums[key].suffix : "")
185
+ : undefined,
186
+ };
187
+ }
188
+ }
189
+ }
190
+
191
+ return {
192
+ page: page,
193
+ max_entries: stats && stats[0] && stats[0].count ? stats[0].count : 0,
194
+ total_pages: stats && stats[0] && stats[0].count > 0 ? Math.ceil(stats[0].count / this.perPage) : 0,
195
+ entries: results,
196
+ sums: sums,
197
+ };
198
+ }
199
+
200
+ async getTotalData(req: Request) {
201
+ const aggregation: any = [];
202
+ const aggregationDef = await this.getAggregation(req);
203
+
204
+ if (aggregationDef && aggregationDef.length > 0) {
205
+ for (let entry of aggregationDef) {
206
+ if (entry.skipStats) {
207
+ delete entry.skipStats;
208
+ aggregation.push(entry);
209
+ }
210
+ }
211
+ }
212
+
213
+ const connection = await DBConnection.getConnection();
214
+ return await connection.client
215
+ .db(null)
216
+ .collection(await this.getCollection(req))
217
+ .aggregate([...(aggregation ? aggregation : [])])
218
+ .toArray();
219
+ }
220
+
221
+ async getFilter(req: Request) {
222
+ if (!this.filter) {
223
+ return null;
224
+ }
225
+
226
+ if (typeof this.filter === "function") {
227
+ return await this.filter(req);
228
+ }
229
+ return this.filter;
230
+ }
231
+
232
+ async getColumns(req: Request) {
233
+ if (!this.columns) {
234
+ return null;
235
+ }
236
+
237
+ if (typeof this.columns === "function") {
238
+ return await this.columns(req);
239
+ }
240
+ return this.columns;
241
+ }
242
+
243
+ async getOnPressLink(req: Request) {
244
+ if (!this.onPressLink) {
245
+ return null;
246
+ }
247
+
248
+ if (typeof this.onPressLink === "function") {
249
+ return await this.onPressLink(req);
250
+ }
251
+ return this.onPressLink;
252
+ }
253
+
254
+ async getFilterMatch(req: Request, searchOrFilter: any) {
255
+ if (!searchOrFilter || Object.keys(searchOrFilter).length < 1) {
256
+ return {};
257
+ }
258
+
259
+ const filterDef = await this.getFilter(req);
260
+ const out: any = {};
261
+ for (let key in searchOrFilter) {
262
+ let value: any = undefined;
263
+
264
+ const entry = searchOrFilter[key];
265
+ const valueClean = entry.value !== undefined && entry.value !== "" ? entry.value : undefined;
266
+ const operator =
267
+ entry.operator && entry.operator.trim() !== ""
268
+ ? entry.operator
269
+ : entry.mode && entry.mode.trim() !== ""
270
+ ? entry.mode
271
+ : "==";
272
+ const filterEl = filterDef && filterDef[key] ? filterDef[key] : null;
273
+ const entryType = entry.type && entry.type.trim() !== "" ? entry.type : filterEl && filterEl.type ? filterEl.type : null;
274
+ switch (operator) {
275
+ case "empty":
276
+ value = null;
277
+ break;
278
+ case "not_empty":
279
+ value = { $ne: null };
280
+ break;
281
+ case "==":
282
+ value = valueClean;
283
+ break;
284
+ case "!=":
285
+ if (valueClean !== undefined) {
286
+ value = { $ne: valueClean };
287
+ }
288
+ break;
289
+ case "$gt":
290
+ case "$gte":
291
+ case "$lt":
292
+ case "$lte":
293
+ if (valueClean !== undefined) {
294
+ value = {
295
+ [operator]: entryType === "number" || entryType === "numeric" ? parseFloat(valueClean) : valueClean,
296
+ };
297
+ }
298
+ break;
299
+ default:
300
+ if (valueClean !== undefined) {
301
+ if (entryType === "number" || entryType === "numeric" || entryType === "currency") {
302
+ value = parseFloat(valueClean.toString().replace(",", "."));
303
+ } else if (entryType === "boolean") {
304
+ if (entry.value === "true") {
305
+ value = true;
306
+ } else if (entry.value === "false") {
307
+ value = false;
308
+ }
309
+ } else {
310
+ value = new RegExp(valueClean, "i");
311
+ }
312
+ }
313
+ break;
314
+ }
315
+
316
+ if (value !== undefined) {
317
+ out[key] = value;
318
+ }
319
+ }
320
+ return out;
321
+ }
322
+ }
@@ -0,0 +1,90 @@
1
+ import { Controller } from "../Controller/Controller";
2
+ import { Request } from "../Router/Request";
3
+ import { Response } from "../Router/Response";
4
+ import { DatatableRegistry } from "./DatatableRegistry";
5
+
6
+ export class DatatableController extends Controller {
7
+ async restApi(req: Request, res: Response) {
8
+ if (!req.body._table || req.body._table.trim() === "") {
9
+ return res.status(404).send({
10
+ status: "error",
11
+ message: "Missing table-key ...",
12
+ });
13
+ }
14
+
15
+ const tableClass = DatatableRegistry.getTable(req.body._table);
16
+ if (!tableClass) {
17
+ return res.status(404).send({
18
+ status: "error",
19
+ message: "Table '" + req.body._table + "' not found ...",
20
+ });
21
+ }
22
+
23
+ if (req.body.init_request) {
24
+ const table = new tableClass();
25
+ return res.send({
26
+ status: "success",
27
+ data: await table.getInit(req),
28
+ });
29
+ }
30
+
31
+ if (!req.body._table || req.body._table.trim() === "") {
32
+ return res.status(404).send({
33
+ status: "error",
34
+ message: "Missing table-key ...",
35
+ });
36
+ }
37
+
38
+ const table = new tableClass();
39
+ return res.send({
40
+ status: "success",
41
+ data: await table.getData(req),
42
+ });
43
+ }
44
+
45
+ async tableExport(req: Request, res: Response) {
46
+ if (!req.body._table || req.body._table.trim() === "") {
47
+ return res.status(404).send({
48
+ status: "error",
49
+ message: "Missing table-key ...",
50
+ });
51
+ }
52
+
53
+ const tableClass = DatatableRegistry.getTable(req.body._table);
54
+ if (!tableClass) {
55
+ return res.status(404).send({
56
+ status: "error",
57
+ message: "Table '" + req.body._table + "' not found ...",
58
+ });
59
+ }
60
+
61
+ const table = new tableClass();
62
+ if (!req.body.export || !table.exports || (table.exports && !table.exports[req.body.export])) {
63
+ return res.status(404).send({
64
+ status: "error",
65
+ message: "Unknown export-type ...",
66
+ });
67
+ }
68
+
69
+ const exp = table.exports[req.body.export];
70
+ const cols: any = [];
71
+ const colsData = await table.getColumns(req);
72
+ if (colsData && Object.keys(colsData).length > 0) {
73
+ for (let i in colsData) {
74
+ cols.push({ ...colsData[i], key: i });
75
+ }
76
+ }
77
+
78
+ const entries = await table.getTotalData(req);
79
+ const exportData = await exp.generate(entries, cols, req);
80
+ const base64 = Buffer.from("\uFEFF" + exportData).toString("base64");
81
+
82
+ return res.send({
83
+ status: "success",
84
+ data: {
85
+ filename: exp.filename ? (typeof exp.filename === "function" ? await exp.filename(req) : exp.filename) : "Export",
86
+ base64: (exp.contentType ? exp.contentType + ";base64," : "") + base64,
87
+ },
88
+ });
89
+ }
90
+ }
@@ -0,0 +1,22 @@
1
+ class DatatableRegistryWrapper {
2
+ tables: any = {};
3
+
4
+ register(key: string, tableClass: any) {
5
+ this.tables[key] = tableClass;
6
+ return this;
7
+ }
8
+
9
+ registerMany(data: any) {
10
+ if (typeof data === "object") {
11
+ for (let key in data) {
12
+ this.register(key, data[key]);
13
+ }
14
+ }
15
+ return this;
16
+ }
17
+
18
+ getTable(key: string) {
19
+ return this.tables[key] ? this.tables[key] : null;
20
+ }
21
+ }
22
+ export const DatatableRegistry = new DatatableRegistryWrapper();
@@ -1,4 +1,5 @@
1
1
  import { Config } from "./Config";
2
+ import { DatatableRegistry } from "./Datatable/DatatableRegistry";
2
3
  import { QueueJobsRegisty } from "./Queue/QueueJobsRegisty";
3
4
  import { Router } from "./Router/Router";
4
5
 
@@ -21,6 +22,10 @@ export class LambdaApplication {
21
22
  tempDir: options && options.tempDir ? options.tempDir : null,
22
23
  });
23
24
 
25
+ if (options && options.datatables) {
26
+ DatatableRegistry.registerMany(options.datatables);
27
+ }
28
+
24
29
  if (options.jobs && options.jobs.length > 0) {
25
30
  QueueJobsRegisty.registerJob(options.jobs);
26
31
  }
@@ -2,6 +2,7 @@ import http, { Server } from "http";
2
2
  import { Config } from "./Config";
3
3
  import { Router } from "./Router/Router";
4
4
  import { QueueJobsRegisty } from "./Queue/QueueJobsRegisty";
5
+ import { DatatableRegistry } from "./Datatable/DatatableRegistry";
5
6
 
6
7
  export class WebApplication {
7
8
  private server!: Server;
@@ -24,6 +25,10 @@ export class WebApplication {
24
25
  tempDir: options && options.tempDir ? options.tempDir : null,
25
26
  });
26
27
 
28
+ if (options && options.datatables) {
29
+ DatatableRegistry.registerMany(options.datatables);
30
+ }
31
+
27
32
  if (options.jobs && options.jobs.length > 0) {
28
33
  QueueJobsRegisty.registerJob(options.jobs);
29
34
  }
package/src/index.ts CHANGED
@@ -22,6 +22,9 @@ export * from "./Facades/StorageDrivers/BaseStorageDriver";
22
22
  export * from "./Facades/Storage";
23
23
  export * from "./DataBuilder/DataBuilder";
24
24
  export * from "./DataBuilder/DataBuilderController";
25
+ export * from "./Datatable/Datatable";
26
+ export * from "./Datatable/DatatableRegistry";
27
+ export * from "./Datatable/DatatableController";
25
28
  export * from "./Queue/BaseQueueJob";
26
29
  export * from "./Queue/QueueJob";
27
30
  export * from "./Queue/QueueJobsRegisty";