express-ext 0.1.19 → 0.1.22
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/lib/GenericController.js +94 -19
- package/lib/LoadSearchController.js +9 -0
- package/lib/client.js +69 -0
- package/lib/index.js +74 -1
- package/lib/search.js +31 -19
- package/package.json +1 -1
- package/src/GenericController.ts +96 -24
- package/src/LoadController.ts +4 -3
- package/src/LoadSearchController.ts +17 -1
- package/src/client.ts +69 -0
- package/src/index.ts +28 -5
- package/src/search.ts +41 -28
package/lib/GenericController.js
CHANGED
|
@@ -20,10 +20,11 @@ var resources_1 = require("./resources");
|
|
|
20
20
|
var view_1 = require("./view");
|
|
21
21
|
var GenericController = (function (_super) {
|
|
22
22
|
__extends(GenericController, _super);
|
|
23
|
-
function GenericController(log, service, status, validate) {
|
|
23
|
+
function GenericController(log, service, status, validate, build) {
|
|
24
24
|
var _this = _super.call(this, log, service) || this;
|
|
25
25
|
_this.service = service;
|
|
26
26
|
_this.validate = validate;
|
|
27
|
+
_this.build = build;
|
|
27
28
|
_this.status = edit_1.initializeStatus(status);
|
|
28
29
|
if (service.metadata) {
|
|
29
30
|
var m = service.metadata();
|
|
@@ -46,18 +47,18 @@ var GenericController = (function (_super) {
|
|
|
46
47
|
return this.insert(req, res);
|
|
47
48
|
};
|
|
48
49
|
GenericController.prototype.insert = function (req, res) {
|
|
49
|
-
validateAndCreate(req, res, this.status, this.service.insert, this.log, this.validate);
|
|
50
|
+
validateAndCreate(req, res, this.status, this.service.insert, this.log, this.validate, this.build);
|
|
50
51
|
};
|
|
51
52
|
GenericController.prototype.update = function (req, res) {
|
|
52
53
|
var id = buildAndCheckIdWithBody(req, res, this.keys, this.service.update);
|
|
53
54
|
if (id) {
|
|
54
|
-
validateAndUpdate(res, this.status, req.body, false, this.service.update, this.log, this.validate);
|
|
55
|
+
validateAndUpdate(res, this.status, req.body, false, this.service.update, this.log, this.validate, this.build);
|
|
55
56
|
}
|
|
56
57
|
};
|
|
57
58
|
GenericController.prototype.patch = function (req, res) {
|
|
58
59
|
var id = buildAndCheckIdWithBody(req, res, this.keys, this.service.patch);
|
|
59
60
|
if (id && this.service.patch) {
|
|
60
|
-
validateAndUpdate(res, this.status, req.body, true, this.service.patch, this.log, this.validate);
|
|
61
|
+
validateAndUpdate(res, this.status, req.body, true, this.service.patch, this.log, this.validate, this.build);
|
|
61
62
|
}
|
|
62
63
|
};
|
|
63
64
|
GenericController.prototype.delete = function (req, res) {
|
|
@@ -77,28 +78,33 @@ var GenericController = (function (_super) {
|
|
|
77
78
|
return GenericController;
|
|
78
79
|
}(LoadController_1.LoadController));
|
|
79
80
|
exports.GenericController = GenericController;
|
|
80
|
-
function validateAndCreate(req, res, status, save, log, validate) {
|
|
81
|
+
function validateAndCreate(req, res, status, save, log, validate, build) {
|
|
81
82
|
var obj = req.body;
|
|
82
83
|
if (!obj || obj === '') {
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
if (validate) {
|
|
86
|
-
validate(obj).then(function (errors) {
|
|
87
|
-
if (errors && errors.length > 0) {
|
|
88
|
-
var r = { status: status.validation_error, errors: errors };
|
|
89
|
-
res.status(getStatusCode(errors)).json(r).end();
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
edit_1.create(res, status, obj, save, log);
|
|
93
|
-
}
|
|
94
|
-
}).catch(function (err) { return http_1.handleError(err, res, log); });
|
|
84
|
+
res.status(400).end('The request body cannot be empty.');
|
|
95
85
|
}
|
|
96
86
|
else {
|
|
97
|
-
|
|
87
|
+
if (validate) {
|
|
88
|
+
validate(obj).then(function (errors) {
|
|
89
|
+
if (errors && errors.length > 0) {
|
|
90
|
+
var r = { status: status.validation_error, errors: errors };
|
|
91
|
+
res.status(getStatusCode(errors)).json(r).end();
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
if (build) {
|
|
95
|
+
build(res, obj, true);
|
|
96
|
+
}
|
|
97
|
+
edit_1.create(res, status, obj, save, log);
|
|
98
|
+
}
|
|
99
|
+
}).catch(function (err) { return http_1.handleError(err, res, log); });
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
edit_1.create(res, status, obj, save, log);
|
|
103
|
+
}
|
|
98
104
|
}
|
|
99
105
|
}
|
|
100
106
|
exports.validateAndCreate = validateAndCreate;
|
|
101
|
-
function validateAndUpdate(res, status, obj, isPatch, save, log, validate) {
|
|
107
|
+
function validateAndUpdate(res, status, obj, isPatch, save, log, validate, build) {
|
|
102
108
|
if (validate) {
|
|
103
109
|
validate(obj, isPatch).then(function (errors) {
|
|
104
110
|
if (errors && errors.length > 0) {
|
|
@@ -106,6 +112,9 @@ function validateAndUpdate(res, status, obj, isPatch, save, log, validate) {
|
|
|
106
112
|
res.status(getStatusCode(errors)).json(r).end();
|
|
107
113
|
}
|
|
108
114
|
else {
|
|
115
|
+
if (build) {
|
|
116
|
+
build(res, obj, false, isPatch);
|
|
117
|
+
}
|
|
109
118
|
edit_1.update(res, status, obj, save, log);
|
|
110
119
|
}
|
|
111
120
|
}).catch(function (err) { return http_1.handleError(err, res, log); });
|
|
@@ -154,3 +163,69 @@ function getStatusCode(errs) {
|
|
|
154
163
|
return (edit_1.isTypeError(errs) ? 400 : 422);
|
|
155
164
|
}
|
|
156
165
|
exports.getStatusCode = getStatusCode;
|
|
166
|
+
function useBuild(c, generate) {
|
|
167
|
+
var b = new Builder(generate, c.id ? c.id : '', c.payload ? c.payload : '', c.user ? c.user : '', c.updatedBy ? c.updatedBy : '', c.updatedAt ? c.updatedAt : '', c.createdBy ? c.createdBy : '', c.createdAt ? c.createdAt : '');
|
|
168
|
+
return b.build;
|
|
169
|
+
}
|
|
170
|
+
exports.useBuild = useBuild;
|
|
171
|
+
var Builder = (function () {
|
|
172
|
+
function Builder(generate, id, payload, user, updatedBy, updatedAt, createdBy, createdAt) {
|
|
173
|
+
this.generate = generate;
|
|
174
|
+
this.id = id;
|
|
175
|
+
this.payload = payload;
|
|
176
|
+
this.user = user;
|
|
177
|
+
this.updatedBy = updatedBy;
|
|
178
|
+
this.updatedAt = updatedAt;
|
|
179
|
+
this.createdBy = createdBy;
|
|
180
|
+
this.createdAt = createdAt;
|
|
181
|
+
this.build = this.build.bind(this);
|
|
182
|
+
}
|
|
183
|
+
Builder.prototype.build = function (res, obj, isCreate, isPatch) {
|
|
184
|
+
var o = obj;
|
|
185
|
+
var usr = '';
|
|
186
|
+
if (this.user.length > 0) {
|
|
187
|
+
if (this.payload.length > 0) {
|
|
188
|
+
var payload = res.locals[this.payload];
|
|
189
|
+
if (payload) {
|
|
190
|
+
usr = payload[this.user];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
usr = res.locals[this.user];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!usr) {
|
|
198
|
+
usr = '';
|
|
199
|
+
}
|
|
200
|
+
var now = new Date();
|
|
201
|
+
if (isCreate) {
|
|
202
|
+
if (this.generate && this.id.length > 0) {
|
|
203
|
+
o[this.id] = this.generate();
|
|
204
|
+
}
|
|
205
|
+
if (usr.length > 0) {
|
|
206
|
+
if (this.createdAt.length > 0) {
|
|
207
|
+
o[this.createdAt] = now;
|
|
208
|
+
}
|
|
209
|
+
if (this.createdBy.length > 0) {
|
|
210
|
+
o[this.createdBy] = usr;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else if (isPatch) {
|
|
215
|
+
var keys = Object.keys(o);
|
|
216
|
+
if (keys.length === 0) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (usr.length > 0) {
|
|
221
|
+
if (this.updatedAt.length > 0) {
|
|
222
|
+
o[this.updatedAt] = now;
|
|
223
|
+
}
|
|
224
|
+
if (this.updatedBy.length > 0) {
|
|
225
|
+
o[this.updatedBy] = usr;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
return Builder;
|
|
230
|
+
}());
|
|
231
|
+
exports.Builder = Builder;
|
|
@@ -17,6 +17,15 @@ var http_1 = require("./http");
|
|
|
17
17
|
var LoadController_1 = require("./LoadController");
|
|
18
18
|
var search_1 = require("./search");
|
|
19
19
|
var search_func_1 = require("./search_func");
|
|
20
|
+
function useSearchController(log, find, viewService, array, dates, numbers, keys, config) {
|
|
21
|
+
var c = new LoadSearchController(log, find, viewService, keys, config, dates, numbers);
|
|
22
|
+
c.array = array;
|
|
23
|
+
return c;
|
|
24
|
+
}
|
|
25
|
+
exports.useSearchController = useSearchController;
|
|
26
|
+
exports.useSearchHandler = useSearchController;
|
|
27
|
+
exports.createSearchController = useSearchController;
|
|
28
|
+
exports.createSearchHandler = useSearchController;
|
|
20
29
|
var LoadSearchController = (function (_super) {
|
|
21
30
|
__extends(LoadSearchController, _super);
|
|
22
31
|
function LoadSearchController(log, find, viewService, keys, config, dates, numbers) {
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var http = require("http");
|
|
4
|
+
var https = require("https");
|
|
5
|
+
function getHealthSecure(url, timeout) {
|
|
6
|
+
return new Promise(function (resolve) {
|
|
7
|
+
https.get(url, { rejectUnauthorized: false }, function (res) {
|
|
8
|
+
var data = '';
|
|
9
|
+
res.on('data', function (d) {
|
|
10
|
+
data += d;
|
|
11
|
+
});
|
|
12
|
+
res.on('end', function () {
|
|
13
|
+
resolve({ statusCode: res.statusCode, data: data, statusMessage: res.statusMessage });
|
|
14
|
+
});
|
|
15
|
+
}).on('error', function (e) {
|
|
16
|
+
return { statusCode: 500, statusMessage: e };
|
|
17
|
+
});
|
|
18
|
+
setTimeout(function () { return resolve({ statusCode: 408, statusMessage: 'Time out' }); }, timeout);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function getHealth(url, timeout) {
|
|
22
|
+
return new Promise(function (resolve) {
|
|
23
|
+
http.get(url, function (res) {
|
|
24
|
+
var data = '';
|
|
25
|
+
res.on('data', function (d) {
|
|
26
|
+
data += d;
|
|
27
|
+
});
|
|
28
|
+
res.on('end', function () {
|
|
29
|
+
resolve({ statusCode: res.statusCode, data: data, statusMessage: res.statusMessage });
|
|
30
|
+
});
|
|
31
|
+
}).on('error', function (e) {
|
|
32
|
+
return { statusCode: 500, statusMessage: e };
|
|
33
|
+
});
|
|
34
|
+
setTimeout(function () { return resolve({ statusCode: 408, statusMessage: 'Time out' }); }, timeout);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
var ClientChecker = (function () {
|
|
38
|
+
function ClientChecker(service, url, timeout) {
|
|
39
|
+
this.service = service;
|
|
40
|
+
this.url = url;
|
|
41
|
+
this.timeout = (timeout ? timeout : 4200);
|
|
42
|
+
this.check = this.check.bind(this);
|
|
43
|
+
this.name = this.name.bind(this);
|
|
44
|
+
this.build = this.build.bind(this);
|
|
45
|
+
}
|
|
46
|
+
ClientChecker.prototype.check = function () {
|
|
47
|
+
var obj = {};
|
|
48
|
+
if (this.url.startsWith('https://')) {
|
|
49
|
+
return getHealthSecure(this.url, this.timeout).then(function (r) { return obj = r; });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
return getHealth(this.url, this.timeout).then(function (r) { return obj = r; });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
ClientChecker.prototype.name = function () {
|
|
56
|
+
return this.service;
|
|
57
|
+
};
|
|
58
|
+
ClientChecker.prototype.build = function (data, err) {
|
|
59
|
+
if (err) {
|
|
60
|
+
if (!data) {
|
|
61
|
+
data = {};
|
|
62
|
+
}
|
|
63
|
+
data['error'] = err;
|
|
64
|
+
}
|
|
65
|
+
return data;
|
|
66
|
+
};
|
|
67
|
+
return ClientChecker;
|
|
68
|
+
}());
|
|
69
|
+
exports.ClientChecker = ClientChecker;
|
package/lib/index.js
CHANGED
|
@@ -1 +1,74 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";
|
|
2
|
+
function __export(m) {
|
|
3
|
+
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
|
|
4
|
+
}
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
var GenericController_1 = require("./GenericController");
|
|
7
|
+
exports.GenericHandler = GenericController_1.GenericController;
|
|
8
|
+
var GenericSearchController_1 = require("./GenericSearchController");
|
|
9
|
+
exports.GenericSearchHandler = GenericSearchController_1.GenericSearchController;
|
|
10
|
+
var HealthController_1 = require("./HealthController");
|
|
11
|
+
exports.HealthHandler = HealthController_1.HealthController;
|
|
12
|
+
var LoadController_1 = require("./LoadController");
|
|
13
|
+
exports.LoadHandler = LoadController_1.LoadController;
|
|
14
|
+
exports.ViewHandler = LoadController_1.LoadController;
|
|
15
|
+
exports.ViewController = LoadController_1.LoadController;
|
|
16
|
+
var LoadSearchController_1 = require("./LoadSearchController");
|
|
17
|
+
exports.LoadSearchHandler = LoadSearchController_1.LoadSearchController;
|
|
18
|
+
var LogController_1 = require("./LogController");
|
|
19
|
+
exports.LogHandler = LogController_1.LogController;
|
|
20
|
+
var LowCodeController_1 = require("./LowCodeController");
|
|
21
|
+
exports.LowCodeHandler = LowCodeController_1.Controller;
|
|
22
|
+
exports.LowCodeController = LowCodeController_1.Controller;
|
|
23
|
+
var SearchController_1 = require("./SearchController");
|
|
24
|
+
exports.SearchHandler = SearchController_1.SearchController;
|
|
25
|
+
__export(require("./health"));
|
|
26
|
+
__export(require("./client"));
|
|
27
|
+
__export(require("./HealthController"));
|
|
28
|
+
__export(require("./LogController"));
|
|
29
|
+
__export(require("./log"));
|
|
30
|
+
__export(require("./http"));
|
|
31
|
+
__export(require("./view"));
|
|
32
|
+
__export(require("./LoadController"));
|
|
33
|
+
__export(require("./search_func"));
|
|
34
|
+
__export(require("./search"));
|
|
35
|
+
__export(require("./SearchController"));
|
|
36
|
+
__export(require("./LoadSearchController"));
|
|
37
|
+
__export(require("./resources"));
|
|
38
|
+
__export(require("./edit"));
|
|
39
|
+
__export(require("./GenericController"));
|
|
40
|
+
__export(require("./GenericSearchController"));
|
|
41
|
+
__export(require("./LowCodeController"));
|
|
42
|
+
function allow(access) {
|
|
43
|
+
var ao = access.origin;
|
|
44
|
+
if (typeof ao === 'string') {
|
|
45
|
+
return function (req, res, next) {
|
|
46
|
+
res.header('Access-Control-Allow-Origin', access.origin);
|
|
47
|
+
res.header('Access-Control-Allow-Credentials', access.credentials);
|
|
48
|
+
res.header('Access-Control-Allow-Methods', access.methods);
|
|
49
|
+
res.setHeader('Access-Control-Allow-Headers', access.headers);
|
|
50
|
+
next();
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
else if (Array.isArray(ao) && ao.length > 0) {
|
|
54
|
+
return function (req, res, next) {
|
|
55
|
+
var origin = req.headers.origin;
|
|
56
|
+
if (origin) {
|
|
57
|
+
if (ao.includes(origin)) {
|
|
58
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
res.header('Access-Control-Allow-Credentials', access.credentials);
|
|
62
|
+
res.header('Access-Control-Allow-Methods', access.methods);
|
|
63
|
+
res.setHeader('Access-Control-Allow-Headers', access.headers);
|
|
64
|
+
next();
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return function (req, res, next) {
|
|
68
|
+
res.header('Access-Control-Allow-Credentials', access.credentials);
|
|
69
|
+
res.header('Access-Control-Allow-Methods', access.methods);
|
|
70
|
+
res.setHeader('Access-Control-Allow-Headers', access.headers);
|
|
71
|
+
next();
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
exports.allow = allow;
|
package/lib/search.js
CHANGED
|
@@ -139,28 +139,40 @@ function inArray(s, arr) {
|
|
|
139
139
|
return false;
|
|
140
140
|
}
|
|
141
141
|
exports.inArray = inArray;
|
|
142
|
-
function setValue(
|
|
143
|
-
var
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
function setValue(o, key, value) {
|
|
143
|
+
var obj = o;
|
|
144
|
+
var replaceKey = key.replace(/\[/g, '.[').replace(/\.\./g, '.');
|
|
145
|
+
if (replaceKey.indexOf('.') === 0) {
|
|
146
|
+
replaceKey = replaceKey.slice(1, replaceKey.length);
|
|
147
|
+
}
|
|
148
|
+
var keys = replaceKey.split('.');
|
|
149
|
+
var firstKey = keys.shift();
|
|
150
|
+
if (!firstKey) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
var isArrayKey = /\[([0-9]+)\]/.test(firstKey);
|
|
154
|
+
if (keys.length > 0) {
|
|
155
|
+
var firstKeyValue = obj[firstKey] || {};
|
|
156
|
+
var returnValue = setValue(firstKeyValue, keys.join('.'), value);
|
|
157
|
+
return setKey(obj, isArrayKey, firstKey, returnValue);
|
|
158
|
+
}
|
|
159
|
+
return setKey(obj, isArrayKey, firstKey, value);
|
|
160
|
+
}
|
|
161
|
+
exports.setValue = setValue;
|
|
162
|
+
var setKey = function (_object, _isArrayKey, _key, _nextValue) {
|
|
163
|
+
if (_isArrayKey) {
|
|
164
|
+
if (_object.length > _key) {
|
|
165
|
+
_object[_key] = _nextValue;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
_object.push(_nextValue);
|
|
169
|
+
}
|
|
146
170
|
}
|
|
147
171
|
else {
|
|
148
|
-
|
|
149
|
-
var l = paths.length - 1;
|
|
150
|
-
for (var i = 0; i < l - 1; i++) {
|
|
151
|
-
var p = paths[i];
|
|
152
|
-
if (p in o) {
|
|
153
|
-
o = o[p];
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
o[p] = {};
|
|
157
|
-
o = o[p];
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
o[paths[paths.length - 1]] = value;
|
|
172
|
+
_object[_key] = _nextValue;
|
|
161
173
|
}
|
|
162
|
-
|
|
163
|
-
|
|
174
|
+
return _object;
|
|
175
|
+
};
|
|
164
176
|
function getParameters(obj, config) {
|
|
165
177
|
var o = obj;
|
|
166
178
|
if (!config) {
|
package/package.json
CHANGED
package/src/GenericController.ts
CHANGED
|
@@ -6,18 +6,21 @@ import {Attribute, Attributes, ErrorMessage} from './metadata';
|
|
|
6
6
|
import {resources} from './resources';
|
|
7
7
|
import {buildAndCheckId, buildId} from './view';
|
|
8
8
|
|
|
9
|
+
export type Build<T> = (res: Response, obj: T, isCreate?: boolean, isPatch?: boolean) => void;
|
|
10
|
+
export type Validate<T> = (obj: T, patch?: boolean) => Promise<ErrorMessage[]>;
|
|
11
|
+
export type Save<T> = (obj: T, ctx?: any) => Promise<number|ResultInfo<T>>;
|
|
9
12
|
export interface GenericService<T, ID, R> {
|
|
10
13
|
metadata?(): Attributes|undefined;
|
|
11
14
|
load(id: ID, ctx?: any): Promise<T|null>;
|
|
12
15
|
insert(obj: T, ctx?: any): Promise<R>;
|
|
13
16
|
update(obj: T, ctx?: any): Promise<R>;
|
|
14
|
-
patch?(obj: T
|
|
17
|
+
patch?(obj: Partial<T>, ctx?: any): Promise<R>;
|
|
15
18
|
delete?(id: ID, ctx?: any): Promise<number>;
|
|
16
19
|
}
|
|
17
20
|
export class GenericController<T, ID> extends LoadController<T, ID> {
|
|
18
21
|
status: StatusConfig;
|
|
19
22
|
metadata?: Attributes;
|
|
20
|
-
constructor(log: Log, public service: GenericService<T, ID, number|ResultInfo<T>>, status?: StatusConfig, public validate?:
|
|
23
|
+
constructor(log: Log, public service: GenericService<T, ID, number|ResultInfo<T>>, status?: StatusConfig, public validate?: Validate<T>, public build?: Build<T>) {
|
|
21
24
|
super(log, service);
|
|
22
25
|
this.status = initializeStatus(status);
|
|
23
26
|
if (service.metadata) {
|
|
@@ -36,25 +39,25 @@ export class GenericController<T, ID> extends LoadController<T, ID> {
|
|
|
36
39
|
this.validate = v.validate;
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
|
-
create(req: Request, res: Response) {
|
|
42
|
+
create(req: Request, res: Response): void {
|
|
40
43
|
return this.insert(req, res);
|
|
41
44
|
}
|
|
42
|
-
insert(req: Request, res: Response) {
|
|
43
|
-
validateAndCreate(req, res, this.status, this.service.insert, this.log, this.validate);
|
|
45
|
+
insert(req: Request, res: Response): void {
|
|
46
|
+
validateAndCreate(req, res, this.status, this.service.insert, this.log, this.validate, this.build);
|
|
44
47
|
}
|
|
45
|
-
update(req: Request, res: Response) {
|
|
48
|
+
update(req: Request, res: Response): void {
|
|
46
49
|
const id = buildAndCheckIdWithBody<T, ID, any>(req, res, this.keys, this.service.update);
|
|
47
50
|
if (id) {
|
|
48
|
-
validateAndUpdate(res, this.status, req.body, false, this.service.update, this.log, this.validate);
|
|
51
|
+
validateAndUpdate(res, this.status, req.body, false, this.service.update, this.log, this.validate, this.build);
|
|
49
52
|
}
|
|
50
53
|
}
|
|
51
|
-
patch(req: Request, res: Response) {
|
|
54
|
+
patch(req: Request, res: Response): void {
|
|
52
55
|
const id = buildAndCheckIdWithBody<T, ID, any>(req, res, this.keys, this.service.patch);
|
|
53
56
|
if (id && this.service.patch) {
|
|
54
|
-
validateAndUpdate(res, this.status, req.body, true, this.service.patch, this.log, this.validate);
|
|
57
|
+
validateAndUpdate(res, this.status, req.body, true, this.service.patch, this.log, this.validate, this.build);
|
|
55
58
|
}
|
|
56
59
|
}
|
|
57
|
-
delete(req: Request, res: Response) {
|
|
60
|
+
delete(req: Request, res: Response): void {
|
|
58
61
|
const id = buildAndCheckId<ID>(req, res, this.keys);
|
|
59
62
|
if (id) {
|
|
60
63
|
if (!this.service.delete) {
|
|
@@ -67,31 +70,38 @@ export class GenericController<T, ID> extends LoadController<T, ID> {
|
|
|
67
70
|
}
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
|
-
export function validateAndCreate<T>(req: Request, res: Response, status: StatusConfig, save:
|
|
73
|
+
export function validateAndCreate<T>(req: Request, res: Response, status: StatusConfig, save: Save<T>, log: Log, validate?: Validate<T>, build?: Build<T>): void {
|
|
71
74
|
const obj = req.body;
|
|
72
75
|
if (!obj || obj === '') {
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
if (validate) {
|
|
76
|
-
validate(obj).then(errors => {
|
|
77
|
-
if (errors && errors.length > 0) {
|
|
78
|
-
const r: ResultInfo<T> = {status: status.validation_error, errors};
|
|
79
|
-
res.status(getStatusCode(errors)).json(r).end();
|
|
80
|
-
} else {
|
|
81
|
-
create(res, status, obj, save, log);
|
|
82
|
-
}
|
|
83
|
-
}).catch(err => handleError(err, res, log));
|
|
76
|
+
res.status(400).end('The request body cannot be empty.');
|
|
84
77
|
} else {
|
|
85
|
-
|
|
78
|
+
if (validate) {
|
|
79
|
+
validate(obj).then(errors => {
|
|
80
|
+
if (errors && errors.length > 0) {
|
|
81
|
+
const r: ResultInfo<T> = {status: status.validation_error, errors};
|
|
82
|
+
res.status(getStatusCode(errors)).json(r).end();
|
|
83
|
+
} else {
|
|
84
|
+
if (build) {
|
|
85
|
+
build(res, obj, true);
|
|
86
|
+
}
|
|
87
|
+
create(res, status, obj, save, log);
|
|
88
|
+
}
|
|
89
|
+
}).catch(err => handleError(err, res, log));
|
|
90
|
+
} else {
|
|
91
|
+
create(res, status, obj, save, log);
|
|
92
|
+
}
|
|
86
93
|
}
|
|
87
94
|
}
|
|
88
|
-
export function validateAndUpdate<T>(res: Response, status: StatusConfig, obj: T, isPatch: boolean, save:
|
|
95
|
+
export function validateAndUpdate<T>(res: Response, status: StatusConfig, obj: T, isPatch: boolean, save: Save<T>, log: Log, validate?: Validate<T>, build?: Build<T>): void {
|
|
89
96
|
if (validate) {
|
|
90
97
|
validate(obj, isPatch).then(errors => {
|
|
91
98
|
if (errors && errors.length > 0) {
|
|
92
99
|
const r: ResultInfo<T> = {status: status.validation_error, errors};
|
|
93
100
|
res.status(getStatusCode(errors)).json(r).end();
|
|
94
101
|
} else {
|
|
102
|
+
if (build) {
|
|
103
|
+
build(res, obj, false, isPatch);
|
|
104
|
+
}
|
|
95
105
|
update(res, status, obj, save, log);
|
|
96
106
|
}
|
|
97
107
|
}).catch(err => handleError(err, res, log));
|
|
@@ -133,3 +143,65 @@ export function getDeleteStatus(count: number): number {
|
|
|
133
143
|
export function getStatusCode(errs: ErrorMessage[]): number {
|
|
134
144
|
return (isTypeError(errs) ? 400 : 422);
|
|
135
145
|
}
|
|
146
|
+
export interface ModelConfig {
|
|
147
|
+
id?: string;
|
|
148
|
+
payload?: string;
|
|
149
|
+
user?: string;
|
|
150
|
+
updatedBy?: string;
|
|
151
|
+
updatedAt?: string;
|
|
152
|
+
createdBy?: string;
|
|
153
|
+
createdAt?: string;
|
|
154
|
+
}
|
|
155
|
+
export function useBuild<T>(c: ModelConfig, generate?: (() => string)): Build<T> {
|
|
156
|
+
const b = new Builder<T>(generate, c.id ? c.id : '', c.payload ? c.payload : '', c.user ? c.user : '', c.updatedBy ? c.updatedBy : '', c.updatedAt ? c.updatedAt : '', c.createdBy ? c.createdBy : '', c.createdAt ? c.createdAt : '');
|
|
157
|
+
return b.build;
|
|
158
|
+
}
|
|
159
|
+
export class Builder<T> {
|
|
160
|
+
constructor(public generate: (() => string)|undefined, public id: string, public payload: string, public user: string, public updatedBy: string, public updatedAt: string, public createdBy: string, public createdAt: string) {
|
|
161
|
+
this.build = this.build.bind(this);
|
|
162
|
+
}
|
|
163
|
+
build(res: Response, obj: T, isCreate?: boolean, isPatch?: boolean): void {
|
|
164
|
+
let o: any = obj;
|
|
165
|
+
let usr = '';
|
|
166
|
+
if (this.user.length > 0) {
|
|
167
|
+
if (this.payload.length > 0) {
|
|
168
|
+
const payload = res.locals[this.payload];
|
|
169
|
+
if (payload) {
|
|
170
|
+
usr = payload[this.user];
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
usr = res.locals[this.user];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (!usr) {
|
|
177
|
+
usr = '';
|
|
178
|
+
}
|
|
179
|
+
const now = new Date();
|
|
180
|
+
if (isCreate) {
|
|
181
|
+
if (this.generate && this.id.length > 0) {
|
|
182
|
+
o[this.id] = this.generate();
|
|
183
|
+
}
|
|
184
|
+
if (usr.length > 0) {
|
|
185
|
+
if (this.createdAt.length > 0) {
|
|
186
|
+
o[this.createdAt] = now;
|
|
187
|
+
}
|
|
188
|
+
if (this.createdBy.length > 0) {
|
|
189
|
+
o[this.createdBy] = usr;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
} else if (isPatch) {
|
|
193
|
+
const keys = Object.keys(o);
|
|
194
|
+
if (keys.length === 0) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (usr.length > 0) {
|
|
199
|
+
if (this.updatedAt.length > 0) {
|
|
200
|
+
o[this.updatedAt] = now;
|
|
201
|
+
}
|
|
202
|
+
if (this.updatedBy.length > 0) {
|
|
203
|
+
o[this.updatedBy] = usr;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
package/src/LoadController.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface ViewService<T, ID> {
|
|
|
7
7
|
metadata?(): Attributes|undefined;
|
|
8
8
|
load(id: ID, ctx?: any): Promise<T|null>;
|
|
9
9
|
}
|
|
10
|
+
export type Load<T, ID> = ((id: ID, ctx?: any) => Promise<T|null>);
|
|
10
11
|
function getViewFunc<T, ID>(viewService: ViewService<T, ID> | ((id: ID, ctx?: any) => Promise<T|null>)): (id: ID, ctx?: any) => Promise<T|null> {
|
|
11
12
|
if (typeof viewService === 'function') {
|
|
12
13
|
return viewService;
|
|
@@ -38,13 +39,13 @@ function getKeysFunc<T, ID>(viewService: ViewService<T, ID> | ((id: ID, ctx?: an
|
|
|
38
39
|
}
|
|
39
40
|
export class LoadController<T, ID> {
|
|
40
41
|
protected keys?: Attribute[];
|
|
41
|
-
protected view:
|
|
42
|
-
constructor(protected log: Log, viewService: ViewService<T, ID> |
|
|
42
|
+
protected view: Load<T, ID>;
|
|
43
|
+
constructor(protected log: Log, viewService: ViewService<T, ID> | Load<T, ID>, keys?: Attributes|Attribute[]|string[]) {
|
|
43
44
|
this.load = this.load.bind(this);
|
|
44
45
|
this.view = getViewFunc(viewService);
|
|
45
46
|
this.keys = getKeysFunc(viewService, keys);
|
|
46
47
|
}
|
|
47
|
-
load(req: Request, res: Response) {
|
|
48
|
+
load(req: Request, res: Response): void {
|
|
48
49
|
const id = buildAndCheckId<ID>(req, res, this.keys);
|
|
49
50
|
if (id) {
|
|
50
51
|
this.view(id)
|
|
@@ -5,6 +5,22 @@ import {Attribute, Attributes} from './metadata';
|
|
|
5
5
|
import {buildArray, Filter, format, fromRequest, getParameters, initializeConfig, jsonResult, SearchConfig, SearchResult} from './search';
|
|
6
6
|
import {getMetadataFunc} from './search_func';
|
|
7
7
|
|
|
8
|
+
export interface Search {
|
|
9
|
+
search(req: Request, res: Response): void;
|
|
10
|
+
load(req: Request, res: Response): void;
|
|
11
|
+
}
|
|
12
|
+
export interface SearchManager {
|
|
13
|
+
search(req: Request, res: Response): void;
|
|
14
|
+
load(req: Request, res: Response): void;
|
|
15
|
+
}
|
|
16
|
+
export function useSearchController<T, ID, S extends Filter>(log: Log, find: (s: S, limit?: number, skip?: number|string, fields?: string[]) => Promise<SearchResult<T>>, viewService: ViewService<T, ID> | ((id: ID, ctx?: any) => Promise<T>), array?: string[], dates?: string[], numbers?: string[], keys?: Attributes|Attribute[]|string[], config?: SearchConfig|boolean): Search {
|
|
17
|
+
const c = new LoadSearchController(log, find, viewService, keys, config, dates, numbers);
|
|
18
|
+
c.array = array;
|
|
19
|
+
return c;
|
|
20
|
+
}
|
|
21
|
+
export const useSearchHandler = useSearchController;
|
|
22
|
+
export const createSearchController = useSearchController;
|
|
23
|
+
export const createSearchHandler = useSearchController;
|
|
8
24
|
export class LoadSearchController<T, ID, S extends Filter> extends LoadController<T, ID> {
|
|
9
25
|
config?: SearchConfig;
|
|
10
26
|
csv?: boolean;
|
|
@@ -37,7 +53,7 @@ export class LoadSearchController<T, ID, S extends Filter> extends LoadControlle
|
|
|
37
53
|
this.numbers = m.numbers;
|
|
38
54
|
}
|
|
39
55
|
}
|
|
40
|
-
search(req: Request, res: Response) {
|
|
56
|
+
search(req: Request, res: Response): void {
|
|
41
57
|
const s = fromRequest<S>(req, buildArray(this.array, this.fields, this.excluding));
|
|
42
58
|
const l = getParameters(s, this.config);
|
|
43
59
|
const s2 = format(s, this.dates, this.numbers);
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as https from 'https';
|
|
3
|
+
import { AnyMap, HealthChecker } from './health';
|
|
4
|
+
|
|
5
|
+
function getHealthSecure(url: string, timeout: number): Promise<AnyMap> {
|
|
6
|
+
return new Promise((resolve) => {
|
|
7
|
+
https.get(url, { rejectUnauthorized: false }, (res: any) => {
|
|
8
|
+
let data = '';
|
|
9
|
+
res.on('data', (d: any) => {
|
|
10
|
+
data += d;
|
|
11
|
+
});
|
|
12
|
+
res.on('end', () => {
|
|
13
|
+
resolve({ statusCode: res.statusCode, data, statusMessage: res.statusMessage });
|
|
14
|
+
});
|
|
15
|
+
}).on('error', (e: any) => {
|
|
16
|
+
return { statusCode: 500, statusMessage: e };
|
|
17
|
+
});
|
|
18
|
+
setTimeout(() => resolve({ statusCode: 408, statusMessage: 'Time out' }), timeout);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function getHealth(url: string, timeout: number): Promise<AnyMap> {
|
|
22
|
+
return new Promise((resolve) => {
|
|
23
|
+
http.get(url, (res: any) => {
|
|
24
|
+
let data = '';
|
|
25
|
+
res.on('data', (d: any) => {
|
|
26
|
+
data += d;
|
|
27
|
+
});
|
|
28
|
+
res.on('end', () => {
|
|
29
|
+
resolve({ statusCode: res.statusCode, data, statusMessage: res.statusMessage });
|
|
30
|
+
});
|
|
31
|
+
}).on('error', (e: any) => {
|
|
32
|
+
return { statusCode: 500, statusMessage: e };
|
|
33
|
+
});
|
|
34
|
+
setTimeout(() => resolve({ statusCode: 408, statusMessage: 'Time out' }), timeout);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export class ClientChecker implements HealthChecker {
|
|
38
|
+
timeout: number;
|
|
39
|
+
constructor(private service: string, private url: string, timeout: number) {
|
|
40
|
+
this.timeout = (timeout ? timeout : 4200);
|
|
41
|
+
this.check = this.check.bind(this);
|
|
42
|
+
this.name = this.name.bind(this);
|
|
43
|
+
this.build = this.build.bind(this);
|
|
44
|
+
}
|
|
45
|
+
check(): Promise<AnyMap> {
|
|
46
|
+
let obj = {} as AnyMap;
|
|
47
|
+
if (this.url.startsWith('https://')) {
|
|
48
|
+
return getHealthSecure(this.url, this.timeout).then(
|
|
49
|
+
r => obj = r
|
|
50
|
+
);
|
|
51
|
+
} else {
|
|
52
|
+
return getHealth(this.url, this.timeout).then(
|
|
53
|
+
r => obj = r
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
name(): string {
|
|
58
|
+
return this.service;
|
|
59
|
+
}
|
|
60
|
+
build(data: AnyMap, err: any): AnyMap {
|
|
61
|
+
if (err) {
|
|
62
|
+
if (!data) {
|
|
63
|
+
data = {} as AnyMap;
|
|
64
|
+
}
|
|
65
|
+
data['error'] = err;
|
|
66
|
+
}
|
|
67
|
+
return data;
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ export {Controller as LowCodeController};
|
|
|
25
25
|
export {Service as LowCodeService};
|
|
26
26
|
|
|
27
27
|
export * from './health';
|
|
28
|
+
export * from './client';
|
|
28
29
|
export * from './HealthController';
|
|
29
30
|
export * from './LogController';
|
|
30
31
|
export * from './log';
|
|
@@ -43,15 +44,37 @@ export * from './GenericSearchController';
|
|
|
43
44
|
export * from './LowCodeController';
|
|
44
45
|
|
|
45
46
|
export interface AccessConfig {
|
|
46
|
-
origin
|
|
47
|
-
credentials
|
|
48
|
-
methods
|
|
49
|
-
headers: string
|
|
47
|
+
origin?: string | string[];
|
|
48
|
+
credentials?: string | string[];
|
|
49
|
+
methods?: string | string[];
|
|
50
|
+
headers: number | string | ReadonlyArray<string>;
|
|
50
51
|
}
|
|
51
52
|
export type AccessControlAllowConfig = AccessConfig;
|
|
52
53
|
export function allow(access: AccessConfig): (req: Request, res: Response, next: NextFunction) => void {
|
|
54
|
+
const ao = access.origin;
|
|
55
|
+
if (typeof ao === 'string') {
|
|
56
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
57
|
+
res.header('Access-Control-Allow-Origin', access.origin);
|
|
58
|
+
res.header('Access-Control-Allow-Credentials', access.credentials);
|
|
59
|
+
res.header('Access-Control-Allow-Methods', access.methods);
|
|
60
|
+
res.setHeader('Access-Control-Allow-Headers', access.headers);
|
|
61
|
+
next();
|
|
62
|
+
};
|
|
63
|
+
} else if (Array.isArray(ao) && ao.length > 0) {
|
|
64
|
+
return (req: Request, res: Response, next: NextFunction) => {
|
|
65
|
+
const origin = req.headers.origin;
|
|
66
|
+
if (origin) {
|
|
67
|
+
if (ao.includes(origin)) {
|
|
68
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
res.header('Access-Control-Allow-Credentials', access.credentials);
|
|
72
|
+
res.header('Access-Control-Allow-Methods', access.methods);
|
|
73
|
+
res.setHeader('Access-Control-Allow-Headers', access.headers);
|
|
74
|
+
next();
|
|
75
|
+
};
|
|
76
|
+
}
|
|
53
77
|
return (req: Request, res: Response, next: NextFunction) => {
|
|
54
|
-
res.header('Access-Control-Allow-Origin', access.origin);
|
|
55
78
|
res.header('Access-Control-Allow-Credentials', access.credentials);
|
|
56
79
|
res.header('Access-Control-Allow-Methods', access.methods);
|
|
57
80
|
res.setHeader('Access-Control-Allow-Headers', access.headers);
|
package/src/search.ts
CHANGED
|
@@ -137,17 +137,17 @@ export function fromUrl<S>(req: Request, arr?: string[]): S {
|
|
|
137
137
|
}
|
|
138
138
|
*/
|
|
139
139
|
const s: any = {};
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
140
|
+
const obj = req.query;
|
|
141
|
+
const keys = Object.keys(obj);
|
|
142
|
+
for (const key of keys) {
|
|
143
|
+
if (inArray(key, arr)) {
|
|
144
|
+
const x = (obj[key] as string).split(',');
|
|
145
|
+
setValue(s, key, x);
|
|
146
|
+
} else {
|
|
147
|
+
setValue(s, key, obj[key] as string);
|
|
149
148
|
}
|
|
150
|
-
|
|
149
|
+
}
|
|
150
|
+
return s;
|
|
151
151
|
}
|
|
152
152
|
export function inArray(s: string, arr?: string[]): boolean {
|
|
153
153
|
if (!arr || arr.length === 0) {
|
|
@@ -179,25 +179,37 @@ export function setValue<T>(obj: T, path: string, value: string): void {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
*/
|
|
182
|
-
export function setValue<T, V>(
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
182
|
+
export function setValue<T, V>(o: T, key: string, value: V): any {
|
|
183
|
+
const obj: any = o;
|
|
184
|
+
let replaceKey = key.replace(/\[/g, '.[').replace(/\.\./g, '.');
|
|
185
|
+
if (replaceKey.indexOf('.') === 0) {
|
|
186
|
+
replaceKey = replaceKey.slice(1, replaceKey.length);
|
|
187
|
+
}
|
|
188
|
+
const keys = replaceKey.split('.');
|
|
189
|
+
const firstKey = keys.shift();
|
|
190
|
+
if (!firstKey) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const isArrayKey = /\[([0-9]+)\]/.test(firstKey);
|
|
194
|
+
if (keys.length > 0) {
|
|
195
|
+
const firstKeyValue = obj[firstKey] || {};
|
|
196
|
+
const returnValue = setValue(firstKeyValue, keys.join('.'), value);
|
|
197
|
+
return setKey(obj, isArrayKey, firstKey, returnValue);
|
|
198
|
+
}
|
|
199
|
+
return setKey(obj, isArrayKey, firstKey, value);
|
|
200
|
+
}
|
|
201
|
+
const setKey = (_object: any, _isArrayKey: boolean, _key: string, _nextValue: any) => {
|
|
202
|
+
if (_isArrayKey) {
|
|
203
|
+
if (_object.length > _key) {
|
|
204
|
+
_object[_key] = _nextValue;
|
|
205
|
+
} else {
|
|
206
|
+
_object.push(_nextValue);
|
|
197
207
|
}
|
|
198
|
-
|
|
208
|
+
} else {
|
|
209
|
+
_object[_key] = _nextValue;
|
|
199
210
|
}
|
|
200
|
-
|
|
211
|
+
return _object;
|
|
212
|
+
};
|
|
201
213
|
export interface Limit {
|
|
202
214
|
limit?: number;
|
|
203
215
|
skip?: number;
|
|
@@ -370,7 +382,8 @@ export function getParameters<T>(obj: T, config?: SearchConfig): Limit {
|
|
|
370
382
|
return r;
|
|
371
383
|
}
|
|
372
384
|
}
|
|
373
|
-
|
|
385
|
+
// tslint:disable-next-line:array-type
|
|
386
|
+
export function deletePageInfo(obj: any, arr?: Array<string | undefined>): void {
|
|
374
387
|
if (!arr || arr.length === 0) {
|
|
375
388
|
delete obj['limit'];
|
|
376
389
|
delete obj['firstLimit'];
|