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

Leandro-reis/challenge-implementation #314

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
a1ad8c3
chore: init project with typescript and jest
leandroepr May 30, 2024
1dd0516
refactor: Remove Calculator class and update entry point to use async…
leandroepr May 31, 2024
56fa796
feat: implement the currency conversion
leandroepr May 31, 2024
397b5fe
refactor: Ensure that gateway throws CurrencyNotFoundError when an in…
leandroepr May 31, 2024
b05bfe7
feat: Add validation for negative amount in CurrencyConverter
leandroepr Jun 2, 2024
96b3bfe
refactor: Add currency converter unit test
leandroepr Jun 2, 2024
d5af5eb
feat: Add validation for zero or negative rates in CurrencyConverter
leandroepr Jun 2, 2024
c789efd
refactor: Use custom error classes for throwing erros
leandroepr Jun 3, 2024
7dd7015
feat: implement CreateCurrencyRate use-case
leandroepr Jun 6, 2024
fcdf825
refactor: Move CurrencyConverter to application/service directory
leandroepr Jun 6, 2024
7d39804
refactor: rename exchange-not-found to currency-rate-not-found
leandroepr Jun 6, 2024
81a02a2
feat: implement DeleteCurrencyRateByCode use case
leandroepr Jun 6, 2024
c9c8a82
refactor: update ConvertCurrency to use CurrencyRateRepository instea…
leandroepr Jun 6, 2024
0ae1afb
refactor: ensure that it is not possible to delete the backing currency
leandroepr Jun 6, 2024
674940f
refactor: remove not used functions
leandroepr Jun 6, 2024
a1b69e3
refactor: move conversion responsibility to the CurrencyRate entity
leandroepr Jun 6, 2024
8fc401d
refactor: Improve CurrencyRate entity to validate constructor attribu…
leandroepr Jun 6, 2024
9a8e7cb
refactor: improve EquivalentRateCalculator to throw InvalidParamError…
leandroepr Jun 7, 2024
d1e786f
feat: add the Express HTTP server to serve the currency endpoints.
leandroepr Jun 10, 2024
d8791ae
feat: add the Hapi HTTP server to serve the currency endpoints.
leandroepr Jun 10, 2024
44c19fb
feat: Integrate PostgreSQL database with Docker support
leandroepr Jun 11, 2024
2a20c1b
fix: update use-case to delete by code
leandroepr Jun 12, 2024
e5c0d5b
feat: add currency-rate repository using remote API gateway
leandroepr Jun 12, 2024
ad05b36
feat: restructured imports and exports, added index.ts files to re-ex…
leandroepr Jun 13, 2024
8496b47
chore: configure Prettier and ESLint, format files
leandroepr Jun 13, 2024
5155775
fix: update GetCurrencyRateByCode to use uppercase code for consistency
leandroepr Jun 13, 2024
ea7fda8
feat: Add swagger documentation for API endpoints
leandroepr Jun 13, 2024
242d097
feat: Add ListCurrenciesController to handle listing currencies via HTTP
leandroepr Jun 14, 2024
67faef1
refactor: split CurrencyController into multiple controllers
leandroepr Jun 14, 2024
5bd7ff4
refactor: extract route responsibility from controllers
leandroepr Jun 14, 2024
64c02ca
refactor: extract route definitions into CurrenciesRoutes class
leandroepr Jun 14, 2024
e209bd6
refactor: update route paths to use '/api/v1' prefix
leandroepr Jun 14, 2024
1a24156
refactor: replace "decimalPoints" to "decimalDigits"
leandroepr Jun 14, 2024
38a371a
refactor: handle currency conversion errors in ConvertCurrencyController
leandroepr Jun 14, 2024
d3f9043
refactor: handle bad request error in ConvertCurrencyController
leandroepr Jun 14, 2024
1838a78
refactor: update currency error handling and messages
leandroepr Jun 14, 2024
68a216f
refactor: handle InvalidParamError on create currency
leandroepr Jun 14, 2024
36ea902
refactor: update currency error handling and messages
leandroepr Jun 14, 2024
fa948d7
test: refactor and enhance the entire currency management process
leandroepr Jun 14, 2024
85b5505
fix: throw CurrencyNotFound when remote gateway throws error 422
leandroepr Jun 14, 2024
34a3075
refactor: Improved script organization and naming for better readabil…
leandroepr Jun 14, 2024
e926ae6
chore: configure Docker and environment variables
leandroepr Jun 15, 2024
772b067
doc: rename the readmes to challenge-README*.md and create a new READ…
leandroepr Jun 15, 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
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
coverage
initdb
node_modules
dist
.build
Dockerfile
docker-compose.yml
.git
.gitignore
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ end_of_line = lf
insert_final_newline = false
charset = utf-8
indent_style = space
indent_size = 4
indent_size = 2
trim_trailing_whitespace = true
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
HTTP_SERVER_PORT=3000
DATABASE_URL=postgres://postgres:123456@localhost:5432
CURRENCYAPI_API_KEY=cur_live_cuO1rc3J9p5XJE9QYRgyadj3ERuP57GKFUdt9yOh
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
coverage
node_modules
dist
.DS_Store
7 changes: 7 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
dist
coverage
README.md
README.pt.md
tsconfig.json
yarn.lock
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"printWidth": 80,
"tabWidth": 2
}
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM node:20.14.0

WORKDIR /usr/src/app

COPY package.json yarn.lock ./

RUN yarn install

COPY . .

EXPOSE 3000

CMD ["yarn", "start"]
271 changes: 214 additions & 57 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,239 @@
# <img src="https://avatars1.githubusercontent.com/u/7063040?v=4&s=200.jpg" alt="Hurb" width="24" /> Bravo Challenge

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

Esta é uma API para conversão monetária. Ela suporta conversões entre diversas moedas, incluindo moedas fiduciárias, criptomoedas e moedas fictícias. A moeda de lastro utilizada é o USD, e as cotações foram obtidas de APIs públicas para garantir que sejam reais e atuais.

A solução foi desenvolvida utilizando, TDD, DDD, Ports & Adapters, Clean Architecture, design-patterns e outras estratégias arquiteturais. Sei que essa arquitetura pode ser considerada demasiadamente robusta para um projeto tão pequeno, mas o intuito é mostrar o que tenho em minha "caixa de ferramentas" e também dar a vocês material de análise de minha forma de codificar. No dia a dia gosto de dimensionar a solução de software para alcançar o melhor proveito no negócio.

## Tecnologias Utilizadas

- Node.js
- TypeScript
- Express
- Hapi
- Swagger UI
- PostgreSQL
- Docker
- Jest

## Endpoints

Indico fortemente a utilização do swagger para consultar a documentação da API e também para executar as requests. Essa documentação pode ser acessada no endereço `http://locahost:3000/api-docs`, mas caso queira ou precise testar utilizando uma ferramenta de requisições http como Postman, o endereço base para as requisições é o `http://localhost:3000/api/v1`.

### Lista Paginada de Moedas

Este endpoint não foi solicitado no desafio mas decidi fazer para ter um bom exemplo da utilização de CQRS. Eu fiz a _query_ do _[ GET ] /currencies/{code}_ também mas ela sozinha não realça as vantagens da separação de comandos e consultas. É importante também mencionar que este endpoint apenas estará disponível se o projeto for inicializado com `yarn start` ou `yarn dev`. O único objetivo desse comportamento é mostrar que as rotas também são configuráveis sem afetar a aplicação.

`[ GET ] /currencies`

```json
// query
{
"currencies": [], // Código das moedas a serem filtradas
"page": 1, // Página corrente
"perPage": 10, // Quantidade de registros por página
"sortBy": "name", // Campo pelo qual a consulta será ordenada
"sortOrder": "asc", // Direção da ordenação
}
```

```json
// response schema
{
"meta": {
"page": 0,
"perPage": 0,
"pageCount": 0,
"totalCount": 0
},
"data": [
{
"id": "string",
"name": "string",
"code": "string",
"symbol": "string",
"decimalDigits": 0,
"rate": 0.1
}
]
}
```

### Adiciona uma nova moeda

O desafio não define ou esclarece quem utilizará essa API, então pensando em praticidade, ao invés de pedir o _rate_ (valor proporcional de uma moeda em relação à moeda de lastro), que resultaria em um cadastro simples, eu preferi solicitar o valor da nova moeda bem como de uma moeda equivalente. Podemos inclusive seguir o exemplo citado no desafio do cadastro de uma moeda fictícia do GTA onde \$ 1.250.000,00 no jogo custam R$ 83,50 no mundo real. Com isso calculamos o valor proporcional da moeda em dólar.

`[ POST ] /currencies`

```json
// body
{
"name": "Grand Theft Auto Dollar",
"code": "GTA",
"symbol": "GTA$",
"baseAmount": 1250000.00,
"equivalentCurrencyAmount": 83.50,
"equivalentCurrencyCode": "BRL"
}
```

```json
// response schema
{
"currencyId": "string"
}
```

### Converte uma moeda em outra

A descrição do desafio propõe a API de conversão de moeda mas não diz como deve ser seu retorno. Por isso tomei a liberdade de devolver um objeto com algumas informações que julguei serem uteis em uma API real de conversão.

`[ POST ] /currencies/convert`

```json
// query
{
"from": "string",
"to": "string",
"amount": 0.1,
}
```

```json
// response schema
{
"from": "string",
"to": "string",
"givenAmount": 0.1,
"convertedAmount": 0.1,
"convertedAmountFormatted": "string",
"decimalDigits": 0,
"symbol": "string"
}
```

### Recupera uma moeda específica

Esse endpoint também não foi solicitado no desafio mas ele é super importante nos testes automatizado garantido que as moedas criadas na aplicação foram criadas corretamente.

`[ GET ] /currencies/{code}`

```json
// param
{
"code": "string",
}
```

```json
// response schema
{
"id": "string",
"name": "string",
"code": "string",
"symbol": "string",
"decimalDigits": 0,
"rate": 0.1
}
```

### Exclui uma moeda do repositório

`[ DELETE ] /currencies/{code}`

Todas as configurações dessa aplicação (veja mais [aqui](#ancora1)) oferecem a possibilidade de criar e remover moedas, mas no caso das configurações que utilizam o gateway externo, apenas as novas moedas podem ser removidas.

```json
// param
{
"code": "string",
}
```

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**.
```json
// response schema
{
"message": "string",
}
```

The API must convert between the following currencies:

- USD
- BRL
- EUR
- BTC
- ETH
## Como Executar

Other coins could be added as usage.
### Pré-requisitos

Ex: USD to BRL, USD to BTC, ETH to BRL, etc...
- Docker & Docker Compose (caso queira rodar com o banco de dados postgres)
- Node
- Yarn (opcional)

The request must receive as parameters: The source currency, the amount to be converted and the final currency.
### Preparação do repositório

Ex: `?from=BTC&to=EUR&amount=123.45`
1. Clone o repositório:

Also build an endpoint to add and remove API supported currencies using HTTP verbs.
```shell
$ git clone https://github.com/hurbcom/challenge-bravo.git
```

The API must support conversion between FIAT, crypto and fictitious. Example: BRL->HURB, HURB->ETH
2. Navegue até o diretório do projeto:

"Currency is the means by which monetary transactions are effected." (Wikipedia, 2021).
```shell
$ cd challenge-bravo
```

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.
3. Acesse a branch que criei com a solução do desafio:

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).
```shell
$ git checkout 'leandro/challenge-implementation'
```

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.
### Passos para rodar a aplicação no Docker

You can use any programming language for the challenge. Below is the list of languages ​​that we here at Hurb have more affinity:
1. Construa e inicie os containers Docker:

- JavaScript (NodeJS)
- Python
- Go
- Ruby
- C++
- PHP
```shell
$ docker-compose build
...
$ docker-compose up
```

## Requirements
* _Para parar a aplicação execute ctrl+D no terminal_
* _Para parar o container execute `docker-compose down`_

- 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
### Passos para rodar a aplicação localmente

## Evaluation criteria
1. Instale as dependências

- **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?
```sh
$ yarn install
```

## Doubts
2. Inicie o banco de dados via docker-compose

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!
```sh
$ docker-compose up db
```

Godspeed! ;)
1. Inicie a aplicação

<p align="center">
<img src="ca.jpg" alt="Challange accepted" />
</p>
```sh
$ yarn start
```

* _Para parar a aplicação execute ctrl+D no terminal_

<a id="ancora1"></a>

> Há ainda outros scripts de inicialização que podem ser utilizados. Cada um sobe o servidor com uma configuração diferente:
> - `start` ou `dev`: Inicia o servidor express com swagger UI e repositório postgres que roda no docker;
> - `dev:express+fake`: Iniciar o servidor express com swagger UI e Repositório em memória;
> - `dev:hapi+fake`: Inicia o servidor hapi com repositório em memória;
> - `dev:express+gw-on`: Inicia o servidor express com swagger UI e o repositório utiliza o gateway currencyapi.com para ter a contação do dia de forma online.
> - `dev:express+gw-off`: Inicia o servidor express com swagger UI e simula a requisição para o gatewayapi.com (para fins de testes sem fazer requisições reais pois são liberadas apenas 300 requisições/mês)

### Executar testes

```sh
$ yarn test
```
- Também pode ser utilizado o `yarn test:watch` que roda os testes com a flag `--watch`.
Loading