@e22m4u/ts-rest-router 0.2.0 → 0.2.2

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 (44) hide show
  1. package/README.md +129 -34
  2. package/dist/cjs/index.cjs +87 -23
  3. package/dist/esm/controller-registry.js +9 -4
  4. package/dist/esm/decorators/after-action/after-action-reflector.spec.js +0 -1
  5. package/dist/esm/decorators/before-action/before-action-reflector.spec.js +0 -1
  6. package/dist/esm/decorators/index.d.ts +1 -0
  7. package/dist/esm/decorators/index.js +1 -0
  8. package/dist/esm/decorators/request-context/request-context-reflector.spec.js +0 -1
  9. package/dist/esm/decorators/request-data/request-data-decorator.d.ts +11 -11
  10. package/dist/esm/decorators/request-data/request-data-decorator.js +3 -3
  11. package/dist/esm/decorators/request-data/request-data-reflector.spec.js +0 -1
  12. package/dist/esm/decorators/response-body/index.d.ts +3 -0
  13. package/dist/esm/decorators/response-body/index.js +3 -0
  14. package/dist/esm/decorators/response-body/response-body-decorator.d.ts +9 -0
  15. package/dist/esm/decorators/response-body/response-body-decorator.js +23 -0
  16. package/dist/esm/decorators/response-body/response-body-decorator.spec.d.ts +1 -0
  17. package/dist/esm/decorators/response-body/response-body-decorator.spec.js +61 -0
  18. package/dist/esm/decorators/response-body/response-body-metadata.d.ts +16 -0
  19. package/dist/esm/decorators/response-body/response-body-metadata.js +5 -0
  20. package/dist/esm/decorators/response-body/response-body-reflector.d.ts +22 -0
  21. package/dist/esm/decorators/response-body/response-body-reflector.js +29 -0
  22. package/dist/esm/decorators/response-body/response-body-reflector.spec.d.ts +1 -0
  23. package/dist/esm/decorators/response-body/response-body-reflector.spec.js +58 -0
  24. package/dist/esm/decorators/rest-action/rest-action-reflector.spec.js +0 -1
  25. package/dist/esm/decorators/rest-controller/rest-controller-reflector.spec.js +0 -1
  26. package/eslint.config.js +1 -1
  27. package/package.json +15 -15
  28. package/src/controller-registry.spec.ts +85 -27
  29. package/src/controller-registry.ts +13 -4
  30. package/src/debuggable-service.spec.ts +0 -1
  31. package/src/decorators/after-action/after-action-reflector.spec.ts +0 -1
  32. package/src/decorators/before-action/before-action-reflector.spec.ts +0 -1
  33. package/src/decorators/index.ts +1 -0
  34. package/src/decorators/request-context/request-context-reflector.spec.ts +0 -1
  35. package/src/decorators/request-data/request-data-decorator.ts +3 -11
  36. package/src/decorators/request-data/request-data-reflector.spec.ts +0 -1
  37. package/src/decorators/response-body/index.ts +3 -0
  38. package/src/decorators/response-body/response-body-decorator.spec.ts +40 -0
  39. package/src/decorators/response-body/response-body-decorator.ts +43 -0
  40. package/src/decorators/response-body/response-body-metadata.ts +20 -0
  41. package/src/decorators/response-body/response-body-reflector.spec.ts +59 -0
  42. package/src/decorators/response-body/response-body-reflector.ts +41 -0
  43. package/src/decorators/rest-action/rest-action-reflector.spec.ts +0 -1
  44. package/src/decorators/rest-controller/rest-controller-reflector.spec.ts +0 -1
@@ -0,0 +1,23 @@
1
+ import { DecoratorTargetType } from '@e22m4u/ts-reflector';
2
+ import { getDecoratorTargetType } from '@e22m4u/ts-reflector';
3
+ import { ResponseBodyReflector } from './response-body-reflector.js';
4
+ /**
5
+ * Response body decorator.
6
+ *
7
+ * @param schemaOrType
8
+ */
9
+ export function responseBody(schemaOrType) {
10
+ return function (target, propertyKey, descriptor) {
11
+ const decoratorType = getDecoratorTargetType(target, propertyKey, descriptor);
12
+ if (decoratorType !== DecoratorTargetType.INSTANCE_METHOD)
13
+ throw new Error('@responseBody decorator is only supported on an instance method.');
14
+ let schema;
15
+ if (typeof schemaOrType === 'object') {
16
+ schema = schemaOrType;
17
+ }
18
+ else if (typeof schemaOrType === 'string') {
19
+ schema = { type: schemaOrType };
20
+ }
21
+ ResponseBodyReflector.setMetadata(schema ? { schema } : {}, target.constructor, propertyKey);
22
+ };
23
+ }
@@ -0,0 +1,61 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { expect } from 'chai';
11
+ import { DataType } from '@e22m4u/ts-data-schema';
12
+ import { responseBody } from './response-body-decorator.js';
13
+ import { ResponseBodyReflector } from './response-body-reflector.js';
14
+ describe('responseBody', function () {
15
+ it('does not require arguments', function () {
16
+ class Target {
17
+ myMethod() { }
18
+ }
19
+ __decorate([
20
+ responseBody(),
21
+ __metadata("design:type", Function),
22
+ __metadata("design:paramtypes", []),
23
+ __metadata("design:returntype", void 0)
24
+ ], Target.prototype, "myMethod", null);
25
+ const res = ResponseBodyReflector.getMetadata(Target);
26
+ expect(res.get('myMethod')).to.be.eql({});
27
+ });
28
+ it('sets the given DataType to the target metadata', function () {
29
+ class Target {
30
+ myMethod() { }
31
+ }
32
+ __decorate([
33
+ responseBody(DataType.STRING),
34
+ __metadata("design:type", Function),
35
+ __metadata("design:paramtypes", []),
36
+ __metadata("design:returntype", void 0)
37
+ ], Target.prototype, "myMethod", null);
38
+ const res = ResponseBodyReflector.getMetadata(Target);
39
+ expect(res.get('myMethod')).to.be.eql({ schema: { type: DataType.STRING } });
40
+ });
41
+ it('sets the given schema to the target metadata', function () {
42
+ const schema = {
43
+ type: DataType.OBJECT,
44
+ properties: {
45
+ foo: { type: DataType.STRING },
46
+ bar: { type: DataType.NUMBER },
47
+ },
48
+ };
49
+ class Target {
50
+ myMethod() { }
51
+ }
52
+ __decorate([
53
+ responseBody(schema),
54
+ __metadata("design:type", Function),
55
+ __metadata("design:paramtypes", []),
56
+ __metadata("design:returntype", void 0)
57
+ ], Target.prototype, "myMethod", null);
58
+ const res = ResponseBodyReflector.getMetadata(Target);
59
+ expect(res.get('myMethod')).to.be.eql({ schema });
60
+ });
61
+ });
@@ -0,0 +1,16 @@
1
+ import { MetadataKey } from '@e22m4u/ts-reflector';
2
+ import { DataSchema } from '@e22m4u/ts-data-schema';
3
+ /**
4
+ * Response body metadata.
5
+ */
6
+ export type ResponseBodyMetadata = {
7
+ schema?: DataSchema;
8
+ };
9
+ /**
10
+ * Response body metadata map.
11
+ */
12
+ export type ResponseBodyMetadataMap = Map<string, ResponseBodyMetadata>;
13
+ /**
14
+ * Response body metadata key.
15
+ */
16
+ export declare const RESPONSE_BODY_METADATA_KEY: MetadataKey<ResponseBodyMetadataMap>;
@@ -0,0 +1,5 @@
1
+ import { MetadataKey } from '@e22m4u/ts-reflector';
2
+ /**
3
+ * Response body metadata key.
4
+ */
5
+ export const RESPONSE_BODY_METADATA_KEY = new MetadataKey('responseBodyMetadataKey');
@@ -0,0 +1,22 @@
1
+ import { Constructor } from '../../types.js';
2
+ import { ResponseBodyMetadata } from './response-body-metadata.js';
3
+ import { ResponseBodyMetadataMap } from './response-body-metadata.js';
4
+ /**
5
+ * Response body reflector.
6
+ */
7
+ export declare class ResponseBodyReflector {
8
+ /**
9
+ * Set metadata.
10
+ *
11
+ * @param metadata
12
+ * @param target
13
+ * @param propertyKey
14
+ */
15
+ static setMetadata(metadata: ResponseBodyMetadata, target: Constructor, propertyKey: string): void;
16
+ /**
17
+ * Get metadata.
18
+ *
19
+ * @param target
20
+ */
21
+ static getMetadata(target: Constructor): ResponseBodyMetadataMap;
22
+ }
@@ -0,0 +1,29 @@
1
+ import { Reflector } from '@e22m4u/ts-reflector';
2
+ import { RESPONSE_BODY_METADATA_KEY } from './response-body-metadata.js';
3
+ /**
4
+ * Response body reflector.
5
+ */
6
+ export class ResponseBodyReflector {
7
+ /**
8
+ * Set metadata.
9
+ *
10
+ * @param metadata
11
+ * @param target
12
+ * @param propertyKey
13
+ */
14
+ static setMetadata(metadata, target, propertyKey) {
15
+ const oldMap = Reflector.getOwnMetadata(RESPONSE_BODY_METADATA_KEY, target);
16
+ const newMap = new Map(oldMap);
17
+ newMap.set(propertyKey, metadata);
18
+ Reflector.defineMetadata(RESPONSE_BODY_METADATA_KEY, newMap, target);
19
+ }
20
+ /**
21
+ * Get metadata.
22
+ *
23
+ * @param target
24
+ */
25
+ static getMetadata(target) {
26
+ const metadata = Reflector.getOwnMetadata(RESPONSE_BODY_METADATA_KEY, target);
27
+ return metadata ?? new Map();
28
+ }
29
+ }
@@ -0,0 +1,58 @@
1
+ import { expect } from 'chai';
2
+ import { Reflector } from '@e22m4u/ts-reflector';
3
+ import { ResponseBodyReflector } from './response-body-reflector.js';
4
+ import { RESPONSE_BODY_METADATA_KEY } from './response-body-metadata.js';
5
+ describe('ResponseBodyReflector', function () {
6
+ describe('setMetadata', function () {
7
+ it('sets a given value as target metadata', function () {
8
+ class Target {
9
+ }
10
+ const md1 = {};
11
+ const md2 = {};
12
+ ResponseBodyReflector.setMetadata(md1, Target, 'propertyKey1');
13
+ ResponseBodyReflector.setMetadata(md2, Target, 'propertyKey2');
14
+ const res = Reflector.getOwnMetadata(RESPONSE_BODY_METADATA_KEY, Target);
15
+ expect(res).to.be.instanceof(Map);
16
+ expect(res.get('propertyKey1')).to.be.eq(md1);
17
+ expect(res.get('propertyKey2')).to.be.eq(md2);
18
+ });
19
+ it('overrides existing metadata', function () {
20
+ class Target {
21
+ }
22
+ const md1 = {};
23
+ const md2 = {};
24
+ ResponseBodyReflector.setMetadata(md1, Target, 'propertyKey');
25
+ const res1 = Reflector.getOwnMetadata(RESPONSE_BODY_METADATA_KEY, Target);
26
+ expect(res1).to.be.instanceof(Map);
27
+ expect(res1.get('propertyKey')).to.be.eq(md1);
28
+ ResponseBodyReflector.setMetadata(md2, Target, 'propertyKey');
29
+ const res2 = Reflector.getOwnMetadata(RESPONSE_BODY_METADATA_KEY, Target);
30
+ expect(res2).to.be.instanceof(Map);
31
+ expect(res2.get('propertyKey')).to.be.eq(md2);
32
+ });
33
+ });
34
+ describe('getMetadata', function () {
35
+ it('returns an existing metadata of the target', function () {
36
+ class Target {
37
+ }
38
+ const md1 = {};
39
+ const md2 = {};
40
+ const mdMap = new Map([
41
+ ['propertyKey1', md1],
42
+ ['propertyKey2', md2],
43
+ ]);
44
+ Reflector.defineMetadata(RESPONSE_BODY_METADATA_KEY, mdMap, Target);
45
+ const res = ResponseBodyReflector.getMetadata(Target);
46
+ expect(res).to.be.instanceof(Map);
47
+ expect(res.get('propertyKey1')).to.be.eq(md1);
48
+ expect(res.get('propertyKey2')).to.be.eq(md2);
49
+ });
50
+ it('returns an empty map if no metadata', function () {
51
+ class Target {
52
+ }
53
+ const res = ResponseBodyReflector.getMetadata(Target);
54
+ expect(res).to.be.instanceof(Map);
55
+ expect(res).to.be.empty;
56
+ });
57
+ });
58
+ });
@@ -1,5 +1,4 @@
1
1
  import { expect } from 'chai';
2
- import { describe } from 'mocha';
3
2
  import { Reflector } from '@e22m4u/ts-reflector';
4
3
  import { HttpMethod } from '@e22m4u/js-trie-router';
5
4
  import { RestActionReflector } from './rest-action-reflector.js';
@@ -1,5 +1,4 @@
1
1
  import { expect } from 'chai';
2
- import { describe } from 'mocha';
3
2
  import { Reflector } from '@e22m4u/ts-reflector';
4
3
  import { RestControllerReflector } from './rest-controller-reflector.js';
5
4
  import { REST_CONTROLLER_METADATA_KEY } from './rest-controller-metadata.js';
package/eslint.config.js CHANGED
@@ -31,7 +31,7 @@ export default [
31
31
  rules: {
32
32
  ...eslintJs.configs.recommended.rules,
33
33
  ...eslintPrettierConfig.rules,
34
- ...eslintMochaPlugin.configs.flat.recommended.rules,
34
+ ...eslintMochaPlugin.configs.recommended.rules,
35
35
  ...eslintChaiExpectPlugin.configs['recommended-flat'].rules,
36
36
  ...eslintTypescript.configs.recommended.reduce(
37
37
  (rules, config) => ({...rules, ...config.rules}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e22m4u/ts-rest-router",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "REST маршрутизатор на основе контроллеров для TypeScript",
5
5
  "author": "e22m4u <e22m4u@yandex.ru>",
6
6
  "license": "MIT",
@@ -41,36 +41,36 @@
41
41
  "prepare": "husky"
42
42
  },
43
43
  "dependencies": {
44
- "@e22m4u/js-debug": "~0.0.6",
44
+ "@e22m4u/js-debug": "~0.1.0",
45
45
  "@e22m4u/js-format": "~0.1.0",
46
46
  "@e22m4u/js-service": "~0.2.0",
47
47
  "@e22m4u/js-trie-router": "~0.0.1",
48
- "@e22m4u/ts-data-schema": "~0.0.1",
48
+ "@e22m4u/ts-data-schema": "~0.2.1",
49
49
  "@e22m4u/ts-reflector": "~0.1.0",
50
50
  "http-errors": "~2.0.0"
51
51
  },
52
52
  "devDependencies": {
53
- "@commitlint/cli": "~19.8.0",
54
- "@commitlint/config-conventional": "~19.8.0",
55
- "@eslint/js": "~9.25.1",
56
- "@types/chai": "~5.2.1",
53
+ "@commitlint/cli": "~19.8.1",
54
+ "@commitlint/config-conventional": "~19.8.1",
55
+ "@eslint/js": "~9.28.0",
56
+ "@types/chai": "~5.2.2",
57
57
  "@types/debug": "~4.1.12",
58
58
  "@types/http-errors": "~2.0.4",
59
59
  "@types/mocha": "~10.0.10",
60
- "@types/node": "~22.14.1",
60
+ "@types/node": "~22.15.29",
61
61
  "c8": "~10.1.3",
62
62
  "chai": "~5.2.0",
63
- "esbuild": "~0.25.3",
64
- "eslint": "~9.25.1",
65
- "eslint-config-prettier": "~10.1.2",
63
+ "esbuild": "~0.25.5",
64
+ "eslint": "~9.28.0",
65
+ "eslint-config-prettier": "~10.1.5",
66
66
  "eslint-plugin-chai-expect": "~3.1.0",
67
- "eslint-plugin-mocha": "~10.5.0",
67
+ "eslint-plugin-mocha": "~11.1.0",
68
68
  "husky": "~9.1.7",
69
- "mocha": "~11.1.0",
69
+ "mocha": "~11.5.0",
70
70
  "prettier": "~3.5.3",
71
71
  "rimraf": "~6.0.1",
72
- "tsx": "~4.19.3",
72
+ "tsx": "~4.19.4",
73
73
  "typescript": "~5.8.3",
74
- "typescript-eslint": "~8.31.0"
74
+ "typescript-eslint": "~8.33.0"
75
75
  }
76
76
  }
@@ -1,32 +1,38 @@
1
1
  /* eslint mocha/no-sibling-hooks: 0 */
2
+ import {
3
+ createRequestMock,
4
+ createResponseMock,
5
+ HookName,
6
+ HttpMethod,
7
+ ParsedCookie,
8
+ ParsedHeaders,
9
+ ParsedParams,
10
+ ParsedQuery,
11
+ RequestContext,
12
+ RequestParser,
13
+ RouteRegistry,
14
+ TrieRouter,
15
+ } from '@e22m4u/js-trie-router';
16
+ import {
17
+ afterAction,
18
+ beforeAction,
19
+ getAction,
20
+ postAction,
21
+ requestBody,
22
+ requestCookie,
23
+ requestCookies,
24
+ requestField,
25
+ requestHeader,
26
+ requestHeaders,
27
+ requestParam,
28
+ requestParams,
29
+ requestQueries,
30
+ requestQuery,
31
+ restController,
32
+ } from './decorators/index.js';
33
+
2
34
  import {expect} from 'chai';
3
- import {HookName} from '@e22m4u/js-trie-router';
4
- import {getAction} from './decorators/index.js';
5
- import {postAction} from './decorators/index.js';
6
- import {TrieRouter} from '@e22m4u/js-trie-router';
7
- import {HttpMethod} from '@e22m4u/js-trie-router';
8
- import {requestBody} from './decorators/index.js';
9
- import {afterAction} from './decorators/index.js';
10
- import {requestQuery} from './decorators/index.js';
11
- import {requestParam} from './decorators/index.js';
12
- import {requestField} from './decorators/index.js';
13
- import {beforeAction} from './decorators/index.js';
14
- import {ParsedQuery} from '@e22m4u/js-trie-router';
15
- import {ParsedCookie} from '@e22m4u/js-trie-router';
16
- import {ParsedParams} from '@e22m4u/js-trie-router';
17
- import {requestCookie} from './decorators/index.js';
18
- import {requestParams} from './decorators/index.js';
19
- import {requestHeader} from './decorators/index.js';
20
- import {requestCookies} from './decorators/index.js';
21
- import {requestQueries} from './decorators/index.js';
22
- import {requestHeaders} from './decorators/index.js';
23
- import {restController} from './decorators/index.js';
24
- import {ParsedHeaders} from '@e22m4u/js-trie-router';
25
- import {RouteRegistry} from '@e22m4u/js-trie-router';
26
- import {RequestParser} from '@e22m4u/js-trie-router';
27
- import {RequestContext} from '@e22m4u/js-trie-router';
28
- import {createRequestMock} from '@e22m4u/js-trie-router';
29
- import {createResponseMock} from '@e22m4u/js-trie-router';
35
+ import {DataType} from '@e22m4u/ts-data-schema';
30
36
  import {ControllerRegistry} from './controller-registry.js';
31
37
 
32
38
  const PRE_HANDLER_1 = () => undefined;
@@ -911,4 +917,56 @@ describe('ControllerRegistry', function () {
911
917
  expect(S.hasController(MyController)).to.be.true;
912
918
  });
913
919
  });
920
+
921
+ describe('createRouteHandler', function () {
922
+ it('uses default values from schema as copy', async function () {
923
+ let invoked = false;
924
+ const defaultValue = {foo: 'bar'};
925
+ class MyController {
926
+ myAction(
927
+ @requestBody({
928
+ type: DataType.OBJECT,
929
+ default: defaultValue,
930
+ })
931
+ body: object,
932
+ ) {
933
+ expect(body).to.be.not.eq(defaultValue);
934
+ expect(body).to.be.eql(defaultValue);
935
+ invoked = true;
936
+ }
937
+ }
938
+ const S = new ControllerRegistry();
939
+ const req = createRequestMock();
940
+ const res = createResponseMock();
941
+ const ctx = new RequestContext(S.container, req, res);
942
+ const handler = S['createRouteHandler'](MyController, 'myAction');
943
+ await handler(ctx);
944
+ expect(invoked).to.be.true;
945
+ });
946
+
947
+ it('uses default values from factory function that defined in schema as copy', async function () {
948
+ let invoked = false;
949
+ const defaultValue = {foo: 'bar'};
950
+ class MyController {
951
+ myAction(
952
+ @requestBody({
953
+ type: DataType.OBJECT,
954
+ default: () => defaultValue,
955
+ })
956
+ body: object,
957
+ ) {
958
+ expect(body).to.be.not.eq(defaultValue);
959
+ expect(body).to.be.eql(defaultValue);
960
+ invoked = true;
961
+ }
962
+ }
963
+ const S = new ControllerRegistry();
964
+ const req = createRequestMock();
965
+ const res = createResponseMock();
966
+ const ctx = new RequestContext(S.container, req, res);
967
+ const handler = S['createRouteHandler'](MyController, 'myAction');
968
+ await handler(ctx);
969
+ expect(invoked).to.be.true;
970
+ });
971
+ });
914
972
  });
@@ -14,6 +14,7 @@ import {DebuggableService} from './debuggable-service.js';
14
14
  import {RestActionReflector} from './decorators/index.js';
15
15
  import {RequestDataReflector} from './decorators/index.js';
16
16
  import {AfterActionReflector} from './decorators/index.js';
17
+ import {DefaultValuesApplier} from '@e22m4u/ts-data-schema';
17
18
  import {BeforeActionReflector} from './decorators/index.js';
18
19
  import {RestControllerReflector} from './decorators/index.js';
19
20
  import {RequestContextReflector} from './decorators/index.js';
@@ -381,6 +382,7 @@ export class ControllerRegistry extends DebuggableService {
381
382
  actionName,
382
383
  );
383
384
  const argsNumber = controllerCtor.prototype[actionName].length;
385
+ const defaultsApplier = this.getService(DefaultValuesApplier);
384
386
  const dataTypeCaster = this.getService(DataTypeCaster);
385
387
  const dataValidator = this.getService(DataValidator);
386
388
  return (requestContext: RequestContext) => {
@@ -445,20 +447,27 @@ export class ControllerRegistry extends DebuggableService {
445
447
  break;
446
448
  }
447
449
  debug('Request data source is %v.', requestDataMd.source);
448
- // при наличии схемы данных выполняется
449
- // их конвертация и валидация
450
+ // при наличии схемы данных применяются значения
451
+ // по умолчанию, выполняется конвертация входящего
452
+ // значения и валидация согласно схеме
450
453
  if (requestDataMd.schema) {
454
+ data = defaultsApplier.applyDefaultValuesIfNeeded(
455
+ data,
456
+ requestDataMd.schema,
457
+ requestDataMd.source,
458
+ );
459
+ debug('Default values applied.');
451
460
  data = dataTypeCaster.cast(data, requestDataMd.schema, {
452
461
  noTypeCastError: true,
453
462
  sourcePath: requestDataMd.source,
454
463
  });
455
- debug('Data type casting is passed.');
464
+ debug('Data type casting applied.');
456
465
  dataValidator.validate(
457
466
  data,
458
467
  requestDataMd.schema,
459
468
  requestDataMd.source,
460
469
  );
461
- debug('Data validation is passed.');
470
+ debug('Data validation passed.');
462
471
  }
463
472
  // если свойство данных не определено,
464
473
  // то используем весь объекта данных
@@ -1,5 +1,4 @@
1
1
  import {expect} from 'chai';
2
- import {describe} from 'mocha';
3
2
  import {Service} from '@e22m4u/js-service';
4
3
  import {DebuggableService} from './debuggable-service.js';
5
4
 
@@ -1,5 +1,4 @@
1
1
  import {expect} from 'chai';
2
- import {describe} from 'mocha';
3
2
  import {Reflector} from '@e22m4u/ts-reflector';
4
3
  import {AfterActionReflector} from './after-action-reflector.js';
5
4
  import {AFTER_ACTION_METADATA_KEY} from './after-action-metadata.js';
@@ -1,5 +1,4 @@
1
1
  import {expect} from 'chai';
2
- import {describe} from 'mocha';
3
2
  import {Reflector} from '@e22m4u/ts-reflector';
4
3
  import {BeforeActionReflector} from './before-action-reflector.js';
5
4
  import {BEFORE_ACTION_METADATA_KEY} from './before-action-metadata.js';
@@ -2,5 +2,6 @@ export * from './rest-action/index.js';
2
2
  export * from './request-data/index.js';
3
3
  export * from './after-action/index.js';
4
4
  export * from './before-action/index.js';
5
+ export * from './response-body/index.js';
5
6
  export * from './rest-controller/index.js';
6
7
  export * from './request-context/index.js';
@@ -1,5 +1,4 @@
1
1
  import {expect} from 'chai';
2
- import {describe} from 'mocha';
3
2
  import {Reflector} from '@e22m4u/ts-reflector';
4
3
  import {RequestContextMetadata} from './request-context-metadata.js';
5
4
  import {RequestContextReflector} from './request-context-reflector.js';
@@ -20,16 +20,8 @@ export type RequestDataOptions = RequestDataMetadata;
20
20
  * @param options
21
21
  */
22
22
  export function requestData<T extends object>(options: RequestDataOptions) {
23
- return function (
24
- target: Prototype<T>,
25
- propertyKey: string,
26
- indexOrDescriptor: number,
27
- ) {
28
- const decoratorType = getDecoratorTargetType(
29
- target,
30
- propertyKey,
31
- indexOrDescriptor,
32
- );
23
+ return function (target: Prototype<T>, propertyKey: string, index: number) {
24
+ const decoratorType = getDecoratorTargetType(target, propertyKey, index);
33
25
  if (decoratorType !== DecoratorTargetType.INSTANCE_METHOD_PARAMETER)
34
26
  throw new Error(
35
27
  '@requestData decorator is only supported ' +
@@ -38,7 +30,7 @@ export function requestData<T extends object>(options: RequestDataOptions) {
38
30
  RequestDataReflector.setMetadata(
39
31
  options,
40
32
  target.constructor as Constructor<T>,
41
- indexOrDescriptor,
33
+ index,
42
34
  propertyKey,
43
35
  );
44
36
  };
@@ -1,5 +1,4 @@
1
1
  import {expect} from 'chai';
2
- import {describe} from 'mocha';
3
2
  import {Reflector} from '@e22m4u/ts-reflector';
4
3
  import {RequestDataSource} from './request-data-metadata.js';
5
4
  import {RequestDataMetadata} from './request-data-metadata.js';
@@ -0,0 +1,3 @@
1
+ export * from './response-body-metadata.js';
2
+ export * from './response-body-decorator.js';
3
+ export * from './response-body-reflector.js';
@@ -0,0 +1,40 @@
1
+ import {expect} from 'chai';
2
+ import {DataType} from '@e22m4u/ts-data-schema';
3
+ import {responseBody} from './response-body-decorator.js';
4
+ import {ResponseBodyReflector} from './response-body-reflector.js';
5
+
6
+ describe('responseBody', function () {
7
+ it('does not require arguments', function () {
8
+ class Target {
9
+ @responseBody()
10
+ myMethod() {}
11
+ }
12
+ const res = ResponseBodyReflector.getMetadata(Target);
13
+ expect(res.get('myMethod')).to.be.eql({});
14
+ });
15
+
16
+ it('sets the given DataType to the target metadata', function () {
17
+ class Target {
18
+ @responseBody(DataType.STRING)
19
+ myMethod() {}
20
+ }
21
+ const res = ResponseBodyReflector.getMetadata(Target);
22
+ expect(res.get('myMethod')).to.be.eql({schema: {type: DataType.STRING}});
23
+ });
24
+
25
+ it('sets the given schema to the target metadata', function () {
26
+ const schema = {
27
+ type: DataType.OBJECT,
28
+ properties: {
29
+ foo: {type: DataType.STRING},
30
+ bar: {type: DataType.NUMBER},
31
+ },
32
+ };
33
+ class Target {
34
+ @responseBody(schema)
35
+ myMethod() {}
36
+ }
37
+ const res = ResponseBodyReflector.getMetadata(Target);
38
+ expect(res.get('myMethod')).to.be.eql({schema});
39
+ });
40
+ });
@@ -0,0 +1,43 @@
1
+ import {Prototype} from '../../types.js';
2
+ import {Constructor} from '../../types.js';
3
+ import {DataType} from '@e22m4u/ts-data-schema';
4
+ import {DataSchema} from '@e22m4u/ts-data-schema';
5
+ import {DecoratorTargetType} from '@e22m4u/ts-reflector';
6
+ import {getDecoratorTargetType} from '@e22m4u/ts-reflector';
7
+ import {ResponseBodyReflector} from './response-body-reflector.js';
8
+
9
+ /**
10
+ * Response body decorator.
11
+ *
12
+ * @param schemaOrType
13
+ */
14
+ export function responseBody<T extends object>(
15
+ schemaOrType?: DataSchema | DataType,
16
+ ) {
17
+ return function (
18
+ target: Prototype<T>,
19
+ propertyKey: string,
20
+ descriptor: PropertyDescriptor,
21
+ ) {
22
+ const decoratorType = getDecoratorTargetType(
23
+ target,
24
+ propertyKey,
25
+ descriptor,
26
+ );
27
+ if (decoratorType !== DecoratorTargetType.INSTANCE_METHOD)
28
+ throw new Error(
29
+ '@responseBody decorator is only supported on an instance method.',
30
+ );
31
+ let schema: DataSchema | undefined;
32
+ if (typeof schemaOrType === 'object') {
33
+ schema = schemaOrType;
34
+ } else if (typeof schemaOrType === 'string') {
35
+ schema = {type: schemaOrType};
36
+ }
37
+ ResponseBodyReflector.setMetadata(
38
+ schema ? {schema} : {},
39
+ target.constructor as Constructor<T>,
40
+ propertyKey,
41
+ );
42
+ };
43
+ }