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
4 changes: 4 additions & 0 deletions modulo6/case4/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
.env
build
package-lock.json
27 changes: 27 additions & 0 deletions modulo6/case4/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/knex": "^0.16.1",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"knex": "^2.2.0",
"mysql": "^2.18.1"
},
"devDependencies": {
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/node": "^18.6.1",
"ts-node-dev": "^2.0.0",
"typescript": "^4.7.4"
}
}
57 changes: 57 additions & 0 deletions modulo6/case4/src/business/ProductBusiness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { CustomError } from "../error/CustomError";
import { Product } from "../model/Product";
import { ProductDataInterface } from "../model/ProductDataInterface";
import { GetProductResponse } from "../types/getProductResponse";
import { RegisterProductInputDTO } from "../types/registerProductInputDTO";

export default class ProductBusiness {
constructor (
private productData: ProductDataInterface
) {}

async register (input: RegisterProductInputDTO) {
const { id, name, tags } = input

const productSameId = await this.productData.getProductById(id)
if (productSameId) {
throw new CustomError(409, "Product with this id already exists")
}
const productSameName = await this.productData.getProductByName(name)
if (productSameName) {
throw new CustomError(409, "Product with this name already exists")
}

const newProduct = new Product(id, name, tags)

await this.productData.insert(newProduct)

return name
}

async getById (id: number) {

if (!id || isNaN(id)) {
throw new CustomError(417, "Invalid id")
}

const result = await this.productData.getProductById(id)
if (!result) {
throw new CustomError(404, "Product not found")
}

return result
}

async search (searchInput: string) {
let result: GetProductResponse[]
if (!searchInput) {
result = await this.productData.getAllProducts()

return result
}

result = await this.productData.searchProducts(searchInput)

return result
}
}
62 changes: 62 additions & 0 deletions modulo6/case4/src/controller/ProductController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Request, Response } from "express";
import ProductBusiness from "../business/ProductBusiness";
import { RegisterProductInputDTO } from "../types/registerProductInputDTO";

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

register = async (req: Request, res: Response) => {
const {id, name, tags} = req.body

const input: RegisterProductInputDTO = {
id,
name,
tags
}
try {
const productName = await this.productBusiness.register(input)

res.status(201).send(`${productName} registered successfully`)

} catch (error) {
if (error instanceof Error) {
return res.status(400).send(error.message)
}
res.status(500).send("Register error.")
}
}

getById = async (req: Request, res: Response) => {
const id = Number(req.params.id)

try {
const product = await this.productBusiness.getById(id)

res.send({ product })

} catch (error) {
if (error instanceof Error) {
return res.status(400).send(error.message)
}
res.status(500).send("Register error.")
}
}

search = async (req: Request, res: Response) => {
const searchInput = req.query.search as string

try {
const searchResult = await this.productBusiness.search(searchInput)

res.send({ productsFromSearch: searchResult })

} catch (error) {
if (error instanceof Error) {
return res.status(400).send(error.message)
}
res.status(500).send("Register error.")
}
}
}
17 changes: 17 additions & 0 deletions modulo6/case4/src/data/BaseDatabase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import knex from "knex";
import dotenv from "dotenv"
dotenv.config()

export default abstract class BaseDatabase {
protected connection = knex({
client: 'mysql',
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_SCHEMA,
port: 3306,
multipleStatements: true
}
})
}
81 changes: 81 additions & 0 deletions modulo6/case4/src/data/ProductData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Product } from "../model/Product";
import { ProductDataInterface } from "../model/ProductDataInterface";
import { productTableName } from "../model/TableNames";
import { GetProductResponse } from "../types/getProductResponse";
import BaseDatabase from "./BaseDatabase";

export default class ProductData extends BaseDatabase implements ProductDataInterface {
private TABLE_NAME = productTableName
insert = async (product: Product) => {
try {
await this.connection(this.TABLE_NAME)
.insert(product)
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message)
} else {
throw new Error("Database error.")
}
}
}

getProductByName = async (name: string) => {
try {
const queryResult: GetProductResponse[] = await this.connection(this.TABLE_NAME)
.where({ name })

return queryResult[0]
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message)
} else {
throw new Error("Database error.")
}
}
}

getProductById = async (id: number) => {
try {
const queryResult: GetProductResponse[] = await this.connection(this.TABLE_NAME)
.where({ id })

return queryResult[0]
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message)
} else {
throw new Error("Database error.")
}
}
}

searchProducts = async (search: string) => {
try {
const queryResult: GetProductResponse[] = await this.connection(this.TABLE_NAME)
.whereLike('name', `%${search}%`)
.orWhereLike('tag', `%${search}%`)

return queryResult
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message)
} else {
throw new Error("Database error.")
}
}
}

getAllProducts = async () => {
try {
const queryResult: GetProductResponse[] = await this.connection(this.TABLE_NAME)

return queryResult
} catch (error) {
if (error instanceof Error) {
throw new Error(error.message)
} else {
throw new Error("Database error.")
}
}
}
}
12 changes: 12 additions & 0 deletions modulo6/case4/src/error/CustomError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class CustomError extends Error {

protected statusCode: number

constructor (
statusCode: number = 400,
message: string
) {
super(message)
this.statusCode = statusCode
}
}
Empty file added modulo6/case4/src/index.ts
Empty file.
7 changes: 7 additions & 0 deletions modulo6/case4/src/model/Product.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class Product {
constructor (
private id: number,
private name: string,
private tags: string[]
) {}
}
14 changes: 14 additions & 0 deletions modulo6/case4/src/model/ProductDataInterface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { GetProductResponse } from "../types/getProductResponse"
import { Product } from "./Product"

export interface ProductDataInterface {
insert: (product: Product) => Promise<void>

getProductByName: (name: string) => Promise<GetProductResponse>

getProductById: (id: number) => Promise<GetProductResponse>

searchProducts: (search: string) => Promise<GetProductResponse[]>

getAllProducts: () => Promise<GetProductResponse[]>
}
1 change: 1 addition & 0 deletions modulo6/case4/src/model/TableNames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const productTableName = 'products_amaro'
12 changes: 12 additions & 0 deletions modulo6/case4/src/model/error/CustomError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export class CustomError extends Error {

protected statusCode: number

constructor (
statusCode: number = 400,
message: string
) {
super(message)
this.statusCode = statusCode
}
}
5 changes: 5 additions & 0 deletions modulo6/case4/src/types/getProductResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type GetProductResponse = {
id: number,
name: string,
tags: string[]
}
5 changes: 5 additions & 0 deletions modulo6/case4/src/types/registerProductInputDTO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type RegisterProductInputDTO = {
id: number,
name: string,
tags: string[]
}
13 changes: 13 additions & 0 deletions modulo6/case4/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es6", /* Specify ECMAScript target version */
"module": "commonjs", /* Specify module code generation */
"sourceMap": true, /* Generates corresponding '.map' file. */
"outDir": "./build", /* Redirect output structure to the directory. */
"rootDir": "./", /* Specify the root directory of input files. */
"removeComments": true, /* Do not emit comments to output. */
"noImplicitAny": true, /* Raise error on declarations with an implied 'any' type. */
"esModuleInterop": true, /*Pra quando da problema importando cors, express no topo do arquivo*/
"resolveJsonModule": true
}
}
13 changes: 13 additions & 0 deletions modulo6/exercicios-logica/checa-parenteses/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "checa-parenteses",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "tsc && node ./build/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Loading