@deepkit/http 1.0.1-alpha.57 → 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.
Files changed (149) 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/index.d.ts +1 -0
  26. package/dist/cjs/index.js +1 -0
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/src/controllers.d.ts +4 -4
  29. package/dist/cjs/src/controllers.js +3 -0
  30. package/dist/cjs/src/controllers.js.map +1 -1
  31. package/dist/cjs/src/decorator.d.ts +29 -94
  32. package/dist/cjs/src/decorator.js +53 -118
  33. package/dist/cjs/src/decorator.js.map +1 -1
  34. package/dist/cjs/src/filter.d.ts +1 -0
  35. package/dist/cjs/src/filter.js +11 -17
  36. package/dist/cjs/src/filter.js.map +1 -1
  37. package/dist/cjs/src/http.d.ts +5 -3
  38. package/dist/cjs/src/http.js +100 -56
  39. package/dist/cjs/src/http.js.map +1 -1
  40. package/dist/cjs/src/kernel.d.ts +3 -3
  41. package/dist/cjs/src/kernel.js +9 -28
  42. package/dist/cjs/src/kernel.js.map +1 -1
  43. package/dist/cjs/src/logger.d.ts +2 -2
  44. package/dist/cjs/src/logger.js +4 -8
  45. package/dist/cjs/src/logger.js.map +1 -1
  46. package/dist/cjs/src/middleware.d.ts +5 -1
  47. package/dist/cjs/src/middleware.js +15 -3
  48. package/dist/cjs/src/middleware.js.map +1 -1
  49. package/dist/cjs/src/model.d.ts +67 -0
  50. package/dist/cjs/src/model.js +95 -24
  51. package/dist/cjs/src/model.js.map +1 -1
  52. package/dist/cjs/src/module.config.d.ts +3 -4
  53. package/dist/cjs/src/module.config.js +8 -6
  54. package/dist/cjs/src/module.config.js.map +1 -1
  55. package/dist/cjs/src/module.d.ts +2 -3
  56. package/dist/cjs/src/module.js +4 -2
  57. package/dist/cjs/src/module.js.map +1 -1
  58. package/dist/cjs/src/router.d.ts +25 -54
  59. package/dist/cjs/src/router.js +162 -186
  60. package/dist/cjs/src/router.js.map +1 -1
  61. package/dist/cjs/src/static-serving.d.ts +1 -0
  62. package/dist/cjs/src/static-serving.js +38 -34
  63. package/dist/cjs/src/static-serving.js.map +1 -1
  64. package/dist/cjs/src/utils.js +1 -0
  65. package/dist/cjs/tests/filter.spec.js +61 -47
  66. package/dist/cjs/tests/filter.spec.js.map +1 -1
  67. package/dist/cjs/tests/middleware.spec.d.ts +1 -1
  68. package/dist/cjs/tests/middleware.spec.js +157 -145
  69. package/dist/cjs/tests/middleware.spec.js.map +1 -1
  70. package/dist/cjs/tests/model.spec.js +4 -4
  71. package/dist/cjs/tests/model.spec.js.map +1 -1
  72. package/dist/cjs/tests/module.spec.js +4 -3
  73. package/dist/cjs/tests/module.spec.js.map +1 -1
  74. package/dist/cjs/tests/router.spec.d.ts +1 -1
  75. package/dist/cjs/tests/router.spec.js +329 -296
  76. package/dist/cjs/tests/router.spec.js.map +1 -1
  77. package/dist/cjs/tests/utils.js +5 -2
  78. package/dist/cjs/tests/utils.js.map +1 -1
  79. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  80. package/dist/esm/index.d.ts +1 -0
  81. package/dist/esm/index.js +1 -0
  82. package/dist/esm/index.js.map +1 -1
  83. package/dist/esm/src/controllers.d.ts +4 -4
  84. package/dist/esm/src/controllers.js +3 -0
  85. package/dist/esm/src/controllers.js.map +1 -1
  86. package/dist/esm/src/decorator.d.ts +29 -94
  87. package/dist/esm/src/decorator.js +49 -114
  88. package/dist/esm/src/decorator.js.map +1 -1
  89. package/dist/esm/src/filter.d.ts +1 -0
  90. package/dist/esm/src/filter.js +11 -18
  91. package/dist/esm/src/filter.js.map +1 -1
  92. package/dist/esm/src/http.d.ts +5 -3
  93. package/dist/esm/src/http.js +98 -56
  94. package/dist/esm/src/http.js.map +1 -1
  95. package/dist/esm/src/kernel.d.ts +3 -3
  96. package/dist/esm/src/kernel.js +10 -30
  97. package/dist/esm/src/kernel.js.map +1 -1
  98. package/dist/esm/src/logger.d.ts +2 -2
  99. package/dist/esm/src/logger.js +4 -9
  100. package/dist/esm/src/logger.js.map +1 -1
  101. package/dist/esm/src/middleware.d.ts +5 -1
  102. package/dist/esm/src/middleware.js +12 -0
  103. package/dist/esm/src/middleware.js.map +1 -1
  104. package/dist/esm/src/model.d.ts +67 -0
  105. package/dist/esm/src/model.js +91 -23
  106. package/dist/esm/src/model.js.map +1 -1
  107. package/dist/esm/src/module.config.d.ts +3 -4
  108. package/dist/esm/src/module.config.js +6 -5
  109. package/dist/esm/src/module.config.js.map +1 -1
  110. package/dist/esm/src/module.d.ts +2 -3
  111. package/dist/esm/src/module.js +5 -3
  112. package/dist/esm/src/module.js.map +1 -1
  113. package/dist/esm/src/router.d.ts +25 -54
  114. package/dist/esm/src/router.js +159 -183
  115. package/dist/esm/src/router.js.map +1 -1
  116. package/dist/esm/src/static-serving.d.ts +1 -0
  117. package/dist/esm/src/static-serving.js +29 -25
  118. package/dist/esm/src/static-serving.js.map +1 -1
  119. package/dist/esm/src/utils.js +1 -0
  120. package/dist/esm/tests/filter.spec.js +14 -0
  121. package/dist/esm/tests/filter.spec.js.map +1 -1
  122. package/dist/esm/tests/middleware.spec.d.ts +1 -1
  123. package/dist/esm/tests/middleware.spec.js +60 -48
  124. package/dist/esm/tests/middleware.spec.js.map +1 -1
  125. package/dist/esm/tests/module.spec.js +1 -0
  126. package/dist/esm/tests/module.spec.js.map +1 -1
  127. package/dist/esm/tests/router.spec.d.ts +1 -1
  128. package/dist/esm/tests/router.spec.js +206 -173
  129. package/dist/esm/tests/router.spec.js.map +1 -1
  130. package/dist/esm/tests/utils.js +4 -1
  131. package/dist/esm/tests/utils.js.map +1 -1
  132. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  133. package/index.ts +1 -0
  134. package/package.json +14 -15
  135. package/src/controllers.ts +3 -3
  136. package/src/decorator.ts +42 -131
  137. package/src/filter.ts +0 -2
  138. package/src/http.ts +40 -33
  139. package/src/kernel.ts +8 -8
  140. package/src/logger.ts +2 -4
  141. package/src/middleware.ts +2 -1
  142. package/src/model.ts +83 -5
  143. package/src/module.config.ts +3 -6
  144. package/src/module.ts +2 -2
  145. package/src/router.ts +147 -193
  146. package/src/static-serving.ts +2 -4
  147. package/tests/middleware.spec.ts +0 -1
  148. package/tests/router.spec.ts +163 -106
  149. package/tsconfig.json +2 -1
@@ -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(Router).addRoute(route1);
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(Router).addRoute(route2);
142
+ module.setupGlobalProvider<Router>().addRoute(route2);
145
143
 
146
144
  module.addProvider(StaticController);
147
145
  module.addListener(serveStaticListener(module, normalizeDirectory(options.path), options.localPath));
@@ -1,5 +1,4 @@
1
1
  import { expect, test } from '@jest/globals';
2
- import 'reflect-metadata';
3
2
  import { http } from '../src/decorator';
4
3
  import { createHttpKernel } from './utils';
5
4
  import { HttpMiddleware, httpMiddleware } from '../src/middleware';
@@ -1,14 +1,12 @@
1
1
  import { expect, test } from '@jest/globals';
2
- import 'reflect-metadata';
3
- import { dotToUrlPath, RouteParameterResolverContext, Router } from '../src/router';
2
+ import { dotToUrlPath, RouteParameterResolverContext, Router, UploadedFile } from '../src/router';
4
3
  import { http, httpClass } from '../src/decorator';
5
- import { getClassSchema, t } from '@deepkit/type';
6
- import { httpWorkflow, JSONResponse } from '../src/http';
4
+ import { HttpBadRequestError, httpWorkflow, JSONResponse } from '../src/http';
7
5
  import { eventDispatcher } from '@deepkit/event';
8
- import { inject } from '@deepkit/injector';
9
- import { HttpRequest } from '../src/model';
6
+ import { HttpBody, HttpBodyValidation, HttpQueries, HttpQuery, HttpRegExp, HttpRequest } from '../src/model';
10
7
  import { getClassName, sleep } from '@deepkit/core';
11
8
  import { createHttpKernel } from './utils';
9
+ import { Group, MinLength } from '@deepkit/type';
12
10
 
13
11
  test('router', async () => {
14
12
  class Controller {
@@ -62,7 +60,6 @@ test('any', async () => {
62
60
  expect((await router.resolve('OPTIONS', '/any'))!.routeConfig.action.methodName).toEqual('any');
63
61
  });
64
62
 
65
-
66
63
  test('router parameters', async () => {
67
64
  class Controller {
68
65
  @http.GET('/user/:name')
@@ -80,8 +77,8 @@ test('router parameters', async () => {
80
77
  return new JSONResponse(yes);
81
78
  }
82
79
 
83
- @http.GET(':path').regexp('path', '.*')
84
- any(path: string) {
80
+ @http.GET(':path')
81
+ any(path: HttpRegExp<string, '.*'>) {
85
82
  return new JSONResponse(path);
86
83
  }
87
84
  }
@@ -93,7 +90,7 @@ test('router parameters', async () => {
93
90
 
94
91
  expect((await httpKernel.request(HttpRequest.GET('/user/peter'))).json).toBe('peter');
95
92
  expect((await httpKernel.request(HttpRequest.GET('/user-id/123'))).json).toBe(123);
96
- expect((await httpKernel.request(HttpRequest.GET('/user-id/asd'))).json).toMatchObject({ message: 'Validation failed: id(invalid_number): No valid number given, got NaN' });
93
+ expect((await httpKernel.request(HttpRequest.GET('/user-id/asd'))).json).toMatchObject({ message: 'Validation error:\nid(type): Cannot convert asd to number' });
97
94
  expect((await httpKernel.request(HttpRequest.GET('/boolean/1'))).json).toBe(true);
98
95
  expect((await httpKernel.request(HttpRequest.GET('/boolean/false'))).json).toBe(false);
99
96
 
@@ -103,8 +100,8 @@ test('router parameters', async () => {
103
100
 
104
101
  test('router HttpRequest', async () => {
105
102
  class Controller {
106
- @http.GET(':path').regexp('path', '.*')
107
- anyReq(req: HttpRequest, path: string) {
103
+ @http.GET(':path')
104
+ anyReq(req: HttpRequest, path: HttpRegExp<string, '.*'>) {
108
105
  return [req.url, path];
109
106
  }
110
107
  }
@@ -246,14 +243,14 @@ test('router parameter resolver by name', async () => {
246
243
  expect((await httpKernel.request(HttpRequest.GET('/nonClass'))).json).toEqual(['auth', 'MyAuth']);
247
244
  });
248
245
 
249
- test('router body', async () => {
246
+ test('router body class', async () => {
250
247
  class Body {
251
- @t username!: string;
248
+ username!: string;
252
249
  }
253
250
 
254
251
  class Controller {
255
252
  @http.POST()
256
- anyReq(@http.body() body: Body, req: HttpRequest) {
253
+ anyReq(body: HttpBody<Body>, req: HttpRequest) {
257
254
  return [body.username, body instanceof Body, req.url];
258
255
  }
259
256
  }
@@ -263,15 +260,54 @@ test('router body', async () => {
263
260
  expect((await httpKernel.request(HttpRequest.POST('/').json({ username: 'Peter' }))).json).toEqual(['Peter', true, '/']);
264
261
  });
265
262
 
263
+ test('router body is safe for simultaneous requests', async () => {
264
+ class Body {
265
+ username!: string;
266
+ }
267
+
268
+ class Controller {
269
+ @http.POST()
270
+ anyReq(body: HttpBody<Body>, req: HttpRequest) {
271
+ return [body.username, body instanceof Body, req.url];
272
+ }
273
+ }
274
+
275
+ const httpKernel = createHttpKernel([Controller]);
276
+
277
+ const makeReq = () => httpKernel.request(HttpRequest.POST('/').json({ username: 'Peter' }));
278
+
279
+ const results = await Promise.all([...new Array(100)].map(() => makeReq().then(res => res.json)));
280
+
281
+ expect(results).toEqual(
282
+ results.map(() => ['Peter', true, '/']),
283
+ )
284
+ });
285
+
286
+ test('router body interface', async () => {
287
+ interface Body {
288
+ username: string;
289
+ }
290
+
291
+ class Controller {
292
+ @http.POST()
293
+ anyReq(body: HttpBody<Body>, req: HttpRequest) {
294
+ return [body.username, typeof body.username, req.url];
295
+ }
296
+ }
297
+
298
+ const httpKernel = createHttpKernel([Controller]);
299
+
300
+ expect((await httpKernel.request(HttpRequest.POST('/').json({ username: 'Peter' }))).json).toEqual(['Peter', 'string', '/']);
301
+ });
266
302
 
267
303
  test('router body double', async () => {
268
304
  class Body {
269
- @t username!: string;
305
+ username!: string;
270
306
  }
271
307
 
272
308
  class Controller {
273
309
  @http.POST()
274
- anyReq(@http.body() body: Body, @http.body() body2: Body, req: HttpRequest) {
310
+ anyReq(body: HttpBody<Body>, body2: HttpBody<Body>, req: HttpRequest) {
275
311
  return [body2.username, body2 instanceof Body && body instanceof Body, req.url];
276
312
  }
277
313
  }
@@ -281,10 +317,6 @@ test('router body double', async () => {
281
317
  const action = [...httpData.getActions()][0];
282
318
  expect(action.methodName).toBe('anyReq');
283
319
  expect(action.httpMethods).toEqual(['POST']);
284
- expect(action.parameters['body']).not.toBeUndefined();
285
- expect(action.parameters['body'].name).toBe('body');
286
- expect(action.parameters['body2']).not.toBeUndefined();
287
- expect(action.parameters['body2'].name).toBe('body2');
288
320
 
289
321
  const httpKernel = createHttpKernel([Controller]);
290
322
 
@@ -341,7 +373,7 @@ test('router groups', async () => {
341
373
  test('router query', async () => {
342
374
  class Controller {
343
375
  @http.GET('my-action')
344
- anyReq(@http.query().optional test?: number) {
376
+ anyReq(test?: HttpQuery<number>) {
345
377
  return test;
346
378
  }
347
379
  }
@@ -351,9 +383,6 @@ test('router query', async () => {
351
383
  const action = [...httpData.getActions()][0];
352
384
  expect(action.methodName).toBe('anyReq');
353
385
  expect(action.httpMethods).toEqual(['GET']);
354
- expect(action.parameters['test']).not.toBeUndefined();
355
- expect(action.parameters['test'].name).toBe('test');
356
- expect(action.parameters['test'].type).toBe('query');
357
386
 
358
387
  const httpKernel = createHttpKernel([Controller]);
359
388
 
@@ -363,14 +392,14 @@ test('router query', async () => {
363
392
 
364
393
  test('router query all', async () => {
365
394
  class AnyReqQuery {
366
- @t.optional test?: string;
367
- @t.optional filter?: string;
368
- @t.optional page?: number;
395
+ test?: string;
396
+ filter?: string;
397
+ page?: number;
369
398
  }
370
399
 
371
400
  class Controller {
372
401
  @http.GET('my-action')
373
- anyReq(@http.queries() anyReqQuery: AnyReqQuery) {
402
+ anyReq(anyReqQuery: HttpQueries<AnyReqQuery>) {
374
403
  return anyReqQuery;
375
404
  }
376
405
  }
@@ -384,14 +413,13 @@ test('router query all', async () => {
384
413
 
385
414
  test('serializer options', async () => {
386
415
  class User {
387
- @t username!: string;
388
- @t.group('sensitive') password!: string;
416
+ username!: string;
417
+ password!: string & Group<'sensitive'>;
389
418
  }
390
419
 
391
420
  class Controller {
392
421
  @http.GET().serialization({ groupsExclude: ['sensitive'] })
393
- @t.type(User)
394
- anyReq() {
422
+ anyReq(): User {
395
423
  return { username: 'Peter', password: 'secret' };
396
424
  }
397
425
  }
@@ -403,14 +431,13 @@ test('serializer options', async () => {
403
431
 
404
432
  test('hook after serializer', async () => {
405
433
  class User {
406
- @t username!: string;
407
- @t.group('sensitive') password!: string;
434
+ username!: string;
435
+ password!: string & Group<'sensitive'>;
408
436
  }
409
437
 
410
438
  class Controller {
411
439
  @http.GET().serialization({ groupsExclude: ['sensitive'] })
412
- @t.type(User)
413
- async anyReq() {
440
+ async anyReq(): Promise<User> {
414
441
  await sleep(0.1);
415
442
  return { username: 'Peter', password: 'secret' };
416
443
  }
@@ -438,10 +465,9 @@ test('invalid route definition', async () => {
438
465
  }
439
466
 
440
467
  const httpKernel = createHttpKernel([Controller]);
441
- expect((await httpKernel.request(HttpRequest.GET('/'))).bodyString).toEqual('Not found');
468
+ expect((await httpKernel.request(HttpRequest.GET('/'))).bodyString).toEqual('Internal error');
442
469
  });
443
470
 
444
-
445
471
  test('inject request storage ClassType', async () => {
446
472
  class User {
447
473
  constructor(public username: string) {
@@ -455,7 +481,7 @@ test('inject request storage ClassType', async () => {
455
481
  }
456
482
 
457
483
  @http.GET('optional')
458
- doItOptional(@inject().optional user?: User) {
484
+ doItOptional(user?: User) {
459
485
  return { isUser: user instanceof User };
460
486
  }
461
487
  }
@@ -471,7 +497,7 @@ test('inject request storage ClassType', async () => {
471
497
 
472
498
  const httpKernel = createHttpKernel([Controller], [
473
499
  {
474
- provide: User, scope: 'http', deps: [HttpRequest], useFactory(request: HttpRequest) {
500
+ provide: User, scope: 'http', useFactory(request: HttpRequest) {
475
501
  return request.store.user;
476
502
  }
477
503
  }
@@ -483,40 +509,6 @@ test('inject request storage ClassType', async () => {
483
509
  expect((await httpKernel.request(HttpRequest.GET('/optional').headers({ authorization: 'no' }))).json).toEqual({ isUser: false });
484
510
  });
485
511
 
486
-
487
- test('inject request storage @inject', async () => {
488
- class User {
489
- constructor(public username: string) {
490
- }
491
- }
492
-
493
- class Controller {
494
- @http.GET()
495
- doIt(@inject('user') user: any) {
496
- return { isUser: user instanceof User, username: user.username };
497
- }
498
- }
499
-
500
- class Listener {
501
- @eventDispatcher.listen(httpWorkflow.onAuth)
502
- onAuth(event: typeof httpWorkflow.onAuth.event) {
503
- event.request.store.user = new User('bar');
504
- }
505
- }
506
-
507
- const httpKernel = createHttpKernel([Controller], [
508
- {
509
- provide: 'user', scope: 'http', deps: [HttpRequest], useFactory(request: HttpRequest) {
510
- return request.store.user;
511
- }
512
- }
513
- ], [Listener]);
514
-
515
- const result = (await httpKernel.request(HttpRequest.GET('/'))).json;
516
- expect(result.isUser).toBe(true);
517
- expect(result.username).toBe('bar');
518
- });
519
-
520
512
  test('custom request handling', async () => {
521
513
  class Listener {
522
514
  @eventDispatcher.listen(httpWorkflow.onRouteNotFound)
@@ -590,9 +582,8 @@ test('promise serializer', async () => {
590
582
  }
591
583
 
592
584
  @http.GET('2')
593
- @t.string
594
- async anyReq2() {
595
- return 1;
585
+ async anyReq2(): Promise<string> {
586
+ return 1 as any;
596
587
  }
597
588
  }
598
589
 
@@ -602,20 +593,31 @@ test('promise serializer', async () => {
602
593
  expect((await httpKernel.request(HttpRequest.GET('/2'))).json).toBe('1');
603
594
  });
604
595
 
605
-
606
596
  test('unions', async () => {
607
597
  class Controller {
608
598
  @http.GET('/list')
609
- list(@http.query() @t.union(t.number, t.boolean) page: number | boolean) {
599
+ list(page: HttpQuery<number | boolean>) {
610
600
  return page;
611
601
  }
612
602
  }
613
603
 
614
604
  const httpKernel = createHttpKernel([Controller]);
615
605
 
606
+ expect((await httpKernel.request(HttpRequest.GET('/list?page=0'))).json).toEqual(0);
616
607
  expect((await httpKernel.request(HttpRequest.GET('/list?page=1'))).json).toEqual(1);
617
608
  expect((await httpKernel.request(HttpRequest.GET('/list?page=2222'))).json).toEqual(2222);
618
609
  expect((await httpKernel.request(HttpRequest.GET('/list?page=false'))).json).toEqual(false);
610
+ expect((await httpKernel.request(HttpRequest.GET('/list?page=true'))).json).toEqual(true);
611
+
612
+ expect((await httpKernel.request(HttpRequest.GET('/list?page=asdasdc'))).json).toMatchObject({
613
+ 'errors': [
614
+ {
615
+ 'code': 'type',
616
+ 'message': 'No value given',
617
+ 'path': 'page'
618
+ }
619
+ ],
620
+ });
619
621
  });
620
622
 
621
623
  test('router dotToUrlPath', () => {
@@ -627,9 +629,9 @@ test('router dotToUrlPath', () => {
627
629
 
628
630
  test('router url resolve', async () => {
629
631
  class AnyReqQuery {
630
- @t.optional test?: string;
631
- @t.optional filter?: string;
632
- @t.optional page?: number;
632
+ test?: string;
633
+ filter?: string;
634
+ page?: number;
633
635
  }
634
636
 
635
637
  class Controller {
@@ -644,22 +646,22 @@ test('router url resolve', async () => {
644
646
  }
645
647
 
646
648
  @http.GET('').name('secondQuery')
647
- secondQuery(@http.query() peter: string) {
649
+ secondQuery(peter: HttpQuery<string>) {
648
650
  return '';
649
651
  }
650
652
 
651
653
  @http.GET('').name('secondQuery2')
652
- secondQuery2(@http.query('changed') peter: string) {
654
+ secondQuery2(peter: HttpQuery<string, { name: 'changed' }>) {
653
655
  return '';
654
656
  }
655
657
 
656
658
  @http.GET('third').name('third')
657
- third(@http.queries() params: AnyReqQuery) {
659
+ third(params: HttpQueries<AnyReqQuery>) {
658
660
  return params;
659
661
  }
660
662
 
661
663
  @http.GET('third2').name('third2')
662
- third2(@http.queries('deep') params: AnyReqQuery) {
664
+ third2(params: HttpQueries<AnyReqQuery, { name: 'deep' }>) {
663
665
  return params;
664
666
  }
665
667
  }
@@ -680,15 +682,14 @@ test('router url resolve', async () => {
680
682
  });
681
683
 
682
684
  test('destructing params', async () => {
683
-
684
- class DTO {
685
- @t.required name!: string;
686
- @t.required title!: string;
685
+ interface DTO {
686
+ name: string;
687
+ title: string;
687
688
  }
688
689
 
689
690
  class Controller {
690
691
  @http.GET('')
691
- first(@http.body() { name, title }: DTO) {
692
+ first({ name, title }: HttpBody<DTO>) {
692
693
  return [name, title];
693
694
  }
694
695
  }
@@ -698,27 +699,83 @@ test('destructing params', async () => {
698
699
  expect((await httpKernel.request(HttpRequest.GET('/').json({ name: 'Peter', title: 'CTO' }))).json).toEqual(['Peter', 'CTO']);
699
700
  });
700
701
 
701
- test('dynamic parameter name', async () => {
702
- @http.controller()
702
+ test('use http.response for serialization', async () => {
703
+ interface DefaultResponse {
704
+ title: string;
705
+ }
706
+
707
+ interface ErrorResponse {
708
+ message: string;
709
+ }
710
+
703
711
  class Controller {
704
- @http.GET('first/:another')
705
- first(@t.description('The identifier').name('another') id: string) {
706
- return [id];
712
+ @http.GET('/action1')
713
+ action1(): DefaultResponse[] {
714
+ return [{ title: 'a' }, { title: '1' }];
715
+ }
716
+
717
+ @http.GET('/action2').response<DefaultResponse[]>(200, `List`)
718
+ action2() {
719
+ return [{ title: 'a' }, { title: 1 }];
707
720
  }
708
721
 
709
- @http.GET('second/:another2')
710
- second(@t.description('The identifier').name('another2') id: string, @http.query() second: string) {
711
- return [id, second];
722
+ @http.GET('/action3')
723
+ .response<DefaultResponse[]>(200, `List`)
724
+ .response<ErrorResponse[]>(400, `Error`)
725
+ action3() {
726
+ return new JSONResponse([{ message: 'error' }, { message: 1 }]).status(400);
712
727
  }
713
728
  }
714
729
 
715
- const schema = getClassSchema(Controller);
716
- expect(schema.getMethodProperties('first')[0].name).toBe('another');
717
- expect(schema.getMethodProperties('second')[0].name).toBe('another2');
718
- expect(schema.getMethodProperties('second')[1].name).toBe('second');
730
+ const httpKernel = createHttpKernel([Controller]);
731
+ expect((await httpKernel.request(HttpRequest.GET('/action1'))).json).toEqual([{ title: 'a' }, { title: '1' }]);
732
+ expect((await httpKernel.request(HttpRequest.GET('/action2'))).json).toEqual([{ title: 'a' }, { title: '1' }]);
733
+ expect((await httpKernel.request(HttpRequest.GET('/action3'))).json).toEqual([{ message: 'error' }, { message: '1' }]);
734
+ });
735
+
736
+ test('BodyValidation', async () => {
737
+ class User {
738
+ username!: string & MinLength<3>;
739
+ }
740
+
741
+ class AddUserDto extends User {
742
+ imageUpload?: UploadedFile;
743
+ }
744
+
745
+ class Controller {
746
+ @http.POST('/action1')
747
+ action1(user: HttpBody<User>): any {
748
+ return user;
749
+ }
750
+
751
+ @http.POST('/action2')
752
+ action2(user: HttpBodyValidation<User>): User {
753
+ if (user.valid()) {
754
+ return user.value;
755
+ }
756
+
757
+ throw new HttpBadRequestError('Invalid: ' + user.error.getErrorMessageForPath('username'));
758
+ }
759
+
760
+ @http.POST('/action3')
761
+ action3(user: HttpBodyValidation<AddUserDto>): User {
762
+ if (user.valid()) {
763
+ return user.value;
764
+ }
765
+
766
+ throw new HttpBadRequestError('Invalid: ' + user.error.getErrorMessageForPath('username'));
767
+ }
768
+ }
719
769
 
720
770
  const httpKernel = createHttpKernel([Controller]);
771
+ expect((await httpKernel.request(HttpRequest.POST('/action1').json({ username: 'Peter' }))).json).toEqual({ username: 'Peter' });
772
+ expect((await httpKernel.request(HttpRequest.POST('/action1').json({ username: 'Pe' }))).json).toEqual({
773
+ errors: [{ code: 'minLength', message: 'Min length is 3', path: 'username' }], message: 'Validation error:\nusername(minLength): Min length is 3'
774
+ });
775
+
776
+ expect((await httpKernel.request(HttpRequest.POST('/action2').json({ username: 'Peter' }))).json).toEqual({ username: 'Peter' });
777
+ expect((await httpKernel.request(HttpRequest.POST('/action2').json({ username: 'Pe' }))).bodyString).toEqual(`{"message":"Invalid: Min length is 3"}`);
721
778
 
722
- expect((await httpKernel.request(HttpRequest.GET('/first/peter'))).json).toEqual(['peter']);
723
- expect((await httpKernel.request(HttpRequest.GET('/second/peter?second=true'))).json).toEqual(['peter', 'true']);
779
+ expect((await httpKernel.request(HttpRequest.POST('/action3').json({ username: 'Peter' }))).json).toEqual({ username: 'Peter' });
780
+ expect((await httpKernel.request(HttpRequest.POST('/action3').json({ username: 'Pe' }))).bodyString).toEqual(`{"message":"Invalid: Min length is 3"}`);
724
781
  });
package/tsconfig.json CHANGED
@@ -15,6 +15,7 @@
15
15
  "declaration": true,
16
16
  "composite": true
17
17
  },
18
+ "reflection": true,
18
19
  "include": [
19
20
  "src",
20
21
  "tests",
@@ -49,4 +50,4 @@
49
50
  "path": "../workflow/tsconfig.json"
50
51
  }
51
52
  ]
52
- }
53
+ }