Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

challenge-bravo-AndreNicacio #303

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7c419c8
Started environment configuration (NodeJS) and development of initial…
AndreNicacio Jan 11, 2024
8d7c957
Developed folder and route structure to increase currency conversion …
AndreNicacio Jan 11, 2024
67b1bf4
Inserted conversion function and update package files config
AndreNicacio Jan 11, 2024
615e383
Development of legacy structure, applying functionality and creation …
AndreNicacio Jan 13, 2024
99e5518
Improved code and folder structure
AndreNicacio Jan 13, 2024
6c28342
Added swagger doc, JWT security in API calls and admin user security …
AndreNicacio Jan 15, 2024
2af33a4
Applied test functions, cache control with Redis, improvement of fold…
AndreNicacio Jan 17, 2024
a64bf82
Applied horizontal scalability system to control the flow of requests…
andreFnicacio Jan 18, 2024
b34d56b
Added test script finalization and removed dev understanding comments
andreFnicacio Jan 18, 2024
01ceb53
Update README.md
andreFnicacio Jan 19, 2024
c4940eb
Update README.md
andreFnicacio Jan 19, 2024
1e4422a
File update, removed reference comments for development
andreFnicacio Jan 19, 2024
8ed2650
Update README.md
andreFnicacio Jan 19, 2024
dec11af
Update README.md
andreFnicacio Jan 19, 2024
fcac05f
Update README.md
andreFnicacio Jan 19, 2024
623cfc4
Update README.md
andreFnicacio Jan 19, 2024
4561012
Update README.md
andreFnicacio Jan 19, 2024
c3755a5
Update README.md
andreFnicacio Jan 19, 2024
f255c0f
Update README.md
andreFnicacio Jan 19, 2024
ccd63c5
Update README.md
andreFnicacio Jan 19, 2024
f19b241
Merge branch 'challenge-bravo-andreNicacio'
andreFnicacio Jan 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.env
15 changes: 15 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM node

WORKDIR /app

COPY package.json ./

RUN npm install

RUN ulimit -n 65536

COPY . ./

EXPOSE 8082

CMD ["npm", "start"]
147 changes: 89 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,113 @@
# <img src="https://avatars1.githubusercontent.com/u/7063040?v=4&s=200.jpg" alt="Hurb" width="24" /> Bravo Challenge
# Challenge Bravo - André Nicácio

[[English](README.md) | [Portuguese](README.pt.md)]
## Descrição do Projeto

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**.
O Challenge Bravo é uma API desenvolvida para conversão de moedas, utilizando o dólar como base para as conversões. O projeto é implementado em JavaScript (NodeJS) e estruturado utilizando o Docker. Optamos por essas ferramentas devido à necessidade de atualizações constantes dos valores das moedas no banco de dados e para criar uma API estável que facilite futuros deployments.

The API must convert between the following currencies:
### Tecnologias Utilizadas

- USD
- BRL
- EUR
- BTC
- ETH
- **NodeJS**: Linguagem principal para o desenvolvimento da API.
- **Docker**: Utilizado para gerar a estrutura da API, facilitando atualizações e futuros deployments.
- **Nginx**: Utilizado para fazer o balanceamento das requisições, dentro da estrutura de escalabilidade horizontal.
- **MongoDB**: Escolhido como banco de dados devido à capacidade de armazenamento dinâmico de dados, atendendo à necessidade de flexibilidade ao lidar com códigos.
- **Redis**: Empregado para controle de cache, reduzindo demandas no banco de dados e mitigando possíveis gargalos.
- **Swagger**: Implantado para documentação de rotas da API.

Other coins could be added as usage.
## Instalação e Execução do Projeto

Ex: USD to BRL, USD to BTC, ETH to BRL, etc...
Para instalar e executar o projeto, siga estes passos:

The request must receive as parameters: The source currency, the amount to be converted and the final currency.
1. Certifique-se de estar no diretório raiz dentro da pasta da API.
2. Execute o seguinte comando Docker:

Ex: `?from=BTC&to=EUR&amount=123.45`
```bash
docker-compose -f "docker-compose.yml" up -d --build
```
Após a execução do comando, a documentação da API estará disponível em [http://localhost:8082/api-docs/#/]. As rotas estarão operacionais após a execução do comando Docker.

Also build an endpoint to add and remove API supported currencies using HTTP verbs.

The API must support conversion between FIAT, crypto and fictitious. Example: BRL->HURB, HURB->ETH
Observação: Certifique-se de que este comando seja executado no mesmo nível do arquivo `docker-compose.yml` ou forneça o caminho correto para o arquivo. Toda alteração feita no código será necessário executar o comando novamente para integração.

"Currency is the means by which monetary transactions are effected." (Wikipedia, 2021).
## Acessando o Container Docker

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.
Para acessar o container Docker criado para a API, utilize os seguintes comandos:

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).
1. Visualize os containers ativos:

Ref:
Wikipedia [Institutional Website]. Available at: <https://pt.wikipedia.org/wiki/Currency>. Accessed on: 28 April 2021.
Playstation Store [Virtual Store]. Available at: <https://store.playstation.com/pt-br/product/UP1004-CUSA00419_00-GTAVCASHPACK000D>. Accessed on: 28 April 2021.
```bash
docker ps
```

You can use any programming language for the challenge. Below is the list of languages ​​that we here at Hurb have more affinity:
2. Obtenha o ID do Container do serviço ativo chamado 'challenge-bravo-api'.

- JavaScript (NodeJS)
- Python
- Go
- Ruby
- C++
- PHP
3. Execute o seguinte comando, substituindo `CONTAINER_ID_` pelo ID do Container obtido:

## Requirements
```bash
docker exec -it CONTAINER_ID_ bash
```
## Link para documentação API - SWAGGER

- 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
- **API-DOCS**: http://localhost:8082/api-docs/#/ (LINK SWAGGER NAVEGADOR)

Observação: Acesso ao Swagger disponivel após execução do 'docker compose'

## Evaluation criteria
## Exemplos de rotas - COINS

- **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?
- **GET**: http://localhost:8082/coins/prod/convert?from=MOEDA_ORIGEM&to=MOEDA_DESTINO&amount=VALOR_A_CONVERTER
- **POST**: http://localhost:8082/coins/prod/insert (NECESSÁRIO API TOKEN)

## Doubts
```json
{
"code": "DD",
"name": "Dungeons&Dragon",
"value": "760.85"
}

- **PUT**: http://localhost:8082/coins/prod/update (NECESSÁRIO API TOKEN)

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!
```json
{
"code": "DD",
"name": "Dungeons&Dragon-UPDATE",
"value": "7.60"
}

- **DELETE**: http://localhost:8082/coins/test/delete/DD (NECESSÁRIO API TOKEN)

Godspeed! ;)
## GERANDO TOKEN API

<p align="center">
<img src="ca.jpg" alt="Challange accepted" />
</p>
- **AUTENTICATION**: http://localhost:8082/autentication/generate (ESSE TOKEN EXPIRA APÓS 1 HORA)

```json
{
"userID": "challenge",
"passwordID": "bravo"
}

Observação: As variaveis de ambiente se encontram no arquivo "docker-compose.yml"

## Executando testes unitários/integração:
Uma vez dentro do container é possivel executar os comandos de testagem
- **UNITÁRIOS&INTEGRAÇÕES**: npm test

## Executando teste de estresse:
- **ESTRESSE**:
Nesta API foi utilizado a biblioteca "Artillery" para testes de estresse:

1. Na pasta raiz do projeto, fora do container. Instale a biblioteca Artillery:

```bash
npm install -g artillery
```

3. Execute o seguinte comando na raiz do projeto (Fora Container Docker), para iniciar o teste:

```bash
artillery run artillery.yaml
```

Observação: O teste de estresse aplicado pela biblioteca Artillery tem como padrão a latencia de mil (1000) requisições em um segundo (1s).

## Conclusão

Obrigado pela oportunidade! Me dediquei bastante para mostrar meu conhecimentos e principalmente do que sou capaz, de aprender conforme as necessidades apontam e a curiosidade direciona. Me diverti fazendo este desafio e certamente aprendi coisas novas que vou aplicar em meu trabalho! Estarei na torcida e aguardando o feedback, grato pela atenção 😊
31 changes: 31 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const express = require('express');
const dotenv = require('dotenv');
dotenv.config();
const connectDb = require('./config/mongo/db');
const populateDb = require('./config/mongo/populateDB');
const updateDbByInterval = require('./config/mongo/updateValuesIntervalTime');
const swaggerUi = require('swagger-ui-express');
const swaggerSpec = require('./swagger/swagger');
const app = express();
const cors = require("cors");;

connectDb();
populateDb();

setInterval(() => {
updateDbByInterval();
}, 5 * 60 * 1000);

app.use(cors());

app.use(express.json({ timeout: 30000 }));

app.use(express.urlencoded({ extended: true }));

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));

app.use('/authentication', require('./src/routes/authenticationRoutes'));

app.use('/coins', require('./src/routes/coinsRoutes'));

module.exports = app;
135 changes: 135 additions & 0 deletions app.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const test = require('tape');
const conversionCoins = require('./src/utils/conversCoins');
const supertest = require('supertest');
const app = require('./app');


test('Teste de conversão de moeda base', (t) => {
const from = 'BRL';
const to = 'EUR';

const result = conversionCoins({ code: to, amount: 0.913 }, { code: from, amount: 4.86 }, 10);

const expected = {
status: 200,
data: {
message: "BRL => EUR",
value: "1.879"
},
};

t.deepEqual(result, expected, 'Converção sucesso');
t.end();
});

test('Testar conversão de valores (zero)', (t) => {
const from = 'BRL';
const to = 'EUR';

const result = conversionCoins({ code: to, amount: 0.913 }, { code: from, amount: 4.86 }, 0);

const expected = {
status: 200,
data: {
message: "BRL => EUR",
value: 0
},
};

t.deepEqual(result, expected, 'Retornado valor');
t.end();
});

test('Testar conversão de valores (negativos)', (t) => {
const from = 'BRL';
const to = 'EUR';

const result = conversionCoins({ code: to, amount: 0.913 }, { code: from, amount: 4.86 }, -10);

const expected = {
status: 200,
data: {
message: "BRL => EUR",
value: 0
},
};

t.deepEqual(result, expected, 'Retornado valor');
t.end();
});

test('GET /coins/:ENV/convert?from=MOEDA_ORIGEM&to=MOEDA_DESTINO&amount=VALOR_A_CONVERTER', (t) => {
supertest(app)
.get('/coins/test/convert?from=EDIESCB&to=BELLYONEPICE&amount=10')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) =>{
t.error(err, 'Error:')
t.assert(res.body.value === "4.286", "Success")
t.end()
})
});

test('POST /coins/:ENV/insert', (t) => {
const body = {
code: "CHALLENGEBRAVO",
name: "CHALLENGEBRAVO",
value: "760.85"
};

supertest(app)
.post('/coins/test/insert')
.send(body)
.set('Authorization', 'HURB-asfeEXamplevix027adsitba')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
t.error(err, 'Erro:');
t.assert(res.body.message === "Currency saved success", "Sucesso");
t.end();
});
});

test('PUT /coins/:ENV/update', (t) => {
const body = {
code: "CHALLENGEBRAVO",
name: "CHALLENGEBRAVO-UPDATE",
value: "5.685"
};

supertest(app)
.put('/coins/test/update')
.send(body)
.set('Authorization', 'HURB-asfeEXamplevix027adsitba')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
t.error(err, 'Erro:');
t.assert(res.body.message === "Currency update success", "Sucesso");
t.end();
});
});

test('DELETE /coins/:ENV/delete/:CODE', (t) => {
const body = {
code: "CHALLENGEBRAVO",
name: "CHALLENGEBRAVO",
value: "5.685"
};

supertest(app)
.delete('/coins/test/delete/CHALLENGEBRAVO')
.send(body)
.set('Authorization', 'HURB-asfeEXamplevix027adsitba')
.expect('Content-Type', /json/)
.expect(200)
.end((err, res) => {
t.error(err, 'Erro:');
t.assert(res.body.message === "Currency Deleted", "Sucesso");
t.end();
});
});

test.onFinish(() => {
process.exit(0);
});
12 changes: 12 additions & 0 deletions artillery.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
config:
target: 'http://localhost:8082'
phases:
- duration: 1
arrivalRate: 1000
http:
timeout: 90

scenarios:
- flow:
- get:
url: '/coins/prod/convert?from=BRL&to=USD&amount=10'
10 changes: 10 additions & 0 deletions config/mongo/db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Conexão MongoDBs
const mongoose = require('mongoose');

const url = "mongodb://challenge:bravo@mongo:27017/coins_rate_constance"

const connectDb = async () => {
await mongoose.connect(url);
}

module.exports = connectDb;
Loading