improvement: error handeling structure changed and unified all across the app. user no longer sees inappropriate messages.
This commit is contained in:
73
backend/src/common/errors/validation-exception.factory.ts
Normal file
73
backend/src/common/errors/validation-exception.factory.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { BadRequestException, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import type { ValidationError } from 'class-validator';
|
||||
import { AppException } from './app.exception';
|
||||
import { ErrorCode, type ErrorCodeValue, type ValidationErrorDetail } from './error-codes';
|
||||
|
||||
const KNOWN_VALIDATION_CODES = new Set<string>(Object.values(ErrorCode));
|
||||
|
||||
function isKnownValidationCode(value: string): value is ErrorCodeValue {
|
||||
return KNOWN_VALIDATION_CODES.has(value);
|
||||
}
|
||||
|
||||
function constraintToCode(constraintKey: string, message: string): ErrorCodeValue {
|
||||
if (isKnownValidationCode(message)) {
|
||||
return message;
|
||||
}
|
||||
|
||||
switch (constraintKey) {
|
||||
case 'isEmail':
|
||||
return ErrorCode.VALIDATION_EMAIL_INVALID;
|
||||
case 'minLength':
|
||||
return ErrorCode.VALIDATION_PASSWORD_TOO_SHORT;
|
||||
case 'isEnum':
|
||||
return ErrorCode.VALIDATION_ORGANIZATION_TYPE_INVALID;
|
||||
case 'matches':
|
||||
return ErrorCode.VALIDATION_MOBILE_INVALID;
|
||||
case 'isIn':
|
||||
return ErrorCode.VALIDATION_LANGUAGE_INVALID;
|
||||
case 'whitelistValidation':
|
||||
return ErrorCode.VALIDATION_INVALID_REQUEST;
|
||||
default:
|
||||
return ErrorCode.VALIDATION_FIELD_REQUIRED;
|
||||
}
|
||||
}
|
||||
|
||||
function flattenValidationErrors(
|
||||
errors: ValidationError[],
|
||||
parentPath = '',
|
||||
): ValidationErrorDetail[] {
|
||||
const details: ValidationErrorDetail[] = [];
|
||||
|
||||
for (const error of errors) {
|
||||
const field = parentPath ? `${parentPath}.${error.property}` : error.property;
|
||||
|
||||
if (error.constraints) {
|
||||
const [constraintKey, message] = Object.entries(error.constraints)[0];
|
||||
details.push({
|
||||
field,
|
||||
code: constraintToCode(constraintKey, message),
|
||||
});
|
||||
}
|
||||
|
||||
if (error.children?.length) {
|
||||
details.push(...flattenValidationErrors(error.children, field));
|
||||
}
|
||||
}
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
export function validationExceptionFactory(errors: ValidationError[]): HttpException {
|
||||
const details = flattenValidationErrors(errors);
|
||||
|
||||
if (details.length === 0) {
|
||||
return new AppException(ErrorCode.VALIDATION_FAILED, HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
return new AppException(ErrorCode.VALIDATION_FAILED, HttpStatus.BAD_REQUEST, details);
|
||||
}
|
||||
|
||||
/** Handles forbidNonWhitelisted errors from ValidationPipe. */
|
||||
export function isValidationPipeBadRequest(exception: unknown): exception is BadRequestException {
|
||||
return exception instanceof BadRequestException;
|
||||
}
|
||||
Reference in New Issue
Block a user