-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4f2e00f
commit 64d751c
Showing
74 changed files
with
2,082 additions
and
718 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
README.md | ||
.env.ex | ||
.gitignore | ||
.github | ||
.git | ||
|
||
tests/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
EXCHANGE_RATES_API_KEY= | ||
OPEN_EXCHANGE_API_KEY= | ||
PORT= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.