Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ecfd687
chore: add docker
DouglasVolcato May 17, 2024
54be862
chore: implement env files
DouglasVolcato May 17, 2024
0f1d6d9
chore: add requirements for docker container
DouglasVolcato May 17, 2024
5aee2a1
chore: add consumer file for redis events
DouglasVolcato May 17, 2024
9155c13
chore: implement configuration
DouglasVolcato May 17, 2024
d1297aa
feat: add user registration service
DouglasVolcato May 17, 2024
4686a1e
chore: implement docker
DouglasVolcato May 17, 2024
40be44b
chore: add app diagram
DouglasVolcato May 17, 2024
93b5ba4
refactor: remove unused files
DouglasVolcato May 17, 2024
cf01d79
refactor: remove unused files
DouglasVolcato May 17, 2024
03a5cd4
chore: implement docker
DouglasVolcato May 17, 2024
3b0df5d
feat: add auth service
DouglasVolcato May 17, 2024
40493a8
chore: implement env variables
DouglasVolcato May 17, 2024
250be32
feat: implement origin request allow between services
DouglasVolcato May 17, 2024
f743e90
chore: implement app diagrams
DouglasVolcato May 17, 2024
0dbf75f
refactor: remove unused files
DouglasVolcato May 17, 2024
efd424c
chore: update requirements
DouglasVolcato May 17, 2024
e7180ee
refactor: remove unused services
DouglasVolcato May 17, 2024
e66e600
feat: implement api service
DouglasVolcato May 17, 2024
6f13b59
feat: implement stock service
DouglasVolcato May 17, 2024
1e6b9a4
feat: implement auth service
DouglasVolcato May 17, 2024
4f2df96
refactor: decouple auth service
DouglasVolcato May 17, 2024
3363004
refactor: decouple stock service
DouglasVolcato May 17, 2024
01fa68b
refactor: decouple api service
DouglasVolcato May 17, 2024
190b464
refactor: remove unused files
DouglasVolcato May 17, 2024
1f8537d
fix: validate not found stock properly
DouglasVolcato May 18, 2024
a9d61ad
feat: add log service
DouglasVolcato May 18, 2024
43b992c
feat: implement request / response logs
DouglasVolcato May 18, 2024
9b88469
chore: implement diagrams
DouglasVolcato May 18, 2024
e445dca
fix: convert stock symbol request to uppercase
DouglasVolcato May 18, 2024
1d0060d
chore: add frontend
DouglasVolcato May 18, 2024
04d7379
feat: add login api gateway
DouglasVolcato May 18, 2024
4db648d
test: ensure login api gateway is working
DouglasVolcato May 18, 2024
819d5bd
chore: ensure login api gateway implements login api interface
DouglasVolcato May 18, 2024
5a75c58
feat: implement login usecase
DouglasVolcato May 18, 2024
0ae58a8
test: ensure login usecase is working
DouglasVolcato May 18, 2024
9079cce
feat: implement login page
DouglasVolcato May 18, 2024
019773a
test: ensure login page is working
DouglasVolcato May 18, 2024
caa3556
refactor: implement format
DouglasVolcato May 18, 2024
d6e6f87
feat: add stock price api gateway
DouglasVolcato May 18, 2024
1afe9e4
test: ensure stock price api gateway is working
DouglasVolcato May 18, 2024
760f538
refactor: implement variable names
DouglasVolcato May 18, 2024
8ab4b0d
feat: implement get stock use case
DouglasVolcato May 18, 2024
fbbff98
test: ensure get stock usecase is working
DouglasVolcato May 18, 2024
54625bd
refactor: remove unused files
DouglasVolcato May 18, 2024
1b5d066
test: ensure stock price api gateway calls request sender with correc…
DouglasVolcato May 18, 2024
ce2e54e
fix: fix api url connection
DouglasVolcato May 18, 2024
2b6664f
test: ensure login gateway sends the correct parameters
DouglasVolcato May 18, 2024
b07d288
test: ensure gateways return api error messages
DouglasVolcato May 18, 2024
338e58b
test: ensure usecases handle gateway errors
DouglasVolcato May 18, 2024
84d670d
refactor: implement variable names
DouglasVolcato May 18, 2024
9e166f8
refactor: remove unused files
DouglasVolcato May 18, 2024
41a6d21
test: ensure login page redirect to stocks after login is completed
DouglasVolcato May 18, 2024
3c4a21c
fix: implement authorization header
DouglasVolcato May 18, 2024
5a57b03
chore: add paragraph component
DouglasVolcato May 18, 2024
9b1343f
fix: fix stock data fetch
DouglasVolcato May 18, 2024
32edb5f
chore: add default values
DouglasVolcato May 18, 2024
279603d
test: ansure stock api gateway send the right parameters
DouglasVolcato May 18, 2024
c3af130
feat: implement get stock price page
DouglasVolcato May 18, 2024
d95a4ba
test: ensure get stock price page is working
DouglasVolcato May 18, 2024
203c367
chore: change frontend app title
DouglasVolcato May 18, 2024
c62cb2c
chore: add default values
DouglasVolcato May 18, 2024
ef3f90d
refactor: remove unused files
DouglasVolcato May 18, 2024
c69f588
chore: add default values
DouglasVolcato May 18, 2024
bbe9447
feat: add private page proxy
DouglasVolcato May 18, 2024
d62ff50
chore: add docs
DouglasVolcato May 18, 2024
ade02b0
feat: add mobile frontend
DouglasVolcato May 22, 2024
5a7e98d
chore: add mobile unit tests
DouglasVolcato May 22, 2024
cdb7464
test: ensure login form validator is working
DouglasVolcato May 22, 2024
ea62359
chore: implement mobile styles
DouglasVolcato May 22, 2024
e41f6ed
refactor: organize folder structure
DouglasVolcato May 22, 2024
522d3c5
chore: update docs for mobile version
DouglasVolcato May 22, 2024
e0c05a9
chore: update project title
DouglasVolcato May 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
31 changes: 0 additions & 31 deletions .gitignore

This file was deleted.

90 changes: 52 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,58 +1,72 @@
# Desafio Python Moniari
# stock-price-tracker

## Descrição
## Project Overview

Este projeto foi desenvolvido para avaliar suas habilidades com tecnologias de back-end, focando em Python, APIs REST, e arquitetura de serviços desacoplados.
This project demonstrates a backend split into microservices made with Python, a frontend made with TypeScript, utilizing Test-Driven Development (TDD) and Clean Architecture principles.

## Tarefa
## Running the Project

O desafio consiste em criar uma API simples utilizando Python (**FastAPI**), que permita aos usuários consultar cotações de ações.
O projeto é dividido em dois serviços distintos:
To run the project using Docker, execute the following command:
```sh
docker compose up --build
```

- Uma API voltada para o usuário final, que processa solicitações de usuários registrados em busca de informações sobre cotações de ações.
- Um serviço interno agregador de cotações, responsável por consultar APIs externas para buscar as cotações solicitadas pelos usuários.
## Services

## Requisitos Mínimos
### Api Service

### Serviço de API
- **Description**: Acts as the main entry point to the backend.
- **Documentation**: [http://localhost:80/docs](http://localhost:80/docs)
- **Routes**:
- `/login`: Route to login.
- `/stock?q={name}`: Route to get stock data by name.

- Os endpoints deste serviço devem exigir **autenticação**, não permitindo solicitações anônimas. Cada solicitação deve ser autenticada utilizando Basic Authentication (exemplo: `Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==` onde o token é a conversão em Base64 de username:senha).
### Stock Service

- Ao receber uma solicitação de cotação de ação (através do endpoint dedicado no serviço de API), se a ação for encontrada, o serviço deve registrar no console o usuário requisitante e a ação solicitada.
- **Description**: Processes and sends stock data.
- **Documentation**: [http://localhost:81/docs](http://localhost:81/docs)
- **Routes**:
- `/stock?symbol={name}`: Route to consult the stock by name.

- A resposta do serviço de API deve seguir o formato: `GET /stock?q=aapl.us`
### Auth Service

```python
{
"simbolo": "AAPL.US",
"nome_da_empresa": "APPLE",
"cotacao": 123
}
```
- **Description**: Handles token generation and authentication.
- **Documentation**: [http://localhost:82/docs](http://localhost:82/docs)
- **Routes**:
- `/login`: Route to generate a token.
- `/validate-token`: Route to authenticate a token.

### Log Service

O valor da cotação deve ser extraído do campo `close` retornado pelo serviço de cotações.
- **Description**: Responsible for logging information about the other services into files located in its logs directory.
- **Documentation**: [http://localhost:83/docs](http://localhost:83/docs)
- **Routes**:
- `/log`: Route to receive the logs.

- Todas as respostas dos endpoints devem ser em formato JSON.
## Frontend Web

### Serviço de Cotações
- **URL**: [http://localhost:3000](http://localhost:3000)

## Frontend Mobile

To run the frontend mobile version, enter the frontend-mobile folder and run the following commands:
```sh
npm install
npm run android
```

- Considerando que este serviço é interno, solicitações para seus endpoints não requerem autenticação.
- Ao receber uma solicitação de cotação, este serviço deve consultar uma API externa para obter as informações necessárias. Para este desafio, utilize a seguinte API: `https://stooq.com/q/l/?s={codigo_da_acao}&f=sd2t2ohlcvn&h&e=csv`.
- O `{codigo_da_acao}` deve ser substituído pelo código da ação desejada pelo usuário.
- Uma lista de códigos de ações disponíveis pode ser encontrada em: https://stooq.com/t/?i=518
## Diagrams

### Mecanismo de Log
### Backend Microservices Architecture
![Backend Microservices Architecture](diagrams/backend/microservices.png)

- Ambos os serviços, **Serviço de API** e **Serviço de Cotações**, devem implementar um **mecanismo de log** que registre as operações realizadas.
- Os logs gerados por ambos os serviços devem ser armazenados em uma pasta denominada `logs` dentro do diretório do projeto.
- Os arquivos de log devem ter a extensão `.log`.
- É importante que os logs incluam informações detalhadas sobre as solicitações processadas, incluindo timestamp, tipo de solicitação, dados da solicitação (se aplicável) e quaisquer mensagens de erro ou alertas gerados durante o processamento.
### Frontend Login Architecture
![Frontend Login Architecture](diagrams/frontend/login.png)

## Arquitetura
### Frontend Get Stock Data Architecture
![Frontend Get Stock Data Architecture](diagrams/frontend/get-stock-data.png)

![Diagrama de Arquitetura](arquitetura.png)
## Author

1. Um usuário solicita a cotação atual de uma ação da Apple: `GET /stock?q=aapl.us`
2. O serviço de API encaminha a solicitação para o serviço de cotações para obter as informações necessárias.
3. O serviço de cotações consulta a API externa, processa a resposta e retorna as informações para o serviço de API.
4. As informações são formatadas e enviadas de volta ao usuário.
Douglas Volcato
[GitHub](https://github.com/DouglasVolcato)
5 changes: 5 additions & 0 deletions api_service/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
PROJECT_NAME="Api Service"
API_VERSION="1.0.0"
STOCK_SERVICE_URL="http://stock_service:81"
AUTH_SERVICE_URL="http://auth_service:82"
LOG_SERVICE_URL="http://log_service:83"
3 changes: 0 additions & 3 deletions api_service/.env_example

This file was deleted.

12 changes: 12 additions & 0 deletions api_service/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.9

WORKDIR /code

COPY ./requirements.txt /code/requirements.txt

RUN pip install --upgrade pip
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt

COPY . /code/

CMD ["sh", "-c", "fastapi run main.py --port 80"]
Empty file removed api_service/__init__.py
Empty file.
Empty file removed api_service/api/__init__.py
Empty file.
Empty file removed api_service/api/v1/__init__.py
Empty file.
5 changes: 0 additions & 5 deletions api_service/api/v1/api.py

This file was deleted.

Empty file.
8 changes: 0 additions & 8 deletions api_service/api/v1/endpoints/test.py

This file was deleted.

16 changes: 0 additions & 16 deletions api_service/config/config.py

This file was deleted.

11 changes: 11 additions & 0 deletions api_service/controllers/login_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from dtos.login_dto import LoginDto

class LoginController:
def __init__(self, auth_service, make_login_request_validator):
self.__auth_service = auth_service
self.__make_login_request_validator = make_login_request_validator

def execute(self, request: LoginDto):
if not self.__make_login_request_validator.validate(request):
return False
return self.__auth_service.create_token(request.username, request.password)
15 changes: 15 additions & 0 deletions api_service/controllers/stock_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class StockController:
def __init__(self, stock_service, get_stock_request_validator):
self.__stock_service = stock_service
self.__get_stock_request_validator = get_stock_request_validator

def execute(self, symbol):
if not self.__get_stock_request_validator.validate(symbol):
return False
stock = self.__stock_service.get_stock(symbol)
if not stock:
return False
price = stock["cotacao"]
if price == 'N/D':
return False
return stock
5 changes: 5 additions & 0 deletions api_service/dtos/login_dto.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from pydantic import BaseModel

class LoginDto(BaseModel):
username: str
password: str
8 changes: 8 additions & 0 deletions api_service/factories/controllers/login_controller_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from validators.make_login_request_validator import MakeLoginRequestValidator
from gateways.auth_service_gateway import AuthServiceGateway
from controllers.login_controller import LoginController

def makeLoginControllerFactory():
auth_service = AuthServiceGateway()
make_login_request_validator = MakeLoginRequestValidator()
return LoginController(auth_service,make_login_request_validator)
8 changes: 8 additions & 0 deletions api_service/factories/controllers/stock_controller_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from validators.get_stock_request_validator import GetStockRequestValidator
from gateways.stock_service_gateway import StockServiceGateway
from controllers.stock_controller import StockController

def makeStockControllerFactory():
stock_service = StockServiceGateway()
make_stock_request_validator = GetStockRequestValidator()
return StockController(stock_service,make_stock_request_validator)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from gateways.auth_service_gateway import AuthServiceGateway
from middlewares.user_auth_middleware import UserAuthMiddleware

def makeUserAuthMiddlewareFactory():
auth_service = AuthServiceGateway()
return UserAuthMiddleware(auth_service)
30 changes: 30 additions & 0 deletions api_service/gateways/auth_service_gateway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

import requests
import os

class AuthServiceGateway:
def __init__(self):
self.__auth_service_url = os.getenv("AUTH_SERVICE_URL")

def validate_token(self, token):
try:
if not token.startswith('Basic '):
return False
formated_token = token.replace('Basic ', '')
response = requests.get(f"{self.__auth_service_url}/validate-token?token={formated_token}")
if response.status_code == 200:
return True
else:
return False
except Exception as e:
return False

def create_token(self, username, password):
try:
response = requests.post(f"{self.__auth_service_url}/login", json={"username": username, "password": password})
if response.status_code == 200:
return response.json().get("token")
else:
return False
except Exception as e:
return False
21 changes: 21 additions & 0 deletions api_service/gateways/file_log_builder_adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import requests
import json
import os

class FileLogBuilderGateway:
def __init__(self, title):
self.__auth_service_url = os.getenv("LOG_SERVICE_URL")
self.__title = title

def log(self, message):
try:
response = requests.post(
f"{self.__auth_service_url}/log", json={"title": self.__title, "message": message}
)
if response.status_code == 200:
return True
else:
return False
except Exception as e:
print(e)
return False
12 changes: 12 additions & 0 deletions api_service/gateways/stock_service_gateway.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import requests
import os

class StockServiceGateway:
def __init__(self):
self.__stock_service_url = os.getenv("STOCK_SERVICE_URL")

def get_stock(self, symbol):
response = requests.get(f"{self.__stock_service_url}/stock?symbol={symbol}")
if response.status_code != 200:
return False
return response.json()
Empty file removed api_service/logs/.gitkeep
Empty file.
Loading