@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.
- package/README.md +129 -34
- package/dist/cjs/index.cjs +87 -23
- package/dist/esm/controller-registry.js +9 -4
- package/dist/esm/decorators/after-action/after-action-reflector.spec.js +0 -1
- package/dist/esm/decorators/before-action/before-action-reflector.spec.js +0 -1
- package/dist/esm/decorators/index.d.ts +1 -0
- package/dist/esm/decorators/index.js +1 -0
- package/dist/esm/decorators/request-context/request-context-reflector.spec.js +0 -1
- package/dist/esm/decorators/request-data/request-data-decorator.d.ts +11 -11
- package/dist/esm/decorators/request-data/request-data-decorator.js +3 -3
- package/dist/esm/decorators/request-data/request-data-reflector.spec.js +0 -1
- package/dist/esm/decorators/response-body/index.d.ts +3 -0
- package/dist/esm/decorators/response-body/index.js +3 -0
- package/dist/esm/decorators/response-body/response-body-decorator.d.ts +9 -0
- package/dist/esm/decorators/response-body/response-body-decorator.js +23 -0
- package/dist/esm/decorators/response-body/response-body-decorator.spec.d.ts +1 -0
- package/dist/esm/decorators/response-body/response-body-decorator.spec.js +61 -0
- package/dist/esm/decorators/response-body/response-body-metadata.d.ts +16 -0
- package/dist/esm/decorators/response-body/response-body-metadata.js +5 -0
- package/dist/esm/decorators/response-body/response-body-reflector.d.ts +22 -0
- package/dist/esm/decorators/response-body/response-body-reflector.js +29 -0
- package/dist/esm/decorators/response-body/response-body-reflector.spec.d.ts +1 -0
- package/dist/esm/decorators/response-body/response-body-reflector.spec.js +58 -0
- package/dist/esm/decorators/rest-action/rest-action-reflector.spec.js +0 -1
- package/dist/esm/decorators/rest-controller/rest-controller-reflector.spec.js +0 -1
- package/eslint.config.js +1 -1
- package/package.json +15 -15
- package/src/controller-registry.spec.ts +85 -27
- package/src/controller-registry.ts +13 -4
- package/src/debuggable-service.spec.ts +0 -1
- package/src/decorators/after-action/after-action-reflector.spec.ts +0 -1
- package/src/decorators/before-action/before-action-reflector.spec.ts +0 -1
- package/src/decorators/index.ts +1 -0
- package/src/decorators/request-context/request-context-reflector.spec.ts +0 -1
- package/src/decorators/request-data/request-data-decorator.ts +3 -11
- package/src/decorators/request-data/request-data-reflector.spec.ts +0 -1
- package/src/decorators/response-body/index.ts +3 -0
- package/src/decorators/response-body/response-body-decorator.spec.ts +40 -0
- package/src/decorators/response-body/response-body-decorator.ts +43 -0
- package/src/decorators/response-body/response-body-metadata.ts +20 -0
- package/src/decorators/response-body/response-body-reflector.spec.ts +59 -0
- package/src/decorators/response-body/response-body-reflector.ts +41 -0
- package/src/decorators/rest-action/rest-action-reflector.spec.ts +0 -1
- package/src/decorators/rest-controller/rest-controller-reflector.spec.ts +0 -1
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
|
-
|
43
|
-
|
44
|
-
|
52
|
+
// объявление контроллера
|
53
|
+
// и базового пути /users
|
54
|
+
@restController('users')
|
55
|
+
class UserController {
|
56
|
+
// объявление метода POST /users/login
|
57
|
+
// (использует базовый путь контроллера)
|
58
|
+
@postAction('login')
|
45
59
|
async login(
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
58
|
-
|
59
|
-
|
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('
|
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: '
|
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('
|
160
|
-
class UserController {
|
161
|
-
@getAction('
|
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('
|
175
|
-
class UserController {
|
176
|
-
@getAction('
|
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('
|
197
|
-
class UserController {
|
198
|
-
@getAction('
|
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
|
package/dist/cjs/index.cjs
CHANGED
@@ -29,11 +29,13 @@ __export(index_exports, {
|
|
29
29
|
NotAControllerError: () => NotAControllerError,
|
30
30
|
REQUEST_CONTEXT_METADATA_KEY: () => REQUEST_CONTEXT_METADATA_KEY,
|
31
31
|
REQUEST_DATA_METADATA_KEY: () => REQUEST_DATA_METADATA_KEY,
|
32
|
+
RESPONSE_BODY_METADATA_KEY: () => RESPONSE_BODY_METADATA_KEY,
|
32
33
|
REST_ACTIONS_METADATA_KEY: () => REST_ACTIONS_METADATA_KEY,
|
33
34
|
REST_CONTROLLER_METADATA_KEY: () => REST_CONTROLLER_METADATA_KEY,
|
34
35
|
RequestContextReflector: () => RequestContextReflector,
|
35
36
|
RequestDataReflector: () => RequestDataReflector,
|
36
37
|
RequestDataSource: () => RequestDataSource,
|
38
|
+
ResponseBodyReflector: () => ResponseBodyReflector,
|
37
39
|
RestActionReflector: () => RestActionReflector,
|
38
40
|
RestControllerReflector: () => RestControllerReflector,
|
39
41
|
RestRouter: () => RestRouter,
|
@@ -60,6 +62,7 @@ __export(index_exports, {
|
|
60
62
|
requestParams: () => requestParams,
|
61
63
|
requestQueries: () => requestQueries,
|
62
64
|
requestQuery: () => requestQuery,
|
65
|
+
responseBody: () => responseBody,
|
63
66
|
restAction: () => restAction,
|
64
67
|
restController: () => restController,
|
65
68
|
toCamelCase: () => toCamelCase
|
@@ -281,11 +284,11 @@ var RequestDataReflector = _RequestDataReflector;
|
|
281
284
|
|
282
285
|
// dist/esm/decorators/request-data/request-data-decorator.js
|
283
286
|
function requestData(options) {
|
284
|
-
return function(target, propertyKey,
|
285
|
-
const decoratorType = (0, import_ts_reflector8.getDecoratorTargetType)(target, propertyKey,
|
287
|
+
return function(target, propertyKey, index) {
|
288
|
+
const decoratorType = (0, import_ts_reflector8.getDecoratorTargetType)(target, propertyKey, index);
|
286
289
|
if (decoratorType !== import_ts_reflector7.DecoratorTargetType.INSTANCE_METHOD_PARAMETER)
|
287
290
|
throw new Error("@requestData decorator is only supported on an instance method parameter.");
|
288
|
-
RequestDataReflector.setMetadata(options, target.constructor,
|
291
|
+
RequestDataReflector.setMetadata(options, target.constructor, index, propertyKey);
|
289
292
|
};
|
290
293
|
}
|
291
294
|
__name(requestData, "requestData");
|
@@ -437,16 +440,70 @@ function beforeAction(middleware) {
|
|
437
440
|
}
|
438
441
|
__name(beforeAction, "beforeAction");
|
439
442
|
|
440
|
-
// dist/esm/decorators/
|
443
|
+
// dist/esm/decorators/response-body/response-body-metadata.js
|
441
444
|
var import_ts_reflector17 = require("@e22m4u/ts-reflector");
|
442
|
-
var
|
445
|
+
var RESPONSE_BODY_METADATA_KEY = new import_ts_reflector17.MetadataKey("responseBodyMetadataKey");
|
443
446
|
|
444
|
-
// dist/esm/decorators/
|
447
|
+
// dist/esm/decorators/response-body/response-body-decorator.js
|
445
448
|
var import_ts_reflector19 = require("@e22m4u/ts-reflector");
|
446
449
|
var import_ts_reflector20 = require("@e22m4u/ts-reflector");
|
447
450
|
|
448
|
-
// dist/esm/decorators/
|
451
|
+
// dist/esm/decorators/response-body/response-body-reflector.js
|
449
452
|
var import_ts_reflector18 = require("@e22m4u/ts-reflector");
|
453
|
+
var _ResponseBodyReflector = class _ResponseBodyReflector {
|
454
|
+
/**
|
455
|
+
* Set metadata.
|
456
|
+
*
|
457
|
+
* @param metadata
|
458
|
+
* @param target
|
459
|
+
* @param propertyKey
|
460
|
+
*/
|
461
|
+
static setMetadata(metadata, target, propertyKey) {
|
462
|
+
const oldMap = import_ts_reflector18.Reflector.getOwnMetadata(RESPONSE_BODY_METADATA_KEY, target);
|
463
|
+
const newMap = new Map(oldMap);
|
464
|
+
newMap.set(propertyKey, metadata);
|
465
|
+
import_ts_reflector18.Reflector.defineMetadata(RESPONSE_BODY_METADATA_KEY, newMap, target);
|
466
|
+
}
|
467
|
+
/**
|
468
|
+
* Get metadata.
|
469
|
+
*
|
470
|
+
* @param target
|
471
|
+
*/
|
472
|
+
static getMetadata(target) {
|
473
|
+
const metadata = import_ts_reflector18.Reflector.getOwnMetadata(RESPONSE_BODY_METADATA_KEY, target);
|
474
|
+
return metadata ?? /* @__PURE__ */ new Map();
|
475
|
+
}
|
476
|
+
};
|
477
|
+
__name(_ResponseBodyReflector, "ResponseBodyReflector");
|
478
|
+
var ResponseBodyReflector = _ResponseBodyReflector;
|
479
|
+
|
480
|
+
// dist/esm/decorators/response-body/response-body-decorator.js
|
481
|
+
function responseBody(schemaOrType) {
|
482
|
+
return function(target, propertyKey, descriptor) {
|
483
|
+
const decoratorType = (0, import_ts_reflector20.getDecoratorTargetType)(target, propertyKey, descriptor);
|
484
|
+
if (decoratorType !== import_ts_reflector19.DecoratorTargetType.INSTANCE_METHOD)
|
485
|
+
throw new Error("@responseBody decorator is only supported on an instance method.");
|
486
|
+
let schema;
|
487
|
+
if (typeof schemaOrType === "object") {
|
488
|
+
schema = schemaOrType;
|
489
|
+
} else if (typeof schemaOrType === "string") {
|
490
|
+
schema = { type: schemaOrType };
|
491
|
+
}
|
492
|
+
ResponseBodyReflector.setMetadata(schema ? { schema } : {}, target.constructor, propertyKey);
|
493
|
+
};
|
494
|
+
}
|
495
|
+
__name(responseBody, "responseBody");
|
496
|
+
|
497
|
+
// dist/esm/decorators/rest-controller/rest-controller-metadata.js
|
498
|
+
var import_ts_reflector21 = require("@e22m4u/ts-reflector");
|
499
|
+
var REST_CONTROLLER_METADATA_KEY = new import_ts_reflector21.MetadataKey("restControllerMetadataKey");
|
500
|
+
|
501
|
+
// dist/esm/decorators/rest-controller/rest-controller-decorator.js
|
502
|
+
var import_ts_reflector23 = require("@e22m4u/ts-reflector");
|
503
|
+
var import_ts_reflector24 = require("@e22m4u/ts-reflector");
|
504
|
+
|
505
|
+
// dist/esm/decorators/rest-controller/rest-controller-reflector.js
|
506
|
+
var import_ts_reflector22 = require("@e22m4u/ts-reflector");
|
450
507
|
var _RestControllerReflector = class _RestControllerReflector {
|
451
508
|
/**
|
452
509
|
* Set metadata.
|
@@ -455,7 +512,7 @@ var _RestControllerReflector = class _RestControllerReflector {
|
|
455
512
|
* @param target
|
456
513
|
*/
|
457
514
|
static setMetadata(metadata, target) {
|
458
|
-
return
|
515
|
+
return import_ts_reflector22.Reflector.defineMetadata(REST_CONTROLLER_METADATA_KEY, metadata, target);
|
459
516
|
}
|
460
517
|
/**
|
461
518
|
* Get metadata.
|
@@ -463,7 +520,7 @@ var _RestControllerReflector = class _RestControllerReflector {
|
|
463
520
|
* @param target
|
464
521
|
*/
|
465
522
|
static getMetadata(target) {
|
466
|
-
return
|
523
|
+
return import_ts_reflector22.Reflector.getOwnMetadata(REST_CONTROLLER_METADATA_KEY, target);
|
467
524
|
}
|
468
525
|
};
|
469
526
|
__name(_RestControllerReflector, "RestControllerReflector");
|
@@ -472,8 +529,8 @@ var RestControllerReflector = _RestControllerReflector;
|
|
472
529
|
// dist/esm/decorators/rest-controller/rest-controller-decorator.js
|
473
530
|
function restController(pathOrOptions, options) {
|
474
531
|
return function(target) {
|
475
|
-
const decoratorType = (0,
|
476
|
-
if (decoratorType !==
|
532
|
+
const decoratorType = (0, import_ts_reflector24.getDecoratorTargetType)(target);
|
533
|
+
if (decoratorType !== import_ts_reflector23.DecoratorTargetType.CONSTRUCTOR)
|
477
534
|
throw new Error("@restController decorator is only supported on a class.");
|
478
535
|
if (typeof pathOrOptions === "string") {
|
479
536
|
if (!options) {
|
@@ -490,15 +547,15 @@ function restController(pathOrOptions, options) {
|
|
490
547
|
__name(restController, "restController");
|
491
548
|
|
492
549
|
// dist/esm/decorators/request-context/request-context-metadata.js
|
493
|
-
var
|
494
|
-
var REQUEST_CONTEXT_METADATA_KEY = new
|
550
|
+
var import_ts_reflector25 = require("@e22m4u/ts-reflector");
|
551
|
+
var REQUEST_CONTEXT_METADATA_KEY = new import_ts_reflector25.MetadataKey("requestContextMetadataKey");
|
495
552
|
|
496
553
|
// dist/esm/decorators/request-context/request-context-decorator.js
|
497
|
-
var
|
498
|
-
var
|
554
|
+
var import_ts_reflector27 = require("@e22m4u/ts-reflector");
|
555
|
+
var import_ts_reflector28 = require("@e22m4u/ts-reflector");
|
499
556
|
|
500
557
|
// dist/esm/decorators/request-context/request-context-reflector.js
|
501
|
-
var
|
558
|
+
var import_ts_reflector26 = require("@e22m4u/ts-reflector");
|
502
559
|
var _RequestContextReflector = class _RequestContextReflector {
|
503
560
|
/**
|
504
561
|
* Set metadata.
|
@@ -509,10 +566,10 @@ var _RequestContextReflector = class _RequestContextReflector {
|
|
509
566
|
* @param propertyKey
|
510
567
|
*/
|
511
568
|
static setMetadata(metadata, target, index, propertyKey) {
|
512
|
-
const oldMap =
|
569
|
+
const oldMap = import_ts_reflector26.Reflector.getOwnMetadata(REQUEST_CONTEXT_METADATA_KEY, target, propertyKey);
|
513
570
|
const newMap = new Map(oldMap);
|
514
571
|
newMap.set(index, metadata);
|
515
|
-
|
572
|
+
import_ts_reflector26.Reflector.defineMetadata(REQUEST_CONTEXT_METADATA_KEY, newMap, target, propertyKey);
|
516
573
|
}
|
517
574
|
/**
|
518
575
|
* Get metadata.
|
@@ -521,7 +578,7 @@ var _RequestContextReflector = class _RequestContextReflector {
|
|
521
578
|
* @param propertyKey
|
522
579
|
*/
|
523
580
|
static getMetadata(target, propertyKey) {
|
524
|
-
const metadata =
|
581
|
+
const metadata = import_ts_reflector26.Reflector.getOwnMetadata(REQUEST_CONTEXT_METADATA_KEY, target, propertyKey);
|
525
582
|
return metadata ?? /* @__PURE__ */ new Map();
|
526
583
|
}
|
527
584
|
};
|
@@ -531,8 +588,8 @@ var RequestContextReflector = _RequestContextReflector;
|
|
531
588
|
// dist/esm/decorators/request-context/request-context-decorator.js
|
532
589
|
function requestContext(propertyName) {
|
533
590
|
return function(target, propertyKey, indexOrDescriptor) {
|
534
|
-
const decoratorType = (0,
|
535
|
-
if (decoratorType !==
|
591
|
+
const decoratorType = (0, import_ts_reflector28.getDecoratorTargetType)(target, propertyKey, indexOrDescriptor);
|
592
|
+
if (decoratorType !== import_ts_reflector27.DecoratorTargetType.INSTANCE_METHOD_PARAMETER)
|
536
593
|
throw new Error("@requestContext decorator is only supported on an instance method parameter.");
|
537
594
|
RequestContextReflector.setMetadata({ property: propertyName }, target.constructor, indexOrDescriptor, propertyKey);
|
538
595
|
};
|
@@ -548,6 +605,7 @@ function httpResponse() {
|
|
548
605
|
__name(httpResponse, "httpResponse");
|
549
606
|
|
550
607
|
// dist/esm/controller-registry.js
|
608
|
+
var import_ts_data_schema4 = require("@e22m4u/ts-data-schema");
|
551
609
|
var _ControllerRegistry = class _ControllerRegistry extends DebuggableService {
|
552
610
|
/**
|
553
611
|
* Controllers.
|
@@ -828,6 +886,7 @@ var _ControllerRegistry = class _ControllerRegistry extends DebuggableService {
|
|
828
886
|
const requestContextMetadataMap = RequestContextReflector.getMetadata(controllerCtor, actionName);
|
829
887
|
const requestDataMetadataMap = RequestDataReflector.getMetadata(controllerCtor, actionName);
|
830
888
|
const argsNumber = controllerCtor.prototype[actionName].length;
|
889
|
+
const defaultsApplier = this.getService(import_ts_data_schema4.DefaultValuesApplier);
|
831
890
|
const dataTypeCaster = this.getService(import_ts_data_schema3.DataTypeCaster);
|
832
891
|
const dataValidator = this.getService(import_ts_data_schema2.DataValidator);
|
833
892
|
return (requestContext2) => {
|
@@ -872,13 +931,15 @@ var _ControllerRegistry = class _ControllerRegistry extends DebuggableService {
|
|
872
931
|
}
|
873
932
|
debug("Request data source is %v.", requestDataMd.source);
|
874
933
|
if (requestDataMd.schema) {
|
934
|
+
data = defaultsApplier.applyDefaultValuesIfNeeded(data, requestDataMd.schema, requestDataMd.source);
|
935
|
+
debug("Default values applied.");
|
875
936
|
data = dataTypeCaster.cast(data, requestDataMd.schema, {
|
876
937
|
noTypeCastError: true,
|
877
938
|
sourcePath: requestDataMd.source
|
878
939
|
});
|
879
|
-
debug("Data type casting
|
940
|
+
debug("Data type casting applied.");
|
880
941
|
dataValidator.validate(data, requestDataMd.schema, requestDataMd.source);
|
881
|
-
debug("Data validation
|
942
|
+
debug("Data validation passed.");
|
882
943
|
}
|
883
944
|
if (requestDataMd.property == null) {
|
884
945
|
debug("Request data property is not specified.");
|
@@ -934,11 +995,13 @@ var RestRouter = _RestRouter;
|
|
934
995
|
NotAControllerError,
|
935
996
|
REQUEST_CONTEXT_METADATA_KEY,
|
936
997
|
REQUEST_DATA_METADATA_KEY,
|
998
|
+
RESPONSE_BODY_METADATA_KEY,
|
937
999
|
REST_ACTIONS_METADATA_KEY,
|
938
1000
|
REST_CONTROLLER_METADATA_KEY,
|
939
1001
|
RequestContextReflector,
|
940
1002
|
RequestDataReflector,
|
941
1003
|
RequestDataSource,
|
1004
|
+
ResponseBodyReflector,
|
942
1005
|
RestActionReflector,
|
943
1006
|
RestControllerReflector,
|
944
1007
|
RestRouter,
|
@@ -965,6 +1028,7 @@ var RestRouter = _RestRouter;
|
|
965
1028
|
requestParams,
|
966
1029
|
requestQueries,
|
967
1030
|
requestQuery,
|
1031
|
+
responseBody,
|
968
1032
|
restAction,
|
969
1033
|
restController,
|
970
1034
|
toCamelCase
|
@@ -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
|
393
|
+
debug('Data type casting applied.');
|
389
394
|
dataValidator.validate(data, requestDataMd.schema, requestDataMd.source);
|
390
|
-
debug('Data validation
|
395
|
+
debug('Data validation passed.');
|
391
396
|
}
|
392
397
|
// если свойство данных не определено,
|
393
398
|
// то используем весь объекта данных
|
@@ -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';
|
@@ -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 { RequestContextReflector } from './request-context-reflector.js';
|
5
4
|
import { REQUEST_CONTEXT_METADATA_KEY } from './request-context-metadata.js';
|
@@ -11,22 +11,22 @@ export type RequestDataOptions = RequestDataMetadata;
|
|
11
11
|
*
|
12
12
|
* @param options
|
13
13
|
*/
|
14
|
-
export declare function requestData<T extends object>(options: RequestDataOptions): (target: Prototype<T>, propertyKey: string,
|
14
|
+
export declare function requestData<T extends object>(options: RequestDataOptions): (target: Prototype<T>, propertyKey: string, index: number) => void;
|
15
15
|
/**
|
16
16
|
* Decorator aliases.
|
17
17
|
*/
|
18
|
-
export declare const requestParams: () => (target: Prototype<object>, propertyKey: string,
|
19
|
-
export declare const requestParam: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string,
|
20
|
-
export declare const requestQueries: () => (target: Prototype<object>, propertyKey: string,
|
21
|
-
export declare const requestQuery: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string,
|
22
|
-
export declare const requestHeaders: () => (target: Prototype<object>, propertyKey: string,
|
23
|
-
export declare const requestHeader: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string,
|
24
|
-
export declare const requestCookies: () => (target: Prototype<object>, propertyKey: string,
|
25
|
-
export declare const requestCookie: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string,
|
26
|
-
export declare const requestField: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string,
|
18
|
+
export declare const requestParams: () => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
19
|
+
export declare const requestParam: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
20
|
+
export declare const requestQueries: () => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
21
|
+
export declare const requestQuery: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
22
|
+
export declare const requestHeaders: () => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
23
|
+
export declare const requestHeader: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
24
|
+
export declare const requestCookies: () => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
25
|
+
export declare const requestCookie: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
26
|
+
export declare const requestField: (propertyKey: string, schemaOrType?: DataSchema | DataType) => (target: Prototype<object>, propertyKey: string, index: number) => void;
|
27
27
|
/**
|
28
28
|
* Request body decorator.
|
29
29
|
*
|
30
30
|
* @param schemaOrType
|
31
31
|
*/
|
32
|
-
export declare function requestBody(schemaOrType?: DataSchema | DataType): (target: Prototype<object>, propertyKey: string,
|
32
|
+
export declare function requestBody(schemaOrType?: DataSchema | DataType): (target: Prototype<object>, propertyKey: string, index: number) => void;
|
@@ -9,12 +9,12 @@ import { RequestDataReflector } from './request-data-reflector.js';
|
|
9
9
|
* @param options
|
10
10
|
*/
|
11
11
|
export function requestData(options) {
|
12
|
-
return function (target, propertyKey,
|
13
|
-
const decoratorType = getDecoratorTargetType(target, propertyKey,
|
12
|
+
return function (target, propertyKey, index) {
|
13
|
+
const decoratorType = getDecoratorTargetType(target, propertyKey, index);
|
14
14
|
if (decoratorType !== DecoratorTargetType.INSTANCE_METHOD_PARAMETER)
|
15
15
|
throw new Error('@requestData decorator is only supported ' +
|
16
16
|
'on an instance method parameter.');
|
17
|
-
RequestDataReflector.setMetadata(options, target.constructor,
|
17
|
+
RequestDataReflector.setMetadata(options, target.constructor, index, propertyKey);
|
18
18
|
};
|
19
19
|
}
|
20
20
|
/**
|
@@ -0,0 +1,9 @@
|
|
1
|
+
import { Prototype } from '../../types.js';
|
2
|
+
import { DataType } from '@e22m4u/ts-data-schema';
|
3
|
+
import { DataSchema } from '@e22m4u/ts-data-schema';
|
4
|
+
/**
|
5
|
+
* Response body decorator.
|
6
|
+
*
|
7
|
+
* @param schemaOrType
|
8
|
+
*/
|
9
|
+
export declare function responseBody<T extends object>(schemaOrType?: DataSchema | DataType): (target: Prototype<T>, propertyKey: string, descriptor: PropertyDescriptor) => void;
|