@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.
Files changed (142) hide show
  1. package/coverage/clover.xml +1086 -0
  2. package/coverage/coverage-final.json +12 -0
  3. package/coverage/lcov-report/base.css +224 -0
  4. package/coverage/lcov-report/block-navigation.js +79 -0
  5. package/coverage/lcov-report/favicon.png +0 -0
  6. package/coverage/lcov-report/index.html +126 -0
  7. package/coverage/lcov-report/prettify.css +1 -0
  8. package/coverage/lcov-report/prettify.js +2 -0
  9. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  10. package/coverage/lcov-report/sorter.js +170 -0
  11. package/coverage/lcov-report/src/controllers.ts.html +113 -0
  12. package/coverage/lcov-report/src/decorator.ts.html +1280 -0
  13. package/coverage/lcov-report/src/filter.ts.html +734 -0
  14. package/coverage/lcov-report/src/http.ts.html +2192 -0
  15. package/coverage/lcov-report/src/index.html +246 -0
  16. package/coverage/lcov-report/src/kernel.ts.html +260 -0
  17. package/coverage/lcov-report/src/middleware.ts.html +428 -0
  18. package/coverage/lcov-report/src/model.ts.html +937 -0
  19. package/coverage/lcov-report/src/module.config.ts.html +89 -0
  20. package/coverage/lcov-report/src/module.ts.html +245 -0
  21. package/coverage/lcov-report/src/router.ts.html +2318 -0
  22. package/coverage/lcov-report/tests/index.html +111 -0
  23. package/coverage/lcov-report/tests/utils.ts.html +176 -0
  24. package/coverage/lcov.info +2225 -0
  25. package/dist/cjs/src/controllers.d.ts +4 -4
  26. package/dist/cjs/src/controllers.js +3 -0
  27. package/dist/cjs/src/controllers.js.map +1 -1
  28. package/dist/cjs/src/decorator.d.ts +29 -94
  29. package/dist/cjs/src/decorator.js +53 -118
  30. package/dist/cjs/src/decorator.js.map +1 -1
  31. package/dist/cjs/src/filter.d.ts +1 -0
  32. package/dist/cjs/src/filter.js +11 -17
  33. package/dist/cjs/src/filter.js.map +1 -1
  34. package/dist/cjs/src/http.d.ts +5 -3
  35. package/dist/cjs/src/http.js +91 -55
  36. package/dist/cjs/src/http.js.map +1 -1
  37. package/dist/cjs/src/kernel.d.ts +3 -3
  38. package/dist/cjs/src/kernel.js +5 -25
  39. package/dist/cjs/src/kernel.js.map +1 -1
  40. package/dist/cjs/src/logger.d.ts +2 -2
  41. package/dist/cjs/src/logger.js +4 -8
  42. package/dist/cjs/src/logger.js.map +1 -1
  43. package/dist/cjs/src/middleware.d.ts +5 -1
  44. package/dist/cjs/src/middleware.js +15 -3
  45. package/dist/cjs/src/middleware.js.map +1 -1
  46. package/dist/cjs/src/model.d.ts +69 -2
  47. package/dist/cjs/src/model.js +96 -25
  48. package/dist/cjs/src/model.js.map +1 -1
  49. package/dist/cjs/src/module.config.d.ts +3 -4
  50. package/dist/cjs/src/module.config.js +8 -6
  51. package/dist/cjs/src/module.config.js.map +1 -1
  52. package/dist/cjs/src/module.d.ts +2 -3
  53. package/dist/cjs/src/module.js +4 -2
  54. package/dist/cjs/src/module.js.map +1 -1
  55. package/dist/cjs/src/router.d.ts +25 -54
  56. package/dist/cjs/src/router.js +157 -175
  57. package/dist/cjs/src/router.js.map +1 -1
  58. package/dist/cjs/src/static-serving.d.ts +1 -0
  59. package/dist/cjs/src/static-serving.js +38 -34
  60. package/dist/cjs/src/static-serving.js.map +1 -1
  61. package/dist/cjs/src/utils.js +1 -0
  62. package/dist/cjs/tests/filter.spec.js +61 -47
  63. package/dist/cjs/tests/filter.spec.js.map +1 -1
  64. package/dist/cjs/tests/middleware.spec.d.ts +1 -1
  65. package/dist/cjs/tests/middleware.spec.js +157 -145
  66. package/dist/cjs/tests/middleware.spec.js.map +1 -1
  67. package/dist/cjs/tests/model.spec.js +4 -4
  68. package/dist/cjs/tests/model.spec.js.map +1 -1
  69. package/dist/cjs/tests/module.spec.js +4 -3
  70. package/dist/cjs/tests/module.spec.js.map +1 -1
  71. package/dist/cjs/tests/router.spec.d.ts +1 -1
  72. package/dist/cjs/tests/router.spec.js +311 -322
  73. package/dist/cjs/tests/router.spec.js.map +1 -1
  74. package/dist/cjs/tests/utils.js +5 -2
  75. package/dist/cjs/tests/utils.js.map +1 -1
  76. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  77. package/dist/esm/src/controllers.d.ts +4 -4
  78. package/dist/esm/src/controllers.js +3 -0
  79. package/dist/esm/src/controllers.js.map +1 -1
  80. package/dist/esm/src/decorator.d.ts +29 -94
  81. package/dist/esm/src/decorator.js +49 -114
  82. package/dist/esm/src/decorator.js.map +1 -1
  83. package/dist/esm/src/filter.d.ts +1 -0
  84. package/dist/esm/src/filter.js +11 -18
  85. package/dist/esm/src/filter.js.map +1 -1
  86. package/dist/esm/src/http.d.ts +5 -3
  87. package/dist/esm/src/http.js +89 -55
  88. package/dist/esm/src/http.js.map +1 -1
  89. package/dist/esm/src/kernel.d.ts +3 -3
  90. package/dist/esm/src/kernel.js +6 -27
  91. package/dist/esm/src/kernel.js.map +1 -1
  92. package/dist/esm/src/logger.d.ts +2 -2
  93. package/dist/esm/src/logger.js +4 -9
  94. package/dist/esm/src/logger.js.map +1 -1
  95. package/dist/esm/src/middleware.d.ts +5 -1
  96. package/dist/esm/src/middleware.js +12 -0
  97. package/dist/esm/src/middleware.js.map +1 -1
  98. package/dist/esm/src/model.d.ts +69 -2
  99. package/dist/esm/src/model.js +92 -24
  100. package/dist/esm/src/model.js.map +1 -1
  101. package/dist/esm/src/module.config.d.ts +3 -4
  102. package/dist/esm/src/module.config.js +6 -5
  103. package/dist/esm/src/module.config.js.map +1 -1
  104. package/dist/esm/src/module.d.ts +2 -3
  105. package/dist/esm/src/module.js +5 -3
  106. package/dist/esm/src/module.js.map +1 -1
  107. package/dist/esm/src/router.d.ts +25 -54
  108. package/dist/esm/src/router.js +154 -172
  109. package/dist/esm/src/router.js.map +1 -1
  110. package/dist/esm/src/static-serving.d.ts +1 -0
  111. package/dist/esm/src/static-serving.js +29 -25
  112. package/dist/esm/src/static-serving.js.map +1 -1
  113. package/dist/esm/src/utils.js +1 -0
  114. package/dist/esm/tests/filter.spec.js +14 -0
  115. package/dist/esm/tests/filter.spec.js.map +1 -1
  116. package/dist/esm/tests/middleware.spec.d.ts +1 -1
  117. package/dist/esm/tests/middleware.spec.js +60 -48
  118. package/dist/esm/tests/middleware.spec.js.map +1 -1
  119. package/dist/esm/tests/module.spec.js +1 -0
  120. package/dist/esm/tests/module.spec.js.map +1 -1
  121. package/dist/esm/tests/router.spec.d.ts +1 -1
  122. package/dist/esm/tests/router.spec.js +184 -195
  123. package/dist/esm/tests/router.spec.js.map +1 -1
  124. package/dist/esm/tests/utils.js +4 -1
  125. package/dist/esm/tests/utils.js.map +1 -1
  126. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  127. package/package.json +14 -15
  128. package/src/controllers.ts +3 -3
  129. package/src/decorator.ts +42 -131
  130. package/src/filter.ts +0 -2
  131. package/src/http.ts +35 -36
  132. package/src/kernel.ts +4 -5
  133. package/src/logger.ts +2 -4
  134. package/src/middleware.ts +2 -1
  135. package/src/model.ts +86 -8
  136. package/src/module.config.ts +3 -6
  137. package/src/module.ts +2 -2
  138. package/src/router.ts +143 -183
  139. package/src/static-serving.ts +2 -4
  140. package/tests/middleware.spec.ts +0 -1
  141. package/tests/router.spec.ts +152 -131
  142. package/tsconfig.json +2 -1
package/src/module.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { HttpListener, HttpResultFormatter, httpWorkflow } from './http';
2
- import { config } from './module.config';
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: 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 'reflect-metadata';
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
- getClassSchema,
15
- getPropertyXtoClassFunction,
16
- JitConverterOptions,
17
- jitValidateProperty,
18
- jsonSerializer,
19
- PropertySchema,
14
+ findMember,
15
+ getSerializeFunction,
16
+ getValidatorFunction,
17
+ metaAnnotation,
18
+ ReflectionClass,
19
+ ReflectionKind,
20
+ ReflectionParameter,
21
+ SerializationOptions,
22
+ serializer,
20
23
  Serializer,
21
- t,
22
- ValidationFailed,
23
- ValidationFailedItem
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 { injectable, InjectOptions, InjectorContext, TagRegistry } from '@deepkit/injector';
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 { AppModule, MiddlewareRegistry, MiddlewareRegistryEntry } from '@deepkit/app';
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
- @t.required size!: number;
54
+ size!: number;
52
55
 
53
56
  /**
54
57
  * The path this file is being written to.
55
58
  */
56
- @t.required path!: string;
59
+ path!: string;
57
60
 
58
61
  /**
59
62
  * The name this file had according to the uploading client.
60
63
  */
61
- @t.string.required.nullable name!: string | null;
64
+ name!: string | null;
62
65
 
63
66
  /**
64
67
  * The mime type of this file, according to the uploading client.
65
68
  */
66
- @t.string.required.nullable type!: string | null;
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
- @t.date.required.nullable lastModifiedDate!: Date | null;
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
- // @t.string.required.nullable hash!: string | 'sha1' | 'md5' | 'sha256' | null;
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?: AppModule<any>;
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?: PropertySchema }[] = [];
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?: JitConverterOptions;
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?: AppModule<any>;
111
+ public module?: InjectorModule<any>;
136
112
 
137
113
  resolverForToken: Map<any, ClassType> = new Map();
138
114
 
139
- middlewares: { config: HttpMiddlewareConfig, module: AppModule<any> }[] = [];
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): PropertySchema | undefined {
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: PropertySchema, config?: RouteParameterConfig): ParsedRouteParameter {
219
- const parameter = new ParsedRouteParameter(property, config);
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 property: PropertySchema,
241
- public config?: RouteParameterConfig,
180
+ public parameter: ReflectionParameter,
242
181
  ) {
243
182
  }
244
183
 
245
184
  get body() {
246
- return this.config ? this.config.type === 'body' : false;
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 this.config ? this.config.type === 'query' : false;
203
+ return metaAnnotation.getForName(this.parameter.type, 'httpQuery') !== undefined;
251
204
  }
252
205
 
253
206
  get queries() {
254
- return this.config ? this.config.type === 'queries' : false;
207
+ return metaAnnotation.getForName(this.parameter.type, 'httpQueries') !== undefined;
255
208
  }
256
209
 
257
- get typePath() {
258
- return this.config ? this.config.typePath : undefined;
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.property.name;
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
- return routeConfig.parameterRegularExpressions[name] ? '(' + routeConfig.parameterRegularExpressions[name] + ')' : String.raw`([^/]+)`;
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 = getClassSchema(routeConfig.action.controller);
249
+ const schema = ReflectionClass.from(routeConfig.action.controller);
286
250
  const parsedRoute = new ParsedRoute(routeConfig);
287
251
 
288
- const methodArgumentProperties = schema.getMethodProperties(routeConfig.action.methodName);
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 decoratorData = routeConfig.parameters[property.name];
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: AppModule<any> }[] {
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: AppModule<any> }[];
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
- //todo, move some settings to KernelConfig
417
- protected form = formidable({
418
- multiples: true,
419
- hash: 'sha1',
420
- enabledPlugins: ['octetstream', 'querystring', 'json'],
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: 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: AppModule<any>, controller: ClassType })[],
409
+ controllers: (ClassType | { module: InjectorModule<any>, controller: ClassType })[],
440
410
  tagRegistry: TagRegistry = new TagRegistry(),
441
411
  middlewareRegistry: MiddlewareRegistry = new MiddlewareRegistry(),
442
- module: AppModule<any> = new AppModule({})
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 ValidationFailed.from(bodyErrors);`;
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 (parsedRoute.customValidationErrorHandling === parameter) {
471
- compiler.context.set('BodyValidation', BodyValidation);
472
- bodyValidationErrorHandling = '';
473
- setParameters.push(`parameters.${parameter.property.name} = new BodyValidation(bodyErrors);`);
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.property.name} = ${converterVar}(bodyFields);`);
481
- parameterValidator.push(`${validatorVar}(parameters.${parameter.property.name}, ${JSON.stringify(parameter.typePath || '')}, bodyErrors);`);
482
- parameterNames.push(`parameters.${parameter.property.name}`);
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.property.type === 'any' ? (v: any) => v : getPropertyXtoClassFunction(parameter.property, jsonSerializer);
485
- const validator = parameter.property.type === 'any' ? (v: any) => undefined : jitValidateProperty(parameter.property);
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.property.name : parameter.typePath;
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.property.name} = ${converterVar}(${queryAccessor});`);
494
- parameterNames.push(`parameters.${parameter.property.name}`);
495
- parameterValidator.push(`${validatorVar}(parameters.${parameter.property.name}, ${JSON.stringify(parameter.typePath)}, validationErrors);`);
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.property.name}`);
470
+ parameterNames.push(`parameters.${parameter.parameter.name}`);
498
471
 
499
472
  if (parameter.isPartOfPath()) {
500
- if (parameter.property.type !== 'class') {
501
- const converted = parameter.property.type === 'any' ? (v: any) => v : getPropertyXtoClassFunction(parameter.property, jsonSerializer);
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.property.name} = ${converterVar}(_match[${1 + (parameter.regexPosition || 0)}]);`);
476
+ setParameters.push(`parameters.${parameter.parameter.name} = ${converterVar}(_match[${1 + (parameter.regexPosition || 0)}], {loosely: true});`);
504
477
 
505
- const validator = parameter.property.type === 'any' ? (v: any) => undefined : jitValidateProperty(parameter.property);
478
+ const validator = getValidatorFunction(undefined, parameter.parameter.parameter);
506
479
  const validatorVar = compiler.reserveVariable('argumentValidator', validator);
507
- parameterValidator.push(`${validatorVar}(parameters.${parameter.property.name}, ${JSON.stringify(parameter.getName())}, validationErrors);`);
480
+ parameterValidator.push(`${validatorVar}(parameters.${parameter.parameter.name}, {errors: validationErrors}, ${JSON.stringify(parameter.getName())});`);
508
481
  } else {
509
- setParameters.push(`parameters.${parameter.property.name} = _match[${1 + (parameter.regexPosition || 0)}];`);
482
+ setParameters.push(`parameters.${parameter.parameter.name} = _match[${1 + (parameter.regexPosition || 0)}];`);
510
483
  }
511
484
  }
512
485
 
513
- const injectorOptions = parameter.property.data['deepkit/inject'] as InjectOptions | undefined;
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.property.name} = await ${instance}.resolve({
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.property.name)},
548
- value: parameters.${parameter.property.name},
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.property.name} = ${injector}.get(${injectorTokenVar});`;
564
- if (injectorOptions && injectorOptions.optional) {
565
- injectorGet = `try {parameters.${parameter.property.name} = ${injector}.get(${injectorTokenVar}); } catch (e) {}`;
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 bodyParsed = (await ${parseBodyVar}(${formVar}, request, uploadedFiles));
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 ValidationFailed.from(validationErrors);
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.property.name : parameter.typePath;
614
+ const queryPath = parameter.typePath === undefined && parameter.query ? parameter.parameter.name : parameter.typePath || '';
653
615
 
654
- if (parameter.property.type === 'class') {
655
- for (const property of parameter.property.getResolvedClassSchema().getProperties()) {
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: AppModule<any>) {
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 = getClassSchema(controller);
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.returnSchema = schema.getMethod(action.methodName);
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('ValidationFailed', ValidationFailed);
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
  }