diff --git a/packages/common/pipes/validation.pipe.ts b/packages/common/pipes/validation.pipe.ts index fd37802b5f0..2e3c582359c 100644 --- a/packages/common/pipes/validation.pipe.ts +++ b/packages/common/pipes/validation.pipe.ts @@ -18,7 +18,7 @@ import { HttpErrorByCode, } from '../utils/http-error-by-code.util'; import { loadPackage } from '../utils/load-package.util'; -import { isNil } from '../utils/shared.utils'; +import { isNil, isUndefined } from '../utils/shared.utils'; export interface ValidationPipeOptions extends ValidatorOptions { transform?: boolean; @@ -179,6 +179,13 @@ export class ValidationPipe implements PipeTransform { return value; } if (metatype === Boolean) { + if (isUndefined(value)) { + // This is an workaround to deal with optional boolean values since + // optional booleans shouldn't be parsed to a valid boolean when + // they were not defined + return undefined; + } + // Any fasly value but `undefined` will be parsed to `false` return value === true || value === 'true'; } if (metatype === Number) { diff --git a/packages/common/test/pipes/validation.pipe.spec.ts b/packages/common/test/pipes/validation.pipe.spec.ts index f5b44e86760..8c407d6adf9 100644 --- a/packages/common/test/pipes/validation.pipe.spec.ts +++ b/packages/common/test/pipes/validation.pipe.spec.ts @@ -221,7 +221,7 @@ describe('ValidationPipe', () => { }); }); describe('when input is a query parameter (boolean)', () => { - it('should parse to boolean', async () => { + it('should parse the string "true" to the boolean true', async () => { target = new ValidationPipe({ transform: true }); const value = 'true'; @@ -233,9 +233,45 @@ describe('ValidationPipe', () => { }), ).to.be.true; }); + it('should parse the string "false" to the boolean false', async () => { + target = new ValidationPipe({ transform: true }); + const value = 'false'; + + expect( + await target.transform(value, { + metatype: Boolean, + data: 'test', + type: 'query', + }), + ).to.be.false; + }); + it('should parse an empty string to false', async () => { + target = new ValidationPipe({ transform: true }); + const value = ''; + + expect( + await target.transform(value, { + metatype: Boolean, + data: 'test', + type: 'query', + }), + ).to.be.false; + }); + it('should parse undefined to undefined', async () => { + target = new ValidationPipe({ transform: true }); + const value = undefined; + + expect( + await target.transform(value, { + metatype: Boolean, + data: 'test', + type: 'query', + }), + ).to.be.undefined; + }); }); describe('when input is a path parameter (boolean)', () => { - it('should parse to boolean', async () => { + it('should parse the string "true" to boolean true', async () => { target = new ValidationPipe({ transform: true }); const value = 'true'; @@ -247,6 +283,42 @@ describe('ValidationPipe', () => { }), ).to.be.true; }); + it('should parse the string "false" to boolean false', async () => { + target = new ValidationPipe({ transform: true }); + const value = 'false'; + + expect( + await target.transform(value, { + metatype: Boolean, + data: 'test', + type: 'param', + }), + ).to.be.false; + }); + it('should parse an empty string to false', async () => { + target = new ValidationPipe({ transform: true }); + const value = ''; + + expect( + await target.transform(value, { + metatype: Boolean, + data: 'test', + type: 'param', + }), + ).to.be.false; + }); + it('should parse undefined to undefined', async () => { + target = new ValidationPipe({ transform: true }); + const value = undefined; + + expect( + await target.transform(value, { + metatype: Boolean, + data: 'test', + type: 'param', + }), + ).to.be.undefined; + }); }); describe('when validation strips', () => { it('should return a TestModel without extra properties', async () => {