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