@deepkit/http 1.0.1-alpha.59 → 1.0.1-alpha.61
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 +60 -0
- package/dist/cjs/src/model.js +95 -24
- 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 +24 -53
- package/dist/cjs/src/router.js +140 -159
- 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 +298 -333
- 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 +60 -0
- package/dist/esm/src/model.js +91 -23
- 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 +24 -53
- package/dist/esm/src/router.js +136 -155
- 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 +167 -202
- 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 +77 -5
- package/src/module.config.ts +3 -6
- package/src/module.ts +2 -2
- package/src/router.ts +121 -161
- package/src/static-serving.ts +2 -4
- package/tests/middleware.spec.ts +0 -1
- package/tests/router.spec.ts +131 -133
- package/tsconfig.json +2 -1
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,38 +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
|
-
//
|
|
80
|
+
// hash!: string | 'sha1' | 'md5' | 'sha256' | null;
|
|
78
81
|
}
|
|
79
82
|
|
|
80
83
|
export interface RouteControllerAction {
|
|
81
84
|
//if not set, the root module is used
|
|
82
|
-
module?:
|
|
85
|
+
module?: InjectorModule<any>;
|
|
83
86
|
controller: ClassType;
|
|
84
87
|
methodName: string;
|
|
85
88
|
}
|
|
@@ -88,43 +91,28 @@ function getRouterControllerActionName(action: RouteControllerAction): string {
|
|
|
88
91
|
return `${getClassName(action.controller)}.${action.methodName}`;
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
export interface RouteParameterConfig {
|
|
92
|
-
type?: 'body' | 'query' | 'queries';
|
|
93
|
-
/**
|
|
94
|
-
* undefined = propertyName, '' === root, else given path
|
|
95
|
-
*/
|
|
96
|
-
typePath?: string;
|
|
97
|
-
|
|
98
|
-
optional: boolean;
|
|
99
|
-
name: string;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
94
|
export class RouteConfig {
|
|
103
95
|
public baseUrl: string = '';
|
|
104
|
-
public parameterRegularExpressions: { [name: string]: any } = {};
|
|
105
96
|
|
|
106
|
-
public responses: { statusCode: number, description: string, type?:
|
|
97
|
+
public responses: { statusCode: number, description: string, type?: Type }[] = [];
|
|
107
98
|
|
|
108
99
|
public description: string = '';
|
|
109
100
|
public groups: string[] = [];
|
|
110
101
|
public category: string = '';
|
|
111
102
|
|
|
112
|
-
|
|
113
|
-
* This is set when the route action has a manual return type defined using @t.
|
|
114
|
-
*/
|
|
115
|
-
public returnSchema?: PropertySchema;
|
|
103
|
+
public returnType?: Type;
|
|
116
104
|
|
|
117
|
-
public serializationOptions?:
|
|
105
|
+
public serializationOptions?: SerializationOptions;
|
|
118
106
|
public serializer?: Serializer;
|
|
119
107
|
|
|
120
108
|
/**
|
|
121
109
|
* When assigned defines where this route came from.
|
|
122
110
|
*/
|
|
123
|
-
public module?:
|
|
111
|
+
public module?: InjectorModule<any>;
|
|
124
112
|
|
|
125
113
|
resolverForToken: Map<any, ClassType> = new Map();
|
|
126
114
|
|
|
127
|
-
middlewares: { config: HttpMiddlewareConfig, module:
|
|
115
|
+
middlewares: { config: HttpMiddlewareConfig, module: InjectorModule<any> }[] = [];
|
|
128
116
|
|
|
129
117
|
resolverForParameterName: Map<string, ClassType> = new Map();
|
|
130
118
|
|
|
@@ -133,10 +121,6 @@ export class RouteConfig {
|
|
|
133
121
|
*/
|
|
134
122
|
data = new Map<any, any>();
|
|
135
123
|
|
|
136
|
-
public parameters: {
|
|
137
|
-
[name: string]: RouteParameterConfig
|
|
138
|
-
} = {};
|
|
139
|
-
|
|
140
124
|
constructor(
|
|
141
125
|
public readonly name: string,
|
|
142
126
|
public readonly httpMethods: string[],
|
|
@@ -146,7 +130,7 @@ export class RouteConfig {
|
|
|
146
130
|
) {
|
|
147
131
|
}
|
|
148
132
|
|
|
149
|
-
getSchemaForResponse(statusCode: number):
|
|
133
|
+
getSchemaForResponse(statusCode: number): Type | undefined {
|
|
150
134
|
if (!this.responses.length) return;
|
|
151
135
|
for (const response of this.responses) {
|
|
152
136
|
if (response.statusCode === statusCode) return response.type;
|
|
@@ -161,40 +145,8 @@ export class RouteConfig {
|
|
|
161
145
|
}
|
|
162
146
|
}
|
|
163
147
|
|
|
164
|
-
/**
|
|
165
|
-
* When this class is injected into a route, then validation errors are not automatically thrown (using onHttpControllerValidationError event),
|
|
166
|
-
* but injected to the route itself. The user is then responsible to handle the errors.
|
|
167
|
-
*
|
|
168
|
-
* Note: The body parameter is still passed, however it might contain now invalid data. The BodyValidation tells what data is invalid.
|
|
169
|
-
*/
|
|
170
|
-
export class BodyValidation {
|
|
171
|
-
constructor(
|
|
172
|
-
public readonly errors: ValidationFailedItem[] = []
|
|
173
|
-
) {
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
hasErrors(prefix?: string): boolean {
|
|
177
|
-
return this.getErrors(prefix).length > 0;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
getErrors(prefix?: string): ValidationFailedItem[] {
|
|
181
|
-
if (prefix) return this.errors.filter(v => v.path.startsWith(prefix));
|
|
182
|
-
|
|
183
|
-
return this.errors;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
getErrorsForPath(path: string): ValidationFailedItem[] {
|
|
187
|
-
return this.errors.filter(v => v.path === path);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
getErrorMessageForPath(path: string): string {
|
|
191
|
-
return this.getErrorsForPath(path).map(v => v.message).join(', ');
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
148
|
class ParsedRoute {
|
|
196
149
|
public regex?: string;
|
|
197
|
-
public customValidationErrorHandling?: ParsedRouteParameter;
|
|
198
150
|
|
|
199
151
|
public pathParameterNames: { [name: string]: number } = {};
|
|
200
152
|
|
|
@@ -203,8 +155,8 @@ class ParsedRoute {
|
|
|
203
155
|
constructor(public routeConfig: RouteConfig) {
|
|
204
156
|
}
|
|
205
157
|
|
|
206
|
-
addParameter(property:
|
|
207
|
-
const parameter = new ParsedRouteParameter(property
|
|
158
|
+
addParameter(property: ReflectionParameter): ParsedRouteParameter {
|
|
159
|
+
const parameter = new ParsedRouteParameter(property);
|
|
208
160
|
this.parameters.push(parameter);
|
|
209
161
|
return parameter;
|
|
210
162
|
}
|
|
@@ -225,29 +177,46 @@ class ParsedRouteParameter {
|
|
|
225
177
|
regexPosition?: number;
|
|
226
178
|
|
|
227
179
|
constructor(
|
|
228
|
-
public
|
|
229
|
-
public config?: RouteParameterConfig,
|
|
180
|
+
public parameter: ReflectionParameter,
|
|
230
181
|
) {
|
|
231
182
|
}
|
|
232
183
|
|
|
233
184
|
get body() {
|
|
234
|
-
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;
|
|
235
200
|
}
|
|
236
201
|
|
|
237
202
|
get query() {
|
|
238
|
-
return
|
|
203
|
+
return metaAnnotation.getForName(this.parameter.type, 'httpQuery') !== undefined;
|
|
239
204
|
}
|
|
240
205
|
|
|
241
206
|
get queries() {
|
|
242
|
-
return
|
|
207
|
+
return metaAnnotation.getForName(this.parameter.type, 'httpQueries') !== undefined;
|
|
243
208
|
}
|
|
244
209
|
|
|
245
|
-
get typePath() {
|
|
246
|
-
|
|
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;
|
|
247
216
|
}
|
|
248
217
|
|
|
249
218
|
getName() {
|
|
250
|
-
return this.
|
|
219
|
+
return this.parameter.name;
|
|
251
220
|
}
|
|
252
221
|
|
|
253
222
|
isPartOfPath(): boolean {
|
|
@@ -259,34 +228,34 @@ function parseRoutePathToRegex(routeConfig: RouteConfig): { regex: string, param
|
|
|
259
228
|
const parameterNames: { [name: string]: number } = {};
|
|
260
229
|
let path = routeConfig.getFullPath();
|
|
261
230
|
|
|
231
|
+
const method = ReflectionClass.from(routeConfig.action.controller).getMethod(routeConfig.action.methodName);
|
|
232
|
+
|
|
262
233
|
let argumentIndex = 0;
|
|
263
234
|
path = path.replace(/:(\w+)/g, (a, name) => {
|
|
264
235
|
parameterNames[name] = argumentIndex;
|
|
265
236
|
argumentIndex++;
|
|
266
|
-
|
|
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`([^/]+)`;
|
|
267
243
|
});
|
|
268
244
|
|
|
269
245
|
return { regex: path, parameterNames };
|
|
270
246
|
}
|
|
271
247
|
|
|
272
248
|
export function parseRouteControllerAction(routeConfig: RouteConfig): ParsedRoute {
|
|
273
|
-
const schema =
|
|
249
|
+
const schema = ReflectionClass.from(routeConfig.action.controller);
|
|
274
250
|
const parsedRoute = new ParsedRoute(routeConfig);
|
|
275
251
|
|
|
276
|
-
const methodArgumentProperties = schema.
|
|
252
|
+
const methodArgumentProperties = schema.getMethodParameters(routeConfig.action.methodName);
|
|
277
253
|
const parsedPath = parseRoutePathToRegex(routeConfig);
|
|
278
254
|
parsedRoute.regex = parsedPath.regex;
|
|
279
255
|
parsedRoute.pathParameterNames = parsedPath.parameterNames;
|
|
280
256
|
|
|
281
257
|
for (const property of methodArgumentProperties) {
|
|
282
|
-
const
|
|
283
|
-
const parsedParameter = parsedRoute.addParameter(property, decoratorData);
|
|
284
|
-
|
|
285
|
-
if (decoratorData && decoratorData.optional) property.isOptional = true;
|
|
286
|
-
|
|
287
|
-
if (property.type === 'class' && property.classType === BodyValidation) {
|
|
288
|
-
parsedRoute.customValidationErrorHandling = parsedParameter;
|
|
289
|
-
}
|
|
258
|
+
const parsedParameter = parsedRoute.addParameter(property);
|
|
290
259
|
parsedParameter.regexPosition = parsedPath.parameterNames[property.name];
|
|
291
260
|
}
|
|
292
261
|
|
|
@@ -323,9 +292,9 @@ export interface RouteParameterResolverContext {
|
|
|
323
292
|
parameters: HttpRequestResolvedParameters;
|
|
324
293
|
}
|
|
325
294
|
|
|
326
|
-
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> }[] {
|
|
327
296
|
const middlewares = middlewareRawConfigs.slice(0);
|
|
328
|
-
middlewares.push(...routeConfig.middlewares);
|
|
297
|
+
middlewares.push(...routeConfig.middlewares as any[]);
|
|
329
298
|
|
|
330
299
|
const middlewareConfigs = middlewares.filter((v) => {
|
|
331
300
|
if (!(v.config instanceof HttpMiddlewareConfig)) return false;
|
|
@@ -385,7 +354,7 @@ function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry
|
|
|
385
354
|
}
|
|
386
355
|
|
|
387
356
|
return true;
|
|
388
|
-
}) as { config: HttpMiddlewareConfig, module:
|
|
357
|
+
}) as { config: HttpMiddlewareConfig, module: any }[];
|
|
389
358
|
|
|
390
359
|
middlewareConfigs.sort((a, b) => {
|
|
391
360
|
return a.config.order - b.config.order;
|
|
@@ -394,7 +363,6 @@ function filterMiddlewaresForRoute(middlewareRawConfigs: MiddlewareRegistryEntry
|
|
|
394
363
|
return middlewareConfigs;
|
|
395
364
|
}
|
|
396
365
|
|
|
397
|
-
@injectable
|
|
398
366
|
export class Router {
|
|
399
367
|
protected fn?: (request: HttpRequest) => ResolvedController | undefined;
|
|
400
368
|
protected resolveFn?: (name: string, parameters: { [name: string]: any }) => string;
|
|
@@ -424,7 +392,7 @@ export class Router {
|
|
|
424
392
|
|
|
425
393
|
constructor(
|
|
426
394
|
controllers: HttpControllers,
|
|
427
|
-
private logger:
|
|
395
|
+
private logger: LoggerInterface,
|
|
428
396
|
tagRegistry: TagRegistry,
|
|
429
397
|
private middlewareRegistry: MiddlewareRegistry = new MiddlewareRegistry,
|
|
430
398
|
) {
|
|
@@ -438,10 +406,10 @@ export class Router {
|
|
|
438
406
|
}
|
|
439
407
|
|
|
440
408
|
static forControllers(
|
|
441
|
-
controllers: (ClassType | { module:
|
|
409
|
+
controllers: (ClassType | { module: InjectorModule<any>, controller: ClassType })[],
|
|
442
410
|
tagRegistry: TagRegistry = new TagRegistry(),
|
|
443
411
|
middlewareRegistry: MiddlewareRegistry = new MiddlewareRegistry(),
|
|
444
|
-
module:
|
|
412
|
+
module: InjectorModule<any> = new InjectorModule()
|
|
445
413
|
): Router {
|
|
446
414
|
return new this(new HttpControllers(controllers.map(v => {
|
|
447
415
|
return isClass(v) ? { controller: v, module } : v;
|
|
@@ -458,7 +426,7 @@ export class Router {
|
|
|
458
426
|
const setParameters: string[] = [];
|
|
459
427
|
const parameterNames: string[] = [];
|
|
460
428
|
const parameterValidator: string[] = [];
|
|
461
|
-
let bodyValidationErrorHandling = `if (bodyErrors.length) throw
|
|
429
|
+
let bodyValidationErrorHandling = `if (bodyErrors.length) throw ValidationError.from(bodyErrors);`;
|
|
462
430
|
|
|
463
431
|
let enableParseBody = false;
|
|
464
432
|
const hasParameters = parsedRoute.getParameters().length > 0;
|
|
@@ -469,52 +437,53 @@ export class Router {
|
|
|
469
437
|
const middlewareConfigs = filterMiddlewaresForRoute(this.middlewareRegistry.configs, routeConfig, fullPath);
|
|
470
438
|
|
|
471
439
|
for (const parameter of parsedRoute.getParameters()) {
|
|
472
|
-
if (
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
parameterNames.push(`parameters.${parameter.property.name}`);
|
|
477
|
-
} else if (parameter.body) {
|
|
478
|
-
const validatorVar = compiler.reserveVariable('argumentValidator', jitValidateProperty(parameter.property));
|
|
479
|
-
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));
|
|
480
444
|
|
|
481
445
|
enableParseBody = true;
|
|
482
|
-
setParameters.push(`parameters.${parameter.
|
|
483
|
-
parameterValidator.push(`${validatorVar}(parameters.${parameter.
|
|
484
|
-
|
|
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
|
+
}
|
|
485
456
|
} else if (parameter.query || parameter.queries) {
|
|
486
|
-
const converted = parameter.
|
|
487
|
-
const validator =
|
|
457
|
+
const converted = getSerializeFunction(parameter.parameter.parameter, serializer.deserializeRegistry, undefined, parameter.getName());
|
|
458
|
+
const validator = getValidatorFunction(undefined, parameter.parameter.parameter,);
|
|
488
459
|
const converterVar = compiler.reserveVariable('argumentConverter', converted);
|
|
489
460
|
const validatorVar = compiler.reserveVariable('argumentValidator', validator);
|
|
490
461
|
|
|
491
|
-
const queryPath = parameter.typePath === undefined ? parameter.
|
|
462
|
+
const queryPath = parameter.typePath === undefined && parameter.query ? parameter.parameter.name : parameter.typePath;
|
|
492
463
|
const accessor = queryPath ? `['` + (queryPath.replace(/\./g, `']['`)) + `']` : '';
|
|
493
464
|
const queryAccessor = queryPath ? `_query${accessor}` : '_query';
|
|
494
465
|
|
|
495
|
-
setParameters.push(`parameters.${parameter.
|
|
496
|
-
parameterNames.push(`parameters.${parameter.
|
|
497
|
-
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())});`);
|
|
498
469
|
} else {
|
|
499
|
-
parameterNames.push(`parameters.${parameter.
|
|
470
|
+
parameterNames.push(`parameters.${parameter.parameter.name}`);
|
|
500
471
|
|
|
501
472
|
if (parameter.isPartOfPath()) {
|
|
502
|
-
if (parameter.
|
|
503
|
-
const converted = parameter.
|
|
473
|
+
if (parameter.parameter.type.kind !== ReflectionKind.class) {
|
|
474
|
+
const converted = getSerializeFunction(parameter.parameter.parameter, serializer.deserializeRegistry, undefined, parameter.getName());
|
|
504
475
|
const converterVar = compiler.reserveVariable('argumentConverter', converted);
|
|
505
|
-
setParameters.push(`parameters.${parameter.
|
|
476
|
+
setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(_match[${1 + (parameter.regexPosition || 0)}], {loosely: true});`);
|
|
506
477
|
|
|
507
|
-
const validator =
|
|
478
|
+
const validator = getValidatorFunction(undefined, parameter.parameter.parameter);
|
|
508
479
|
const validatorVar = compiler.reserveVariable('argumentValidator', validator);
|
|
509
|
-
parameterValidator.push(`${validatorVar}(parameters.${parameter.
|
|
480
|
+
parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: validationErrors}, ${JSON.stringify(parameter.getName())});`);
|
|
510
481
|
} else {
|
|
511
|
-
setParameters.push(`parameters.${parameter.
|
|
482
|
+
setParameters.push(`parameters.${parameter.parameter.name} = _match[${1 + (parameter.regexPosition || 0)}];`);
|
|
512
483
|
}
|
|
513
484
|
}
|
|
514
485
|
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
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;
|
|
518
487
|
const injectorTokenVar = compiler.reserveVariable('classType', injectorToken);
|
|
519
488
|
const parameterResolverFoundVar = compiler.reserveVariable('parameterResolverFound', false);
|
|
520
489
|
|
|
@@ -542,29 +511,22 @@ export class Router {
|
|
|
542
511
|
${instance} = ${injector}.get(${resolverProvideTokenVar}${moduleVar});
|
|
543
512
|
if (!${parameterResolverFoundVar}) {
|
|
544
513
|
${parameterResolverFoundVar} = true;
|
|
545
|
-
parameters.${parameter.
|
|
514
|
+
parameters.${parameter.parameter.name} = await ${instance}.resolve({
|
|
546
515
|
token: ${injectorTokenVar},
|
|
547
516
|
routeConfig: ${routeConfigVar},
|
|
548
517
|
request: request,
|
|
549
|
-
name: ${JSON.stringify(parameter.
|
|
550
|
-
value: parameters.${parameter.
|
|
518
|
+
name: ${JSON.stringify(parameter.parameter.name)},
|
|
519
|
+
value: parameters.${parameter.parameter.name},
|
|
551
520
|
query: _query,
|
|
552
521
|
parameters: parameters
|
|
553
522
|
});
|
|
554
523
|
}`);
|
|
555
|
-
} else {
|
|
556
|
-
if (!parameter.isPartOfPath() && parameter.property.type !== 'class' && (!injectorOptions || !injectorOptions.token)) {
|
|
557
|
-
throw new Error(
|
|
558
|
-
`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.` +
|
|
559
|
-
`If its a query parameter use '@http.query() ${parameter.property.name}' or if its a body '@http.body() ${parameter.property.name}'.`
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
524
|
}
|
|
563
525
|
|
|
564
526
|
if (!parameter.isPartOfPath()) {
|
|
565
|
-
let injectorGet = `parameters.${parameter.
|
|
566
|
-
if (
|
|
567
|
-
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) {}`;
|
|
568
530
|
}
|
|
569
531
|
setParameters.push(`if (!${parameterResolverFoundVar}) ${injectorGet}`);
|
|
570
532
|
}
|
|
@@ -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
|
}
|
package/src/static-serving.ts
CHANGED
|
@@ -15,14 +15,12 @@ import { HtmlResponse, httpWorkflow } from './http';
|
|
|
15
15
|
import { AppModule } from '@deepkit/app';
|
|
16
16
|
import { normalizeDirectory } from './utils';
|
|
17
17
|
import { ClassType, urlJoin } from '@deepkit/core';
|
|
18
|
-
import { injectable } from '@deepkit/injector';
|
|
19
18
|
import { HttpRequest, HttpResponse } from './model';
|
|
20
19
|
import send from 'send';
|
|
21
20
|
import { eventDispatcher } from '@deepkit/event';
|
|
22
21
|
import { RouteConfig, Router } from './router';
|
|
23
22
|
|
|
24
23
|
export function serveStaticListener(module: AppModule<any>, path: string, localPath: string = path): ClassType {
|
|
25
|
-
@injectable
|
|
26
24
|
class HttpRequestStaticServingListener {
|
|
27
25
|
serve(path: string, request: HttpRequest, response: HttpResponse) {
|
|
28
26
|
return new Promise((resolve, reject) => {
|
|
@@ -133,7 +131,7 @@ export function registerStaticHttpController(module: AppModule<any>, options: St
|
|
|
133
131
|
methodName: 'serveIndex'
|
|
134
132
|
});
|
|
135
133
|
route1.groups = groups;
|
|
136
|
-
module.setupGlobalProvider(
|
|
134
|
+
module.setupGlobalProvider<Router>().addRoute(route1);
|
|
137
135
|
|
|
138
136
|
const route2 = new RouteConfig('static', ['GET'], normalizeDirectory(options.path).slice(0, -1), {
|
|
139
137
|
controller: StaticController,
|
|
@@ -141,7 +139,7 @@ export function registerStaticHttpController(module: AppModule<any>, options: St
|
|
|
141
139
|
methodName: 'serveIndex'
|
|
142
140
|
});
|
|
143
141
|
route2.groups = groups;
|
|
144
|
-
module.setupGlobalProvider(
|
|
142
|
+
module.setupGlobalProvider<Router>().addRoute(route2);
|
|
145
143
|
|
|
146
144
|
module.addProvider(StaticController);
|
|
147
145
|
module.addListener(serveStaticListener(module, normalizeDirectory(options.path), options.localPath));
|
package/tests/middleware.spec.ts
CHANGED