From d1eb4505798392283848a937c36c8733e32a1b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hermerson=20Ara=C3=BAjo?= Date: Tue, 13 Feb 2024 17:40:32 -0300 Subject: [PATCH] refactor: add expired_at in the confirmation_code --- script.sql | 7 +++-- src/application/middlewares/error_handler.ts | 2 +- src/b3_stock_alerts/AuthService.ts | 3 ++ src/b3_stock_alerts/AuthService.unit.test.ts | 3 +- src/b3_stock_alerts/EmailGateway.ts | 4 +-- src/b3_stock_alerts/EmailGateway.unit.test.ts | 6 ++-- src/b3_stock_alerts/PgUserRepository.ts | 25 +++++++++++----- .../PgUserRepository.unit.test.ts | 30 ++++++++++++------- src/b3_stock_alerts/UserConfirmationCode.ts | 1 + src/b3_stock_alerts/UserRepository.ts | 3 +- 10 files changed, 55 insertions(+), 29 deletions(-) diff --git a/script.sql b/script.sql index cd76c9e..19574f5 100644 --- a/script.sql +++ b/script.sql @@ -20,7 +20,8 @@ CREATE TABLE alerts ( CREATE TABLE user_confirmation_codes ( id VARCHAR(36) NOT NULL, user_id VARCHAR(36) NOT NULL, - code VARCHAR(4) NOT NULL, - CONSTRAINT user_confirmation_codes_pk PRIMARY KEY (id), - FOREIGN KEY (user_id) REFERENCES users(id) + code VARCHAR(4) NOT NULL, + expired_at TIMESTAMP NOT NULL, + CONSTRAINT user_confirmation_codes_pk PRIMARY KEY (id), + FOREIGN KEY (user_id) REFERENCES users(id) ); diff --git a/src/application/middlewares/error_handler.ts b/src/application/middlewares/error_handler.ts index 1909b95..7e24b9c 100644 --- a/src/application/middlewares/error_handler.ts +++ b/src/application/middlewares/error_handler.ts @@ -7,7 +7,7 @@ export default function errorHandler( response: Response, _next: NextFunction, ) { - console.log(error); + console.error(error); if (request.originalUrl.startsWith('/forms') || request.originalUrl.startsWith('/pages')) { return response.redirect('/pages/500'); diff --git a/src/b3_stock_alerts/AuthService.ts b/src/b3_stock_alerts/AuthService.ts index e88670f..22ce52a 100644 --- a/src/b3_stock_alerts/AuthService.ts +++ b/src/b3_stock_alerts/AuthService.ts @@ -56,6 +56,8 @@ export default class AuthService { } const code = randomInt(1000, 9999).toString(); + const expired_at = new Date(); + expired_at.setMinutes(expired_at.getMinutes() + 10); await this.confirmation_code.sendCode({ email, code }); @@ -63,6 +65,7 @@ export default class AuthService { id: randomUUID(), user_id: user.id, code, + expired_at, }); return {}; diff --git a/src/b3_stock_alerts/AuthService.unit.test.ts b/src/b3_stock_alerts/AuthService.unit.test.ts index 626a1ab..d4fe9db 100644 --- a/src/b3_stock_alerts/AuthService.unit.test.ts +++ b/src/b3_stock_alerts/AuthService.unit.test.ts @@ -153,7 +153,7 @@ describe("AuthService's unit tests", () => { }); it('saves the code after send it for user email', async () => { - expect.assertions(3); + expect.assertions(4); const email = faker.internet.email(); @@ -175,6 +175,7 @@ describe("AuthService's unit tests", () => { expect(params.id).toBeDefined(); expect(params.user_id).toBe(user.id); expect(params.code).toBe('1234'); + expect(params.expired_at).toBeInstanceOf(Date); }); }); diff --git a/src/b3_stock_alerts/EmailGateway.ts b/src/b3_stock_alerts/EmailGateway.ts index 7009fff..e3302cc 100644 --- a/src/b3_stock_alerts/EmailGateway.ts +++ b/src/b3_stock_alerts/EmailGateway.ts @@ -44,8 +44,8 @@ export default class EmailGateway implements AlertNotification, ConfirmationCode from: process.env.APPLICATION_EMAIL, to: params.email, subject: 'Código de confirmação', - text: `Segue o código de confirmação ${params.code}. Acesse o link ${process.env.SERVER_URL}/pages/confirm-code?email=${params.email}.`, - html: `

Segue o código de confirmação ${params.code}.


Acesse o link.

`, + text: `Segue o código de confirmação ${params.code}. Acesse o link ${process.env.SERVER_URL}/pages/confirm-code?email=${params.email}. O código expira em 10 minutos.`, + html: `

Segue o código de confirmação ${params.code}.

Acesse o link.

O código expira em 10 minutos.

`, }; await this.transporter.sendMail(message); diff --git a/src/b3_stock_alerts/EmailGateway.unit.test.ts b/src/b3_stock_alerts/EmailGateway.unit.test.ts index 531eedf..e4f8e55 100644 --- a/src/b3_stock_alerts/EmailGateway.unit.test.ts +++ b/src/b3_stock_alerts/EmailGateway.unit.test.ts @@ -94,7 +94,7 @@ describe("EmailGateway's unit tests", () => { expect.assertions(1); const email = faker.internet.email(); - const code = faker.string.alphanumeric() + const code = faker.string.alphanumeric(); await email_gateway.sendCode({ email, code }); @@ -102,8 +102,8 @@ describe("EmailGateway's unit tests", () => { from: 'test@server.com', to: email, subject: 'Código de confirmação', - text: `Segue o código de confirmação ${code}. Acesse o link http://localhost:5000/pages/confirm-code?email=${email}.`, - html: `

Segue o código de confirmação ${code}.


Acesse o link http://localhost:5000/pages/confirm-code?email=${email}.

`, + text: `Segue o código de confirmação ${code}. Acesse o link http://localhost:5000/pages/confirm-code?email=${email}. O código expira em 10 minutos.`, + html: `

Segue o código de confirmação ${code}.

Acesse o link.

O código expira em 10 minutos.

`, }); }); }); diff --git a/src/b3_stock_alerts/PgUserRepository.ts b/src/b3_stock_alerts/PgUserRepository.ts index 17ce593..8aadf4f 100644 --- a/src/b3_stock_alerts/PgUserRepository.ts +++ b/src/b3_stock_alerts/PgUserRepository.ts @@ -1,6 +1,5 @@ import Postgres from '@shared/Postgres'; import { Client } from 'pg'; -import ConfirmationCode from './ConfirmationCode'; import { User } from './User'; import { UserConfirmationCode } from './UserConfirmationCode'; import UserRepository from './UserRepository'; @@ -79,22 +78,34 @@ export default class PgUserRepository implements UserRepository { } async createConfirmationCode(confirmation_code: UserConfirmationCode): Promise { + const { + id, user_id, code, expired_at, + } = confirmation_code; await this.client.query( - 'INSERT INTO user_confirmation_codes (id, user_id, code) VALUES ($1, $2, $3)', - [confirmation_code.id, confirmation_code.user_id, confirmation_code.code], + 'INSERT INTO user_confirmation_codes (id, user_id, code, expired_at) VALUES ($1, $2, $3, $4)', + [ + id, + user_id, + code, + `${expired_at.getFullYear()}-${expired_at.getMonth() + 1}-${expired_at.getDate()} ${expired_at.getHours()}:${expired_at.getMinutes()}:${expired_at.getSeconds()}`, + ], ); } - async getConfirmationCode(email: string, code: string): Promise { + async getConfirmationCode(email: string, code: string): Promise { const result = await this.client.query( - 'SELECT ucc.id, user_id, code FROM user_confirmation_codes ucc JOIN users ON users.email = $1 WHERE code = $2', + 'SELECT ucc.id, user_id, code, expired_at FROM user_confirmation_codes ucc JOIN users ON users.email = $1 WHERE code = $2', [email, code], ); - if (result.rows[0] === undefined) { + const confirmation_code = result.rows[0]; + + if (confirmation_code === undefined) { return null; } - return result.rows[0]; + confirmation_code.expired_at = new Date(confirmation_code.expired_at); + + return confirmation_code; } } diff --git a/src/b3_stock_alerts/PgUserRepository.unit.test.ts b/src/b3_stock_alerts/PgUserRepository.unit.test.ts index f9653f1..c0f2dbd 100644 --- a/src/b3_stock_alerts/PgUserRepository.unit.test.ts +++ b/src/b3_stock_alerts/PgUserRepository.unit.test.ts @@ -3,7 +3,6 @@ import { faker } from '@faker-js/faker/locale/pt_BR'; import Postgres from '@shared/Postgres'; import PgUserRepository from './PgUserRepository'; import { User } from './User'; -import { UserConfirmationCode } from './UserConfirmationCode'; const get_client_spy = jest.spyOn(Postgres, 'getClient'); @@ -174,23 +173,30 @@ describe('PgUserRepository', () => { it('inserts a new confirmation code', async () => { expect.assertions(1); - const confirmation_code: UserConfirmationCode = { + const confirmation_code = { id: faker.string.uuid(), user_id: faker.string.uuid(), code: faker.string.numeric(4), + expired_at: new Date('2024-02-13 17:15:00'), }; await repository.createConfirmationCode(confirmation_code); - expect(query_mock).toHaveBeenCalledWith('INSERT INTO user_confirmation_codes (id, user_id, code) VALUES ($1, $2, $3)', [ - confirmation_code.id, confirmation_code.user_id, confirmation_code.code, - ]); + expect(query_mock).toHaveBeenCalledWith( + 'INSERT INTO user_confirmation_codes (id, user_id, code, expired_at) VALUES ($1, $2, $3, $4)', + [ + confirmation_code.id, + confirmation_code.user_id, + confirmation_code.code, + '2024-2-13 17:15:0', + ], + ); }); }); describe('PgUserRepository.getConfirmationCode', () => { it('returns a confirmation code', async () => { - expect.assertions(2); + expect.assertions(5); const email = faker.internet.email(); const code = faker.string.numeric(4); @@ -199,17 +205,21 @@ describe('PgUserRepository', () => { id: faker.string.uuid(), user_id: faker.string.uuid(), code, + expired_at: '2004-10-19 10:23:54', } query_mock.mockResolvedValueOnce({ rows: [confirmation_code] }); - const result = await repository.getConfirmationCode(email, code); + const result = (await repository.getConfirmationCode(email, code))!; expect(query_mock).toHaveBeenCalledWith( - 'SELECT ucc.id, user_id, code FROM user_confirmation_codes ucc JOIN users ON users.email = $1 WHERE code = $2', + 'SELECT ucc.id, user_id, code, expired_at FROM user_confirmation_codes ucc JOIN users ON users.email = $1 WHERE code = $2', [email, code], ); - expect(result).toEqual(confirmation_code); + expect(result.id).toEqual(confirmation_code.id); + expect(result.user_id).toEqual(confirmation_code.user_id); + expect(result.code).toEqual(confirmation_code.code); + expect(result.expired_at.getTime()).toEqual(new Date('2004-10-19 10:23:54').getTime()); }); it("returns NULL if confirmation code doesn't exit", async () => { @@ -223,7 +233,7 @@ describe('PgUserRepository', () => { const result = await repository.getConfirmationCode(email, code); expect(query_mock).toHaveBeenCalledWith( - 'SELECT ucc.id, user_id, code FROM user_confirmation_codes ucc JOIN users ON users.email = $1 WHERE code = $2', + 'SELECT ucc.id, user_id, code, expired_at FROM user_confirmation_codes ucc JOIN users ON users.email = $1 WHERE code = $2', [email, code], ); diff --git a/src/b3_stock_alerts/UserConfirmationCode.ts b/src/b3_stock_alerts/UserConfirmationCode.ts index 664e8b7..363836e 100644 --- a/src/b3_stock_alerts/UserConfirmationCode.ts +++ b/src/b3_stock_alerts/UserConfirmationCode.ts @@ -2,4 +2,5 @@ export type UserConfirmationCode = { id: string; user_id: string; code: string; + expired_at: Date; } diff --git a/src/b3_stock_alerts/UserRepository.ts b/src/b3_stock_alerts/UserRepository.ts index fc44b46..f944d35 100644 --- a/src/b3_stock_alerts/UserRepository.ts +++ b/src/b3_stock_alerts/UserRepository.ts @@ -1,4 +1,3 @@ -import ConfirmationCode from './ConfirmationCode'; import { User } from './User'; import { UserConfirmationCode } from './UserConfirmationCode'; @@ -10,5 +9,5 @@ export default interface UserRepository { deleteUser(user_id: string): Promise; getUserByEmail(email: string): Promise; createConfirmationCode(confirmation_code: UserConfirmationCode): Promise; - getConfirmationCode(email: string, code: string): Promise; + getConfirmationCode(email: string, code: string): Promise; }