Skip to content

Commit 3c13d39

Browse files
committed
Add support for normalizing objects (query, path, etc.)
1 parent 4df235e commit 3c13d39

File tree

2 files changed

+33
-32
lines changed

2 files changed

+33
-32
lines changed

src/ActionParameterHandler.ts

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
4343

4444
// get parameter value from request and normalize it
4545
const value = this.normalizeParamValue(this.driver.getParamFromRequest(action, param), param);
46+
4647
if (isPromiseLike(value))
4748
return value.then(value => this.handleValue(value, action, param));
4849

@@ -73,7 +74,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
7374
// check cases when parameter is required but its empty and throw errors in this case
7475
if (param.required) {
7576
const isValueEmpty = value === null || value === undefined || value === "";
76-
const isValueEmptyObject = value instanceof Object && Object.keys(value).length === 0;
77+
const isValueEmptyObject = typeof value === "object" && Object.keys(value).length === 0;
7778

7879
if (param.type === "body" && !param.name && (isValueEmpty || isValueEmptyObject)) { // body has a special check and error message
7980
return Promise.reject(new ParamRequiredError(action, param));
@@ -108,39 +109,40 @@ export class ActionParameterHandler<T extends BaseDriver> {
108109
if (value === null || value === undefined)
109110
return value;
110111

111-
// map @QueryParams object properties from string to basic types (normalize)
112-
if (param.type === "queries" && typeof value === "object") {
112+
// if param value is an object and param type match, normalize its string properties
113+
if (typeof value === "object" && ["queries", "headers", "params", "cookies"].indexOf(param.type) !== -1) {
113114
Object.keys(value).map(key => {
114-
const ParamType = Reflect.getMetadata("design:type", param.targetType.prototype, key);
115-
if (ParamType) {
116-
const typeString = typeof ParamType(); // reflected type is always constructor-like (?)
117-
value[key] = this.normalizeValue(value[key], typeString);
115+
const keyValue = (value as any)[key];
116+
if (typeof keyValue === "string") {
117+
const ParamType = Reflect.getMetadata("design:type", param.targetType.prototype, key);
118+
if (ParamType) {
119+
const typeString = ParamType.name.toLowerCase(); // reflected type is always constructor-like (?)
120+
(value as any)[key] = this.normalizeStringValue(keyValue, param.name, typeString);
121+
}
118122
}
119123
});
120124
}
125+
// if value is a string, normalize it to demand type
126+
else if (typeof value === "string") {
127+
switch (param.targetName) {
128+
case "number":
129+
case "string":
130+
case "boolean":
131+
case "date":
132+
return this.normalizeStringValue(value, param.name, param.targetName);
133+
}
134+
}
121135

122-
switch (param.targetName) {
123-
124-
case "number":
125-
case "string":
126-
case "boolean":
127-
return this.normalizeValue(value, param.targetName);
128-
129-
case "date":
130-
const parsedDate = new Date(value);
131-
if (isNaN(parsedDate.getTime())) {
132-
throw new BadRequestError(`${param.name} is invalid! It can't be parsed to date.`);
133-
}
134-
return parsedDate;
135-
136-
default:
137-
if (value && (param.parse || param.isTargetObject)) {
138-
value = this.parseValue(value, param);
139-
value = this.transformValue(value, param);
140-
value = this.validateValue(value, param); // note this one can return promise
141-
}
142-
return value;
136+
// if target type is not primitive, transform and validate it
137+
if ((["number", "string", "boolean"].indexOf(param.targetName) === -1)
138+
&& (param.parse || param.isTargetObject)
139+
) {
140+
value = this.parseValue(value, param);
141+
value = this.transformValue(value, param);
142+
value = this.validateValue(value, param); // note this one can return promise
143143
}
144+
145+
return value;
144146
}
145147

146148
/**
@@ -168,7 +170,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
168170
} else {
169171
throw new ParamNormalizationError(value, parameterName, parameterType);
170172
}
171-
173+
172174
case "date":
173175
const parsedDate = new Date(value);
174176
if (Number.isNaN(parsedDate.getTime())) {

test/functional/action-params.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ describe("action parameters", () => {
461461

462462
// todo: enable koa test when #227 fixed
463463
describe("@QueryParams should give a proper values from request query parameters", () => {
464-
assertRequest([3001, /*3002*/], "get", "photos-params?sortBy=name&count=2&limit=10&showAll=true", response => {
464+
assertRequest([3001, /*3002*/], "get", "photos-params?sortBy=name&count=2&limit=10&showAll", response => {
465465
expect(response).to.be.status(200);
466466
expect(response).to.have.header("content-type", "text/html; charset=utf-8");
467467
expect(queryParams1.sortBy).to.be.equal("name");
@@ -530,8 +530,7 @@ describe("action parameters", () => {
530530
describe("for @QueryParam when the type is Date and it is invalid then the response should be a BadRequest error", () => {
531531
assertRequest([3001, 3002], "get", "posts-after/?from=InvalidDate", response => {
532532
expect(response).to.be.status(400);
533-
expect(response.body.name).to.be.equals("BadRequestError");
534-
expect(response.body.message).to.be.equals("from is invalid! It can't be parsed to date.");
533+
expect(response.body.name).to.be.equals("ParamNormalizationError");
535534
});
536535
});
537536

0 commit comments

Comments
 (0)