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
3 changes: 3 additions & 0 deletions modulo6/cases-semana-4/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
build
.env
3,193 changes: 3,193 additions & 0 deletions modulo6/cases-semana-4/package-lock.json

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions modulo6/cases-semana-4/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "cases-semana-4",
"version": "1.0.0",
"description": "Case simulado de processo seletivo da Labenu. Processo AMARO.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "tsc && node ./build/Controller/index.js",
"dev": "ts-node-dev ./src/Controller/index.ts",
"build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"ts-node-dev": "^2.0.0",
"typescript": "^4.7.4"
},
"dependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/jsonwebtoken": "^8.5.8",
"@types/uuid": "^8.3.4",
"axios": "^0.27.2",
"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"
}
}
73 changes: 73 additions & 0 deletions modulo6/cases-semana-4/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Case de Back-end: Amaro

Este projeto de Back-end foi proposto pela Labenu para treinar a participação em projetos técnicos de processos de seleção. O sistema controla o registro de produtos.

## 🎯 Endpoints:

- SignUp: Endpoint para cadastrar um administrador no sistema. Retorna um token de acesso para realizar a manipulação dos produtos. Recebe um body com as seguintes informações:
```
{
"name": "",
"email": "",
"password": ""
}
```
- SignIn: Endpoint para fazer o login do administrador no sistema. Retorna um token de acesso.
```
{
"email": "",
"password": ""
}
```

- Register: Endpoint para cadastrar um produto no sistema. Recebe o token via headers e um body com as seguintes informações:
```
{
"photo": "",
"name": "",
"price": "",
"type": "",
gender": "",
"description": ""
}
```
- Details: Endpoint para pegar os detalhes de um produto. Recebe o nome do produto por parâmetro e também um token de autorização via headers.


### 🛠 Tecnologias utilizadas:
- TypeScript;
- Node.js;
- MySQL;
### 📚 Bibliotecas utilizadas:
- Express;
- CORS;
- Dotenv;
- Knex;
- Bcrypt;
- Jsonwebtoken;
- uuid.


## 📊 Tabelas Sql:
```
CREATE TABLE amaro_sellers (
id VARCHAR(255) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL
);
CREATE TABLE amaro_products (
id VARCHAR(255) PRIMARY KEY,
photo VARCHAR(255) NOT NULL UNIQUE,
name VARCHAR(255) NOT NULL UNIQUE,
description TEXT,
type VARCHAR(255) NOT NULL,
price FLOAT NOT NULL,
gender VARCHAR(255) NOT NULL

);
```
### Link para a documentação:


https://documenter.getpostman.com/view/20353584/VUjTihxS
39 changes: 39 additions & 0 deletions modulo6/cases-semana-4/request.rest
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# @name SignUp

POST http://localhost:3306/seller/signUp Content-Type: application/json

{
"name": "marisa", "email": "marisa@labenu.com", "password": "mulher"
}

###

@token = {{SignUp.response.body.token}}

###

# @name SignIn

POST http://localhost:3306/seller/signIn Content-Type: application/json

{
"email": "marisa@labenu.com", "password": "mulher"
}

###

@token = {{SignIn.response.body.token}}

###

POST http://localhost:3306/product/register Content-Type: application/json Authorization: {{token}}

{
"photo": "https://cdn.pixabay.com/photo/2019/03/12/13/26/jeans-4050815_960_720.jpg", "name": "jeans", "price": "156.00", "type": "calça", "gender": "masculino", "description": "Tamanhos disponíveis: P, M, G"
}

###

GET http://localhost:3306/product/details/Calça jeans Content-Type: application/json Authorization: {{token}}

###
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface authenticationData {
id: string
}
4 changes: 4 additions & 0 deletions modulo6/cases-semana-4/src/Types/loginDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type loginModel = {
email: string,
password: string
}
8 changes: 8 additions & 0 deletions modulo6/cases-semana-4/src/Types/newProductDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export interface newProductModel {
photo: string
name: string
price: string
type: string
gender: string
description: string
}
25 changes: 25 additions & 0 deletions modulo6/cases-semana-4/src/Types/productInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export enum PRODUCTS_TYPES {
SHIRT = "blusa",
PANTS = "calça",
ACESSORY = "acessório",
SHOES = "sapato",
COAT = "casaco",
PERFUME = "perfumaria",
DRESS = "vestido",
SHORT = "short",
SKIRT = "saia"
}

export enum PRODUCTS_GENDER {
FEMALE = "feminino",
MALE = "masculino"
}

export interface productInput {
photo: string
name: string
description?: string
price: number
type: PRODUCTS_TYPES
gender: PRODUCTS_GENDER
}
5 changes: 5 additions & 0 deletions modulo6/cases-semana-4/src/Types/sellerInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface sellerInput {
name: string,
email: string,
password: string
}
57 changes: 57 additions & 0 deletions modulo6/cases-semana-4/src/business/ProductBusiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import ProductDatabase from "../data/ProductDatabase";
import ProductModel from "../models/ProductModel";
import IdGenerator from "../services/IdGenerator";
import { newProductModel } from "../Types/newProductDTO";
import { PRODUCTS_GENDER } from "../Types/productInterface";

export default class ProductBussiness {
constructor(
private idGenerator: IdGenerator,
private productDatabase: ProductDatabase
){}

createProduct = async(input: newProductModel, token: string): Promise<void> => {
const {photo, name, price, type, gender, description} = input

if(!token) {
throw new Error("You're not allowed to execute this action!")
}

if(!photo || !name || !price || !type || !gender || !description) {
throw new Error("Only the description field can be empty. check you request for missing fields.")
}

const productPrice: number = +price

const productGender = gender.toLowerCase()
if(productGender !== PRODUCTS_GENDER.FEMALE && productGender !== PRODUCTS_GENDER.MALE) {
throw new Error("This product department doesn't exist in system.")
}

const productId: string = this.idGenerator.createId()

const newProduct: ProductModel = new ProductModel(productId, photo ,name, productPrice, type, gender, description)

await this.productDatabase.insertNewProduct(newProduct)

}

getProductDetails = async(searchParam: string, token: string): Promise<ProductModel> => {
if(!token) {
throw new Error("You need an acess token to execute this action.")
}

if(!searchParam) {
throw new Error("Send a search param to get a product's details.")
}

const productsDetails = await this.productDatabase.getDetails(searchParam)

if(!productsDetails) {
throw new Error("Couldn't find any product. Maybe it doesn't exists anymore, or it's a invalid search param.")
}

return productsDetails
}

}
76 changes: 76 additions & 0 deletions modulo6/cases-semana-4/src/business/SellerBusiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import SellerDatabase from "../data/SellerDatabase";
import SellerModel from "../models/SellerModel";
import Authenticator from "../services/Authenticator";
import HashManager from "../services/HashManager";
import IdGenerator from "../services/IdGenerator";
import { authenticationData } from "../Types/authenticationDataInterface";
import { loginModel } from "../Types/loginDTO";
import { sellerInput } from "../Types/sellerInterface";

export default class SellerBusiness {
constructor(
private idGenerator: IdGenerator,
private hashManager: HashManager,
private authenticator: Authenticator,
private sellerDatabase: SellerDatabase
){}

signUp = async(input: sellerInput): Promise<string> => {
const {name, email, password} = input

if(!name || !email || !password) {
throw new Error("There's an empty field. Check your request.")
}

if(email.includes("@") !== true || password.length < 6) {
throw new Error( "Invalid email or password too short. Must be larger than 6.")

}
const id: string = this.idGenerator.createId() as string
const hashedPass: string = await this.hashManager.createHash(password)

const verifySellersExistance = await this.sellerDatabase.getSellerByEmail(email)

if(verifySellersExistance) {
throw new Error ("This seller is already registered. Can't register twice.")
}

const newSeller = new SellerModel(id, name, email, hashedPass)
await this.sellerDatabase.insertSeller(newSeller)

const payload: authenticationData = {
id: newSeller.getSellersId()
}

const token = this.authenticator.generateToken(payload)

return token;
}

signIn = async(login: loginModel):Promise<string> => {

const {email, password} = login

if(!email || !password) {
throw new Error("One of the fields is empty, please check your request.")
}

if(email.includes("@") !== true) {
throw new Error("Invalid email format.")
}

const sellerResult = await this.sellerDatabase.getSellerByEmail(email)
const verifySellersPass = this.hashManager.compareHash(password, sellerResult.password)

if(!sellerResult || verifySellersPass === false) {
throw new Error("Invalid Credentials")
}

const payload: authenticationData = {
id: sellerResult.id
}

const token: string = this.authenticator.generateToken(payload)
return token;
}
}
47 changes: 47 additions & 0 deletions modulo6/cases-semana-4/src/controller/ProductController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import ProductBussiness from "../business/ProductBusiness";
import { Request, Response } from 'express'
import { newProductModel } from "../Types/newProductDTO";

export default class ProductController {
constructor(
private productBusiness: ProductBussiness
){}

createProduct = async(req: Request, res: Response):Promise<void> => {
try{
const{photo, name, price, type, gender, description} = req.body
const token: string = req.headers.authorization as string
console.log(token)

const newProduct: newProductModel = {
photo,
name,
price,
type,
gender,
description
}

await this.productBusiness.createProduct(newProduct, token)

res.status(201).send({message: "Product registered successfully!"})
}catch(error: any) {
res.send(error.message)
}
}

getProductDetails = async(req: Request, res: Response): Promise<void> => {
try {
const searchParam: string = req.params.searchParam as string
const token: string = req.headers.authorization as string


const response = await this.productBusiness.getProductDetails(searchParam, token)

res.status(200).send({details: response})

} catch(error: any) {
res.send(error.message)
}
}
}
Loading