@deepkit/http 1.0.1-alpha.59 → 1.0.1-alpha.63

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 (144) 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.js +5 -1
  26. package/dist/cjs/index.js.map +1 -1
  27. package/dist/cjs/src/controllers.d.ts +4 -4
  28. package/dist/cjs/src/controllers.js +3 -0
  29. package/dist/cjs/src/controllers.js.map +1 -1
  30. package/dist/cjs/src/decorator.d.ts +29 -94
  31. package/dist/cjs/src/decorator.js +53 -118
  32. package/dist/cjs/src/decorator.js.map +1 -1
  33. package/dist/cjs/src/filter.d.ts +1 -0
  34. package/dist/cjs/src/filter.js +11 -17
  35. package/dist/cjs/src/filter.js.map +1 -1
  36. package/dist/cjs/src/http.d.ts +5 -3
  37. package/dist/cjs/src/http.js +91 -55
  38. package/dist/cjs/src/http.js.map +1 -1
  39. package/dist/cjs/src/kernel.d.ts +3 -3
  40. package/dist/cjs/src/kernel.js +5 -25
  41. package/dist/cjs/src/kernel.js.map +1 -1
  42. package/dist/cjs/src/logger.d.ts +2 -2
  43. package/dist/cjs/src/logger.js +4 -8
  44. package/dist/cjs/src/logger.js.map +1 -1
  45. package/dist/cjs/src/middleware.d.ts +5 -1
  46. package/dist/cjs/src/middleware.js +15 -3
  47. package/dist/cjs/src/middleware.js.map +1 -1
  48. package/dist/cjs/src/model.d.ts +62 -2
  49. package/dist/cjs/src/model.js +101 -26
  50. package/dist/cjs/src/model.js.map +1 -1
  51. package/dist/cjs/src/module.config.d.ts +3 -4
  52. package/dist/cjs/src/module.config.js +8 -6
  53. package/dist/cjs/src/module.config.js.map +1 -1
  54. package/dist/cjs/src/module.d.ts +2 -3
  55. package/dist/cjs/src/module.js +4 -2
  56. package/dist/cjs/src/module.js.map +1 -1
  57. package/dist/cjs/src/router.d.ts +24 -53
  58. package/dist/cjs/src/router.js +140 -159
  59. package/dist/cjs/src/router.js.map +1 -1
  60. package/dist/cjs/src/static-serving.d.ts +1 -0
  61. package/dist/cjs/src/static-serving.js +46 -39
  62. package/dist/cjs/src/static-serving.js.map +1 -1
  63. package/dist/cjs/src/utils.js +1 -0
  64. package/dist/cjs/tests/filter.spec.js +61 -47
  65. package/dist/cjs/tests/filter.spec.js.map +1 -1
  66. package/dist/cjs/tests/middleware.spec.d.ts +1 -1
  67. package/dist/cjs/tests/middleware.spec.js +157 -145
  68. package/dist/cjs/tests/middleware.spec.js.map +1 -1
  69. package/dist/cjs/tests/model.spec.js +4 -4
  70. package/dist/cjs/tests/model.spec.js.map +1 -1
  71. package/dist/cjs/tests/module.spec.js +4 -3
  72. package/dist/cjs/tests/module.spec.js.map +1 -1
  73. package/dist/cjs/tests/router.spec.d.ts +1 -1
  74. package/dist/cjs/tests/router.spec.js +298 -333
  75. package/dist/cjs/tests/router.spec.js.map +1 -1
  76. package/dist/cjs/tests/utils.js +5 -2
  77. package/dist/cjs/tests/utils.js.map +1 -1
  78. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  79. package/dist/esm/src/controllers.d.ts +4 -4
  80. package/dist/esm/src/controllers.js +3 -0
  81. package/dist/esm/src/controllers.js.map +1 -1
  82. package/dist/esm/src/decorator.d.ts +29 -94
  83. package/dist/esm/src/decorator.js +49 -114
  84. package/dist/esm/src/decorator.js.map +1 -1
  85. package/dist/esm/src/filter.d.ts +1 -0
  86. package/dist/esm/src/filter.js +11 -18
  87. package/dist/esm/src/filter.js.map +1 -1
  88. package/dist/esm/src/http.d.ts +5 -3
  89. package/dist/esm/src/http.js +89 -55
  90. package/dist/esm/src/http.js.map +1 -1
  91. package/dist/esm/src/kernel.d.ts +3 -3
  92. package/dist/esm/src/kernel.js +6 -27
  93. package/dist/esm/src/kernel.js.map +1 -1
  94. package/dist/esm/src/logger.d.ts +2 -2
  95. package/dist/esm/src/logger.js +4 -9
  96. package/dist/esm/src/logger.js.map +1 -1
  97. package/dist/esm/src/middleware.d.ts +5 -1
  98. package/dist/esm/src/middleware.js +12 -0
  99. package/dist/esm/src/middleware.js.map +1 -1
  100. package/dist/esm/src/model.d.ts +62 -2
  101. package/dist/esm/src/model.js +92 -24
  102. package/dist/esm/src/model.js.map +1 -1
  103. package/dist/esm/src/module.config.d.ts +3 -4
  104. package/dist/esm/src/module.config.js +6 -5
  105. package/dist/esm/src/module.config.js.map +1 -1
  106. package/dist/esm/src/module.d.ts +2 -3
  107. package/dist/esm/src/module.js +5 -3
  108. package/dist/esm/src/module.js.map +1 -1
  109. package/dist/esm/src/router.d.ts +24 -53
  110. package/dist/esm/src/router.js +136 -155
  111. package/dist/esm/src/router.js.map +1 -1
  112. package/dist/esm/src/static-serving.d.ts +1 -0
  113. package/dist/esm/src/static-serving.js +39 -32
  114. package/dist/esm/src/static-serving.js.map +1 -1
  115. package/dist/esm/src/utils.js +1 -0
  116. package/dist/esm/tests/filter.spec.js +14 -0
  117. package/dist/esm/tests/filter.spec.js.map +1 -1
  118. package/dist/esm/tests/middleware.spec.d.ts +1 -1
  119. package/dist/esm/tests/middleware.spec.js +60 -48
  120. package/dist/esm/tests/middleware.spec.js.map +1 -1
  121. package/dist/esm/tests/module.spec.js +1 -0
  122. package/dist/esm/tests/module.spec.js.map +1 -1
  123. package/dist/esm/tests/router.spec.d.ts +1 -1
  124. package/dist/esm/tests/router.spec.js +167 -202
  125. package/dist/esm/tests/router.spec.js.map +1 -1
  126. package/dist/esm/tests/utils.js +4 -1
  127. package/dist/esm/tests/utils.js.map +1 -1
  128. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  129. package/package.json +14 -15
  130. package/src/controllers.ts +3 -3
  131. package/src/decorator.ts +42 -131
  132. package/src/filter.ts +0 -2
  133. package/src/http.ts +35 -36
  134. package/src/kernel.ts +4 -5
  135. package/src/logger.ts +2 -4
  136. package/src/middleware.ts +2 -1
  137. package/src/model.ts +80 -8
  138. package/src/module.config.ts +3 -6
  139. package/src/module.ts +2 -2
  140. package/src/router.ts +121 -161
  141. package/src/static-serving.ts +14 -12
  142. package/tests/middleware.spec.ts +0 -1
  143. package/tests/router.spec.ts +131 -133
  144. package/tsconfig.json +2 -1
@@ -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
  }
@@ -265,12 +262,12 @@ test('router body', async () => {
265
262
 
266
263
  test('router body is safe for simultaneous requests', async () => {
267
264
  class Body {
268
- @t username!: string;
265
+ username!: string;
269
266
  }
270
267
 
271
268
  class Controller {
272
269
  @http.POST()
273
- anyReq(@http.body() body: Body, req: HttpRequest) {
270
+ anyReq(body: HttpBody<Body>, req: HttpRequest) {
274
271
  return [body.username, body instanceof Body, req.url];
275
272
  }
276
273
  }
@@ -286,15 +283,31 @@ test('router body is safe for simultaneous requests', async () => {
286
283
  )
287
284
  });
288
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
+ });
289
302
 
290
303
  test('router body double', async () => {
291
304
  class Body {
292
- @t username!: string;
305
+ username!: string;
293
306
  }
294
307
 
295
308
  class Controller {
296
309
  @http.POST()
297
- anyReq(@http.body() body: Body, @http.body() body2: Body, req: HttpRequest) {
310
+ anyReq(body: HttpBody<Body>, body2: HttpBody<Body>, req: HttpRequest) {
298
311
  return [body2.username, body2 instanceof Body && body instanceof Body, req.url];
299
312
  }
300
313
  }
@@ -304,10 +317,6 @@ test('router body double', async () => {
304
317
  const action = [...httpData.getActions()][0];
305
318
  expect(action.methodName).toBe('anyReq');
306
319
  expect(action.httpMethods).toEqual(['POST']);
307
- expect(action.parameters['body']).not.toBeUndefined();
308
- expect(action.parameters['body'].name).toBe('body');
309
- expect(action.parameters['body2']).not.toBeUndefined();
310
- expect(action.parameters['body2'].name).toBe('body2');
311
320
 
312
321
  const httpKernel = createHttpKernel([Controller]);
313
322
 
@@ -364,7 +373,7 @@ test('router groups', async () => {
364
373
  test('router query', async () => {
365
374
  class Controller {
366
375
  @http.GET('my-action')
367
- anyReq(@http.query().optional test?: number) {
376
+ anyReq(test?: HttpQuery<number>) {
368
377
  return test;
369
378
  }
370
379
  }
@@ -374,9 +383,6 @@ test('router query', async () => {
374
383
  const action = [...httpData.getActions()][0];
375
384
  expect(action.methodName).toBe('anyReq');
376
385
  expect(action.httpMethods).toEqual(['GET']);
377
- expect(action.parameters['test']).not.toBeUndefined();
378
- expect(action.parameters['test'].name).toBe('test');
379
- expect(action.parameters['test'].type).toBe('query');
380
386
 
381
387
  const httpKernel = createHttpKernel([Controller]);
382
388
 
@@ -386,14 +392,14 @@ test('router query', async () => {
386
392
 
387
393
  test('router query all', async () => {
388
394
  class AnyReqQuery {
389
- @t.optional test?: string;
390
- @t.optional filter?: string;
391
- @t.optional page?: number;
395
+ test?: string;
396
+ filter?: string;
397
+ page?: number;
392
398
  }
393
399
 
394
400
  class Controller {
395
401
  @http.GET('my-action')
396
- anyReq(@http.queries() anyReqQuery: AnyReqQuery) {
402
+ anyReq(anyReqQuery: HttpQueries<AnyReqQuery>) {
397
403
  return anyReqQuery;
398
404
  }
399
405
  }
@@ -407,14 +413,13 @@ test('router query all', async () => {
407
413
 
408
414
  test('serializer options', async () => {
409
415
  class User {
410
- @t username!: string;
411
- @t.group('sensitive') password!: string;
416
+ username!: string;
417
+ password!: string & Group<'sensitive'>;
412
418
  }
413
419
 
414
420
  class Controller {
415
421
  @http.GET().serialization({ groupsExclude: ['sensitive'] })
416
- @t.type(User)
417
- anyReq() {
422
+ anyReq(): User {
418
423
  return { username: 'Peter', password: 'secret' };
419
424
  }
420
425
  }
@@ -426,14 +431,13 @@ test('serializer options', async () => {
426
431
 
427
432
  test('hook after serializer', async () => {
428
433
  class User {
429
- @t username!: string;
430
- @t.group('sensitive') password!: string;
434
+ username!: string;
435
+ password!: string & Group<'sensitive'>;
431
436
  }
432
437
 
433
438
  class Controller {
434
439
  @http.GET().serialization({ groupsExclude: ['sensitive'] })
435
- @t.type(User)
436
- async anyReq() {
440
+ async anyReq(): Promise<User> {
437
441
  await sleep(0.1);
438
442
  return { username: 'Peter', password: 'secret' };
439
443
  }
@@ -461,10 +465,9 @@ test('invalid route definition', async () => {
461
465
  }
462
466
 
463
467
  const httpKernel = createHttpKernel([Controller]);
464
- expect((await httpKernel.request(HttpRequest.GET('/'))).bodyString).toEqual('Not found');
468
+ expect((await httpKernel.request(HttpRequest.GET('/'))).bodyString).toEqual('Internal error');
465
469
  });
466
470
 
467
-
468
471
  test('inject request storage ClassType', async () => {
469
472
  class User {
470
473
  constructor(public username: string) {
@@ -478,7 +481,7 @@ test('inject request storage ClassType', async () => {
478
481
  }
479
482
 
480
483
  @http.GET('optional')
481
- doItOptional(@inject().optional user?: User) {
484
+ doItOptional(user?: User) {
482
485
  return { isUser: user instanceof User };
483
486
  }
484
487
  }
@@ -494,7 +497,7 @@ test('inject request storage ClassType', async () => {
494
497
 
495
498
  const httpKernel = createHttpKernel([Controller], [
496
499
  {
497
- provide: User, scope: 'http', deps: [HttpRequest], useFactory(request: HttpRequest) {
500
+ provide: User, scope: 'http', useFactory(request: HttpRequest) {
498
501
  return request.store.user;
499
502
  }
500
503
  }
@@ -506,40 +509,6 @@ test('inject request storage ClassType', async () => {
506
509
  expect((await httpKernel.request(HttpRequest.GET('/optional').headers({ authorization: 'no' }))).json).toEqual({ isUser: false });
507
510
  });
508
511
 
509
-
510
- test('inject request storage @inject', async () => {
511
- class User {
512
- constructor(public username: string) {
513
- }
514
- }
515
-
516
- class Controller {
517
- @http.GET()
518
- doIt(@inject('user') user: any) {
519
- return { isUser: user instanceof User, username: user.username };
520
- }
521
- }
522
-
523
- class Listener {
524
- @eventDispatcher.listen(httpWorkflow.onAuth)
525
- onAuth(event: typeof httpWorkflow.onAuth.event) {
526
- event.request.store.user = new User('bar');
527
- }
528
- }
529
-
530
- const httpKernel = createHttpKernel([Controller], [
531
- {
532
- provide: 'user', scope: 'http', deps: [HttpRequest], useFactory(request: HttpRequest) {
533
- return request.store.user;
534
- }
535
- }
536
- ], [Listener]);
537
-
538
- const result = (await httpKernel.request(HttpRequest.GET('/'))).json;
539
- expect(result.isUser).toBe(true);
540
- expect(result.username).toBe('bar');
541
- });
542
-
543
512
  test('custom request handling', async () => {
544
513
  class Listener {
545
514
  @eventDispatcher.listen(httpWorkflow.onRouteNotFound)
@@ -613,9 +582,8 @@ test('promise serializer', async () => {
613
582
  }
614
583
 
615
584
  @http.GET('2')
616
- @t.string
617
- async anyReq2() {
618
- return 1;
585
+ async anyReq2(): Promise<string> {
586
+ return 1 as any;
619
587
  }
620
588
  }
621
589
 
@@ -625,20 +593,31 @@ test('promise serializer', async () => {
625
593
  expect((await httpKernel.request(HttpRequest.GET('/2'))).json).toBe('1');
626
594
  });
627
595
 
628
-
629
596
  test('unions', async () => {
630
597
  class Controller {
631
598
  @http.GET('/list')
632
- list(@http.query() @t.union(t.number, t.boolean) page: number | boolean) {
599
+ list(page: HttpQuery<number | boolean>) {
633
600
  return page;
634
601
  }
635
602
  }
636
603
 
637
604
  const httpKernel = createHttpKernel([Controller]);
638
605
 
606
+ expect((await httpKernel.request(HttpRequest.GET('/list?page=0'))).json).toEqual(0);
639
607
  expect((await httpKernel.request(HttpRequest.GET('/list?page=1'))).json).toEqual(1);
640
608
  expect((await httpKernel.request(HttpRequest.GET('/list?page=2222'))).json).toEqual(2222);
641
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
+ });
642
621
  });
643
622
 
644
623
  test('router dotToUrlPath', () => {
@@ -650,9 +629,9 @@ test('router dotToUrlPath', () => {
650
629
 
651
630
  test('router url resolve', async () => {
652
631
  class AnyReqQuery {
653
- @t.optional test?: string;
654
- @t.optional filter?: string;
655
- @t.optional page?: number;
632
+ test?: string;
633
+ filter?: string;
634
+ page?: number;
656
635
  }
657
636
 
658
637
  class Controller {
@@ -667,22 +646,22 @@ test('router url resolve', async () => {
667
646
  }
668
647
 
669
648
  @http.GET('').name('secondQuery')
670
- secondQuery(@http.query() peter: string) {
649
+ secondQuery(peter: HttpQuery<string>) {
671
650
  return '';
672
651
  }
673
652
 
674
653
  @http.GET('').name('secondQuery2')
675
- secondQuery2(@http.query('changed') peter: string) {
654
+ secondQuery2(peter: HttpQuery<string, { name: 'changed' }>) {
676
655
  return '';
677
656
  }
678
657
 
679
658
  @http.GET('third').name('third')
680
- third(@http.queries() params: AnyReqQuery) {
659
+ third(params: HttpQueries<AnyReqQuery>) {
681
660
  return params;
682
661
  }
683
662
 
684
663
  @http.GET('third2').name('third2')
685
- third2(@http.queries('deep') params: AnyReqQuery) {
664
+ third2(params: HttpQueries<AnyReqQuery, { name: 'deep' }>) {
686
665
  return params;
687
666
  }
688
667
  }
@@ -703,15 +682,14 @@ test('router url resolve', async () => {
703
682
  });
704
683
 
705
684
  test('destructing params', async () => {
706
-
707
- class DTO {
708
- @t.required name!: string;
709
- @t.required title!: string;
685
+ interface DTO {
686
+ name: string;
687
+ title: string;
710
688
  }
711
689
 
712
690
  class Controller {
713
691
  @http.GET('')
714
- first(@http.body() { name, title }: DTO) {
692
+ first({ name, title }: HttpBody<DTO>) {
715
693
  return [name, title];
716
694
  }
717
695
  }
@@ -721,63 +699,83 @@ test('destructing params', async () => {
721
699
  expect((await httpKernel.request(HttpRequest.GET('/').json({ name: 'Peter', title: 'CTO' }))).json).toEqual(['Peter', 'CTO']);
722
700
  });
723
701
 
724
- test('dynamic parameter name', async () => {
725
- @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
+
726
711
  class Controller {
727
- @http.GET('first/:another')
728
- first(@t.description('The identifier').name('another') id: string) {
729
- return [id];
712
+ @http.GET('/action1')
713
+ action1(): DefaultResponse[] {
714
+ return [{ title: 'a' }, { title: '1' }];
730
715
  }
731
716
 
732
- @http.GET('second/:another2')
733
- second(@t.description('The identifier').name('another2') id: string, @http.query() second: string) {
734
- return [id, second];
717
+ @http.GET('/action2').response<DefaultResponse[]>(200, `List`)
718
+ action2() {
719
+ return [{ title: 'a' }, { title: 1 }];
735
720
  }
736
- }
737
721
 
738
- const schema = getClassSchema(Controller);
739
- expect(schema.getMethodProperties('first')[0].name).toBe('another');
740
- expect(schema.getMethodProperties('second')[0].name).toBe('another2');
741
- expect(schema.getMethodProperties('second')[1].name).toBe('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);
727
+ }
728
+ }
742
729
 
743
730
  const httpKernel = createHttpKernel([Controller]);
744
-
745
- expect((await httpKernel.request(HttpRequest.GET('/first/peter'))).json).toEqual(['peter']);
746
- expect((await httpKernel.request(HttpRequest.GET('/second/peter?second=true'))).json).toEqual(['peter', 'true']);
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' }]);
747
734
  });
748
735
 
736
+ test('BodyValidation', async () => {
737
+ class User {
738
+ username!: string & MinLength<3>;
739
+ }
749
740
 
750
- test('use http.response for serialization', async () => {
751
- const schemaString = t.schema({
752
- title: t.string
753
- });
754
-
755
- const errorSchema = t.schema({
756
- message: t.string
757
- });
741
+ class AddUserDto extends User {
742
+ imageUpload?: UploadedFile;
743
+ }
758
744
 
759
745
  class Controller {
760
- @http.GET('/action1')
761
- @t.array(schemaString)
762
- action1() {
763
- return [{title: 'a'}, {title: 1}];
746
+ @http.POST('/action1')
747
+ action1(user: HttpBody<User>): any {
748
+ return user;
764
749
  }
765
750
 
766
- @http.GET('/action2').response(200, `List`, t.array(schemaString))
767
- action2() {
768
- return [{title: 'a'}, {title: 1}];
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'));
769
758
  }
770
759
 
771
- @http.GET('/action3')
772
- .response(200, `List`, t.array(schemaString))
773
- .response(400, `Error`, t.array(errorSchema))
774
- action3() {
775
- return new JSONResponse([{message: 'error'}, {message: 1}]).status(400);
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'));
776
767
  }
777
768
  }
778
769
 
779
770
  const httpKernel = createHttpKernel([Controller]);
780
- expect((await httpKernel.request(HttpRequest.GET('/action1'))).json).toEqual([{title: 'a'}, {title: '1'}]);
781
- expect((await httpKernel.request(HttpRequest.GET('/action2'))).json).toEqual([{title: 'a'}, {title: '1'}]);
782
- expect((await httpKernel.request(HttpRequest.GET('/action3'))).json).toEqual([{message: 'error'}, {message: '1'}]);
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"}`);
778
+
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"}`);
783
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
+ }