Created
March 22, 2024 19:31
-
-
Save tbhaxor/0d509fbe07fcf10e61df8a92e3f28150 to your computer and use it in GitHub Desktop.
Nestjs length validation pipe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { BadRequestException, InternalServerErrorException } from '@nestjs/common'; | |
import { ValidateLengthPipe } from './validate-length.pipe'; | |
import { faker } from '@faker-js/faker'; | |
describe('ValidateLengthPipe', () => { | |
it('should throw InternalServerErrorException on empty object', () => { | |
expect(() => new ValidateLengthPipe({})).toThrow(InternalServerErrorException); | |
}); | |
it('should throw InternalServerErrorException when min < 0', () => { | |
const number = faker.number.int({ min: -100, max: -1 }); | |
expect(() => new ValidateLengthPipe({ min: number })).toThrow(InternalServerErrorException); | |
}); | |
it('should throw InternalServerErrorException when max < min', () => { | |
const minNumber = faker.number.int({ min: 0, max: 10 }); | |
const maxNumber = faker.number.int({ min: -10, max: -1 }); | |
expect(() => new ValidateLengthPipe({ min: minNumber, max: maxNumber })).toThrow(InternalServerErrorException); | |
}); | |
it('should throw InternalServerErrorException when either value is not integer', () => { | |
expect(() => new ValidateLengthPipe({ min: faker.number.float() })).toThrow(InternalServerErrorException); | |
expect(() => new ValidateLengthPipe({ max: faker.number.float() })).toThrow(InternalServerErrorException); | |
}); | |
it('should be defined when either of field is defined', () => { | |
expect(new ValidateLengthPipe({ min: 0 })).toBeDefined(); | |
expect(new ValidateLengthPipe({ max: 0 })).toBeDefined(); | |
}); | |
it('should be defined when both length constraints are valid', () => { | |
const minNumber = faker.number.int({ min: 0 }); | |
const maxNumber = faker.number.int({ min: minNumber }); | |
expect(new ValidateLengthPipe({ min: minNumber, max: maxNumber })).toBeDefined(); | |
}); | |
it('should return isExact true if min == max', () => { | |
const minMax = faker.number.int(); | |
expect(new ValidateLengthPipe({ min: minMax, max: faker.number.int({ min: minMax }) }).isExact).toBe(false); | |
expect(new ValidateLengthPipe({ min: minMax, max: minMax }).isExact).toBe(true); | |
}); | |
it('should throw BadRequestException if length property on object is not defined', () => { | |
const validator = new ValidateLengthPipe({ min: 0 }); | |
expect(() => validator.transform(null, { type: 'body' })).toThrow(BadRequestException); | |
expect(() => validator.transform(undefined, { type: 'body' })).toThrow(BadRequestException); | |
expect(() => validator.transform({}, { type: 'body' })).toThrow(BadRequestException); | |
}); | |
it('should throw BadRequestException when length is less than min', () => { | |
const minNumber = faker.number.int({ max: 50 }); | |
const str = faker.string.sample(minNumber - 1); | |
const validator = new ValidateLengthPipe({ min: minNumber }); | |
expect(() => validator.transform(str, { type: 'body', data: 'body' })).toThrow(BadRequestException); | |
}); | |
it('should throw BadRequestException when length is greater than max', () => { | |
const maxNumber = faker.number.int({ max: 50 }); | |
const str = faker.string.sample(maxNumber + 1); | |
const validator = new ValidateLengthPipe({ max: maxNumber }); | |
expect(() => validator.transform(str, { type: 'body', data: 'body' })).toThrow(BadRequestException); | |
}); | |
it('should return unchanged value if length constraints matches', () => { | |
const minNumber = faker.number.int({ max: 50 }); | |
const maxNumber = faker.number.int({ min: minNumber, max: minNumber * 2 }); | |
const str = faker.string.sample({ min: minNumber, max: maxNumber }); | |
const validator = new ValidateLengthPipe({ min: minNumber, max: maxNumber }); | |
expect(validator.transform(str, { type: 'body' })).toBe(str); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ArgumentMetadata, BadRequestException, Injectable, InternalServerErrorException, PipeTransform } from '@nestjs/common'; | |
export type ValidateLengthPipeOptions = { | |
min?: number; | |
max?: number; | |
}; | |
@Injectable() | |
export class ValidateLengthPipe implements PipeTransform { | |
private readonly options: ValidateLengthPipeOptions; | |
constructor(options: ValidateLengthPipeOptions) { | |
this.options = options; | |
if (typeof options.max != 'number' && typeof options.min != 'number') { | |
throw new InternalServerErrorException("Both `min` and `max` can't be left optional at same time."); | |
} | |
if (typeof options.max === 'number' && !Number.isInteger(options.max)) { | |
throw new InternalServerErrorException('Maximum length must be integeral value.'); | |
} | |
if (typeof options.min === 'number' && !Number.isInteger(options.min)) { | |
throw new InternalServerErrorException('Minimum length must be integeral value.'); | |
} | |
if (options.min < 0) { | |
throw new InternalServerErrorException("Minimum value can't be less than 0."); | |
} | |
if (options.max < options.min) { | |
throw new InternalServerErrorException("Minimum value can't be greater than maximum value."); | |
} | |
} | |
transform(value: any, metadata: ArgumentMetadata) { | |
if (typeof value?.length === 'undefined') { | |
throw new BadRequestException(`${metadata.data || metadata.type} doesn't have length property.`); | |
} | |
if (this.isExact && value.length != this.options.min) { | |
throw new BadRequestException(`Length of ${metadata.data || metadata.type} should be exactly ${this.options.min}.`); | |
} | |
if (this.options.min > value.length) { | |
throw new BadRequestException(`Length of ${metadata.data || metadata.type} should greater than ${this.options.min}.`); | |
} | |
if (this.options.max < value.length) { | |
throw new BadRequestException(`Length of ${metadata.data || metadata.type} should less than ${this.options.max}.`); | |
} | |
return value; | |
} | |
get isExact() { | |
return this.options.min == this.options.max; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment