From bd9d563618d6b5398530a0a5a069fc8c9de879bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Peveri?= Date: Sat, 27 Jan 2024 22:28:36 +0100 Subject: [PATCH] feat: improve error handler --- .../countryAlreadyExistsException.ts | 6 ++-- .../expressionAlreadyExistsException.ts | 6 ++-- .../user/exceptions/invalidUserIdException.ts | 6 ++-- .../domain/user/exceptions/loginException.ts | 6 ++-- .../exceptions/userDoesNotExistsException.ts | 6 ++-- .../exceptions/wordAlreadyExistsException.ts | 6 ++-- .../projectionHandlerNotFoundError.ts | 2 +- .../domain/exceptions/conflictException.ts | 7 ++++ .../domain/exceptions/domainException.ts | 3 +- .../exceptions/invalidArgumentException.ts | 6 ++-- .../exceptions/invalidEmailException.ts | 6 ++-- .../domain/exceptions/notFoundException.ts | 7 ++++ .../exceptions/unauthorizedException.ts | 7 ++++ src/shared/domain/loggerInterface.ts | 2 +- .../api/filters/nestErrorFilter.ts | 32 +++++++++++++++++-- .../api/serializers/apiExceptionSerializer.ts | 4 +-- .../infrastructure/logger/nestLogger.ts | 2 +- 17 files changed, 81 insertions(+), 33 deletions(-) create mode 100644 src/shared/domain/exceptions/conflictException.ts create mode 100644 src/shared/domain/exceptions/notFoundException.ts create mode 100644 src/shared/domain/exceptions/unauthorizedException.ts diff --git a/src/languages/domain/country/exceptions/countryAlreadyExistsException.ts b/src/languages/domain/country/exceptions/countryAlreadyExistsException.ts index c7746189..e87ba54b 100644 --- a/src/languages/domain/country/exceptions/countryAlreadyExistsException.ts +++ b/src/languages/domain/country/exceptions/countryAlreadyExistsException.ts @@ -1,7 +1,7 @@ -import DomainException from '@src/shared/domain/exceptions/domainException'; +import ConflictException from '@src/shared/domain/exceptions/conflictException'; -export default class CountryAlreadyExistsException extends DomainException { +export default class CountryAlreadyExistsException extends ConflictException { constructor(countryId: string) { - super(`Country with id ${countryId} already exists`, 500, 'country_already_exists'); + super(`Country with id ${countryId} already exists`, 'country_already_exists'); } } diff --git a/src/languages/domain/expression/exceptions/expressionAlreadyExistsException.ts b/src/languages/domain/expression/exceptions/expressionAlreadyExistsException.ts index f76a7fdc..57eea33c 100644 --- a/src/languages/domain/expression/exceptions/expressionAlreadyExistsException.ts +++ b/src/languages/domain/expression/exceptions/expressionAlreadyExistsException.ts @@ -1,7 +1,7 @@ -import DomainException from '@src/shared/domain/exceptions/domainException'; +import ConflictException from '@src/shared/domain/exceptions/conflictException'; -export default class ExpressionAlreadyExistsException extends DomainException { +export default class ExpressionAlreadyExistsException extends ConflictException { constructor(expressionId: string) { - super(`Expression with id ${expressionId} already exists`, 500, 'expression_already_exists'); + super(`Expression with id ${expressionId} already exists`, 'expression_already_exists'); } } diff --git a/src/languages/domain/user/exceptions/invalidUserIdException.ts b/src/languages/domain/user/exceptions/invalidUserIdException.ts index 457902bc..77b3246a 100644 --- a/src/languages/domain/user/exceptions/invalidUserIdException.ts +++ b/src/languages/domain/user/exceptions/invalidUserIdException.ts @@ -1,7 +1,7 @@ -import DomainException from '@src/shared/domain/exceptions/domainException'; +import ConflictException from '@src/shared/domain/exceptions/conflictException'; -export default class InvalidUserIdException extends DomainException { +export default class InvalidUserIdException extends ConflictException { constructor(userId: string) { - super(`Invalid user id ${userId}`, 400, 'invalid_user_id'); + super(`Invalid user id ${userId}`, 'invalid_user_id'); } } diff --git a/src/languages/domain/user/exceptions/loginException.ts b/src/languages/domain/user/exceptions/loginException.ts index e00ef8e7..3e8a0d1f 100644 --- a/src/languages/domain/user/exceptions/loginException.ts +++ b/src/languages/domain/user/exceptions/loginException.ts @@ -1,7 +1,7 @@ -import DomainException from '@src/shared/domain/exceptions/domainException'; +import UnauthorizedException from '@src/shared/domain/exceptions/unauthorizedException'; -export default class LoginException extends DomainException { +export default class LoginException extends UnauthorizedException { constructor() { - super('Invalid login', 403, 'invalid_login'); + super('Invalid login', 'invalid_login'); } } diff --git a/src/languages/domain/user/exceptions/userDoesNotExistsException.ts b/src/languages/domain/user/exceptions/userDoesNotExistsException.ts index c145f1fe..1d1cadaf 100644 --- a/src/languages/domain/user/exceptions/userDoesNotExistsException.ts +++ b/src/languages/domain/user/exceptions/userDoesNotExistsException.ts @@ -1,7 +1,7 @@ -import DomainException from '@src/shared/domain/exceptions/domainException'; +import NotFoundException from '@src/shared/domain/exceptions/notFoundException'; -export default class UserDoesNotExistsException extends DomainException { +export default class UserDoesNotExistsException extends NotFoundException { constructor(userId: string) { - super(`User ${userId} doesn not exists`, 404, 'user_does_not_exists'); + super(`User ${userId} doesn not exists`, 'user_does_not_exists'); } } diff --git a/src/languages/domain/word/exceptions/wordAlreadyExistsException.ts b/src/languages/domain/word/exceptions/wordAlreadyExistsException.ts index b3face9d..74864da4 100644 --- a/src/languages/domain/word/exceptions/wordAlreadyExistsException.ts +++ b/src/languages/domain/word/exceptions/wordAlreadyExistsException.ts @@ -1,7 +1,7 @@ -import DomainException from '@src/shared/domain/exceptions/domainException'; +import ConflictException from '@src/shared/domain/exceptions/conflictException'; -export default class WordAlreadyExistsException extends DomainException { +export default class WordAlreadyExistsException extends ConflictException { constructor(wordId: string) { - super(`Word with id ${wordId} already exists`, 500, 'word_already_exists'); + super(`Word with id ${wordId} already exists`, 'word_already_exists'); } } diff --git a/src/shared/domain/bus/projectionBus/projectionHandlerNotFoundError.ts b/src/shared/domain/bus/projectionBus/projectionHandlerNotFoundError.ts index 87d6c962..f8992da1 100644 --- a/src/shared/domain/bus/projectionBus/projectionHandlerNotFoundError.ts +++ b/src/shared/domain/bus/projectionBus/projectionHandlerNotFoundError.ts @@ -5,6 +5,6 @@ export default class ProjectionHandlerNotFoundError extends DomainException { public message: string = 'Projection handler not found', public code: string = 'project_handler_not_found', ) { - super(message, 500, code); + super(message, code); } } diff --git a/src/shared/domain/exceptions/conflictException.ts b/src/shared/domain/exceptions/conflictException.ts new file mode 100644 index 00000000..19cafb14 --- /dev/null +++ b/src/shared/domain/exceptions/conflictException.ts @@ -0,0 +1,7 @@ +import DomainException from '@src/shared/domain/exceptions/domainException'; + +export default class ConflictException extends DomainException { + constructor(public message: string, public code: string) { + super(message, code); + } +} diff --git a/src/shared/domain/exceptions/domainException.ts b/src/shared/domain/exceptions/domainException.ts index c6e2f5a1..092d4926 100644 --- a/src/shared/domain/exceptions/domainException.ts +++ b/src/shared/domain/exceptions/domainException.ts @@ -1,7 +1,6 @@ export default class DomainException extends Error { - constructor(public message: string, public status: number, public code: string) { + constructor(public message: string, public code: string) { super(message); - this.status = status; this.code = code; } } diff --git a/src/shared/domain/exceptions/invalidArgumentException.ts b/src/shared/domain/exceptions/invalidArgumentException.ts index ea042ed9..0d4bac2c 100644 --- a/src/shared/domain/exceptions/invalidArgumentException.ts +++ b/src/shared/domain/exceptions/invalidArgumentException.ts @@ -1,7 +1,7 @@ -import DomainException from './domainException'; +import ConflictException from '@src/shared/domain/exceptions/conflictException'; -export default class InvalidArgumentException extends DomainException { +export default class InvalidArgumentException extends ConflictException { constructor(public message: string = 'Invalid argument', public code: string = 'invalid_argument') { - super(message, 500, code); + super(message, code); } } diff --git a/src/shared/domain/exceptions/invalidEmailException.ts b/src/shared/domain/exceptions/invalidEmailException.ts index 7823a7a8..f291b157 100644 --- a/src/shared/domain/exceptions/invalidEmailException.ts +++ b/src/shared/domain/exceptions/invalidEmailException.ts @@ -1,7 +1,7 @@ -import DomainException from './domainException'; +import { ConflictException } from '@nestjs/common'; -export default class InvalidEmailException extends DomainException { +export default class InvalidEmailException extends ConflictException { constructor(public message: string = 'Invalid email', public code: string = 'invalid_email') { - super(message, 500, code); + super(message, code); } } diff --git a/src/shared/domain/exceptions/notFoundException.ts b/src/shared/domain/exceptions/notFoundException.ts new file mode 100644 index 00000000..34e768cd --- /dev/null +++ b/src/shared/domain/exceptions/notFoundException.ts @@ -0,0 +1,7 @@ +import DomainException from '@src/shared/domain/exceptions/domainException'; + +export default class NotFoundException extends DomainException { + constructor(public message: string, public code: string) { + super(message, code); + } +} diff --git a/src/shared/domain/exceptions/unauthorizedException.ts b/src/shared/domain/exceptions/unauthorizedException.ts new file mode 100644 index 00000000..ca2b5fd1 --- /dev/null +++ b/src/shared/domain/exceptions/unauthorizedException.ts @@ -0,0 +1,7 @@ +import DomainException from '@src/shared/domain/exceptions/domainException'; + +export default class UnauthorizedException extends DomainException { + constructor(public message: string, public code: string) { + super(message, code); + } +} diff --git a/src/shared/domain/loggerInterface.ts b/src/shared/domain/loggerInterface.ts index fc8f7396..9dc72fd5 100644 --- a/src/shared/domain/loggerInterface.ts +++ b/src/shared/domain/loggerInterface.ts @@ -1,6 +1,6 @@ interface LoggerInterface { log(message: string): void; - error(message: string, trace: string): void; + error(message: string, trace?: string): void; warning(message: string): void; } diff --git a/src/shared/infrastructure/api/filters/nestErrorFilter.ts b/src/shared/infrastructure/api/filters/nestErrorFilter.ts index 964afe15..ca64b7a0 100644 --- a/src/shared/infrastructure/api/filters/nestErrorFilter.ts +++ b/src/shared/infrastructure/api/filters/nestErrorFilter.ts @@ -1,16 +1,44 @@ -import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common'; +import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '@nestjs/common'; import { Response } from 'express'; import ApiExceptionSerializer from '@src/shared/infrastructure/api/serializers/apiExceptionSerializer'; import DomainException from '@src/shared/domain/exceptions/domainException'; +import ConflictException from '@src/shared/domain/exceptions/conflictException'; +import NotFoundException from '@src/shared/domain/exceptions/notFoundException'; +import UnauthorizedException from '@src/shared/domain/exceptions/unauthorizedException'; +import LoggerInterface, { LOGGER_INTERFACE } from '@src/shared/domain/loggerInterface'; +import { Inject } from '@src/shared/domain/injector/inject.decorator'; @Catch(Error) export class NestErrorFilter implements ExceptionFilter { + constructor(@Inject(LOGGER_INTERFACE) private readonly logger: LoggerInterface) {} + catch(exception: Error, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); + this.logger.error(`Exception: ${exception}`); + + if (exception instanceof ConflictException) { + response.status(HttpStatus.CONFLICT).json(ApiExceptionSerializer.serialize(exception, HttpStatus.CONFLICT)); + return; + } + + if (exception instanceof NotFoundException) { + response.status(HttpStatus.NOT_FOUND).json(ApiExceptionSerializer.serialize(exception, HttpStatus.NOT_FOUND)); + return; + } + + if (exception instanceof UnauthorizedException) { + response + .status(HttpStatus.UNAUTHORIZED) + .json(ApiExceptionSerializer.serialize(exception, HttpStatus.UNAUTHORIZED)); + return; + } + if (exception instanceof DomainException) { - response.status(exception.status).json(ApiExceptionSerializer.serialize(exception)); + response + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .json(ApiExceptionSerializer.serialize(exception, HttpStatus.INTERNAL_SERVER_ERROR)); return; } diff --git a/src/shared/infrastructure/api/serializers/apiExceptionSerializer.ts b/src/shared/infrastructure/api/serializers/apiExceptionSerializer.ts index 3220a9ef..b3418ea4 100644 --- a/src/shared/infrastructure/api/serializers/apiExceptionSerializer.ts +++ b/src/shared/infrastructure/api/serializers/apiExceptionSerializer.ts @@ -1,9 +1,9 @@ import DomainException from '@src/shared/domain/exceptions/domainException'; export default class ApiExceptionSerializer { - public static serialize(error: DomainException): Record { + public static serialize(error: DomainException, status: number): Record { return { - statusCode: error.status || 500, + statusCode: status, message: error.message, code: error.code, }; diff --git a/src/shared/infrastructure/logger/nestLogger.ts b/src/shared/infrastructure/logger/nestLogger.ts index 7a318a17..d394d228 100644 --- a/src/shared/infrastructure/logger/nestLogger.ts +++ b/src/shared/infrastructure/logger/nestLogger.ts @@ -21,7 +21,7 @@ export default class NestLogger extends NestJsLogger implements LoggerInterface }); } - error(message: string, trace: string) { + error(message: string, trace?: string) { this.logger.error(message, trace); }