From bb58fc6310bf17454c1041e837d212ad4454cfbf Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:30:26 -0300 Subject: [PATCH 01/26] chore(database): define 'user' and 'post' tables into init.sql --- init.sql | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/init.sql b/init.sql index e2c36c6f..49acfd96 100644 --- a/init.sql +++ b/init.sql @@ -1,5 +1,18 @@ USE test_db; ---TODO Crie a tabela de user; +CREATE TABLE IF NOT EXISTS user ( + id INT AUTO_INCREMENT, + firstName VARCHAR(100) NOT NULL, + lastName VARCHAR(100) NOT NULL, + email VARCHAR(100) NOT NULL, + PRIMARY KEY (id) +); ---TODO Crie a tabela de posts; +CREATE TABLE IF NOT EXISTS post ( + id INT AUTO_INCREMENT, + title VARCHAR(100) NOT NULL, + description VARCHAR(100) NOT NULL, + userId INT NOT NULL, + PRIMARY KEY (id), + CONSTRAINT fk_user_post FOREIGN KEY (userId) REFERENCES user (id) +); From 271f68e1549760c18c20af81f21cd9956b8a5940 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:37:30 -0300 Subject: [PATCH 02/26] feat: define User and Post entities --- src/entity/Post.ts | 19 +++++++++++++++++-- src/entity/User.ts | 21 +++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/entity/Post.ts b/src/entity/Post.ts index a1f68038..5b4543a2 100644 --- a/src/entity/Post.ts +++ b/src/entity/Post.ts @@ -1,3 +1,18 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from "typeorm"; +import { User } from './User'; -//TODO Crie a entidade de Post +@Entity('post') +export class Post { + @PrimaryGeneratedColumn() + id!: number; + + @Column({ type: 'varchar', length: 100 }) + title!: string; + + @Column({ type: 'varchar', length: 100 }) + description!: string; + + @ManyToOne(() => User, (user) => user.posts, { nullable: false, onDelete: 'CASCADE' }) + @JoinColumn({ name: 'userId', referencedColumnName: 'id', foreignKeyConstraintName: 'fk_user_post' }) + user!: User; +} \ No newline at end of file diff --git a/src/entity/User.ts b/src/entity/User.ts index a8e22632..39224a01 100644 --- a/src/entity/User.ts +++ b/src/entity/User.ts @@ -1,3 +1,20 @@ -import { Entity, PrimaryGeneratedColumn, Column } from "typeorm"; +import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"; +import { Post } from './Post'; -//TODO Crie a entidade de User +@Entity('user') +export class User { + @PrimaryGeneratedColumn() + id!: number; + + @Column({ type: 'varchar', length: 100 }) + firstName!: string; + + @Column({ type: 'varchar', length: 100 }) + lastName!: string; + + @Column({ type: 'varchar', length: 100 }) + email!: string; + + @OneToMany(() => Post, (post) => post.user) + posts!: Post[]; +} \ No newline at end of file From 6951e0a64a1014de60b218480d346ef065b2a5d8 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:38:46 -0300 Subject: [PATCH 03/26] refactor: move AppDataSource to a separate file --- src/data-source.ts | 16 ++++++++++++++++ src/index.ts | 21 ++++----------------- 2 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 src/data-source.ts diff --git a/src/data-source.ts b/src/data-source.ts new file mode 100644 index 00000000..6f645215 --- /dev/null +++ b/src/data-source.ts @@ -0,0 +1,16 @@ +import { DataSource } from 'typeorm'; +import { User } from './entity/User'; +import { Post } from './entity/Post'; + +const AppDataSource = new DataSource({ + type: "mysql", + host: process.env.DB_HOST || "127.0.0.1", + port: 3306, + username: process.env.DB_USER || "root", + password: process.env.DB_PASSWORD || "password", + database: process.env.DB_NAME || "test_db", + entities: [User, Post], + synchronize: true, +}); + +export default AppDataSource; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index 1af52a84..f8d2c148 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,23 +1,10 @@ -import 'reflect-metadata'; import express from 'express'; -import { DataSource } from 'typeorm'; -import { User } from './entity/User'; -import { Post } from './entity/Post'; +import 'reflect-metadata'; +import AppDataSource from './data-source'; const app = express(); app.use(express.json()); -const AppDataSource = new DataSource({ - type: "mysql", - host: process.env.DB_HOST || "localhost", - port: 3306, - username: process.env.DB_USER || "root", - password: process.env.DB_PASSWORD || "password", - database: process.env.DB_NAME || "test_db", - entities: [User,Post], - synchronize: true, -}); - const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); const initializeDatabase = async () => { @@ -34,11 +21,11 @@ const initializeDatabase = async () => { initializeDatabase(); app.post('/users', async (req, res) => { -// Crie o endpoint de users + // Crie o endpoint de users }); app.post('/posts', async (req, res) => { -// Crie o endpoint de posts + // Crie o endpoint de posts }); const PORT = process.env.PORT || 3000; From 0443bcc7a0aed34dad7581ab11b82c660bb8c728 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:45:36 -0300 Subject: [PATCH 04/26] feat: get enviroment variable or a default value --- src/config/env.ts | 1 + src/data-source.ts | 13 +++++++------ 2 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 src/config/env.ts diff --git a/src/config/env.ts b/src/config/env.ts new file mode 100644 index 00000000..7f11d00d --- /dev/null +++ b/src/config/env.ts @@ -0,0 +1 @@ +export const getEnv = (name: string, defaultValue: string = '') => process.env[name] || defaultValue \ No newline at end of file diff --git a/src/data-source.ts b/src/data-source.ts index 6f645215..790c4699 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -1,14 +1,15 @@ import { DataSource } from 'typeorm'; import { User } from './entity/User'; import { Post } from './entity/Post'; +import { getEnv } from './config/env'; const AppDataSource = new DataSource({ - type: "mysql", - host: process.env.DB_HOST || "127.0.0.1", - port: 3306, - username: process.env.DB_USER || "root", - password: process.env.DB_PASSWORD || "password", - database: process.env.DB_NAME || "test_db", + type: 'mysql', + host: getEnv('DB_HOST', 'localhost'), + port: parseInt(getEnv('DB_PORT', '3306')), + username: getEnv('DB_USER', 'root'), + password: getEnv('DB_PASSWORD', 'password'), + database: getEnv('DB_NAME', 'test_db'), entities: [User, Post], synchronize: true, }); From 3f1f64538f299281b07ec329a0ecf3d2a0ccac6f Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:48:41 -0300 Subject: [PATCH 05/26] feat: add user repository with 'create' and 'get' (by id) methods --- src/repositories/user-repository.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/repositories/user-repository.ts diff --git a/src/repositories/user-repository.ts b/src/repositories/user-repository.ts new file mode 100644 index 00000000..1f045cdf --- /dev/null +++ b/src/repositories/user-repository.ts @@ -0,0 +1,16 @@ +import AppDataSource from '../data-source'; +import { User } from '../entity/User'; + +export class UserRepository { + private repo = AppDataSource.getRepository(User); + + async create(data: Partial): Promise { + const createdUser = this.repo.create(data) + return this.repo.save(createdUser); + } + + async get(id: number): Promise { + const user = this.repo.findOne({ where: { id } }); + return user; + } +} \ No newline at end of file From 75baa3de2adf178eea04de23a98858b3ae726b5f Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sat, 16 Nov 2024 23:49:13 -0300 Subject: [PATCH 06/26] feat: add post repository with 'create' method --- src/repositories/post-repository.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/repositories/post-repository.ts diff --git a/src/repositories/post-repository.ts b/src/repositories/post-repository.ts new file mode 100644 index 00000000..69df6143 --- /dev/null +++ b/src/repositories/post-repository.ts @@ -0,0 +1,11 @@ +import AppDataSource from '../data-source'; +import { Post } from '../entity/Post'; + +export class PostRepository { + private repo = AppDataSource.getRepository(Post); + + async create(data: Partial): Promise { + const createdPost = this.repo.create(data) + return this.repo.save(createdPost); + } +} \ No newline at end of file From 79943ddfc021b180c1ba5a0d35fef06ec686a032 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 11:50:09 -0300 Subject: [PATCH 07/26] feat(user-repository): add getByEmail method --- src/repositories/user-repository.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/repositories/user-repository.ts b/src/repositories/user-repository.ts index 1f045cdf..27579586 100644 --- a/src/repositories/user-repository.ts +++ b/src/repositories/user-repository.ts @@ -13,4 +13,9 @@ export class UserRepository { const user = this.repo.findOne({ where: { id } }); return user; } + + async getByEmail(email: string): Promise { + const user = this.repo.findOne({ where: { email } }); + return user; + } } \ No newline at end of file From 7419da91cf2577e69e01da1721281c011ce19e36 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:23:36 -0300 Subject: [PATCH 08/26] feat(utils): add utility function validating if a string is empty --- src/utils/string-util.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/utils/string-util.ts diff --git a/src/utils/string-util.ts b/src/utils/string-util.ts new file mode 100644 index 00000000..bbb15dd8 --- /dev/null +++ b/src/utils/string-util.ts @@ -0,0 +1,3 @@ +const isEmpty = (value: string) => !value || !value.trim().length; + +export { isEmpty } \ No newline at end of file From 26fe47830878c5a0418400062b7f3f69bff46aaf Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:24:28 -0300 Subject: [PATCH 09/26] feat(utils): add utility function validating if a string is an email --- src/utils/string-util.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/string-util.ts b/src/utils/string-util.ts index bbb15dd8..6908c9d9 100644 --- a/src/utils/string-util.ts +++ b/src/utils/string-util.ts @@ -1,3 +1,8 @@ const isEmpty = (value: string) => !value || !value.trim().length; -export { isEmpty } \ No newline at end of file +const isEmail = (email: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) + +export { + isEmpty, + isEmail +} \ No newline at end of file From c09e1b63a0351ae19d239b3203db448ae4848664 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:24:52 -0300 Subject: [PATCH 10/26] feat: add userDTO --- src/dtos/user-dto.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/dtos/user-dto.ts diff --git a/src/dtos/user-dto.ts b/src/dtos/user-dto.ts new file mode 100644 index 00000000..e472b826 --- /dev/null +++ b/src/dtos/user-dto.ts @@ -0,0 +1,22 @@ +import { isEmail, isEmpty } from '../utils/string-util'; + +export class UserDTO { + + constructor( + public firstName: string, + public lastName: string, + public email: string + ) { + this.validate(firstName, lastName, email); + + this.firstName = firstName; + this.lastName = lastName; + this.email = email; + } + + private validate(firstName: string, lastName: string, email: string): void { + if (isEmpty(firstName)) throw new Error('First name is required') + if (isEmpty(lastName)) throw new Error('Last name is required') + if (isEmpty(email) || !isEmail(this.email)) throw new Error('Invalid email format') + } +} \ No newline at end of file From 16d5b80b081ab3caced491d755790563572d14e9 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:29:02 -0300 Subject: [PATCH 11/26] feat: add user service with with 'createUser' method --- src/services/user-service.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/services/user-service.ts diff --git a/src/services/user-service.ts b/src/services/user-service.ts new file mode 100644 index 00000000..6743d28b --- /dev/null +++ b/src/services/user-service.ts @@ -0,0 +1,14 @@ +import { UserDTO } from '../dtos/user-dto'; +import { UserRepository } from '../repositories/user-repository'; + +export class UserService { + constructor(private userRepository: UserRepository) {} + + async createUser(user: UserDTO) { + const existingUser = await this.userRepository.getByEmail(user.email); + if (existingUser) throw new Error('This email is already in use.'); + + const createdUser = await this.userRepository.create(user); + return createdUser; + } +} \ No newline at end of file From 6052c037b4aef1f42d4b90bba46bf7c028012b19 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:42:12 -0300 Subject: [PATCH 12/26] feat: throw BadRequestError if an userDTO is invalid --- src/dtos/user-dto.ts | 7 ++++--- src/utils/errors/bad-request-error.ts | 7 +++++++ 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 src/utils/errors/bad-request-error.ts diff --git a/src/dtos/user-dto.ts b/src/dtos/user-dto.ts index e472b826..f20b3776 100644 --- a/src/dtos/user-dto.ts +++ b/src/dtos/user-dto.ts @@ -1,3 +1,4 @@ +import { BadRequestError } from '../utils/errors/bad-request-error'; import { isEmail, isEmpty } from '../utils/string-util'; export class UserDTO { @@ -15,8 +16,8 @@ export class UserDTO { } private validate(firstName: string, lastName: string, email: string): void { - if (isEmpty(firstName)) throw new Error('First name is required') - if (isEmpty(lastName)) throw new Error('Last name is required') - if (isEmpty(email) || !isEmail(this.email)) throw new Error('Invalid email format') + if (isEmpty(firstName)) throw new BadRequestError('First name is required') + if (isEmpty(lastName)) throw new BadRequestError('Last name is required') + if (isEmpty(email) || !isEmail(this.email)) throw new BadRequestError('Invalid email format') } } \ No newline at end of file diff --git a/src/utils/errors/bad-request-error.ts b/src/utils/errors/bad-request-error.ts new file mode 100644 index 00000000..2a504223 --- /dev/null +++ b/src/utils/errors/bad-request-error.ts @@ -0,0 +1,7 @@ +export class BadRequestError extends Error { + public readonly statusCode = 400; + + constructor(message: string) { + super(message) + } +} \ No newline at end of file From 66649c695f6d7faafc2966a40a7864ac3d420896 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 17:44:08 -0300 Subject: [PATCH 13/26] feat(user-service): throw BadRequestError if an email is already in use before creating a new user --- src/services/user-service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/user-service.ts b/src/services/user-service.ts index 6743d28b..bf5236d5 100644 --- a/src/services/user-service.ts +++ b/src/services/user-service.ts @@ -1,12 +1,13 @@ import { UserDTO } from '../dtos/user-dto'; import { UserRepository } from '../repositories/user-repository'; +import { BadRequestError } from '../utils/errors/bad-request-error'; export class UserService { constructor(private userRepository: UserRepository) {} async createUser(user: UserDTO) { const existingUser = await this.userRepository.getByEmail(user.email); - if (existingUser) throw new Error('This email is already in use.'); + if (existingUser) throw new BadRequestError('This email is already in use.'); const createdUser = await this.userRepository.create(user); return createdUser; From d73dc27d36d1e972f0368ac93b53c4590178d231 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 18:56:15 -0300 Subject: [PATCH 14/26] feat: add PostDTO with required fields validation --- src/dtos/post-dto.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/dtos/post-dto.ts diff --git a/src/dtos/post-dto.ts b/src/dtos/post-dto.ts new file mode 100644 index 00000000..493aa1b5 --- /dev/null +++ b/src/dtos/post-dto.ts @@ -0,0 +1,22 @@ +import { BadRequestError } from '../utils/errors/bad-request-error'; +import { isEmpty } from '../utils/string-util'; + +export class PostDTO { + + constructor( + public title: string, + public description: string, + public userId: number + ) { + this.validate(title, description, userId); + + this.title = title; + this.description = description; + } + + private validate(title: string, description: string, userId: number): void { + if (isEmpty(title)) throw new BadRequestError('Title is required') + if (isEmpty(description)) throw new BadRequestError('Description is required') + if (isNaN(userId)) throw new BadRequestError('User is required to create a post') + } +} \ No newline at end of file From ff5ae047927ce3f20185a5768bbcc727d648d440 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 18:57:18 -0300 Subject: [PATCH 15/26] feat: add PostService with 'createPost' method --- src/services/post-service.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/services/post-service.ts diff --git a/src/services/post-service.ts b/src/services/post-service.ts new file mode 100644 index 00000000..91bf4592 --- /dev/null +++ b/src/services/post-service.ts @@ -0,0 +1,16 @@ +import { PostDTO } from '../dtos/post-dto'; +import { PostRepository } from '../repositories/post-repository'; +import { UserRepository } from '../repositories/user-repository'; + +export class PostService { + constructor(private userRepository: UserRepository, private postRepository: PostRepository) {} + + async createPost(data: PostDTO) { + const { userId, ...post } = data; + const user = await this.userRepository.get(data.userId); + if (!user) throw new Error(`Couldn't find the specified user.`); + + const createdPost = await this.postRepository.create({ ...post, user }) + return createdPost; + } +} \ No newline at end of file From 6db8106bd3ba8c57ea2be3e9f0c1018857f351d6 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 19:02:48 -0300 Subject: [PATCH 16/26] feat(post-service): throw NotFoundError if the specified user is not found when creating a post --- src/services/post-service.ts | 3 ++- src/utils/errors/not-found-error.ts | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/utils/errors/not-found-error.ts diff --git a/src/services/post-service.ts b/src/services/post-service.ts index 91bf4592..bfe3bba9 100644 --- a/src/services/post-service.ts +++ b/src/services/post-service.ts @@ -1,6 +1,7 @@ import { PostDTO } from '../dtos/post-dto'; import { PostRepository } from '../repositories/post-repository'; import { UserRepository } from '../repositories/user-repository'; +import { NotFoundError } from '../utils/errors/not-found-error'; export class PostService { constructor(private userRepository: UserRepository, private postRepository: PostRepository) {} @@ -8,7 +9,7 @@ export class PostService { async createPost(data: PostDTO) { const { userId, ...post } = data; const user = await this.userRepository.get(data.userId); - if (!user) throw new Error(`Couldn't find the specified user.`); + if (!user) throw new NotFoundError(`Couldn't find the specified user.`); const createdPost = await this.postRepository.create({ ...post, user }) return createdPost; diff --git a/src/utils/errors/not-found-error.ts b/src/utils/errors/not-found-error.ts new file mode 100644 index 00000000..173da347 --- /dev/null +++ b/src/utils/errors/not-found-error.ts @@ -0,0 +1,7 @@ +export class NotFoundError extends Error { + public readonly statusCode = 404; + + constructor(message: string) { + super(message) + } +} \ No newline at end of file From e341ee30b8c8b7260000f3bf104b8910ddefa969 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 19:59:26 -0300 Subject: [PATCH 17/26] feat: return an UserResponseDTO on creating an user --- src/dtos/user-response-dto.ts | 15 +++++++++++++++ src/services/user-service.ts | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/dtos/user-response-dto.ts diff --git a/src/dtos/user-response-dto.ts b/src/dtos/user-response-dto.ts new file mode 100644 index 00000000..92c45d5f --- /dev/null +++ b/src/dtos/user-response-dto.ts @@ -0,0 +1,15 @@ +import { User } from '../entity/User'; + +export class UserResponseDTO { + id: number; + firstName: string; + lastName: string; + email: string; + + constructor(user: User) { + this.id = user.id, + this.firstName = user.firstName, + this.lastName = user.lastName, + this.email = user.email + } +} \ No newline at end of file diff --git a/src/services/user-service.ts b/src/services/user-service.ts index bf5236d5..0e60fef6 100644 --- a/src/services/user-service.ts +++ b/src/services/user-service.ts @@ -1,4 +1,5 @@ import { UserDTO } from '../dtos/user-dto'; +import { UserResponseDTO } from '../dtos/user-response-dto'; import { UserRepository } from '../repositories/user-repository'; import { BadRequestError } from '../utils/errors/bad-request-error'; @@ -10,6 +11,6 @@ export class UserService { if (existingUser) throw new BadRequestError('This email is already in use.'); const createdUser = await this.userRepository.create(user); - return createdUser; + return new UserResponseDTO(createdUser); } } \ No newline at end of file From 680efd9df4d1003b66accee5b85208871905493f Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:00:21 -0300 Subject: [PATCH 18/26] feat: add UserController with 'store' method --- src/controllers/user-controller.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/controllers/user-controller.ts diff --git a/src/controllers/user-controller.ts b/src/controllers/user-controller.ts new file mode 100644 index 00000000..3810888a --- /dev/null +++ b/src/controllers/user-controller.ts @@ -0,0 +1,23 @@ +import { Request, Response } from "express"; +import { UserDTO } from '../dtos/user-dto'; +import { UserService } from '../services/user-service'; +import { BadRequestError } from '../utils/errors/bad-request-error'; + +export class UserController { + constructor(private userService: UserService) {} + + async store(req: Request, res: Response) { + try { + const { firstName, lastName, email } = req.body; + const userDTO = new UserDTO(firstName, lastName, email) + + const createdUser = await this.userService.createUser(userDTO); + res.status(201).json(createdUser) + } catch (error) { + if (error instanceof BadRequestError) return res.status(400).send({ message: error.message }) + + console.error("Unexpected error:", error); + res.status(500).send({ message: "Internal Server Error" }) + } + } +} \ No newline at end of file From ff876ac0c60cb8f2903f5e40d28d7906de4345f1 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:05:29 -0300 Subject: [PATCH 19/26] feat: add a factory to make UserController --- src/factories/user-factory.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/factories/user-factory.ts diff --git a/src/factories/user-factory.ts b/src/factories/user-factory.ts new file mode 100644 index 00000000..a4ffe761 --- /dev/null +++ b/src/factories/user-factory.ts @@ -0,0 +1,11 @@ +import { UserController } from '../controllers/user-controller'; +import { UserRepository } from '../repositories/user-repository'; +import { UserService } from '../services/user-service'; + +const makeUserController = (): UserController => { + const userRepository = new UserRepository(); + const userService = new UserService(userRepository); + return new UserController(userService); +}; + +export default makeUserController; \ No newline at end of file From 36741d84071bcf394e7d3a2d23dbd884d8341916 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:08:29 -0300 Subject: [PATCH 20/26] feat: setup 'POST /users' endpoint --- src/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index f8d2c148..75f9b183 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import express from 'express'; import 'reflect-metadata'; import AppDataSource from './data-source'; +import makeUserController from './factories/user-factory'; const app = express(); app.use(express.json()); @@ -20,9 +21,9 @@ const initializeDatabase = async () => { initializeDatabase(); -app.post('/users', async (req, res) => { - // Crie o endpoint de users -}); +const userController = makeUserController() + +app.post('/users', async (req, res) => userController.store(req, res)); app.post('/posts', async (req, res) => { // Crie o endpoint de posts From 93e24db53653fb0042f6f318fd7de7c8cd43a4b2 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:20:20 -0300 Subject: [PATCH 21/26] feat: return a PostResponseDTO on creating a post --- src/dtos/post-response-dto.ts | 15 +++++++++++++++ src/services/post-service.ts | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 src/dtos/post-response-dto.ts diff --git a/src/dtos/post-response-dto.ts b/src/dtos/post-response-dto.ts new file mode 100644 index 00000000..9e2e4e48 --- /dev/null +++ b/src/dtos/post-response-dto.ts @@ -0,0 +1,15 @@ +import { Post } from '../entity/Post'; + +export class PostResponseDTO { + id: number; + title: string; + description: string; + userId: number; + + constructor(post: Post) { + this.id = post.id, + this.title = post.title, + this.description = post.description, + this.userId = post.user.id + } +} \ No newline at end of file diff --git a/src/services/post-service.ts b/src/services/post-service.ts index bfe3bba9..f19c003c 100644 --- a/src/services/post-service.ts +++ b/src/services/post-service.ts @@ -1,4 +1,5 @@ import { PostDTO } from '../dtos/post-dto'; +import { PostResponseDTO } from '../dtos/post-response-dto'; import { PostRepository } from '../repositories/post-repository'; import { UserRepository } from '../repositories/user-repository'; import { NotFoundError } from '../utils/errors/not-found-error'; @@ -12,6 +13,6 @@ export class PostService { if (!user) throw new NotFoundError(`Couldn't find the specified user.`); const createdPost = await this.postRepository.create({ ...post, user }) - return createdPost; + return new PostResponseDTO(createdPost); } } \ No newline at end of file From 4e16108e82c4cfe21bc6ccce45fc772bb2041769 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:20:53 -0300 Subject: [PATCH 22/26] feat: add PostController with 'store' method --- src/controllers/post-controller.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/controllers/post-controller.ts diff --git a/src/controllers/post-controller.ts b/src/controllers/post-controller.ts new file mode 100644 index 00000000..3da84783 --- /dev/null +++ b/src/controllers/post-controller.ts @@ -0,0 +1,24 @@ +import { Request, Response } from "express"; +import { PostDTO } from '../dtos/post-dto'; +import { PostService } from '../services/post-service'; +import { BadRequestError } from '../utils/errors/bad-request-error'; +import { NotFoundError } from '../utils/errors/not-found-error'; + +export class PostController { + constructor(private postService: PostService) {} + + async store(req: Request, res: Response) { + try { + const { title, description, userId } = req.body; + const postDTO = new PostDTO(title, description, userId) + + const createdPost = await this.postService.createPost(postDTO); + res.status(201).json(createdPost) + } catch (error) { + if (error instanceof BadRequestError || error instanceof NotFoundError) return res.status(400).send({ message: error.message }) + + console.error("Unexpected error:", error); + res.status(500).send({ message: "Internal Server Error" }) + } + } +} \ No newline at end of file From 326cf32d88c7f2fb3ad99508758ac439617ccb42 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:21:38 -0300 Subject: [PATCH 23/26] feat: add a factory to make PostController --- src/factories/post-factory.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/factories/post-factory.ts diff --git a/src/factories/post-factory.ts b/src/factories/post-factory.ts new file mode 100644 index 00000000..ec87f7b2 --- /dev/null +++ b/src/factories/post-factory.ts @@ -0,0 +1,13 @@ +import { PostController } from '../controllers/post-controller'; +import { PostRepository } from '../repositories/post-repository'; +import { UserRepository } from '../repositories/user-repository'; +import { PostService } from '../services/post-service'; + +const makePostController = (): PostController => { + const userRepository = new UserRepository(); + const postRepository = new PostRepository(); + const postService = new PostService(userRepository, postRepository); + return new PostController(postService); +}; + +export default makePostController; \ No newline at end of file From f980b5a402fd0774ef38351188d12612b87f3265 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:22:19 -0300 Subject: [PATCH 24/26] feat: setup 'POST /posts' endpoint --- src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 75f9b183..e7bba4f5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,7 @@ import express from 'express'; import 'reflect-metadata'; import AppDataSource from './data-source'; import makeUserController from './factories/user-factory'; +import makePostController from './factories/post-factory'; const app = express(); app.use(express.json()); @@ -22,12 +23,11 @@ const initializeDatabase = async () => { initializeDatabase(); const userController = makeUserController() +const postController = makePostController(); app.post('/users', async (req, res) => userController.store(req, res)); -app.post('/posts', async (req, res) => { - // Crie o endpoint de posts -}); +app.post('/posts', async (req, res) => postController.store(req, res)); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { From a0b85faf0a4a46fc61dd77b4256ead7147968c71 Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:40:05 -0300 Subject: [PATCH 25/26] fix(controllers): return the correct status code in case of BadResquestError or NotFoundError --- src/controllers/post-controller.ts | 2 +- src/controllers/user-controller.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/controllers/post-controller.ts b/src/controllers/post-controller.ts index 3da84783..2da606be 100644 --- a/src/controllers/post-controller.ts +++ b/src/controllers/post-controller.ts @@ -15,7 +15,7 @@ export class PostController { const createdPost = await this.postService.createPost(postDTO); res.status(201).json(createdPost) } catch (error) { - if (error instanceof BadRequestError || error instanceof NotFoundError) return res.status(400).send({ message: error.message }) + if (error instanceof BadRequestError || error instanceof NotFoundError) return res.status(error.statusCode).send({ message: error.message }) console.error("Unexpected error:", error); res.status(500).send({ message: "Internal Server Error" }) diff --git a/src/controllers/user-controller.ts b/src/controllers/user-controller.ts index 3810888a..595e5bff 100644 --- a/src/controllers/user-controller.ts +++ b/src/controllers/user-controller.ts @@ -14,7 +14,7 @@ export class UserController { const createdUser = await this.userService.createUser(userDTO); res.status(201).json(createdUser) } catch (error) { - if (error instanceof BadRequestError) return res.status(400).send({ message: error.message }) + if (error instanceof BadRequestError) return res.status(error.statusCode).send({ message: error.message }) console.error("Unexpected error:", error); res.status(500).send({ message: "Internal Server Error" }) From e27ca7447799d744fe2b2cd93a99eb450f2b923b Mon Sep 17 00:00:00 2001 From: kaio-nink <51462685+kaio-nink@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:56:08 -0300 Subject: [PATCH 26/26] chore: setup the Dockerfile --- Dockerfile | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index baec7ba2..6b1742d2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1 +1,12 @@ -#TODO Configure o Dockerfile +FROM node:20 + +WORKDIR /app +COPY package*.json ./ + +RUN npm install +COPY . . + +RUN npm run build +EXPOSE 3000 + +CMD ["npm", "run", "dev"]