UNPKG

@webilix/nestjs-helper

Version:

Helper library for NestJS

205 lines (159 loc) 8.45 kB
import { Injectable, PipeTransform, ArgumentMetadata, HttpException, HttpStatus } from '@nestjs/common'; import { Helper } from '@webilix/helper-library'; import { Errors } from '../errors'; import { FormatsEnum } from '../formats'; import { Condition } from './validator.type'; import { IDateCondition, INumberCondition, IStringCondition } from './validator.interface'; @Injectable() export class ValidatorPipe implements PipeTransform { private errors: string[] = []; private setError(error: string): void { this.errors.push(error); } transform(values: { [key: string]: any }, meta: ArgumentMetadata) { this.errors = []; const conditions = Reflect.getMetadata('validator', meta.metatype || {}); if (conditions) { values = this.updateValues(conditions, values); this.validate(conditions, values); } if (this.errors.length !== 0) throw new HttpException(this.errors, HttpStatus.BAD_REQUEST); return values; } private updateValues(conditions: any, values: { [key: string]: any }): { [key: string]: any } { const update = (condition: Condition, value: any): any => { if (Helper.IS.empty(value)) return value; switch (condition.type) { case 'BOOLEAN': case 'NUMBER': return value; case 'DATE': if (!Helper.IS.STRING.jsonDate(value)) return value; value = Helper.STRING.changeNumbers(value, 'EN'); if (!condition.omitConvert) value = new Date(value); return value; case 'STRING': if (!Helper.IS.string(value)) return value; if (!condition.omitTrim) value = value.trim(); if (condition.format) value = Helper.STRING.changeNumbers(value, 'EN'); else if (condition.changeNumbers) value = Helper.STRING.changeNumbers(value, condition.changeNumbers); return value; case 'OBJECT': const properties: string[] = Object.keys(condition.properties); properties.forEach((property: string) => { if (Helper.IS.empty(value[property])) return; value[property] = update(condition.properties[property], value[property]); }); return value; } }; Object.keys(conditions).forEach((key: string) => { const condition: Condition = conditions[key]; if (condition.array) { if (Helper.IS.array(values[key])) values[key].map((_: any, index: number) => update(condition, values[key][index])); } else values[key] = update(condition, values[key]); }); return values; } private validate(conditions: any, values: { [key: string]: any }) { Object.keys(conditions).forEach((key: string) => { const condition: Condition = conditions[key]; if (condition.array) this.validateArray(condition, values[key]); else this.validateValue(condition, values[key]); }); } private validateType(condition: Condition, value: any): boolean { if (Helper.IS.empty(value)) return true; switch (condition.type) { case 'BOOLEAN': return Helper.IS.boolean(value); case 'DATE': return Helper.IS.date(value) || Helper.IS.STRING.jsonDate(value); case 'NUMBER': return Helper.IS.number(value); case 'OBJECT': return Helper.IS.object(value); case 'STRING': return Helper.IS.string(value); } } private validateArray(condition: Condition, value: any[]): void { if (!condition.array) return; const title: string = condition.title; // UNDEFINED if (value === undefined) return this.setError(Errors.undefined(title)); // NULLABLE const isEmpty: boolean = Helper.IS.empty(value); if (!condition.nullable && isEmpty) return this.setError(Errors.empty(title)); if (isEmpty) return; // TYPE if (!Helper.IS.array(value)) return this.setError(Errors.invalid(title)); // COUNT && UNIQUE if (condition.array !== true) { if (condition.array.minCount && value.length < condition.array.minCount) return this.setError(Errors.minCount(title, condition.array.minCount)); if (condition.array.maxCount && value.length > condition.array.maxCount) return this.setError(Errors.maxCount(title, condition.array.maxCount)); if ( condition.array.unique && !Helper.IS.ARRAY.unique(value, condition.array.unique === true ? undefined : condition.array.unique) ) return this.setError(Errors.unique(title)); } // Change nullable option for array values condition = { ...condition, nullable: false }; value.forEach((_: any, index: number) => this.validateValue(condition, value[index], index + 1)); } private validateValue(condition: Condition, value: any, index?: number, parent?: string): void { const title: string = (parent ? `${parent}: ` : '') + condition.title + (index ? ` ${Helper.NUMBER.getTitle(index)}` : ''); // UNDEFINED if (value === undefined) return this.setError(Errors.undefined(title)); // NULLABLE const isEmpty: boolean = Helper.IS.empty(value); if (!condition.nullable && isEmpty) return this.setError(Errors.empty(title)); if (isEmpty) return; // TYPE if (!this.validateType(condition, value)) return this.setError(Errors.invalid(title)); // CONDITIONS switch (condition.type) { case 'BOOLEAN': return; case 'DATE': return this.validateDate(condition, title, new Date(value)); case 'NUMBER': return this.validateNumber(condition, title, +value); case 'STRING': return this.validateString(condition, title, value.toString()); case 'OBJECT': const properties: string[] = Object.keys(condition.properties); properties.forEach((property: string) => this.validateValue(condition.properties[property], value[property], undefined, title), ); return; } } private validateDate(condition: IDateCondition, title: string, value: Date): void { if (condition.minDate && value.getTime() < condition.minDate.getTime()) return this.setError(Errors.minDate(title, condition.minDate)); if (condition.maxDate && value.getTime() > condition.maxDate.getTime()) return this.setError(Errors.maxDate(title, condition.maxDate)); } private validateNumber(condition: INumberCondition, title: string, value: number): void { if (condition.minimum && value < condition.minimum) return this.setError(Errors.minimum(title, condition.minimum)); if (condition.maximum && value > condition.maximum) return this.setError(Errors.maximum(title, condition.maximum)); if (condition.enum && !condition.enum.includes(value)) return this.setError(Errors.invalid(title)); } private validateString(condition: IStringCondition, title: string, value: string): void { if (condition.format && !FormatsEnum[condition.format].validate(value)) return this.setError(Errors.invalid(title)); if (condition.enum && !condition.enum.includes(value)) return this.setError(Errors.invalid(title)); if (condition.length && value.length !== condition.length) return this.setError(Errors.eqLength(title, condition.length)); if (condition.minLength && value.length < condition.minLength) return this.setError(Errors.minLength(title, condition.minLength)); if (condition.maxLength && value.length > condition.maxLength) return this.setError(Errors.maxLength(title, condition.maxLength)); if (condition.pattern && !condition.pattern.test(value)) return this.setError(Errors.invalid(title)); } }