Skip to content

Commit

Permalink
Merge pull request #91 from mapeveri/feat/90
Browse files Browse the repository at this point in the history
feat: improve error handler
  • Loading branch information
mapeveri authored Jan 28, 2024
2 parents 44ee536 + fd095ab commit 5ee1bdd
Show file tree
Hide file tree
Showing 19 changed files with 95 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -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');
}
}
Original file line number Diff line number Diff line change
@@ -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');
}
}
Original file line number Diff line number Diff line change
@@ -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');
}
}
6 changes: 3 additions & 3 deletions src/languages/domain/user/exceptions/loginException.ts
Original file line number Diff line number Diff line change
@@ -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');
}
}
Original file line number Diff line number Diff line change
@@ -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} does not exists`, 'user_does_not_exists');
}
}
Original file line number Diff line number Diff line change
@@ -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');
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Body, Controller, HttpCode, HttpException, HttpStatus, Inject, Post } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import LoginPostResponseDto from './loginPostResponseDto';
import { ApiBadRequestResponse, ApiInternalServerErrorResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import {
ApiBadRequestResponse,
ApiForbiddenResponse,
ApiInternalServerErrorResponse,
ApiOkResponse,
ApiTags,
} from '@nestjs/swagger';
import RefreshTokenPostDto from './refreshTokenPostDto';
import RefreshTokenPostResponseDto from './refreshTokenPostResponseDto';
import { QUERY_BUS, QueryBus } from '@src/shared/domain/bus/queryBus/queryBus';
Expand All @@ -15,12 +21,20 @@ export default class RefreshTokenPostController {
@Post('auth/refresh-token')
@HttpCode(200)
@ApiOkResponse({ type: LoginPostResponseDto })
@ApiForbiddenResponse({ description: 'Forbidden.' })
@ApiBadRequestResponse({ description: 'Bad Request.' })
@ApiInternalServerErrorResponse({ description: 'Internal Server Error.' })
async run(@Body() payload: RefreshTokenPostDto): Promise<RefreshTokenPostResponseDto> {
const decodedRefreshToken = this.jwtService.verify(payload.refreshToken);
let decodedRefreshToken;

try {
decodedRefreshToken = this.jwtService.verify(payload.refreshToken);
} catch (e: any) {
throw new HttpException(e.message, HttpStatus.FORBIDDEN);
}

if (decodedRefreshToken.revoked) {
throw new HttpException('Token revocado', HttpStatus.FORBIDDEN);
throw new HttpException('Token revoked', HttpStatus.FORBIDDEN);
}

const user = await this.queryBus.ask(new FindUserQuery(decodedRefreshToken.id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
7 changes: 7 additions & 0 deletions src/shared/domain/exceptions/conflictException.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
3 changes: 1 addition & 2 deletions src/shared/domain/exceptions/domainException.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
6 changes: 3 additions & 3 deletions src/shared/domain/exceptions/invalidArgumentException.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
6 changes: 3 additions & 3 deletions src/shared/domain/exceptions/invalidEmailException.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
7 changes: 7 additions & 0 deletions src/shared/domain/exceptions/notFoundException.ts
Original file line number Diff line number Diff line change
@@ -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);
}
}
7 changes: 7 additions & 0 deletions src/shared/domain/exceptions/unauthorizedException.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import DomainException from '@src/shared/domain/exceptions/domainException';

export default class UnauthorizedException extends DomainException {
constructor(public message: string = 'User unauthorized', public code: string = 'user_unauthorized') {
super(message, code);
}
}
2 changes: 1 addition & 1 deletion src/shared/domain/loggerInterface.ts
Original file line number Diff line number Diff line change
@@ -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;
}

Expand Down
31 changes: 25 additions & 6 deletions src/shared/infrastructure/api/filters/nestErrorFilter.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
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 {
private mappedExceptions = [
{ exceptionType: ConflictException, status: HttpStatus.CONFLICT },
{ exceptionType: NotFoundException, status: HttpStatus.NOT_FOUND },
{ exceptionType: UnauthorizedException, status: HttpStatus.UNAUTHORIZED },
{ exceptionType: DomainException, status: HttpStatus.BAD_REQUEST },
];

constructor(@Inject(LOGGER_INTERFACE) private readonly logger: LoggerInterface) {}

catch(exception: Error, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();

if (exception instanceof DomainException) {
response.status(exception.status).json(ApiExceptionSerializer.serialize(exception));
this.logger.error(`${exception.stack}`);

const matchedException = this.mappedExceptions.find((item) => exception instanceof item.exceptionType);
if (matchedException) {
const status = matchedException.status;
response.status(status).json(ApiExceptionSerializer.serialize(exception as DomainException, status));
return;
}

if (exception instanceof HttpException) {
response.status(exception.getStatus()).json(exception.getResponse());
const status = exception.getStatus();
response.status(status).json({ statusCode: status, message: exception.message });
return;
}

response.status(500).json({
statusCode: 500,
response.status(HttpStatus.INTERNAL_SERVER_ERROR).json({
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
message: 'Internal Server Error',
code: 'generic_error',
});
Expand Down
3 changes: 2 additions & 1 deletion src/shared/infrastructure/api/guards/nestJwtAuthGuard.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';
import { ConfigService } from '@nestjs/config';
import UnauthorizedException from '@src/shared/domain/exceptions/unauthorizedException';

@Injectable()
export class NestJwtAuthGuard implements CanActivate {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import DomainException from '@src/shared/domain/exceptions/domainException';

export default class ApiExceptionSerializer {
public static serialize(error: DomainException): Record<string, any> {
public static serialize(error: DomainException, status: number): Record<string, any> {
return {
statusCode: error.status || 500,
statusCode: status,
message: error.message,
code: error.code,
};
Expand Down
2 changes: 1 addition & 1 deletion src/shared/infrastructure/logger/nestLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down

0 comments on commit 5ee1bdd

Please sign in to comment.