Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Impossible to use UUID as param for object construction #706 #708

Closed
13 changes: 8 additions & 5 deletions src/ActionParameterHandler.ts
Expand Up @@ -96,11 +96,13 @@ export class ActionParameterHandler<T extends BaseDriver> {
protected async normalizeParamValue(value: any, param: ParamMetadata): Promise<any> {
if (value === null || value === undefined) return value;

const isNormalisationNeeded =
ivanproskuryakov marked this conversation as resolved.
Show resolved Hide resolved
typeof value === 'object' && ['queries', 'headers', 'params', 'cookies'].includes(param.type);
const isTargetPrimitive = ['number', 'string', 'boolean'].includes(param.targetName);
const isTransformationNeeded = (param.parse || param.isTargetObject) && param.type !== 'param';

// if param value is an object and param type match, normalize its string properties
if (
typeof value === 'object' &&
['queries', 'headers', 'params', 'cookies'].some(paramType => paramType === param.type)
) {
if (isNormalisationNeeded) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (isNormalisationNeeded) {
if (isNormalizationNeeded) {

await Promise.all(
Object.keys(value).map(async key => {
const keyValue = value[key];
Expand All @@ -123,6 +125,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
})
);
}

// if value is a string, normalize it to demanded type
else if (typeof value === 'string') {
switch (param.targetName) {
Expand All @@ -135,7 +138,7 @@ export class ActionParameterHandler<T extends BaseDriver> {
}

// if target type is not primitive, transform and validate it
if (['number', 'string', 'boolean'].indexOf(param.targetName) === -1 && (param.parse || param.isTargetObject)) {
if (!isTargetPrimitive && isTransformationNeeded) {
value = this.parseValue(value, param);
value = this.transformValue(value, param);
value = await this.validateValue(value, param);
Expand Down
156 changes: 156 additions & 0 deletions test/ActionParameterHandler.spec.ts
@@ -0,0 +1,156 @@
import { ActionParameterHandler } from '../src/ActionParameterHandler';
import { ActionMetadata, ControllerMetadata, ExpressDriver, ParamMetadata } from '../src';
import { ActionMetadataArgs } from '../src/metadata/args/ActionMetadataArgs';
import { ControllerMetadataArgs } from '../src/metadata/args/ControllerMetadataArgs';
import { ParamType } from '../src/metadata/types/ParamType';

const expect = require('chakram').expect;

describe('ActionParameterHandler', () => {
const buildParamMetadata = (
name: string = 'id',
type: ParamType = 'param',
isRequired: boolean = false
): ParamMetadata => {
const controllerMetadataArgs: ControllerMetadataArgs = {
target: function () {},
route: '',
type: 'json',
options: {},
};
const controllerMetadata = new ControllerMetadata(controllerMetadataArgs);
const args: ActionMetadataArgs = {
route: '',
method: 'getProduct',
options: {},
target: function () {},
type: 'get',
appendParams: undefined,
};
const actionMetadata = new ActionMetadata(controllerMetadata, args, {});

return {
type,
name,
targetName: 'product',
isTargetObject: true,
actionMetadata,
target: () => {},
method: 'getProduct',
object: 'getProduct',
extraOptions: undefined,
index: 0,
parse: undefined,
required: isRequired,
transform: function (action, value) {
return value;
},
classTransform: undefined,
validate: undefined,
targetType: function () {},
};
};
const driver = new ExpressDriver();
const actionParameterHandler = new ActionParameterHandler(driver);

describe('positive', () => {
it('handle - should process string parameters', async () => {
const param = buildParamMetadata('uuid');
const action = {
request: {
params: {
uuid: '0b5ec98f-e26d-4414-b798-dcd35a5ef859',
},
},
response: {},
};

const processedValue = await actionParameterHandler.handle(action, param);

expect(processedValue).to.be.eq(action.request.params.uuid);
});

it('handle - should process string parameters, returns empty if a given string is empty', async () => {
const param = buildParamMetadata('uuid');
const action = {
request: {
params: {
uuid: '',
},
},
response: {},
};

const processedValue = await actionParameterHandler.handle(action, param);

expect(processedValue).to.be.eq(action.request.params.uuid);
});

it('handle - should process number parameters', async () => {
const param = buildParamMetadata('id');
const action = {
request: {
params: {
id: 10000,
},
},
response: {},
};

const processedValue = await actionParameterHandler.handle(action, param);

expect(processedValue).to.be.eq(action.request.params.id);
});

it('handle - undefined on empty object provided', async () => {
const param = buildParamMetadata();
const action = {
request: {
params: {},
},
response: {},
};

const processedValue = await actionParameterHandler.handle(action, param);

expect(processedValue).to.be.eq(undefined);
});
});
describe('negative', () => {
it('handle - throws error if the parameter is required', async () => {
const param = buildParamMetadata('uuid', 'param', true);
const action = {
request: {},
response: {},
};
let error;

try {
await actionParameterHandler.handle(action, param);
} catch (e) {
error = e;
}

expect(error.toString()).to.be.eq("TypeError: Cannot read property 'uuid' of undefined");
});

it('handle - throws error if the parameter is required, type file provided', async () => {
const param = buildParamMetadata('uuid', 'file', true);
const action = {
request: {},
response: {},
};
let error;

try {
await actionParameterHandler.handle(action, param);
} catch (e) {
error = e;
}

expect(error.httpCode).to.be.eq(400);
expect(error.name).to.be.eq('ParamRequiredError');
expect(error.message).to.be.eq('Uploaded file "uuid" is required for request on undefined undefined');
});
});
});