Skip to content

Commit f163c34

Browse files
authored
Merge pull request #753 from ivanproskuryakov/uuid-parameters-703
feat: Impossible to use UUID as param for object construction #706
2 parents af9a973 + 796fe9b commit f163c34

File tree

2 files changed

+164
-5
lines changed

2 files changed

+164
-5
lines changed

src/ActionParameterHandler.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,13 @@ export class ActionParameterHandler<T extends BaseDriver> {
9696
protected async normalizeParamValue(value: any, param: ParamMetadata): Promise<any> {
9797
if (value === null || value === undefined) return value;
9898

99+
const isNormalizationNeeded =
100+
typeof value === 'object' && ['queries', 'headers', 'params', 'cookies'].includes(param.type);
101+
const isTargetPrimitive = ['number', 'string', 'boolean'].includes(param.targetName);
102+
const isTransformationNeeded = (param.parse || param.isTargetObject) && param.type !== 'param';
103+
99104
// if param value is an object and param type match, normalize its string properties
100-
if (
101-
typeof value === 'object' &&
102-
['queries', 'headers', 'params', 'cookies'].some(paramType => paramType === param.type)
103-
) {
105+
if (isNormalizationNeeded) {
104106
await Promise.all(
105107
Object.keys(value).map(async key => {
106108
const keyValue = value[key];
@@ -123,6 +125,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
123125
})
124126
);
125127
}
128+
126129
// if value is a string, normalize it to demanded type
127130
else if (typeof value === 'string') {
128131
switch (param.targetName) {
@@ -135,7 +138,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
135138
}
136139

137140
// if target type is not primitive, transform and validate it
138-
if (['number', 'string', 'boolean'].indexOf(param.targetName) === -1 && (param.parse || param.isTargetObject)) {
141+
if (!isTargetPrimitive && isTransformationNeeded) {
139142
value = this.parseValue(value, param);
140143
value = this.transformValue(value, param);
141144
value = await this.validateValue(value, param);

test/ActionParameterHandler.spec.ts

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import { ActionParameterHandler } from '../src/ActionParameterHandler';
2+
import { ActionMetadata, ControllerMetadata, ExpressDriver, ParamMetadata } from '../src';
3+
import { ActionMetadataArgs } from '../src/metadata/args/ActionMetadataArgs';
4+
import { ControllerMetadataArgs } from '../src/metadata/args/ControllerMetadataArgs';
5+
import { ParamType } from '../src/metadata/types/ParamType';
6+
7+
const expect = require('chakram').expect;
8+
9+
describe('ActionParameterHandler', () => {
10+
const buildParamMetadata = (
11+
name: string = 'id',
12+
type: ParamType = 'param',
13+
isRequired: boolean = false
14+
): ParamMetadata => {
15+
const controllerMetadataArgs: ControllerMetadataArgs = {
16+
target: function () {},
17+
route: '',
18+
type: 'json',
19+
options: {},
20+
};
21+
const controllerMetadata = new ControllerMetadata(controllerMetadataArgs);
22+
const args: ActionMetadataArgs = {
23+
route: '',
24+
method: 'getProduct',
25+
options: {},
26+
target: function () {},
27+
type: 'get',
28+
appendParams: undefined,
29+
};
30+
const actionMetadata = new ActionMetadata(controllerMetadata, args, {});
31+
32+
return {
33+
type,
34+
name,
35+
targetName: 'product',
36+
isTargetObject: true,
37+
actionMetadata,
38+
target: () => {},
39+
method: 'getProduct',
40+
object: 'getProduct',
41+
extraOptions: undefined,
42+
index: 0,
43+
parse: undefined,
44+
required: isRequired,
45+
transform: function (action, value) {
46+
return value;
47+
},
48+
classTransform: undefined,
49+
validate: undefined,
50+
targetType: function () {},
51+
};
52+
};
53+
const driver = new ExpressDriver();
54+
const actionParameterHandler = new ActionParameterHandler(driver);
55+
56+
describe('positive', () => {
57+
it('handle - should process string parameters', async () => {
58+
const param = buildParamMetadata('uuid');
59+
const action = {
60+
request: {
61+
params: {
62+
uuid: '0b5ec98f-e26d-4414-b798-dcd35a5ef859',
63+
},
64+
},
65+
response: {},
66+
};
67+
68+
const processedValue = await actionParameterHandler.handle(action, param);
69+
70+
expect(processedValue).to.be.eq(action.request.params.uuid);
71+
});
72+
73+
it('handle - should process string parameters, returns empty if a given string is empty', async () => {
74+
const param = buildParamMetadata('uuid');
75+
const action = {
76+
request: {
77+
params: {
78+
uuid: '',
79+
},
80+
},
81+
response: {},
82+
};
83+
84+
const processedValue = await actionParameterHandler.handle(action, param);
85+
86+
expect(processedValue).to.be.eq(action.request.params.uuid);
87+
});
88+
89+
it('handle - should process number parameters', async () => {
90+
const param = buildParamMetadata('id');
91+
const action = {
92+
request: {
93+
params: {
94+
id: 10000,
95+
},
96+
},
97+
response: {},
98+
};
99+
100+
const processedValue = await actionParameterHandler.handle(action, param);
101+
102+
expect(processedValue).to.be.eq(action.request.params.id);
103+
});
104+
105+
it('handle - undefined on empty object provided', async () => {
106+
const param = buildParamMetadata();
107+
const action = {
108+
request: {
109+
params: {},
110+
},
111+
response: {},
112+
};
113+
114+
const processedValue = await actionParameterHandler.handle(action, param);
115+
116+
expect(processedValue).to.be.eq(undefined);
117+
});
118+
});
119+
describe('negative', () => {
120+
it('handle - throws error if the parameter is required', async () => {
121+
const param = buildParamMetadata('uuid', 'param', true);
122+
const action = {
123+
request: {},
124+
response: {},
125+
};
126+
let error;
127+
128+
try {
129+
await actionParameterHandler.handle(action, param);
130+
} catch (e) {
131+
error = e;
132+
}
133+
134+
expect(error.toString()).to.be.eq("TypeError: Cannot read property 'uuid' of undefined");
135+
});
136+
137+
it('handle - throws error if the parameter is required, type file provided', async () => {
138+
const param = buildParamMetadata('uuid', 'file', true);
139+
const action = {
140+
request: {},
141+
response: {},
142+
};
143+
let error;
144+
145+
try {
146+
await actionParameterHandler.handle(action, param);
147+
} catch (e) {
148+
error = e;
149+
}
150+
151+
expect(error.httpCode).to.be.eq(400);
152+
expect(error.name).to.be.eq('ParamRequiredError');
153+
expect(error.message).to.be.eq('Uploaded file "uuid" is required for request on undefined undefined');
154+
});
155+
});
156+
});

0 commit comments

Comments
 (0)