From 858146e751fae33f903b68c17e318cde16a3391a Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 15:09:50 -0300 Subject: [PATCH 01/43] =?UTF-8?q?Algoritmo=20base=20para=20convers=C3=A3o?= =?UTF-8?q?=20de=20moedas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 index.js diff --git a/index.js b/index.js new file mode 100644 index 000000000..d75f2f22e --- /dev/null +++ b/index.js @@ -0,0 +1,46 @@ +const exchangeRates = { + USD: { + USD: 1 + }, + BRL: { + USD: 0.2016, + }, + EUR: { + USD: 1.08, + }, + BTC: { + USD: 47619.05, + }, + ETH: { + USD: 1538.46, + }, + ARS: { + USD: 0.0012 + } +}; + +const convertCurrency = (from, to, amount) => { + const currency = to; + from = exchangeRates[from]; + to = exchangeRates[to]; + const calc = ( + (amount * to.USD) / from.USD + ).toFixed(4); + + const formattedNumber = new Intl.NumberFormat('pt-BR', { style: 'currency', currency }).format(calc); + + return formattedNumber; +}; + + +console.log(convertCurrency('USD', 'USD', 100)); +console.log(convertCurrency('USD', 'BRL', 1)); +console.log(convertCurrency('USD', 'EUR', 100)); +console.log(convertCurrency('USD', 'BTC', 100)); +console.log(convertCurrency('USD', 'ETH', 100)); + +console.log(convertCurrency('BRL', 'EUR', 1)); +console.log(convertCurrency('EUR', 'BRL', 1)); + +console.log(convertCurrency('BRL', 'ARS', 1)); +console.log(convertCurrency('ARS', 'BRL', 1)); \ No newline at end of file From 76c7b567f950448bb1e2def3691c1078112367dc Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 15:12:57 -0300 Subject: [PATCH 02/43] Adicionando .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..25c8fdbab --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +package-lock.json \ No newline at end of file From ee6215bff288847c126530bd1fd92987728020aa Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 16:23:11 -0300 Subject: [PATCH 03/43] =?UTF-8?q?Constru=C3=ADdo=20o=20servidor=20base=20d?= =?UTF-8?q?a=20aplica=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 25 +++++++++++++++++++++++++ routes/main.js | 25 +++++++++++++++++++++++++ server.js | 10 ++++++++++ services/currency_exchange.js | 31 +++++++++++++++++++++++++++++++ utils/response.js | 7 +++++++ 5 files changed, 98 insertions(+) create mode 100644 package.json create mode 100644 routes/main.js create mode 100644 server.js create mode 100644 services/currency_exchange.js create mode 100644 utils/response.js diff --git a/package.json b/package.json new file mode 100644 index 000000000..be48b56a4 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "challenge-bravo", + "version": "1.0.0", + "description": "[[English](README.md) | [Portuguese](README.pt.md)]", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js", + "debug": "nodemon server.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/nadoneves/challenge-bravo.git" + }, + "keywords": [], + "author": "Leonardo Neves ", + "license": "ISC", + "bugs": { + "url": "https://github.com/nadoneves/challenge-bravo/issues" + }, + "homepage": "https://github.com/nadoneves/challenge-bravo#readme", + "dependencies": { + "express": "^4.18.2" + } +} diff --git a/routes/main.js b/routes/main.js new file mode 100644 index 000000000..a0209286d --- /dev/null +++ b/routes/main.js @@ -0,0 +1,25 @@ +const express = require('express'); +const router = express.Router(); +const { Response } = require('../utils/response'); + +const { ConvertCurrency } = require('../services/currency_exchange'); + +router.get('/', (req, res) => { + const { from, to, amount } = req.query; + const result = {}; + result[from] = amount; + result[to] = ConvertCurrency(from, to, amount); + Response(res, 200, result); +}); + +router.post('/', (req, res) => { + // Handle the POST request for the root path + Response(res, 201); +}); + +router.delete('/', (req, res) => { + // Handle the DELETE request for the root path + Response(res, 204); +}); + +module.exports = router; \ No newline at end of file diff --git a/server.js b/server.js new file mode 100644 index 000000000..547e44f3b --- /dev/null +++ b/server.js @@ -0,0 +1,10 @@ +const express = require('express'); +const app = express(); +const port = 3000; + +const mainRoutes = require('./routes/main'); +app.use('/', mainRoutes); + +app.listen(port, () => { + console.log(`API rodando em http://localhost:${port}`); +}); \ No newline at end of file diff --git a/services/currency_exchange.js b/services/currency_exchange.js new file mode 100644 index 000000000..f0b582d3b --- /dev/null +++ b/services/currency_exchange.js @@ -0,0 +1,31 @@ +const exchangeRates = { + USD: { + USD: 1 + }, + BRL: { + USD: 0.2016, + }, + EUR: { + USD: 1.08, + }, + BTC: { + USD: 47619.05, + }, + ETH: { + USD: 1538.46, + }, + ARS: { + USD: 0.0012 + } +}; + +exports.ConvertCurrency = (from, to, amount) => { + from = exchangeRates[from]; + to = exchangeRates[to]; + const calc = ( + (amount * from.USD) / to.USD + ).toFixed(4); + + const formattedNumber = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD'}).format(calc); + return formattedNumber; +}; \ No newline at end of file diff --git a/utils/response.js b/utils/response.js new file mode 100644 index 000000000..2ead7475b --- /dev/null +++ b/utils/response.js @@ -0,0 +1,7 @@ +exports.Response = (res, statusCode, data = null, cors = true) => { + if(cors) + res.set('Access-Control-Allow-Origin', '*'); + + if(!data) res.status(statusCode).send(); + else res.status(statusCode).send(data); +}; \ No newline at end of file From 4319dfb7aa878d85b4d33f4f7f0a355571f2a7ee Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 16:24:08 -0300 Subject: [PATCH 04/43] Update .gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 25c8fdbab..2e07c68e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules -package-lock.json \ No newline at end of file +package-lock.json +notes.txt +.env \ No newline at end of file From c3b8d015546c7da9722ce409938e24745856f3fb Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 16:58:44 -0300 Subject: [PATCH 05/43] Validando query paramenters na rota principal --- package.json | 3 ++- routes/main.js | 32 ++++++++++++++++++++++++-------- server.js | 2 ++ services/currency_exchange.js | 22 +++++++++++++++++----- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index be48b56a4..235241e9b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "homepage": "https://github.com/nadoneves/challenge-bravo#readme", "dependencies": { - "express": "^4.18.2" + "express": "^4.18.2", + "express-validator": "^7.0.1" } } diff --git a/routes/main.js b/routes/main.js index a0209286d..e70699c4c 100644 --- a/routes/main.js +++ b/routes/main.js @@ -1,16 +1,32 @@ const express = require('express'); const router = express.Router(); + +const { query, validationResult } = require('express-validator'); + const { Response } = require('../utils/response'); +const { ConvertCurrency, ExistsCurrency } = require('../services/currency_exchange'); -const { ConvertCurrency } = require('../services/currency_exchange'); +router.get('/', + query('from').notEmpty().escape().isLength({ min: 3, max: 3 }).withMessage('Currency code \'from\' must be 3 characters long'), + query('to').notEmpty().escape().isLength({ min: 3, max: 3 }).withMessage('Currency code \'to\' must be 3 characters long'), + query('amount').notEmpty().escape().isFloat().withMessage('Amount must be a number'), + (req, res) => { + const validateQuery = validationResult(req); + if (!validateQuery.isEmpty()) { + return Response(res, 400, validateQuery.array()); + } -router.get('/', (req, res) => { - const { from, to, amount } = req.query; - const result = {}; - result[from] = amount; - result[to] = ConvertCurrency(from, to, amount); - Response(res, 200, result); -}); + const { from, to, amount } = req.query; + + if(!ExistsCurrency(from)) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); + if(!ExistsCurrency(to)) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); + + const result = {}; + result[from] = amount; + result[to] = ConvertCurrency(from, to, amount); + Response(res, 200, result); + } +); router.post('/', (req, res) => { // Handle the POST request for the root path diff --git a/server.js b/server.js index 547e44f3b..458d311fd 100644 --- a/server.js +++ b/server.js @@ -2,6 +2,8 @@ const express = require('express'); const app = express(); const port = 3000; +app.use(express.json()); + const mainRoutes = require('./routes/main'); app.use('/', mainRoutes); diff --git a/services/currency_exchange.js b/services/currency_exchange.js index f0b582d3b..2fd07479f 100644 --- a/services/currency_exchange.js +++ b/services/currency_exchange.js @@ -19,12 +19,24 @@ const exchangeRates = { } }; +exports.ExistsCurrency = (currency) => { + return Object.keys(exchangeRates).includes(currency.toUpperCase()); +}; + exports.ConvertCurrency = (from, to, amount) => { - from = exchangeRates[from]; - to = exchangeRates[to]; - const calc = ( - (amount * from.USD) / to.USD - ).toFixed(4); + let calc = 0; + + from = from.toUpperCase(); + to = to.toUpperCase(); + + if(from === to) calc = amount + else { + from = exchangeRates[from]; + to = exchangeRates[to]; + calc = ( + (amount * from.USD) / to.USD + ).toFixed(4); + } const formattedNumber = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD'}).format(calc); return formattedNumber; From f94a0727cb41a2e29b48698e747b4b2fee67aef5 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 17:14:37 -0300 Subject: [PATCH 06/43] Adicionando helmet ao servidor --- package.json | 3 ++- server.js | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 235241e9b..139b2db4b 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "homepage": "https://github.com/nadoneves/challenge-bravo#readme", "dependencies": { "express": "^4.18.2", - "express-validator": "^7.0.1" + "express-validator": "^7.0.1", + "helmet": "^7.1.0" } } diff --git a/server.js b/server.js index 458d311fd..7e975ef4e 100644 --- a/server.js +++ b/server.js @@ -1,8 +1,11 @@ const express = require('express'); +const helmet = require('helmet'); + const app = express(); const port = 3000; app.use(express.json()); +app.use(helmet()); const mainRoutes = require('./routes/main'); app.use('/', mainRoutes); From dfe42b8c98dfc86edc81711db57e47679e078af6 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 18:38:17 -0300 Subject: [PATCH 07/43] Aplicando API rate limite e script yml para stress teste --- package.json | 1 + server.js | 9 +++++++++ tests/stress_test.yml | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 tests/stress_test.yml diff --git a/package.json b/package.json index 139b2db4b..3e158ec79 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "homepage": "https://github.com/nadoneves/challenge-bravo#readme", "dependencies": { "express": "^4.18.2", + "express-rate-limit": "^7.1.5", "express-validator": "^7.0.1", "helmet": "^7.1.0" } diff --git a/server.js b/server.js index 7e975ef4e..c8342dedc 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,6 @@ const express = require('express'); const helmet = require('helmet'); +const rateLimit = require('express-rate-limit'); const app = express(); const port = 3000; @@ -7,6 +8,14 @@ const port = 3000; app.use(express.json()); app.use(helmet()); +const limiter = rateLimit({ + windowMs: 1000, + limit: 10000, + standardHeaders: true, + message: 'You have exceeded your ~1000 requests per second limit.', +}); +app.use(limiter); + const mainRoutes = require('./routes/main'); app.use('/', mainRoutes); diff --git a/tests/stress_test.yml b/tests/stress_test.yml new file mode 100644 index 000000000..86df89cb6 --- /dev/null +++ b/tests/stress_test.yml @@ -0,0 +1,19 @@ +config: + target: 'http://localhost:3000' + phases: + # - duration: 10 + # arrivalRate: 10 + # name: Warm up + - duration: 20 + arrivalRate: 100 + maxVusers: 1000 + name: Ramp up load + +scenarios: + - flow: + - loop: + - get: + url: '/?from=USD&to=BRL&amount=1' + headers: + User-Agent: 'Artillery' + count: 10 \ No newline at end of file From 914d10986616d57c4390435686be7b9c2863bc7f Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 19 Feb 2024 19:13:31 -0300 Subject: [PATCH 08/43] Adicionando docker ao projeto --- .dockerignore | 5 +++++ Dockerfile | 7 +++++++ docker-compose.yml | 12 ++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..04417d5cb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +node_modules +npm-debug.log +.DS_Store +.git +.gitignore \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..e529147f1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM node:18 +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY . . +EXPOSE 3000 +CMD ["npm", "start"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..841c87e97 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: '3.8' +services: + app: + container_name: challenge-bravo + build: + context: . + dockerfile: Dockerfile + ports: + - "3000:3000" + environment: + NODE_ENV: development + command: npm start \ No newline at end of file From 9b65f296bc11918fabb25a56019eb5e681419996 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 20 Feb 2024 00:10:51 -0300 Subject: [PATCH 09/43] Add redis e mysql ao projeto --- .dockerignore | 6 +++++- .gitignore | 3 ++- docker-compose.yml | 27 +++++++++++++++++++++++++-- sql/currencydb.sql | 12 ++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 sql/currencydb.sql diff --git a/.dockerignore b/.dockerignore index 04417d5cb..1926bb639 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,4 +2,8 @@ node_modules npm-debug.log .DS_Store .git -.gitignore \ No newline at end of file +.gitignore +notes.txt +.env +mysql_data +sql \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2e07c68e6..ee4d806d7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules package-lock.json notes.txt -.env \ No newline at end of file +.env +mysql_data \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 841c87e97..882d8730c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.8' services: app: - container_name: challenge-bravo + container_name: backend build: context: . dockerfile: Dockerfile @@ -9,4 +9,27 @@ services: - "3000:3000" environment: NODE_ENV: development - command: npm start \ No newline at end of file + command: npm start + + redis: + container_name: redis + image: redis + command: redis-server --requirepass Redis2023! + ports: + - "6379:6379" + + db: + image: mysql:8 + container_name: mysql + command: --default-authentication-plugin=mysql_native_password + restart: always + environment: + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: currencydb + MYSQL_USER: user + MYSQL_PASSWORD: password + ports: + - "3306:3306" + volumes: + - ./mysql_data:/var/lib/mysql + - ./sql/currencydb.sql:/docker-entrypoint-initdb.d/currencydb.sql \ No newline at end of file diff --git a/sql/currencydb.sql b/sql/currencydb.sql new file mode 100644 index 000000000..ba699abe1 --- /dev/null +++ b/sql/currencydb.sql @@ -0,0 +1,12 @@ +-- Create a new database +CREATE DATABASE IF NOT EXISTS currencydb; +USE currencydb; + +-- Create a table for storing users +CREATE TABLE IF NOT EXISTS currencys ( + id INT AUTO_INCREMENT PRIMARY KEY, + currency VARCHAR(3) NOT NULL, + ballast FLOAT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); \ No newline at end of file From bc2187fc9c1da11a362b258cf2b8031abac7d7c3 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 20 Feb 2024 02:13:26 -0300 Subject: [PATCH 10/43] Criando workers para manter o mysql e o redis atualizados --- .dockerignore | 1 - Dockerfile | 12 +++++- docker-compose.yml | 16 ++++++-- models/connection.js | 13 +++++++ models/currencys.js | 29 +++++++++++++++ package.json | 7 +++- sql/currencydb.sql | 8 ++-- workers/currencys.js | 88 ++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 163 insertions(+), 11 deletions(-) create mode 100644 models/connection.js create mode 100644 models/currencys.js create mode 100644 workers/currencys.js diff --git a/.dockerignore b/.dockerignore index 1926bb639..9b866a030 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,6 +4,5 @@ npm-debug.log .git .gitignore notes.txt -.env mysql_data sql \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index e529147f1..fb961260e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,4 +4,14 @@ COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 -CMD ["npm", "start"] \ No newline at end of file +CMD ["npm", "start"] + + +FROM node:18 +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY models/ . +COPY workers/ . +COPY .env . +CMD ["node", "workers/currencys.js"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 882d8730c..5f057a3f1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,15 +14,14 @@ services: redis: container_name: redis image: redis - command: redis-server --requirepass Redis2023! + command: redis-server --requirepass Redis2024! ports: - "6379:6379" db: image: mysql:8 container_name: mysql - command: --default-authentication-plugin=mysql_native_password - restart: always + restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: rootpassword MYSQL_DATABASE: currencydb @@ -32,4 +31,13 @@ services: - "3306:3306" volumes: - ./mysql_data:/var/lib/mysql - - ./sql/currencydb.sql:/docker-entrypoint-initdb.d/currencydb.sql \ No newline at end of file + - ./sql/currencydb.sql:/docker-entrypoint-initdb.d/currencydb.sql + + worker: + container_name: worker + build: + context: . + dockerfile: Dockerfile + environment: + NODE_ENV: development + command: node currencys.js \ No newline at end of file diff --git a/models/connection.js b/models/connection.js new file mode 100644 index 000000000..c6a4d4ba2 --- /dev/null +++ b/models/connection.js @@ -0,0 +1,13 @@ +const Sequelize = require('sequelize'); + +module.exports.sequelize = new Sequelize( + process.env.MYSQL_DATABASE, + process.env.MYSQL_USER, + process.env.MYSQL_PASSWORD, + { + host: process.env.MYSQL_HOST, + dialect: 'mysql', + logging: false + } +); + diff --git a/models/currencys.js b/models/currencys.js new file mode 100644 index 000000000..0d769c9c5 --- /dev/null +++ b/models/currencys.js @@ -0,0 +1,29 @@ +const Sequelize = require('sequelize'); + +const { sequelize } = require('./connection'); + +const CurrencysModel = sequelize.define('currencys', { + id: { + type: Sequelize.INTEGER, + autoIncrement: true, + allowNull: false, + primaryKey: true, + }, + currency: { + type: Sequelize.STRING(10), + allowNull: false, + }, + ballast_usd: { + type: Sequelize.FLOAT, + allowNull: false, + }, + createdAt: { type: Sequelize.DATE }, + updatedAt: { type: Sequelize.DATE } +}, { + freezeTableName: true +}); + +module.exports = { + CurrencysModel, + CurrencysRaw: sequelize +}; \ No newline at end of file diff --git a/package.json b/package.json index 3e158ec79..1cf729537 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,14 @@ }, "homepage": "https://github.com/nadoneves/challenge-bravo#readme", "dependencies": { + "axios": "^1.6.7", + "dotenv": "^16.4.5", "express": "^4.18.2", "express-rate-limit": "^7.1.5", "express-validator": "^7.0.1", - "helmet": "^7.1.0" + "helmet": "^7.1.0", + "mysql2": "^3.9.1", + "redis": "^4.6.13", + "sequelize": "^6.37.1" } } diff --git a/sql/currencydb.sql b/sql/currencydb.sql index ba699abe1..265bebd84 100644 --- a/sql/currencydb.sql +++ b/sql/currencydb.sql @@ -5,8 +5,8 @@ USE currencydb; -- Create a table for storing users CREATE TABLE IF NOT EXISTS currencys ( id INT AUTO_INCREMENT PRIMARY KEY, - currency VARCHAR(3) NOT NULL, - ballast FLOAT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + currency VARCHAR(10) NOT NULL, + ballast_usd FLOAT NOT NULL, + createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); \ No newline at end of file diff --git a/workers/currencys.js b/workers/currencys.js new file mode 100644 index 000000000..2da963463 --- /dev/null +++ b/workers/currencys.js @@ -0,0 +1,88 @@ +require('dotenv').config(); + +const axios = require('axios'); +const redis = require("redis"); +const { CurrencysModel } = require('../models/currencys'); + +const sleep = (ms) => { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}; + +const getBallastDefault = async () => { + try { + const { data } = await axios.get(`https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/usd.min.json`); + return data.usd; + } catch (error) { + throw new Error(error); + } +} + +const setCurrencyRedis = async (currency, ballast_usd) => { + try { + const redisClient = redis.createClient({ + url: process.env.REDIS_HOST_TLS, + password: process.env.REDIS_PASSWORD_TLS + }); + + await redisClient.connect(); + + + await redisClient.set(currency, ballast_usd); + + await redisClient.disconnect(); + } catch (error) { + throw new Error(error); + } +}; + +const handler = async () => { + while (true) { + try { + console.log('>> CONSULTANDO DADOS E ATUALIZANDO DATABASE'); + + const ballastDefault = await getBallastDefault(); + const currencys = Object.keys(ballastDefault); + const countDataInDB = await CurrencysModel.findAll(); + if(countDataInDB.length === 0) { + console.log('>>> POPULATE DATABASE'); + for (let i = 0; i < currencys.length; i++) { + const currency = currencys[i]; + await CurrencysModel.create({ + currency: currency.toUpperCase(), + ballast_usd: ballastDefault[currency], + createdAt: new Date(), + updatedAt: new Date() + }); + } + console.log('>>> POPULATE DATABASE COMPLETED'); + } else { + console.log('>>> UPDATE'); + for (let i = 0; i < currencys.length; i++) { + const currency = currencys[i]; + await CurrencysModel.update({ + ballast_usd: ballastDefault[currency], + updatedAt: new Date() + }, { where: { currency: currency.toUpperCase() } }); + } + console.log('>>> UPDATE COMPLETED'); + } + + console.log('>> UPDATE REDIS DATABASE'); + const currencysInDB = await CurrencysModel.findAll(); + for (let i = 0; i < currencysInDB.length; i++) { + const currency = currencysInDB[i]; + await setCurrencyRedis(currency.currency, currency.ballast_usd); + } + console.log('>> UPDATE REDIS DATABASE COMPLETED'); + } catch (error) { + console.error('>> ERRO AO CONSULTAR DADOS', error); + } + + console.log() + await sleep(30 * 1000); + } +}; + +handler(); \ No newline at end of file From 111c0df21d18921be8918c20da762ae2f840b476 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 20 Feb 2024 02:34:08 -0300 Subject: [PATCH 11/43] Refatorando o ambiente em docker --- Dockerfile | 17 ----------------- Dockerfile.backend | 7 +++++++ Dockerfile.worker | 8 ++++++++ docker-compose.yml | 32 +++++++++++++++++++------------- 4 files changed, 34 insertions(+), 30 deletions(-) delete mode 100644 Dockerfile create mode 100644 Dockerfile.backend create mode 100644 Dockerfile.worker diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index fb961260e..000000000 --- a/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM node:18 -WORKDIR /app -COPY package*.json ./ -RUN npm install -COPY . . -EXPOSE 3000 -CMD ["npm", "start"] - - -FROM node:18 -WORKDIR /app -COPY package*.json ./ -RUN npm install -COPY models/ . -COPY workers/ . -COPY .env . -CMD ["node", "workers/currencys.js"] \ No newline at end of file diff --git a/Dockerfile.backend b/Dockerfile.backend new file mode 100644 index 000000000..e529147f1 --- /dev/null +++ b/Dockerfile.backend @@ -0,0 +1,7 @@ +FROM node:18 +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY . . +EXPOSE 3000 +CMD ["npm", "start"] \ No newline at end of file diff --git a/Dockerfile.worker b/Dockerfile.worker new file mode 100644 index 000000000..72cde0db5 --- /dev/null +++ b/Dockerfile.worker @@ -0,0 +1,8 @@ +FROM node:18 +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY models/ models/ +COPY workers/ workers/ +COPY .env . +CMD ["node", "workers/currencys.js"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 5f057a3f1..00b50d29b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,16 +1,5 @@ version: '3.8' services: - app: - container_name: backend - build: - context: . - dockerfile: Dockerfile - ports: - - "3000:3000" - environment: - NODE_ENV: development - command: npm start - redis: container_name: redis image: redis @@ -37,7 +26,24 @@ services: container_name: worker build: context: . - dockerfile: Dockerfile + dockerfile: Dockerfile.worker environment: NODE_ENV: development - command: node currencys.js \ No newline at end of file + command: node workers/currencys.js + depends_on: + - redis + - db + + app: + container_name: backend + build: + context: . + dockerfile: Dockerfile.backend + ports: + - "3000:3000" + environment: + NODE_ENV: development + command: npm start + depends_on: + - redis + - db \ No newline at end of file From aa240af2586968e87ed4dee493581568ea4240b9 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 20 Feb 2024 14:19:30 -0300 Subject: [PATCH 12/43] Refatorando docker para ordenar os starts seguindo a arquitetura desenhada --- Dockerfile.worker | 1 - docker-compose.yml | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Dockerfile.worker b/Dockerfile.worker index 72cde0db5..177f37c65 100644 --- a/Dockerfile.worker +++ b/Dockerfile.worker @@ -4,5 +4,4 @@ COPY package*.json ./ RUN npm install COPY models/ models/ COPY workers/ workers/ -COPY .env . CMD ["node", "workers/currencys.js"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 00b50d29b..30e2106da 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,11 @@ services: command: redis-server --requirepass Redis2024! ports: - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + retries: 3 + timeout: 5s db: image: mysql:8 @@ -21,6 +26,11 @@ services: volumes: - ./mysql_data:/var/lib/mysql - ./sql/currencydb.sql:/docker-entrypoint-initdb.d/currencydb.sql + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h db -u user -ppassword"] + interval: 10s + timeout: 12s + retries: 1 worker: container_name: worker @@ -29,10 +39,18 @@ services: dockerfile: Dockerfile.worker environment: NODE_ENV: development + MYSQL_HOST: db + MYSQL_USER: user + MYSQL_PASSWORD: password + MYSQL_DATABASE: currencydb + REDIS_HOST_TLS: redis://redis + REDIS_PASSWORD_TLS: Redis2024! command: node workers/currencys.js depends_on: - - redis - - db + db: + condition: service_healthy + redis: + condition: service_healthy app: container_name: backend @@ -43,7 +61,17 @@ services: - "3000:3000" environment: NODE_ENV: development - command: npm start + MYSQL_HOST: db + MYSQL_USER: user + MYSQL_PASSWORD: password + MYSQL_DATABASE: currencydb + REDIS_HOST_TLS: redis://redis + REDIS_PASSWORD_TLS: Redis2024! + command: bash -c "sleep 10; npm start" depends_on: - - redis - - db \ No newline at end of file + db: + condition: service_healthy + redis: + condition: service_healthy + worker: + condition: service_started \ No newline at end of file From 5dbdfa2648516b11902b7e322787abbfe4c01d6f Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 20 Feb 2024 22:44:00 -0300 Subject: [PATCH 13/43] =?UTF-8?q?Consultando=20dados=20no=20redis=20e=20fa?= =?UTF-8?q?zendo=20a=20convers=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 1 + controllers/currency_exchange.js | 31 +++++++++++++++++++++ index.js | 46 -------------------------------- routes/main.js | 15 ++++++----- server.js | 4 ++- services/currency_exchange.js | 43 ----------------------------- services/redis.js | 36 +++++++++++++++++++++++++ utils/formatter.js | 21 +++++++++++++++ 8 files changed, 100 insertions(+), 97 deletions(-) create mode 100644 controllers/currency_exchange.js delete mode 100644 index.js delete mode 100644 services/currency_exchange.js create mode 100755 services/redis.js create mode 100644 utils/formatter.js diff --git a/.dockerignore b/.dockerignore index 9b866a030..05a8d8714 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,6 +3,7 @@ npm-debug.log .DS_Store .git .gitignore +.env notes.txt mysql_data sql \ No newline at end of file diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js new file mode 100644 index 000000000..1fa499fc9 --- /dev/null +++ b/controllers/currency_exchange.js @@ -0,0 +1,31 @@ +const Redis = require('../services/redis'); +const { format2float, formatCurrency } = require('../utils/formatter'); + +const ExistsCurrency = async currency => { + const exists = await Redis.get(currency); + return exists ? true : false; +}; + +const ConvertCurrency = async (from, to, amount, cuurrency = 'USD') => { + let calc = 0; + + from = from.toUpperCase(); + to = to.toUpperCase(); + + if(from === to) calc = amount + else { + amount = format2float(amount); + from = await Redis.get(from); + to = await Redis.get(to); + calc = ( + (amount * to) / from + ); + } + + return formatCurrency(calc, cuurrency); +}; + +module.exports = { + ExistsCurrency, + ConvertCurrency +}; diff --git a/index.js b/index.js deleted file mode 100644 index d75f2f22e..000000000 --- a/index.js +++ /dev/null @@ -1,46 +0,0 @@ -const exchangeRates = { - USD: { - USD: 1 - }, - BRL: { - USD: 0.2016, - }, - EUR: { - USD: 1.08, - }, - BTC: { - USD: 47619.05, - }, - ETH: { - USD: 1538.46, - }, - ARS: { - USD: 0.0012 - } -}; - -const convertCurrency = (from, to, amount) => { - const currency = to; - from = exchangeRates[from]; - to = exchangeRates[to]; - const calc = ( - (amount * to.USD) / from.USD - ).toFixed(4); - - const formattedNumber = new Intl.NumberFormat('pt-BR', { style: 'currency', currency }).format(calc); - - return formattedNumber; -}; - - -console.log(convertCurrency('USD', 'USD', 100)); -console.log(convertCurrency('USD', 'BRL', 1)); -console.log(convertCurrency('USD', 'EUR', 100)); -console.log(convertCurrency('USD', 'BTC', 100)); -console.log(convertCurrency('USD', 'ETH', 100)); - -console.log(convertCurrency('BRL', 'EUR', 1)); -console.log(convertCurrency('EUR', 'BRL', 1)); - -console.log(convertCurrency('BRL', 'ARS', 1)); -console.log(convertCurrency('ARS', 'BRL', 1)); \ No newline at end of file diff --git a/routes/main.js b/routes/main.js index e70699c4c..ef0c74de9 100644 --- a/routes/main.js +++ b/routes/main.js @@ -4,13 +4,14 @@ const router = express.Router(); const { query, validationResult } = require('express-validator'); const { Response } = require('../utils/response'); -const { ConvertCurrency, ExistsCurrency } = require('../services/currency_exchange'); +const { ConvertCurrency, ExistsCurrency } = require('../controllers/currency_exchange'); +const { formatCurrency } = require('../utils/formatter'); router.get('/', - query('from').notEmpty().escape().isLength({ min: 3, max: 3 }).withMessage('Currency code \'from\' must be 3 characters long'), - query('to').notEmpty().escape().isLength({ min: 3, max: 3 }).withMessage('Currency code \'to\' must be 3 characters long'), - query('amount').notEmpty().escape().isFloat().withMessage('Amount must be a number'), - (req, res) => { + query('from').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'from\' must be 3 characters long'), + query('to').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'to\' must be 3 characters long'), + query('amount').notEmpty().escape().withMessage('Amount must be a number'), + async (req, res) => { const validateQuery = validationResult(req); if (!validateQuery.isEmpty()) { return Response(res, 400, validateQuery.array()); @@ -22,8 +23,8 @@ router.get('/', if(!ExistsCurrency(to)) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); const result = {}; - result[from] = amount; - result[to] = ConvertCurrency(from, to, amount); + result[from] = formatCurrency(amount, from); + result[to] = await ConvertCurrency(from, to, amount, to); Response(res, 200, result); } ); diff --git a/server.js b/server.js index c8342dedc..510ffc60e 100644 --- a/server.js +++ b/server.js @@ -1,9 +1,11 @@ +require('dotenv').config(); + const express = require('express'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); const app = express(); -const port = 3000; +const port = process.env.PORT || 3000; app.use(express.json()); app.use(helmet()); diff --git a/services/currency_exchange.js b/services/currency_exchange.js deleted file mode 100644 index 2fd07479f..000000000 --- a/services/currency_exchange.js +++ /dev/null @@ -1,43 +0,0 @@ -const exchangeRates = { - USD: { - USD: 1 - }, - BRL: { - USD: 0.2016, - }, - EUR: { - USD: 1.08, - }, - BTC: { - USD: 47619.05, - }, - ETH: { - USD: 1538.46, - }, - ARS: { - USD: 0.0012 - } -}; - -exports.ExistsCurrency = (currency) => { - return Object.keys(exchangeRates).includes(currency.toUpperCase()); -}; - -exports.ConvertCurrency = (from, to, amount) => { - let calc = 0; - - from = from.toUpperCase(); - to = to.toUpperCase(); - - if(from === to) calc = amount - else { - from = exchangeRates[from]; - to = exchangeRates[to]; - calc = ( - (amount * from.USD) / to.USD - ).toFixed(4); - } - - const formattedNumber = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD'}).format(calc); - return formattedNumber; -}; \ No newline at end of file diff --git a/services/redis.js b/services/redis.js new file mode 100755 index 000000000..30c805802 --- /dev/null +++ b/services/redis.js @@ -0,0 +1,36 @@ +const redis = require("redis"); + +module.exports = { + set: async (key, value) => { + try { + const redisClient = redis.createClient({ + url: process.env.REDIS_HOST_TLS, + password: process.env.REDIS_PASSWORD_TLS + }); + + await redisClient.connect(); + + await redisClient.set(key, value); + + await redisClient.disconnect(); + } catch (error) { + throw new Error(error); + } + }, + get: async (key) => { + try { + const redisClient = redis.createClient({ + url: process.env.REDIS_HOST_TLS, + password: process.env.REDIS_PASSWORD_TLS + }); + + await redisClient.connect(); + + const msg = await redisClient.get(key); + await redisClient.disconnect(); + return msg; + } catch (error) { + throw new Error(error); + } + } +}; diff --git a/utils/formatter.js b/utils/formatter.js new file mode 100644 index 000000000..a0c4802a1 --- /dev/null +++ b/utils/formatter.js @@ -0,0 +1,21 @@ + +const format2float = (amount) => { + amount = amount.toString(); + const commaIndex = amount.indexOf(','); + const dotIndex = amount.indexOf('.'); + + if(commaIndex < dotIndex) amount = amount.replace(/\,/g, ''); + else amount = amount.replace(/\./g, '').replace(',', '.'); + + return parseFloat(amount); +}; + +const formatCurrency = (amount, currency = 'USD') => { + amount = format2float(amount); + return amount.toLocaleString('pt-BR', { style: 'currency', currency: currency, minimumFractionDigits: 10 }); +}; + +module.exports = { + format2float, + formatCurrency +}; From ea91767e8e4ab90cb91e1bf116e91941906f8f1a Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Thu, 22 Feb 2024 17:56:02 -0300 Subject: [PATCH 14/43] =?UTF-8?q?FIX:=20tratando=20exce=C3=A7=C3=B5es=20ao?= =?UTF-8?q?=20realizar=20uma=20convers=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/currency_exchange.js | 31 ++++++++++++++++++------------- routes/main.js | 9 +++++++-- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 1fa499fc9..20a095797 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -7,22 +7,27 @@ const ExistsCurrency = async currency => { }; const ConvertCurrency = async (from, to, amount, cuurrency = 'USD') => { - let calc = 0; + try { - from = from.toUpperCase(); - to = to.toUpperCase(); + let calc = 0; - if(from === to) calc = amount - else { - amount = format2float(amount); - from = await Redis.get(from); - to = await Redis.get(to); - calc = ( - (amount * to) / from - ); - } + from = from.toUpperCase(); + to = to.toUpperCase(); + + if (from === to) calc = amount + else { + amount = format2float(amount); + from = await Redis.get(from); + to = await Redis.get(to); + calc = ( + (amount * to) / from + ); + } - return formatCurrency(calc, cuurrency); + return formatCurrency(calc, cuurrency); + } catch (error) { + throw new Error(error); + } }; module.exports = { diff --git a/routes/main.js b/routes/main.js index ef0c74de9..8878d3ae3 100644 --- a/routes/main.js +++ b/routes/main.js @@ -24,8 +24,13 @@ router.get('/', const result = {}; result[from] = formatCurrency(amount, from); - result[to] = await ConvertCurrency(from, to, amount, to); - Response(res, 200, result); + try { + result[to] = await ConvertCurrency(from, to, amount, to); + Response(res, 200, result); + } catch (error) { + console.error('routes/main.js ~ get ~ ERROR: ', error); + Response(res, 500, {message: error.message}); + } } ); From a441fca822e314a35e0c52df4b632a34de3cfed3 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Thu, 22 Feb 2024 18:56:15 -0300 Subject: [PATCH 15/43] =?UTF-8?q?Aumentando=20a=20atualiza=C3=A7=C3=A3o=20?= =?UTF-8?q?das=20moedas=20para=205=20minutos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- workers/currencys.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/currencys.js b/workers/currencys.js index 2da963463..26965dd87 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -81,7 +81,7 @@ const handler = async () => { } console.log() - await sleep(30 * 1000); + await sleep(300 * 1000); } }; From 72fa1a2cb90664f82b9d633240d9c4584e931b03 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Thu, 22 Feb 2024 18:56:56 -0300 Subject: [PATCH 16/43] Corrigindo o healthcheck do mysql para computadores mais fracos --- docker-compose.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 30e2106da..c53f2d53e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: ports: - "6379:6379" healthcheck: - test: ["CMD", "redis-cli", "ping"] + test: ["CMD-SHELL", "redis-cli ping"] interval: 10s retries: 3 timeout: 5s @@ -28,9 +28,10 @@ services: - ./sql/currencydb.sql:/docker-entrypoint-initdb.d/currencydb.sql healthcheck: test: ["CMD-SHELL", "mysqladmin ping -h db -u user -ppassword"] - interval: 10s - timeout: 12s - retries: 1 + interval: 30s + timeout: 30s + retries: 10 + start_interval: 30s worker: container_name: worker From 721278054ac705d2d59d8d8fb220c91e8bbecfb5 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Fri, 23 Feb 2024 18:15:53 -0300 Subject: [PATCH 17/43] =?UTF-8?q?REF:=20algoritmo=20de=20convers=C3=A3o=20?= =?UTF-8?q?de=20moeda=20FEAT:=20cadastrando=20moedas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/currency_exchange.js | 43 +++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 20a095797..5c134f938 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -1,5 +1,6 @@ const Redis = require('../services/redis'); const { format2float, formatCurrency } = require('../utils/formatter'); +const { CurrencysModel, CurrencysRaw } = require('../models/currencys'); const ExistsCurrency = async currency => { const exists = await Redis.get(currency); @@ -19,9 +20,9 @@ const ConvertCurrency = async (from, to, amount, cuurrency = 'USD') => { amount = format2float(amount); from = await Redis.get(from); to = await Redis.get(to); - calc = ( - (amount * to) / from - ); + + if(from > to) calc = (amount * to) / from; + else calc = (amount * to) * from; } return formatCurrency(calc, cuurrency); @@ -30,7 +31,41 @@ const ConvertCurrency = async (from, to, amount, cuurrency = 'USD') => { } }; +const NewCurrency = async (currency, ballast_usd) => { + const transaction = await CurrencysRaw.transaction(); + try { + + let existCurrency = await CurrencysModel.findOne({ where: { currency: currency.toUpperCase() } }); + if (existCurrency) { + return { + status: 409, + message: 'Currency already exists' + }; + } + + await CurrencysModel.create({ + currency: currency.toUpperCase(), + ballast_usd: ballast_usd, + createdAt: new Date(), + updatedAt: new Date() + }, { transaction }); + + await Redis.set(currency, ballast_usd); + + await transaction.commit(); + + return { + status: 201, + message: 'Currency created successfully' + }; + } catch (error) { + await transaction.rollback(); + throw new Error(error); + } +}; + module.exports = { ExistsCurrency, - ConvertCurrency + ConvertCurrency, + NewCurrency }; From d57410b1b5a930a7be924fa3d013fbcb501c2ab8 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Fri, 23 Feb 2024 18:16:25 -0300 Subject: [PATCH 18/43] Cadastrando nova moeda --- routes/main.js | 54 ++++++++++++++++++++++++++++++---------------- server.js | 8 +++++++ utils/formatter.js | 6 +++++- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/routes/main.js b/routes/main.js index 8878d3ae3..f6c842f25 100644 --- a/routes/main.js +++ b/routes/main.js @@ -1,30 +1,31 @@ const express = require('express'); const router = express.Router(); -const { query, validationResult } = require('express-validator'); +const { query, body, validationResult } = require('express-validator'); const { Response } = require('../utils/response'); -const { ConvertCurrency, ExistsCurrency } = require('../controllers/currency_exchange'); +const { ConvertCurrency, ExistsCurrency, NewCurrency } = require('../controllers/currency_exchange'); const { formatCurrency } = require('../utils/formatter'); router.get('/', - query('from').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'from\' must be 3 characters long'), - query('to').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'to\' must be 3 characters long'), + query('from').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'from\' must be 1 characters long'), + query('to').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'to\' must be 1 characters long'), query('amount').notEmpty().escape().withMessage('Amount must be a number'), async (req, res) => { - const validateQuery = validationResult(req); - if (!validateQuery.isEmpty()) { - return Response(res, 400, validateQuery.array()); - } + try { + const validateQuery = validationResult(req); + if (!validateQuery.isEmpty()) { + return Response(res, 400, validateQuery.array()); + } - const { from, to, amount } = req.query; + const { from, to, amount } = req.query; - if(!ExistsCurrency(from)) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); - if(!ExistsCurrency(to)) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); + if(!ExistsCurrency(from.toUpperCase())) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); + if(!ExistsCurrency(to.toUpperCase())) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); - const result = {}; - result[from] = formatCurrency(amount, from); - try { + const result = {}; + result[from] = formatCurrency(amount, from); + result[to] = await ConvertCurrency(from, to, amount, to); Response(res, 200, result); } catch (error) { @@ -34,10 +35,27 @@ router.get('/', } ); -router.post('/', (req, res) => { - // Handle the POST request for the root path - Response(res, 201); -}); +router.post('/', + body('currency').notEmpty().trim().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code must be 3 characters long'), + body('ballast_usd').notEmpty().trim().escape().isLength({ min: 1 }).isFloat().withMessage('Ballast USD must be a number'), + async (req, res) => { + try { + const validateBody = validationResult(req); + if (!validateBody.isEmpty()) { + return Response(res, 400, validateBody.array()); + } + + const { currency, ballast_usd } = req.body; + const result = await NewCurrency(currency, ballast_usd); + + Response(res, result.status, {message: result.message}); + } catch (error) { + console.error('routes/main.js ~ post ~ ERROR: ', error); + return Response(res, 500, {message: error.message}); + } + + } +); router.delete('/', (req, res) => { // Handle the DELETE request for the root path diff --git a/server.js b/server.js index 510ffc60e..329dd864b 100644 --- a/server.js +++ b/server.js @@ -10,6 +10,14 @@ const port = process.env.PORT || 3000; app.use(express.json()); app.use(helmet()); +app.use((err, req, res, next) => { + if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { + console.error(err); + return res.status(400).send({ message: err.message, body: err.body }); + } + next(); +}); + const limiter = rateLimit({ windowMs: 1000, limit: 10000, diff --git a/utils/formatter.js b/utils/formatter.js index a0c4802a1..218040a5b 100644 --- a/utils/formatter.js +++ b/utils/formatter.js @@ -12,7 +12,11 @@ const format2float = (amount) => { const formatCurrency = (amount, currency = 'USD') => { amount = format2float(amount); - return amount.toLocaleString('pt-BR', { style: 'currency', currency: currency, minimumFractionDigits: 10 }); + try { + return amount.toLocaleString('pt-BR', { style: 'currency', currency: currency, minimumFractionDigits: 10 }); + } catch (error) { + return "$ " + amount; + } }; module.exports = { From ae28a4d51be143d46dbaf2edda3cba3ec3b0e1dd Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Fri, 23 Feb 2024 18:30:09 -0300 Subject: [PATCH 19/43] Removendo moeda --- controllers/currency_exchange.js | 19 ++++++++++++++++++- routes/main.js | 31 ++++++++++++++++++++++++++----- services/redis.js | 7 +++++-- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 5c134f938..9786aee09 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -64,8 +64,25 @@ const NewCurrency = async (currency, ballast_usd) => { } }; +const DeleteCurrency = async currency => { + const transaction = await CurrencysRaw.transaction(); + try { + await CurrencysModel.destroy({ where: { currency: currency.toUpperCase() }, transaction }); + await Redis.set(currency, 0, 1); + await transaction.commit(); + return { + status: 200, + message: 'Currency deleted successfully' + }; + } catch (error) { + await transaction.rollback(); + throw new Error(error); + } +}; + module.exports = { ExistsCurrency, ConvertCurrency, - NewCurrency + NewCurrency, + DeleteCurrency }; diff --git a/routes/main.js b/routes/main.js index f6c842f25..5649e6ec5 100644 --- a/routes/main.js +++ b/routes/main.js @@ -4,7 +4,9 @@ const router = express.Router(); const { query, body, validationResult } = require('express-validator'); const { Response } = require('../utils/response'); -const { ConvertCurrency, ExistsCurrency, NewCurrency } = require('../controllers/currency_exchange'); +const { + ConvertCurrency, ExistsCurrency, NewCurrency, DeleteCurrency +} = require('../controllers/currency_exchange'); const { formatCurrency } = require('../utils/formatter'); router.get('/', @@ -57,9 +59,28 @@ router.post('/', } ); -router.delete('/', (req, res) => { - // Handle the DELETE request for the root path - Response(res, 204); -}); +router.delete('/', + query('currency').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'from\' must be 1 characters long'), + async (req, res) => { + try { + const validateBody = validationResult(req); + if (!validateBody.isEmpty()) { + return Response(res, 400, validateBody.array()); + } + + const { currency } = req.query; + + if(!ExistsCurrency(currency.toUpperCase())) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); + + await DeleteCurrency(currency.toUpperCase()); + + Response(res, 200, {message: 'Currency deleted successfully'}); + } catch (error) { + console.error('routes/main.js ~ delete ~ ERROR: ', error); + return Response(res, 500, {message: error.message}); + } + + } +); module.exports = router; \ No newline at end of file diff --git a/services/redis.js b/services/redis.js index 30c805802..ccf853979 100755 --- a/services/redis.js +++ b/services/redis.js @@ -1,7 +1,7 @@ const redis = require("redis"); module.exports = { - set: async (key, value) => { + set: async (key, value, ttl = null) => { try { const redisClient = redis.createClient({ url: process.env.REDIS_HOST_TLS, @@ -10,7 +10,10 @@ module.exports = { await redisClient.connect(); - await redisClient.set(key, value); + if(ttl) + await redisClient.set(key, value, { EX: ttl }); + else + await redisClient.set(key, value); await redisClient.disconnect(); } catch (error) { From be446bc6c8d3747a6cab8cd2e1817b9ad4b253ec Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 13:37:33 -0300 Subject: [PATCH 20/43] Atualizando banco de dados --- models/currencys.js | 14 ++++++++++--- sql/currencydb.sql | 2 ++ workers/currencys.js | 50 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/models/currencys.js b/models/currencys.js index 0d769c9c5..1726651b6 100644 --- a/models/currencys.js +++ b/models/currencys.js @@ -7,15 +7,23 @@ const CurrencysModel = sequelize.define('currencys', { type: Sequelize.INTEGER, autoIncrement: true, allowNull: false, - primaryKey: true, + primaryKey: true }, currency: { type: Sequelize.STRING(10), - allowNull: false, + allowNull: false }, ballast_usd: { type: Sequelize.FLOAT, - allowNull: false, + allowNull: false + }, + crypto: { + type: Sequelize.BOOLEAN, + allowNull: false + }, + imported: { + type: Sequelize.BOOLEAN, + allowNull: false }, createdAt: { type: Sequelize.DATE }, updatedAt: { type: Sequelize.DATE } diff --git a/sql/currencydb.sql b/sql/currencydb.sql index 265bebd84..4056896df 100644 --- a/sql/currencydb.sql +++ b/sql/currencydb.sql @@ -7,6 +7,8 @@ CREATE TABLE IF NOT EXISTS currencys ( id INT AUTO_INCREMENT PRIMARY KEY, currency VARCHAR(10) NOT NULL, ballast_usd FLOAT NOT NULL, + crypto BOOLEAN NOT NULL DEFAULT 0, + imported BOOLEAN NOT NULL DEFAULT 0, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updatedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); \ No newline at end of file diff --git a/workers/currencys.js b/workers/currencys.js index 26965dd87..3a1729f63 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -2,7 +2,7 @@ require('dotenv').config(); const axios = require('axios'); const redis = require("redis"); -const { CurrencysModel } = require('../models/currencys'); +const { CurrencysModel, CurrencysRaw } = require('../models/currencys'); const sleep = (ms) => { return new Promise((resolve) => { @@ -28,7 +28,6 @@ const setCurrencyRedis = async (currency, ballast_usd) => { await redisClient.connect(); - await redisClient.set(currency, ballast_usd); await redisClient.disconnect(); @@ -37,8 +36,19 @@ const setCurrencyRedis = async (currency, ballast_usd) => { } }; +const selectedCurrencys = [ + { currency:'USD', crypto: false }, + { currency:'BRL', crypto: false }, + { currency:'EUR', crypto: false }, + { currency:'BTC', crypto: true }, + { currency:'ETH', crypto: true } +]; + const handler = async () => { while (true) { + + const transaction = await CurrencysRaw.transaction(); + try { console.log('>> CONSULTANDO DADOS E ATUALIZANDO DATABASE'); @@ -49,22 +59,40 @@ const handler = async () => { console.log('>>> POPULATE DATABASE'); for (let i = 0; i < currencys.length; i++) { const currency = currencys[i]; + + const defaultCurrency = selectedCurrencys.find(f => f.currency == currency.toUpperCase()); + if(!defaultCurrency) continue; + await CurrencysModel.create({ - currency: currency.toUpperCase(), - ballast_usd: ballastDefault[currency], + currency: defaultCurrency.currency, + ballast_usd: ballastDefault[currency], + crypto: defaultCurrency.crypto, + imported: true, createdAt: new Date(), updatedAt: new Date() - }); + }, { transaction }); } console.log('>>> POPULATE DATABASE COMPLETED'); } else { console.log('>>> UPDATE'); for (let i = 0; i < currencys.length; i++) { const currency = currencys[i]; - await CurrencysModel.update({ - ballast_usd: ballastDefault[currency], - updatedAt: new Date() - }, { where: { currency: currency.toUpperCase() } }); + + const defaultCurrency = selectedCurrencys.find(f => f.currency == currency.toUpperCase()); + if(!defaultCurrency) continue; + + await CurrencysModel.update( + { + ballast_usd: ballastDefault[currency], + crypto: defaultCurrency.crypto, + imported: true, + updatedAt: new Date() + }, + { + where: { currency: currency.toUpperCase() }, + transaction + } + ); } console.log('>>> UPDATE COMPLETED'); } @@ -76,11 +104,13 @@ const handler = async () => { await setCurrencyRedis(currency.currency, currency.ballast_usd); } console.log('>> UPDATE REDIS DATABASE COMPLETED'); + + await transaction.commit(); } catch (error) { console.error('>> ERRO AO CONSULTAR DADOS', error); + await transaction.rollback(); } - console.log() await sleep(300 * 1000); } }; From b7ace14b330a58f3e5ef933ec5201c6c2293accc Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 13:44:15 -0300 Subject: [PATCH 21/43] =?UTF-8?q?Validando=20remo=C3=A7=C3=A3o=20de=20moed?= =?UTF-8?q?as=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/currency_exchange.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 9786aee09..765295d77 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -31,7 +31,7 @@ const ConvertCurrency = async (from, to, amount, cuurrency = 'USD') => { } }; -const NewCurrency = async (currency, ballast_usd) => { +const NewCurrency = async (currency, ballast_usd, crypto = false) => { const transaction = await CurrencysRaw.transaction(); try { @@ -46,6 +46,8 @@ const NewCurrency = async (currency, ballast_usd) => { await CurrencysModel.create({ currency: currency.toUpperCase(), ballast_usd: ballast_usd, + crypto, + imported: false, createdAt: new Date(), updatedAt: new Date() }, { transaction }); @@ -67,6 +69,24 @@ const NewCurrency = async (currency, ballast_usd) => { const DeleteCurrency = async currency => { const transaction = await CurrencysRaw.transaction(); try { + const curr = await CurrencysModel.findOne({ + where: { + currency: currency.toUpperCase() + } + }); + if(!curr) { + return { + status: 404, + message: 'Currency not found' + }; + } + if(curr.imported) { + return { + status: 403, + message: 'Not authorized delete default currencies' + }; + } + await CurrencysModel.destroy({ where: { currency: currency.toUpperCase() }, transaction }); await Redis.set(currency, 0, 1); await transaction.commit(); From 34689531e327aa9b7bd3ebe1e93af4a603d05525 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 15:45:13 -0300 Subject: [PATCH 22/43] =?UTF-8?q?REF:=20valida=C3=A7=C3=B5es,=20formata?= =?UTF-8?q?=C3=A7=C3=B5es=20e=20melhorando=20as=20requisi=C3=A7=C3=B5es=20?= =?UTF-8?q?para=20o=20redis?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/currency_exchange.js | 32 +++++++++++++++++--------------- routes/main.js | 27 ++++++++++++++++++++------- utils/formatter.js | 17 ++++++++++++----- workers/currencys.js | 10 +++++++++- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 765295d77..9c806ea93 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -2,30 +2,32 @@ const Redis = require('../services/redis'); const { format2float, formatCurrency } = require('../utils/formatter'); const { CurrencysModel, CurrencysRaw } = require('../models/currencys'); -const ExistsCurrency = async currency => { - const exists = await Redis.get(currency); - return exists ? true : false; +const GetCurrency = async currency => { + try { + const result = await Redis.get(currency); + return result; + } catch (error) { + throw new Error(error); + } }; -const ConvertCurrency = async (from, to, amount, cuurrency = 'USD') => { +const ConvertCurrency = async (from, to, amount) => { try { - let calc = 0; + let crypto = from.crypto || to.crypto; - from = from.toUpperCase(); - to = to.toUpperCase(); - - if (from === to) calc = amount + if (from.currency === to.currency) calc = amount; else { amount = format2float(amount); - from = await Redis.get(from); - to = await Redis.get(to); - if(from > to) calc = (amount * to) / from; - else calc = (amount * to) * from; + if(from.ballast_usd > to.ballast_usd) calc = (amount * to.ballast_usd) / from.ballast_usd; + else calc = (amount * to.ballast_usd) * from.ballast_usd; } - return formatCurrency(calc, cuurrency); + return { + from: formatCurrency(amount, from.currency, crypto), + to: formatCurrency(calc, to.currency, crypto) + } } catch (error) { throw new Error(error); } @@ -101,7 +103,7 @@ const DeleteCurrency = async currency => { }; module.exports = { - ExistsCurrency, + GetCurrency, ConvertCurrency, NewCurrency, DeleteCurrency diff --git a/routes/main.js b/routes/main.js index 5649e6ec5..8404e9bcf 100644 --- a/routes/main.js +++ b/routes/main.js @@ -5,7 +5,7 @@ const { query, body, validationResult } = require('express-validator'); const { Response } = require('../utils/response'); const { - ConvertCurrency, ExistsCurrency, NewCurrency, DeleteCurrency + ConvertCurrency, GetCurrency, NewCurrency, DeleteCurrency } = require('../controllers/currency_exchange'); const { formatCurrency } = require('../utils/formatter'); @@ -20,15 +20,28 @@ router.get('/', return Response(res, 400, validateQuery.array()); } - const { from, to, amount } = req.query; + let from = req.query.from; + let to = req.query.to; + const amount = req.query.amount; - if(!ExistsCurrency(from.toUpperCase())) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); - if(!ExistsCurrency(to.toUpperCase())) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); + let f = GetCurrency(from.toUpperCase()); + let t = GetCurrency(to.toUpperCase()); + + from = await f; + to = await t; + + if(!from) return Response(res, 400, {message: `Currency code 'from' ${req.query.from} not found`}); + if(!to) return Response(res, 400, {message: `Currency code 'to' ${req.query.to} not found`}); + + from = JSON.parse(from); + to = JSON.parse(to); + + const converted = await ConvertCurrency(from, to, amount); const result = {}; - result[from] = formatCurrency(amount, from); - - result[to] = await ConvertCurrency(from, to, amount, to); + result[from.currency] = converted.from; + result[to.currency] = converted.to; + Response(res, 200, result); } catch (error) { console.error('routes/main.js ~ get ~ ERROR: ', error); diff --git a/utils/formatter.js b/utils/formatter.js index 218040a5b..6d8601a3c 100644 --- a/utils/formatter.js +++ b/utils/formatter.js @@ -1,5 +1,5 @@ -const format2float = (amount) => { +const format2float = (amount, crypto = false) => { amount = amount.toString(); const commaIndex = amount.indexOf(','); const dotIndex = amount.indexOf('.'); @@ -7,13 +7,20 @@ const format2float = (amount) => { if(commaIndex < dotIndex) amount = amount.replace(/\,/g, ''); else amount = amount.replace(/\./g, '').replace(',', '.'); - return parseFloat(amount); + if(!crypto) amount = parseFloat(parseFloat(amount).toFixed(2)); + else amount = parseFloat(amount); + + return amount; }; -const formatCurrency = (amount, currency = 'USD') => { - amount = format2float(amount); +const formatCurrency = (amount, currency = 'USD', crypto = false) => { + amount = format2float(amount, crypto); + + let minimumFractionDigits = 2; + if(crypto) minimumFractionDigits = 10; + try { - return amount.toLocaleString('pt-BR', { style: 'currency', currency: currency, minimumFractionDigits: 10 }); + return amount.toLocaleString('pt-BR', { style: 'currency', currency: currency, minimumFractionDigits }); } catch (error) { return "$ " + amount; } diff --git a/workers/currencys.js b/workers/currencys.js index 3a1729f63..61db21f7d 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -101,7 +101,15 @@ const handler = async () => { const currencysInDB = await CurrencysModel.findAll(); for (let i = 0; i < currencysInDB.length; i++) { const currency = currencysInDB[i]; - await setCurrencyRedis(currency.currency, currency.ballast_usd); + await setCurrencyRedis(currency.currency, + JSON.stringify( + { + currency: currency.currency, + ballast_usd: currency.ballast_usd, + crypto: currency.crypto + } + ) + ); } console.log('>> UPDATE REDIS DATABASE COMPLETED'); From 980b1d381dbbd69b958922ed211a92f6f8288bfc Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 16:08:06 -0300 Subject: [PATCH 23/43] REF: adicionando propriedade crypto ao criar uma nova meda --- controllers/currency_exchange.js | 1 + routes/main.js | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 9c806ea93..85a5b7a01 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -15,6 +15,7 @@ const ConvertCurrency = async (from, to, amount) => { try { let calc = 0; let crypto = from.crypto || to.crypto; + crypto = typeof crypto === 'boolean' ? crypto : crypto === 'true'; if (from.currency === to.currency) calc = amount; else { diff --git a/routes/main.js b/routes/main.js index 8404e9bcf..795ed458b 100644 --- a/routes/main.js +++ b/routes/main.js @@ -53,6 +53,7 @@ router.get('/', router.post('/', body('currency').notEmpty().trim().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code must be 3 characters long'), body('ballast_usd').notEmpty().trim().escape().isLength({ min: 1 }).isFloat().withMessage('Ballast USD must be a number'), + body('crypto').optional().trim().escape().isLength({ min: 1 }).isBoolean().withMessage('Crypto must be a boolean'), async (req, res) => { try { const validateBody = validationResult(req); @@ -60,8 +61,16 @@ router.post('/', return Response(res, 400, validateBody.array()); } - const { currency, ballast_usd } = req.body; - const result = await NewCurrency(currency, ballast_usd); + const currency = req.body.currency; + const ballast_usd = req.body.ballast_usd; + let crypto = req.body.crypto; + crypto = typeof crypto === 'undefined' ? false : crypto === 'true'; + + const result = await NewCurrency( + currency, + ballast_usd, + typeof crypto === 'undefined' ? false : crypto + ); Response(res, result.status, {message: result.message}); } catch (error) { From 9376735f5488318bda3854977116d56791f14578 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 16:13:05 -0300 Subject: [PATCH 24/43] REF: delete para o novo formato do redis --- controllers/currency_exchange.js | 2 +- routes/main.js | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 85a5b7a01..706e25293 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -79,7 +79,7 @@ const DeleteCurrency = async currency => { }); if(!curr) { return { - status: 404, + status: 400, message: 'Currency not found' }; } diff --git a/routes/main.js b/routes/main.js index 795ed458b..17756ccf8 100644 --- a/routes/main.js +++ b/routes/main.js @@ -92,11 +92,13 @@ router.delete('/', const { currency } = req.query; - if(!ExistsCurrency(currency.toUpperCase())) return Response(res, 400, {message: `Currency code 'from' ${from} not found`}); + const existCurrency = await GetCurrency(currency.toUpperCase()); - await DeleteCurrency(currency.toUpperCase()); + if(!existCurrency) return Response(res, 400, {message: `Currency code not found`}); - Response(res, 200, {message: 'Currency deleted successfully'}); + const result = await DeleteCurrency(currency.toUpperCase()); + + Response(res, result.status, {message: result.message}); } catch (error) { console.error('routes/main.js ~ delete ~ ERROR: ', error); return Response(res, 500, {message: error.message}); From 7f57f93a88de6a2acf2e3b03bea3bccacbc3263c Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 17:28:12 -0300 Subject: [PATCH 25/43] REF: status code 404 --- controllers/currency_exchange.js | 2 +- routes/main.js | 4 ++-- workers/currencys.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 706e25293..85a5b7a01 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -79,7 +79,7 @@ const DeleteCurrency = async currency => { }); if(!curr) { return { - status: 400, + status: 404, message: 'Currency not found' }; } diff --git a/routes/main.js b/routes/main.js index 17756ccf8..0bea986f8 100644 --- a/routes/main.js +++ b/routes/main.js @@ -30,8 +30,8 @@ router.get('/', from = await f; to = await t; - if(!from) return Response(res, 400, {message: `Currency code 'from' ${req.query.from} not found`}); - if(!to) return Response(res, 400, {message: `Currency code 'to' ${req.query.to} not found`}); + if(!from) return Response(res, 404, {message: `Currency code 'from' ${req.query.from} not found`}); + if(!to) return Response(res, 404, {message: `Currency code 'to' ${req.query.to} not found`}); from = JSON.parse(from); to = JSON.parse(to); diff --git a/workers/currencys.js b/workers/currencys.js index 61db21f7d..b9a26b233 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -19,7 +19,7 @@ const getBallastDefault = async () => { } } -const setCurrencyRedis = async (currency, ballast_usd) => { +const setCurrencyRedis = async (key, value) => { try { const redisClient = redis.createClient({ url: process.env.REDIS_HOST_TLS, @@ -28,7 +28,7 @@ const setCurrencyRedis = async (currency, ballast_usd) => { await redisClient.connect(); - await redisClient.set(currency, ballast_usd); + await redisClient.set(key, value); await redisClient.disconnect(); } catch (error) { From 98135f969bcc09ca7cfebfb78edfd71062fb02d7 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 17:54:17 -0300 Subject: [PATCH 26/43] =?UTF-8?q?Adicionando=20teste=20unit=C3=A1rio=20com?= =?UTF-8?q?=20jest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 5 +++- tests/convert_currency.test.js | 55 ++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/convert_currency.test.js diff --git a/package.json b/package.json index 1cf729537..bfab18c17 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "[[English](README.md) | [Portuguese](README.pt.md)]", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "jest", "start": "node server.js", "debug": "nodemon server.js" }, @@ -29,5 +29,8 @@ "mysql2": "^3.9.1", "redis": "^4.6.13", "sequelize": "^6.37.1" + }, + "devDependencies": { + "jest": "^29.7.0" } } diff --git a/tests/convert_currency.test.js b/tests/convert_currency.test.js new file mode 100644 index 000000000..6603847ad --- /dev/null +++ b/tests/convert_currency.test.js @@ -0,0 +1,55 @@ +const { ConvertCurrency } = require('../controllers/currency_exchange'); + +test('BRL: It should show the original monetary value of 100,00', async () => { + const result = await ConvertCurrency( + {"currency":"BRL","ballast_usd":4.93119,"crypto":false}, + {"currency":"USD","ballast_usd":1,"crypto":false}, + 100 + ); + expect(result.from.split(/\s/)[1]).toBe('100,00'); +}); + +test('BRL to USD: It should show the destination monetary value of 20,28', async () => { + const result = await ConvertCurrency( + {"currency":"BRL","ballast_usd":4.93119,"crypto":false}, + {"currency":"USD","ballast_usd":1,"crypto":false}, + 100 + ); + expect(result.to.split(/\s/)[1]).toBe('20,28'); +}); + +test('GTA: It should show the original monetary value of 1.250.00,00', async () => { + const result = await ConvertCurrency( + {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"USD","ballast_usd":1,"crypto":false}, + 1250000 + ); + expect(result.from.split(/\s/)[1]).toBe('1.250.000,00'); +}); + +test('GTA to USD: It should show the original monetary value of 16,93', async () => { + const result = await ConvertCurrency( + {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"USD","ballast_usd":1,"crypto":false}, + 1250000 + ); + expect(result.to.split(/\s/)[1]).toBe('16,93'); +}); + +test('GTA: It should show the original monetary value of 1.250.00,00', async () => { + const result = await ConvertCurrency( + {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"BRL","ballast_usd":4.93119,"crypto":false}, + 1250000 + ); + expect(result.from.split(/\s/)[1]).toBe('1.250.000,00'); +}); + +test('GTA to BRL: It should show the original monetary value of 83,49', async () => { + const result = await ConvertCurrency( + {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"BRL","ballast_usd":4.93119,"crypto":false}, + 1250000 + ); + expect(result.to.split(/\s/)[1]).toBe('83,49'); +}); \ No newline at end of file From 4316bfb351bbf14811633a889d41b1efcd80d5a0 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 18:09:51 -0300 Subject: [PATCH 27/43] Adicionando rota para swagger --- docs/swagger/swagger.json | 339 ++++++++++++++++++++++++++++++++++++++ docs/swagger/swagger.yml | 212 ++++++++++++++++++++++++ package.json | 3 +- server.js | 4 + 4 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 docs/swagger/swagger.json create mode 100644 docs/swagger/swagger.yml diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json new file mode 100644 index 000000000..3a6b6abfd --- /dev/null +++ b/docs/swagger/swagger.json @@ -0,0 +1,339 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Challenge-Bravo", + "description": "API for currency conversion.", + "contact": { + "name": "Leonardo Neves ", + "email": "neves.leo@outlook.com" + }, + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost:3000/" + } + ], + "tags": [ + { + "name": "/" + } + ], + "paths": { + "/": { + "get": { + "tags": [ + "/" + ], + "summary": "Convert value between two currencies", + "description": "Convert value between two currencies", + "parameters": [ + { + "name": "from", + "in": "query", + "description": "Currency origin", + "required": true, + "schema": { + "type": "string", + "default": "BRL" + } + }, + { + "name": "to", + "in": "query", + "description": "Currency destination", + "required": true, + "schema": { + "type": "string", + "default": "USD" + } + }, + { + "name": "amount", + "in": "query", + "description": "Amount to convert", + "required": true, + "schema": { + "type": "number", + "default": 1 + } + } + ], + "responses": { + "200": { + "description": "Ok" + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationResponseErrorArray" + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Currency code not found" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Server internal error" + } + } + } + } + } + } + } + }, + "post": { + "tags": [ + "/" + ], + "summary": "Add a new currency", + "description": "Add a new currency", + "requestBody": { + "description": "Create a new pet in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Currency" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Currency created successfully" + } + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationResponseErrorArray" + } + } + } + }, + "409": { + "description": "Conflict", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Currency already exists" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Server internal error" + } + } + } + } + } + } + } + }, + "delete": { + "tags": [ + "/" + ], + "summary": "Delete currency", + "description": "Delete currency", + "parameters": [ + { + "name": "currency", + "in": "query", + "description": "Currency that needs to be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "string" + } + } + ], + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Currency deleted successfully" + } + } + } + } + } + }, + "400": { + "description": "Bad Request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidationResponseErrorArray" + } + } + } + }, + "403": { + "description": "Forbidden", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Not authorized delete default currencies" + } + } + } + } + } + }, + "404": { + "description": "Not found", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Currency code not found" + } + } + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "properties": { + "message": { + "type": "string", + "default": "Server internal error" + } + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "Currency": { + "required": [ + "currency", + "ballast_usd" + ], + "type": "object", + "properties": { + "currency": { + "type": "string", + "example": "GTA" + }, + "ballast_usd": { + "type": "number", + "example": 0.000013544 + }, + "crypto": { + "type": "boolean", + "example": false + } + } + }, + "ValidationResponseError": { + "type": "object", + "properties": { + "type": { + "type": "string", + "example": "field" + }, + "msg": { + "type": "string", + "example": "Invalid value" + }, + "path": { + "type": "string", + "example": "currency" + }, + "location": { + "type": "string", + "example": "query" + } + } + }, + "ValidationResponseErrorArray": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationResponseError" + } + } + }, + "requestBodies": { + "Currency": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Currency" + } + } + } + } + } + } +} \ No newline at end of file diff --git a/docs/swagger/swagger.yml b/docs/swagger/swagger.yml new file mode 100644 index 000000000..6eb5ee05e --- /dev/null +++ b/docs/swagger/swagger.yml @@ -0,0 +1,212 @@ +openapi: 3.0.3 +info: + title: Challenge-Bravo + description: |- + API for currency conversion. + contact: + email: neves.leo@outlook.com + version: 1.0.0 +servers: + - url: http://localhost:3000/ +tags: + - name: / +paths: + /: + get: + tags: + - / + summary: Convert value between two currencies + description: Convert value between two currencies + parameters: + - name: from + in: query + description: Currency origin + required: true + schema: + type: string + default: BRL + - name: to + in: query + description: Currency destination + required: true + schema: + type: string + default: USD + - name: amount + in: query + description: Amount to convert + required: true + schema: + type: number + default: 1 + responses: + '200': + description: Ok + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationResponseErrorArray' + '404': + description: Not found + content: + application/json: + schema: + properties: + message: + type: string + default: Currency code not found + '500': + description: Internal Server Error + content: + application/json: + schema: + properties: + message: + type: string + default: Server internal error + post: + tags: + - / + summary: Add a new currency + description: Add a new currency + requestBody: + description: Create a new pet in the store + content: + application/json: + schema: + $ref: '#/components/schemas/Currency' + required: true + responses: + '201': + description: Created + content: + application/json: + schema: + properties: + message: + type: string + default: Currency created successfully + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationResponseErrorArray' + '409': + description: Conflict + content: + application/json: + schema: + properties: + message: + type: string + default: Currency already exists + '500': + description: Internal Server Error + content: + application/json: + schema: + properties: + message: + type: string + default: Server internal error + delete: + tags: + - / + summary: Delete currency + description: Delete currency + parameters: + - name: currency + in: query + description: Currency that needs to be deleted + required: true + schema: + type: integer + format: string + responses: + '200': + description: Ok + content: + application/json: + schema: + properties: + message: + type: string + default: Currency deleted successfully + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/ValidationResponseErrorArray' + '403': + description: Forbidden + content: + application/json: + schema: + properties: + message: + type: string + default: Not authorized delete default currencies + '404': + description: Not found + content: + application/json: + schema: + properties: + message: + type: string + default: Currency code not found + '500': + description: Internal Server Error + content: + application/json: + schema: + properties: + message: + type: string + default: Server internal error +components: + schemas: + Currency: + required: + - currency + - ballast_usd + type: object + properties: + currency: + type: string + example: GTA + ballast_usd: + type: number + example: 0.000013544 + crypto: + type: boolean + example: false + ValidationResponseError: + type: object + properties: + type: + type: string + example: field + msg: + type: string + example: Invalid value + path: + type: string + example: currency + location: + type: string + example: query + ValidationResponseErrorArray: + type: array + items: + $ref: '#/components/schemas/ValidationResponseError' + requestBodies: + Currency: + content: + application/json: + schema: + $ref: '#/components/schemas/Currency' \ No newline at end of file diff --git a/package.json b/package.json index bfab18c17..7660a1cfe 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "helmet": "^7.1.0", "mysql2": "^3.9.1", "redis": "^4.6.13", - "sequelize": "^6.37.1" + "sequelize": "^6.37.1", + "swagger-ui-express": "^5.0.0" }, "devDependencies": { "jest": "^29.7.0" diff --git a/server.js b/server.js index 329dd864b..5fab28067 100644 --- a/server.js +++ b/server.js @@ -3,6 +3,8 @@ require('dotenv').config(); const express = require('express'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); +const swaggerUi = require('swagger-ui-express'); +const swaggerDocument = require('./docs/swagger/swagger.json'); const app = express(); const port = process.env.PORT || 3000; @@ -26,6 +28,8 @@ const limiter = rateLimit({ }); app.use(limiter); +app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {explorer: true})); + const mainRoutes = require('./routes/main'); app.use('/', mainRoutes); From 882e635ec17e6530eee46f7dc6ec70dc3c7977f7 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 18:19:21 -0300 Subject: [PATCH 28/43] FIX: corrigindo swagger parameter string --- docs/swagger/swagger.json | 2 +- docs/swagger/swagger.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/swagger/swagger.json b/docs/swagger/swagger.json index 3a6b6abfd..ec6702777 100644 --- a/docs/swagger/swagger.json +++ b/docs/swagger/swagger.json @@ -193,7 +193,7 @@ "description": "Currency that needs to be deleted", "required": true, "schema": { - "type": "integer", + "type": "string", "format": "string" } } diff --git a/docs/swagger/swagger.yml b/docs/swagger/swagger.yml index 6eb5ee05e..b6d8b51f5 100644 --- a/docs/swagger/swagger.yml +++ b/docs/swagger/swagger.yml @@ -123,7 +123,7 @@ paths: description: Currency that needs to be deleted required: true schema: - type: integer + type: string format: string responses: '200': From 14b4986306246a4d10ae19cdaa8f752108488daa Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Sat, 24 Feb 2024 18:25:52 -0300 Subject: [PATCH 29/43] FIX: corrigindo novo formato do redis ao criar uma nova moeda --- controllers/currency_exchange.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index 85a5b7a01..e26bb97f0 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -55,7 +55,13 @@ const NewCurrency = async (currency, ballast_usd, crypto = false) => { updatedAt: new Date() }, { transaction }); - await Redis.set(currency, ballast_usd); + await Redis.set(currency, JSON.stringify( + { + currency: currency.toUpperCase(), + ballast_usd, + crypto + } + )); await transaction.commit(); From 0d2f367611284fa04dffacb004039531914b831f Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 26 Feb 2024 23:50:30 -0300 Subject: [PATCH 30/43] README e desenhos com drawio --- README.md | 99 +++++++++++--------------- README.pt.md | 82 ---------------------- ca.jpg | Bin 24785 -> 0 bytes docs/Architecture.drawio | 71 +++++++++++++++++++ docs/Architecture.png | Bin 0 -> 72596 bytes docs/Sequence-API.drawio | 77 ++++++++++++++++++++ docs/Sequence-API.png | Bin 0 -> 25668 bytes docs/Sequence-Worker.drawio | 136 ++++++++++++++++++++++++++++++++++++ docs/Sequence-Worker.png | Bin 0 -> 43762 bytes pull-request.txt | 3 - tests/stress_test.yml | 2 +- 11 files changed, 325 insertions(+), 145 deletions(-) delete mode 100644 README.pt.md delete mode 100644 ca.jpg create mode 100644 docs/Architecture.drawio create mode 100644 docs/Architecture.png create mode 100644 docs/Sequence-API.drawio create mode 100644 docs/Sequence-API.png create mode 100644 docs/Sequence-Worker.drawio create mode 100644 docs/Sequence-Worker.png delete mode 100644 pull-request.txt diff --git a/README.md b/README.md index 22af01577..c30c978e7 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,63 @@ # Hurb Bravo Challenge -[[English](README.md) | [Portuguese](README.pt.md)] +Para esse desafio foi usada a seguinte arquitetura: -Build an API, which responds to JSON, for currency conversion. It must have a backing currency (USD) and make conversions between different currencies with **real and live values**. - -The API must convert between the following currencies: - -- USD -- BRL -- EUR -- BTC -- ETH +

+ Architecture +

-Other coins could be added as usage. +## Diagramas de sequência -Ex: USD to BRL, USD to BTC, ETH to BRL, etc... +###Worker -The request must receive as parameters: The source currency, the amount to be converted and the final currency. +

+ [UML] Sequence - Worker +

-Ex: `?from=BTC&to=EUR&amount=123.45` +###API -Also build an endpoint to add and remove API supported currencies using HTTP verbs. +

+ [UML] Sequence - Worker +

-The API must support conversion between FIAT, crypto and fictitious. Example: BRL->HURB, HURB->ETH +##Contâiners (docker): -"Currency is the means by which monetary transactions are effected." (Wikipedia, 2021). +###db -Therefore, it is possible to imagine that new coins come into existence or cease to exist, it is also possible to imagine fictitious coins such as Dungeons & Dragons coins being used in these transactions, such as how much is a Gold Piece (Dungeons & Dragons) in Real or how much is the GTA$1 in Real. +Banco de dados MySQL responsável por persistir os dados em um estado sólido. -Let's consider the PSN quote where GTA$1,250,000.00 cost R$83.50 we clearly have a relationship between the currencies, so it is possible to create a quote. (Playstation Store, 2021). +###redis -Ref: -Wikipedia [Institutional Website]. Available at: . Accessed on: 28 April 2021. -Playstation Store [Virtual Store]. Available at: . Accessed on: 28 April 2021. +Banco de dados em memória (volátil) que dará melhor desempenho e velocidade em nosso response. -You can use any programming language for the challenge. Below is the list of languages ​​that we here at Hurb have more affinity: +###worker -- JavaScript (NodeJS) -- Python -- Go -- Ruby -- C++ -- PHP +Serviço responsável por manter o db e o redis sempre atualizados com dados reais. O tempo de atualização default é de 5 minutos, porém esse parâmetro pode ser alterado atualizando o docker-compose.yml -## Requirements +###app -- Fork this challenge and create your project (or workspace) using your version of that repository, as soon as you finish the challenge, submit a _pull request_. - - If you have any reason not to submit a _pull request_, create a private repository on Github, do every challenge on the **main** branch and don't forget to fill in the `pull-request.txt` file. As soon as you finish your development, add the user `automator-hurb` to your repository as a contributor and make it available for at least 30 days. **Do not add the `automator-hurb` until development is complete.** - - If you have any problem creating the private repository, at the end of the challenge fill in the file called `pull-request.txt`, compress the project folder - including the `.git` folder - and send it to us by email. -- The code needs to run on macOS or Ubuntu (preferably as a Docker container) -- To run your code, all you need to do is run the following commands: - - git clone \$your-fork - - cd \$your-fork - - command to install dependencies - - command to run the application -- The API can be written with or without the help of _frameworks_ - - If you choose to use a _framework_ that results in _boilerplate code_, mark in the README which piece of code was written by you. The more code you make, the more content we will have to rate. -- The API needs to support a volume of 1000 requests per second in a stress test. -- The API needs to include real and current quotes through integration with public currency quote APIs +Nosso servidor backend escrito em NodeJS utilizando o framework [ExpressJS](https://expressjs.com/pt-br/). -## Evaluation criteria -- **Organization of code**: Separation of modules, view and model, back-end and front-end -- **Clarity**: Does the README explain briefly what the problem is and how can I run the application? -- **Assertiveness**: Is the application doing what is expected? If something is missing, does the README explain why? -- **Code readability** (including comments) -- **Security**: Are there any clear vulnerabilities? -- **Test coverage** (We don't expect full coverage) -- **History of commits** (structure and quality) -- **UX**: Is the interface user-friendly and self-explanatory? Is the API intuitive? -- **Technical choices**: Is the choice of libraries, database, architecture, etc. the best choice for the application? +##Executando o projeto -## Doubts +Para execução do projeto basta executarmos o comando: +```sh +docker compose up +``` -Any questions you may have, check the [_issues_](https://github.com/HurbCom/challenge-bravo/issues) to see if someone hasn't already and if you can't find your answer, open one yourself. new issue! +A ordem de execução será a seguinte: +1. redis +2. mysql +3. worker +4. app (com delay de 10 segundos, para que dê tempo do worker realizar as atualizações) -Godspeed! ;) +Para encerramento dos serviços: +```sh +docker compose down +``` -

- Challange accepted -

+Se precisar alterar o fonte, envs, ou algum outro arquivo, para rebuildar os serviços, basta executar o comando abaixo: +```sh +docker compose up --build +``` diff --git a/README.pt.md b/README.pt.md deleted file mode 100644 index 0159db9f0..000000000 --- a/README.pt.md +++ /dev/null @@ -1,82 +0,0 @@ -# Hurb Desafio Bravo - -[[English](README.md) | [Português](README.pt.md)] - -Construa uma API, que responda JSON, para conversão monetária. Ela deve ter uma moeda de lastro (USD) e fazer conversões entre diferentes moedas com **cotações de verdade e atuais**. - -A API precisa converter entre as seguintes moedas: - -- USD -- BRL -- EUR -- BTC -- ETH - -Outras moedas podem ser adicionadas conforme o uso. - -Ex: USD para BRL, USD para BTC, ETH para BRL, etc... - -A requisição deve receber como parâmetros: A moeda de origem, o valor a ser convertido e a moeda final. - -Ex: `?from=BTC&to=EUR&amount=123.45` - -Construa também um endpoint para adicionar e remover moedas suportadas pela API, usando os verbos HTTP. - -A API deve suportar conversão entre moedas fiduciárias, crypto e fictícias. Exemplo: BRL->HURB, HURB->ETH - -"Moeda é o meio pelo qual são efetuadas as transações monetárias." (Wikipedia, 2021). - -Sendo assim, é possível imaginar que novas moedas passem a existir ou deixem de existir, é possível também imaginar moedas fictícias como as de Dungeons & Dragons sendo utilizadas nestas transações, como por exemplo quanto vale uma Peça de Ouro (D&D) em Real ou quanto vale a GTA$ 1 em Real. - -Vamos considerar a cotação da PSN onde GTA$ 1.250.000,00 custam R$ 83,50 claramente temos uma relação entre as moedas, logo é possível criar uma cotação. (Playstation Store, 2021). - -Ref: -Wikipedia [Site Institucional]. Disponível em: . Acesso em: 28 abril 2021. -Playstation Store [Loja Virtual]. Disponível em: . Acesso em: 28 abril 2021. - -Você pode usar qualquer linguagem de programação para o desafio. Abaixo a lista de linguagens que nós aqui do Hurb temos mais afinidade: - -- JavaScript (NodeJS) -- Python -- Go -- Ruby -- C++ -- PHP - -## Requisitos - -- Forkar esse desafio e criar o seu projeto (ou workspace) usando a sua versão desse repositório, tão logo acabe o desafio, submeta um _pull request_. - - Caso você tenha algum motivo para não submeter um _pull request_, crie um repositório privado no Github, faça todo desafio na branch **main** e não se esqueça de preencher o arquivo `pull-request.txt`. Tão logo termine seu desenvolvimento, adicione como colaborador o usuário `automator-hurb` no seu repositório e o deixe disponível por pelo menos 30 dias. **Não adicione o `automator-hurb` antes do término do desenvolvimento.** - - Caso você tenha algum problema para criar o repositório privado, ao término do desafio preencha o arquivo chamado `pull-request.txt`, comprima a pasta do projeto - incluindo a pasta `.git` - e nos envie por email. -- O código precisa rodar em macOS ou Ubuntu (preferencialmente como container Docker) -- Para executar seu código, deve ser preciso apenas rodar os seguintes comandos: - - git clone \$seu-fork - - cd \$seu-fork - - comando para instalar dependências - - comando para executar a aplicação -- A API pode ser escrita com ou sem a ajuda de _frameworks_ - - Se optar por usar um _framework_ que resulte em _boilerplate code_, assinale no README qual pedaço de código foi escrito por você. Quanto mais código feito por você, mais conteúdo teremos para avaliar. -- A API precisa suportar um volume de 1000 requisições por segundo em um teste de estresse. -- A API precisa contemplar cotações de verdade e atuais através de integração com APIs públicas de cotação de moedas - -## Critério de avaliação - -- **Organização do código**: Separação de módulos, view e model, back-end e front-end -- **Clareza**: O README explica de forma resumida qual é o problema e como pode rodar a aplicação? -- **Assertividade**: A aplicação está fazendo o que é esperado? Se tem algo faltando, o README explica o porquê? -- **Legibilidade do código** (incluindo comentários) -- **Segurança**: Existe alguma vulnerabilidade clara? -- **Cobertura de testes** (Não esperamos cobertura completa) -- **Histórico de commits** (estrutura e qualidade) -- **UX**: A interface é de fácil uso e auto-explicativa? A API é intuitiva? -- **Escolhas técnicas**: A escolha das bibliotecas, banco de dados, arquitetura, etc, é a melhor escolha para a aplicação? - -## Dúvidas - -Quaisquer dúvidas que você venha a ter, consulte as [_issues_](https://github.com/HurbCom/challenge-bravo/issues) para ver se alguém já não a fez e caso você não ache sua resposta, abra você mesmo uma nova issue! - -Boa sorte e boa viagem! ;) - -

- Challange accepted -

diff --git a/ca.jpg b/ca.jpg deleted file mode 100644 index 939944346528b7a3c64b241c6203020c0f83d294..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24785 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/Architecture.png b/docs/Architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..0ab3b3d5f2c131711db8d6596f67753fdd6ac7c2 GIT binary patch literal 72596 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 B59j~@ literal 0 HcmV?d00001 diff --git a/docs/Sequence-API.drawio b/docs/Sequence-API.drawio new file mode 100644 index 000000000..7df0c0d47 --- /dev/null +++ b/docs/Sequence-API.drawio @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/Sequence-API.png b/docs/Sequence-API.png new file mode 100644 index 0000000000000000000000000000000000000000..95c51e2f72651ca227bcc1526ae69176b03f6454 GIT binary patch literal 25668 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 GaPuE>w7Z`G literal 0 HcmV?d00001 diff --git a/docs/Sequence-Worker.drawio b/docs/Sequence-Worker.drawio new file mode 100644 index 000000000..43541924e --- /dev/null +++ b/docs/Sequence-Worker.drawio @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/Sequence-Worker.png b/docs/Sequence-Worker.png new file mode 100644 index 0000000000000000000000000000000000000000..42072380b3d6d327e4f0729994aed05f4a3e3c02 GIT binary patch literal 43762 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 IrF7osU$5%C)c^nh literal 0 HcmV?d00001 diff --git a/pull-request.txt b/pull-request.txt deleted file mode 100644 index 4eae37418..000000000 --- a/pull-request.txt +++ /dev/null @@ -1,3 +0,0 @@ -Your name: ___ -Your Github homepage: ___ -Original challenge URL: http://github.com/hurbcom/challenge-___ diff --git a/tests/stress_test.yml b/tests/stress_test.yml index 86df89cb6..aae4205da 100644 --- a/tests/stress_test.yml +++ b/tests/stress_test.yml @@ -1,5 +1,5 @@ config: - target: 'http://localhost:3000' + target: 'http://192.168.1.2:3000' phases: # - duration: 10 # arrivalRate: 10 From b5ee5efca493005e0b0874f988fc256025e52075 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 26 Feb 2024 23:52:44 -0300 Subject: [PATCH 31/43] REF: readme --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c30c978e7..fa0dc62a8 100644 --- a/README.md +++ b/README.md @@ -8,38 +8,38 @@ Para esse desafio foi usada a seguinte arquitetura: ## Diagramas de sequência -###Worker +### Worker

[UML] Sequence - Worker

-###API +### API

[UML] Sequence - Worker

-##Contâiners (docker): +## Contâiners (docker): -###db +### db Banco de dados MySQL responsável por persistir os dados em um estado sólido. -###redis +### redis Banco de dados em memória (volátil) que dará melhor desempenho e velocidade em nosso response. -###worker +### worker Serviço responsável por manter o db e o redis sempre atualizados com dados reais. O tempo de atualização default é de 5 minutos, porém esse parâmetro pode ser alterado atualizando o docker-compose.yml -###app +### app Nosso servidor backend escrito em NodeJS utilizando o framework [ExpressJS](https://expressjs.com/pt-br/). -##Executando o projeto +## Executando o projeto Para execução do projeto basta executarmos o comando: ```sh From c944c0c41f3f458ddb49b6dc771d429c5e86411b Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Mon, 26 Feb 2024 23:54:41 -0300 Subject: [PATCH 32/43] REF: readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fa0dc62a8..bbdb1f5a1 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Nosso servidor backend escrito em NodeJS utilizando o framework [ExpressJS](http ## Executando o projeto Para execução do projeto basta executarmos o comando: -```sh +```shell docker compose up ``` @@ -53,11 +53,11 @@ A ordem de execução será a seguinte: 4. app (com delay de 10 segundos, para que dê tempo do worker realizar as atualizações) Para encerramento dos serviços: -```sh +```shell docker compose down ``` Se precisar alterar o fonte, envs, ou algum outro arquivo, para rebuildar os serviços, basta executar o comando abaixo: -```sh +```shell docker compose up --build ``` From dacffe5958ff6a59aae7fa615fed388e1ecd3325 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 27 Feb 2024 00:32:08 -0300 Subject: [PATCH 33/43] Melhorando o readme --- README.md | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/README.md b/README.md index bbdb1f5a1..79083d782 100644 --- a/README.md +++ b/README.md @@ -61,3 +61,91 @@ Se precisar alterar o fonte, envs, ou algum outro arquivo, para rebuildar os ser ```shell docker compose up --build ``` + +## API + +O projeto estará executando na porta padrão 3000. + +O endereço padrão para testes: +``` +http://localhost:3000/ +``` + +Documentação da API foi escrita utilizando a [OpenAPI 3.0 Specification](https://swagger.io/docs/specification/about/) e se encontra no endereço: +``` +http://localhost:3000/api-docs/ +``` + +### [Documentação](http://localhost:3000/api-docs/) (resumo) + +#### Solicitar uma conversão entre duas moedas +##### GET + +```shell +curl --request GET \ + --url 'http://localhost:3000/?from=BRL&to=USD&amount=1' +``` +Parâmetros: +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| from | query | Moeda de origem | Yes | string | +| to | query | Moeda de destino | Yes | string | +| amount | query | Valor a ser convertido | Yes | number | + +Response: +| Code | Description | +| ---- | ----------- | +| 200 | Ok | +| 400 | Bad Request | +| 404 | Not found | +| 500 | Internal Server Error | + +#### -> Criar uma nova moeda +##### POST +```shell +curl --request POST \ + --url http://localhost:3000/ \ + --header 'Content-Type: application/json' \ + --data '{ + "currency": "GTA", + "ballast_usd": 0.000013544, + "crypto": false +}' +``` +Body: +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| currency | body | Nome da moeda | Yes | string | +| ballast_usd | body | Valor da moeds em Dólar Americano (USD) | Yes | number | +| crypto | body | Se é uma criptomoeda | Yes | boolean | + +Response: +| Code | Description | +| ---- | ----------- | +| 201 | Created | +| 400 | Bad Request | +| 409 | Conflict | +| 500 | Internal Server Error | + +#### Remover uma moeda +##### DELETE +*Só poderão ser removidas as moedas criadas pelo usuário. +**As moedas obtidas pelo [worker](#worker-1) não poderão ser removidas + +```shell +curl --request DELETE \ + --url 'http://localhost:3000/?currency=GTA' +``` +Parâmetros: +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| currency | query | Moeda a ser removida | Yes | string (string) | + +Response: +| Code | Description | +| ---- | ----------- | +| 200 | Ok | +| 400 | Bad Request | +| 403 | Forbidden | +| 404 | Not found | +| 500 | Internal Server Error | \ No newline at end of file From bfb6151211f007889dc1d03079d8a74db35cfe50 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 27 Feb 2024 00:39:40 -0300 Subject: [PATCH 34/43] Refatorando variaveis de ambiente --- docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index c53f2d53e..d55d4e126 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,6 +46,7 @@ services: MYSQL_DATABASE: currencydb REDIS_HOST_TLS: redis://redis REDIS_PASSWORD_TLS: Redis2024! + AWAIT_DELAY_SECONDS: 300 command: node workers/currencys.js depends_on: db: @@ -68,6 +69,7 @@ services: MYSQL_DATABASE: currencydb REDIS_HOST_TLS: redis://redis REDIS_PASSWORD_TLS: Redis2024! + PORT: 3000 command: bash -c "sleep 10; npm start" depends_on: db: From 6220075f8b7aea337e06144ced260eea0f3660f3 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Tue, 27 Feb 2024 00:40:00 -0300 Subject: [PATCH 35/43] Refatorando variaveis de ambiente --- workers/currencys.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workers/currencys.js b/workers/currencys.js index b9a26b233..5fd8eb48b 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -119,7 +119,7 @@ const handler = async () => { await transaction.rollback(); } - await sleep(300 * 1000); + await sleep(process.env.AWAIT_DELAY_SECONDS * 1000); } }; From 89693bfdf2f30153a9959b58dd3104f10b97337a Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Wed, 28 Feb 2024 12:58:19 -0300 Subject: [PATCH 36/43] FIX: alterando api publica devido ao encerramento das atividades da api anterior --- workers/currencys.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/workers/currencys.js b/workers/currencys.js index 5fd8eb48b..74e94d485 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -4,6 +4,8 @@ const axios = require('axios'); const redis = require("redis"); const { CurrencysModel, CurrencysRaw } = require('../models/currencys'); +const AWAIT_DELAY_SECONDS = process.env.AWAIT_DELAY_SECONDS || 300; + const sleep = (ms) => { return new Promise((resolve) => { setTimeout(resolve, ms); @@ -12,8 +14,16 @@ const sleep = (ms) => { const getBallastDefault = async () => { try { - const { data } = await axios.get(`https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/latest/currencies/usd.min.json`); - return data.usd; + const { data } = await axios.get(`https://api.coingate.com/v2/rates`); + const {BTC, ETH, USD} = data.merchant; + + return { + BTC: 1 / BTC.USD, + ETH: 1 / ETH.USD, + USD: 1, + BRL: USD.BRL * 1, + EUR: USD.EUR * 1 + } } catch (error) { throw new Error(error); } @@ -50,7 +60,7 @@ const handler = async () => { const transaction = await CurrencysRaw.transaction(); try { - console.log('>> CONSULTANDO DADOS E ATUALIZANDO DATABASE'); + console.log('>> GET CURRENCYS ON PUBLIC API AND POPULATE DATABASE/REDIS'); const ballastDefault = await getBallastDefault(); const currencys = Object.keys(ballastDefault); @@ -97,7 +107,7 @@ const handler = async () => { console.log('>>> UPDATE COMPLETED'); } - console.log('>> UPDATE REDIS DATABASE'); + console.log('>>> UPDATE REDIS DATABASE'); const currencysInDB = await CurrencysModel.findAll(); for (let i = 0; i < currencysInDB.length; i++) { const currency = currencysInDB[i]; @@ -111,7 +121,7 @@ const handler = async () => { ) ); } - console.log('>> UPDATE REDIS DATABASE COMPLETED'); + console.log('>>> UPDATE REDIS DATABASE COMPLETED'); await transaction.commit(); } catch (error) { @@ -119,7 +129,8 @@ const handler = async () => { await transaction.rollback(); } - await sleep(process.env.AWAIT_DELAY_SECONDS * 1000); + console.log('>> AWAITING ' + AWAIT_DELAY_SECONDS + ' SECONDS TO NEXT RUN'); + await sleep(AWAIT_DELAY_SECONDS * 1000); } }; From d8a042d26cf973b1e82a19f6a63f3e50a6c3d431 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Wed, 28 Feb 2024 13:02:14 -0300 Subject: [PATCH 37/43] REF: alterando variaveis e requests para promise.all --- routes/main.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/routes/main.js b/routes/main.js index 0bea986f8..6e5e262d4 100644 --- a/routes/main.js +++ b/routes/main.js @@ -7,7 +7,6 @@ const { Response } = require('../utils/response'); const { ConvertCurrency, GetCurrency, NewCurrency, DeleteCurrency } = require('../controllers/currency_exchange'); -const { formatCurrency } = require('../utils/formatter'); router.get('/', query('from').notEmpty().escape().isLength({ min: 1, max: 10 }).withMessage('Currency code \'from\' must be 1 characters long'), @@ -20,15 +19,12 @@ router.get('/', return Response(res, 400, validateQuery.array()); } - let from = req.query.from; - let to = req.query.to; const amount = req.query.amount; - let f = GetCurrency(from.toUpperCase()); - let t = GetCurrency(to.toUpperCase()); + const fromPromise = GetCurrency(req.query.from.toUpperCase()); + const toPromise = GetCurrency(req.query.to.toUpperCase()); - from = await f; - to = await t; + let [from, to] = await Promise.all([fromPromise, toPromise]); if(!from) return Response(res, 404, {message: `Currency code 'from' ${req.query.from} not found`}); if(!to) return Response(res, 404, {message: `Currency code 'to' ${req.query.to} not found`}); From 5a26834f1679d15e6ecf1e614368630f9f599429 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Wed, 28 Feb 2024 16:35:14 -0300 Subject: [PATCH 38/43] =?UTF-8?q?FIX:=20commit=20ap=C3=B3s=20o=20redis=20i?= =?UTF-8?q?nvalidando=20o=20processo=20do=20worker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- workers/currencys.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/workers/currencys.js b/workers/currencys.js index 74e94d485..30a0a0206 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -107,6 +107,8 @@ const handler = async () => { console.log('>>> UPDATE COMPLETED'); } + await transaction.commit(); + console.log('>>> UPDATE REDIS DATABASE'); const currencysInDB = await CurrencysModel.findAll(); for (let i = 0; i < currencysInDB.length; i++) { @@ -122,8 +124,6 @@ const handler = async () => { ); } console.log('>>> UPDATE REDIS DATABASE COMPLETED'); - - await transaction.commit(); } catch (error) { console.error('>> ERRO AO CONSULTAR DADOS', error); await transaction.rollback(); From 6ca44e23ea70ea5153aa5693b9b93564da792c30 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Wed, 28 Feb 2024 18:33:26 -0300 Subject: [PATCH 39/43] =?UTF-8?q?Organizando=20test=20unitario=20e=20adici?= =?UTF-8?q?onando=20teste=20de=20integra=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 9 ++- routes/main.js | 6 +- server.js | 4 +- tests/integration/integrations.test.js | 69 +++++++++++++++++++++++ tests/{ => unit}/convert_currency.test.js | 2 +- 5 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 tests/integration/integrations.test.js rename tests/{ => unit}/convert_currency.test.js (96%) diff --git a/package.json b/package.json index 7660a1cfe..ee72dd5c2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "[[English](README.md) | [Portuguese](README.pt.md)]", "main": "index.js", "scripts": { - "test": "jest", + "test": "npm run test:unit && npm run test:integration", + "test:unit": "jest ./tests/unit/*.test.js", + "test:integration": "mocha tests/integration/*.test.js", "start": "node server.js", "debug": "nodemon server.js" }, @@ -32,6 +34,9 @@ "swagger-ui-express": "^5.0.0" }, "devDependencies": { - "jest": "^29.7.0" + "chai": "^5.1.0", + "jest": "^29.7.0", + "mocha": "^10.3.0", + "supertest": "^6.3.4" } } diff --git a/routes/main.js b/routes/main.js index 6e5e262d4..70f374bdc 100644 --- a/routes/main.js +++ b/routes/main.js @@ -41,7 +41,7 @@ router.get('/', Response(res, 200, result); } catch (error) { console.error('routes/main.js ~ get ~ ERROR: ', error); - Response(res, 500, {message: error.message}); + Response(res, 500, {message: "Internal server error"}); } } ); @@ -71,7 +71,7 @@ router.post('/', Response(res, result.status, {message: result.message}); } catch (error) { console.error('routes/main.js ~ post ~ ERROR: ', error); - return Response(res, 500, {message: error.message}); + Response(res, 500, {message: "Internal server error"}); } } @@ -97,7 +97,7 @@ router.delete('/', Response(res, result.status, {message: result.message}); } catch (error) { console.error('routes/main.js ~ delete ~ ERROR: ', error); - return Response(res, 500, {message: error.message}); + Response(res, 500, {message: "Internal server error"}); } } diff --git a/server.js b/server.js index 5fab28067..94c8aaf8a 100644 --- a/server.js +++ b/server.js @@ -35,4 +35,6 @@ app.use('/', mainRoutes); app.listen(port, () => { console.log(`API rodando em http://localhost:${port}`); -}); \ No newline at end of file +}); + +module.exports = app; \ No newline at end of file diff --git a/tests/integration/integrations.test.js b/tests/integration/integrations.test.js new file mode 100644 index 000000000..0e95ba433 --- /dev/null +++ b/tests/integration/integrations.test.js @@ -0,0 +1,69 @@ +'use strict'; + +const request = require('supertest'); +const app = require('../../server'); // Assuming your Express app is defined in app.js + + + +describe('Integration tests', () => { + it('should return status code 404 (Not Found) when the route does not exist', async () => { + const { expect } = await import('chai'); + const response = await request(app).get('/anything'); + expect(response.status).to.equal(404); + }); + + it('should return status code 404 (Bad Request) on get exchange when the currency does not exist', async () => { + const { expect } = await import('chai'); + const response = await request(app).get('/?from=INTTESTMON&to=USD&amount=1'); + expect(response.status).to.equal(404); + expect(response.body.message).to.equal('Currency code \'from\' INTTESTMON not found'); + }); + + it('should return status code 400 (Bad Request) on get exchange', async () => { + const { expect } = await import('chai'); + const response = await request(app).get('/'); + expect(response.status).to.equal(400); + }); + + it('should return status code 200 (OK) on get exchange', async () => { + const { expect } = await import('chai'); + const response = await request(app).get('/?from=USD&to=USD&amount=1'); + expect(response.status).to.equal(200); + response.body = JSON.stringify(response.body).replace(/\s/g, ' '); + expect(response.body).to.deep.equal('{"USD":"US$ 1,00"}'); + }); + + it('should return status code 201 (Created) on create a new currency', async () => { + const { expect } = await import('chai'); + const response = await request(app) + .post('/') + .set('Content-Type', 'application/json') + .send({currency: 'MONEYTEST', ballast_usd: 0.05, crypto: false}); + expect(response.status).to.equal(201); + expect(response.body.message).to.deep.equal('Currency created successfully'); + }); + + it('should return status code 409 (Conflict) on create a new currency', async () => { + const { expect } = await import('chai'); + const response = await request(app) + .post('/') + .set('Content-Type', 'application/json') + .send({currency: 'MONEYTEST', ballast_usd: 0.05, crypto: false}); + expect(response.status).to.equal(409); + expect(response.body.message).to.deep.equal('Currency already exists'); + }); + + it('should return status code 200 (OK) on remove a currency added manually', async () => { + const { expect } = await import('chai'); + const response = await request(app).delete('/?currency=MONEYTEST'); + expect(response.status).to.equal(200); + expect(response.body.message).to.deep.equal('Currency deleted successfully'); + }); + + it('should return status code 403 (Forbidden) on remove a imported currency', async () => { + const { expect } = await import('chai'); + const response = await request(app).delete('/?currency=USD'); + expect(response.status).to.equal(403); + expect(response.body.message).to.deep.equal('Not authorized delete default currencies'); + }); +}); \ No newline at end of file diff --git a/tests/convert_currency.test.js b/tests/unit/convert_currency.test.js similarity index 96% rename from tests/convert_currency.test.js rename to tests/unit/convert_currency.test.js index 6603847ad..c4ce23c03 100644 --- a/tests/convert_currency.test.js +++ b/tests/unit/convert_currency.test.js @@ -1,4 +1,4 @@ -const { ConvertCurrency } = require('../controllers/currency_exchange'); +const { ConvertCurrency } = require('../../controllers/currency_exchange'); test('BRL: It should show the original monetary value of 100,00', async () => { const result = await ConvertCurrency( From 27ec38f08d6e7541da794ad1be5ac3ab5b3ff816 Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Wed, 28 Feb 2024 19:02:28 -0300 Subject: [PATCH 40/43] Organizando melhor o README --- README.md | 95 ++++++++++++++------------------------------------- resume-doc.md | 77 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 70 deletions(-) create mode 100644 resume-doc.md diff --git a/README.md b/README.md index 79083d782..93091d2d6 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Para esse desafio foi usada a seguinte arquitetura: [UML] Sequence - Worker

-## Contâiners (docker): +## Containers (docker): ### db @@ -64,6 +64,9 @@ docker compose up --build ## API +> [!IMPORTANT] +> É necessário que os containers estejam em execução a partir deste momento + O projeto estará executando na porta padrão 3000. O endereço padrão para testes: @@ -71,81 +74,33 @@ O endereço padrão para testes: http://localhost:3000/ ``` -Documentação da API foi escrita utilizando a [OpenAPI 3.0 Specification](https://swagger.io/docs/specification/about/) e se encontra no endereço: +### Documentação +A documentação da API foi escrita utilizando a [OpenAPI 3.0 Specification](https://swagger.io/docs/specification/about/) e se encontra no endereço: ``` http://localhost:3000/api-docs/ ``` +ou -### [Documentação](http://localhost:3000/api-docs/) (resumo) +[resume-doc.md](resume-doc.md) -#### Solicitar uma conversão entre duas moedas -##### GET +### Observações -```shell -curl --request GET \ - --url 'http://localhost:3000/?from=BRL&to=USD&amount=1' -``` -Parâmetros: -| Name | Located in | Description | Required | Schema | -| ---- | ---------- | ----------- | -------- | ---- | -| from | query | Moeda de origem | Yes | string | -| to | query | Moeda de destino | Yes | string | -| amount | query | Valor a ser convertido | Yes | number | - -Response: -| Code | Description | -| ---- | ----------- | -| 200 | Ok | -| 400 | Bad Request | -| 404 | Not found | -| 500 | Internal Server Error | - -#### -> Criar uma nova moeda -##### POST -```shell -curl --request POST \ - --url http://localhost:3000/ \ - --header 'Content-Type: application/json' \ - --data '{ - "currency": "GTA", - "ballast_usd": 0.000013544, - "crypto": false -}' +Para formatação monetária é utilizado o padrão brasileiro. + +Para moedas correntes foi utilzado o padrão de duas casas decimais, exemplo: + +```json +{ + "BRL": "R$ 1,00", + "USD": "US$ 0,20" +} ``` -Body: -| Name | Located in | Description | Required | Schema | -| ---- | ---------- | ----------- | -------- | ---- | -| currency | body | Nome da moeda | Yes | string | -| ballast_usd | body | Valor da moeds em Dólar Americano (USD) | Yes | number | -| crypto | body | Se é uma criptomoeda | Yes | boolean | - -Response: -| Code | Description | -| ---- | ----------- | -| 201 | Created | -| 400 | Bad Request | -| 409 | Conflict | -| 500 | Internal Server Error | - -#### Remover uma moeda -##### DELETE -*Só poderão ser removidas as moedas criadas pelo usuário. -**As moedas obtidas pelo [worker](#worker-1) não poderão ser removidas -```shell -curl --request DELETE \ - --url 'http://localhost:3000/?currency=GTA' +para criptomoedas, utilizamos dez casas decimais: + +```json +{ + "BRL": "R$ 1,0000000000", + "BTC": "BTC 0,0000033427" +} ``` -Parâmetros: -| Name | Located in | Description | Required | Schema | -| ---- | ---------- | ----------- | -------- | ---- | -| currency | query | Moeda a ser removida | Yes | string (string) | - -Response: -| Code | Description | -| ---- | ----------- | -| 200 | Ok | -| 400 | Bad Request | -| 403 | Forbidden | -| 404 | Not found | -| 500 | Internal Server Error | \ No newline at end of file diff --git a/resume-doc.md b/resume-doc.md new file mode 100644 index 000000000..eb856e92f --- /dev/null +++ b/resume-doc.md @@ -0,0 +1,77 @@ +#### Documentação resumida + +> [!NOTE] +> Para uma experiência mais completa acesse o endereço:
+> http://localhost:3000/api-docs/ + + +##### Solicitar uma conversão entre duas moedas + +```shell +curl --request GET \ + --url 'http://localhost:3000/?from=BRL&to=USD&amount=1' +``` +Parâmetros: +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| from | query | Moeda de origem | Yes | string | +| to | query | Moeda de destino | Yes | string | +| amount | query | Valor a ser convertido | Yes | number | + +Response: +| Code | Description | +| ---- | ----------- | +| 200 | Ok | +| 400 | Bad Request | +| 404 | Not found | +| 500 | Internal Server Error | + +##### Criar uma nova moeda + +```shell +curl --request POST \ + --url http://localhost:3000/ \ + --header 'Content-Type: application/json' \ + --data '{ + "currency": "GTA", + "ballast_usd": 0.000013544, + "crypto": false +}' +``` +Body: +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| currency | body | Nome da moeda | Yes | string | +| ballast_usd | body | Valor da moeds em Dólar Americano (USD) | Yes | number | +| crypto | body | Se é uma criptomoeda | Yes | boolean | + +Response: +| Code | Description | +| ---- | ----------- | +| 201 | Created | +| 400 | Bad Request | +| 409 | Conflict | +| 500 | Internal Server Error | + +##### Remover uma moeda + +*Só poderão ser removidas as moedas criadas pelo usuário. +**As moedas obtidas pelo [worker](#worker-1) não poderão ser removidas + +```shell +curl --request DELETE \ + --url 'http://localhost:3000/?currency=GTA' +``` +Parâmetros: +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| currency | query | Moeda a ser removida | Yes | string (string) | + +Response: +| Code | Description | +| ---- | ----------- | +| 200 | Ok | +| 400 | Bad Request | +| 403 | Forbidden | +| 404 | Not found | +| 500 | Internal Server Error | \ No newline at end of file From d6b0089d81dd0dea98c9edbd04a9137887f1c27c Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Thu, 29 Feb 2024 10:56:11 -0300 Subject: [PATCH 41/43] =?UTF-8?q?Corrigindo=20bugs=20no=20algoritmo=20de?= =?UTF-8?q?=20convers=C3=A3o=20de=20moedas.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- controllers/currency_exchange.js | 4 ++-- tests/unit/convert_currency.test.js | 8 ++++---- utils/formatter.js | 13 ++++++++++++- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index e26bb97f0..e37d43a7d 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -21,8 +21,8 @@ const ConvertCurrency = async (from, to, amount) => { else { amount = format2float(amount); - if(from.ballast_usd > to.ballast_usd) calc = (amount * to.ballast_usd) / from.ballast_usd; - else calc = (amount * to.ballast_usd) * from.ballast_usd; + const amountIdUSD = amount / from.ballast_usd; + calc = amountIdUSD * to.ballast_usd; } return { diff --git a/tests/unit/convert_currency.test.js b/tests/unit/convert_currency.test.js index c4ce23c03..1a71e6a5d 100644 --- a/tests/unit/convert_currency.test.js +++ b/tests/unit/convert_currency.test.js @@ -20,7 +20,7 @@ test('BRL to USD: It should show the destination monetary value of 20,28', async test('GTA: It should show the original monetary value of 1.250.00,00', async () => { const result = await ConvertCurrency( - {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"GTA","ballast_usd":73833.43178,"crypto":false}, {"currency":"USD","ballast_usd":1,"crypto":false}, 1250000 ); @@ -29,7 +29,7 @@ test('GTA: It should show the original monetary value of 1.250.00,00', async () test('GTA to USD: It should show the original monetary value of 16,93', async () => { const result = await ConvertCurrency( - {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"GTA","ballast_usd":73833.43178,"crypto":false}, {"currency":"USD","ballast_usd":1,"crypto":false}, 1250000 ); @@ -38,7 +38,7 @@ test('GTA to USD: It should show the original monetary value of 16,93', async () test('GTA: It should show the original monetary value of 1.250.00,00', async () => { const result = await ConvertCurrency( - {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"GTA","ballast_usd":73833.43178,"crypto":false}, {"currency":"BRL","ballast_usd":4.93119,"crypto":false}, 1250000 ); @@ -47,7 +47,7 @@ test('GTA: It should show the original monetary value of 1.250.00,00', async () test('GTA to BRL: It should show the original monetary value of 83,49', async () => { const result = await ConvertCurrency( - {"currency":"GTA","ballast_usd":0.000013544,"crypto":false}, + {"currency":"GTA","ballast_usd":73833.43178,"crypto":false}, {"currency":"BRL","ballast_usd":4.93119,"crypto":false}, 1250000 ); diff --git a/utils/formatter.js b/utils/formatter.js index 6d8601a3c..9515aac62 100644 --- a/utils/formatter.js +++ b/utils/formatter.js @@ -26,7 +26,18 @@ const formatCurrency = (amount, currency = 'USD', crypto = false) => { } }; +const useCryptoFormat = value => { + if(!value.toString().includes('.')) return false; + else { + const dotSepareted = value.toString().split('.'); + if (dotSepareted[0] !== '0') return false; + + return dotSepareted[1].length > 2; + } +}; + module.exports = { format2float, - formatCurrency + formatCurrency, + useCryptoFormat }; From c68ba405d6e567522f1650712de6f100a1f41bcd Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Thu, 29 Feb 2024 11:27:03 -0300 Subject: [PATCH 42/43] Refatorando retorno de casas decimais --- controllers/currency_exchange.js | 8 ++++---- models/currencys.js | 2 +- sql/currencydb.sql | 2 +- utils/formatter.js | 3 ++- workers/currencys.js | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/controllers/currency_exchange.js b/controllers/currency_exchange.js index e37d43a7d..a06671761 100644 --- a/controllers/currency_exchange.js +++ b/controllers/currency_exchange.js @@ -1,5 +1,5 @@ const Redis = require('../services/redis'); -const { format2float, formatCurrency } = require('../utils/formatter'); +const { format2float, formatCurrency, useCryptoFormat } = require('../utils/formatter'); const { CurrencysModel, CurrencysRaw } = require('../models/currencys'); const GetCurrency = async currency => { @@ -19,15 +19,15 @@ const ConvertCurrency = async (from, to, amount) => { if (from.currency === to.currency) calc = amount; else { - amount = format2float(amount); + amount = format2float(amount, from.crypto); const amountIdUSD = amount / from.ballast_usd; calc = amountIdUSD * to.ballast_usd; } return { - from: formatCurrency(amount, from.currency, crypto), - to: formatCurrency(calc, to.currency, crypto) + from: formatCurrency(amount, from.currency, useCryptoFormat(amount, from.crypto)), + to: formatCurrency(calc, to.currency, useCryptoFormat(calc, to.crypto)) } } catch (error) { throw new Error(error); diff --git a/models/currencys.js b/models/currencys.js index 1726651b6..a82712959 100644 --- a/models/currencys.js +++ b/models/currencys.js @@ -14,7 +14,7 @@ const CurrencysModel = sequelize.define('currencys', { allowNull: false }, ballast_usd: { - type: Sequelize.FLOAT, + type: Sequelize.STRING(45), allowNull: false }, crypto: { diff --git a/sql/currencydb.sql b/sql/currencydb.sql index 4056896df..74e8cde3f 100644 --- a/sql/currencydb.sql +++ b/sql/currencydb.sql @@ -6,7 +6,7 @@ USE currencydb; CREATE TABLE IF NOT EXISTS currencys ( id INT AUTO_INCREMENT PRIMARY KEY, currency VARCHAR(10) NOT NULL, - ballast_usd FLOAT NOT NULL, + ballast_usd VARCHAR(45) NOT NULL, crypto BOOLEAN NOT NULL DEFAULT 0, imported BOOLEAN NOT NULL DEFAULT 0, createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, diff --git a/utils/formatter.js b/utils/formatter.js index 9515aac62..7e733d404 100644 --- a/utils/formatter.js +++ b/utils/formatter.js @@ -26,12 +26,13 @@ const formatCurrency = (amount, currency = 'USD', crypto = false) => { } }; -const useCryptoFormat = value => { +const useCryptoFormat = (value, crypto) => { if(!value.toString().includes('.')) return false; else { const dotSepareted = value.toString().split('.'); if (dotSepareted[0] !== '0') return false; + if(!crypto) return false; return dotSepareted[1].length > 2; } }; diff --git a/workers/currencys.js b/workers/currencys.js index 30a0a0206..737702ce1 100644 --- a/workers/currencys.js +++ b/workers/currencys.js @@ -117,7 +117,7 @@ const handler = async () => { JSON.stringify( { currency: currency.currency, - ballast_usd: currency.ballast_usd, + ballast_usd: parseFloat(currency.ballast_usd), crypto: currency.crypto } ) From 8b0a9b29b1e1000434ac71f933cf66c18a50bb8d Mon Sep 17 00:00:00 2001 From: Leonardo Neves Date: Thu, 29 Feb 2024 12:03:59 -0300 Subject: [PATCH 43/43] Atualizando diagrama de sequencia da API --- docs/Sequence-API.drawio | 253 ++++++++++++++++++++++++++++++++++++--- docs/Sequence-API.png | Bin 25668 -> 115504 bytes 2 files changed, 237 insertions(+), 16 deletions(-) diff --git a/docs/Sequence-API.drawio b/docs/Sequence-API.drawio index 7df0c0d47..fe7f0aa96 100644 --- a/docs/Sequence-API.drawio +++ b/docs/Sequence-API.drawio @@ -1,15 +1,34 @@ - + - + - + + + + + + + + + + + + + + + + + + + + @@ -19,13 +38,16 @@ - - + + - + - + + + + @@ -33,44 +55,243 @@ - + - + - + - + - + - + - + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/Sequence-API.png b/docs/Sequence-API.png index 95c51e2f72651ca227bcc1526ae69176b03f6454..1e51001c9372bdfa81ff5f8522c9c84efd16d8aa 100644 GIT binary patch literal 115504 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 z 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ïJ9*lGf6Y5}+`lFr1gO#t^% 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 sr245_PU-GaO1eRi?vRr1t^uUst?_v7 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 GaPuE>w7Z`G