@e22m4u/ts-rest-router 0.5.4 → 0.6.0
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 +9 -0
- package/dist/cjs/index.cjs +36 -22
- package/dist/esm/controller-registry.js +11 -3
- package/dist/esm/data-schema-types.d.ts +15 -0
- package/dist/esm/data-schema-types.js +1 -0
- package/dist/esm/decorators/before-action/before-action-reflector.spec.js +15 -15
- package/dist/esm/decorators/request-data/request-data-decorator.d.ts +11 -12
- package/dist/esm/decorators/request-data/request-data-decorator.js +20 -12
- package/dist/esm/decorators/request-data/request-data-decorator.spec.js +183 -1
- package/dist/esm/decorators/request-data/request-data-metadata.d.ts +2 -2
- package/dist/esm/decorators/response-body/response-body-decorator.d.ts +2 -3
- package/dist/esm/decorators/response-body/response-body-decorator.js +7 -7
- package/dist/esm/decorators/response-body/response-body-decorator.spec.js +21 -1
- package/dist/esm/decorators/response-body/response-body-metadata.d.ts +2 -2
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/package.json +5 -5
- package/src/controller-registry.spec.ts +178 -5
- package/src/controller-registry.ts +11 -7
- package/src/data-schema-types.ts +18 -0
- package/src/decorators/before-action/before-action-reflector.spec.ts +15 -15
- package/src/decorators/request-data/request-data-decorator.spec.ts +174 -1
- package/src/decorators/request-data/request-data-decorator.ts +22 -13
- package/src/decorators/request-data/request-data-metadata.ts +2 -2
- package/src/decorators/response-body/response-body-decorator.spec.ts +17 -1
- package/src/decorators/response-body/response-body-decorator.ts +9 -11
- package/src/decorators/response-body/response-body-metadata.ts +2 -2
- package/src/index.ts +1 -0
|
@@ -38,7 +38,7 @@ describe('responseBody', function () {
|
|
|
38
38
|
const res = ResponseBodyReflector.getMetadata(Target);
|
|
39
39
|
expect(res.get('myMethod')).to.be.eql({ schema: { type: DataType.STRING } });
|
|
40
40
|
});
|
|
41
|
-
it('sets the given
|
|
41
|
+
it('sets the given DataSchema to the target metadata', function () {
|
|
42
42
|
const schema = {
|
|
43
43
|
type: DataType.OBJECT,
|
|
44
44
|
properties: {
|
|
@@ -58,4 +58,24 @@ describe('responseBody', function () {
|
|
|
58
58
|
const res = ResponseBodyReflector.getMetadata(Target);
|
|
59
59
|
expect(res.get('myMethod')).to.be.eql({ schema });
|
|
60
60
|
});
|
|
61
|
+
it('sets the given DataSchemaFactory to the target metadata', function () {
|
|
62
|
+
const factory = () => ({
|
|
63
|
+
type: DataType.OBJECT,
|
|
64
|
+
properties: {
|
|
65
|
+
foo: { type: DataType.STRING },
|
|
66
|
+
bar: { type: DataType.NUMBER },
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
class Target {
|
|
70
|
+
myMethod() { }
|
|
71
|
+
}
|
|
72
|
+
__decorate([
|
|
73
|
+
responseBody(factory),
|
|
74
|
+
__metadata("design:type", Function),
|
|
75
|
+
__metadata("design:paramtypes", []),
|
|
76
|
+
__metadata("design:returntype", void 0)
|
|
77
|
+
], Target.prototype, "myMethod", null);
|
|
78
|
+
const res = ResponseBodyReflector.getMetadata(Target);
|
|
79
|
+
expect(res.get('myMethod')).to.be.eql({ schema: factory });
|
|
80
|
+
});
|
|
61
81
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { MetadataKey } from '@e22m4u/ts-reflector';
|
|
2
|
-
import {
|
|
2
|
+
import { DataSchemaOrFactory } from '../../data-schema-types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Response body metadata.
|
|
5
5
|
*/
|
|
6
6
|
export type ResponseBodyMetadata = {
|
|
7
|
-
schema?:
|
|
7
|
+
schema?: DataSchemaOrFactory;
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
10
|
* Response body metadata map.
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e22m4u/ts-rest-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Декларативный REST-маршрутизатор на основе контроллеров для TypeScript",
|
|
5
5
|
"author": "Mikhail Evstropov <e22m4u@yandex.ru>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"@e22m4u/js-debug": "~0.3.1",
|
|
45
45
|
"@e22m4u/js-format": "~0.2.0",
|
|
46
46
|
"@e22m4u/js-service": "~0.4.0",
|
|
47
|
-
"@e22m4u/js-trie-router": "~0.3.
|
|
48
|
-
"@e22m4u/ts-data-schema": "~0.4.
|
|
47
|
+
"@e22m4u/js-trie-router": "~0.3.1",
|
|
48
|
+
"@e22m4u/ts-data-schema": "~0.4.3",
|
|
49
49
|
"@e22m4u/ts-reflector": "~0.1.7",
|
|
50
50
|
"http-errors": "~2.0.0"
|
|
51
51
|
},
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@types/debug": "~4.1.12",
|
|
58
58
|
"@types/http-errors": "~2.0.5",
|
|
59
59
|
"@types/mocha": "~10.0.10",
|
|
60
|
-
"@types/node": "~24.6.
|
|
60
|
+
"@types/node": "~24.6.1",
|
|
61
61
|
"c8": "~10.1.3",
|
|
62
62
|
"chai": "~6.2.0",
|
|
63
63
|
"esbuild": "~0.25.10",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"prettier": "~3.6.2",
|
|
71
71
|
"rimraf": "~6.0.1",
|
|
72
72
|
"tsx": "~4.20.6",
|
|
73
|
-
"typescript": "~5.9.
|
|
73
|
+
"typescript": "~5.9.3",
|
|
74
74
|
"typescript-eslint": "~8.45.0"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -20,22 +20,24 @@ import {
|
|
|
20
20
|
getAction,
|
|
21
21
|
postAction,
|
|
22
22
|
requestBody,
|
|
23
|
-
requestCookie,
|
|
24
|
-
requestCookies,
|
|
25
23
|
requestField,
|
|
26
|
-
|
|
24
|
+
requestCookies,
|
|
25
|
+
requestCookie,
|
|
27
26
|
requestHeaders,
|
|
28
|
-
|
|
27
|
+
requestHeader,
|
|
29
28
|
requestParams,
|
|
29
|
+
requestParam,
|
|
30
30
|
requestQueries,
|
|
31
31
|
requestQuery,
|
|
32
32
|
restController,
|
|
33
33
|
} from './decorators/index.js';
|
|
34
34
|
|
|
35
35
|
import {expect} from 'chai';
|
|
36
|
+
import {Service} from '@e22m4u/js-service';
|
|
36
37
|
import {DataType} from '@e22m4u/ts-data-schema';
|
|
38
|
+
import {ServiceContainer} from '@e22m4u/js-service';
|
|
39
|
+
import {DataSchemaFactory} from './data-schema-types.js';
|
|
37
40
|
import {ControllerRegistry} from './controller-registry.js';
|
|
38
|
-
import {Service, ServiceContainer} from '@e22m4u/js-service';
|
|
39
41
|
|
|
40
42
|
const PRE_HANDLER_1 = () => undefined;
|
|
41
43
|
const PRE_HANDLER_2 = () => undefined;
|
|
@@ -1039,5 +1041,176 @@ describe('ControllerRegistry', function () {
|
|
|
1039
1041
|
await handler(ctx1);
|
|
1040
1042
|
expect(counter).to.be.eq(1);
|
|
1041
1043
|
});
|
|
1044
|
+
|
|
1045
|
+
describe('data schema', function () {
|
|
1046
|
+
describe('@requestBody', function () {
|
|
1047
|
+
describe('DataType', function () {
|
|
1048
|
+
it('should cast request data by the specified type', async function () {
|
|
1049
|
+
let invoked = 0;
|
|
1050
|
+
class MyController {
|
|
1051
|
+
myAction(
|
|
1052
|
+
@requestBody(DataType.NUMBER)
|
|
1053
|
+
body: number,
|
|
1054
|
+
) {
|
|
1055
|
+
expect(body).to.be.eq(10);
|
|
1056
|
+
invoked++;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
const req = createRequestMock();
|
|
1060
|
+
const res = createResponseMock();
|
|
1061
|
+
const S = new ControllerRegistry();
|
|
1062
|
+
const ctx = new RequestContext(S.container, req, res);
|
|
1063
|
+
ctx.body = '10';
|
|
1064
|
+
const handler = S['createRouteHandler'](MyController, 'myAction');
|
|
1065
|
+
await handler(ctx);
|
|
1066
|
+
expect(invoked).to.be.eq(1);
|
|
1067
|
+
});
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
describe('DataSchema', function () {
|
|
1071
|
+
it('should apply default value to request data', async function () {
|
|
1072
|
+
let invoked = 0;
|
|
1073
|
+
class MyController {
|
|
1074
|
+
myAction(
|
|
1075
|
+
@requestBody({
|
|
1076
|
+
type: DataType.STRING,
|
|
1077
|
+
default: 'OK',
|
|
1078
|
+
})
|
|
1079
|
+
body: string,
|
|
1080
|
+
) {
|
|
1081
|
+
expect(body).to.be.eq('OK');
|
|
1082
|
+
invoked++;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
const req = createRequestMock();
|
|
1086
|
+
const res = createResponseMock();
|
|
1087
|
+
const S = new ControllerRegistry();
|
|
1088
|
+
const ctx = new RequestContext(S.container, req, res);
|
|
1089
|
+
const handler = S['createRouteHandler'](MyController, 'myAction');
|
|
1090
|
+
await handler(ctx);
|
|
1091
|
+
expect(invoked).to.be.eq(1);
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
it('should cast request data by the specified type', async function () {
|
|
1095
|
+
let invoked = 0;
|
|
1096
|
+
class MyController {
|
|
1097
|
+
myAction(
|
|
1098
|
+
@requestBody({type: DataType.NUMBER})
|
|
1099
|
+
body: number,
|
|
1100
|
+
) {
|
|
1101
|
+
expect(body).to.be.eq(10);
|
|
1102
|
+
invoked++;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
const req = createRequestMock();
|
|
1106
|
+
const res = createResponseMock();
|
|
1107
|
+
const S = new ControllerRegistry();
|
|
1108
|
+
const ctx = new RequestContext(S.container, req, res);
|
|
1109
|
+
ctx.body = '10';
|
|
1110
|
+
const handler = S['createRouteHandler'](MyController, 'myAction');
|
|
1111
|
+
await handler(ctx);
|
|
1112
|
+
expect(invoked).to.be.eq(1);
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
it('should validate request data by the given schema', async function () {
|
|
1116
|
+
class MyController {
|
|
1117
|
+
myAction(
|
|
1118
|
+
@requestBody({
|
|
1119
|
+
type: DataType.OBJECT,
|
|
1120
|
+
required: true,
|
|
1121
|
+
})
|
|
1122
|
+
body: object,
|
|
1123
|
+
) {
|
|
1124
|
+
throw new Error('Must not to be invoked');
|
|
1125
|
+
return body;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
const req = createRequestMock();
|
|
1130
|
+
const res = createResponseMock();
|
|
1131
|
+
const S = new ControllerRegistry();
|
|
1132
|
+
const ctx = new RequestContext(S.container, req, res);
|
|
1133
|
+
const handler = S['createRouteHandler'](MyController, 'myAction');
|
|
1134
|
+
const throwable = () => handler(ctx);
|
|
1135
|
+
expect(throwable).to.throw(/is required, but undefined was given/);
|
|
1136
|
+
});
|
|
1137
|
+
});
|
|
1138
|
+
|
|
1139
|
+
describe('DataSchemaFactory', function () {
|
|
1140
|
+
it('should apply default value to request data', async function () {
|
|
1141
|
+
let invoked = 0;
|
|
1142
|
+
const S = new ControllerRegistry();
|
|
1143
|
+
const factory: DataSchemaFactory = sc => {
|
|
1144
|
+
expect(sc).to.be.eq(S.container);
|
|
1145
|
+
return {type: DataType.STRING, default: 'OK'};
|
|
1146
|
+
};
|
|
1147
|
+
class MyController {
|
|
1148
|
+
myAction(
|
|
1149
|
+
@requestBody(factory)
|
|
1150
|
+
body: string,
|
|
1151
|
+
) {
|
|
1152
|
+
expect(body).to.be.eq('OK');
|
|
1153
|
+
invoked++;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
const req = createRequestMock();
|
|
1157
|
+
const res = createResponseMock();
|
|
1158
|
+
const ctx = new RequestContext(S.container, req, res);
|
|
1159
|
+
const handler = S['createRouteHandler'](MyController, 'myAction');
|
|
1160
|
+
await handler(ctx);
|
|
1161
|
+
expect(invoked).to.be.eq(1);
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
it('should cast request data by the specified type', async function () {
|
|
1165
|
+
let invoked = 0;
|
|
1166
|
+
const S = new ControllerRegistry();
|
|
1167
|
+
const factory: DataSchemaFactory = sc => {
|
|
1168
|
+
expect(sc).to.be.eq(S.container);
|
|
1169
|
+
return {type: DataType.NUMBER};
|
|
1170
|
+
};
|
|
1171
|
+
class MyController {
|
|
1172
|
+
myAction(
|
|
1173
|
+
@requestBody(factory)
|
|
1174
|
+
body: number,
|
|
1175
|
+
) {
|
|
1176
|
+
expect(body).to.be.eq(10);
|
|
1177
|
+
invoked++;
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
const req = createRequestMock();
|
|
1181
|
+
const res = createResponseMock();
|
|
1182
|
+
const ctx = new RequestContext(S.container, req, res);
|
|
1183
|
+
ctx.body = '10';
|
|
1184
|
+
const handler = S['createRouteHandler'](MyController, 'myAction');
|
|
1185
|
+
await handler(ctx);
|
|
1186
|
+
expect(invoked).to.be.eq(1);
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
it('should validate request data by the given schema', async function () {
|
|
1190
|
+
const S = new ControllerRegistry();
|
|
1191
|
+
const factory: DataSchemaFactory = sc => {
|
|
1192
|
+
expect(sc).to.be.eq(S.container);
|
|
1193
|
+
return {type: DataType.OBJECT, required: true};
|
|
1194
|
+
};
|
|
1195
|
+
class MyController {
|
|
1196
|
+
myAction(
|
|
1197
|
+
@requestBody(factory)
|
|
1198
|
+
body: object,
|
|
1199
|
+
) {
|
|
1200
|
+
throw new Error('Must not to be invoked');
|
|
1201
|
+
return body;
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
const req = createRequestMock();
|
|
1206
|
+
const res = createResponseMock();
|
|
1207
|
+
const ctx = new RequestContext(S.container, req, res);
|
|
1208
|
+
const handler = S['createRouteHandler'](MyController, 'myAction');
|
|
1209
|
+
const throwable = () => handler(ctx);
|
|
1210
|
+
expect(throwable).to.throw(/is required, but undefined was given/);
|
|
1211
|
+
});
|
|
1212
|
+
});
|
|
1213
|
+
});
|
|
1214
|
+
});
|
|
1042
1215
|
});
|
|
1043
1216
|
});
|
|
@@ -2,6 +2,7 @@ import {AnyObject} from './types.js';
|
|
|
2
2
|
import {Constructor} from './types.js';
|
|
3
3
|
import {Errorf} from '@e22m4u/js-format';
|
|
4
4
|
import {TrieRouter} from '@e22m4u/js-trie-router';
|
|
5
|
+
import {DataSchema} from '@e22m4u/ts-data-schema';
|
|
5
6
|
import {RouteHandler} from '@e22m4u/js-trie-router';
|
|
6
7
|
import {DataValidator} from '@e22m4u/ts-data-schema';
|
|
7
8
|
import {DataTypeCaster} from '@e22m4u/ts-data-schema';
|
|
@@ -461,22 +462,25 @@ export class ControllerRegistry extends DebuggableService {
|
|
|
461
462
|
// по умолчанию, выполняется конвертация входящего
|
|
462
463
|
// значения и валидация согласно схеме
|
|
463
464
|
if (requestDataMd.schema) {
|
|
465
|
+
let dataSchema: DataSchema;
|
|
466
|
+
if (typeof requestDataMd.schema === 'function') {
|
|
467
|
+
dataSchema = requestDataMd.schema(this.container);
|
|
468
|
+
debug('Data schema extracted from factory function.');
|
|
469
|
+
} else {
|
|
470
|
+
dataSchema = requestDataMd.schema;
|
|
471
|
+
}
|
|
464
472
|
data = defaultsApplier.applyDefaultValuesIfNeeded(
|
|
465
473
|
data,
|
|
466
|
-
|
|
474
|
+
dataSchema,
|
|
467
475
|
requestDataMd.source,
|
|
468
476
|
);
|
|
469
477
|
debug('Default values applied.');
|
|
470
|
-
data = dataTypeCaster.cast(data,
|
|
478
|
+
data = dataTypeCaster.cast(data, dataSchema, {
|
|
471
479
|
noTypeCastError: true,
|
|
472
480
|
sourcePath: requestDataMd.source,
|
|
473
481
|
});
|
|
474
482
|
debug('Data type casting applied.');
|
|
475
|
-
dataValidator.validate(
|
|
476
|
-
data,
|
|
477
|
-
requestDataMd.schema,
|
|
478
|
-
requestDataMd.source,
|
|
479
|
-
);
|
|
483
|
+
dataValidator.validate(data, dataSchema, requestDataMd.source);
|
|
480
484
|
debug('Data validation passed.');
|
|
481
485
|
}
|
|
482
486
|
// если свойство данных не определено,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {DataType} from '@e22m4u/ts-data-schema';
|
|
2
|
+
import {DataSchema} from '@e22m4u/ts-data-schema';
|
|
3
|
+
import {ServiceContainer} from '@e22m4u/js-service';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Data schema factory.
|
|
7
|
+
*/
|
|
8
|
+
export type DataSchemaFactory = (container: ServiceContainer) => DataSchema;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Data schema or factory.
|
|
12
|
+
*/
|
|
13
|
+
export type DataSchemaOrFactory = DataSchema | DataSchemaFactory;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Data schema input.
|
|
17
|
+
*/
|
|
18
|
+
export type DataSchemaInput = DataSchemaOrFactory | DataType;
|
|
@@ -3,17 +3,17 @@ import {Reflector} from '@e22m4u/ts-reflector';
|
|
|
3
3
|
import {BeforeActionReflector} from './before-action-reflector.js';
|
|
4
4
|
import {BEFORE_ACTION_METADATA_KEY} from './before-action-metadata.js';
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
const HOOK_1 = () => undefined;
|
|
7
|
+
const HOOK_2 = () => undefined;
|
|
8
|
+
const HOOK_3 = () => undefined;
|
|
9
9
|
|
|
10
10
|
describe('BeforeActionReflector', function () {
|
|
11
11
|
describe('class target', function () {
|
|
12
12
|
describe('addMetadata', function () {
|
|
13
13
|
it('adds a given value to the target metadata', function () {
|
|
14
14
|
class Target {}
|
|
15
|
-
const md1 = {hook:
|
|
16
|
-
const md2 = {hook: [
|
|
15
|
+
const md1 = {hook: HOOK_1};
|
|
16
|
+
const md2 = {hook: [HOOK_2, HOOK_3]};
|
|
17
17
|
BeforeActionReflector.addMetadata(md1, Target);
|
|
18
18
|
BeforeActionReflector.addMetadata(md2, Target);
|
|
19
19
|
const res = Reflector.getOwnMetadata(
|
|
@@ -33,8 +33,8 @@ describe('BeforeActionReflector', function () {
|
|
|
33
33
|
|
|
34
34
|
it('returns existing metadata by the target', function () {
|
|
35
35
|
class Target {}
|
|
36
|
-
const md1 = {hook:
|
|
37
|
-
const md2 = {hook: [
|
|
36
|
+
const md1 = {hook: HOOK_1};
|
|
37
|
+
const md2 = {hook: [HOOK_2, HOOK_3]};
|
|
38
38
|
const mdArray = [md1, md2];
|
|
39
39
|
Reflector.defineMetadata(BEFORE_ACTION_METADATA_KEY, mdArray, Target);
|
|
40
40
|
const res = BeforeActionReflector.getMetadata(Target);
|
|
@@ -47,8 +47,8 @@ describe('BeforeActionReflector', function () {
|
|
|
47
47
|
describe('addMetadata', function () {
|
|
48
48
|
it('adds a given value to the target metadata', function () {
|
|
49
49
|
class Target {}
|
|
50
|
-
const md1 = {hook:
|
|
51
|
-
const md2 = {hook: [
|
|
50
|
+
const md1 = {hook: HOOK_1};
|
|
51
|
+
const md2 = {hook: [HOOK_2, HOOK_3]};
|
|
52
52
|
BeforeActionReflector.addMetadata(md1, Target, 'prop');
|
|
53
53
|
BeforeActionReflector.addMetadata(md2, Target, 'prop');
|
|
54
54
|
const res = Reflector.getOwnMetadata(
|
|
@@ -69,8 +69,8 @@ describe('BeforeActionReflector', function () {
|
|
|
69
69
|
|
|
70
70
|
it('returns existing metadata by the target', function () {
|
|
71
71
|
class Target {}
|
|
72
|
-
const md1 = {hook:
|
|
73
|
-
const md2 = {hook: [
|
|
72
|
+
const md1 = {hook: HOOK_1};
|
|
73
|
+
const md2 = {hook: [HOOK_2, HOOK_3]};
|
|
74
74
|
const mdArray = [md1, md2];
|
|
75
75
|
Reflector.defineMetadata(
|
|
76
76
|
BEFORE_ACTION_METADATA_KEY,
|
|
@@ -87,8 +87,8 @@ describe('BeforeActionReflector', function () {
|
|
|
87
87
|
describe('addMetadata', function () {
|
|
88
88
|
it('can distinguish class and method metadata', function () {
|
|
89
89
|
class Target {}
|
|
90
|
-
const md1 = {hook:
|
|
91
|
-
const md2 = {hook:
|
|
90
|
+
const md1 = {hook: HOOK_1};
|
|
91
|
+
const md2 = {hook: HOOK_2};
|
|
92
92
|
BeforeActionReflector.addMetadata(md1, Target);
|
|
93
93
|
BeforeActionReflector.addMetadata(md2, Target, 'prop');
|
|
94
94
|
const res1 = Reflector.getOwnMetadata(BEFORE_ACTION_METADATA_KEY, Target);
|
|
@@ -105,8 +105,8 @@ describe('BeforeActionReflector', function () {
|
|
|
105
105
|
describe('getMetadata', function () {
|
|
106
106
|
it('can distinguish class and method metadata', function () {
|
|
107
107
|
class Target {}
|
|
108
|
-
const md1 = {hook:
|
|
109
|
-
const md2 = {hook:
|
|
108
|
+
const md1 = {hook: HOOK_1};
|
|
109
|
+
const md2 = {hook: HOOK_2};
|
|
110
110
|
Reflector.defineMetadata(BEFORE_ACTION_METADATA_KEY, [md1], Target);
|
|
111
111
|
Reflector.defineMetadata(
|
|
112
112
|
BEFORE_ACTION_METADATA_KEY,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
2
|
import {expect} from 'chai';
|
|
3
3
|
import {DataType} from '@e22m4u/ts-data-schema';
|
|
4
|
+
import {ServiceContainer} from '@e22m4u/js-service';
|
|
4
5
|
import {requestData} from './request-data-decorator.js';
|
|
5
6
|
import {requestBody} from './request-data-decorator.js';
|
|
6
7
|
import {requestField} from './request-data-decorator.js';
|
|
@@ -13,6 +14,8 @@ import {requestQueries} from './request-data-decorator.js';
|
|
|
13
14
|
import {requestHeaders} from './request-data-decorator.js';
|
|
14
15
|
import {requestCookies} from './request-data-decorator.js';
|
|
15
16
|
import {RequestDataSource} from './request-data-metadata.js';
|
|
17
|
+
import {DataSchemaFactory} from '../../data-schema-types.js';
|
|
18
|
+
import {RequestDataMetadata} from './request-data-metadata.js';
|
|
16
19
|
import {RequestDataReflector} from './request-data-reflector.js';
|
|
17
20
|
|
|
18
21
|
describe('requestData', function () {
|
|
@@ -140,7 +143,7 @@ describe('requestData', function () {
|
|
|
140
143
|
});
|
|
141
144
|
});
|
|
142
145
|
|
|
143
|
-
it('
|
|
146
|
+
it('sets a given DataSchema to the target metadata', function () {
|
|
144
147
|
const schema = {type: DataType.STRING, required: true};
|
|
145
148
|
class Target {
|
|
146
149
|
myMethod(
|
|
@@ -154,6 +157,21 @@ describe('requestData', function () {
|
|
|
154
157
|
schema,
|
|
155
158
|
});
|
|
156
159
|
});
|
|
160
|
+
|
|
161
|
+
it('sets a given DataSchemaFactory to the target metadata', function () {
|
|
162
|
+
const factory = () => ({type: DataType.STRING, required: true});
|
|
163
|
+
class Target {
|
|
164
|
+
myMethod(
|
|
165
|
+
@requestBody(factory)
|
|
166
|
+
prop: unknown,
|
|
167
|
+
) {}
|
|
168
|
+
}
|
|
169
|
+
const res = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
170
|
+
expect(res.get(0)).to.be.eql({
|
|
171
|
+
source: RequestDataSource.BODY,
|
|
172
|
+
schema: factory,
|
|
173
|
+
});
|
|
174
|
+
});
|
|
157
175
|
});
|
|
158
176
|
});
|
|
159
177
|
|
|
@@ -230,6 +248,37 @@ describe('requestData', function () {
|
|
|
230
248
|
property: propertyKey,
|
|
231
249
|
});
|
|
232
250
|
});
|
|
251
|
+
|
|
252
|
+
it('sets a given DataSchemaFactory to the target metadata', function () {
|
|
253
|
+
const container = {} as ServiceContainer;
|
|
254
|
+
const factory: DataSchemaFactory = sc => {
|
|
255
|
+
expect(sc).to.be.eq(container);
|
|
256
|
+
return {type: DataType.STRING, required: true};
|
|
257
|
+
};
|
|
258
|
+
const propertyKey = 'myPropertyKey';
|
|
259
|
+
class Target {
|
|
260
|
+
myMethod(
|
|
261
|
+
@requestParam(propertyKey, factory)
|
|
262
|
+
prop: unknown,
|
|
263
|
+
) {}
|
|
264
|
+
}
|
|
265
|
+
const mdMap = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
266
|
+
const md = mdMap.get(0) as RequestDataMetadata;
|
|
267
|
+
expect(md.source).to.be.eq(RequestDataSource.PARAMS);
|
|
268
|
+
expect(md.schema).to.be.a('function');
|
|
269
|
+
expect(md.property).to.be.eq(propertyKey);
|
|
270
|
+
const res1 = md.schema as DataSchemaFactory;
|
|
271
|
+
const res2 = res1(container);
|
|
272
|
+
expect(res2).to.be.eql({
|
|
273
|
+
type: DataType.OBJECT,
|
|
274
|
+
properties: {
|
|
275
|
+
[propertyKey]: {
|
|
276
|
+
type: DataType.STRING,
|
|
277
|
+
required: true,
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
});
|
|
233
282
|
});
|
|
234
283
|
|
|
235
284
|
describe('query', function () {
|
|
@@ -304,6 +353,37 @@ describe('requestData', function () {
|
|
|
304
353
|
property: propertyKey,
|
|
305
354
|
});
|
|
306
355
|
});
|
|
356
|
+
|
|
357
|
+
it('sets a given DataSchemaFactory to the target metadata', function () {
|
|
358
|
+
const container = {} as ServiceContainer;
|
|
359
|
+
const factory: DataSchemaFactory = sc => {
|
|
360
|
+
expect(sc).to.be.eq(container);
|
|
361
|
+
return {type: DataType.STRING, required: true};
|
|
362
|
+
};
|
|
363
|
+
const propertyKey = 'myPropertyKey';
|
|
364
|
+
class Target {
|
|
365
|
+
myMethod(
|
|
366
|
+
@requestQuery(propertyKey, factory)
|
|
367
|
+
prop: unknown,
|
|
368
|
+
) {}
|
|
369
|
+
}
|
|
370
|
+
const mdMap = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
371
|
+
const md = mdMap.get(0) as RequestDataMetadata;
|
|
372
|
+
expect(md.source).to.be.eq(RequestDataSource.QUERY);
|
|
373
|
+
expect(md.schema).to.be.a('function');
|
|
374
|
+
expect(md.property).to.be.eq(propertyKey);
|
|
375
|
+
const res1 = md.schema as DataSchemaFactory;
|
|
376
|
+
const res2 = res1(container);
|
|
377
|
+
expect(res2).to.be.eql({
|
|
378
|
+
type: DataType.OBJECT,
|
|
379
|
+
properties: {
|
|
380
|
+
[propertyKey]: {
|
|
381
|
+
type: DataType.STRING,
|
|
382
|
+
required: true,
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
});
|
|
307
387
|
});
|
|
308
388
|
|
|
309
389
|
describe('header', function () {
|
|
@@ -378,6 +458,37 @@ describe('requestData', function () {
|
|
|
378
458
|
property: propertyKey,
|
|
379
459
|
});
|
|
380
460
|
});
|
|
461
|
+
|
|
462
|
+
it('sets a given DataSchemaFactory to the target metadata', function () {
|
|
463
|
+
const container = {} as ServiceContainer;
|
|
464
|
+
const factory: DataSchemaFactory = sc => {
|
|
465
|
+
expect(sc).to.be.eq(container);
|
|
466
|
+
return {type: DataType.STRING, required: true};
|
|
467
|
+
};
|
|
468
|
+
const propertyKey = 'myPropertyKey';
|
|
469
|
+
class Target {
|
|
470
|
+
myMethod(
|
|
471
|
+
@requestHeader(propertyKey, factory)
|
|
472
|
+
prop: unknown,
|
|
473
|
+
) {}
|
|
474
|
+
}
|
|
475
|
+
const mdMap = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
476
|
+
const md = mdMap.get(0) as RequestDataMetadata;
|
|
477
|
+
expect(md.source).to.be.eq(RequestDataSource.HEADERS);
|
|
478
|
+
expect(md.schema).to.be.a('function');
|
|
479
|
+
expect(md.property).to.be.eq(propertyKey);
|
|
480
|
+
const res1 = md.schema as DataSchemaFactory;
|
|
481
|
+
const res2 = res1(container);
|
|
482
|
+
expect(res2).to.be.eql({
|
|
483
|
+
type: DataType.OBJECT,
|
|
484
|
+
properties: {
|
|
485
|
+
[propertyKey]: {
|
|
486
|
+
type: DataType.STRING,
|
|
487
|
+
required: true,
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
});
|
|
491
|
+
});
|
|
381
492
|
});
|
|
382
493
|
|
|
383
494
|
describe('cookie', function () {
|
|
@@ -452,6 +563,37 @@ describe('requestData', function () {
|
|
|
452
563
|
property: propertyKey,
|
|
453
564
|
});
|
|
454
565
|
});
|
|
566
|
+
|
|
567
|
+
it('sets a given DataSchemaFactory to the target metadata', function () {
|
|
568
|
+
const container = {} as ServiceContainer;
|
|
569
|
+
const factory: DataSchemaFactory = sc => {
|
|
570
|
+
expect(sc).to.be.eq(container);
|
|
571
|
+
return {type: DataType.STRING, required: true};
|
|
572
|
+
};
|
|
573
|
+
const propertyKey = 'myPropertyKey';
|
|
574
|
+
class Target {
|
|
575
|
+
myMethod(
|
|
576
|
+
@requestCookie(propertyKey, factory)
|
|
577
|
+
prop: unknown,
|
|
578
|
+
) {}
|
|
579
|
+
}
|
|
580
|
+
const mdMap = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
581
|
+
const md = mdMap.get(0) as RequestDataMetadata;
|
|
582
|
+
expect(md.source).to.be.eq(RequestDataSource.COOKIE);
|
|
583
|
+
expect(md.schema).to.be.a('function');
|
|
584
|
+
expect(md.property).to.be.eq(propertyKey);
|
|
585
|
+
const res1 = md.schema as DataSchemaFactory;
|
|
586
|
+
const res2 = res1(container);
|
|
587
|
+
expect(res2).to.be.eql({
|
|
588
|
+
type: DataType.OBJECT,
|
|
589
|
+
properties: {
|
|
590
|
+
[propertyKey]: {
|
|
591
|
+
type: DataType.STRING,
|
|
592
|
+
required: true,
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
});
|
|
455
597
|
});
|
|
456
598
|
|
|
457
599
|
describe('field', function () {
|
|
@@ -526,6 +668,37 @@ describe('requestData', function () {
|
|
|
526
668
|
property: propertyKey,
|
|
527
669
|
});
|
|
528
670
|
});
|
|
671
|
+
|
|
672
|
+
it('sets a given DataSchemaFactory to the target metadata', function () {
|
|
673
|
+
const container = {} as ServiceContainer;
|
|
674
|
+
const factory: DataSchemaFactory = sc => {
|
|
675
|
+
expect(sc).to.be.eq(container);
|
|
676
|
+
return {type: DataType.STRING, required: true};
|
|
677
|
+
};
|
|
678
|
+
const propertyKey = 'myPropertyKey';
|
|
679
|
+
class Target {
|
|
680
|
+
myMethod(
|
|
681
|
+
@requestField(propertyKey, factory)
|
|
682
|
+
prop: unknown,
|
|
683
|
+
) {}
|
|
684
|
+
}
|
|
685
|
+
const mdMap = RequestDataReflector.getMetadata(Target, 'myMethod');
|
|
686
|
+
const md = mdMap.get(0) as RequestDataMetadata;
|
|
687
|
+
expect(md.source).to.be.eq(RequestDataSource.BODY);
|
|
688
|
+
expect(md.schema).to.be.a('function');
|
|
689
|
+
expect(md.property).to.be.eq(propertyKey);
|
|
690
|
+
const res1 = md.schema as DataSchemaFactory;
|
|
691
|
+
const res2 = res1(container);
|
|
692
|
+
expect(res2).to.be.eql({
|
|
693
|
+
type: DataType.OBJECT,
|
|
694
|
+
properties: {
|
|
695
|
+
[propertyKey]: {
|
|
696
|
+
type: DataType.STRING,
|
|
697
|
+
required: true,
|
|
698
|
+
},
|
|
699
|
+
},
|
|
700
|
+
});
|
|
701
|
+
});
|
|
529
702
|
});
|
|
530
703
|
});
|
|
531
704
|
});
|