Skip to content

Commit

Permalink
converting in api
Browse files Browse the repository at this point in the history
  • Loading branch information
rmottainfo committed Feb 15, 2024
1 parent 4f2e00f commit 64d751c
Show file tree
Hide file tree
Showing 74 changed files with 2,082 additions and 718 deletions.
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
README.md
.env.ex
.gitignore
.github
.git

tests/
3 changes: 3 additions & 0 deletions .env.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
EXCHANGE_RATES_API_KEY=
OPEN_EXCHANGE_API_KEY=
PORT=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Start from the latest golang base image
FROM golang:latest

# Maintainer Info
LABEL maintainer="Rodrigo Motta [email protected]"

# Set the Current Working Directory
WORKDIR /api

# Copy the source
COPY . .

# Download go modules
RUN go mod download

# Build the Go api
RUN go build -o main ./api/cmd

# Expose the port based on the environment variable
ARG PORT=8000
ENV PORT=${PORT}
EXPOSE ${PORT}

# Comando para executar sua aplicação quando o container for iniciado
CMD ["./main"]
69 changes: 56 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,65 @@
# CLI da Rentabilidade - Calcule Seu Lucro, ou Não 💸
# Profitability Golang

Bem-vindo ao CLI da Rentabilidade, a ferramenta que te dirá exatamente quanto dinheiro você está (ou não) ganhando nos seus investimentos! Porque quem precisa de riqueza, certo?
## ctrl+s :v:

## Sobre
O Profitability é uma API RESTful que fornece a rentabilidade líquida e a tributação correspondente para investimentos de renda fixa, como CBD, CRI, LC, LCA e LCI, pré ou pós-fixados, com referência à SELIC ou ao IPCA.

Este projeto magnífico é uma aplicação de linha de comando que devora avidamente a API do Banco Central do Brasil. O objetivo? Informar com uma rapidez impressionante a rentabilidade líquida de contratos de CDB, LC, LCI e LCA, com referência a Selic ou IPCA. Mágico, não?

### Modalidades Disponíveis
## Funcionalidades:

1. **Pré-Fixado**: Porque quem precisa de certezas na vida?
2. **Pós-Fixado**: Porque prever o futuro é superestimado.
3. **Pós-Fixado no IPCA + Taxa Fixa**: Porque adoramos adicionar complexidade à nossa vida financeira.
- A API RESTful oferece cinco rotas principais:
- Recuperação de dados em cache obtidos de consultas a dados diretamente obtidos do Banco Central através de sua [API](https://www.bcb.gov.br/).
- Cálculo da rentabilidade líquida considerando o desconto de impostos, a partir da taxa bruta nominal do contrato e da duração do contrato.

Se estiver se sentindo aventureiro, há o cálculo proporcional. Perfeito para contratos não tributados como LCI e LCA. Sim, isso mesmo, proporcional! Porque quem quer entender completamente suas finanças, não é mesmo?

## Como Usar
## Configuração e Inicialização:

1. Clone este repositório.
2. Execute `go run main.go --help` e deixe a magia acontecer.
- A aplicação carrega a configuração do ambiente, incluindo a porta na qual o servidor será executado.
- Utiliza o pacote `mux` para roteamento HTTP e `cors` para lidar com solicitações CORS.
- Inicializa serviços, controladores e cache.
- Integra-se com APIs externas para obter dados, como as taxas SELIC e IPCA.

Pronto! Agora você está no caminho para descobrir se está ficando rico ou apenas pagando mais impostos. Divirta-se! 💰

## Uso

Para usar a API de Rentabilidade, os desenvolvedores precisam enviar solicitações HTTP para os pontos finais designados. A API responde com dados JSON contendo as informações solicitadas ou valores de moeda convertidos.


## API Endpoints


- **Taxas Atuais:**

Este endpoint retorna as taxas de juros SELIC e IPCA utilizadas nos cálculos de rentabilidade.

Endpoint:

GET /api/taxas

Exemplo de Uso:

```bash
curl "http://localhost:8000/api/taxas"
```

Este exemplo retorna as taxas SELIC e IPCA em formato JSON.


Para obter informações mais detalhadas sobre as rotas e suas funcionalidades, consulte a [documentação central da API](https://rmottanet.gitbook.io/profitability).


## Contribuições

Contribuições para o projeto da API de Rentabilidade são bem-vindas! Se você tiver ideias para melhorias, solicitações de funcionalidades ou relatórios de bugs, sinta-se à vontade para abrir um problema ou enviar um pull request.

Obrigado por considerar a API de Rentabilidade para suas necessidades de cálculo. Se você tiver alguma dúvida ou precisar de mais assistência, não hesite em entrar em contato. Feliz codificação! 🚀

<br />
<br />
<p align="center">
<a href="https://gitlab.com/rmotta.net"><img src="https://img.shields.io/badge/Gitlab--_.svg?style=social&logo=gitlab" alt="GitLab"></a>
<a href="https://github.com/rmottanet"><img src="https://img.shields.io/badge/Github--_.svg?style=social&logo=github" alt="GitHub"></a>
<a href="https://instagram.com/rmottanet/"><img src="https://img.shields.io/badge/Instagram--_.svg?style=social&logo=instagram" alt="Instagram"></a>
<a href="https://www.linkedin.com/in/rmottanet/"><img src="https://img.shields.io/badge/Linkedin--_.svg?style=social&logo=linkedin" alt="Linkedin"></a>
</p>
<br />
87 changes: 87 additions & 0 deletions api/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"os"
"log"
"net/http"

"github.com/rs/cors"
"github.com/gorilla/mux"

"profitability/api/pkg/config"
"profitability/api/pkg/cache"
"profitability/api/pkg/integrations"
"profitability/api/pkg/middleware"
"profitability/api/pkg/controllers"
"profitability/api/pkg/services"
"profitability/api/pkg/html"
)

func main() {

config.LoadEnv()

PORT := os.Getenv("PORT")
if PORT == "" {
PORT = "8000"
}

corsHandler := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET"},
AllowedHeaders: []string{"Content-Type"},
AllowCredentials: true,
})

ratesCache := cache.NewRatesCache()

router := mux.NewRouter()

router.Use(corsHandler.Handler)

// Services
ratesService := services.NewRatesService(ratesCache)

propService := services.NewPropService(ratesCache)
preService := services.NewPreService()
posService := services.NewPosService(ratesCache)
ipcaService := services.NewIpcaService(ratesCache)

// Controllers
ratesController := controllers.NewRatesController(ratesService)

propController := controllers.NewPropController(propService)
preController := controllers.NewPreController(preService)
posController := controllers.NewPosController(posService)
ipcaController := controllers.NewIpcaController(ipcaService)

// Routes
router.HandleFunc("/api/rates", ratesController.GetRates).Methods("GET")

router.Handle("/api/prop", middleware.ValidateInput(middleware.ProfitHandler(propController.ProfitProp)))
router.Handle("/api/pre", middleware.ValidateInput(middleware.ProfitHandler(preController.ProfitPre)))
router.Handle("/api/pos", middleware.ValidateInput(middleware.ProfitHandler(posController.ProfitPos)))
router.Handle("/api/ipca", middleware.ValidateInput(middleware.ProfitHandler(ipcaController.ProfitIpca)))

// Ingetregrations
if _, err := integrations.GetSELICData(ratesCache); err != nil {
log.Fatalf("Error retrieving exchange rate data: %v", err)
}

if _, err := integrations.GetIPCAData(ratesCache); err != nil {
log.Fatalf("Error retrieving exchange rate data: %v", err)
}


router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

welcomeHTML := html.WelcomePageHTML()

w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusOK)
w.Write([]byte(welcomeHTML))
})

log.Printf("Server started on port %s\n", PORT)
log.Fatal(http.ListenAndServe(":"+PORT, router))
}
85 changes: 85 additions & 0 deletions api/pkg/cache/rates_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cache

import (
"fmt"
"sync"
"time"
)


type Cache interface {
Get(key string) (float64, bool)
Set(key string, value float64, expiration time.Duration)
GetAll() (map[string]float64, time.Time, error)
GetTimestamp() time.Time
}


type RatesCache struct {
cache map[string]float64
timeUpdated time.Time
mutex sync.RWMutex
}


func NewRatesCache() *RatesCache {
return &RatesCache{
cache: make(map[string]float64),
}
}


func (cc *RatesCache) Get(key string) (float64, bool) {
cc.mutex.RLock()
defer cc.mutex.RUnlock()
value, ok := cc.cache[key]
return value, ok
}


func (cc *RatesCache) Set(key string, value float64, expiration time.Duration) {
cc.mutex.Lock()
defer cc.mutex.Unlock()

// no update to zero or null
if value == 0 {
return
}

// check code on cache
if _, ok := cc.cache[key]; !ok {
// insert new codes
cc.cache[key] = value
cc.timeUpdated = time.Now().UTC()

go cc.deleteAfter(key, expiration)
}
}


func (cc *RatesCache) deleteAfter(key string, expiration time.Duration) {
<-time.After(expiration)
cc.mutex.Lock()
defer cc.mutex.Unlock()
delete(cc.cache, key)
}


func (cc *RatesCache) GetAll() (map[string]float64, time.Time, error) {
cc.mutex.RLock()
defer cc.mutex.RUnlock()

if cc.cache == nil || cc.timeUpdated.IsZero() {
return nil, time.Time{}, fmt.Errorf("Index rates cache empty or not updated")
}

return cc.cache, cc.timeUpdated, nil
}


func (cc *RatesCache) GetTimestamp() time.Time {
cc.mutex.RLock()
defer cc.mutex.RUnlock()

return cc.timeUpdated
}
31 changes: 31 additions & 0 deletions api/pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package config

import (
"os"
"log"
"github.com/joho/godotenv"
)


var EnvVars map[string]string


func LoadEnv() {
EnvVars = make(map[string]string)

// check .env
if _, err := os.Stat(".env"); err != nil {
if os.IsNotExist(err) {
log.Println("File .env not found, using environment variables from service")
return
}
log.Fatalf("Error checking .env file: %v", err)
}


if err := godotenv.Load(); err != nil {
log.Fatalf("Error loading .env file: %v", err)
}

EnvVars["PORT"] = os.Getenv("PORT")
}
62 changes: 62 additions & 0 deletions api/pkg/controllers/ipca_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package controllers

import (
"strconv"
"net/http"
"encoding/json"

"profitability/api/pkg/models"
"profitability/api/pkg/services"
)


type IpcaController struct {
Service services.IpcaServiceInterface
}


func NewIpcaController(service services.IpcaServiceInterface) *IpcaController {
return &IpcaController{
Service: service,
}
}


// Lida com a requisição de contratos pós fixados com referência ao IPCA.
func (ipcaCtrl *IpcaController) ProfitIpca(w http.ResponseWriter, r *http.Request) {
// Lida com os parâmetros da solicitação
rateStr := r.URL.Query().Get("rate")
termStr := r.URL.Query().Get("term")

rate, err := strconv.ParseFloat(rateStr, 64)
if err != nil {
http.Error(w, "Erro ao converter a taxa.", http.StatusBadRequest)
return
}

term, err := strconv.Atoi(termStr)
if err != nil {
http.Error(w, "Erro ao converter o prazo.", http.StatusBadRequest)
return
}

// Obém o resultado usando o serviço
ipcaResult, taxRatePercentage, err := ipcaCtrl.Service.Ipca(rate, term)
if err != nil {
http.Error(w, "Erro ao calcular rendimento pré fixado.", http.StatusInternalServerError)
return
}

// Cria a resposta com os valores obtidos do serviço
response := models.NewProfitResponse("IPCA+", ipcaResult, taxRatePercentage)

jsonResponse, err := json.Marshal(response)
if err != nil {
http.Error(w, "Error formatting response", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(jsonResponse)
}
Loading

0 comments on commit 64d751c

Please sign in to comment.