Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions modulo5/testes-erros/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.env
.DS_STORE
node_modules
build
coverage
8 changes: 8 additions & 0 deletions modulo5/testes-erros/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
roots: ["<rootDir>/tests"],
transform: {
"^.+\\.tsx?$": "ts-jest",
},
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$",
moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
}
40 changes: 40 additions & 0 deletions modulo5/testes-erros/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "template-jest",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node ./build/src/index.js",
"build": "tsc",
"dev": "ts-node-dev ./src/index.ts",
"migrations": "tsc && node ./build/src/database/migrations/Migrations.js",
"test": "jest"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/jest": "^28.1.6",
"@types/jsonwebtoken": "^8.5.8",
"@types/knex": "^0.16.1",
"@types/node": "^18.0.6",
"@types/uuid": "^8.3.4",
"jest": "^28.1.3",
"ts-jest": "^28.0.8",
"ts-node-dev": "^2.0.0",
"typescript": "^4.7.4"
},
"dependencies": {
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"jsonwebtoken": "^8.5.1",
"knex": "^2.2.0",
"mysql": "^2.18.1",
"uuid": "^8.3.2"
}
}
46 changes: 46 additions & 0 deletions modulo5/testes-erros/requests.rest
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
### Endpoint de teste
GET http://localhost:3003/ping

### 1) Signup
POST http://localhost:3003/users/signup
Content-Type: application/json

{
"name": "alice",
"email": "alice@gmail.com",
"password": "alice99"
}

### 2) Login
POST http://localhost:3003/users/login
Content-Type: application/json

{
"email": "astrodev@gmail.com",
"password": "bananinha"
}

### 3) Create post
POST http://localhost:3003/posts
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwMSIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTY2NDI4MzY3MCwiZXhwIjoxNjY0MzcwMDcwfQ.1N7AOM3aE1pMnygnLSaGhuutArCndMWt4TektFlUuKU
Content-Type: application/json

{
"content": "Hello world!"
}

### 4) Get posts
GET http://localhost:3003/posts
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwMSIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTY2NDI4MzY3MCwiZXhwIjoxNjY0MzcwMDcwfQ.1N7AOM3aE1pMnygnLSaGhuutArCndMWt4TektFlUuKU

### 5) Delete post
DELETE http://localhost:3003/posts/048e2da8-f739-4780-a28b-e4c6732d2c83
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwMSIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTY2NDI4MzY3MCwiZXhwIjoxNjY0MzcwMDcwfQ.1N7AOM3aE1pMnygnLSaGhuutArCndMWt4TektFlUuKU

### 6) Like post
POST http://localhost:3003/posts/like/ed4b5302-c6f0-45b6-8055-6c519bbf62f0
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwMSIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTY2NDI4MzY3MCwiZXhwIjoxNjY0MzcwMDcwfQ.1N7AOM3aE1pMnygnLSaGhuutArCndMWt4TektFlUuKU

### 7) Remove Like from post
DELETE http://localhost:3003/posts/like/ed4b5302-c6f0-45b6-8055-6c519bbf62f0
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEwMSIsInJvbGUiOiJBRE1JTiIsImlhdCI6MTY2NDI4MzY3MCwiZXhwIjoxNjY0MzcwMDcwfQ.1N7AOM3aE1pMnygnLSaGhuutArCndMWt4TektFlUuKU
9 changes: 9 additions & 0 deletions modulo5/testes-erros/src/business/PingBusiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class PingBusiness {
public ping = async () => {
const response = {
message: "Pong!"
}

return response
}
}
187 changes: 187 additions & 0 deletions modulo5/testes-erros/src/business/PostBusiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { PostDatabase } from "../database/PostDatabase"
import { AuthenticationError } from "../errors/AuthenticationError"
import { AuthorizationError } from "../errors/AuthorizationError"
import { ConflictError } from "../errors/ConflictError"
import { NotFoundError } from "../errors/NotFoundError"
import { ParamsError } from "../errors/ParamsError"
import { IAddLikeInputDTO, IAddLikeOutputDTO, ICreatePostInputDTO, ICreatePostOutputDTO, IDeletePostInputDTO, IDeletePostOutputDTO, IGetPostsInputDTO, IGetPostsOutputDTO, ILikeDB, IRemoveLikeInputDTO, IRemoveLikeOutputDTO, Post } from "../models/Post"
import { USER_ROLES } from "../models/User"
import { Authenticator } from "../services/Authenticator"
import { IdGenerator } from "../services/IdGenerator"

export class PostBusiness {
constructor(
private postDatabase: PostDatabase,
private idGenerator: IdGenerator,
private authenticator: Authenticator
) {}

public createPost = async (input: ICreatePostInputDTO) => {
const { token, content } = input

const payload = this.authenticator.getTokenPayload(token)

if (!payload) {
throw new AuthenticationError()
}

if (typeof content !== "string") {
throw new ParamsError("Parâmetro 'content' inválido")
}

if (content.length < 1) {
throw new ParamsError("Parâmetro 'content' inválido: mínimo de 1 caracteres")
}

const id = this.idGenerator.generate()

const post = new Post(
id,
content,
payload.id
)

await this.postDatabase.createPost(post)

const response: ICreatePostOutputDTO = {
message: "Post criado com sucesso",
post
}

return response
}

public getPosts = async (input: IGetPostsInputDTO) => {
const { token } = input

const payload = this.authenticator.getTokenPayload(token)

if (!payload) {
throw new AuthenticationError()
}

const postsDB = await this.postDatabase.getPosts()

const posts = postsDB.map(postDB => {
return new Post(
postDB.id,
postDB.content,
postDB.user_id
)
})

for (let post of posts) {
const postId = post.getId()
const likes = await this.postDatabase.getLikes(postId)
post.setLikes(likes)
}

const response: IGetPostsOutputDTO = {
posts
}

return response
}

public deletePost = async (input: IDeletePostInputDTO) => {
const { token, postId } = input

const payload = this.authenticator.getTokenPayload(token)

if (!payload) {
throw new AuthenticationError()
}

const postDB = await this.postDatabase.findPostById(postId)

if (!postDB) {
throw new NotFoundError("Post não encontrado")
}

if (payload.role === USER_ROLES.NORMAL) {
if (postDB.user_id !== payload.id) {
throw new AuthorizationError()
}
}

await this.postDatabase.deletePost(postId)

const response: IDeletePostOutputDTO = {
message: "Post deletado com sucesso"
}

return response
}

public addLike = async (input: IAddLikeInputDTO) => {
const { token, postId } = input

const payload = this.authenticator.getTokenPayload(token)

if (!payload) {
throw new AuthenticationError()
}

const postDB = await this.postDatabase.findPostById(postId)

if (!postDB) {
throw new NotFoundError("Post não encontrado")
}

const isAlreadyLiked = await this.postDatabase.findLike(
postId,
payload.id
)

if (isAlreadyLiked) {
throw new ConflictError("Já deu like")
}

const likeDB: ILikeDB = {
id: this.idGenerator.generate(),
post_id: postId,
user_id: payload.id
}

await this.postDatabase.addLike(likeDB)

const response: IAddLikeOutputDTO = {
message: "Like realizado com sucesso"
}

return response
}

public removeLike = async (input: IRemoveLikeInputDTO) => {
const { token, postId } = input

const payload = this.authenticator.getTokenPayload(token)

if (!payload) {
throw new AuthenticationError()
}

const postDB = await this.postDatabase.findPostById(postId)

if (!postDB) {
throw new NotFoundError("Post não encontrado")
}

const isAlreadyLiked = await this.postDatabase.findLike(
postId,
payload.id
)

if (!isAlreadyLiked) {
throw new NotFoundError("Ainda não deu like")
}

await this.postDatabase.removeLike(postId, payload.id)

const response: IRemoveLikeOutputDTO = {
message: "Like removido com sucesso"
}

return response
}
}
Loading