@tsed/platform-params 8.0.1 → 8.0.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/package.json +14 -13
- package/src/builder/PlatformParams.spec.ts +431 -0
- package/src/builder/PlatformParams.ts +86 -0
- package/src/decorators/bodyParams.spec.ts +121 -0
- package/src/decorators/bodyParams.ts +93 -0
- package/src/decorators/context.spec.ts +16 -0
- package/src/decorators/context.ts +70 -0
- package/src/decorators/cookies.spec.ts +19 -0
- package/src/decorators/cookies.ts +102 -0
- package/src/decorators/headerParams.spec.ts +16 -0
- package/src/decorators/headerParams.ts +49 -0
- package/src/decorators/locals.spec.ts +16 -0
- package/src/decorators/locals.ts +57 -0
- package/src/decorators/paramFn.ts +14 -0
- package/src/decorators/pathParams.spec.ts +58 -0
- package/src/decorators/pathParams.ts +97 -0
- package/src/decorators/queryParams.spec.ts +58 -0
- package/src/decorators/queryParams.ts +102 -0
- package/src/decorators/session.spec.ts +47 -0
- package/src/decorators/session.ts +51 -0
- package/src/decorators/useDeserialization.ts +14 -0
- package/src/decorators/useParam.spec.ts +82 -0
- package/src/decorators/useParam.ts +38 -0
- package/src/decorators/useParamExpression.ts +16 -0
- package/src/decorators/useParamType.ts +36 -0
- package/src/decorators/usePipe.ts +21 -0
- package/src/decorators/useType.ts +35 -0
- package/src/decorators/useValidation.ts +18 -0
- package/src/domain/ParamMetadata.ts +1 -0
- package/src/domain/ParamOptions.ts +12 -0
- package/src/domain/ParamTypes.ts +37 -0
- package/src/errors/ParamValidationError.spec.ts +152 -0
- package/src/errors/ParamValidationError.ts +38 -0
- package/src/errors/RequiredValidationError.spec.ts +49 -0
- package/src/errors/RequiredValidationError.ts +30 -0
- package/src/errors/ValidationError.spec.ts +18 -0
- package/src/errors/ValidationError.ts +11 -0
- package/src/exports.ts +1 -0
- package/src/index.ts +31 -0
- package/src/pipes/DeserializerPipe.spec.ts +39 -0
- package/src/pipes/DeserializerPipe.ts +15 -0
- package/src/pipes/ParseExpressionPipe.spec.ts +242 -0
- package/src/pipes/ParseExpressionPipe.ts +33 -0
- package/src/pipes/ValidationPipe.spec.ts +645 -0
- package/src/pipes/ValidationPipe.ts +112 -0
- package/src/utils/mapParamsOptions.spec.ts +28 -0
- package/src/utils/mapParamsOptions.ts +34 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {Type} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {ParamOptions} from "../domain/ParamOptions.js";
|
|
4
|
+
import {ParamTypes} from "../domain/ParamTypes.js";
|
|
5
|
+
import {mapParamsOptions} from "../utils/mapParamsOptions.js";
|
|
6
|
+
import {UseParam} from "./useParam.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* QueryParams return the value from [request.query](http://expressjs.com/en/4x/api.html#req.query) object.
|
|
10
|
+
*
|
|
11
|
+
* #### Example
|
|
12
|
+
*
|
|
13
|
+
* ```typescript
|
|
14
|
+
* @Controller('/')
|
|
15
|
+
* class MyCtrl {
|
|
16
|
+
* @Get('/')
|
|
17
|
+
* get(@QueryParams() query: any) {
|
|
18
|
+
* console.log('Entire query', query);
|
|
19
|
+
* }
|
|
20
|
+
*
|
|
21
|
+
* @Get('/')
|
|
22
|
+
* get(@QueryParams('id') id: string) {
|
|
23
|
+
* console.log('ID', id);
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* @Get('/')
|
|
27
|
+
* get(@QueryParams('user') user: User) { // with deserialization
|
|
28
|
+
* console.log('user', user);
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* @Get('/')
|
|
32
|
+
* get(@QueryParams('users', User) users: User[]) { // with deserialization
|
|
33
|
+
* console.log('users', users);
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
* > For more information on deserialization see [converters](/docs/json-mapper.md) page.
|
|
38
|
+
*
|
|
39
|
+
* @param expression The path of the property to get.
|
|
40
|
+
* @param useType The type of the class that to be used to deserialize the data.
|
|
41
|
+
* @decorator
|
|
42
|
+
* @operation
|
|
43
|
+
* @input
|
|
44
|
+
*/
|
|
45
|
+
export function QueryParams(expression: string, useType: Type<any>): ParameterDecorator;
|
|
46
|
+
export function QueryParams(expression: string): ParameterDecorator;
|
|
47
|
+
export function QueryParams(useType: Type<any>): ParameterDecorator;
|
|
48
|
+
export function QueryParams(options: Partial<ParamOptions>): ParameterDecorator;
|
|
49
|
+
export function QueryParams(): ParameterDecorator;
|
|
50
|
+
export function QueryParams(...args: any[]): ParameterDecorator {
|
|
51
|
+
const {expression, useType, useMapper = true, useValidation = true} = mapParamsOptions(args);
|
|
52
|
+
|
|
53
|
+
return UseParam({
|
|
54
|
+
paramType: ParamTypes.QUERY,
|
|
55
|
+
expression,
|
|
56
|
+
useType,
|
|
57
|
+
useMapper,
|
|
58
|
+
useValidation
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* RawQueryParams return the value from [request.query](http://expressjs.com/en/4x/api.html#req.query) object.
|
|
64
|
+
*
|
|
65
|
+
* Any validation and transformation are performed on the value. Use [pipes](/docs/pipes.html) to validate and/or transform the value.
|
|
66
|
+
*
|
|
67
|
+
* #### Example
|
|
68
|
+
*
|
|
69
|
+
* ```typescript
|
|
70
|
+
* @Controller('/')
|
|
71
|
+
* class MyCtrl {
|
|
72
|
+
* @Get('/')
|
|
73
|
+
* get(@RawPathParams() params: any) {
|
|
74
|
+
* console.log('Entire params', params);
|
|
75
|
+
* }
|
|
76
|
+
*
|
|
77
|
+
* @Get('/')
|
|
78
|
+
* get(@RawPathParams('id') id: string) {
|
|
79
|
+
* console.log('ID', id);
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
* > For more information on deserialization see [converters](/docs/json-mapper.md) page.
|
|
84
|
+
*
|
|
85
|
+
* @param expression The path of the property to get.
|
|
86
|
+
* @decorator
|
|
87
|
+
* @operation
|
|
88
|
+
* @input
|
|
89
|
+
*/
|
|
90
|
+
export function RawQueryParams(expression: string): ParameterDecorator;
|
|
91
|
+
export function RawQueryParams(options: Partial<ParamOptions>): ParameterDecorator;
|
|
92
|
+
export function RawQueryParams(): ParameterDecorator;
|
|
93
|
+
export function RawQueryParams(...args: any[]) {
|
|
94
|
+
const {expression, useType, useMapper = false, useValidation = false} = mapParamsOptions(args);
|
|
95
|
+
|
|
96
|
+
return QueryParams({
|
|
97
|
+
expression,
|
|
98
|
+
useType,
|
|
99
|
+
useMapper,
|
|
100
|
+
useValidation
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {JsonParameterStore} from "@tsed/schema";
|
|
2
|
+
|
|
3
|
+
import {ParamTypes} from "../domain/ParamTypes.js";
|
|
4
|
+
import {Session} from "./session.js";
|
|
5
|
+
|
|
6
|
+
describe("@Session", () => {
|
|
7
|
+
it("should declare a session params", () => {
|
|
8
|
+
class Test {}
|
|
9
|
+
|
|
10
|
+
class Ctrl {
|
|
11
|
+
test(@Session() body: any) {}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const param = JsonParameterStore.get(Ctrl, "test", 0);
|
|
15
|
+
expect(param.expression).toEqual(undefined);
|
|
16
|
+
expect(param.paramType).toEqual(ParamTypes.SESSION);
|
|
17
|
+
expect(param.type).toEqual(Object);
|
|
18
|
+
expect(param.pipes).toHaveLength(0);
|
|
19
|
+
});
|
|
20
|
+
it("should declare a session params with options", () => {
|
|
21
|
+
class Test {}
|
|
22
|
+
|
|
23
|
+
class Ctrl {
|
|
24
|
+
test(@Session({expression: "expression", useType: Test}) body: Test) {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const param = JsonParameterStore.get(Ctrl, "test", 0);
|
|
28
|
+
expect(param.expression).toEqual("expression");
|
|
29
|
+
expect(param.paramType).toEqual(ParamTypes.SESSION);
|
|
30
|
+
expect(param.type).toEqual(Test);
|
|
31
|
+
expect(param.pipes).toHaveLength(0);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("should declare a session params with options (validation + mapping)", () => {
|
|
35
|
+
class Test {}
|
|
36
|
+
|
|
37
|
+
class Ctrl {
|
|
38
|
+
test(@Session({expression: "expression", useType: Test, useValidation: true, useMapper: true}) body: Test) {}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const param = JsonParameterStore.get(Ctrl, "test", 0);
|
|
42
|
+
expect(param.expression).toEqual("expression");
|
|
43
|
+
expect(param.paramType).toEqual(ParamTypes.SESSION);
|
|
44
|
+
expect(param.type).toEqual(Test);
|
|
45
|
+
expect(param.pipes).toHaveLength(2);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {ParamOptions} from "../domain/ParamOptions.js";
|
|
2
|
+
import {ParamTypes} from "../domain/ParamTypes.js";
|
|
3
|
+
import {mapParamsOptions} from "../utils/mapParamsOptions.js";
|
|
4
|
+
import {UseParam} from "./useParam.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Session return the value from [request.session](http://expressjs.com/en/4x/api.html#req.session) object.
|
|
8
|
+
*
|
|
9
|
+
* #### Example
|
|
10
|
+
*
|
|
11
|
+
* ```typescript
|
|
12
|
+
* @Controller('/')
|
|
13
|
+
* class MyCtrl {
|
|
14
|
+
* @Post('/')
|
|
15
|
+
* create(@Session() session: Express.Session) {
|
|
16
|
+
* console.log('Entire session', session);
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* @Post('/')
|
|
20
|
+
* create(@Session('id') id: string) {
|
|
21
|
+
* console.log('ID', id);
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* @Post('/') // Example to deserialize use from session
|
|
25
|
+
* create(@Session({expression: 'user', useMapper: true}) user: User) {
|
|
26
|
+
* console.log('user', user);
|
|
27
|
+
* console.log('instanceOf user', user instanceof User);
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
* > For more information on deserialization see [converters](/docs/json-mapper.md) page.
|
|
32
|
+
*
|
|
33
|
+
* @param expression The path of the property to get.
|
|
34
|
+
* @decorator
|
|
35
|
+
* @operation
|
|
36
|
+
* @input
|
|
37
|
+
*/
|
|
38
|
+
export function Session(expression: string): ParameterDecorator;
|
|
39
|
+
export function Session(options: Partial<ParamOptions>): ParameterDecorator;
|
|
40
|
+
export function Session(): ParameterDecorator;
|
|
41
|
+
export function Session(...args: any[]): ParameterDecorator {
|
|
42
|
+
const {expression, useType, useMapper = false, useValidation = false} = mapParamsOptions(args);
|
|
43
|
+
|
|
44
|
+
return UseParam({
|
|
45
|
+
paramType: ParamTypes.SESSION,
|
|
46
|
+
expression,
|
|
47
|
+
useType,
|
|
48
|
+
useMapper,
|
|
49
|
+
useValidation
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {DeserializerPipe} from "../pipes/DeserializerPipe.js";
|
|
2
|
+
import {UsePipe} from "./usePipe.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Use JsonMapper to deserialize the data.
|
|
6
|
+
*
|
|
7
|
+
* @decorator
|
|
8
|
+
* @operation
|
|
9
|
+
* @input
|
|
10
|
+
* @pipe
|
|
11
|
+
*/
|
|
12
|
+
export function UseDeserialization(options: any = {}) {
|
|
13
|
+
return UsePipe(DeserializerPipe, options);
|
|
14
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {Description, Example, Get, getSpec, JsonParameterStore, Property, Required, SpecTypes, Title} from "@tsed/schema";
|
|
2
|
+
|
|
3
|
+
import {ParamTypes} from "../domain/ParamTypes.js";
|
|
4
|
+
import {QueryParams} from "./queryParams.js";
|
|
5
|
+
import {UseParam} from "./useParam.js";
|
|
6
|
+
|
|
7
|
+
export class MyModel {
|
|
8
|
+
@Title("iD")
|
|
9
|
+
@Description("Description of calendar model id")
|
|
10
|
+
@Example("Description example")
|
|
11
|
+
@Property()
|
|
12
|
+
public id: string;
|
|
13
|
+
|
|
14
|
+
@Property()
|
|
15
|
+
@Required()
|
|
16
|
+
public name: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("@UseParam", () => {
|
|
20
|
+
describe("when use a param", () => {
|
|
21
|
+
it("should create useParam", () => {
|
|
22
|
+
class Test {}
|
|
23
|
+
|
|
24
|
+
class Ctrl {
|
|
25
|
+
test(
|
|
26
|
+
@UseParam({
|
|
27
|
+
expression: "expression",
|
|
28
|
+
useMapper: true,
|
|
29
|
+
useValidation: true,
|
|
30
|
+
paramType: ParamTypes.BODY,
|
|
31
|
+
useType: Test
|
|
32
|
+
})
|
|
33
|
+
body: Test
|
|
34
|
+
) {}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const param = JsonParameterStore.get(Ctrl, "test", 0);
|
|
38
|
+
expect(param.expression).toEqual("expression");
|
|
39
|
+
expect(param.paramType).toEqual(ParamTypes.BODY);
|
|
40
|
+
expect(param.type).toEqual(Test);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("when is a Query param with boolean", () => {
|
|
44
|
+
it("should return the right spec", () => {
|
|
45
|
+
class Ctrl {
|
|
46
|
+
@Get("/")
|
|
47
|
+
get(@QueryParams("test") value: boolean) {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const spec = getSpec(Ctrl, {specType: SpecTypes.OPENAPI});
|
|
51
|
+
|
|
52
|
+
expect(spec).toEqual({
|
|
53
|
+
paths: {
|
|
54
|
+
"/": {
|
|
55
|
+
get: {
|
|
56
|
+
operationId: "ctrlGet",
|
|
57
|
+
parameters: [
|
|
58
|
+
{
|
|
59
|
+
in: "query",
|
|
60
|
+
name: "test",
|
|
61
|
+
required: false,
|
|
62
|
+
schema: {type: "boolean"}
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
responses: {
|
|
66
|
+
"200": {
|
|
67
|
+
description: "Success"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
tags: ["Ctrl"]
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
tags: [
|
|
75
|
+
{
|
|
76
|
+
name: "Ctrl"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {useDecorators} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {ParamOptions} from "../domain/ParamOptions.js";
|
|
4
|
+
import {ParamTypes} from "../domain/ParamTypes.js";
|
|
5
|
+
import {UseDeserialization} from "./useDeserialization.js";
|
|
6
|
+
import {UseParamType} from "./useParamType.js";
|
|
7
|
+
import {UseType} from "./useType.js";
|
|
8
|
+
import {UseValidation} from "./useValidation.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @ignore
|
|
12
|
+
* @param options
|
|
13
|
+
*/
|
|
14
|
+
function mapPipes(options: Partial<ParamOptions>) {
|
|
15
|
+
const {paramType, useType, expression, useValidation, useMapper, ...props} = options;
|
|
16
|
+
|
|
17
|
+
return [paramType !== ParamTypes.FILES && UseType(useType), useValidation && UseValidation(), useMapper && UseDeserialization(props)];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Register a new param. It uses the paramType to extract value and give it to the next pipe.
|
|
22
|
+
*
|
|
23
|
+
* Given options allow to enable or disable following pipes:
|
|
24
|
+
*
|
|
25
|
+
* - useType: Add extra type for the json mapper,
|
|
26
|
+
* - expression: Get property from the returned value by the previous pipe.
|
|
27
|
+
* - useValidation: Apply validation from the returned value by the previous pipe.
|
|
28
|
+
* - useMapper: Apply json mapper from the returned value by the previous pipe.
|
|
29
|
+
*
|
|
30
|
+
* @param options
|
|
31
|
+
* @decorator
|
|
32
|
+
* @operation
|
|
33
|
+
* @input
|
|
34
|
+
* @pipe
|
|
35
|
+
*/
|
|
36
|
+
export function UseParam(options: Partial<ParamOptions>): ParameterDecorator {
|
|
37
|
+
return useDecorators(UseParamType(options as any), ...mapPipes(options)) as ParameterDecorator;
|
|
38
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {ParamFn} from "./paramFn.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get data from a path inside the given object
|
|
5
|
+
*
|
|
6
|
+
* @param expression
|
|
7
|
+
* @decorator
|
|
8
|
+
* @operation
|
|
9
|
+
* @input
|
|
10
|
+
* @pipe
|
|
11
|
+
*/
|
|
12
|
+
export function UseParamExpression(expression?: string) {
|
|
13
|
+
return ParamFn((param) => {
|
|
14
|
+
param.expression = expression || "";
|
|
15
|
+
});
|
|
16
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import {isBuffer, useDecorators} from "@tsed/core";
|
|
2
|
+
import {isParameterType, Name} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {ParamOptions} from "../domain/ParamOptions.js";
|
|
5
|
+
import {PARAM_TYPES_DATA_PATH, ParamTypes} from "../domain/ParamTypes.js";
|
|
6
|
+
import {ParamFn} from "./paramFn.js";
|
|
7
|
+
import {UseParamExpression} from "./useParamExpression.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get the object from request (like body, params, query, etc...).
|
|
11
|
+
*
|
|
12
|
+
* @decorator
|
|
13
|
+
* @operation
|
|
14
|
+
* @input
|
|
15
|
+
* @pipe
|
|
16
|
+
*/
|
|
17
|
+
export function UseParamType(options: ParamOptions) {
|
|
18
|
+
return useDecorators(
|
|
19
|
+
ParamFn((param) => {
|
|
20
|
+
if (isParameterType(options.paramType)) {
|
|
21
|
+
param.parameter!.in(options.paramType);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
param.paramType = options.paramType;
|
|
25
|
+
|
|
26
|
+
if (isBuffer(param.type) || isBuffer(options.useType) || options.paramType === ParamTypes.RAW_BODY) {
|
|
27
|
+
param.paramType = ParamTypes.RAW_BODY;
|
|
28
|
+
param.parent.operation?.consumes(["*/*"]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
param.dataPath = options.dataPath || PARAM_TYPES_DATA_PATH[param.paramType] || "$ctx";
|
|
32
|
+
}),
|
|
33
|
+
options.expression && UseParamExpression(options.expression),
|
|
34
|
+
options.expression && Name(options.expression)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {Type} from "@tsed/core";
|
|
2
|
+
import {PipeMethods} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {ParamFn} from "./paramFn.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Register a Pipe to be used with the current decorated param.
|
|
8
|
+
*
|
|
9
|
+
* @param token
|
|
10
|
+
* @param options
|
|
11
|
+
* @decorator
|
|
12
|
+
* @operation
|
|
13
|
+
* @input
|
|
14
|
+
* @pipe
|
|
15
|
+
*/
|
|
16
|
+
export function UsePipe(token: Type<PipeMethods>, options: any = {}) {
|
|
17
|
+
return ParamFn((param) => {
|
|
18
|
+
param.store.set(token, options);
|
|
19
|
+
param.pipes.push(token);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {Type} from "@tsed/core";
|
|
2
|
+
import {Any, CollectionOf, type JsonParameterStore} from "@tsed/schema";
|
|
3
|
+
|
|
4
|
+
import {ParamFn} from "./paramFn.js";
|
|
5
|
+
|
|
6
|
+
function shouldFallBackToAny(entity: JsonParameterStore) {
|
|
7
|
+
if (entity.itemSchema.has("allOf") || entity.itemSchema.has("anyOf") || entity.itemSchema.has("oneOf")) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return entity.isCollection && entity.type === Object && [undefined, "object"].includes(entity.itemSchema.get("type"));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Set the type of the item collection.
|
|
16
|
+
*
|
|
17
|
+
* Prefer @@CollectionOf@@ usage.
|
|
18
|
+
*
|
|
19
|
+
* @param useType
|
|
20
|
+
* @decorator
|
|
21
|
+
* @operation
|
|
22
|
+
* @input
|
|
23
|
+
* @alias CollectionOf
|
|
24
|
+
*/
|
|
25
|
+
export function UseType(useType: undefined | any | Type<any>) {
|
|
26
|
+
return ParamFn((entity, parameters) => {
|
|
27
|
+
if (useType) {
|
|
28
|
+
return CollectionOf(useType);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (shouldFallBackToAny(entity)) {
|
|
32
|
+
Any()(...parameters);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {ValidationPipe} from "../pipes/ValidationPipe.js";
|
|
2
|
+
import {ParamFn} from "./paramFn.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enable validation on the decoratored parameter.
|
|
6
|
+
*
|
|
7
|
+
* @decorator
|
|
8
|
+
* @operation
|
|
9
|
+
* @input
|
|
10
|
+
* @pipe
|
|
11
|
+
*/
|
|
12
|
+
export function UseValidation() {
|
|
13
|
+
return ParamFn((param) => {
|
|
14
|
+
if (param.type || param.collectionType) {
|
|
15
|
+
param.pipes.push(ValidationPipe);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {ParamMetadata} from "@tsed/schema";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export enum ParamTypes {
|
|
2
|
+
$CTX = "$CTX",
|
|
3
|
+
BODY = "BODY",
|
|
4
|
+
PATH = "PATH",
|
|
5
|
+
QUERY = "QUERY",
|
|
6
|
+
HEADER = "HEADER",
|
|
7
|
+
RAW_BODY = "RAW_BODY",
|
|
8
|
+
COOKIES = "COOKIES",
|
|
9
|
+
LOCALS = "LOCALS",
|
|
10
|
+
SESSION = "SESSION",
|
|
11
|
+
FILES = "FILES",
|
|
12
|
+
|
|
13
|
+
/// NOT STD
|
|
14
|
+
NEXT_FN = "NEXT_FN",
|
|
15
|
+
ERR = "ERR",
|
|
16
|
+
REQUEST = "REQUEST",
|
|
17
|
+
PLATFORM_REQUEST = "PLATFORM_REQUEST",
|
|
18
|
+
NODE_REQUEST = "NODE_REQUEST",
|
|
19
|
+
RESPONSE = "RESPONSE",
|
|
20
|
+
PLATFORM_RESPONSE = "PLATFORM_RESPONSE",
|
|
21
|
+
NODE_RESPONSE = "NODE_RESPONSE",
|
|
22
|
+
RESPONSE_DATA = "RESPONSE_DATA",
|
|
23
|
+
ENDPOINT_INFO = "ENDPOINT_INFO",
|
|
24
|
+
RES = "RES",
|
|
25
|
+
REQ = "REQ"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const PARAM_TYPES_DATA_PATH: Record<string, string> = {
|
|
29
|
+
[ParamTypes.HEADER]: "$ctx.request.headers",
|
|
30
|
+
[ParamTypes.RAW_BODY]: "$ctx.request.rawBody",
|
|
31
|
+
[ParamTypes.BODY]: "$ctx.request.body",
|
|
32
|
+
[ParamTypes.QUERY]: "$ctx.request.query",
|
|
33
|
+
[ParamTypes.PATH]: "$ctx.request.params",
|
|
34
|
+
[ParamTypes.COOKIES]: "$ctx.request.cookies",
|
|
35
|
+
[ParamTypes.SESSION]: "$ctx.request.session",
|
|
36
|
+
[ParamTypes.LOCALS]: "$ctx.response.locals"
|
|
37
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {ParamValidationError} from "./ParamValidationError.js";
|
|
2
|
+
import {RequiredValidationError} from "./RequiredValidationError.js";
|
|
3
|
+
import {ValidationError} from "./ValidationError.js";
|
|
4
|
+
|
|
5
|
+
describe("ParseExpressionError", () => {
|
|
6
|
+
it("should return error without transformation", () => {
|
|
7
|
+
expect(ParamValidationError.from({} as any, new Error("error")).message).toEqual("error");
|
|
8
|
+
});
|
|
9
|
+
it("should return error", () => {
|
|
10
|
+
const validationError = new ValidationError("message");
|
|
11
|
+
|
|
12
|
+
const error = ParamValidationError.from(
|
|
13
|
+
{
|
|
14
|
+
paramType: "name",
|
|
15
|
+
expression: "expression"
|
|
16
|
+
} as any,
|
|
17
|
+
validationError
|
|
18
|
+
);
|
|
19
|
+
expect(error.message).toEqual('Bad request on parameter "request.name.expression".\nmessage');
|
|
20
|
+
expect(error.name).toEqual("PARAM_VALIDATION_ERROR");
|
|
21
|
+
expect(error.dataPath).toEqual("expression");
|
|
22
|
+
expect(error.requestType).toEqual("name");
|
|
23
|
+
expect({
|
|
24
|
+
name: error.name,
|
|
25
|
+
status: error.status,
|
|
26
|
+
headers: error.headers,
|
|
27
|
+
type: error.type,
|
|
28
|
+
message: error.message,
|
|
29
|
+
dataPath: error.dataPath,
|
|
30
|
+
requestType: error.requestType,
|
|
31
|
+
origin: {
|
|
32
|
+
name: error.origin.name,
|
|
33
|
+
status: error.origin.status,
|
|
34
|
+
headers: error.origin.headers,
|
|
35
|
+
type: error.origin.type,
|
|
36
|
+
message: error.origin.message,
|
|
37
|
+
errors: error.origin.errors
|
|
38
|
+
}
|
|
39
|
+
}).toEqual({
|
|
40
|
+
dataPath: "expression",
|
|
41
|
+
headers: {},
|
|
42
|
+
name: "PARAM_VALIDATION_ERROR",
|
|
43
|
+
requestType: "name",
|
|
44
|
+
status: 400,
|
|
45
|
+
type: "HTTP_EXCEPTION",
|
|
46
|
+
message: 'Bad request on parameter "request.name.expression".\nmessage',
|
|
47
|
+
origin: {
|
|
48
|
+
errors: [],
|
|
49
|
+
headers: {},
|
|
50
|
+
message: "message",
|
|
51
|
+
name: "VALIDATION_ERROR",
|
|
52
|
+
status: 400,
|
|
53
|
+
type: "HTTP_EXCEPTION"
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
it("should throw error from origin error (RequiredValidationError)", () => {
|
|
58
|
+
const metadata = {
|
|
59
|
+
paramType: "name",
|
|
60
|
+
expression: "expression"
|
|
61
|
+
} as any;
|
|
62
|
+
const origin = RequiredValidationError.from(metadata);
|
|
63
|
+
|
|
64
|
+
const error = ParamValidationError.from(metadata, origin);
|
|
65
|
+
expect(error.message).toEqual("Bad request on parameter \"request.name.expression\".\nIt should have required parameter 'expression'");
|
|
66
|
+
expect(error.dataPath).toEqual("expression");
|
|
67
|
+
expect(error.requestType).toEqual("name");
|
|
68
|
+
|
|
69
|
+
expect({
|
|
70
|
+
...error,
|
|
71
|
+
message: error.message,
|
|
72
|
+
origin: {
|
|
73
|
+
...error.origin,
|
|
74
|
+
message: error.origin.message
|
|
75
|
+
}
|
|
76
|
+
}).toEqual({
|
|
77
|
+
dataPath: "expression",
|
|
78
|
+
headers: {},
|
|
79
|
+
name: "PARAM_VALIDATION_ERROR",
|
|
80
|
+
message: "Bad request on parameter \"request.name.expression\".\nIt should have required parameter 'expression'",
|
|
81
|
+
origin: {
|
|
82
|
+
errors: [
|
|
83
|
+
{
|
|
84
|
+
requestPath: "name",
|
|
85
|
+
dataPath: ".expression",
|
|
86
|
+
keyword: "required",
|
|
87
|
+
message: "It should have required parameter 'expression'",
|
|
88
|
+
modelName: "name",
|
|
89
|
+
params: {
|
|
90
|
+
missingProperty: "expression"
|
|
91
|
+
},
|
|
92
|
+
schemaPath: "#/required"
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
headers: {},
|
|
96
|
+
name: "REQUIRED_VALIDATION_ERROR",
|
|
97
|
+
message: "It should have required parameter 'expression'",
|
|
98
|
+
status: 400,
|
|
99
|
+
type: "HTTP_EXCEPTION"
|
|
100
|
+
},
|
|
101
|
+
requestType: "name",
|
|
102
|
+
status: 400,
|
|
103
|
+
type: "HTTP_EXCEPTION"
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
it("should throw error from origin error (ValidationError)", () => {
|
|
107
|
+
const metadata = {
|
|
108
|
+
paramType: "name",
|
|
109
|
+
expression: "expression"
|
|
110
|
+
} as any;
|
|
111
|
+
const origin = new ValidationError("It should have 1 item", [
|
|
112
|
+
{
|
|
113
|
+
dataPath: "hello"
|
|
114
|
+
}
|
|
115
|
+
]);
|
|
116
|
+
|
|
117
|
+
const error = ParamValidationError.from(metadata, origin);
|
|
118
|
+
expect(error.message).toEqual('Bad request on parameter "request.name.expression".\nIt should have 1 item');
|
|
119
|
+
expect(error.dataPath).toEqual("expression");
|
|
120
|
+
expect(error.requestType).toEqual("name");
|
|
121
|
+
|
|
122
|
+
expect({
|
|
123
|
+
...error,
|
|
124
|
+
message: error.message,
|
|
125
|
+
origin: {
|
|
126
|
+
...error.origin,
|
|
127
|
+
message: error.origin.message
|
|
128
|
+
}
|
|
129
|
+
}).toEqual({
|
|
130
|
+
dataPath: "expression",
|
|
131
|
+
headers: {},
|
|
132
|
+
name: "PARAM_VALIDATION_ERROR",
|
|
133
|
+
origin: {
|
|
134
|
+
errors: [
|
|
135
|
+
{
|
|
136
|
+
requestPath: "name",
|
|
137
|
+
dataPath: "hello"
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
headers: {},
|
|
141
|
+
message: "It should have 1 item",
|
|
142
|
+
name: "VALIDATION_ERROR",
|
|
143
|
+
status: 400,
|
|
144
|
+
type: "HTTP_EXCEPTION"
|
|
145
|
+
},
|
|
146
|
+
message: 'Bad request on parameter "request.name.expression".\nIt should have 1 item',
|
|
147
|
+
requestType: "name",
|
|
148
|
+
status: 400,
|
|
149
|
+
type: "HTTP_EXCEPTION"
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|