@tsed/platform-params 8.0.1 → 8.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/lib/types/decorators/locals.d.ts +1 -1
  2. package/package.json +14 -13
  3. package/src/builder/PlatformParams.spec.ts +431 -0
  4. package/src/builder/PlatformParams.ts +86 -0
  5. package/src/decorators/bodyParams.spec.ts +121 -0
  6. package/src/decorators/bodyParams.ts +93 -0
  7. package/src/decorators/context.spec.ts +16 -0
  8. package/src/decorators/context.ts +70 -0
  9. package/src/decorators/cookies.spec.ts +19 -0
  10. package/src/decorators/cookies.ts +102 -0
  11. package/src/decorators/headerParams.spec.ts +16 -0
  12. package/src/decorators/headerParams.ts +49 -0
  13. package/src/decorators/locals.spec.ts +16 -0
  14. package/src/decorators/locals.ts +57 -0
  15. package/src/decorators/paramFn.ts +14 -0
  16. package/src/decorators/pathParams.spec.ts +58 -0
  17. package/src/decorators/pathParams.ts +97 -0
  18. package/src/decorators/queryParams.spec.ts +58 -0
  19. package/src/decorators/queryParams.ts +102 -0
  20. package/src/decorators/session.spec.ts +47 -0
  21. package/src/decorators/session.ts +51 -0
  22. package/src/decorators/useDeserialization.ts +14 -0
  23. package/src/decorators/useParam.spec.ts +82 -0
  24. package/src/decorators/useParam.ts +38 -0
  25. package/src/decorators/useParamExpression.ts +16 -0
  26. package/src/decorators/useParamType.ts +36 -0
  27. package/src/decorators/usePipe.ts +21 -0
  28. package/src/decorators/useType.ts +35 -0
  29. package/src/decorators/useValidation.ts +18 -0
  30. package/src/domain/ParamMetadata.ts +1 -0
  31. package/src/domain/ParamOptions.ts +12 -0
  32. package/src/domain/ParamTypes.ts +37 -0
  33. package/src/errors/ParamValidationError.spec.ts +152 -0
  34. package/src/errors/ParamValidationError.ts +38 -0
  35. package/src/errors/RequiredValidationError.spec.ts +49 -0
  36. package/src/errors/RequiredValidationError.ts +30 -0
  37. package/src/errors/ValidationError.spec.ts +18 -0
  38. package/src/errors/ValidationError.ts +11 -0
  39. package/src/exports.ts +1 -0
  40. package/src/index.ts +31 -0
  41. package/src/pipes/DeserializerPipe.spec.ts +39 -0
  42. package/src/pipes/DeserializerPipe.ts +15 -0
  43. package/src/pipes/ParseExpressionPipe.spec.ts +242 -0
  44. package/src/pipes/ParseExpressionPipe.ts +33 -0
  45. package/src/pipes/ValidationPipe.spec.ts +645 -0
  46. package/src/pipes/ValidationPipe.ts +112 -0
  47. package/src/utils/mapParamsOptions.spec.ts +28 -0
  48. 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,12 @@
1
+ import {Type} from "@tsed/core";
2
+
3
+ export interface ParamOptions<T = any> {
4
+ dataPath: string;
5
+ paramType: string;
6
+ expression?: string;
7
+ useType?: Type<T>;
8
+ useMapper?: boolean;
9
+ useValidation?: boolean;
10
+
11
+ [key: string]: any;
12
+ }
@@ -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
+ });