import { Optional } from '../decorators'; import { ArgumentMetadata, BadRequestException } from '../index'; import { ValidatorOptions } from '../interfaces/external/validator-options.interface'; import { PipeTransform } from '../interfaces/features/pipe-transform.interface'; import { loadPackage } from '../utils/load-package.util'; import { isNil } from '../utils/shared.utils'; import { Injectable } from './../decorators/core/component.decorator'; export interface ValidationPipeOptions extends ValidatorOptions { transform?: boolean; disableErrorMessages?: boolean; } let classValidator: any = {}; let classTransformer: any = {}; @Injectable() export class ValidationPipe implements PipeTransform { protected isTransformEnabled: boolean; protected isDetailedOutputDisabled: boolean; protected validatorOptions: ValidatorOptions; constructor(@Optional() options?: ValidationPipeOptions) { options = options || {}; const { transform, disableErrorMessages, ...validatorOptions } = options; this.isTransformEnabled = !!transform; this.validatorOptions = validatorOptions; this.isDetailedOutputDisabled = disableErrorMessages; const loadPkg = pkg => loadPackage(pkg, 'ValidationPipe'); classValidator = loadPkg('class-validator'); classTransformer = loadPkg('class-transformer'); } public async transform(value, metadata: ArgumentMetadata) { const { metatype } = metadata; if (!metatype || !this.toValidate(metadata)) { return value; } const entity = classTransformer.plainToClass( metatype, this.toEmptyIfNil(value), ); const errors = await classValidator.validate(entity, this.validatorOptions); if (errors.length > 0) { throw new BadRequestException( this.isDetailedOutputDisabled ? undefined : errors, ); } return this.isTransformEnabled ? entity : Object.keys(this.validatorOptions).length > 0 ? classTransformer.classToPlain(entity) : value; } private toValidate(metadata: ArgumentMetadata): boolean { const { metatype, type } = metadata; if (type === 'custom') { return false; } const types = [String, Boolean, Number, Array, Object]; return !types.find(t => metatype === t) && !isNil(metatype); } toEmptyIfNil(value: T): R | {} { return isNil(value) ? {} : value; } }