@nattyjs/core 0.0.1-beta.0
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/README.md +11 -0
- package/dist/index.cjs +1273 -0
- package/dist/index.d.ts +468 -0
- package/dist/index.mjs +1233 -0
- package/package.json +25 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1233 @@
|
|
|
1
|
+
import { commonContainer, isObject, List, BLANK as BLANK$1, RIGHT_SLASH } from '@nattyjs/common';
|
|
2
|
+
import { container, injectable as injectable$1 } from 'tsyringe';
|
|
3
|
+
import { match } from 'path-to-regexp';
|
|
4
|
+
|
|
5
|
+
function defineNattyConfig(config) {
|
|
6
|
+
return config;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
var RunOn = /* @__PURE__ */ ((RunOn2) => {
|
|
10
|
+
RunOn2[RunOn2["AzureFunction"] = 0] = "AzureFunction";
|
|
11
|
+
RunOn2[RunOn2["ExpressJs"] = 1] = "ExpressJs";
|
|
12
|
+
RunOn2[RunOn2["Koa"] = 2] = "Koa";
|
|
13
|
+
RunOn2[RunOn2["FastifyJs"] = 3] = "FastifyJs";
|
|
14
|
+
RunOn2[RunOn2["GoogleFunction"] = 4] = "GoogleFunction";
|
|
15
|
+
RunOn2[RunOn2["Lambda"] = 5] = "Lambda";
|
|
16
|
+
return RunOn2;
|
|
17
|
+
})(RunOn || {});
|
|
18
|
+
|
|
19
|
+
function route(path) {
|
|
20
|
+
return function(target, propertyKey, parameterIndex) {
|
|
21
|
+
return target;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const CONTROLLER = "controller";
|
|
26
|
+
const INVALID_VALUE = "INVALID_VALUE";
|
|
27
|
+
const BLANK = "";
|
|
28
|
+
|
|
29
|
+
function get(path) {
|
|
30
|
+
return function(target, propertyKey, descriptor) {
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function post(path) {
|
|
35
|
+
return function(target, propertyKey, descriptor) {
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function put(path) {
|
|
40
|
+
return function(target, propertyKey, descriptor) {
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function Delete() {
|
|
45
|
+
return function(target, propertyKey, descriptor) {
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const _StaticContainer = class {
|
|
50
|
+
setup(types) {
|
|
51
|
+
_StaticContainer.types = types;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
let StaticContainer = _StaticContainer;
|
|
55
|
+
StaticContainer.types = void 0;
|
|
56
|
+
const nattyContainer = new class {
|
|
57
|
+
constructor() {
|
|
58
|
+
this.container = /* @__PURE__ */ new Map();
|
|
59
|
+
this.containerState = /* @__PURE__ */ new Map();
|
|
60
|
+
}
|
|
61
|
+
setup(config, routes, types) {
|
|
62
|
+
this.config = config;
|
|
63
|
+
this.routes = routes;
|
|
64
|
+
this.types = types;
|
|
65
|
+
}
|
|
66
|
+
getTypes() {
|
|
67
|
+
return StaticContainer.types;
|
|
68
|
+
}
|
|
69
|
+
getControllerInfo(target) {
|
|
70
|
+
return this.container.get(target);
|
|
71
|
+
}
|
|
72
|
+
getOrCreateControllerInfo(target) {
|
|
73
|
+
let controllerInfo = this.getControllerInfo(target);
|
|
74
|
+
if (!controllerInfo)
|
|
75
|
+
controllerInfo = this.registerController(target, { path: "" });
|
|
76
|
+
return controllerInfo;
|
|
77
|
+
}
|
|
78
|
+
registerController(target, routeInfo) {
|
|
79
|
+
console.log("controller", target);
|
|
80
|
+
let controllerInfo = this.getControllerInfo(target);
|
|
81
|
+
let controllerName = target.name;
|
|
82
|
+
const passed = controllerName.length > 10 && controllerName.substr(controllerName.length - 10, controllerName.length).toLowerCase() == CONTROLLER;
|
|
83
|
+
if (!controllerInfo && passed) {
|
|
84
|
+
if (!routeInfo.path)
|
|
85
|
+
routeInfo.path = controllerName.length > 10 ? controllerName.substr(0, controllerName.length - 10).toLowerCase() : BLANK;
|
|
86
|
+
controllerInfo = { route: routeInfo, actions: {} };
|
|
87
|
+
this.container.set(target, controllerInfo);
|
|
88
|
+
} else if (!passed)
|
|
89
|
+
console.warn(
|
|
90
|
+
`${target.name} is not register for http request.`
|
|
91
|
+
);
|
|
92
|
+
return controllerInfo;
|
|
93
|
+
}
|
|
94
|
+
registerAction(target, action, actionInfo) {
|
|
95
|
+
const controllerInfo = this.getOrCreateControllerInfo(target);
|
|
96
|
+
if (controllerInfo) {
|
|
97
|
+
const aInfo = controllerInfo.actions[actionInfo.route.path];
|
|
98
|
+
console.log("actions", controllerInfo.actions);
|
|
99
|
+
actionInfo = { ...{ action }, ...actionInfo };
|
|
100
|
+
if (!aInfo)
|
|
101
|
+
controllerInfo.actions[actionInfo.route.path] = [actionInfo];
|
|
102
|
+
else if (aInfo.filter((t) => t.requestMethod == actionInfo.requestMethod).length == 0)
|
|
103
|
+
aInfo.push(actionInfo);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
getControllerRoutePath(instance) {
|
|
107
|
+
let routePath = null;
|
|
108
|
+
let controllerName = instance.name;
|
|
109
|
+
const passed = controllerName.length > 10 && controllerName.substr(controllerName.length - 10, controllerName.length).toLowerCase() == CONTROLLER;
|
|
110
|
+
return routePath ? routePath : passed ? controllerName.substr(0, controllerName.length - 10).toLowerCase() : BLANK;
|
|
111
|
+
}
|
|
112
|
+
}();
|
|
113
|
+
|
|
114
|
+
function init(config, appConfig) {
|
|
115
|
+
commonContainer.setupConfig(config);
|
|
116
|
+
commonContainer.setEnvTsDefinition(appConfig.envTsDefinition);
|
|
117
|
+
nattyContainer.setup(config, appConfig.routes, appConfig.types);
|
|
118
|
+
initializeModule(config);
|
|
119
|
+
return appConfig.routes;
|
|
120
|
+
}
|
|
121
|
+
function initializeModule(config) {
|
|
122
|
+
if (config.app) {
|
|
123
|
+
config.app.init(config);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function getPreResponseBody(body) {
|
|
128
|
+
let bodyInfo;
|
|
129
|
+
if (body) {
|
|
130
|
+
if (isObject(body) || Array.isArray(body))
|
|
131
|
+
bodyInfo = { json: body };
|
|
132
|
+
const typeText = typeof body;
|
|
133
|
+
switch (typeText) {
|
|
134
|
+
case "string":
|
|
135
|
+
bodyInfo = { string: body };
|
|
136
|
+
break;
|
|
137
|
+
case "number":
|
|
138
|
+
bodyInfo = { number: body };
|
|
139
|
+
break;
|
|
140
|
+
case "boolean":
|
|
141
|
+
bodyInfo = { boolean: body };
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return bodyInfo;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
class HttpResponse {
|
|
149
|
+
constructor(response) {
|
|
150
|
+
this._cookies = new Array();
|
|
151
|
+
this._headers = new Headers();
|
|
152
|
+
this.setValues(response);
|
|
153
|
+
}
|
|
154
|
+
setValues(responseInit) {
|
|
155
|
+
if (responseInit) {
|
|
156
|
+
if (responseInit.headers)
|
|
157
|
+
for (const [key, value] of Object.entries(responseInit.headers))
|
|
158
|
+
this.headers.append(key, value);
|
|
159
|
+
if (responseInit.cookies)
|
|
160
|
+
for (const cookie of responseInit.cookies)
|
|
161
|
+
this.addCookie(cookie);
|
|
162
|
+
if (responseInit.status)
|
|
163
|
+
this.status = responseInit.status;
|
|
164
|
+
if (responseInit.body)
|
|
165
|
+
this.body = responseInit.body;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
addCookie(cookie) {
|
|
169
|
+
this._cookies.push(cookie);
|
|
170
|
+
}
|
|
171
|
+
get cookies() {
|
|
172
|
+
return this._cookies;
|
|
173
|
+
}
|
|
174
|
+
get status() {
|
|
175
|
+
return this._status;
|
|
176
|
+
}
|
|
177
|
+
set status(value) {
|
|
178
|
+
this._status = value;
|
|
179
|
+
}
|
|
180
|
+
get headers() {
|
|
181
|
+
return this._headers;
|
|
182
|
+
}
|
|
183
|
+
get body() {
|
|
184
|
+
return this._body;
|
|
185
|
+
}
|
|
186
|
+
set body(value) {
|
|
187
|
+
this._body = getPreResponseBody(value);
|
|
188
|
+
}
|
|
189
|
+
write(responseInit) {
|
|
190
|
+
this.setValues(responseInit);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class HttpException {
|
|
195
|
+
constructor(response) {
|
|
196
|
+
this.httpResponse = new HttpResponse(response);
|
|
197
|
+
}
|
|
198
|
+
getResponse() {
|
|
199
|
+
return this.httpResponse;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
var RequestPipeline = /* @__PURE__ */ ((RequestPipeline2) => {
|
|
204
|
+
RequestPipeline2[RequestPipeline2["onAuthentication"] = 0] = "onAuthentication";
|
|
205
|
+
RequestPipeline2[RequestPipeline2["onAuthorization"] = 1] = "onAuthorization";
|
|
206
|
+
return RequestPipeline2;
|
|
207
|
+
})(RequestPipeline || {});
|
|
208
|
+
|
|
209
|
+
function isBoolean(value) {
|
|
210
|
+
return typeof value === "boolean" || value === "1" || value === "true" || value === "0" || value === "false";
|
|
211
|
+
}
|
|
212
|
+
function isNumeric(value) {
|
|
213
|
+
return value - parseFloat(value) + 1 >= 0;
|
|
214
|
+
}
|
|
215
|
+
function isNotBlank(value) {
|
|
216
|
+
return value !== void 0 && value !== null && value !== "";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function trim(value) {
|
|
220
|
+
if (isNotBlank(value)) {
|
|
221
|
+
if (typeof value === "string")
|
|
222
|
+
return value.trim();
|
|
223
|
+
}
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
function ltrim(value) {
|
|
227
|
+
if (isNotBlank(value)) {
|
|
228
|
+
if (typeof value === "string")
|
|
229
|
+
return value.replace(/^\s+/g, "");
|
|
230
|
+
}
|
|
231
|
+
return value;
|
|
232
|
+
}
|
|
233
|
+
function rtrim(value) {
|
|
234
|
+
if (isNotBlank(value)) {
|
|
235
|
+
if (typeof value === "string")
|
|
236
|
+
return value.replace(/\s+$/g, "");
|
|
237
|
+
}
|
|
238
|
+
return value;
|
|
239
|
+
}
|
|
240
|
+
function blacklist(value, chars) {
|
|
241
|
+
if (isNotBlank(value)) {
|
|
242
|
+
if (typeof value === "string")
|
|
243
|
+
return value.replace(new RegExp("[$" + chars + "]+", "g"), "");
|
|
244
|
+
}
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
function stripLow(value, keepNewLines) {
|
|
248
|
+
let chars = keepNewLines === true ? "\0- \v\f-\x7F" : "\0-\x7F";
|
|
249
|
+
return blacklist(value, chars);
|
|
250
|
+
}
|
|
251
|
+
function toBoolean(value, strict) {
|
|
252
|
+
if (isNotBlank(value)) {
|
|
253
|
+
if (strict) {
|
|
254
|
+
return value === "1" || value === "true" || value === true;
|
|
255
|
+
}
|
|
256
|
+
return value !== 0 && value !== "0" && value !== "false" && value !== "" && value !== false;
|
|
257
|
+
}
|
|
258
|
+
return value;
|
|
259
|
+
}
|
|
260
|
+
function toInt(value, radix = 0) {
|
|
261
|
+
if (isNotBlank(value)) {
|
|
262
|
+
if (isNumeric(value))
|
|
263
|
+
return parseInt(value, radix || 10);
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
function toString(value) {
|
|
268
|
+
if (isNotBlank(value))
|
|
269
|
+
return String(value);
|
|
270
|
+
return value;
|
|
271
|
+
}
|
|
272
|
+
function whitelist(value, chars) {
|
|
273
|
+
if (isNotBlank(value)) {
|
|
274
|
+
if (typeof value === "string")
|
|
275
|
+
return value.replace(new RegExp(`[^${chars}]+`, "g"), "");
|
|
276
|
+
}
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
279
|
+
function escape(value) {
|
|
280
|
+
if (isNotBlank(value))
|
|
281
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/\//g, "/").replace(/\\/g, "\").replace(/`/g, "`");
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
function prefix(value, text) {
|
|
285
|
+
if (isNotBlank(value))
|
|
286
|
+
return `${text}${value}`;
|
|
287
|
+
return value;
|
|
288
|
+
}
|
|
289
|
+
function suffix(value, text) {
|
|
290
|
+
if (isNotBlank(value))
|
|
291
|
+
return `${value}${text}`;
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
const SANITIZERS = {
|
|
295
|
+
trim,
|
|
296
|
+
ltrim,
|
|
297
|
+
rtrim,
|
|
298
|
+
blacklist,
|
|
299
|
+
stripLow,
|
|
300
|
+
boolean: toBoolean,
|
|
301
|
+
number: toInt,
|
|
302
|
+
"string": toString,
|
|
303
|
+
whitelist,
|
|
304
|
+
escape,
|
|
305
|
+
prefix,
|
|
306
|
+
suffix
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
class HttpBadRequestException extends HttpException {
|
|
310
|
+
constructor(data) {
|
|
311
|
+
super({
|
|
312
|
+
...{
|
|
313
|
+
body: data
|
|
314
|
+
},
|
|
315
|
+
...{ status: 400 }
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
class HttpNotFoundException extends HttpException {
|
|
321
|
+
constructor(data) {
|
|
322
|
+
super({ ...{ body: data, status: 404 } });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
var HttpStatusCode = /* @__PURE__ */ ((HttpStatusCode2) => {
|
|
327
|
+
HttpStatusCode2[HttpStatusCode2["success"] = 200] = "success";
|
|
328
|
+
HttpStatusCode2[HttpStatusCode2["created"] = 201] = "created";
|
|
329
|
+
HttpStatusCode2[HttpStatusCode2["noContent"] = 204] = "noContent";
|
|
330
|
+
HttpStatusCode2[HttpStatusCode2["notFound"] = 404] = "notFound";
|
|
331
|
+
HttpStatusCode2[HttpStatusCode2["unAuthorized"] = 401] = "unAuthorized";
|
|
332
|
+
HttpStatusCode2[HttpStatusCode2["forbiddenAccess"] = 403] = "forbiddenAccess";
|
|
333
|
+
HttpStatusCode2[HttpStatusCode2["badRequest"] = 400] = "badRequest";
|
|
334
|
+
HttpStatusCode2[HttpStatusCode2["serverError"] = 500] = "serverError";
|
|
335
|
+
return HttpStatusCode2;
|
|
336
|
+
})(HttpStatusCode || {});
|
|
337
|
+
|
|
338
|
+
class BaseResult {
|
|
339
|
+
constructor(response, responseHeaders) {
|
|
340
|
+
this.response = response;
|
|
341
|
+
if (responseHeaders)
|
|
342
|
+
this.response = { ...this.response, ...responseHeaders };
|
|
343
|
+
}
|
|
344
|
+
getResponse() {
|
|
345
|
+
return this.response;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function getResponseBodyObject(body, props) {
|
|
350
|
+
if (body instanceof List)
|
|
351
|
+
return getResponseBodyObject(body.values, body.props);
|
|
352
|
+
if (Array.isArray(body)) {
|
|
353
|
+
const responseBody = [];
|
|
354
|
+
body.forEach((item) => {
|
|
355
|
+
responseBody.push(getResponseBodyObject(item, props));
|
|
356
|
+
});
|
|
357
|
+
return responseBody;
|
|
358
|
+
} else if (isObject(body)) {
|
|
359
|
+
const jObject = {};
|
|
360
|
+
const keys = Object.keys(body);
|
|
361
|
+
const getterProps = props ? Object.keys(props).map((key) => props[key]) : [];
|
|
362
|
+
for (const key of [...keys, ...getterProps])
|
|
363
|
+
jObject[key] = getResponseBodyObject(body[key]);
|
|
364
|
+
return jObject;
|
|
365
|
+
}
|
|
366
|
+
return body;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
class BaseResponse {
|
|
370
|
+
badRequest() {
|
|
371
|
+
throw new HttpBadRequestException(
|
|
372
|
+
{ message: "Invalid parameters" }
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
success(body) {
|
|
376
|
+
let httpResponseInit;
|
|
377
|
+
if (body instanceof BaseResult)
|
|
378
|
+
httpResponseInit = body.getResponse();
|
|
379
|
+
else
|
|
380
|
+
httpResponseInit = { body: getResponseBodyObject(body), status: HttpStatusCode.success };
|
|
381
|
+
this.httpContext.response.write(httpResponseInit);
|
|
382
|
+
return this.httpContext.response;
|
|
383
|
+
}
|
|
384
|
+
notFound() {
|
|
385
|
+
throw new HttpNotFoundException({});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function getTypedErrorMessage(type, value) {
|
|
390
|
+
const message = commonContainer.nattyConfig.modelBinding.errorMessage.typed[type];
|
|
391
|
+
return parseMessage(message, [value]);
|
|
392
|
+
}
|
|
393
|
+
function parseMessage(message, value) {
|
|
394
|
+
value.forEach((item, index) => {
|
|
395
|
+
message = message.replace(`{${index}}`, item);
|
|
396
|
+
});
|
|
397
|
+
return message;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const entityContainer = new class {
|
|
401
|
+
constructor() {
|
|
402
|
+
this.entityConfig = {};
|
|
403
|
+
}
|
|
404
|
+
register(config) {
|
|
405
|
+
const params = config.decoratorParams;
|
|
406
|
+
const name = config.decoratorParams.target.name || config.decoratorParams.target.constructor.name;
|
|
407
|
+
let entityInfo = this.entityConfig[name] || null;
|
|
408
|
+
let propertyInfo = null;
|
|
409
|
+
if (!this.entityConfig[name])
|
|
410
|
+
entityInfo = this.entityConfig[name] = { target: config.decoratorParams.target, properties: {} };
|
|
411
|
+
if (config.modelConfig) {
|
|
412
|
+
entityInfo.modelInfo = config.modelConfig;
|
|
413
|
+
} else {
|
|
414
|
+
propertyInfo = entityInfo.properties[params.propertyKey];
|
|
415
|
+
if (!propertyInfo)
|
|
416
|
+
propertyInfo = entityInfo.properties[params.propertyKey] = { validators: {} };
|
|
417
|
+
if (!propertyInfo.propConfig && config.propConfig)
|
|
418
|
+
propertyInfo.propConfig = {};
|
|
419
|
+
if (config.propConfig)
|
|
420
|
+
propertyInfo.propConfig = { ...config.propConfig, ...propertyInfo.propConfig };
|
|
421
|
+
if (config.validatorConfig)
|
|
422
|
+
propertyInfo.validators[config.validatorConfig.name] = config.validatorConfig;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
getTarget(entityName) {
|
|
426
|
+
const entityInfo = this.entityConfig[entityName];
|
|
427
|
+
return entityInfo ? entityInfo.target : null;
|
|
428
|
+
}
|
|
429
|
+
getModelConfig(entityName) {
|
|
430
|
+
const entityInfo = this.entityConfig[entityName];
|
|
431
|
+
return entityInfo ? entityInfo.modelInfo : null;
|
|
432
|
+
}
|
|
433
|
+
getPrimaryKeys(entityName) {
|
|
434
|
+
const primaryKeys = new Array();
|
|
435
|
+
const entityInfo = this.entityConfig[entityName];
|
|
436
|
+
if (entityInfo) {
|
|
437
|
+
for (const [key, value] of Object.entries(entityInfo.properties)) {
|
|
438
|
+
if (value.propConfig?.db?.isPrimaryKey)
|
|
439
|
+
primaryKeys.push(key);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return primaryKeys;
|
|
443
|
+
}
|
|
444
|
+
getPropsDbConfig(entityName) {
|
|
445
|
+
const props = /* @__PURE__ */ new Map();
|
|
446
|
+
const entityInfo = this.entityConfig[entityName];
|
|
447
|
+
if (entityInfo) {
|
|
448
|
+
for (const [key, value] of Object.entries(entityInfo.properties)) {
|
|
449
|
+
if (value.propConfig?.db)
|
|
450
|
+
props.set(key, value.propConfig?.db);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return props;
|
|
454
|
+
}
|
|
455
|
+
getPropertyValidators(entityName, propName) {
|
|
456
|
+
const entityInfo = this.entityConfig[entityName];
|
|
457
|
+
const propertyInfo = entityInfo.properties[propName];
|
|
458
|
+
return propertyInfo ? propertyInfo.validators : {};
|
|
459
|
+
}
|
|
460
|
+
getProperties(entityName) {
|
|
461
|
+
const typeInfo = nattyContainer.types[entityName];
|
|
462
|
+
return typeInfo.props;
|
|
463
|
+
}
|
|
464
|
+
}();
|
|
465
|
+
|
|
466
|
+
class ParameterTypeConverter extends BaseResponse {
|
|
467
|
+
constructor() {
|
|
468
|
+
super(...arguments);
|
|
469
|
+
this.sanitizer = {
|
|
470
|
+
number: (value) => isNotBlank(value) ? isNumeric(value) ? SANITIZERS.number(value) : INVALID_VALUE : value,
|
|
471
|
+
boolean: (value) => isNotBlank(value) ? isBoolean(value) ? SANITIZERS.boolean(value) : INVALID_VALUE : value,
|
|
472
|
+
string: (value) => isNotBlank(value) ? SANITIZERS.string(value) : value,
|
|
473
|
+
date: (value) => isNotBlank(value) ? new Date(value) : value
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
get types() {
|
|
477
|
+
return nattyContainer.types;
|
|
478
|
+
}
|
|
479
|
+
getObjectTypeInfo(typeName) {
|
|
480
|
+
return this.types[typeName];
|
|
481
|
+
}
|
|
482
|
+
isArrayType(typeName) {
|
|
483
|
+
return typeName.indexOf("[]") > 0;
|
|
484
|
+
}
|
|
485
|
+
getTypeName(typeName) {
|
|
486
|
+
const extractor = /<([^>]+)>/g;
|
|
487
|
+
const replacer = /[<>[\]]/g;
|
|
488
|
+
const matches = typeName.match(extractor);
|
|
489
|
+
let name = typeName.replace(replacer, BLANK$1);
|
|
490
|
+
if (matches)
|
|
491
|
+
matches.forEach((match) => {
|
|
492
|
+
name = match.replace(replacer, BLANK$1);
|
|
493
|
+
});
|
|
494
|
+
return name;
|
|
495
|
+
}
|
|
496
|
+
castObject(requestBody, properties) {
|
|
497
|
+
const body = {};
|
|
498
|
+
const invalidProps = {};
|
|
499
|
+
for (const property of properties) {
|
|
500
|
+
const typeName = this.getTypeName(property.type);
|
|
501
|
+
const objectTypeInfo = this.types[typeName];
|
|
502
|
+
const value = requestBody[property.name];
|
|
503
|
+
if (value !== void 0 && value != null) {
|
|
504
|
+
if (objectTypeInfo) {
|
|
505
|
+
if (this.isArrayType(property.type) && Array.isArray(value)) {
|
|
506
|
+
let arrayValue = body[property.name] = [];
|
|
507
|
+
let arrayInvalidProps = [];
|
|
508
|
+
const index = 0;
|
|
509
|
+
for (const item of value) {
|
|
510
|
+
const { body: body2, invalidProps: invalidProps2 } = this.castObject(item, objectTypeInfo.props);
|
|
511
|
+
arrayValue.push(body2);
|
|
512
|
+
if (Object.keys(invalidProps2).length > 0)
|
|
513
|
+
arrayInvalidProps.push({ ...invalidProps2, index });
|
|
514
|
+
}
|
|
515
|
+
if (arrayInvalidProps.length > 0)
|
|
516
|
+
invalidProps[property.name] = arrayInvalidProps;
|
|
517
|
+
} else if (objectTypeInfo) {
|
|
518
|
+
const result = this.castObject(requestBody[property.name], objectTypeInfo.props);
|
|
519
|
+
body[property.name] = result.body;
|
|
520
|
+
if (Object.keys(result.invalidProps).length > 0)
|
|
521
|
+
invalidProps[property.name] = result.invalidProps;
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
if (this.isArrayType(property.type) && Array.isArray(value)) {
|
|
525
|
+
let arrayValue = body[property.name] = [];
|
|
526
|
+
let arrayInvalidProps = invalidProps[property.name] = [];
|
|
527
|
+
for (const item of value) {
|
|
528
|
+
const sanitizeValue = this.sanitizer[property.type.toLowerCase()] ? this.sanitizer[property.type.toLowerCase()](item) : item;
|
|
529
|
+
if (sanitizeValue === INVALID_VALUE)
|
|
530
|
+
arrayInvalidProps.push(property);
|
|
531
|
+
else
|
|
532
|
+
arrayValue.push(sanitizeValue);
|
|
533
|
+
}
|
|
534
|
+
} else
|
|
535
|
+
this.setValue(value, property, { body, invalidProps });
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return { body, invalidProps };
|
|
540
|
+
}
|
|
541
|
+
setValue(value, property, json) {
|
|
542
|
+
const sanitizeValue = this.sanitizer[property.type.toLowerCase()] ? this.sanitizer[property.type.toLowerCase()](value) : value;
|
|
543
|
+
if (sanitizeValue === INVALID_VALUE)
|
|
544
|
+
json.invalidProps[property.name] = getTypedErrorMessage(property.type, value);
|
|
545
|
+
else
|
|
546
|
+
json.body[property.name] = sanitizeValue;
|
|
547
|
+
}
|
|
548
|
+
convert(methodInfo, jObject) {
|
|
549
|
+
for (const parameterInfo of methodInfo.parameters) {
|
|
550
|
+
const value = jObject[parameterInfo.name];
|
|
551
|
+
if (value !== void 0) {
|
|
552
|
+
jObject[parameterInfo.name] = SANITIZERS[parameterInfo.type](value);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return jObject;
|
|
556
|
+
}
|
|
557
|
+
convertToInstance(entityName, data) {
|
|
558
|
+
const typesInfo = this.types[entityName];
|
|
559
|
+
const target = this.getClassTarget(typesInfo.path) || entityContainer.getTarget(entityName);
|
|
560
|
+
let instance = null;
|
|
561
|
+
if (target) {
|
|
562
|
+
instance = new target();
|
|
563
|
+
this.mapToInstanceProperties(data, instance, typesInfo.props);
|
|
564
|
+
} else
|
|
565
|
+
instance = data;
|
|
566
|
+
return instance;
|
|
567
|
+
}
|
|
568
|
+
getClassTarget(resolver) {
|
|
569
|
+
if (resolver) {
|
|
570
|
+
const classInfo = resolver();
|
|
571
|
+
const name = Object.keys(classInfo)[0];
|
|
572
|
+
return classInfo[name];
|
|
573
|
+
}
|
|
574
|
+
return void 0;
|
|
575
|
+
}
|
|
576
|
+
mapToInstanceProperties(requestBody, instance, properties) {
|
|
577
|
+
for (const property of properties) {
|
|
578
|
+
const typeName = this.getTypeName(property.type);
|
|
579
|
+
const objectTypeInfo = this.types[typeName];
|
|
580
|
+
const value = requestBody[property.name];
|
|
581
|
+
if (value !== void 0) {
|
|
582
|
+
if (objectTypeInfo) {
|
|
583
|
+
if (this.isArrayType(property.type) && Array.isArray(value)) {
|
|
584
|
+
const items = new Array();
|
|
585
|
+
for (const item of value)
|
|
586
|
+
items.push(this.convertToInstance(typeName, item));
|
|
587
|
+
instance[property.name] = items;
|
|
588
|
+
} else {
|
|
589
|
+
instance[property.name] = this.convertToInstance(typeName, value);
|
|
590
|
+
}
|
|
591
|
+
} else {
|
|
592
|
+
instance[property.name] = value;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
class RouteParser extends ParameterTypeConverter {
|
|
600
|
+
constructor(httpContext) {
|
|
601
|
+
super();
|
|
602
|
+
this.httpContext = httpContext;
|
|
603
|
+
this.init();
|
|
604
|
+
}
|
|
605
|
+
get httpMethod() {
|
|
606
|
+
return this.httpContext.request.method.toLowerCase();
|
|
607
|
+
}
|
|
608
|
+
get appRoutes() {
|
|
609
|
+
return nattyContainer.routes;
|
|
610
|
+
}
|
|
611
|
+
init() {
|
|
612
|
+
const isMatched = this.matchRoute();
|
|
613
|
+
if (!isMatched)
|
|
614
|
+
this.notFound();
|
|
615
|
+
}
|
|
616
|
+
getController(controllerRoute) {
|
|
617
|
+
const controllerInfo = controllerRoute.controller();
|
|
618
|
+
const name = Object.keys(controllerInfo)[0];
|
|
619
|
+
return controllerInfo[name];
|
|
620
|
+
}
|
|
621
|
+
matchRoute() {
|
|
622
|
+
let isMatched = false;
|
|
623
|
+
const requestPathname = this.getRequestPathname();
|
|
624
|
+
for (const [key, value] of Object.entries(this.appRoutes)) {
|
|
625
|
+
const rootPath = key;
|
|
626
|
+
const routeConfig = value;
|
|
627
|
+
const childRoutes = routeConfig[this.httpMethod];
|
|
628
|
+
if (childRoutes) {
|
|
629
|
+
for (const [key2, value2] of Object.entries(childRoutes)) {
|
|
630
|
+
const childPath = key2.indexOf(RIGHT_SLASH) == 0 ? key2 === RIGHT_SLASH ? BLANK$1 : key2 : `/${key2}`;
|
|
631
|
+
const methodInfo = value2;
|
|
632
|
+
const configuredRoutePath = `${rootPath}${childPath}`;
|
|
633
|
+
const routeMatch = match(`${rootPath}${childPath}`, { decode: decodeURIComponent });
|
|
634
|
+
const matched = routeMatch(requestPathname);
|
|
635
|
+
if (matched) {
|
|
636
|
+
isMatched = true;
|
|
637
|
+
this.routeInfo = {
|
|
638
|
+
path: `${commonContainer.nattyConfig.api.rootPath}/${requestPathname}`,
|
|
639
|
+
configuredRoutePath: `${commonContainer.nattyConfig.api.rootPath}/${configuredRoutePath}`,
|
|
640
|
+
controller: this.getController(routeConfig),
|
|
641
|
+
parameters: routeConfig.parameters,
|
|
642
|
+
methodInfo,
|
|
643
|
+
params: this.convert(methodInfo, matched.params),
|
|
644
|
+
queryParams: this.getQueryParams()
|
|
645
|
+
};
|
|
646
|
+
break;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return isMatched;
|
|
652
|
+
}
|
|
653
|
+
getRequestPathname() {
|
|
654
|
+
const apiRootPath = commonContainer.nattyConfig.api.rootPath;
|
|
655
|
+
const url = new URL(this.httpContext.request.url);
|
|
656
|
+
let splitUrl = url.pathname.split(`${apiRootPath}/`);
|
|
657
|
+
if (splitUrl.length > 1)
|
|
658
|
+
return splitUrl[1];
|
|
659
|
+
return url.pathname;
|
|
660
|
+
}
|
|
661
|
+
getQueryParams() {
|
|
662
|
+
const url = new URL(this.httpContext.request.url);
|
|
663
|
+
const queryParams = {};
|
|
664
|
+
for (const param of url.searchParams.keys())
|
|
665
|
+
queryParams[param] = url.searchParams.get(param);
|
|
666
|
+
return queryParams;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
class UnauthorizedAccessException extends HttpException {
|
|
671
|
+
constructor(data) {
|
|
672
|
+
super({
|
|
673
|
+
body: data,
|
|
674
|
+
status: 401
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const decoratorStateContainer = new class {
|
|
680
|
+
constructor() {
|
|
681
|
+
this.controllerConfig = {};
|
|
682
|
+
}
|
|
683
|
+
register(params, type, additionalConfig) {
|
|
684
|
+
const name = params.target.name || params.target.constructor.name;
|
|
685
|
+
let controllerInfo = this.controllerConfig[name] || null;
|
|
686
|
+
let controllerMethodInfo = null;
|
|
687
|
+
if (!this.controllerConfig[name])
|
|
688
|
+
controllerInfo = this.controllerConfig[name] = { config: {}, methods: {} };
|
|
689
|
+
if (params.propertyKey) {
|
|
690
|
+
controllerMethodInfo = controllerInfo.methods[params.propertyKey] || null;
|
|
691
|
+
if (!controllerInfo.methods[params.propertyKey])
|
|
692
|
+
controllerMethodInfo = controllerInfo.methods[params.propertyKey] = {};
|
|
693
|
+
controllerMethodInfo[type] = additionalConfig;
|
|
694
|
+
} else
|
|
695
|
+
controllerInfo.config[type] = additionalConfig;
|
|
696
|
+
}
|
|
697
|
+
getInfo(controllerName, methodName, type) {
|
|
698
|
+
const controllerInfo = this.controllerConfig[controllerName];
|
|
699
|
+
const methodInfo = controllerInfo && methodName ? controllerInfo.methods[methodName] : null;
|
|
700
|
+
let methodConfig = void 0;
|
|
701
|
+
let controllerConfig = void 0;
|
|
702
|
+
if (controllerInfo && controllerInfo.config)
|
|
703
|
+
controllerConfig = controllerInfo.config[type];
|
|
704
|
+
if (methodInfo)
|
|
705
|
+
methodConfig = methodInfo[type];
|
|
706
|
+
return { controllerConfig, methodConfig };
|
|
707
|
+
}
|
|
708
|
+
}();
|
|
709
|
+
|
|
710
|
+
var DecoratorType = /* @__PURE__ */ ((DecoratorType2) => {
|
|
711
|
+
DecoratorType2[DecoratorType2["anonymous"] = 0] = "anonymous";
|
|
712
|
+
DecoratorType2[DecoratorType2["get"] = 1] = "get";
|
|
713
|
+
DecoratorType2[DecoratorType2["post"] = 2] = "post";
|
|
714
|
+
DecoratorType2[DecoratorType2["put"] = 3] = "put";
|
|
715
|
+
DecoratorType2[DecoratorType2["delete"] = 4] = "delete";
|
|
716
|
+
DecoratorType2[DecoratorType2["route"] = 5] = "route";
|
|
717
|
+
DecoratorType2[DecoratorType2["authenticationOnly"] = 6] = "authenticationOnly";
|
|
718
|
+
DecoratorType2[DecoratorType2["authorize"] = 7] = "authorize";
|
|
719
|
+
DecoratorType2[DecoratorType2["useFilter"] = 8] = "useFilter";
|
|
720
|
+
DecoratorType2[DecoratorType2["onException"] = 9] = "onException";
|
|
721
|
+
return DecoratorType2;
|
|
722
|
+
})(DecoratorType || {});
|
|
723
|
+
|
|
724
|
+
class ForbiddenAccessException extends HttpException {
|
|
725
|
+
constructor(data) {
|
|
726
|
+
super({
|
|
727
|
+
body: data,
|
|
728
|
+
status: 403
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
class AbstractExecutionContext {
|
|
734
|
+
constructor(context, routeInfo) {
|
|
735
|
+
this.context = context;
|
|
736
|
+
this.routeInfo = routeInfo;
|
|
737
|
+
}
|
|
738
|
+
resolveService(instance) {
|
|
739
|
+
return container.resolve(instance);
|
|
740
|
+
}
|
|
741
|
+
get request() {
|
|
742
|
+
return this.context.request;
|
|
743
|
+
}
|
|
744
|
+
get user() {
|
|
745
|
+
return this.context.user;
|
|
746
|
+
}
|
|
747
|
+
get result() {
|
|
748
|
+
return this._result;
|
|
749
|
+
}
|
|
750
|
+
set result(value) {
|
|
751
|
+
this._result = value;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
class ActionExecutingContext extends AbstractExecutionContext {
|
|
756
|
+
constructor(models, context, routeInfo) {
|
|
757
|
+
super(context, routeInfo);
|
|
758
|
+
this.models = models;
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function CreateProblemDetail(modelName, detail) {
|
|
763
|
+
return {
|
|
764
|
+
type: "https://tools.ietf.org/html/rfc7231#section-6.5.1",
|
|
765
|
+
title: `The specified ${modelName} model props are invalid.`,
|
|
766
|
+
detail
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
function runValidators(entityName, value) {
|
|
771
|
+
const typeInfo = nattyContainer.types[entityName];
|
|
772
|
+
let errors = {};
|
|
773
|
+
if (typeInfo) {
|
|
774
|
+
for (const propertyInfo of typeInfo.props) {
|
|
775
|
+
const childEntityName = getTypeName(propertyInfo.type);
|
|
776
|
+
const childEntity = nattyContainer.types[childEntityName];
|
|
777
|
+
const validators = entityContainer.getPropertyValidators(entityName, propertyInfo.name);
|
|
778
|
+
if (childEntity) {
|
|
779
|
+
const propValue = value[propertyInfo.name];
|
|
780
|
+
if (propValue) {
|
|
781
|
+
if (Array.isArray(propValue)) {
|
|
782
|
+
let index = 0;
|
|
783
|
+
let arrayPropErrors = [];
|
|
784
|
+
for (const item of propValue) {
|
|
785
|
+
let errors2 = runValidators(childEntityName, item);
|
|
786
|
+
if (errors2)
|
|
787
|
+
arrayPropErrors.push({ ...errors2, index });
|
|
788
|
+
}
|
|
789
|
+
errors[propertyInfo.name] = arrayPropErrors.length > 0 ? arrayPropErrors : null;
|
|
790
|
+
} else
|
|
791
|
+
errors[propertyInfo.name] = runValidators(childEntityName, propValue);
|
|
792
|
+
if (Array.isArray(errors[propertyInfo.name]) && errors[propertyInfo.name].length == 0 || Object.keys(errors[propertyInfo.name]).length == 0)
|
|
793
|
+
errors[propertyInfo.name] = null;
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
errors[propertyInfo.name] = runPropertyValidators(validators, { value: value[propertyInfo.name], current: value, root: this });
|
|
797
|
+
}
|
|
798
|
+
if (!errors[propertyInfo.name])
|
|
799
|
+
delete errors[propertyInfo.name];
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return Object.keys(errors).length === 0 ? null : errors;
|
|
803
|
+
}
|
|
804
|
+
function runPropertyValidators(validators, params) {
|
|
805
|
+
for (const key of Object.keys(validators)) {
|
|
806
|
+
const validatorConfig = validators[key];
|
|
807
|
+
const errors = validatorConfig.validator(validatorConfig.config, params);
|
|
808
|
+
return errors;
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
function getTypeName(typeName) {
|
|
813
|
+
return typeName.replace("[]", BLANK);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
class ModelBindingContext extends ParameterTypeConverter {
|
|
817
|
+
constructor(type, typeInfo, data) {
|
|
818
|
+
super();
|
|
819
|
+
this.type = type;
|
|
820
|
+
this.typeInfo = typeInfo;
|
|
821
|
+
this.data = data;
|
|
822
|
+
this.serialize();
|
|
823
|
+
}
|
|
824
|
+
serialize() {
|
|
825
|
+
const { body, invalidProps } = this.castObject(this.data, this.typeInfo);
|
|
826
|
+
if (Object.keys(invalidProps).length > 0)
|
|
827
|
+
throw new HttpBadRequestException(CreateProblemDetail(this.type, invalidProps));
|
|
828
|
+
else
|
|
829
|
+
this.data = body;
|
|
830
|
+
this.instance = this.convertToInstance(this.type, this.data);
|
|
831
|
+
}
|
|
832
|
+
get isValid() {
|
|
833
|
+
const errors = runValidators(this.type, this.instance);
|
|
834
|
+
return errors === null;
|
|
835
|
+
}
|
|
836
|
+
get errors() {
|
|
837
|
+
const errors = runValidators(this.type, this.instance);
|
|
838
|
+
return errors;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
class ActionExecutedContext extends AbstractExecutionContext {
|
|
843
|
+
constructor(content, httpContext, routeInfo) {
|
|
844
|
+
super(httpContext, routeInfo);
|
|
845
|
+
this.content = content;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
class RequestProcessor extends RouteParser {
|
|
850
|
+
constructor() {
|
|
851
|
+
super(...arguments);
|
|
852
|
+
this.actionFilterInstances = new Array();
|
|
853
|
+
}
|
|
854
|
+
async runPipeline(requestPipeline) {
|
|
855
|
+
switch (requestPipeline) {
|
|
856
|
+
case RequestPipeline.onAuthentication:
|
|
857
|
+
await this.onAuthentication();
|
|
858
|
+
break;
|
|
859
|
+
case RequestPipeline.onAuthorization:
|
|
860
|
+
await this.onAuthorization();
|
|
861
|
+
break;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
resolveFilter(instance) {
|
|
865
|
+
return container.resolve(instance);
|
|
866
|
+
}
|
|
867
|
+
getAuthenticationClass() {
|
|
868
|
+
let authentication = void 0;
|
|
869
|
+
const instanceLevelAuthenticationFilter = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.useFilter);
|
|
870
|
+
if (instanceLevelAuthenticationFilter.methodConfig?.authentication)
|
|
871
|
+
authentication = instanceLevelAuthenticationFilter.methodConfig?.authentication;
|
|
872
|
+
else if (instanceLevelAuthenticationFilter.controllerConfig?.authentication)
|
|
873
|
+
authentication = instanceLevelAuthenticationFilter.controllerConfig?.authentication;
|
|
874
|
+
else
|
|
875
|
+
authentication = commonContainer.globalConfig.authentication;
|
|
876
|
+
return authentication;
|
|
877
|
+
}
|
|
878
|
+
async onAuthentication() {
|
|
879
|
+
const authentication = this.getAuthenticationClass();
|
|
880
|
+
const authenticationFilter = authentication ? this.resolveFilter(authentication) : void 0;
|
|
881
|
+
const anonymousInfo = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.anonymous);
|
|
882
|
+
if (authenticationFilter) {
|
|
883
|
+
const result = await authenticationFilter.onAuthentication(this.httpContext);
|
|
884
|
+
this.httpContext.user = result;
|
|
885
|
+
if (!result.isAuthenticate && !anonymousInfo.controllerConfig && !anonymousInfo.methodConfig)
|
|
886
|
+
throw new UnauthorizedAccessException(authenticationFilter.onFailedResponse());
|
|
887
|
+
await this.onAuthorization();
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
async onAuthorization() {
|
|
891
|
+
const authorization = commonContainer.globalConfig.authorization;
|
|
892
|
+
const authorizationFilter = authorization ? this.resolveFilter(authorization) : void 0;
|
|
893
|
+
const authorizeConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.authorize);
|
|
894
|
+
const authenticationOnly = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.authenticationOnly);
|
|
895
|
+
if (this.httpContext.user?.isAuthenticate && authorizationFilter && (!authenticationOnly.controllerConfig && !authenticationOnly.methodConfig)) {
|
|
896
|
+
const result = await authorizationFilter.onAuthorization(this.httpContext, authorizeConfig.methodConfig || authorizeConfig.controllerConfig);
|
|
897
|
+
if (!result)
|
|
898
|
+
throw new ForbiddenAccessException(authorizationFilter.onFailedAuthorization());
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
async onActionExecuting(methodParameters) {
|
|
902
|
+
let actionFilters = commonContainer.globalConfig.actionFilters || [];
|
|
903
|
+
const actionFiltersConfig = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.useFilter);
|
|
904
|
+
actionFilters = [...actionFilters, ...actionFiltersConfig.controllerConfig?.actionFilters || [], ...actionFiltersConfig.methodConfig?.actionFilters || []];
|
|
905
|
+
const actionExecutingContext = new ActionExecutingContext(
|
|
906
|
+
methodParameters.filter((t) => t instanceof ModelBindingContext),
|
|
907
|
+
this.httpContext,
|
|
908
|
+
this.routeInfo
|
|
909
|
+
);
|
|
910
|
+
for (const actionFilter of actionFilters) {
|
|
911
|
+
const filter = this.resolveFilter(actionFilter);
|
|
912
|
+
this.actionFilterInstances.push(filter);
|
|
913
|
+
await filter.onActionExecuting(actionExecutingContext);
|
|
914
|
+
if (actionExecutingContext.result) {
|
|
915
|
+
return actionExecutingContext.result;
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
async onActionExecuted(content) {
|
|
921
|
+
const executedContext = new ActionExecutedContext(content, this.httpContext, this.routeInfo);
|
|
922
|
+
for (const actionFilter of this.actionFilterInstances) {
|
|
923
|
+
await actionFilter.onActionExecuted(executedContext);
|
|
924
|
+
if (executedContext.result)
|
|
925
|
+
return executedContext.result;
|
|
926
|
+
}
|
|
927
|
+
return null;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
class Resolver extends RequestProcessor {
|
|
932
|
+
constructor(httpContext) {
|
|
933
|
+
super(httpContext);
|
|
934
|
+
}
|
|
935
|
+
getControllerInstance() {
|
|
936
|
+
const controller = this.routeInfo.controller;
|
|
937
|
+
const parameters = this.resolveConstructorParameters();
|
|
938
|
+
const instance = new controller(...parameters);
|
|
939
|
+
return instance;
|
|
940
|
+
}
|
|
941
|
+
resolveClass(classConstruct) {
|
|
942
|
+
return container.resolve(classConstruct);
|
|
943
|
+
}
|
|
944
|
+
resolveConstructorParameters() {
|
|
945
|
+
let parameters = [];
|
|
946
|
+
for (const parameter of this.routeInfo.parameters) {
|
|
947
|
+
const classConstruct = this.getClass(parameter.type);
|
|
948
|
+
if (classConstruct)
|
|
949
|
+
parameters.push(this.resolveClass(classConstruct));
|
|
950
|
+
}
|
|
951
|
+
return parameters;
|
|
952
|
+
}
|
|
953
|
+
getMethodParameters() {
|
|
954
|
+
const parameters = new Array();
|
|
955
|
+
for (const parameter of this.routeInfo.methodInfo.parameters) {
|
|
956
|
+
const params = this.routeInfo.params;
|
|
957
|
+
const queryParams = this.routeInfo.queryParams;
|
|
958
|
+
const typeInfo = this.getObjectTypeInfo(parameter.type);
|
|
959
|
+
if (params[parameter.name] !== void 0)
|
|
960
|
+
parameters.push(params[parameter.name]);
|
|
961
|
+
else if (queryParams[parameter.name] !== void 0)
|
|
962
|
+
parameters.push(queryParams[parameter.name]);
|
|
963
|
+
else if (typeInfo && this.httpContext.request.body.json) {
|
|
964
|
+
const context = new ModelBindingContext(parameter.type, typeInfo.props, this.httpContext.request.body.json);
|
|
965
|
+
parameters.push(context);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
return parameters;
|
|
969
|
+
}
|
|
970
|
+
get onException() {
|
|
971
|
+
return commonContainer.globalConfig.onException;
|
|
972
|
+
}
|
|
973
|
+
async invoke() {
|
|
974
|
+
return new Promise(async (resolve, reject) => {
|
|
975
|
+
try {
|
|
976
|
+
if (this.routeInfo.controller && this.routeInfo.methodInfo) {
|
|
977
|
+
await this.runPipeline(RequestPipeline.onAuthentication);
|
|
978
|
+
const instance = this.getControllerInstance();
|
|
979
|
+
instance.httpContext = this.httpContext;
|
|
980
|
+
const methodParameters = this.getMethodParameters();
|
|
981
|
+
let result = null;
|
|
982
|
+
result = await this.onActionExecuting(methodParameters);
|
|
983
|
+
if (!result)
|
|
984
|
+
result = instance[this.routeInfo.methodInfo.name](...methodParameters.map((t) => {
|
|
985
|
+
if (t instanceof ModelBindingContext)
|
|
986
|
+
return t.instance;
|
|
987
|
+
return t;
|
|
988
|
+
}));
|
|
989
|
+
if (result && result.then) {
|
|
990
|
+
let content = await result;
|
|
991
|
+
let actionResult = await this.onActionExecuted(content);
|
|
992
|
+
resolve(actionResult || this.success(content));
|
|
993
|
+
return content;
|
|
994
|
+
} else {
|
|
995
|
+
let actionResult = await this.onActionExecuted(result);
|
|
996
|
+
resolve(actionResult || this.success(result));
|
|
997
|
+
return result;
|
|
998
|
+
}
|
|
999
|
+
} else {
|
|
1000
|
+
}
|
|
1001
|
+
} catch (ex) {
|
|
1002
|
+
resolve(this.exceptionResult(ex));
|
|
1003
|
+
}
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
exceptionResult(ex) {
|
|
1007
|
+
let result = ex;
|
|
1008
|
+
if (ex instanceof HttpException) {
|
|
1009
|
+
result = ex.getResponse();
|
|
1010
|
+
} else {
|
|
1011
|
+
let onException = this.onException;
|
|
1012
|
+
const { controllerConfig, methodConfig } = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.onException);
|
|
1013
|
+
onException = methodConfig || controllerConfig || onException;
|
|
1014
|
+
if (onException) {
|
|
1015
|
+
const instance = this.resolveClass(onException);
|
|
1016
|
+
if (instance.onException)
|
|
1017
|
+
result = instance.onException({
|
|
1018
|
+
error: ex,
|
|
1019
|
+
request: this.httpContext.request,
|
|
1020
|
+
routeInfo: this.routeInfo
|
|
1021
|
+
});
|
|
1022
|
+
} else
|
|
1023
|
+
result = new HttpException({
|
|
1024
|
+
body: getPreResponseBody({ message: ex.message, stack: ex.stack }),
|
|
1025
|
+
status: HttpStatusCode.serverError
|
|
1026
|
+
}).getResponse();
|
|
1027
|
+
}
|
|
1028
|
+
return result;
|
|
1029
|
+
}
|
|
1030
|
+
getClass(className) {
|
|
1031
|
+
return commonContainer.getMetadataValue(className, "services");
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
class RequestHandler extends Resolver {
|
|
1036
|
+
constructor(httpContext) {
|
|
1037
|
+
super(httpContext);
|
|
1038
|
+
}
|
|
1039
|
+
async onRequest() {
|
|
1040
|
+
try {
|
|
1041
|
+
const result = await this.invoke();
|
|
1042
|
+
return result;
|
|
1043
|
+
} catch (ex) {
|
|
1044
|
+
if (ex instanceof HttpException)
|
|
1045
|
+
return ex.getResponse();
|
|
1046
|
+
let onException = this.onException;
|
|
1047
|
+
const { controllerConfig, methodConfig } = decoratorStateContainer.getInfo(this.routeInfo.controller.name, this.routeInfo.methodInfo.name, DecoratorType.onException);
|
|
1048
|
+
onException = methodConfig || controllerConfig || onException;
|
|
1049
|
+
if (onException) {
|
|
1050
|
+
const instance = this.resolveClass(onException);
|
|
1051
|
+
if (instance.onException)
|
|
1052
|
+
return instance.onException({
|
|
1053
|
+
error: ex,
|
|
1054
|
+
request: this.httpContext.request,
|
|
1055
|
+
routeInfo: this.routeInfo
|
|
1056
|
+
});
|
|
1057
|
+
} else
|
|
1058
|
+
return new HttpException({
|
|
1059
|
+
body: ex,
|
|
1060
|
+
status: HttpStatusCode.serverError
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
class HttpHandler {
|
|
1067
|
+
constructor() {
|
|
1068
|
+
}
|
|
1069
|
+
async processRequest(httpContext) {
|
|
1070
|
+
const requestProcessor = new RequestHandler(httpContext);
|
|
1071
|
+
const result = await requestProcessor.onRequest();
|
|
1072
|
+
return result;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
class HttpRequest {
|
|
1077
|
+
constructor(http) {
|
|
1078
|
+
this.httpRequest = http;
|
|
1079
|
+
}
|
|
1080
|
+
get cookies() {
|
|
1081
|
+
return this.httpRequest.cookies;
|
|
1082
|
+
}
|
|
1083
|
+
get url() {
|
|
1084
|
+
return this.httpRequest.url;
|
|
1085
|
+
}
|
|
1086
|
+
get method() {
|
|
1087
|
+
return this.httpRequest.method;
|
|
1088
|
+
}
|
|
1089
|
+
get headers() {
|
|
1090
|
+
return this.httpRequest.headers;
|
|
1091
|
+
}
|
|
1092
|
+
get user() {
|
|
1093
|
+
return null;
|
|
1094
|
+
}
|
|
1095
|
+
get body() {
|
|
1096
|
+
return this.httpRequest.body;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
class HttpContext {
|
|
1101
|
+
constructor(request, context) {
|
|
1102
|
+
this.request = new HttpRequest(request);
|
|
1103
|
+
this.response = new HttpResponse();
|
|
1104
|
+
this.context = context;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
function injectable(options = {}) {
|
|
1109
|
+
return (targetConstructor) => {
|
|
1110
|
+
injectable$1()(targetConstructor);
|
|
1111
|
+
commonContainer.setMetadata(targetConstructor.name, targetConstructor, "services");
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
function $request(request) {
|
|
1116
|
+
if (!request.url)
|
|
1117
|
+
request.url = `${request.host || "http://127.0.0.1/"}${request.pathName}`;
|
|
1118
|
+
return commonContainer.nattyConfig.testModule.onRequest(request);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
function filter(config) {
|
|
1122
|
+
return (targetConstructor) => {
|
|
1123
|
+
injectable$1()(targetConstructor);
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
function registerDecorator(config) {
|
|
1128
|
+
entityContainer.register(config);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
class AbstractModelState {
|
|
1132
|
+
constructor() {
|
|
1133
|
+
this._errors = {};
|
|
1134
|
+
}
|
|
1135
|
+
get isValid() {
|
|
1136
|
+
this._errors = this.runValidators(this.constructor.name, this);
|
|
1137
|
+
return this._errors === null;
|
|
1138
|
+
}
|
|
1139
|
+
get errors() {
|
|
1140
|
+
this._errors = this.runValidators(this.constructor.name, this);
|
|
1141
|
+
return this._errors;
|
|
1142
|
+
}
|
|
1143
|
+
runValidators(entityName, value) {
|
|
1144
|
+
return runValidators(entityName, value);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
class BaseController {
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
class OkResult extends BaseResult {
|
|
1152
|
+
constructor(value, response) {
|
|
1153
|
+
super({ ...{ body: getResponseBodyObject(value) }, ...{ status: HttpStatusCode.success } }, response);
|
|
1154
|
+
this.value = value;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
function ok(value, response) {
|
|
1158
|
+
return new OkResult(value || BLANK$1, response);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
class NotFoundResult extends BaseResult {
|
|
1162
|
+
constructor(response) {
|
|
1163
|
+
super({ ...{ body: BLANK$1 }, ...{ status: HttpStatusCode.notFound } }, response);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
function notFound(response) {
|
|
1167
|
+
return new NotFoundResult(response);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
class NoContentResult extends BaseResult {
|
|
1171
|
+
constructor(response) {
|
|
1172
|
+
super({ ...{ body: BLANK$1 }, ...{ status: HttpStatusCode.noContent } }, response);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
function noContent(response) {
|
|
1176
|
+
return new NoContentResult(response);
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
class ForbiddenAccessInfoResult extends BaseResult {
|
|
1180
|
+
constructor(value, response) {
|
|
1181
|
+
super({ ...{ body: value }, ...{ status: HttpStatusCode.forbiddenAccess } }, response);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
function forbiddenAccessInfo(value, response) {
|
|
1185
|
+
return new ForbiddenAccessInfoResult(value, response);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
class CreatedResult extends BaseResult {
|
|
1189
|
+
constructor(response) {
|
|
1190
|
+
super({ ...{ body: BLANK$1 }, ...{ status: HttpStatusCode.created } }, response);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
function created(response) {
|
|
1194
|
+
return new CreatedResult(response);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
class BadRequestResult extends BaseResult {
|
|
1198
|
+
constructor(value, response) {
|
|
1199
|
+
super({ ...{ body: value }, ...{ status: HttpStatusCode.badRequest } }, response);
|
|
1200
|
+
this.value = value;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
function badRequest(value, response) {
|
|
1204
|
+
return new BadRequestResult(value || BLANK$1, response);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
function base(params, type, additionaConfig) {
|
|
1208
|
+
decoratorStateContainer.register(params, type, additionaConfig);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
function useFilter(config) {
|
|
1212
|
+
return function(target, propertyKey, descriptor) {
|
|
1213
|
+
base({ target, propertyKey, descriptor }, DecoratorType.useFilter, config);
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
function anonymous() {
|
|
1218
|
+
return function(target, propertyKey, descriptor) {
|
|
1219
|
+
base({ target, propertyKey, descriptor }, DecoratorType.anonymous, true);
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
function authenticationOnly() {
|
|
1224
|
+
return function(target, propertyKey, descriptor) {
|
|
1225
|
+
base({
|
|
1226
|
+
target,
|
|
1227
|
+
propertyKey,
|
|
1228
|
+
descriptor
|
|
1229
|
+
}, DecoratorType.authenticationOnly, true);
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
export { $request, AbstractModelState, BadRequestResult, BaseController, CreatedResult, Delete, ForbiddenAccessException, ForbiddenAccessInfoResult, HttpBadRequestException, HttpContext, HttpException, HttpHandler, HttpNotFoundException, HttpResponse, ModelBindingContext, NoContentResult, NotFoundResult, OkResult, RunOn, UnauthorizedAccessException, anonymous, authenticationOnly, badRequest, created, defineNattyConfig, entityContainer, filter, forbiddenAccessInfo, get, init, injectable, noContent, notFound, ok, post, put, registerDecorator, route, useFilter };
|