diff --git a/src/app.module.ts b/src/app.module.ts index c3b81e0..dcac160 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,23 +1,7 @@ import { Module } from '@nestjs/common'; -import { PrismaModule } from './prisma/prisma.module'; -import { - AccountModule, - SessionModule, - PasswordTokenModule, - MailingModule, - TaskModule, - GoalModule, -} from './modules'; +import { AccountModule, TaskModule, GoalModule } from './modules'; @Module({ - imports: [ - PrismaModule, - AccountModule, - SessionModule, - PasswordTokenModule, - MailingModule, - TaskModule, - GoalModule, - ], + imports: [AccountModule, TaskModule, GoalModule], }) export class AppModule {} diff --git a/src/config/exceptions.ts b/src/config/exceptions.ts new file mode 100644 index 0000000..c9ff6c6 --- /dev/null +++ b/src/config/exceptions.ts @@ -0,0 +1,84 @@ +import { HttpException } from '@nestjs/common'; + +type iCustomException = { + property?: string | undefined; + message?: string | undefined; +}; + +export class CustomException extends HttpException { + constructor(message: string, status = 500, property?: string) { + super( + { + errors: [ + { + property, + message, + }, + ], + }, + status + ); + } +} + +// 400 +export class DataValidationError extends CustomException { + constructor(props: iCustomException) { + super(props.message || 'Erro em um dado', 400, props.property); + } +} + +// 401 +export class AuthorizationError extends CustomException { + constructor(props: iCustomException) { + super(props.message || 'Credenciais inválidas', 401, props.property); + } +} + +// 403 +export class PermissionError extends CustomException { + constructor(props: iCustomException) { + super( + props.message || 'Você não tem permissão para executar esta ação', + 403, + props.property + ); + } +} + +// 404 +export class NotFoundError extends CustomException { + constructor(props: iCustomException) { + super(props.message || 'Informação não encontrada', 404, props.property); + } +} + +// 422 +export class UnprocessableEntityError extends CustomException { + constructor(props: iCustomException) { + super( + props.message || 'Você não tem permissão alterar esta informação', + 422, + props.property + ); + } +} + +// 429 +export class RatelimitError extends CustomException { + constructor(props: iCustomException) { + super( + props.message || + 'Muitas requisições em um curto período, aguarde e tente novamente mais tarde', + 429, + props.property + ); + } +} + +// 500 +export class InternalServerError extends CustomException { + constructor(props: iCustomException) { + super(props.message || 'Erro desconhecido', 500); + } +} diff --git a/src/config/responses.ts b/src/config/responses.ts new file mode 100644 index 0000000..c3251b4 --- /dev/null +++ b/src/config/responses.ts @@ -0,0 +1,44 @@ +import type { iValidationResponses } from './types'; + +export const responses: iValidationResponses = { + notEmpty: 'Não pode estar vazio', + boolean: 'Deve ser verdadeiro ou falso (true | false)', + string: 'Deve ser um texto', + arrayOfString: 'Deve ser uma lista de textos', + number: 'Deve ser um número', + integer: 'Deve ser um número inteiro', + enum(validationArguments) { + const enums = validationArguments.constraints[1]; + return `Deve ser um texto entre essas opções: ${enums}`; + }, + arrayMinSize(validationArguments) { + const min = validationArguments.constraints[0]; + return `Deve ter no mínimo ${min} item(ns)`; + }, + arrayMaxSize(validationArguments) { + const max = validationArguments.constraints[0]; + return `Deve ter no máximo ${max} item(ns)`; + }, + minLength(validationArguments) { + const min = validationArguments.constraints[0]; + return `Deve ter no mínimo ${min} caracteres`; + }, + maxLength(validationArguments) { + const max = validationArguments.constraints[0]; + return `Deve ter no máximo ${max} caracteres`; + }, + minValue(validationArguments) { + const min = validationArguments.constraints[0]; + return `O valor mínimo deve ser ${min}`; + }, + maxValue(validationArguments) { + const max = validationArguments.constraints[0]; + return `O valor máximo deve ser ${max}`; + }, + email: 'O e-mail deve ter um formato válido', + strongPassword: + 'Deve conter no mínimo 6 caracteres com uma letra maiúscula, uma letra minúscula, um número e um símbolo', + fullname: 'Deve conter apenas letras e espaço em branco entre palavras', + datePattern: 'Deve ser um texto nesse padrão, YYYY-MM-DD', + dateRange: 'O mês e o dia devem ser válidos', +}; diff --git a/src/config/swagger.ts b/src/config/swagger.ts index 36ccee7..12d7052 100644 --- a/src/config/swagger.ts +++ b/src/config/swagger.ts @@ -10,6 +10,6 @@ export const swaggerDocumentConfig = new DocumentBuilder() ) .addBearerAuth({ type: 'http', - description: 'Get the `token` property after logging in', + description: 'Use o `token` adquirido ao acessar a conta', }) .build(); diff --git a/src/config/types.d.ts b/src/config/types.d.ts new file mode 100644 index 0000000..47e8a8e --- /dev/null +++ b/src/config/types.d.ts @@ -0,0 +1,24 @@ +import { ValidationOptions } from 'class-validator'; + +type iValidator = ValidationOptions['message'] | string; + +export type iValidationResponses = { + notEmpty: iValidator; + boolean: iValidator; + string: iValidator; + arrayOfString: iValidator; + number: iValidator; + integer: iValidator; + enum: iValidator; + arrayMinSize: iValidator; + arrayMaxSize: iValidator; + minLength: iValidator; + maxLength: iValidator; + minValue: iValidator; + maxValue: iValidator; + email: iValidator; + strongPassword: iValidator; + fullname: iValidator; + datePattern: iValidator; + dateRange: iValidator; +}; diff --git a/src/config/validation-pipe.ts b/src/config/validation-pipe.ts new file mode 100644 index 0000000..cc5f8a9 --- /dev/null +++ b/src/config/validation-pipe.ts @@ -0,0 +1,18 @@ +import { + BadRequestException, + ValidationPipe, + ValidationPipeOptions, +} from '@nestjs/common'; + +const config: ValidationPipeOptions = { + exceptionFactory(errors) { + const formated = errors.map((error) => ({ + property: error.property, + message: error.constraints[Object.keys(error.constraints)[0]], + })); + + return new BadRequestException({ errors: formated }); + }, +}; + +export default new ValidationPipe(config); diff --git a/src/guards/roles.guard.ts b/src/guards/roles.guard.ts index 2a6cec4..a215ea9 100644 --- a/src/guards/roles.guard.ts +++ b/src/guards/roles.guard.ts @@ -60,7 +60,9 @@ export class RolesGuard implements CanActivate { const firstRequiredRole = requiredRoles?.length && requiredRoles[0]; if (!firstRequiredRole) { - throw new InternalServerErrorException('unspecified permissions'); + throw new InternalServerErrorException( + 'As permissões necessárias não foram definidas' + ); } const isResetPasswordRequest = firstRequiredRole === Permissions['001']; @@ -70,13 +72,13 @@ export class RolesGuard implements CanActivate { } if (token && !this.isHexString(token)) { - throw new BadRequestException('Access token has a type error'); + throw new BadRequestException('O formato do token não está correto'); } const isRefreshTokenRequest = firstRequiredRole === Permissions['000']; if (isRefreshTokenRequest && !token) { - throw new BadRequestException('Original access token is required'); + throw new BadRequestException('O token de acesso é necessário'); } if (isRefreshTokenRequest) { @@ -97,7 +99,7 @@ export class RolesGuard implements CanActivate { if (isInvalid) { throw new ForbiddenException( - 'You do not have permission to perform this action.' + 'Você não tem permissão para realizar esta ação.' ); } diff --git a/src/main.ts b/src/main.ts index 390c888..a5fc218 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,8 +1,8 @@ import { NestFactory } from '@nestjs/core'; -import { ValidationPipe } from '@nestjs/common'; import { SwaggerModule } from '@nestjs/swagger'; import helmet from 'helmet'; +import ValidationPipe from './config/validation-pipe'; import { corsOptionsConfig } from './config/cors'; import { swaggerDocumentConfig } from './config/swagger'; import { AppModule } from './app.module'; @@ -12,7 +12,7 @@ async function bootstrap() { app.use(helmet()); app.enableCors(corsOptionsConfig); - app.useGlobalPipes(new ValidationPipe()); + app.useGlobalPipes(ValidationPipe); const document = SwaggerModule.createDocument(app, swaggerDocumentConfig); SwaggerModule.setup('', app, document); diff --git a/src/modules/Account/account.dtos.ts b/src/modules/Account/account.dtos.ts index 6e60bb8..5193a6f 100644 --- a/src/modules/Account/account.dtos.ts +++ b/src/modules/Account/account.dtos.ts @@ -6,8 +6,10 @@ import { IsStrongPassword, IsBoolean, IsHexadecimal, + IsString, } from 'class-validator'; import { IsEqualTo } from 'src/utils/decorators/isEqualTo'; +import { responses } from 'src/config/responses'; class AccountBaseDto { id: string; @@ -20,26 +22,29 @@ class AccountBaseDto { updatedAt: Date; } -// Create +const nameRegex = /^[a-zA-ZÀ-ÿ ]+$/; + export class CreateAccountControllerInput { - @IsNotEmpty() - @Matches(/^[a-zA-ZÀ-ÿ ]+$/) - @ApiProperty() + @ApiProperty({ example: 'John Doe' }) + @IsNotEmpty({ message: responses.notEmpty }) + @IsString({ message: responses.string }) + @Matches(nameRegex, { message: responses.fullname }) name: string; - @IsNotEmpty() - @IsEmail() - @ApiProperty() + @ApiProperty({ example: 'example@string.com' }) + @IsNotEmpty({ message: responses.notEmpty }) + @IsString({ message: responses.string }) + @IsEmail({}, { message: responses.email }) email: string; - @IsNotEmpty() - @IsStrongPassword() - @ApiProperty() + @ApiProperty({ example: 'strW#3' }) + @IsNotEmpty({ message: responses.notEmpty }) + @IsStrongPassword({ minLength: 6 }, { message: responses.strongPassword }) password: string; - @IsNotEmpty() - @IsBoolean() @ApiProperty() + @IsNotEmpty({ message: responses.notEmpty }) + @IsBoolean({ message: responses.boolean }) acceptedTerms?: boolean; } @@ -98,6 +103,7 @@ export class RefreshSessionControllerInput { // password export class ResetPasswordInput { + @ApiProperty() @IsNotEmpty() @IsEmail() email: string; @@ -108,18 +114,22 @@ export class ResetPasswordOutput { } export class ChangePasswordInput { + @ApiProperty() @IsNotEmpty() @IsStrongPassword() password: string; + @ApiProperty() @IsNotEmpty() @IsStrongPassword() @IsEqualTo('password') repeatPassword: string; + @ApiProperty() @IsNotEmpty() code: string; + @ApiProperty() @IsNotEmpty() accountId: string; } diff --git a/src/modules/Account/account.errors.ts b/src/modules/Account/account.errors.ts index 93402fe..0cd05bd 100644 --- a/src/modules/Account/account.errors.ts +++ b/src/modules/Account/account.errors.ts @@ -1,13 +1,13 @@ export class AccountNotFoundError extends Error { constructor() { - super('Account does not exists'); + super('A conta não existe'); this.name = 'AccountNotFoundError'; } } export class InvalidCodeError extends Error { constructor() { - super('Invalid code'); + super('Código inválido'); this.name = 'InvalidCodeError'; } } diff --git a/src/modules/Account/account.service.spec.ts b/src/modules/Account/account.service.spec.ts index e902ac7..39f96fb 100644 --- a/src/modules/Account/account.service.spec.ts +++ b/src/modules/Account/account.service.spec.ts @@ -10,11 +10,14 @@ import { createAccountInput, resetPasswordInput, } from './tests/stubs/account.stubs'; -import { AccountNotFoundError, InvalidCodeError } from './account.errors'; import { PasswordTokenService } from '../PasswordToken/passwordToken.service'; import { faker } from '@faker-js/faker'; import { MailingService } from '../Mailing/mailing.service'; -import { SendEmailError } from '../Mailing/mailing.errors'; +import { + AuthorizationError, + InternalServerError, + NotFoundError, +} from 'src/config/exceptions'; describe('AccountService Unit Tests', () => { let service: AccountService; @@ -207,7 +210,7 @@ describe('AccountService Unit Tests', () => { const promise = service.resetPassword(resetPasswordInput); - await expect(promise).rejects.toThrow(new AccountNotFoundError()); + await expect(promise).rejects.toThrow(new NotFoundError({})); }); it('should call AccountRepository.find with correct params', async () => { @@ -246,7 +249,7 @@ describe('AccountService Unit Tests', () => { expect(mailingServiceSpy).toHaveBeenCalledWith({ from: process.env.FROM_EMAIL, to: resetPasswordInput.email, - subject: 'Reset Password - Routinely', + subject: 'Alterar senha Routinely', payload: { code: '123789', name: accountStub.name }, template: resetPasswordTemplatePath, }); @@ -255,12 +258,16 @@ describe('AccountService Unit Tests', () => { it('should throw error if MailingService.sendEmail throws', async () => { accountRepositoryMock.alreadyExists.mockResolvedValue(true); jest.spyOn(mailingServiceMock, 'sendEmail').mockImplementation(() => { - throw new SendEmailError(); + throw new InternalServerError({ + message: 'Erro ao tentar enviar e-mail', + }); }); const promise = service.resetPassword(resetPasswordInput); - await expect(promise).rejects.toThrow(new SendEmailError()); + await expect(promise).rejects.toThrow( + new InternalServerError({ message: 'Erro ao tentar enviar e-mail' }) + ); }); it.todo('check if user already has token'); @@ -302,7 +309,11 @@ describe('AccountService Unit Tests', () => { const promise = service.changePassword(changePasswordInput); - await expect(promise).rejects.toThrow(new InvalidCodeError()); + await expect(promise).rejects.toThrow( + new AuthorizationError({ + message: 'Código inválido', + }) + ); }); it('calls AccountRepository.changePassword with correct data', async () => { diff --git a/src/modules/Account/account.service.ts b/src/modules/Account/account.service.ts index 5113233..72ea824 100644 --- a/src/modules/Account/account.service.ts +++ b/src/modules/Account/account.service.ts @@ -1,10 +1,5 @@ import { hash, compare } from 'bcrypt'; -import { - Injectable, - BadRequestException, - UnprocessableEntityException, - UnauthorizedException, -} from '@nestjs/common/'; +import { Injectable } from '@nestjs/common/'; import { AccessAccountControllerInput, CreateAccountServiceOutput, @@ -15,12 +10,17 @@ import { ResetPasswordInput, } from './account.dtos'; import { AccountRepository } from './account.repository'; -import { AccountNotFoundError, InvalidCodeError } from './account.errors'; import { hashDataAsync } from 'src/utils/hashes'; import { RoleLevel } from 'src/guards'; import { PasswordTokenService } from '../PasswordToken/passwordToken.service'; import { MailingService } from '../Mailing/mailing.service'; -import { SendEmailError } from '../Mailing/mailing.errors'; +import { + AuthorizationError, + DataValidationError, + InternalServerError, + NotFoundError, + UnprocessableEntityError, +} from 'src/config/exceptions'; @Injectable() export class AccountService { @@ -45,7 +45,10 @@ export class AccountService { createAccountInput: CreateAccountControllerInput ): Promise { if (createAccountInput.acceptedTerms !== true) { - throw new BadRequestException('Please accept our privacy policies'); + throw new DataValidationError({ + message: 'Por favor, aceite nossos termos de uso', + property: 'acceptedTerms', + }); } const hashedEmail = await hashDataAsync( @@ -54,7 +57,9 @@ export class AccountService { ); if (!hashedEmail) { - throw new UnprocessableEntityException('Unknown error'); + throw new UnprocessableEntityError({ + message: 'Erro desconhecido', + }); } const alreadyExists = await this.accountRepository.alreadyExists( @@ -62,7 +67,10 @@ export class AccountService { ); if (alreadyExists) { - throw new UnprocessableEntityException('This e-mail already exists'); + throw new UnprocessableEntityError({ + property: 'email', + message: 'O e-mail já existe na base de dados', + }); } const hashedPassword = await this.hashPassword(createAccountInput.password); @@ -75,7 +83,7 @@ export class AccountService { if (created) { return { - message: 'Account created!', + message: 'Conta criada!', }; } @@ -93,7 +101,8 @@ export class AccountService { const accountExists = await this.accountRepository.alreadyExists( hashedEmail ); - if (!accountExists) throw new AccountNotFoundError(); + if (!accountExists) + throw new NotFoundError({ message: 'Conta não encontrada' }); const account = await this.accountRepository.findAccountByEmail( hashedEmail ); @@ -106,13 +115,13 @@ export class AccountService { await this.mailingService.sendEmail({ from: process.env.FROM_EMAIL, to: resetPasswordInput.email, - subject: 'Reset Password - Routinely', + subject: 'Alterar senha Routinely', payload: { name: account.name, code: createdCode.code }, template: 'resetPassword.handlebars', }); return { accountId: account.id }; } catch (e) { - throw new SendEmailError(); + throw new InternalServerError({}); } } @@ -133,7 +142,9 @@ export class AccountService { }); await this.tokenService.deleteToken(changePasswordInput.accountId); - } else throw new InvalidCodeError(); + } else { + throw new AuthorizationError({ message: 'Código inválido' }); + } return; } @@ -148,7 +159,7 @@ export class AccountService { await this.accountRepository.findAccountByEmail(hashedEmail); if (!credentialFromDatabase) { - throw new UnauthorizedException('Invalid credentials. Please try again.'); + throw new AuthorizationError({}); } const validatePass = await this.comparePassword( @@ -157,7 +168,7 @@ export class AccountService { ); if (!validatePass) { - throw new UnauthorizedException('Invalid credentials. Please try again.'); + throw new AuthorizationError({}); } return { diff --git a/src/modules/Mailing/mailing.errors.ts b/src/modules/Mailing/mailing.errors.ts deleted file mode 100644 index 5bc9aef..0000000 --- a/src/modules/Mailing/mailing.errors.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class SendEmailError extends Error { - constructor() { - super('Nodemailer failed at sending email'); - this.name = 'SendEmailError'; - } -} diff --git a/src/modules/Mailing/mailing.service.spec.ts b/src/modules/Mailing/mailing.service.spec.ts index 72a71fd..2eb407f 100644 --- a/src/modules/Mailing/mailing.service.spec.ts +++ b/src/modules/Mailing/mailing.service.spec.ts @@ -4,8 +4,8 @@ import * as nodemailer from 'nodemailer'; import * as handlebars from 'handlebars'; import { CreateEmailInput } from './mailing.dtos'; import { faker } from '@faker-js/faker'; -import { SendEmailError } from './mailing.errors'; import * as fs from 'fs'; +import { InternalServerError } from 'src/config/exceptions'; describe('MailingService Unit Tests', () => { let service: MailingService; @@ -104,7 +104,7 @@ describe('MailingService Unit Tests', () => { const promise = service.sendEmail(createEmailInput); - await expect(promise).rejects.toThrow(new SendEmailError()); + await expect(promise).rejects.toThrow(new InternalServerError({})); }); }); }); diff --git a/src/modules/Mailing/mailing.service.ts b/src/modules/Mailing/mailing.service.ts index a43263e..f681d62 100644 --- a/src/modules/Mailing/mailing.service.ts +++ b/src/modules/Mailing/mailing.service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@nestjs/common'; import * as nodemailer from 'nodemailer'; import { CreateEmailInput } from './mailing.dtos'; -import { SendEmailError } from './mailing.errors'; import { readFileSync } from 'fs'; import { join } from 'path'; import * as handlebars from 'handlebars'; +import { InternalServerError } from 'src/config/exceptions'; @Injectable() export class MailingService { @@ -36,8 +36,7 @@ export class MailingService { try { await transporter.sendMail(emailData); } catch (e) { - console.log(e, 'mailing service'); - throw new SendEmailError(); + throw new InternalServerError({}); } } } diff --git a/src/modules/Session/session.service.spec.ts b/src/modules/Session/session.service.spec.ts index d92d43f..22fef1f 100644 --- a/src/modules/Session/session.service.spec.ts +++ b/src/modules/Session/session.service.spec.ts @@ -4,10 +4,7 @@ import { SessionRepository } from './session.repository'; import * as constants from 'src/utils/constants'; import * as stubs from './tests/session.stubs'; -import { - InternalServerErrorException, - UnauthorizedException, -} from '@nestjs/common'; +import { AuthorizationError, InternalServerError } from 'src/config/exceptions'; describe('SessionService unit test', () => { let service: SessionService; @@ -71,7 +68,7 @@ describe('SessionService unit test', () => { try { await service.createSession(stubs.createInput); } catch (actual) { - expect(actual).toBeInstanceOf(InternalServerErrorException); + expect(actual).toBeInstanceOf(InternalServerError); } }); }); @@ -93,8 +90,7 @@ describe('SessionService unit test', () => { try { await service.findSessionToken(stubs.findByTokenInput.token); } catch (actual) { - expect(actual.message).toEqual(stubs.expectedMessages.sessionExpired); - expect(actual).toBeInstanceOf(UnauthorizedException); + expect(actual).toBeInstanceOf(AuthorizationError); } }); }); @@ -123,8 +119,7 @@ describe('SessionService unit test', () => { try { await service.findExpiredSessionByTokenAndRefreshToken('', ''); } catch (actual) { - expect(actual.message).toEqual(stubs.expectedMessages.expiredOrDeleted); - expect(actual).toBeInstanceOf(UnauthorizedException); + expect(actual).toBeInstanceOf(AuthorizationError); } }); @@ -135,10 +130,7 @@ describe('SessionService unit test', () => { 'invalidtoken' ); } catch (actual) { - expect(actual.message).toEqual( - stubs.expectedMessages.invalidCredentials - ); - expect(actual).toBeInstanceOf(UnauthorizedException); + expect(actual).toBeInstanceOf(AuthorizationError); } }); }); diff --git a/src/modules/Session/session.service.ts b/src/modules/Session/session.service.ts index 815a0cc..425a8e5 100644 --- a/src/modules/Session/session.service.ts +++ b/src/modules/Session/session.service.ts @@ -1,9 +1,5 @@ import { randomBytes } from 'node:crypto'; -import { - Injectable, - InternalServerErrorException, - UnauthorizedException, -} from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { SessionRepository } from './session.repository'; import { CreateSessionServiceInput, @@ -13,6 +9,7 @@ import { RefreshTokenServiceOutput, } from './session.dtos'; import { hashDataAsync } from 'src/utils/hashes'; +import { AuthorizationError, InternalServerError } from 'src/config/exceptions'; @Injectable() export class SessionService { @@ -32,7 +29,9 @@ export class SessionService { hashedToken = await hashDataAsync(token, process.env.SALT_SESSION); if (!hashedToken) { - throw new InternalServerErrorException('Error on try create session'); + throw new InternalServerError({ + message: 'Erro ao tentar criar a sessão', + }); } } @@ -83,7 +82,7 @@ export class SessionService { ); if (!sessionSaved) { - throw new InternalServerErrorException(); + throw new InternalServerError({}); } return { @@ -103,7 +102,9 @@ export class SessionService { }); if (!sessionOutput) { - throw new UnauthorizedException('Session expired'); + throw new AuthorizationError({ + message: 'Sessão expirada', + }); } return sessionOutput; @@ -119,9 +120,9 @@ export class SessionService { }); if (!expiredSession) { - throw new UnauthorizedException( - 'This session has expired or does not exist' - ); + throw new AuthorizationError({ + message: 'Essa sessão está expirada ou foi finalizada', + }); } const hashedRefreshToken = await hashDataAsync( @@ -130,11 +131,13 @@ export class SessionService { ); if (!hashedRefreshToken) { - throw new InternalServerErrorException('Error on try verify session'); + throw new InternalServerError({ + message: 'Erro ao tentar verificar a sessão', + }); } if (hashedRefreshToken !== expiredSession.refreshToken) { - throw new UnauthorizedException('Invalid credentials'); + throw new AuthorizationError({}); } const randomSessionToken = await this.randomToken(); @@ -163,10 +166,10 @@ export class SessionService { async closeSession(closeSessionInput: ExcludeSessionsServiceInput) { if (closeSessionInput.closeAllSessions) { await this.sessionRepository.excludeAllSessions(closeSessionInput); - return { message: 'Closed sessions' }; + return { message: 'Sessões finalizadas' }; } await this.sessionRepository.excludeSession(closeSessionInput); - return { message: 'Session closed' }; + return { message: 'Sessão finalizada' }; } } diff --git a/src/modules/Session/tests/session.stubs.ts b/src/modules/Session/tests/session.stubs.ts index 79ea2d9..f275f15 100644 --- a/src/modules/Session/tests/session.stubs.ts +++ b/src/modules/Session/tests/session.stubs.ts @@ -17,11 +17,11 @@ const remember = faker.datatype.boolean(); const username = faker.person.fullName(); export const expectedMessages = { - sessionExpired: 'Session expired', - invalidCredentials: 'Invalid credentials', - expiredOrDeleted: 'This session has expired or does not exist', - manySessionClosed: 'Closed sessions', - aClosedSession: 'Session closed', + sessionExpired: 'Sessão expirada', + invalidCredentials: 'Credenciais inválidas', + expiredOrDeleted: 'Essa sessão está expirada ou foi finalizada', + manySessionClosed: 'Sessões finalizadas', + aClosedSession: 'Sessão finalizada', }; export const createInput: CreateSessionServiceInput = { diff --git a/src/modules/Task/task.service.ts b/src/modules/Task/task.service.ts index e4d74fb..1bcd1e4 100644 --- a/src/modules/Task/task.service.ts +++ b/src/modules/Task/task.service.ts @@ -1,4 +1,4 @@ -import { Injectable, UnprocessableEntityException } from '@nestjs/common'; +import { Injectable } from '@nestjs/common'; import { CreateTaskInput, FindTasksRepositoryInput, @@ -6,6 +6,7 @@ import { UpdateTaskInput, } from './task.dtos'; import { TaskRepository } from './task.repository'; +import { UnprocessableEntityError } from 'src/config/exceptions'; @Injectable() export class TaskService { @@ -41,7 +42,7 @@ export class TaskService { async updateById(id: string, updateTaskInput: UpdateTaskInput) { const taskExist = await this.repository.findById(id); - if (taskExist === null) throw new UnprocessableEntityException(); + if (taskExist === null) throw new UnprocessableEntityError({}); const date = new Date(updateTaskInput.date); const now = new Date();