@e22m4u/ts-rest-router 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,16 @@ REST маршрутизатор на основе контроллеров дл
10
10
  - Валидация входящих данных.
11
11
  - Поддержка всех HTTP методов (GET, POST, PUT, PATCH и DELETE).
12
12
 
13
+ ## Содержание
14
+
15
+ - [Установка](#установка)
16
+ - [Поддержка декораторов](#поддержка-декораторов)
17
+ - [Базовый пример](#базовый-пример)
18
+ - [Валидация](#валидация)
19
+ - [Декораторы](#декораторы)
20
+ - [Отладка](#отладка)
21
+ - [Тесты](#тесты)
22
+
13
23
  ## Установка
14
24
 
15
25
  ```bash
@@ -28,7 +38,7 @@ npm install @e22m4u/ts-rest-router
28
38
  }
29
39
  ```
30
40
 
31
- ## Базовое использование
41
+ ## Базовый пример
32
42
 
33
43
  Создание контроллера и методов.
34
44
 
@@ -39,24 +49,25 @@ import {postAction} from '@e22m4u/ts-rest-router';
39
49
  import {requestField} from '@e22m4u/ts-rest-router';
40
50
  import {restController} from '@e22m4u/ts-rest-router';
41
51
 
42
- @restController('/users') // путь контроллера
43
- class UserController { // класс контроллера
44
- @postAction('/login') // метод POST /users/login
52
+ // объявление контроллера
53
+ // и базового пути /users
54
+ @restController('users')
55
+ class UserController {
56
+ // объявление метода POST /users/login
57
+ // (использует базовый путь контроллера)
58
+ @postAction('login')
45
59
  async login(
46
- @requestField('username', { // поле "username" в теле запроса
47
- type: DataType.STRING, // тип параметра допускает только строки
48
- required: true, // параметр является обязательным
49
- })
50
- username: string,
51
- @requestField('password', { // поле "password" в теле запроса
52
- type: DataType.STRING, // тип параметра допускает только строки
53
- required: true, // параметр является обязательным
54
- })
55
- password: string,
60
+ // инъекция значений указанных полей
61
+ // извлеизвлекаемых из тела запроса
62
+ @requestField('username') username?: string,
63
+ @requestField('password') password?: string,
56
64
  ) {
57
- return { // если метод возвращает объект,
58
- id: '123', // то результат будет представлен как
59
- firstName: 'John', // "Content-Type: application/json"
65
+ // так как метод возвращает объект,
66
+ // результат будет представлен как
67
+ // "Content-Type: application/json"
68
+ return {
69
+ id: 1,
70
+ firstName: 'John',
60
71
  lastName: 'Doe',
61
72
  };
62
73
  }
@@ -84,6 +95,90 @@ server.listen('8080', '0.0.0.0', () => {
84
95
  });
85
96
  ```
86
97
 
98
+ ## Валидация
99
+
100
+ Указанные ниже декораторы используются для инъекции соответствующих параметров
101
+ запроса в качестве аргументов метода контроллера. Каждый из указанных декораторов
102
+ имеет параметр `schemaOrType`, в котором определяется тип ожидаемого значения
103
+ или схема для проверки данных.
104
+
105
+ - `@requestParam(name: string, schemaOrType?: DataSchema | DataType)`
106
+ *- извлечение URL параметра по названию;*
107
+ - `@requestQuery(name: string, schemaOrType?: DataSchema | DataType)`
108
+ *- извлечение query параметра по названию;*
109
+ - `@requestBody(schemaOrType?: DataSchema | DataType)`
110
+ *- извлечение тела запроса;*
111
+ - `@requestField(name: string, schemaOrType?: DataSchema | DataType)`
112
+ *- извлечение свойства из тела запроса;*
113
+ - `@requestHeader(name: string, schemaOrType?: DataSchema | DataType)`
114
+ *- извлечение заголовка запроса по названию;*
115
+ - `@requestCookie(name: string, schemaOrType?: DataSchema | DataType)`
116
+ *- извлечение cookie запроса по названию;*
117
+
118
+ Проверка входящих данных выполняется встроенным модулем
119
+ [@e22m4u/ts-data-schema](https://www.npmjs.com/package/@e22m4u/ts-data-schema)
120
+ (не требует установки). Ниже приводятся константы для определения допустимых
121
+ типов извлекаемого значения.
122
+
123
+ - `DataType.ANY` - принимает любой тип
124
+ - `DataType.STRING` - строковые значения
125
+ - `DataType.NUMBER` - числовые значения
126
+ - `DataType.BOOLEAN` - логические значения
127
+ - `DataType.ARRAY` - массивы
128
+ - `DataType.OBJECT` - объекты (не экземпляры)
129
+
130
+ Для определения дополнительных условий, используется объект `DataSchema`,
131
+ с помощью которого можно определить структуру ожидаемого объекта, допустимые
132
+ элементы массива, функции-валидаторы и другие ограничения входящих данных.
133
+
134
+ ```ts
135
+ type DataSchema = {
136
+ type: DataType;
137
+ items?: DataSchema;
138
+ properties?: {[key: string]: DataSchema};
139
+ required?: boolean;
140
+ validate?: CallableValidator | CallableValidator[];
141
+ default?: unknown;
142
+ }
143
+ ```
144
+
145
+ Пример проверки передаваемого объекта методом POST:
146
+
147
+ ```ts
148
+ import {DataType} from '@e22m4u/ts-rest-router';
149
+ import {getAction} from '@e22m4u/ts-rest-router';
150
+ import {postAction} from '@e22m4u/ts-rest-router';
151
+ import {requestField} from '@e22m4u/ts-rest-router';
152
+ import {restController} from '@e22m4u/ts-rest-router';
153
+
154
+ @restController('users')
155
+ class UserController {
156
+ @postAction() // POST /users
157
+ async create(
158
+ @requestBody({ // декоратор тела запроса
159
+ type: DataType.OBJECT, // в теле запроса ожидается объект
160
+ properties: {
161
+ name: { // схема свойства "name"
162
+ type: DataType.STRING, // свойство должно содержать строку
163
+ required: true, // свойство не может содержать undefined или null
164
+ validate: v => v.length > 2, // проверка длины строки
165
+ },
166
+ age: { // схема свойства "age"
167
+ type: DataType.NUMBER, // свойство должно являться числом
168
+ }
169
+ },
170
+ })
171
+ body: {name: string, age?: number},
172
+ ) {
173
+ return {
174
+ id: 1,
175
+ name: body.name,
176
+ age: body.age,
177
+ };
178
+ }
179
+ }
180
+ ```
181
+
87
182
  ## Декораторы
88
183
 
89
184
  Контроллер и методы:
@@ -125,16 +220,16 @@ server.listen('8080', '0.0.0.0', () => {
125
220
  ```ts
126
221
  @restController()
127
222
  class UserController {
128
- // методы контроллера
223
+ // ...
129
224
  }
130
225
  ```
131
226
 
132
- Определение пути контроллера.
227
+ Определение базового пути.
133
228
 
134
229
  ```ts
135
- @restController('/users') // путь контроллера
230
+ @restController('users')
136
231
  class UserController {
137
- // методы контроллера
232
+ // ...
138
233
  }
139
234
  ```
140
235
 
@@ -142,12 +237,12 @@ class UserController {
142
237
 
143
238
  ```ts
144
239
  @restController({
145
- path: '/api', // путь контроллера
240
+ path: 'api', // базовый путь
146
241
  before: [authMiddleware], // middleware до обработки запроса
147
242
  after: [loggerMiddleware], // middleware после обработки запроса
148
243
  })
149
244
  class UserController {
150
- // методы контроллера
245
+ // ...
151
246
  }
152
247
  ```
153
248
 
@@ -156,9 +251,9 @@ class UserController {
156
251
  Определение метода GET.
157
252
 
158
253
  ```ts
159
- @restController('/users') // путь контроллера
160
- class UserController { // класс контроллера
161
- @getAction('/whoAmI') // маршрут GET /users/whoAmI
254
+ @restController('users')
255
+ class UserController {
256
+ @getAction('whoAmI') // маршрут GET /users/whoAmI
162
257
  async whoAmI() {
163
258
  return { // если метод возвращает объект,
164
259
  name: 'John', // то результат будет представлен
@@ -171,9 +266,9 @@ class UserController { // класс контроллера
171
266
  Дополнительные параметры декоратора.
172
267
 
173
268
  ```ts
174
- @restController('/users') // путь контроллера
175
- class UserController { // класс контроллера
176
- @getAction('/whoAmI', { // маршрут GET /users/whoAmI
269
+ @restController('users')
270
+ class UserController {
271
+ @getAction('whoAmI', { // маршрут GET /users/whoAmI
177
272
  before: [authMiddleware], // middleware до обработки запроса
178
273
  after: [loggerMiddleware], // middleware после обработки запроса
179
274
  })
@@ -193,12 +288,12 @@ class UserController { // класс контроллера
193
288
  ```ts
194
289
  import {RequestContext} from '@e22m4u/js-trie-router';
195
290
 
196
- @restController('/users') // путь контроллера
197
- class UserController { // класс контроллера
198
- @getAction('/:id') // маршрут GET /users/:id
291
+ @restController('users')
292
+ class UserController {
293
+ @getAction(':id')
199
294
  findById(
200
- @requestContext() // включениее контекста запроса
201
- ctx: RequestContext, // в качестве параметра метода
295
+ @requestContext() // инъекция контекста запроса
296
+ ctx: RequestContext, // в качестве аргумента
202
297
  ) {
203
298
  console.log(ctx.req); // IncomingMessage
204
299
  console.log(ctx.res); // ServerResponse
@@ -548,6 +548,7 @@ function httpResponse() {
548
548
  __name(httpResponse, "httpResponse");
549
549
 
550
550
  // dist/esm/controller-registry.js
551
+ var import_ts_data_schema4 = require("@e22m4u/ts-data-schema");
551
552
  var _ControllerRegistry = class _ControllerRegistry extends DebuggableService {
552
553
  /**
553
554
  * Controllers.
@@ -828,6 +829,7 @@ var _ControllerRegistry = class _ControllerRegistry extends DebuggableService {
828
829
  const requestContextMetadataMap = RequestContextReflector.getMetadata(controllerCtor, actionName);
829
830
  const requestDataMetadataMap = RequestDataReflector.getMetadata(controllerCtor, actionName);
830
831
  const argsNumber = controllerCtor.prototype[actionName].length;
832
+ const defaultsApplier = this.getService(import_ts_data_schema4.DefaultValuesApplier);
831
833
  const dataTypeCaster = this.getService(import_ts_data_schema3.DataTypeCaster);
832
834
  const dataValidator = this.getService(import_ts_data_schema2.DataValidator);
833
835
  return (requestContext2) => {
@@ -872,13 +874,15 @@ var _ControllerRegistry = class _ControllerRegistry extends DebuggableService {
872
874
  }
873
875
  debug("Request data source is %v.", requestDataMd.source);
874
876
  if (requestDataMd.schema) {
877
+ data = defaultsApplier.applyDefaultValuesIfNeeded(data, requestDataMd.schema, requestDataMd.source);
878
+ debug("Default values applied.");
875
879
  data = dataTypeCaster.cast(data, requestDataMd.schema, {
876
880
  noTypeCastError: true,
877
881
  sourcePath: requestDataMd.source
878
882
  });
879
- debug("Data type casting is passed.");
883
+ debug("Data type casting applied.");
880
884
  dataValidator.validate(data, requestDataMd.schema, requestDataMd.source);
881
- debug("Data validation is passed.");
885
+ debug("Data validation passed.");
882
886
  }
883
887
  if (requestDataMd.property == null) {
884
888
  debug("Request data property is not specified.");
@@ -8,6 +8,7 @@ import { DebuggableService } from './debuggable-service.js';
8
8
  import { RestActionReflector } from './decorators/index.js';
9
9
  import { RequestDataReflector } from './decorators/index.js';
10
10
  import { AfterActionReflector } from './decorators/index.js';
11
+ import { DefaultValuesApplier } from '@e22m4u/ts-data-schema';
11
12
  import { BeforeActionReflector } from './decorators/index.js';
12
13
  import { RestControllerReflector } from './decorators/index.js';
13
14
  import { RequestContextReflector } from './decorators/index.js';
@@ -320,6 +321,7 @@ export class ControllerRegistry extends DebuggableService {
320
321
  const requestContextMetadataMap = RequestContextReflector.getMetadata(controllerCtor, actionName);
321
322
  const requestDataMetadataMap = RequestDataReflector.getMetadata(controllerCtor, actionName);
322
323
  const argsNumber = controllerCtor.prototype[actionName].length;
324
+ const defaultsApplier = this.getService(DefaultValuesApplier);
323
325
  const dataTypeCaster = this.getService(DataTypeCaster);
324
326
  const dataValidator = this.getService(DataValidator);
325
327
  return (requestContext) => {
@@ -378,16 +380,19 @@ export class ControllerRegistry extends DebuggableService {
378
380
  break;
379
381
  }
380
382
  debug('Request data source is %v.', requestDataMd.source);
381
- // при наличии схемы данных выполняется
382
- // их конвертация и валидация
383
+ // при наличии схемы данных применяются значения
384
+ // по умолчанию, выполняется конвертация входящего
385
+ // значения и валидация согласно схеме
383
386
  if (requestDataMd.schema) {
387
+ data = defaultsApplier.applyDefaultValuesIfNeeded(data, requestDataMd.schema, requestDataMd.source);
388
+ debug('Default values applied.');
384
389
  data = dataTypeCaster.cast(data, requestDataMd.schema, {
385
390
  noTypeCastError: true,
386
391
  sourcePath: requestDataMd.source,
387
392
  });
388
- debug('Data type casting is passed.');
393
+ debug('Data type casting applied.');
389
394
  dataValidator.validate(data, requestDataMd.schema, requestDataMd.source);
390
- debug('Data validation is passed.');
395
+ debug('Data validation passed.');
391
396
  }
392
397
  // если свойство данных не определено,
393
398
  // то используем весь объекта данных
@@ -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';
@@ -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 { RequestContextReflector } from './request-context-reflector.js';
5
4
  import { REQUEST_CONTEXT_METADATA_KEY } from './request-context-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 { RequestDataSource } from './request-data-metadata.js';
5
4
  import { RequestDataReflector } from './request-data-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 { 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.1",
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.26.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.17",
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.4",
64
+ "eslint": "~9.26.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.0.0",
68
68
  "husky": "~9.1.7",
69
- "mocha": "~11.1.0",
69
+ "mocha": "~11.2.2",
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.32.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';
@@ -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';
@@ -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';
@@ -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';