Skip to content

Commit

Permalink
Merge pull request #8459 from titivuk/fix/ParseUUIDPipe
Browse files Browse the repository at this point in the history
fix(common): ParseUUIDPipe - throw exceptions with exceptionFactory only
  • Loading branch information
kamilmysliwiec committed May 17, 2022
2 parents bad85d9 + ef70418 commit 28afaa2
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 38 deletions.
19 changes: 17 additions & 2 deletions packages/common/pipes/parse-uuid.pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ErrorHttpStatusCode,
HttpErrorByCode,
} from '../utils/http-error-by-code.util';
import { isUUID } from '../utils/is-uuid';
import { isString } from '../utils/shared.utils';

export interface ParseUUIDPipeOptions {
version?: '3' | '4' | '5';
Expand All @@ -19,6 +19,12 @@ export interface ParseUUIDPipeOptions {

@Injectable()
export class ParseUUIDPipe implements PipeTransform<string> {
protected static uuidRegExps = {
3: /^[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
4: /^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
5: /^[0-9A-F]{8}-[0-9A-F]{4}-5[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i,
all: /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i,
};
private readonly version: '3' | '4' | '5';
protected exceptionFactory: (errors: string) => any;

Expand All @@ -35,8 +41,9 @@ export class ParseUUIDPipe implements PipeTransform<string> {
exceptionFactory ||
(error => new HttpErrorByCode[errorHttpStatusCode](error));
}

async transform(value: string, metadata: ArgumentMetadata): Promise<string> {
if (!isUUID(value, this.version)) {
if (!this.isUUID(value, this.version)) {
throw this.exceptionFactory(
`Validation failed (uuid ${
this.version ? 'v' + this.version : ''
Expand All @@ -45,4 +52,12 @@ export class ParseUUIDPipe implements PipeTransform<string> {
}
return value;
}

protected isUUID(str: unknown, version = 'all') {
if (!isString(str)) {
throw this.exceptionFactory('The value passed as UUID is not a string');
}
const pattern = ParseUUIDPipe.uuidRegExps[version];
return pattern?.test(str);
}
}
1 change: 0 additions & 1 deletion packages/common/test/pipes/parse-int.pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as sinon from 'sinon';
import { expect } from 'chai';
import { ArgumentMetadata } from '../../interfaces';
import { ParseIntPipe } from '../../pipes/parse-int.pipe';
Expand Down
72 changes: 54 additions & 18 deletions packages/common/test/pipes/parse-uuid.pipe.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
import { expect } from 'chai';
import { HttpStatus } from '../../enums';
import { HttpException } from '../../exceptions';
import { ArgumentMetadata } from '../../interfaces';
import { ParseUUIDPipe } from '../../pipes/parse-uuid.pipe';

class TestException extends HttpException {
constructor() {
super('This is a TestException', HttpStatus.I_AM_A_TEAPOT);
}
}

describe('ParseUUIDPipe', () => {
let target: ParseUUIDPipe;
const exceptionFactory = (error: any) => new TestException();

describe('transform', () => {
const v3 = 'e8b5a51d-11c8-3310-a6ab-367563f20686';
Expand All @@ -12,53 +21,80 @@ describe('ParseUUIDPipe', () => {

describe('when validation passes', () => {
it('should return string if value is uuid v3, v4 or v5', async () => {
target = new ParseUUIDPipe();
target = new ParseUUIDPipe({ exceptionFactory });
expect(await target.transform(v3, {} as ArgumentMetadata)).to.equal(v3);
expect(await target.transform(v4, {} as ArgumentMetadata)).to.equal(v4);
expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5);
});

it('should return string if value is uuid v3', async () => {
target = new ParseUUIDPipe({ version: '3' });
target = new ParseUUIDPipe({ version: '3', exceptionFactory });
expect(await target.transform(v3, {} as ArgumentMetadata)).to.equal(v3);
});

it('should return string if value is uuid v4', async () => {
target = new ParseUUIDPipe({ version: '4' });
target = new ParseUUIDPipe({ version: '4', exceptionFactory });
expect(await target.transform(v4, {} as ArgumentMetadata)).to.equal(v4);
});

it('should return string if value is uuid v5', async () => {
target = new ParseUUIDPipe({ version: '5' });
target = new ParseUUIDPipe({ version: '5', exceptionFactory });
expect(await target.transform(v5, {} as ArgumentMetadata)).to.equal(v5);
});
});

describe('when validation fails', () => {
it('should throw an error', async () => {
target = new ParseUUIDPipe();
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
target = new ParseUUIDPipe({ exceptionFactory });
await expect(
target.transform('123a', {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
});

it('should throw an error - not a string', async () => {
target = new ParseUUIDPipe({ exceptionFactory });
await expect(
target.transform(undefined, {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
});

it('should throw an error - v3', async () => {
target = new ParseUUIDPipe({ version: '3' });
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
expect(target.transform(v4, {} as ArgumentMetadata)).to.be.rejected;
expect(target.transform(v5, {} as ArgumentMetadata)).to.be.rejected;
target = new ParseUUIDPipe({ version: '3', exceptionFactory });
await expect(
target.transform('123a', {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
await expect(
target.transform(v4, {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
await expect(
target.transform(v5, {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
});

it('should throw an error - v4', async () => {
target = new ParseUUIDPipe({ version: '4' });
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
expect(target.transform(v3, {} as ArgumentMetadata)).to.be.rejected;
expect(target.transform(v5, {} as ArgumentMetadata)).to.be.rejected;
target = new ParseUUIDPipe({ version: '4', exceptionFactory });
await expect(
target.transform('123a', {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
await expect(
target.transform(v3, {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
await expect(
target.transform(v5, {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
});

it('should throw an error - v5 ', async () => {
target = new ParseUUIDPipe({ version: '5' });
expect(target.transform('123a', {} as ArgumentMetadata)).to.be.rejected;
expect(target.transform(v3, {} as ArgumentMetadata)).to.be.rejected;
expect(target.transform(v4, {} as ArgumentMetadata)).to.be.rejected;
target = new ParseUUIDPipe({ version: '5', exceptionFactory });
await expect(
target.transform('123a', {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
await expect(
target.transform(v3, {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
await expect(
target.transform(v4, {} as ArgumentMetadata),
).to.be.rejectedWith(TestException);
});
});
});
Expand Down
17 changes: 0 additions & 17 deletions packages/common/utils/is-uuid.ts

This file was deleted.

0 comments on commit 28afaa2

Please sign in to comment.