@deepkit/http 1.0.1-alpha.71 → 1.0.1-alpha.72
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/dist/cjs/src/decorator.d.ts +8 -6
- package/dist/cjs/src/decorator.js +49 -12
- package/dist/cjs/src/decorator.js.map +1 -1
- package/dist/cjs/src/filter.d.ts +3 -3
- package/dist/cjs/src/filter.js +3 -3
- package/dist/cjs/src/filter.js.map +1 -1
- package/dist/cjs/src/http.d.ts +49 -11
- package/dist/cjs/src/http.js +129 -38
- package/dist/cjs/src/http.js.map +1 -1
- package/dist/cjs/src/kernel.d.ts +3 -3
- package/dist/cjs/src/kernel.js +1 -1
- package/dist/cjs/src/kernel.js.map +1 -1
- package/dist/cjs/src/model.d.ts +11 -3
- package/dist/cjs/src/model.js +58 -26
- package/dist/cjs/src/model.js.map +1 -1
- package/dist/cjs/src/module.js +5 -3
- package/dist/cjs/src/module.js.map +1 -1
- package/dist/cjs/src/router.d.ts +84 -11
- package/dist/cjs/src/router.js +280 -94
- package/dist/cjs/src/router.js.map +1 -1
- package/dist/cjs/src/static-serving.js +15 -8
- package/dist/cjs/src/static-serving.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/src/decorator.d.ts +8 -6
- package/dist/esm/src/decorator.js +47 -11
- package/dist/esm/src/decorator.js.map +1 -1
- package/dist/esm/src/filter.d.ts +3 -3
- package/dist/esm/src/filter.js +4 -4
- package/dist/esm/src/filter.js.map +1 -1
- package/dist/esm/src/http.d.ts +49 -11
- package/dist/esm/src/http.js +102 -24
- package/dist/esm/src/http.js.map +1 -1
- package/dist/esm/src/kernel.d.ts +3 -3
- package/dist/esm/src/kernel.js +2 -2
- package/dist/esm/src/kernel.js.map +1 -1
- package/dist/esm/src/model.d.ts +11 -3
- package/dist/esm/src/model.js +47 -15
- package/dist/esm/src/model.js.map +1 -1
- package/dist/esm/src/module.js +5 -3
- package/dist/esm/src/module.js.map +1 -1
- package/dist/esm/src/router.d.ts +84 -11
- package/dist/esm/src/router.js +278 -94
- package/dist/esm/src/router.js.map +1 -1
- package/dist/esm/src/static-serving.js +16 -9
- package/dist/esm/src/static-serving.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +17 -14
- package/src/decorator.ts +9 -6
- package/src/filter.ts +4 -4
- package/src/http.ts +88 -15
- package/src/kernel.ts +2 -2
- package/src/model.ts +35 -6
- package/src/module.ts +5 -3
- package/src/router.ts +286 -73
- package/src/static-serving.ts +6 -3
- package/coverage/clover.xml +0 -1086
- package/coverage/coverage-final.json +0 -12
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -79
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -126
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -170
- package/coverage/lcov-report/src/controllers.ts.html +0 -113
- package/coverage/lcov-report/src/decorator.ts.html +0 -1280
- package/coverage/lcov-report/src/filter.ts.html +0 -734
- package/coverage/lcov-report/src/http.ts.html +0 -2192
- package/coverage/lcov-report/src/index.html +0 -246
- package/coverage/lcov-report/src/kernel.ts.html +0 -260
- package/coverage/lcov-report/src/middleware.ts.html +0 -428
- package/coverage/lcov-report/src/model.ts.html +0 -937
- package/coverage/lcov-report/src/module.config.ts.html +0 -89
- package/coverage/lcov-report/src/module.ts.html +0 -245
- package/coverage/lcov-report/src/router.ts.html +0 -2318
- package/coverage/lcov-report/tests/index.html +0 -111
- package/coverage/lcov-report/tests/utils.ts.html +0 -176
- package/coverage/lcov.info +0 -2225
- package/dist/cjs/tests/filter.spec.d.ts +0 -1
- package/dist/cjs/tests/filter.spec.js +0 -251
- package/dist/cjs/tests/filter.spec.js.map +0 -1
- package/dist/cjs/tests/middleware.spec.d.ts +0 -1
- package/dist/cjs/tests/middleware.spec.js +0 -471
- package/dist/cjs/tests/middleware.spec.js.map +0 -1
- package/dist/cjs/tests/model.spec.d.ts +0 -1
- package/dist/cjs/tests/model.spec.js +0 -23
- package/dist/cjs/tests/model.spec.js.map +0 -1
- package/dist/cjs/tests/module.spec.d.ts +0 -1
- package/dist/cjs/tests/module.spec.js +0 -46
- package/dist/cjs/tests/module.spec.js.map +0 -1
- package/dist/cjs/tests/router.spec.d.ts +0 -1
- package/dist/cjs/tests/router.spec.js +0 -987
- package/dist/cjs/tests/router.spec.js.map +0 -1
- package/dist/cjs/tests/utils.d.ts +0 -8
- package/dist/cjs/tests/utils.js +0 -30
- package/dist/cjs/tests/utils.js.map +0 -1
- package/dist/esm/tests/filter.spec.d.ts +0 -1
- package/dist/esm/tests/filter.spec.js +0 -249
- package/dist/esm/tests/filter.spec.js.map +0 -1
- package/dist/esm/tests/middleware.spec.d.ts +0 -1
- package/dist/esm/tests/middleware.spec.js +0 -469
- package/dist/esm/tests/middleware.spec.js.map +0 -1
- package/dist/esm/tests/model.spec.d.ts +0 -1
- package/dist/esm/tests/model.spec.js +0 -21
- package/dist/esm/tests/model.spec.js.map +0 -1
- package/dist/esm/tests/module.spec.d.ts +0 -1
- package/dist/esm/tests/module.spec.js +0 -44
- package/dist/esm/tests/module.spec.js.map +0 -1
- package/dist/esm/tests/router.spec.d.ts +0 -1
- package/dist/esm/tests/router.spec.js +0 -985
- package/dist/esm/tests/router.spec.js.map +0 -1
- package/dist/esm/tests/utils.d.ts +0 -8
- package/dist/esm/tests/utils.js +0 -26
- package/dist/esm/tests/utils.js.map +0 -1
- package/tests/filter.spec.ts +0 -181
- package/tests/middleware.spec.ts +0 -440
- package/tests/model.spec.ts +0 -26
- package/tests/module.spec.ts +0 -32
- package/tests/router.spec.ts +0 -781
- package/tests/utils.ts +0 -32
package/src/router.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
getValidatorFunction,
|
|
17
17
|
metaAnnotation,
|
|
18
18
|
ReflectionClass,
|
|
19
|
+
ReflectionFunction,
|
|
19
20
|
ReflectionKind,
|
|
20
21
|
ReflectionParameter,
|
|
21
22
|
SerializationOptions,
|
|
@@ -29,7 +30,7 @@ import {
|
|
|
29
30
|
// @ts-ignore
|
|
30
31
|
import formidable from 'formidable';
|
|
31
32
|
import querystring from 'querystring';
|
|
32
|
-
import { httpClass } from './decorator';
|
|
33
|
+
import { HttpAction, httpClass, HttpController, HttpDecorator } from './decorator';
|
|
33
34
|
import { BodyValidationError, getRegExp, HttpRequest, HttpRequestQuery, HttpRequestResolvedParameters, ValidatedBody } from './model';
|
|
34
35
|
import { InjectorContext, InjectorModule, TagRegistry } from '@deepkit/injector';
|
|
35
36
|
import { Logger, LoggerInterface } from '@deepkit/logger';
|
|
@@ -39,6 +40,7 @@ import { HttpMiddlewareConfig, HttpMiddlewareFn } from './middleware';
|
|
|
39
40
|
|
|
40
41
|
//@ts-ignore
|
|
41
42
|
import qs from 'qs';
|
|
43
|
+
import { isArray } from '@deepkit/core';
|
|
42
44
|
|
|
43
45
|
export type RouteParameterResolverForInjector = ((injector: InjectorContext) => any[] | Promise<any[]>);
|
|
44
46
|
|
|
@@ -57,7 +59,7 @@ export class UploadedFile {
|
|
|
57
59
|
size!: number;
|
|
58
60
|
|
|
59
61
|
/**
|
|
60
|
-
* The path this file is being written to.
|
|
62
|
+
* The local path this file is being written to. Will be deleted when request is handled.
|
|
61
63
|
*/
|
|
62
64
|
path!: string;
|
|
63
65
|
|
|
@@ -83,15 +85,19 @@ export class UploadedFile {
|
|
|
83
85
|
// hash!: string | 'sha1' | 'md5' | 'sha256' | null;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
export interface
|
|
88
|
+
export interface RouteFunctionControllerAction {
|
|
89
|
+
type: 'function';
|
|
87
90
|
//if not set, the root module is used
|
|
88
91
|
module?: InjectorModule<any>;
|
|
89
|
-
|
|
90
|
-
methodName: string;
|
|
92
|
+
fn: (...args: any[]) => any;
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
+
export interface RouteClassControllerAction {
|
|
96
|
+
type: 'controller';
|
|
97
|
+
//if not set, the root module is used
|
|
98
|
+
module?: InjectorModule<any>;
|
|
99
|
+
controller: ClassType;
|
|
100
|
+
methodName: string;
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
export class RouteConfig {
|
|
@@ -115,7 +121,7 @@ export class RouteConfig {
|
|
|
115
121
|
|
|
116
122
|
resolverForToken: Map<any, ClassType> = new Map();
|
|
117
123
|
|
|
118
|
-
middlewares: { config: HttpMiddlewareConfig, module
|
|
124
|
+
middlewares: { config: HttpMiddlewareConfig, module?: InjectorModule<any> }[] = [];
|
|
119
125
|
|
|
120
126
|
resolverForParameterName: Map<string, ClassType> = new Map();
|
|
121
127
|
|
|
@@ -128,11 +134,17 @@ export class RouteConfig {
|
|
|
128
134
|
public readonly name: string,
|
|
129
135
|
public readonly httpMethods: string[],
|
|
130
136
|
public readonly path: string,
|
|
131
|
-
public readonly action:
|
|
137
|
+
public readonly action: RouteClassControllerAction | RouteFunctionControllerAction,
|
|
132
138
|
public internal: boolean = false,
|
|
133
139
|
) {
|
|
134
140
|
}
|
|
135
141
|
|
|
142
|
+
getReflectionFunction(): ReflectionFunction {
|
|
143
|
+
return this.action.type === 'controller' ?
|
|
144
|
+
ReflectionClass.from(this.action.controller).getMethod(this.action.methodName)
|
|
145
|
+
: ReflectionFunction.from(this.action.fn);
|
|
146
|
+
}
|
|
147
|
+
|
|
136
148
|
getSchemaForResponse(statusCode: number): Type | undefined {
|
|
137
149
|
if (!this.responses.length) return;
|
|
138
150
|
for (const response of this.responses) {
|
|
@@ -195,7 +207,7 @@ class ParsedRouteParameter {
|
|
|
195
207
|
getType(): Type {
|
|
196
208
|
if (this.bodyValidation) {
|
|
197
209
|
assertType(this.parameter.type, ReflectionKind.class);
|
|
198
|
-
const valueType = findMember('value', this.parameter.type);
|
|
210
|
+
const valueType = findMember('value', this.parameter.type.types);
|
|
199
211
|
if (!valueType || valueType.kind !== ReflectionKind.property) throw new Error(`No property value found at ${stringifyType(this.parameter.type)}`);
|
|
200
212
|
return valueType.type as Type;
|
|
201
213
|
}
|
|
@@ -231,16 +243,20 @@ function parseRoutePathToRegex(routeConfig: RouteConfig): { regex: string, param
|
|
|
231
243
|
const parameterNames: { [name: string]: number } = {};
|
|
232
244
|
let path = routeConfig.getFullPath();
|
|
233
245
|
|
|
234
|
-
const
|
|
246
|
+
const fn = routeConfig.getReflectionFunction();
|
|
235
247
|
|
|
236
248
|
let argumentIndex = 0;
|
|
237
249
|
path = path.replace(/:(\w+)/g, (a, name) => {
|
|
238
250
|
parameterNames[name] = argumentIndex;
|
|
239
251
|
argumentIndex++;
|
|
240
|
-
const parameter =
|
|
252
|
+
const parameter = fn.getParameterOrUndefined(name);
|
|
241
253
|
if (parameter) {
|
|
242
254
|
const regExp = getRegExp(parameter.type);
|
|
243
|
-
if (regExp
|
|
255
|
+
if (regExp instanceof RegExp) {
|
|
256
|
+
return '(' + regExp.source + ')';
|
|
257
|
+
} else if (regExp) {
|
|
258
|
+
return '(' + regExp + ')';
|
|
259
|
+
}
|
|
244
260
|
}
|
|
245
261
|
return String.raw`([^/]+)`;
|
|
246
262
|
});
|
|
@@ -249,10 +265,9 @@ function parseRoutePathToRegex(routeConfig: RouteConfig): { regex: string, param
|
|
|
249
265
|
}
|
|
250
266
|
|
|
251
267
|
export function parseRouteControllerAction(routeConfig: RouteConfig): ParsedRoute {
|
|
252
|
-
const schema = ReflectionClass.from(routeConfig.action.controller);
|
|
253
268
|
const parsedRoute = new ParsedRoute(routeConfig);
|
|
254
269
|
|
|
255
|
-
const methodArgumentProperties =
|
|
270
|
+
const methodArgumentProperties = routeConfig.getReflectionFunction().getParameters();
|
|
256
271
|
const parsedPath = parseRoutePathToRegex(routeConfig);
|
|
257
272
|
parsedRoute.regex = parsedPath.regex;
|
|
258
273
|
parsedRoute.pathParameterNames = parsedPath.parameterNames;
|
|
@@ -302,11 +317,11 @@ function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry
|
|
|
302
317
|
const middlewareConfigs = middlewares.filter((v) => {
|
|
303
318
|
if (!(v.config instanceof HttpMiddlewareConfig)) return false;
|
|
304
319
|
|
|
305
|
-
if (v.config.controllers.length && !v.config.controllers.includes(routeConfig.action.controller)) {
|
|
320
|
+
if (v.config.controllers.length && routeConfig.action.type === 'controller' && !v.config.controllers.includes(routeConfig.action.controller)) {
|
|
306
321
|
return false;
|
|
307
322
|
}
|
|
308
323
|
|
|
309
|
-
if (v.config.excludeControllers.length && v.config.excludeControllers.includes(routeConfig.action.controller)) {
|
|
324
|
+
if (v.config.excludeControllers.length && routeConfig.action.type === 'controller' && v.config.excludeControllers.includes(routeConfig.action.controller)) {
|
|
310
325
|
return false;
|
|
311
326
|
}
|
|
312
327
|
|
|
@@ -320,14 +335,20 @@ function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry
|
|
|
320
335
|
if (v.config.selfModule && v.module.id !== routeConfig.module?.id) return false;
|
|
321
336
|
|
|
322
337
|
if (v.config.routeNames.length) {
|
|
338
|
+
let found: boolean = false;
|
|
323
339
|
for (const name of v.config.routeNames) {
|
|
324
340
|
if (name.includes('*')) {
|
|
325
341
|
const regex = new RegExp('^' + name.replace(/\*/g, '.*') + '$');
|
|
326
|
-
if (
|
|
327
|
-
|
|
328
|
-
|
|
342
|
+
if (regex.test(routeConfig.name)) {
|
|
343
|
+
found = true;
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
} else if (name === routeConfig.name) {
|
|
347
|
+
found = true;
|
|
348
|
+
break;
|
|
329
349
|
}
|
|
330
350
|
}
|
|
351
|
+
if (!found) return false;
|
|
331
352
|
}
|
|
332
353
|
|
|
333
354
|
if (v.config.excludeRouteNames.length) {
|
|
@@ -366,17 +387,236 @@ function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry
|
|
|
366
387
|
return middlewareConfigs;
|
|
367
388
|
}
|
|
368
389
|
|
|
369
|
-
export
|
|
370
|
-
|
|
371
|
-
|
|
390
|
+
export interface HttpRouterFunctionOptions {
|
|
391
|
+
path: string;
|
|
392
|
+
name?: string;
|
|
393
|
+
methods?: string[];
|
|
394
|
+
description?: string;
|
|
395
|
+
category?: string;
|
|
396
|
+
groups?: string[];
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* An arbitrary data container the user can use to store app specific settings/values.
|
|
400
|
+
*/
|
|
401
|
+
data?: Record<any, any>;
|
|
402
|
+
|
|
403
|
+
baseUrl?: string;
|
|
404
|
+
middlewares?: (() => HttpMiddlewareConfig)[];
|
|
405
|
+
|
|
406
|
+
serializer?: Serializer;
|
|
407
|
+
serializationOptions?: SerializationOptions;
|
|
408
|
+
|
|
409
|
+
resolverForToken?: Map<any, ClassType>;
|
|
410
|
+
resolverForParameterName?: Map<string, ClassType>;
|
|
411
|
+
|
|
412
|
+
responses?: { statusCode: number, description: string, type?: Type }[];
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function convertOptions(methods: string[], pathOrOptions: string | HttpRouterFunctionOptions, defaultOptions: Partial<HttpRouterFunctionOptions>): HttpRouterFunctionOptions {
|
|
416
|
+
const options = 'string' === typeof pathOrOptions ? { path: pathOrOptions } : pathOrOptions;
|
|
417
|
+
if (options.methods) return options;
|
|
418
|
+
return { ...options, methods };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export abstract class HttpRouterRegistryFunctionRegistrar {
|
|
422
|
+
protected defaultOptions: Partial<HttpRouterFunctionOptions> = {};
|
|
423
|
+
|
|
424
|
+
abstract addRoute(routeConfig: RouteConfig): void;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Returns a new registrar object with default options that apply to each registered route through this registrar.
|
|
428
|
+
*
|
|
429
|
+
* ```typescript
|
|
430
|
+
* const registry: HttpRouterRegistry = ...;
|
|
431
|
+
*
|
|
432
|
+
* const secretRegistry = registry.forOptions({groups: ['secret']});
|
|
433
|
+
*
|
|
434
|
+
* secretRegistry.get('/admin/groups', () => {
|
|
435
|
+
* });
|
|
436
|
+
*
|
|
437
|
+
* secretRegistry.get('/admin/users', () => {
|
|
438
|
+
* });
|
|
439
|
+
* ```
|
|
440
|
+
*/
|
|
441
|
+
forOptions(options: Partial<HttpRouterFunctionOptions>): HttpRouterRegistryFunctionRegistrar {
|
|
442
|
+
const that = this;
|
|
443
|
+
return new class extends HttpRouterRegistryFunctionRegistrar {
|
|
444
|
+
defaultOptions = options;
|
|
445
|
+
|
|
446
|
+
addRoute(routeConfig: RouteConfig) {
|
|
447
|
+
that.addRoute(routeConfig);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
public any(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
453
|
+
this.register(convertOptions([], pathOrOptions, this.defaultOptions), callback);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
public add(decorator: HttpDecorator, callback: (...args: any[]) => any) {
|
|
457
|
+
const data = decorator(Object, '_');
|
|
458
|
+
const action = isArray(data) ? data.find(v => v instanceof HttpAction) : undefined;
|
|
459
|
+
if (!action) throw new Error('No HttpAction available');
|
|
460
|
+
|
|
461
|
+
const fn = ReflectionFunction.from(callback);
|
|
462
|
+
const routeConfig = createRouteConfigFromHttpAction({
|
|
463
|
+
type: 'function',
|
|
464
|
+
fn: callback,
|
|
465
|
+
}, action);
|
|
466
|
+
routeConfig.returnType = fn.getReturnType();
|
|
467
|
+
this.addRoute(routeConfig);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
public get(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
471
|
+
this.register(convertOptions(['GET'], pathOrOptions, this.defaultOptions), callback);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
public post(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
475
|
+
this.register(convertOptions(['POST'], pathOrOptions, this.defaultOptions), callback);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
public put(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
479
|
+
this.register(convertOptions(['PUT'], pathOrOptions, this.defaultOptions), callback);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
public patch(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
483
|
+
this.register(convertOptions(['PATCH'], pathOrOptions, this.defaultOptions), callback);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
public delete(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
487
|
+
this.register(convertOptions(['DELETE'], pathOrOptions, this.defaultOptions), callback);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
public options(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
491
|
+
this.register(convertOptions(['OPTIONS'], pathOrOptions, this.defaultOptions), callback);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
public trace(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
495
|
+
this.register(convertOptions(['TRACE'], pathOrOptions, this.defaultOptions), callback);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
public head(pathOrOptions: string | HttpRouterFunctionOptions, callback: (...args: any[]) => any) {
|
|
499
|
+
this.register(convertOptions(['HEAD'], pathOrOptions, this.defaultOptions), callback);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
private register(options: HttpRouterFunctionOptions, callback: (...args: any[]) => any, module?: InjectorModule<any>) {
|
|
503
|
+
const fn = ReflectionFunction.from(callback);
|
|
504
|
+
|
|
505
|
+
const routeConfig = new RouteConfig(options.name || '', options.methods || [], options.path, {
|
|
506
|
+
type: 'function',
|
|
507
|
+
fn: callback,
|
|
508
|
+
});
|
|
509
|
+
routeConfig.module = module;
|
|
510
|
+
if (options.responses) routeConfig.responses = options.responses;
|
|
511
|
+
if (options.description) routeConfig.description = options.description;
|
|
512
|
+
if (options.category) routeConfig.category = options.category;
|
|
513
|
+
if (options.groups) routeConfig.groups = options.groups;
|
|
514
|
+
if (options.data) routeConfig.data = new Map(Object.entries(options.data));
|
|
515
|
+
if (options.baseUrl) routeConfig.baseUrl = options.baseUrl;
|
|
516
|
+
if (options.middlewares) {
|
|
517
|
+
routeConfig.middlewares = options.middlewares.map(v => {
|
|
518
|
+
return { config: v(), module };
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (options.resolverForToken) {
|
|
523
|
+
for (const item of options.resolverForToken) routeConfig.resolverForToken.set(...item);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (options.resolverForToken) {
|
|
527
|
+
for (const item of options.resolverForToken) routeConfig.resolverForToken.set(...item);
|
|
528
|
+
}
|
|
372
529
|
|
|
530
|
+
if (options.resolverForParameterName) {
|
|
531
|
+
for (const item of options.resolverForParameterName) routeConfig.resolverForParameterName.set(...item);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
routeConfig.serializer = options.serializer;
|
|
535
|
+
routeConfig.serializationOptions = options.serializationOptions;
|
|
536
|
+
|
|
537
|
+
routeConfig.returnType = fn.getReturnType();
|
|
538
|
+
this.addRoute(routeConfig);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
function createRouteConfigFromHttpAction(routeAction: RouteClassControllerAction | RouteFunctionControllerAction, action: HttpAction, module?: InjectorModule<any>, controller?: HttpController) {
|
|
543
|
+
const routeConfig = new RouteConfig(action.name, action.httpMethods, action.path, routeAction);
|
|
544
|
+
routeConfig.responses = action.responses;
|
|
545
|
+
routeConfig.description = action.description;
|
|
546
|
+
routeConfig.category = action.category;
|
|
547
|
+
routeConfig.groups = action.groups;
|
|
548
|
+
routeConfig.data = new Map(action.data);
|
|
549
|
+
if (controller) {
|
|
550
|
+
routeConfig.baseUrl = controller.baseUrl;
|
|
551
|
+
|
|
552
|
+
routeConfig.middlewares = controller.middlewares.map(v => {
|
|
553
|
+
return { config: v(), module };
|
|
554
|
+
});
|
|
555
|
+
routeConfig.resolverForToken = new Map(controller.resolverForToken);
|
|
556
|
+
routeConfig.resolverForParameterName = new Map(controller.resolverForParameterName);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
routeConfig.middlewares.push(...action.middlewares.map(v => {
|
|
560
|
+
return { config: v(), module };
|
|
561
|
+
}));
|
|
562
|
+
|
|
563
|
+
for (const item of action.resolverForToken) routeConfig.resolverForToken.set(...item);
|
|
564
|
+
|
|
565
|
+
for (const item of action.resolverForParameterName) routeConfig.resolverForParameterName.set(...item);
|
|
566
|
+
|
|
567
|
+
routeConfig.serializer = action.serializer;
|
|
568
|
+
routeConfig.serializationOptions = action.serializationOptions;
|
|
569
|
+
return routeConfig;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export class HttpRouterRegistry extends HttpRouterRegistryFunctionRegistrar {
|
|
373
573
|
protected routes: RouteConfig[] = [];
|
|
574
|
+
private buildId: number = 1;
|
|
575
|
+
|
|
576
|
+
public getBuildId(): number {
|
|
577
|
+
return this.buildId;
|
|
578
|
+
}
|
|
374
579
|
|
|
375
|
-
|
|
580
|
+
public getRoutes(): RouteConfig[] {
|
|
581
|
+
return this.routes;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
public addRouteForController(controller: ClassType, module: InjectorModule<any>) {
|
|
585
|
+
const controllerData = httpClass._fetch(controller);
|
|
586
|
+
if (!controllerData) throw new Error(`Http controller class ${getClassName(controller)} has no @http.controller decorator.`);
|
|
587
|
+
const schema = ReflectionClass.from(controller);
|
|
588
|
+
|
|
589
|
+
for (const action of controllerData.getActions()) {
|
|
590
|
+
const routeAction: RouteClassControllerAction = {
|
|
591
|
+
type: 'controller',
|
|
592
|
+
controller,
|
|
593
|
+
module,
|
|
594
|
+
methodName: action.methodName
|
|
595
|
+
};
|
|
596
|
+
const routeConfig = createRouteConfigFromHttpAction(routeAction, action, module, controllerData);
|
|
597
|
+
|
|
598
|
+
routeConfig.module = module;
|
|
599
|
+
|
|
600
|
+
if (schema.hasMethod(action.methodName)) routeConfig.returnType = schema.getMethod(action.methodName).getReturnType();
|
|
601
|
+
this.addRoute(routeConfig);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
public addRoute(routeConfig: RouteConfig) {
|
|
606
|
+
this.routes.push(routeConfig);
|
|
607
|
+
this.buildId++;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
export class HttpRouter {
|
|
612
|
+
protected fn?: (request: HttpRequest) => ResolvedController | undefined;
|
|
613
|
+
protected buildId: number = 0;
|
|
614
|
+
protected resolveFn?: (name: string, parameters: { [name: string]: any }) => string;
|
|
615
|
+
|
|
616
|
+
private parseBody(req: HttpRequest, foundFiles: { [name: string]: UploadedFile }) {
|
|
376
617
|
const form = formidable({
|
|
377
618
|
multiples: true,
|
|
378
619
|
hash: 'sha1',
|
|
379
|
-
enabledPlugins: ['octetstream', 'querystring', 'json'],
|
|
380
620
|
});
|
|
381
621
|
return asyncOperation((resolve, reject) => {
|
|
382
622
|
if (req.body) {
|
|
@@ -386,7 +626,17 @@ export class Router {
|
|
|
386
626
|
if (err) {
|
|
387
627
|
reject(err);
|
|
388
628
|
} else {
|
|
389
|
-
const
|
|
629
|
+
for (const [name, file] of Object.entries(files) as any) {
|
|
630
|
+
if (file.size === 0) continue;
|
|
631
|
+
foundFiles[name] = {
|
|
632
|
+
size: file.size,
|
|
633
|
+
path: file.filepath,
|
|
634
|
+
name: file.originalFilename,
|
|
635
|
+
type: file.mimetype,
|
|
636
|
+
lastModifiedDate: file.lastModifiedDate,
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const body = req.body = { ...fields, ...foundFiles };
|
|
390
640
|
resolve(body);
|
|
391
641
|
}
|
|
392
642
|
});
|
|
@@ -398,6 +648,7 @@ export class Router {
|
|
|
398
648
|
private logger: LoggerInterface,
|
|
399
649
|
tagRegistry: TagRegistry,
|
|
400
650
|
private middlewareRegistry: MiddlewareRegistry = new MiddlewareRegistry,
|
|
651
|
+
private registry: HttpRouterRegistry = new HttpRouterRegistry,
|
|
401
652
|
) {
|
|
402
653
|
for (const controller of controllers.controllers) {
|
|
403
654
|
this.addRouteForController(controller.controller, controller.module);
|
|
@@ -405,7 +656,7 @@ export class Router {
|
|
|
405
656
|
}
|
|
406
657
|
|
|
407
658
|
getRoutes(): RouteConfig[] {
|
|
408
|
-
return this.
|
|
659
|
+
return this.registry.getRoutes();
|
|
409
660
|
}
|
|
410
661
|
|
|
411
662
|
static forControllers(
|
|
@@ -413,7 +664,7 @@ export class Router {
|
|
|
413
664
|
tagRegistry: TagRegistry = new TagRegistry(),
|
|
414
665
|
middlewareRegistry: MiddlewareRegistry = new MiddlewareRegistry(),
|
|
415
666
|
module: InjectorModule<any> = new InjectorModule()
|
|
416
|
-
):
|
|
667
|
+
): HttpRouter {
|
|
417
668
|
return new this(new HttpControllers(controllers.map(v => {
|
|
418
669
|
return isClass(v) ? { controller: v, module } : v;
|
|
419
670
|
})), new Logger([], []), tagRegistry, middlewareRegistry);
|
|
@@ -516,7 +767,7 @@ export class Router {
|
|
|
516
767
|
${parameterResolverFoundVar} = true;
|
|
517
768
|
parameters.${parameter.parameter.name} = await ${instance}.resolve({
|
|
518
769
|
token: ${injectorTokenVar},
|
|
519
|
-
|
|
770
|
+
route: ${routeConfigVar},
|
|
520
771
|
request: request,
|
|
521
772
|
name: ${JSON.stringify(parameter.parameter.name)},
|
|
522
773
|
value: parameters.${parameter.parameter.name},
|
|
@@ -640,60 +891,22 @@ export class Router {
|
|
|
640
891
|
}
|
|
641
892
|
|
|
642
893
|
public addRoute(routeConfig: RouteConfig) {
|
|
643
|
-
this.
|
|
644
|
-
this.fn = undefined;
|
|
894
|
+
this.registry.addRoute(routeConfig);
|
|
645
895
|
}
|
|
646
896
|
|
|
647
897
|
public addRouteForController(controller: ClassType, module: InjectorModule<any>) {
|
|
648
|
-
|
|
649
|
-
if (!data) throw new Error(`Http controller class ${getClassName(controller)} has no @http.controller decorator.`);
|
|
650
|
-
const schema = ReflectionClass.from(controller);
|
|
651
|
-
|
|
652
|
-
for (const action of data.getActions()) {
|
|
653
|
-
const routeConfig = new RouteConfig(action.name, action.httpMethods, action.path, {
|
|
654
|
-
controller,
|
|
655
|
-
module,
|
|
656
|
-
methodName: action.methodName
|
|
657
|
-
});
|
|
658
|
-
routeConfig.module = module;
|
|
659
|
-
routeConfig.responses = action.responses;
|
|
660
|
-
routeConfig.description = action.description;
|
|
661
|
-
routeConfig.category = action.category;
|
|
662
|
-
routeConfig.groups = action.groups;
|
|
663
|
-
routeConfig.data = new Map(action.data);
|
|
664
|
-
routeConfig.baseUrl = data.baseUrl;
|
|
665
|
-
|
|
666
|
-
routeConfig.middlewares = data.middlewares.map(v => {
|
|
667
|
-
return { config: v(), module };
|
|
668
|
-
});
|
|
669
|
-
routeConfig.middlewares.push(...action.middlewares.map(v => {
|
|
670
|
-
return { config: v(), module };
|
|
671
|
-
}));
|
|
672
|
-
|
|
673
|
-
for (const item of action.resolverForToken) routeConfig.resolverForToken.set(...item);
|
|
674
|
-
|
|
675
|
-
routeConfig.resolverForToken = new Map(data.resolverForToken);
|
|
676
|
-
for (const item of action.resolverForToken) routeConfig.resolverForToken.set(...item);
|
|
677
|
-
|
|
678
|
-
routeConfig.resolverForParameterName = new Map(data.resolverForParameterName);
|
|
679
|
-
for (const item of action.resolverForParameterName) routeConfig.resolverForParameterName.set(...item);
|
|
680
|
-
|
|
681
|
-
routeConfig.serializationOptions = action.serializationOptions;
|
|
682
|
-
routeConfig.serializer = action.serializer;
|
|
683
|
-
routeConfig.serializer = action.serializer;
|
|
684
|
-
if (schema.hasMethod(action.methodName)) routeConfig.returnType = schema.getMethod(action.methodName).getReturnType();
|
|
685
|
-
this.addRoute(routeConfig);
|
|
686
|
-
}
|
|
898
|
+
this.registry.addRouteForController(controller, module);
|
|
687
899
|
}
|
|
688
900
|
|
|
689
901
|
protected build(): (request: HttpRequest) => ResolvedController | undefined {
|
|
902
|
+
this.buildId = this.registry.getBuildId();
|
|
690
903
|
const compiler = new CompilerContext;
|
|
691
904
|
compiler.context.set('ValidationError', ValidationError);
|
|
692
905
|
compiler.context.set('qs', qs);
|
|
693
906
|
|
|
694
907
|
const code: string[] = [];
|
|
695
908
|
|
|
696
|
-
for (const route of this.
|
|
909
|
+
for (const route of this.getRoutes()) {
|
|
697
910
|
code.push(this.getRouteCode(compiler, route));
|
|
698
911
|
}
|
|
699
912
|
|
|
@@ -713,7 +926,7 @@ export class Router {
|
|
|
713
926
|
const compiler = new CompilerContext;
|
|
714
927
|
const code: string[] = [];
|
|
715
928
|
|
|
716
|
-
for (const route of this.
|
|
929
|
+
for (const route of this.getRoutes()) {
|
|
717
930
|
code.push(this.getRouteUrlResolveCode(compiler, route));
|
|
718
931
|
}
|
|
719
932
|
|
|
@@ -734,7 +947,7 @@ export class Router {
|
|
|
734
947
|
}
|
|
735
948
|
|
|
736
949
|
public resolveRequest(request: HttpRequest): ResolvedController | undefined {
|
|
737
|
-
if (!this.fn) {
|
|
950
|
+
if (!this.fn || this.buildId !== this.registry.getBuildId()) {
|
|
738
951
|
this.fn = this.build();
|
|
739
952
|
}
|
|
740
953
|
|
package/src/static-serving.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { ClassType, urlJoin } from '@deepkit/core';
|
|
|
18
18
|
import { HttpRequest, HttpResponse } from './model';
|
|
19
19
|
import send from 'send';
|
|
20
20
|
import { eventDispatcher } from '@deepkit/event';
|
|
21
|
-
import { RouteConfig,
|
|
21
|
+
import { RouteConfig, HttpRouter } from './router';
|
|
22
22
|
|
|
23
23
|
export function serveStaticListener(module: AppModule<any>, path: string, localPath: string = path): ClassType {
|
|
24
24
|
class HttpRequestStaticServingListener {
|
|
@@ -45,6 +45,7 @@ export function serveStaticListener(module: AppModule<any>, path: string, localP
|
|
|
45
45
|
if (stat && stat.isFile()) {
|
|
46
46
|
event.routeFound(
|
|
47
47
|
new RouteConfig('static', ['GET'], event.url, {
|
|
48
|
+
type: 'controller',
|
|
48
49
|
controller: HttpRequestStaticServingListener,
|
|
49
50
|
module,
|
|
50
51
|
methodName: 'serve'
|
|
@@ -128,21 +129,23 @@ export function registerStaticHttpController(module: AppModule<any>, options: St
|
|
|
128
129
|
const path = normalizeDirectory(options.path);
|
|
129
130
|
|
|
130
131
|
const route1 = new RouteConfig('static', ['GET'], path, {
|
|
132
|
+
type: 'controller',
|
|
131
133
|
controller: StaticController,
|
|
132
134
|
module,
|
|
133
135
|
methodName: 'serveIndex'
|
|
134
136
|
});
|
|
135
137
|
route1.groups = groups;
|
|
136
|
-
module.setupGlobalProvider<
|
|
138
|
+
module.setupGlobalProvider<HttpRouter>().addRoute(route1);
|
|
137
139
|
|
|
138
140
|
if (path !== '/') {
|
|
139
141
|
const route2 = new RouteConfig('static', ['GET'], path.slice(0, -1), {
|
|
142
|
+
type: 'controller',
|
|
140
143
|
controller: StaticController,
|
|
141
144
|
module,
|
|
142
145
|
methodName: 'serveIndex'
|
|
143
146
|
});
|
|
144
147
|
route2.groups = groups;
|
|
145
|
-
module.setupGlobalProvider<
|
|
148
|
+
module.setupGlobalProvider<HttpRouter>().addRoute(route2);
|
|
146
149
|
}
|
|
147
150
|
|
|
148
151
|
module.addProvider(StaticController);
|