@deepkit/http 1.0.1-alpha.58 → 1.0.1-alpha.62
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/coverage/clover.xml +1086 -0
- package/coverage/coverage-final.json +12 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +79 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +126 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +170 -0
- package/coverage/lcov-report/src/controllers.ts.html +113 -0
- package/coverage/lcov-report/src/decorator.ts.html +1280 -0
- package/coverage/lcov-report/src/filter.ts.html +734 -0
- package/coverage/lcov-report/src/http.ts.html +2192 -0
- package/coverage/lcov-report/src/index.html +246 -0
- package/coverage/lcov-report/src/kernel.ts.html +260 -0
- package/coverage/lcov-report/src/middleware.ts.html +428 -0
- package/coverage/lcov-report/src/model.ts.html +937 -0
- package/coverage/lcov-report/src/module.config.ts.html +89 -0
- package/coverage/lcov-report/src/module.ts.html +245 -0
- package/coverage/lcov-report/src/router.ts.html +2318 -0
- package/coverage/lcov-report/tests/index.html +111 -0
- package/coverage/lcov-report/tests/utils.ts.html +176 -0
- package/coverage/lcov.info +2225 -0
- package/dist/cjs/src/controllers.d.ts +4 -4
- package/dist/cjs/src/controllers.js +3 -0
- package/dist/cjs/src/controllers.js.map +1 -1
- package/dist/cjs/src/decorator.d.ts +29 -94
- package/dist/cjs/src/decorator.js +53 -118
- package/dist/cjs/src/decorator.js.map +1 -1
- package/dist/cjs/src/filter.d.ts +1 -0
- package/dist/cjs/src/filter.js +11 -17
- package/dist/cjs/src/filter.js.map +1 -1
- package/dist/cjs/src/http.d.ts +5 -3
- package/dist/cjs/src/http.js +91 -55
- package/dist/cjs/src/http.js.map +1 -1
- package/dist/cjs/src/kernel.d.ts +3 -3
- package/dist/cjs/src/kernel.js +5 -25
- package/dist/cjs/src/kernel.js.map +1 -1
- package/dist/cjs/src/logger.d.ts +2 -2
- package/dist/cjs/src/logger.js +4 -8
- package/dist/cjs/src/logger.js.map +1 -1
- package/dist/cjs/src/middleware.d.ts +5 -1
- package/dist/cjs/src/middleware.js +15 -3
- package/dist/cjs/src/middleware.js.map +1 -1
- package/dist/cjs/src/model.d.ts +69 -2
- package/dist/cjs/src/model.js +96 -25
- package/dist/cjs/src/model.js.map +1 -1
- package/dist/cjs/src/module.config.d.ts +3 -4
- package/dist/cjs/src/module.config.js +8 -6
- package/dist/cjs/src/module.config.js.map +1 -1
- package/dist/cjs/src/module.d.ts +2 -3
- package/dist/cjs/src/module.js +4 -2
- package/dist/cjs/src/module.js.map +1 -1
- package/dist/cjs/src/router.d.ts +25 -54
- package/dist/cjs/src/router.js +157 -175
- package/dist/cjs/src/router.js.map +1 -1
- package/dist/cjs/src/static-serving.d.ts +1 -0
- package/dist/cjs/src/static-serving.js +38 -34
- package/dist/cjs/src/static-serving.js.map +1 -1
- package/dist/cjs/src/utils.js +1 -0
- package/dist/cjs/tests/filter.spec.js +61 -47
- package/dist/cjs/tests/filter.spec.js.map +1 -1
- package/dist/cjs/tests/middleware.spec.d.ts +1 -1
- package/dist/cjs/tests/middleware.spec.js +157 -145
- package/dist/cjs/tests/middleware.spec.js.map +1 -1
- package/dist/cjs/tests/model.spec.js +4 -4
- package/dist/cjs/tests/model.spec.js.map +1 -1
- package/dist/cjs/tests/module.spec.js +4 -3
- package/dist/cjs/tests/module.spec.js.map +1 -1
- package/dist/cjs/tests/router.spec.d.ts +1 -1
- package/dist/cjs/tests/router.spec.js +311 -322
- package/dist/cjs/tests/router.spec.js.map +1 -1
- package/dist/cjs/tests/utils.js +5 -2
- package/dist/cjs/tests/utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/src/controllers.d.ts +4 -4
- package/dist/esm/src/controllers.js +3 -0
- package/dist/esm/src/controllers.js.map +1 -1
- package/dist/esm/src/decorator.d.ts +29 -94
- package/dist/esm/src/decorator.js +49 -114
- package/dist/esm/src/decorator.js.map +1 -1
- package/dist/esm/src/filter.d.ts +1 -0
- package/dist/esm/src/filter.js +11 -18
- package/dist/esm/src/filter.js.map +1 -1
- package/dist/esm/src/http.d.ts +5 -3
- package/dist/esm/src/http.js +89 -55
- package/dist/esm/src/http.js.map +1 -1
- package/dist/esm/src/kernel.d.ts +3 -3
- package/dist/esm/src/kernel.js +6 -27
- package/dist/esm/src/kernel.js.map +1 -1
- package/dist/esm/src/logger.d.ts +2 -2
- package/dist/esm/src/logger.js +4 -9
- package/dist/esm/src/logger.js.map +1 -1
- package/dist/esm/src/middleware.d.ts +5 -1
- package/dist/esm/src/middleware.js +12 -0
- package/dist/esm/src/middleware.js.map +1 -1
- package/dist/esm/src/model.d.ts +69 -2
- package/dist/esm/src/model.js +92 -24
- package/dist/esm/src/model.js.map +1 -1
- package/dist/esm/src/module.config.d.ts +3 -4
- package/dist/esm/src/module.config.js +6 -5
- package/dist/esm/src/module.config.js.map +1 -1
- package/dist/esm/src/module.d.ts +2 -3
- package/dist/esm/src/module.js +5 -3
- package/dist/esm/src/module.js.map +1 -1
- package/dist/esm/src/router.d.ts +25 -54
- package/dist/esm/src/router.js +154 -172
- package/dist/esm/src/router.js.map +1 -1
- package/dist/esm/src/static-serving.d.ts +1 -0
- package/dist/esm/src/static-serving.js +29 -25
- package/dist/esm/src/static-serving.js.map +1 -1
- package/dist/esm/src/utils.js +1 -0
- package/dist/esm/tests/filter.spec.js +14 -0
- package/dist/esm/tests/filter.spec.js.map +1 -1
- package/dist/esm/tests/middleware.spec.d.ts +1 -1
- package/dist/esm/tests/middleware.spec.js +60 -48
- package/dist/esm/tests/middleware.spec.js.map +1 -1
- package/dist/esm/tests/module.spec.js +1 -0
- package/dist/esm/tests/module.spec.js.map +1 -1
- package/dist/esm/tests/router.spec.d.ts +1 -1
- package/dist/esm/tests/router.spec.js +184 -195
- package/dist/esm/tests/router.spec.js.map +1 -1
- package/dist/esm/tests/utils.js +4 -1
- package/dist/esm/tests/utils.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +14 -15
- package/src/controllers.ts +3 -3
- package/src/decorator.ts +42 -131
- package/src/filter.ts +0 -2
- package/src/http.ts +35 -36
- package/src/kernel.ts +4 -5
- package/src/logger.ts +2 -4
- package/src/middleware.ts +2 -1
- package/src/model.ts +86 -8
- package/src/module.config.ts +3 -6
- package/src/module.ts +2 -2
- package/src/router.ts +143 -183
- package/src/static-serving.ts +2 -4
- package/tests/middleware.spec.ts +0 -1
- package/tests/router.spec.ts +152 -131
- package/tsconfig.json +2 -1
package/src/module.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpListener, HttpResultFormatter, httpWorkflow } from './http';
|
|
2
|
-
import {
|
|
2
|
+
import { HttpConfig } from './module.config';
|
|
3
3
|
import { AppModule, createModule } from '@deepkit/app';
|
|
4
4
|
import { Router } from './router';
|
|
5
5
|
import { HttpKernel } from './kernel';
|
|
@@ -12,7 +12,7 @@ import { ClassType } from '@deepkit/core';
|
|
|
12
12
|
import { httpClass } from './decorator';
|
|
13
13
|
|
|
14
14
|
export class HttpModule extends createModule({
|
|
15
|
-
config:
|
|
15
|
+
config: HttpConfig,
|
|
16
16
|
providers: [
|
|
17
17
|
Router,
|
|
18
18
|
HttpKernel,
|
package/src/router.ts
CHANGED
|
@@ -7,31 +7,34 @@
|
|
|
7
7
|
*
|
|
8
8
|
* You should have received a copy of the MIT License along with this program.
|
|
9
9
|
*/
|
|
10
|
-
import '
|
|
11
|
-
import { asyncOperation, ClassType, CompilerContext, getClassName, isClass, urlJoin } from '@deepkit/core';
|
|
10
|
+
import { asyncOperation, ClassType, CompilerContext, getClassName, isClass, isObject, urlJoin } from '@deepkit/core';
|
|
12
11
|
import {
|
|
12
|
+
assertType,
|
|
13
13
|
entity,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
findMember,
|
|
15
|
+
getSerializeFunction,
|
|
16
|
+
getValidatorFunction,
|
|
17
|
+
metaAnnotation,
|
|
18
|
+
ReflectionClass,
|
|
19
|
+
ReflectionKind,
|
|
20
|
+
ReflectionParameter,
|
|
21
|
+
SerializationOptions,
|
|
22
|
+
serializer,
|
|
20
23
|
Serializer,
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
stringifyType,
|
|
25
|
+
Type,
|
|
26
|
+
typeToObject,
|
|
27
|
+
ValidationError
|
|
24
28
|
} from '@deepkit/type';
|
|
25
29
|
// @ts-ignore
|
|
26
30
|
import formidable from 'formidable';
|
|
27
|
-
import { IncomingMessage } from 'http';
|
|
28
31
|
import querystring from 'querystring';
|
|
29
32
|
import { httpClass } from './decorator';
|
|
30
|
-
import { HttpRequest, HttpRequestQuery, HttpRequestResolvedParameters } from './model';
|
|
31
|
-
import {
|
|
32
|
-
import { Logger } from '@deepkit/logger';
|
|
33
|
+
import { BodyValidationError, getRegExp, HttpRequest, HttpRequestQuery, HttpRequestResolvedParameters, ValidatedBody } from './model';
|
|
34
|
+
import { InjectorContext, InjectorModule, TagRegistry } from '@deepkit/injector';
|
|
35
|
+
import { Logger, LoggerInterface } from '@deepkit/logger';
|
|
33
36
|
import { HttpControllers } from './controllers';
|
|
34
|
-
import {
|
|
37
|
+
import { MiddlewareRegistry, MiddlewareRegistryEntry } from '@deepkit/app';
|
|
35
38
|
import { HttpMiddlewareConfig, HttpMiddlewareFn } from './middleware';
|
|
36
39
|
|
|
37
40
|
export type RouteParameterResolverForInjector = ((injector: InjectorContext) => any[] | Promise<any[]>);
|
|
@@ -48,50 +51,38 @@ export class UploadedFile {
|
|
|
48
51
|
/**
|
|
49
52
|
* The size of the uploaded file in bytes.
|
|
50
53
|
*/
|
|
51
|
-
|
|
54
|
+
size!: number;
|
|
52
55
|
|
|
53
56
|
/**
|
|
54
57
|
* The path this file is being written to.
|
|
55
58
|
*/
|
|
56
|
-
|
|
59
|
+
path!: string;
|
|
57
60
|
|
|
58
61
|
/**
|
|
59
62
|
* The name this file had according to the uploading client.
|
|
60
63
|
*/
|
|
61
|
-
|
|
64
|
+
name!: string | null;
|
|
62
65
|
|
|
63
66
|
/**
|
|
64
67
|
* The mime type of this file, according to the uploading client.
|
|
65
68
|
*/
|
|
66
|
-
|
|
69
|
+
type!: string | null;
|
|
67
70
|
|
|
68
71
|
/**
|
|
69
72
|
* A Date object (or `null`) containing the time this file was last written to.
|
|
70
73
|
* Mostly here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
|
|
71
74
|
*/
|
|
72
|
-
|
|
75
|
+
lastModifiedDate!: Date | null;
|
|
73
76
|
|
|
74
77
|
// /**
|
|
75
78
|
// * If `options.hash` calculation was set, you can read the hex digest out of this var.
|
|
76
79
|
// */
|
|
77
|
-
//
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function parseBody(form: any, req: IncomingMessage, files: { [name: string]: UploadedFile }) {
|
|
81
|
-
return asyncOperation((resolve, reject) => {
|
|
82
|
-
form.parse(req, (err: any, fields: any, files: any) => {
|
|
83
|
-
if (err) {
|
|
84
|
-
reject(err);
|
|
85
|
-
} else {
|
|
86
|
-
resolve({ fields, files });
|
|
87
|
-
}
|
|
88
|
-
});
|
|
89
|
-
});
|
|
80
|
+
// hash!: string | 'sha1' | 'md5' | 'sha256' | null;
|
|
90
81
|
}
|
|
91
82
|
|
|
92
83
|
export interface RouteControllerAction {
|
|
93
84
|
//if not set, the root module is used
|
|
94
|
-
module?:
|
|
85
|
+
module?: InjectorModule<any>;
|
|
95
86
|
controller: ClassType;
|
|
96
87
|
methodName: string;
|
|
97
88
|
}
|
|
@@ -100,43 +91,28 @@ function getRouterControllerActionName(action: RouteControllerAction): string {
|
|
|
100
91
|
return `${getClassName(action.controller)}.${action.methodName}`;
|
|
101
92
|
}
|
|
102
93
|
|
|
103
|
-
export interface RouteParameterConfig {
|
|
104
|
-
type?: 'body' | 'query' | 'queries';
|
|
105
|
-
/**
|
|
106
|
-
* undefined = propertyName, '' === root, else given path
|
|
107
|
-
*/
|
|
108
|
-
typePath?: string;
|
|
109
|
-
|
|
110
|
-
optional: boolean;
|
|
111
|
-
name: string;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
94
|
export class RouteConfig {
|
|
115
95
|
public baseUrl: string = '';
|
|
116
|
-
public parameterRegularExpressions: { [name: string]: any } = {};
|
|
117
96
|
|
|
118
|
-
public responses: { statusCode: number, description: string, type?:
|
|
97
|
+
public responses: { statusCode: number, description: string, type?: Type }[] = [];
|
|
119
98
|
|
|
120
99
|
public description: string = '';
|
|
121
100
|
public groups: string[] = [];
|
|
122
101
|
public category: string = '';
|
|
123
102
|
|
|
124
|
-
|
|
125
|
-
* This is set when the route action has a manual return type defined using @t.
|
|
126
|
-
*/
|
|
127
|
-
public returnSchema?: PropertySchema;
|
|
103
|
+
public returnType?: Type;
|
|
128
104
|
|
|
129
|
-
public serializationOptions?:
|
|
105
|
+
public serializationOptions?: SerializationOptions;
|
|
130
106
|
public serializer?: Serializer;
|
|
131
107
|
|
|
132
108
|
/**
|
|
133
109
|
* When assigned defines where this route came from.
|
|
134
110
|
*/
|
|
135
|
-
public module?:
|
|
111
|
+
public module?: InjectorModule<any>;
|
|
136
112
|
|
|
137
113
|
resolverForToken: Map<any, ClassType> = new Map();
|
|
138
114
|
|
|
139
|
-
middlewares: { config: HttpMiddlewareConfig, module:
|
|
115
|
+
middlewares: { config: HttpMiddlewareConfig, module: InjectorModule<any> }[] = [];
|
|
140
116
|
|
|
141
117
|
resolverForParameterName: Map<string, ClassType> = new Map();
|
|
142
118
|
|
|
@@ -145,10 +121,6 @@ export class RouteConfig {
|
|
|
145
121
|
*/
|
|
146
122
|
data = new Map<any, any>();
|
|
147
123
|
|
|
148
|
-
public parameters: {
|
|
149
|
-
[name: string]: RouteParameterConfig
|
|
150
|
-
} = {};
|
|
151
|
-
|
|
152
124
|
constructor(
|
|
153
125
|
public readonly name: string,
|
|
154
126
|
public readonly httpMethods: string[],
|
|
@@ -158,7 +130,7 @@ export class RouteConfig {
|
|
|
158
130
|
) {
|
|
159
131
|
}
|
|
160
132
|
|
|
161
|
-
getSchemaForResponse(statusCode: number):
|
|
133
|
+
getSchemaForResponse(statusCode: number): Type | undefined {
|
|
162
134
|
if (!this.responses.length) return;
|
|
163
135
|
for (const response of this.responses) {
|
|
164
136
|
if (response.statusCode === statusCode) return response.type;
|
|
@@ -173,40 +145,8 @@ export class RouteConfig {
|
|
|
173
145
|
}
|
|
174
146
|
}
|
|
175
147
|
|
|
176
|
-
/**
|
|
177
|
-
* When this class is injected into a route, then validation errors are not automatically thrown (using onHttpControllerValidationError event),
|
|
178
|
-
* but injected to the route itself. The user is then responsible to handle the errors.
|
|
179
|
-
*
|
|
180
|
-
* Note: The body parameter is still passed, however it might contain now invalid data. The BodyValidation tells what data is invalid.
|
|
181
|
-
*/
|
|
182
|
-
export class BodyValidation {
|
|
183
|
-
constructor(
|
|
184
|
-
public readonly errors: ValidationFailedItem[] = []
|
|
185
|
-
) {
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
hasErrors(prefix?: string): boolean {
|
|
189
|
-
return this.getErrors(prefix).length > 0;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
getErrors(prefix?: string): ValidationFailedItem[] {
|
|
193
|
-
if (prefix) return this.errors.filter(v => v.path.startsWith(prefix));
|
|
194
|
-
|
|
195
|
-
return this.errors;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
getErrorsForPath(path: string): ValidationFailedItem[] {
|
|
199
|
-
return this.errors.filter(v => v.path === path);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
getErrorMessageForPath(path: string): string {
|
|
203
|
-
return this.getErrorsForPath(path).map(v => v.message).join(', ');
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
148
|
class ParsedRoute {
|
|
208
149
|
public regex?: string;
|
|
209
|
-
public customValidationErrorHandling?: ParsedRouteParameter;
|
|
210
150
|
|
|
211
151
|
public pathParameterNames: { [name: string]: number } = {};
|
|
212
152
|
|
|
@@ -215,8 +155,8 @@ class ParsedRoute {
|
|
|
215
155
|
constructor(public routeConfig: RouteConfig) {
|
|
216
156
|
}
|
|
217
157
|
|
|
218
|
-
addParameter(property:
|
|
219
|
-
const parameter = new ParsedRouteParameter(property
|
|
158
|
+
addParameter(property: ReflectionParameter): ParsedRouteParameter {
|
|
159
|
+
const parameter = new ParsedRouteParameter(property);
|
|
220
160
|
this.parameters.push(parameter);
|
|
221
161
|
return parameter;
|
|
222
162
|
}
|
|
@@ -237,29 +177,46 @@ class ParsedRouteParameter {
|
|
|
237
177
|
regexPosition?: number;
|
|
238
178
|
|
|
239
179
|
constructor(
|
|
240
|
-
public
|
|
241
|
-
public config?: RouteParameterConfig,
|
|
180
|
+
public parameter: ReflectionParameter,
|
|
242
181
|
) {
|
|
243
182
|
}
|
|
244
183
|
|
|
245
184
|
get body() {
|
|
246
|
-
return
|
|
185
|
+
return metaAnnotation.getForName(this.parameter.type, 'httpBody') !== undefined;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
get bodyValidation() {
|
|
189
|
+
return metaAnnotation.getForName(this.parameter.type, 'httpBodyValidation') !== undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
getType(): Type {
|
|
193
|
+
if (this.bodyValidation) {
|
|
194
|
+
assertType(this.parameter.type, ReflectionKind.class);
|
|
195
|
+
const valueType = findMember('value', this.parameter.type);
|
|
196
|
+
if (!valueType || valueType.kind !== ReflectionKind.property) throw new Error(`No property value found at ${stringifyType(this.parameter.type)}`);
|
|
197
|
+
return valueType.type as Type;
|
|
198
|
+
}
|
|
199
|
+
return this.parameter.type;
|
|
247
200
|
}
|
|
248
201
|
|
|
249
202
|
get query() {
|
|
250
|
-
return
|
|
203
|
+
return metaAnnotation.getForName(this.parameter.type, 'httpQuery') !== undefined;
|
|
251
204
|
}
|
|
252
205
|
|
|
253
206
|
get queries() {
|
|
254
|
-
return
|
|
207
|
+
return metaAnnotation.getForName(this.parameter.type, 'httpQueries') !== undefined;
|
|
255
208
|
}
|
|
256
209
|
|
|
257
|
-
get typePath() {
|
|
258
|
-
|
|
210
|
+
get typePath(): string | undefined {
|
|
211
|
+
const typeOptions = metaAnnotation.getForName(this.parameter.type, 'httpQueries') || metaAnnotation.getForName(this.parameter.type, 'httpQuery');
|
|
212
|
+
if (!typeOptions) return;
|
|
213
|
+
const options = typeToObject(typeOptions[0]);
|
|
214
|
+
if (isObject(options)) return options.name;
|
|
215
|
+
return;
|
|
259
216
|
}
|
|
260
217
|
|
|
261
218
|
getName() {
|
|
262
|
-
return this.
|
|
219
|
+
return this.parameter.name;
|
|
263
220
|
}
|
|
264
221
|
|
|
265
222
|
isPartOfPath(): boolean {
|
|
@@ -271,34 +228,34 @@ function parseRoutePathToRegex(routeConfig: RouteConfig): { regex: string, param
|
|
|
271
228
|
const parameterNames: { [name: string]: number } = {};
|
|
272
229
|
let path = routeConfig.getFullPath();
|
|
273
230
|
|
|
231
|
+
const method = ReflectionClass.from(routeConfig.action.controller).getMethod(routeConfig.action.methodName);
|
|
232
|
+
|
|
274
233
|
let argumentIndex = 0;
|
|
275
234
|
path = path.replace(/:(\w+)/g, (a, name) => {
|
|
276
235
|
parameterNames[name] = argumentIndex;
|
|
277
236
|
argumentIndex++;
|
|
278
|
-
|
|
237
|
+
const parameter = method.getParameterOrUndefined(name);
|
|
238
|
+
if (parameter) {
|
|
239
|
+
const regExp = getRegExp(parameter.type);
|
|
240
|
+
if (regExp) return '(' + regExp + ')';
|
|
241
|
+
}
|
|
242
|
+
return String.raw`([^/]+)`;
|
|
279
243
|
});
|
|
280
244
|
|
|
281
245
|
return { regex: path, parameterNames };
|
|
282
246
|
}
|
|
283
247
|
|
|
284
248
|
export function parseRouteControllerAction(routeConfig: RouteConfig): ParsedRoute {
|
|
285
|
-
const schema =
|
|
249
|
+
const schema = ReflectionClass.from(routeConfig.action.controller);
|
|
286
250
|
const parsedRoute = new ParsedRoute(routeConfig);
|
|
287
251
|
|
|
288
|
-
const methodArgumentProperties = schema.
|
|
252
|
+
const methodArgumentProperties = schema.getMethodParameters(routeConfig.action.methodName);
|
|
289
253
|
const parsedPath = parseRoutePathToRegex(routeConfig);
|
|
290
254
|
parsedRoute.regex = parsedPath.regex;
|
|
291
255
|
parsedRoute.pathParameterNames = parsedPath.parameterNames;
|
|
292
256
|
|
|
293
257
|
for (const property of methodArgumentProperties) {
|
|
294
|
-
const
|
|
295
|
-
const parsedParameter = parsedRoute.addParameter(property, decoratorData);
|
|
296
|
-
|
|
297
|
-
if (decoratorData && decoratorData.optional) property.isOptional = true;
|
|
298
|
-
|
|
299
|
-
if (property.type === 'class' && property.classType === BodyValidation) {
|
|
300
|
-
parsedRoute.customValidationErrorHandling = parsedParameter;
|
|
301
|
-
}
|
|
258
|
+
const parsedParameter = parsedRoute.addParameter(property);
|
|
302
259
|
parsedParameter.regexPosition = parsedPath.parameterNames[property.name];
|
|
303
260
|
}
|
|
304
261
|
|
|
@@ -335,9 +292,9 @@ export interface RouteParameterResolverContext {
|
|
|
335
292
|
parameters: HttpRequestResolvedParameters;
|
|
336
293
|
}
|
|
337
294
|
|
|
338
|
-
function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry[], routeConfig: RouteConfig, fullPath: string): { config: HttpMiddlewareConfig, module:
|
|
295
|
+
function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry[], routeConfig: RouteConfig, fullPath: string): { config: HttpMiddlewareConfig, module: InjectorModule<any> }[] {
|
|
339
296
|
const middlewares = middlewareRawConfigs.slice(0);
|
|
340
|
-
middlewares.push(...routeConfig.middlewares);
|
|
297
|
+
middlewares.push(...routeConfig.middlewares as any[]);
|
|
341
298
|
|
|
342
299
|
const middlewareConfigs = middlewares.filter((v) => {
|
|
343
300
|
if (!(v.config instanceof HttpMiddlewareConfig)) return false;
|
|
@@ -397,7 +354,7 @@ function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry
|
|
|
397
354
|
}
|
|
398
355
|
|
|
399
356
|
return true;
|
|
400
|
-
}) as { config: HttpMiddlewareConfig, module:
|
|
357
|
+
}) as { config: HttpMiddlewareConfig, module: any }[];
|
|
401
358
|
|
|
402
359
|
middlewareConfigs.sort((a, b) => {
|
|
403
360
|
return a.config.order - b.config.order;
|
|
@@ -406,23 +363,36 @@ function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry
|
|
|
406
363
|
return middlewareConfigs;
|
|
407
364
|
}
|
|
408
365
|
|
|
409
|
-
@injectable
|
|
410
366
|
export class Router {
|
|
411
367
|
protected fn?: (request: HttpRequest) => ResolvedController | undefined;
|
|
412
368
|
protected resolveFn?: (name: string, parameters: { [name: string]: any }) => string;
|
|
413
369
|
|
|
414
370
|
protected routes: RouteConfig[] = [];
|
|
415
371
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
372
|
+
private parseBody(req: HttpRequest, files: { [name: string]: UploadedFile }) {
|
|
373
|
+
const form = formidable({
|
|
374
|
+
multiples: true,
|
|
375
|
+
hash: 'sha1',
|
|
376
|
+
enabledPlugins: ['octetstream', 'querystring', 'json'],
|
|
377
|
+
});
|
|
378
|
+
return asyncOperation((resolve, reject) => {
|
|
379
|
+
if (req.body) {
|
|
380
|
+
return resolve(req.body);
|
|
381
|
+
}
|
|
382
|
+
form.parse(req, (err: any, fields: any, files: any) => {
|
|
383
|
+
if (err) {
|
|
384
|
+
reject(err);
|
|
385
|
+
} else {
|
|
386
|
+
const body = req.body = { ...fields, ...files };
|
|
387
|
+
resolve(body);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
}
|
|
422
392
|
|
|
423
393
|
constructor(
|
|
424
394
|
controllers: HttpControllers,
|
|
425
|
-
private logger:
|
|
395
|
+
private logger: LoggerInterface,
|
|
426
396
|
tagRegistry: TagRegistry,
|
|
427
397
|
private middlewareRegistry: MiddlewareRegistry = new MiddlewareRegistry,
|
|
428
398
|
) {
|
|
@@ -436,10 +406,10 @@ export class Router {
|
|
|
436
406
|
}
|
|
437
407
|
|
|
438
408
|
static forControllers(
|
|
439
|
-
controllers: (ClassType | { module:
|
|
409
|
+
controllers: (ClassType | { module: InjectorModule<any>, controller: ClassType })[],
|
|
440
410
|
tagRegistry: TagRegistry = new TagRegistry(),
|
|
441
411
|
middlewareRegistry: MiddlewareRegistry = new MiddlewareRegistry(),
|
|
442
|
-
module:
|
|
412
|
+
module: InjectorModule<any> = new InjectorModule()
|
|
443
413
|
): Router {
|
|
444
414
|
return new this(new HttpControllers(controllers.map(v => {
|
|
445
415
|
return isClass(v) ? { controller: v, module } : v;
|
|
@@ -456,7 +426,7 @@ export class Router {
|
|
|
456
426
|
const setParameters: string[] = [];
|
|
457
427
|
const parameterNames: string[] = [];
|
|
458
428
|
const parameterValidator: string[] = [];
|
|
459
|
-
let bodyValidationErrorHandling = `if (bodyErrors.length) throw
|
|
429
|
+
let bodyValidationErrorHandling = `if (bodyErrors.length) throw ValidationError.from(bodyErrors);`;
|
|
460
430
|
|
|
461
431
|
let enableParseBody = false;
|
|
462
432
|
const hasParameters = parsedRoute.getParameters().length > 0;
|
|
@@ -467,52 +437,53 @@ export class Router {
|
|
|
467
437
|
const middlewareConfigs = filterMiddlewaresForRoute(this.middlewareRegistry.configs, routeConfig, fullPath);
|
|
468
438
|
|
|
469
439
|
for (const parameter of parsedRoute.getParameters()) {
|
|
470
|
-
if (
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
parameterNames.push(`parameters.${parameter.property.name}`);
|
|
475
|
-
} else if (parameter.body) {
|
|
476
|
-
const validatorVar = compiler.reserveVariable('argumentValidator', jitValidateProperty(parameter.property));
|
|
477
|
-
const converterVar = compiler.reserveVariable('argumentConverter', getPropertyXtoClassFunction(parameter.property, jsonSerializer));
|
|
440
|
+
if (parameter.body || parameter.bodyValidation) {
|
|
441
|
+
const type = parameter.getType();
|
|
442
|
+
const validatorVar = compiler.reserveVariable('argumentValidator', getValidatorFunction(undefined, type));
|
|
443
|
+
const converterVar = compiler.reserveVariable('argumentConverter', getSerializeFunction(type, serializer.deserializeRegistry));
|
|
478
444
|
|
|
479
445
|
enableParseBody = true;
|
|
480
|
-
setParameters.push(`parameters.${parameter.
|
|
481
|
-
parameterValidator.push(`${validatorVar}(parameters.${parameter.
|
|
482
|
-
|
|
446
|
+
setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(bodyFields, {loosely: true});`);
|
|
447
|
+
parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: bodyErrors});`);
|
|
448
|
+
if (parameter.bodyValidation) {
|
|
449
|
+
compiler.context.set('BodyValidation', ValidatedBody);
|
|
450
|
+
compiler.context.set('BodyValidationError', BodyValidationError);
|
|
451
|
+
parameterNames.push(`new BodyValidation(new BodyValidationError(bodyErrors), bodyErrors.length === 0 ? parameters.${parameter.parameter.name} : undefined)`);
|
|
452
|
+
bodyValidationErrorHandling = '';
|
|
453
|
+
} else {
|
|
454
|
+
parameterNames.push(`parameters.${parameter.parameter.name}`);
|
|
455
|
+
}
|
|
483
456
|
} else if (parameter.query || parameter.queries) {
|
|
484
|
-
const converted = parameter.
|
|
485
|
-
const validator =
|
|
457
|
+
const converted = getSerializeFunction(parameter.parameter.parameter, serializer.deserializeRegistry, undefined, parameter.getName());
|
|
458
|
+
const validator = getValidatorFunction(undefined, parameter.parameter.parameter,);
|
|
486
459
|
const converterVar = compiler.reserveVariable('argumentConverter', converted);
|
|
487
460
|
const validatorVar = compiler.reserveVariable('argumentValidator', validator);
|
|
488
461
|
|
|
489
|
-
const queryPath = parameter.typePath === undefined ? parameter.
|
|
462
|
+
const queryPath = parameter.typePath === undefined && parameter.query ? parameter.parameter.name : parameter.typePath;
|
|
490
463
|
const accessor = queryPath ? `['` + (queryPath.replace(/\./g, `']['`)) + `']` : '';
|
|
491
464
|
const queryAccessor = queryPath ? `_query${accessor}` : '_query';
|
|
492
465
|
|
|
493
|
-
setParameters.push(`parameters.${parameter.
|
|
494
|
-
parameterNames.push(`parameters.${parameter.
|
|
495
|
-
parameterValidator.push(`${validatorVar}(parameters.${parameter.
|
|
466
|
+
setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(${queryAccessor}, {loosely: true});`);
|
|
467
|
+
parameterNames.push(`parameters.${parameter.parameter.name}`);
|
|
468
|
+
parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: validationErrors}, ${JSON.stringify(parameter.typePath || parameter.getName())});`);
|
|
496
469
|
} else {
|
|
497
|
-
parameterNames.push(`parameters.${parameter.
|
|
470
|
+
parameterNames.push(`parameters.${parameter.parameter.name}`);
|
|
498
471
|
|
|
499
472
|
if (parameter.isPartOfPath()) {
|
|
500
|
-
if (parameter.
|
|
501
|
-
const converted = parameter.
|
|
473
|
+
if (parameter.parameter.type.kind !== ReflectionKind.class) {
|
|
474
|
+
const converted = getSerializeFunction(parameter.parameter.parameter, serializer.deserializeRegistry, undefined, parameter.getName());
|
|
502
475
|
const converterVar = compiler.reserveVariable('argumentConverter', converted);
|
|
503
|
-
setParameters.push(`parameters.${parameter.
|
|
476
|
+
setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(_match[${1 + (parameter.regexPosition || 0)}], {loosely: true});`);
|
|
504
477
|
|
|
505
|
-
const validator =
|
|
478
|
+
const validator = getValidatorFunction(undefined, parameter.parameter.parameter);
|
|
506
479
|
const validatorVar = compiler.reserveVariable('argumentValidator', validator);
|
|
507
|
-
parameterValidator.push(`${validatorVar}(parameters.${parameter.
|
|
480
|
+
parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: validationErrors}, ${JSON.stringify(parameter.getName())});`);
|
|
508
481
|
} else {
|
|
509
|
-
setParameters.push(`parameters.${parameter.
|
|
482
|
+
setParameters.push(`parameters.${parameter.parameter.name} = _match[${1 + (parameter.regexPosition || 0)}];`);
|
|
510
483
|
}
|
|
511
484
|
}
|
|
512
485
|
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
const injectorToken = injectorOptions && injectorOptions.token ? injectorOptions.token : (parameter.property.type === 'class' ? parameter.property.getResolvedClassType() : undefined);
|
|
486
|
+
const injectorToken = parameter.parameter.type.kind === ReflectionKind.class ? parameter.parameter.type.classType : undefined;
|
|
516
487
|
const injectorTokenVar = compiler.reserveVariable('classType', injectorToken);
|
|
517
488
|
const parameterResolverFoundVar = compiler.reserveVariable('parameterResolverFound', false);
|
|
518
489
|
|
|
@@ -540,29 +511,22 @@ export class Router {
|
|
|
540
511
|
${instance} = ${injector}.get(${resolverProvideTokenVar}${moduleVar});
|
|
541
512
|
if (!${parameterResolverFoundVar}) {
|
|
542
513
|
${parameterResolverFoundVar} = true;
|
|
543
|
-
parameters.${parameter.
|
|
514
|
+
parameters.${parameter.parameter.name} = await ${instance}.resolve({
|
|
544
515
|
token: ${injectorTokenVar},
|
|
545
516
|
routeConfig: ${routeConfigVar},
|
|
546
517
|
request: request,
|
|
547
|
-
name: ${JSON.stringify(parameter.
|
|
548
|
-
value: parameters.${parameter.
|
|
518
|
+
name: ${JSON.stringify(parameter.parameter.name)},
|
|
519
|
+
value: parameters.${parameter.parameter.name},
|
|
549
520
|
query: _query,
|
|
550
521
|
parameters: parameters
|
|
551
522
|
});
|
|
552
523
|
}`);
|
|
553
|
-
} else {
|
|
554
|
-
if (!parameter.isPartOfPath() && parameter.property.type !== 'class' && (!injectorOptions || !injectorOptions.token)) {
|
|
555
|
-
throw new Error(
|
|
556
|
-
`Route parameter '${parameter.property.name}' of ${getRouterControllerActionName(routeConfig.action)} is not a ClassType nor is a @inject(T) set. It can not be requested from the DI container like that.` +
|
|
557
|
-
`If its a query parameter use '@http.query() ${parameter.property.name}' or if its a body '@http.body() ${parameter.property.name}'.`
|
|
558
|
-
);
|
|
559
|
-
}
|
|
560
524
|
}
|
|
561
525
|
|
|
562
526
|
if (!parameter.isPartOfPath()) {
|
|
563
|
-
let injectorGet = `parameters.${parameter.
|
|
564
|
-
if (
|
|
565
|
-
injectorGet = `try {parameters.${parameter.
|
|
527
|
+
let injectorGet = `parameters.${parameter.parameter.name} = ${injector}.get(${injectorTokenVar});`;
|
|
528
|
+
if (parameter.parameter.isOptional()) {
|
|
529
|
+
injectorGet = `try {parameters.${parameter.parameter.name} = ${injector}.get(${injectorTokenVar}); } catch (e) {}`;
|
|
566
530
|
}
|
|
567
531
|
setParameters.push(`if (!${parameterResolverFoundVar}) ${injectorGet}`);
|
|
568
532
|
}
|
|
@@ -571,11 +535,9 @@ export class Router {
|
|
|
571
535
|
|
|
572
536
|
let parseBodyLoading = '';
|
|
573
537
|
if (enableParseBody) {
|
|
574
|
-
const parseBodyVar = compiler.reserveVariable('parseBody', parseBody);
|
|
575
|
-
const formVar = compiler.reserveVariable('form', this.form);
|
|
538
|
+
const parseBodyVar = compiler.reserveVariable('parseBody', this.parseBody.bind(this));
|
|
576
539
|
parseBodyLoading = `
|
|
577
|
-
const
|
|
578
|
-
const bodyFields = {...bodyParsed.fields, ...bodyParsed.files};`;
|
|
540
|
+
const bodyFields = (await ${parseBodyVar}(request, uploadedFiles));`;
|
|
579
541
|
requiresAsyncParameters = true;
|
|
580
542
|
}
|
|
581
543
|
|
|
@@ -588,7 +550,7 @@ export class Router {
|
|
|
588
550
|
if (middlewareConfigs.length) {
|
|
589
551
|
const middlewareItems: string[] = [];
|
|
590
552
|
for (const middlewareConfig of middlewareConfigs) {
|
|
591
|
-
const moduleVar = middlewareConfig.module ? ', ' + compiler.reserveVariable('module', middlewareConfig.module): '';
|
|
553
|
+
const moduleVar = middlewareConfig.module ? ', ' + compiler.reserveVariable('module', middlewareConfig.module) : '';
|
|
592
554
|
|
|
593
555
|
for (const middleware of middlewareConfig.config.middlewares) {
|
|
594
556
|
if (isClass(middleware)) {
|
|
@@ -618,7 +580,7 @@ export class Router {
|
|
|
618
580
|
${setParameters.join('\n')}
|
|
619
581
|
${parameterValidator.join('\n')}
|
|
620
582
|
${bodyValidationErrorHandling}
|
|
621
|
-
if (validationErrors.length) throw
|
|
583
|
+
if (validationErrors.length) throw new ValidationError(validationErrors);
|
|
622
584
|
return [${parameterNames.join(',')}];
|
|
623
585
|
}`;
|
|
624
586
|
}
|
|
@@ -649,10 +611,10 @@ export class Router {
|
|
|
649
611
|
const modify: string[] = [];
|
|
650
612
|
for (const parameter of parsedRoute.getParameters()) {
|
|
651
613
|
if (parameter.query || parameter.queries) {
|
|
652
|
-
const queryPath = parameter.typePath === undefined ? parameter.
|
|
614
|
+
const queryPath = parameter.typePath === undefined && parameter.query ? parameter.parameter.name : parameter.typePath || '';
|
|
653
615
|
|
|
654
|
-
if (parameter.
|
|
655
|
-
for (const property of parameter.
|
|
616
|
+
if (parameter.parameter.type.kind === ReflectionKind.class || parameter.parameter.type.kind === ReflectionKind.objectLiteral) {
|
|
617
|
+
for (const property of ReflectionClass.from(parameter.parameter.type).getProperties()) {
|
|
656
618
|
const accessor = `parameters.${parameter.getName()}?.${property.name}`;
|
|
657
619
|
const thisPath = queryPath ? queryPath + '.' + property.name : property.name;
|
|
658
620
|
modify.push(`${accessor} !== undefined && query.push(${JSON.stringify(dotToUrlPath(thisPath))} + '=' + encodeURIComponent(${accessor}))`);
|
|
@@ -679,10 +641,10 @@ export class Router {
|
|
|
679
641
|
this.fn = undefined;
|
|
680
642
|
}
|
|
681
643
|
|
|
682
|
-
public addRouteForController(controller: ClassType, module:
|
|
644
|
+
public addRouteForController(controller: ClassType, module: InjectorModule<any>) {
|
|
683
645
|
const data = httpClass._fetch(controller);
|
|
684
646
|
if (!data) throw new Error(`Http controller class ${getClassName(controller)} has no @http.controller decorator.`);
|
|
685
|
-
const schema =
|
|
647
|
+
const schema = ReflectionClass.from(controller);
|
|
686
648
|
|
|
687
649
|
for (const action of data.getActions()) {
|
|
688
650
|
const routeConfig = new RouteConfig(action.name, action.httpMethods, action.path, {
|
|
@@ -691,7 +653,6 @@ export class Router {
|
|
|
691
653
|
methodName: action.methodName
|
|
692
654
|
});
|
|
693
655
|
routeConfig.module = module;
|
|
694
|
-
routeConfig.parameterRegularExpressions = action.parameterRegularExpressions;
|
|
695
656
|
routeConfig.responses = action.responses;
|
|
696
657
|
routeConfig.description = action.description;
|
|
697
658
|
routeConfig.category = action.category;
|
|
@@ -714,18 +675,17 @@ export class Router {
|
|
|
714
675
|
routeConfig.resolverForParameterName = new Map(data.resolverForParameterName);
|
|
715
676
|
for (const item of action.resolverForParameterName) routeConfig.resolverForParameterName.set(...item);
|
|
716
677
|
|
|
717
|
-
routeConfig.parameters = { ...action.parameters };
|
|
718
678
|
routeConfig.serializationOptions = action.serializationOptions;
|
|
719
679
|
routeConfig.serializer = action.serializer;
|
|
720
680
|
routeConfig.serializer = action.serializer;
|
|
721
|
-
if (schema.hasMethod(action.methodName)) routeConfig.
|
|
681
|
+
if (schema.hasMethod(action.methodName)) routeConfig.returnType = schema.getMethod(action.methodName).getReturnType();
|
|
722
682
|
this.addRoute(routeConfig);
|
|
723
683
|
}
|
|
724
684
|
}
|
|
725
685
|
|
|
726
686
|
protected build(): (request: HttpRequest) => ResolvedController | undefined {
|
|
727
687
|
const compiler = new CompilerContext;
|
|
728
|
-
compiler.context.set('
|
|
688
|
+
compiler.context.set('ValidationError', ValidationError);
|
|
729
689
|
compiler.context.set('qs', require('qs'));
|
|
730
690
|
|
|
731
691
|
const code: string[] = [];
|
|
@@ -780,6 +740,6 @@ export class Router {
|
|
|
780
740
|
|
|
781
741
|
public resolve(method: string, url: string): ResolvedController | undefined {
|
|
782
742
|
method = method.toUpperCase();
|
|
783
|
-
return this.resolveRequest({url, method} as any);
|
|
743
|
+
return this.resolveRequest({ url, method } as any);
|
|
784
744
|
}
|
|
785
745
|
}
|