improvement: error handeling structure changed and unified all across the app. user no longer sees inappropriate messages.

This commit is contained in:
2026-07-12 18:27:38 +03:30
parent 901d838a2c
commit fab5111aa8
45 changed files with 977 additions and 241 deletions

View 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;
}