FIRST commit
All checks were successful
dev test / test (push) Successful in 17s
dev test / vulnCheck (push) Successful in 30s
dev test / Ci-Lint (push) Successful in 19s
${{ github.actor }} executed Build Push Prod / build (push) Successful in 4m24s
${{ github.actor }} executed Build Push Prod / deploy (push) Successful in 18s
All checks were successful
dev test / test (push) Successful in 17s
dev test / vulnCheck (push) Successful in 30s
dev test / Ci-Lint (push) Successful in 19s
${{ github.actor }} executed Build Push Prod / build (push) Successful in 4m24s
${{ github.actor }} executed Build Push Prod / deploy (push) Successful in 18s
This commit is contained in:
commit
4b60ebc7a7
14
.dir-locals.el
Normal file
14
.dir-locals.el
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
((nil (eglot-workspace-configuration . ((gopls . ((staticcheck . t)
|
||||||
|
(matcher . "CaseSensitive")
|
||||||
|
(hints . (
|
||||||
|
(compositeLiteralFields . t)
|
||||||
|
(compositeLiteralTypes . t)
|
||||||
|
(constantValues . t)
|
||||||
|
(functionTypeParameters . t)
|
||||||
|
(parameterNames . t)
|
||||||
|
(rangeVariableTypes . t)
|
||||||
|
(assignVariableTypes . t)
|
||||||
|
))))))
|
||||||
|
(eglot-server-programs
|
||||||
|
((go-mode go-ts-mode) . ("gopls")))
|
||||||
|
))
|
||||||
105
.github/workflows/image_creation.yml
vendored
Normal file
105
.github/workflows/image_creation.yml
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
name: ${{ github.actor }} executed Build Push Prod
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v[0-9]+\.[0-9]+\.[0-9]+'
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
USERNAME: ${{ vars.USERNAME }}
|
||||||
|
TAG_VERSION: ${{ github.ref_name }}
|
||||||
|
steps:
|
||||||
|
- name: echo repo dir
|
||||||
|
id: repo-url
|
||||||
|
run: |
|
||||||
|
echo "repourl=$(echo ${{ github.server_url }} | sed -e 's;https://;;')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Quemu
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set Up BuildX
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to Docker
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: https://git.maximotejeda.com
|
||||||
|
username: ${{ vars.USERNAME }}
|
||||||
|
password: ${{ secrets.PWD }}
|
||||||
|
|
||||||
|
- name: calculate short sha
|
||||||
|
id: calculate-sha
|
||||||
|
run: |
|
||||||
|
echo "shortsha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
tags: "${{ steps.repo-url.outputs.repourl }}/${{ github.repository }}:${{ github.ref_name }}"
|
||||||
|
build-args: |
|
||||||
|
version=${{ github.ref_name }}
|
||||||
|
SHORTSHA=${{ steps.calculate-sha.outputs.shortsha }}
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
TAG: ${{ github.ref_name }}
|
||||||
|
PASSWORD: $PWD
|
||||||
|
USERNAME: ${{ vars.USERNAME }}
|
||||||
|
TAG_VERSION: ${{ github.ref_name }}
|
||||||
|
NATS_SERVICE_URL: "${{ vars.NATS_SERVICE_URL }}"
|
||||||
|
ENV: "production"
|
||||||
|
APPLICATION_PORT: "${{ vars.APPLICATION_PORT }}"
|
||||||
|
DATA_SOURCE_URL: "${{ vars.DATA_SOURCE_URL }}"
|
||||||
|
steps:
|
||||||
|
- name: echo repo dir
|
||||||
|
id: repo-url
|
||||||
|
run: |
|
||||||
|
echo "repourl=$(echo ${{ github.server_url }} | sed -e 's;https://;;')" >> $GITHUB_OUTPUT
|
||||||
|
echo "repoName=$(echo ${{ github.repository }} | sed -e 's;${{ github.actor }}/;;')" >> $GITHUB_OUTPUT
|
||||||
|
echo "${{ github.repository }} | sed -e 's;${{ github.actor }}/;;')"
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: install envsubst
|
||||||
|
run: |
|
||||||
|
apt update
|
||||||
|
apt install -y gettext-base
|
||||||
|
apt clean
|
||||||
|
|
||||||
|
- name: create deployment files
|
||||||
|
env:
|
||||||
|
IMAGE: "${{ steps.repo-url.outputs.repourl }}/${{ github.repository }}:${{ github.ref_name }}"
|
||||||
|
REPONAME: "${{ steps.repo-url.outputs.repoName }}"
|
||||||
|
run: |
|
||||||
|
envsubst < k8s/deployment.yml.template > k8s/deployment.yml
|
||||||
|
- name: Download kubectl
|
||||||
|
run: curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
|
||||||
|
|
||||||
|
- name: install kubectl
|
||||||
|
run: sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
|
||||||
|
|
||||||
|
- name: insert kube config
|
||||||
|
run: |
|
||||||
|
mkdir ~/.kube
|
||||||
|
export IMAGE="$( echo ${{ github.server_url }} | sed -e 's;https://;;')/${{ github.repository }}:${{ github.ref_name }}"
|
||||||
|
echo "${{ secrets.KUBECONFIG }}" > ~/.kube/config
|
||||||
|
- name: check pod
|
||||||
|
run: kubectl apply -f k8s/deployment.yml
|
||||||
|
|
||||||
|
- name: scale down deployment
|
||||||
|
continue-on-error: true
|
||||||
|
run: kubectl scale deployment ${{ steps.repo-url.outputs.repoName }}-grpc --replicas=0
|
||||||
|
|
||||||
|
- name: scale up deployment
|
||||||
|
run: kubectl scale deployment ${{ steps.repo-url.outputs.repoName }}-grpc --replicas=1
|
||||||
68
.github/workflows/test_project.yml
vendored
Normal file
68
.github/workflows/test_project.yml
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
name: "dev test"
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- dev
|
||||||
|
- 'feature/**'
|
||||||
|
paths:
|
||||||
|
- '**.go'
|
||||||
|
- '**.yml'
|
||||||
|
- '**.yaml'
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
USERNAME: ${{ vars.USERNAME }}
|
||||||
|
TAG_VERSION: ${{ github.ref_name }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: 'go.mod'
|
||||||
|
- name: run tidy
|
||||||
|
run: |
|
||||||
|
go mod tidy
|
||||||
|
- name: test with go
|
||||||
|
run: go test -v ./...
|
||||||
|
|
||||||
|
vulnCheck:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: test
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
USERNAME: ${{ vars.USERNAME }}
|
||||||
|
TAG_VERSION: ${{ github.ref_name }}
|
||||||
|
steps:
|
||||||
|
- name: checkout Code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: go vuln check
|
||||||
|
uses: golang/govulncheck-action@v1
|
||||||
|
with:
|
||||||
|
go-version-file: 'go.mod'
|
||||||
|
go-package: ./...
|
||||||
|
|
||||||
|
Ci-Lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: vulnCheck
|
||||||
|
env:
|
||||||
|
TOKEN: ${{ secrets.TOKEN }}
|
||||||
|
USERNAME: ${{ vars.USERNAME }}
|
||||||
|
TAG_VERSION: ${{ github.ref_name }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: 'go.mod'
|
||||||
|
- name: CI lint Check
|
||||||
|
uses: golangci/golangci-lint-action@v6
|
||||||
|
with:
|
||||||
|
only-new-issues: true
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/.env
|
||||||
|
/bin/
|
||||||
|
/dolar.db
|
||||||
|
/dolardb/
|
||||||
|
k8s/deplayment.yml
|
||||||
|
/k8s/deployment.yml
|
||||||
|
/internal/ports/dolar.db
|
||||||
21
.vscode/launch.json
vendored
Normal file
21
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Package",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${fileDirname}/main.go",
|
||||||
|
"env": {
|
||||||
|
"DATA_SOURCE_URL": "dolar.db",
|
||||||
|
"APPLICATION_PORT": "3000",
|
||||||
|
"NATS_SERVICE_URL": ":4222",
|
||||||
|
"ENV" : "development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
Dockerfile
Normal file
17
Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
FROM golang:alpine AS builder
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG version=not-set
|
||||||
|
ARG SHORTSHA=not-set
|
||||||
|
WORKDIR /app
|
||||||
|
COPY . .
|
||||||
|
# https://stackoverflow.com/questions/70369368/check-architecture-in-dockerfile-to-get-amd-arm
|
||||||
|
RUN go build -o bin/dolar-grpc \
|
||||||
|
-ldflags "-X main.Shortsha=${SHORTSHA} \
|
||||||
|
-X main.Version=${version} \
|
||||||
|
-X main.Aarch=${TARGETARCH}" ./cmd
|
||||||
|
|
||||||
|
FROM alpine AS runner
|
||||||
|
COPY --from=builder /app/bin/dolar-grpc /usr/bin/
|
||||||
|
WORKDIR /app
|
||||||
|
ENTRYPOINT /usr/bin/dolar-grpc
|
||||||
66
Makefile
Normal file
66
Makefile
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# must create a .env file with info
|
||||||
|
# must have compose installed
|
||||||
|
include .env
|
||||||
|
export
|
||||||
|
OS:=${shell go env GOOS}
|
||||||
|
ARCH=$(shell go env GOARCH)
|
||||||
|
OOSS="linux"
|
||||||
|
ARRCHS="arm 386"
|
||||||
|
DEBUG=1
|
||||||
|
SERVICE=dolar-grpc
|
||||||
|
VERSION=0.0.0_7
|
||||||
|
BINAME=$(SERVICE)-$(OS)-amd64-$(VERSION)
|
||||||
|
BINAMEARM=$(SERVICE)-$(OS)-arm64-$(VERSION)
|
||||||
|
# can be docker or podman or whatever
|
||||||
|
CONTAINERS=docker
|
||||||
|
COMPOSE=$(CONTAINERS)-compose
|
||||||
|
# Configure local registry
|
||||||
|
REGADDR=git.maximotejeda.com/maximo
|
||||||
|
# K8SRSNAME=$(shell kubectl get rs --no-headers -o custom-columns=":metadata.name" | grep $(SERVICE))
|
||||||
|
.phony: all clean build test clean-image build-image build-image-debug run-image run-image-debug run-local
|
||||||
|
|
||||||
|
|
||||||
|
build-image: build
|
||||||
|
# here we made the images and push to registry with buildx
|
||||||
|
@$(CONTAINERS) buildx build --build-arg="BINAME=$(BINAMEARM)" --platform linux/arm64 --push -t $(REGADDR)/$(SERVICE):latest .
|
||||||
|
|
||||||
|
# Here we upload it to local
|
||||||
|
|
||||||
|
build-test-image:
|
||||||
|
@$(CONTAINERS) buildx build --build-arg="BINAME=$(BINAMEARM)" --platform linux/arm64 --push -t $(REGADDR)/$(SERVICE):latest .
|
||||||
|
|
||||||
|
run-image: build-image
|
||||||
|
@$(CONTAINERS) compose -f docker-compose.yaml up
|
||||||
|
|
||||||
|
build-image-debug: clean
|
||||||
|
@$(CONTAINERS) compose -f docker-compose-debug.yaml build
|
||||||
|
|
||||||
|
run-image-debug: build-image-debug
|
||||||
|
@$(CONTAINERS) compose -f docker-compose-debug.yaml up
|
||||||
|
|
||||||
|
run-local:clean build
|
||||||
|
@bin/$(BINAME)
|
||||||
|
|
||||||
|
build: clean
|
||||||
|
#@mkdir dolardb
|
||||||
|
@protoc -I ./proto --go_out ./proto/golang --go_opt paths=source_relative --go-grpc_out ./proto/golang --go-grpc_opt paths=source_relative ./proto/dolar/dolar.proto
|
||||||
|
@env CGO_ENABLED=0 GOOS=$(OS) GOARCH=amd64 go build -o ./bin/$(BINAME) ./cmd/.
|
||||||
|
@env CGO_ENABLED=0 GOOS=$(OS) GOARCH=arm64 go build -o ./bin/$(BINAMEARM) ./cmd/.
|
||||||
|
|
||||||
|
create-descriptors:
|
||||||
|
@envsubst < k8s/deployment.yml.template > k8s/deployment.yml
|
||||||
|
|
||||||
|
deploy: build-image create-descriptors
|
||||||
|
@kubectl apply -f k8s/deployment.yml
|
||||||
|
@kubectl scale rs $(K8SRSNAME) --replicas=0
|
||||||
|
@kubectl scale rs $(K8SRSNAME) --replicas=1
|
||||||
|
|
||||||
|
test:
|
||||||
|
@go -count=1 test ./...
|
||||||
|
clean:
|
||||||
|
@rm -rf ./bin
|
||||||
|
|
||||||
|
clean-image:
|
||||||
|
@$(CONTAINERS) system prune -f
|
||||||
|
|
||||||
|
|
||||||
32
cmd/main.go
Normal file
32
cmd/main.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.maximotejeda.com/maximo/dolar/config"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/adapter/db"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/adapter/grpc"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/adapter/nats"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/application/core/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log := slog.Default()
|
||||||
|
log.With("adapter", "main")
|
||||||
|
natsAdapter, err := nats.NewAdapter(config.GetNatsServiceUrl())
|
||||||
|
if err != nil {
|
||||||
|
log.Error("failed to connect to nats.", "error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbAdapter, err := db.NewAdapter(config.GetDataSourceURL(), natsAdapter)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
log.Error("failed to connect to database.", "error", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
application := api.NewApplication(dbAdapter)
|
||||||
|
grpcAdapter := grpc.NewAdapter(application, config.GetApplicationPort())
|
||||||
|
log.Info("grpc server running", "port", config.GetApplicationPort())
|
||||||
|
grpcAdapter.Run()
|
||||||
|
}
|
||||||
35
config/config.go
Normal file
35
config/config.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetEnv() string {
|
||||||
|
return getEnvironmentValue("ENV")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDataSourceURL() string {
|
||||||
|
return getEnvironmentValue("DATA_SOURCE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetApplicationPort() int {
|
||||||
|
portStr := getEnvironmentValue("APPLICATION_PORT")
|
||||||
|
port, err := strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("port: %s is invalid", portStr)
|
||||||
|
}
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetNatsServiceUrl() string {
|
||||||
|
return getEnvironmentValue("NATS_SERVICE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEnvironmentValue(key string) string {
|
||||||
|
if os.Getenv(key) == "" {
|
||||||
|
log.Fatalf("%s environment variable is missing.", key)
|
||||||
|
}
|
||||||
|
return os.Getenv(key)
|
||||||
|
}
|
||||||
34
go.mod
Normal file
34
go.mod
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
module git.maximotejeda.com/maximo/dolar
|
||||||
|
|
||||||
|
|
||||||
|
go 1.23
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/nats-io/nats.go v1.34.1
|
||||||
|
google.golang.org/grpc v1.63.2
|
||||||
|
google.golang.org/protobuf v1.33.0
|
||||||
|
modernc.org/sqlite v1.29.6
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
|
github.com/klauspost/compress v1.17.2 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||||
|
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||||
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
golang.org/x/crypto v0.22.0 // indirect
|
||||||
|
golang.org/x/net v0.24.0 // indirect
|
||||||
|
golang.org/x/sys v0.19.0 // indirect
|
||||||
|
golang.org/x/text v0.14.0 // indirect
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
|
modernc.org/libc v1.41.0 // indirect
|
||||||
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
|
modernc.org/memory v1.7.2 // indirect
|
||||||
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
|
modernc.org/token v1.1.0 // indirect
|
||||||
|
)
|
||||||
65
go.sum
Normal file
65
go.sum
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||||
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
|
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
|
||||||
|
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||||
|
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
|
github.com/nats-io/nats.go v1.34.1 h1:syWey5xaNHZgicYBemv0nohUPPmaLteiBEUT6Q5+F/4=
|
||||||
|
github.com/nats-io/nats.go v1.34.1/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||||
|
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||||
|
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||||
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||||
|
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||||
|
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||||
|
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||||
|
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
|
||||||
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||||
|
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||||
|
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||||
|
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||||
|
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
|
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
|
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||||
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
|
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
||||||
|
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
||||||
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
|
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||||
|
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
||||||
|
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
||||||
|
modernc.org/sqlite v1.29.6 h1:0lOXGrycJPptfHDuohfYgNqoe4hu+gYuN/pKgY5XjS4=
|
||||||
|
modernc.org/sqlite v1.29.6/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U=
|
||||||
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
453
internal/adapter/db/db.go
Normal file
453
internal/adapter/db/db.go
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
package db
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
_ "embed"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/adapter/nats"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/application/core/domain"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/ports"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed schema.sql
|
||||||
|
var schema string
|
||||||
|
|
||||||
|
type History struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
NameID int64 `json:"name_id"`
|
||||||
|
Compra float64 `json:"compra"`
|
||||||
|
Venta float64 `json:"venta"`
|
||||||
|
Parser string `json:"parser"`
|
||||||
|
Parsed int64 `json:"parsed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Institution struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ShortName string `json:"short_name"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Adapter struct {
|
||||||
|
db *sql.DB
|
||||||
|
log *slog.Logger
|
||||||
|
nats ports.NATSPort
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdapter(dataSourceURL string, natsConn ports.NATSPort) (*Adapter, error) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
log := slog.Default().With("adapter", "db")
|
||||||
|
pragmas := "cache=shared&_foreign_keys=on&_busy_timeout=3000&_journal_mode=WAL"
|
||||||
|
db, err := sql.Open("sqlite", fmt.Sprintf("file:%s?%s", dataSourceURL, pragmas))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("connecion error: %w", err)
|
||||||
|
}
|
||||||
|
err = db.PingContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("ping error: %w", err)
|
||||||
|
}
|
||||||
|
db.SetConnMaxIdleTime(10 * time.Second)
|
||||||
|
CreateTables(db)
|
||||||
|
// TODO create tables and trigers on first run
|
||||||
|
return &Adapter{db: db, log: log, nats: natsConn}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schema
|
||||||
|
func CreateTables(db *sql.DB) {
|
||||||
|
_, err := db.Exec(schema)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save
|
||||||
|
// TODO
|
||||||
|
func (a *Adapter) Save(history *domain.History) error {
|
||||||
|
if a.db == nil {
|
||||||
|
return fmt.Errorf("nil or empty database")
|
||||||
|
}
|
||||||
|
if history == nil {
|
||||||
|
return fmt.Errorf("nil struct passed [%v]", history)
|
||||||
|
}
|
||||||
|
inst, err := a.GETInstitution(history.Institution.Name)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("geting inst ", "error", err)
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
change := domain.NewChange(domain.History{}, *history)
|
||||||
|
message := domain.NewMessage("change adding institution", change, err)
|
||||||
|
if v, ok := a.nats.(*nats.Adapter); ok && v != nil {
|
||||||
|
err = a.nats.NewInstRegistered(message)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
id, _ := a.ADDInstitution(history.Institution.Name, history.Institution.ShortName)
|
||||||
|
inst = &Institution{ID: id}
|
||||||
|
a.log.Info("Adding institution", "inst", inst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: look for institution inside db
|
||||||
|
// If inst is alredy in db pass ID to History
|
||||||
|
// Else Create a new institution
|
||||||
|
hist := History{
|
||||||
|
NameID: inst.ID,
|
||||||
|
Compra: history.Compra,
|
||||||
|
Venta: history.Venta,
|
||||||
|
Parser: history.Parser,
|
||||||
|
Parsed: history.Parsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
lH, err := a.GetLatest(history.Institution.Name)
|
||||||
|
|
||||||
|
if errors.Is(err, sql.ErrNoRows ) {
|
||||||
|
a.log.Info("adding new item to table: ", "parse", history.Parser, "name", history.Institution.Name)
|
||||||
|
change := domain.NewChange(domain.History{}, *history)
|
||||||
|
message := domain.NewMessage("change adding institution", change, err)
|
||||||
|
if v, ok := a.nats.(*nats.Adapter); ok && v != nil {
|
||||||
|
err = a.nats.ChangeRegistered(message)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a.AddNew(hist, inst.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if history.Compra == lH.Compra && history.Venta == lH.Venta {
|
||||||
|
return nil
|
||||||
|
}else if history.Compra == 0 || history.Venta == 0 {
|
||||||
|
return nil
|
||||||
|
}else {
|
||||||
|
// if one of them changes create a new row
|
||||||
|
a.log.Info("change registered, adding item", "parse", history.Parser, "name", history.Institution.Name, "compra enter", history.Compra, "compra db", hist.Compra, "venta enter", history.Venta, "venta db", hist.Venta)
|
||||||
|
ins, err := a.GETInstitution(history.Institution.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
change := domain.NewChange(*lH, *history)
|
||||||
|
message := domain.NewMessage("change registered", change, nil)
|
||||||
|
if v, ok := a.nats.(*nats.Adapter); ok && v != nil {
|
||||||
|
a.log.Info("calling nats", "struct", ok)
|
||||||
|
err = a.nats.ChangeRegistered(message)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("sending change to nats", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.AddNew(hist, int64(ins.ID))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLatest
|
||||||
|
// TODO
|
||||||
|
func (a *Adapter) GetLatest(name string) (*domain.History, error) {
|
||||||
|
inst := Institution{}
|
||||||
|
|
||||||
|
// TODO check inst in db and get latest price
|
||||||
|
hist := &History{}
|
||||||
|
stmtt, err := a.db.Prepare(`
|
||||||
|
SELECT i.id, i.name, i.short_name, h.parser, h.compra, h.venta, h.parsed FROM histories AS h
|
||||||
|
JOIN institutions as i ON h.name_id = i.id
|
||||||
|
JOIN actual_price as ac ON i.id = ac.inst_id AND h.id = ac.hist_id
|
||||||
|
WHERE i.name = ? AND ac.hist_id = h.id;`)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("preparing stmtt", "error", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmtt.Close()
|
||||||
|
|
||||||
|
if err := stmtt.QueryRow(name).Scan(&hist.ID, &inst.Name, &inst.ShortName, &hist.Parser, &hist.Compra, &hist.Venta, &hist.Parsed); err != nil {
|
||||||
|
a.log.Error("getting latest", "error", err.Error(), "parser", hist.Parser, "name", name)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dHist := &domain.History{
|
||||||
|
ID: hist.ID,
|
||||||
|
Institution: domain.Institution{Name: inst.Name, ShortName: inst.ShortName},
|
||||||
|
Compra: hist.Compra,
|
||||||
|
Venta: hist.Venta,
|
||||||
|
Parser: hist.Parser,
|
||||||
|
Parsed: hist.Parsed,
|
||||||
|
}
|
||||||
|
|
||||||
|
return dHist, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSince
|
||||||
|
// TODO
|
||||||
|
func (a *Adapter) GetSince(name string, duration int64) (hists []*domain.History, err error) {
|
||||||
|
tDuration := time.Now().Add(-time.Minute * time.Duration(duration)).Unix()
|
||||||
|
a.log.Info("getsince", "name", name, "duration", duration, "unixDuration", tDuration)
|
||||||
|
stmt, err := a.db.Prepare("SELECT h.id, i.name, h.parser, h.compra, h.venta, h.parsed FROM histories AS h JOIN institutions as i ON h.name_id = i.id WHERE i.name = ? AND h.parsed > ? ORDER BY parsed DESC;")
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("[GetChangeSince] preparing", "error", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.Query(name, tDuration)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("[GetChangeSince] preparing", "error", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
inst := Institution{}
|
||||||
|
hist := History{}
|
||||||
|
if err := rows.Scan(&hist.ID, &inst.Name, &hist.Parser, &hist.Compra, &hist.Venta, &hist.Parsed); err != nil {
|
||||||
|
a.log.Error("[GetChangeSince] scanning", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hists = append(hists,
|
||||||
|
&domain.History{
|
||||||
|
ID: hist.ID,
|
||||||
|
Institution: domain.Institution{
|
||||||
|
Name: inst.Name,
|
||||||
|
},
|
||||||
|
Compra: hist.Compra,
|
||||||
|
Venta: hist.Venta,
|
||||||
|
Parser: hist.Parser,
|
||||||
|
Parsed: hist.Parsed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return hists, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetInstByType
|
||||||
|
// Get institutions names if bancos, cajas or agentes is passed
|
||||||
|
func (a *Adapter) GetInstByType(name string) ([]string, error) {
|
||||||
|
var (
|
||||||
|
stmt *sql.Stmt
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
switch name {
|
||||||
|
case "bancos":
|
||||||
|
stmt, err = a.db.Prepare(`
|
||||||
|
SELECT i.name
|
||||||
|
FROM institutions AS i
|
||||||
|
JOIN histories AS h ON i.id = h.name_id
|
||||||
|
WHERE (i.name LIKE '%ban%' OR i.name LIKE '%scoti%') AND h.name_id IS NOT NULL
|
||||||
|
`)
|
||||||
|
|
||||||
|
case "cajas":
|
||||||
|
stmt, err = a.db.Prepare(`
|
||||||
|
SELECT i.name
|
||||||
|
FROM institutions AS i
|
||||||
|
JOIN histories AS h ON i.id = h.name_id
|
||||||
|
WHERE i.name LIKE '%asociacion%' AND h.name_id IS NOT NULL`)
|
||||||
|
case "agentes":
|
||||||
|
stmt, err = a.db.Prepare(`
|
||||||
|
SELECT i.name FROM
|
||||||
|
institutions AS i
|
||||||
|
JOIN histories AS h ON i.id = h.name_id
|
||||||
|
WHERE i.name NOT LIKE '%ban%' AND i.name NOT LIKE '%scoti%' AND i.name NOT LIKE '%asociacion%'`)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("name not recognized")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("[inst-GetAll]", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.Query()
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("[inst-GetAll-stmt]", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
insts := []string{}
|
||||||
|
for rows.Next() {
|
||||||
|
inst := ""
|
||||||
|
if err = rows.Scan(&inst); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if inst == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
insts = append(insts, inst)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return insts, err
|
||||||
|
}
|
||||||
|
return insts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Adapter) ADDInstitution(name, short string) (id int64, err error) {
|
||||||
|
stmt, err := a.db.Prepare("INSERT INTO institutions (name, short_name, created) VALUES(?,?,?);")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
parsed := time.Now().Format(time.DateTime)
|
||||||
|
res, err := stmt.Exec(&name, short, &parsed)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
id, err = res.LastInsertId()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// GETInstitutionByID
|
||||||
|
func (a *Adapter) GETInstitutionByIDS(ids string) (instList []*Institution, err error) {
|
||||||
|
|
||||||
|
stmtt, err := a.db.Prepare(fmt.Sprintf("SELECT id, name, short_name FROM institutions WHERE id IN (%s)", ids))
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("preparing stmt", "error", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmtt.Close()
|
||||||
|
|
||||||
|
rows, err := stmtt.Query()
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("getting institution", "error", err.Error(), "id", ids)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
insti := Institution{}
|
||||||
|
if err = rows.Scan(&insti.ID, &insti.Name, &insti.ShortName); err != nil {
|
||||||
|
a.log.Error("[getting institution scanning]", "error", err.Error(), "id", ids)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
instList = append(instList, &insti)
|
||||||
|
}
|
||||||
|
return instList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GETInstitution
|
||||||
|
func (a *Adapter) GETInstitution(name string) (inst *Institution, err error) {
|
||||||
|
inst = &Institution{}
|
||||||
|
stmtt, err := a.db.Prepare("SELECT i.id, i.name, i.short_name FROM institutions AS i WHERE i.name = ?")
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("preparing stmt", "error", err.Error())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmtt.Close()
|
||||||
|
if err := stmtt.QueryRow(name).Scan(&inst.ID, &inst.Name, &inst.ShortName); err != nil {
|
||||||
|
a.log.Error("getting institution", "error", err.Error(), "short name", inst.ShortName, "name", name)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return inst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddNew
|
||||||
|
// Add a new row in the dolar table
|
||||||
|
// Will send to nats changes on prices
|
||||||
|
func (a *Adapter) AddNew(row History, id int64) error {
|
||||||
|
stmt, err := a.db.Prepare("INSERT INTO histories (name_id, compra, venta, parser, parsed) VALUES(?,?,?,?,?);")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
_, err = stmt.Exec(&id, &row.Compra, &row.Venta, &row.Parser, time.Now().Unix())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) GetInstByName(name string) (*domain.Institution, error) {
|
||||||
|
inst, err := a.GETInstitution(name)
|
||||||
|
insti := &domain.Institution{
|
||||||
|
ID: inst.ID,
|
||||||
|
Name: inst.Name,
|
||||||
|
ShortName: inst.ShortName,
|
||||||
|
Created: inst.Created,
|
||||||
|
}
|
||||||
|
return insti, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) TGBSubscribe(tgb_id int64, inst_name string) (bool, error) {
|
||||||
|
stmt, err := a.db.Prepare(`
|
||||||
|
INSERT INTO subscriptions(tgb_id, inst_id, created)
|
||||||
|
VALUES(?,(SELECT id FROM institutions WHERE name = ?),strftime('%s', 'now'));
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
_, err = stmt.Exec(tgb_id, inst_name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
func (a Adapter) TGBUnsubscribe(tgb_id int64, inst_name string) (bool, error) {
|
||||||
|
stmt, err := a.db.Prepare(`
|
||||||
|
DELETE FROM subscriptions
|
||||||
|
WHERE tgb_id = ? AND inst_id = (SELECT id FROM institutions WHERE name = ?)
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
_, err = stmt.Exec(tgb_id, inst_name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
func (a Adapter) TGBGetSubscribedUsers(inst_name string) ([]int64, error) {
|
||||||
|
stmt, err := a.db.Prepare(`
|
||||||
|
SELECT s.tgb_id FROM subscriptions AS s
|
||||||
|
JOIN institutions AS i ON s.inst_id = i.id
|
||||||
|
WHERE i.name = ?;
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.Query(inst_name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
tgbList := []int64{}
|
||||||
|
for rows.Next() {
|
||||||
|
var tgb int64
|
||||||
|
err := rows.Scan(&tgb)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tgbList = append(tgbList, tgb)
|
||||||
|
}
|
||||||
|
return tgbList, nil
|
||||||
|
}
|
||||||
|
func (a Adapter) TGBGetSubscribedInst(tgb_id int64) ([]string, error) {
|
||||||
|
stmt, err := a.db.Prepare(`
|
||||||
|
SELECT i.name FROM subscriptions AS s
|
||||||
|
JOIN institutions AS i ON s.inst_id = i.id
|
||||||
|
WHERE s.tgb_id = ?;
|
||||||
|
`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
rows, err := stmt.Query(tgb_id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
instList := []string{}
|
||||||
|
for rows.Next() {
|
||||||
|
var inst string
|
||||||
|
err := rows.Scan(&inst)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
instList = append(instList, inst)
|
||||||
|
}
|
||||||
|
return instList, nil
|
||||||
|
}
|
||||||
41
internal/adapter/db/institutions_202404151807.sql
Normal file
41
internal/adapter/db/institutions_202404151807.sql
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
insert into institutions (id,name,short_name,created) values
|
||||||
|
(1,'banco activo dominicana','bacd','2024-02-29 23:23:13'),
|
||||||
|
(2,'girosol','girosol','2024-02-29 23:23:14'),
|
||||||
|
(3,'scotiabank cambio online','scline','2024-02-29 23:23:15'),
|
||||||
|
(4,'banreservas','brd','2024-02-29 23:23:15'),
|
||||||
|
(5,'moneycorps','moneycorps','2024-02-29 23:23:17'),
|
||||||
|
(6,'imbert y balbuena','imb','2024-02-29 23:23:18'),
|
||||||
|
(7,'rm','rm','2024-02-29 23:23:18'),
|
||||||
|
(8,'motor credito','mcr','2024-02-29 23:23:19'),
|
||||||
|
(9,'cambio extranjero','cex','2024-02-29 23:23:20'),
|
||||||
|
(10,'capla','capla','2024-02-29 23:23:20');
|
||||||
|
insert into institutions (id,name,short_name,created) values
|
||||||
|
(11,'scotiabank','scotiabank','2024-02-29 23:23:21'),
|
||||||
|
(12,'banco promerica','bpr','2024-02-29 23:23:22'),
|
||||||
|
(13,'asociacion cibao de ahorros y prestamos','acap','2024-02-29 23:23:22'),
|
||||||
|
(14,'banco bdi','bbd','2024-02-29 23:23:23'),
|
||||||
|
(15,'banco caribe','bca','2024-02-29 23:23:23'),
|
||||||
|
(16,'taveras','taveras','2024-02-29 23:23:24'),
|
||||||
|
(17,'gamelin','gamelin','2024-02-29 23:23:24'),
|
||||||
|
(18,'sct','sct','2024-02-29 23:23:25'),
|
||||||
|
(19,'banco santa cruz','bsc','2024-02-29 23:23:25'),
|
||||||
|
(20,'asociacion la nacional de ahorros y prestamos','alnap','2024-02-29 23:23:26');
|
||||||
|
insert into institutions (id,name,short_name,created) values
|
||||||
|
(21,'bonanza banco','bba','2024-02-29 23:23:26'),
|
||||||
|
(22,'banco popular','bpd','2024-02-29 23:23:26'),
|
||||||
|
(23,'banco atlantico','bat','2024-02-29 23:23:27'),
|
||||||
|
(24,'asociacion peravia de ahorros y prestamos','apeap','2024-02-29 23:23:27'),
|
||||||
|
(25,'asociacion popular de ahorros y prestamos','apap','2024-02-29 23:23:28'),
|
||||||
|
(26,'banco lopez de haro','blh','2024-02-29 23:23:28'),
|
||||||
|
(27,'banco ademi','bad','2024-02-29 23:23:29'),
|
||||||
|
(28,'banco lafise','bla','2024-02-29 23:23:31'),
|
||||||
|
(29,'banreservas','brd','2024-02-29 23:24:03'),
|
||||||
|
(30,'asociacion popular de ahorros y prestamos','apap','2024-02-29 23:25:23');
|
||||||
|
insert into institutions (id,name,short_name,created) values
|
||||||
|
(31,'banco central dominicano','bcd','2024-02-29 23:33:23'),
|
||||||
|
(32,'banco popular','bpd','2024-02-29 23:35:25'),
|
||||||
|
(33,'banco hipotecario dominicano','bhd','2024-02-29 23:38:21'),
|
||||||
|
(34,'banesco','banesco','2024-03-22 16:11:13'),
|
||||||
|
(35,'banco vimenca','bvi','2024-03-22 16:14:42'),
|
||||||
|
(36,'scotiabank cambio online','scline','2024-03-22 16:17:09'),
|
||||||
|
(37,'scotiabank','scotiabank','2024-03-22 16:17:10');
|
||||||
79
internal/adapter/db/schema.sql
Normal file
79
internal/adapter/db/schema.sql
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
PRAGMA foreign_keys = ON;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS histories (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name_id INTEGER NOT NULL,
|
||||||
|
compra REAL NOT NULL,
|
||||||
|
venta REAL NOT NULL,
|
||||||
|
parser TEXT NOT NULL,
|
||||||
|
parsed INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(name_id) REFERENCES institutions(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS institutions (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL UNIQUE,
|
||||||
|
short_name TEXT NOT NULL UNIQUE,
|
||||||
|
created INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- SAVE the last record with the last price for an inst
|
||||||
|
CREATE TABLE IF NOT EXISTS actual_price (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
inst_id INTEGER,
|
||||||
|
hist_id INTEGER,
|
||||||
|
FOREIGN KEY(inst_id) REFERENCES institutions(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY(hist_id) REFERENCES histories(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS subscriptions (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
tgb_id INTEGER NOT NULL,
|
||||||
|
inst_id INTEGER NOT NULL,
|
||||||
|
created INTEGER NOT NULL,
|
||||||
|
FOREIGN KEY(inst_id) REFERENCES institutions(id) ON DELETE CASCADE,
|
||||||
|
UNIQUE(tgb_id, inst_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TRIGGER IF NOT EXISTS ai_history
|
||||||
|
AFTER INSERT ON histories
|
||||||
|
BEGIN
|
||||||
|
INSERT OR REPLACE INTO actual_price(id, inst_id, hist_id)
|
||||||
|
VALUES ((SELECT ap.id from actual_price as ap WHERE ap.inst_id = new.name_id), new.name_id, new.id);
|
||||||
|
END;
|
||||||
|
|
||||||
|
|
||||||
|
insert or ignore into institutions (name,short_name,created) values
|
||||||
|
('banco central dominicano','bcd','2024-02-29 23:33:23'),
|
||||||
|
('banco popular','bpd','2024-02-29 23:35:25'),
|
||||||
|
('banco hipotecario dominicano','bhd','2024-02-29 23:38:21'),
|
||||||
|
('banreservas','brd','2024-02-29 23:23:15'),
|
||||||
|
('asociacion peravia de ahorros y prestamos','apeap','2024-02-29 23:23:27'),
|
||||||
|
('asociacion popular de ahorros y prestamos','apap','2024-02-29 23:23:28'), ('asociacion cibao de ahorros y prestamos','acap','2024-02-29 23:23:22'), ('asociacion la nacional de ahorros y prestamos','alnap','2024-02-29 23:23:26');
|
||||||
|
insert or ignore into institutions (name,short_name,created) values
|
||||||
|
('banco promerica','bpr','2024-02-29 23:23:22'),
|
||||||
|
('banco bdi','bbd','2024-02-29 23:23:23'),
|
||||||
|
('banco caribe','bca','2024-02-29 23:23:23'),
|
||||||
|
('banco santa cruz','bsc','2024-02-29 23:23:25'),
|
||||||
|
('banco vimenca','bvi','2024-03-22 16:14:42'),
|
||||||
|
('scotiabank cambio online','scline','2024-03-22 16:17:09'),
|
||||||
|
('scotiabank','scotiabank','2024-03-22 16:17:10');
|
||||||
|
insert or ignore into institutions (name,short_name,created) values
|
||||||
|
('bonanza banco','bba','2024-02-29 23:23:26'),
|
||||||
|
('banco atlantico','bat','2024-02-29 23:23:27'),
|
||||||
|
('banco lopez de haro','blh','2024-02-29 23:23:28'),
|
||||||
|
('banco ademi','bad','2024-02-29 23:23:29'),
|
||||||
|
('banco lafise','bla','2024-02-29 23:23:31'),
|
||||||
|
('banesco','banesco','2024-03-22 16:11:13'),
|
||||||
|
('banco activo dominicana','bacd','2024-02-29 23:23:13');
|
||||||
|
insert or ignore into institutions (name,short_name,created) values
|
||||||
|
('girosol','girosol','2024-02-29 23:23:14'),
|
||||||
|
('moneycorps','moneycorps','2024-02-29 23:23:17'),
|
||||||
|
('imbert y balbuena','imb','2024-02-29 23:23:18'),
|
||||||
|
('rm','rm','2024-02-29 23:23:18'),
|
||||||
|
('motor credito','mcr','2024-02-29 23:23:19'),
|
||||||
|
('cambio extranjero','cex','2024-02-29 23:23:20'),
|
||||||
|
('capla','capla','2024-02-29 23:23:20'),
|
||||||
|
('taveras','taveras','2024-02-29 23:23:24'),
|
||||||
|
('gamelin','gamelin','2024-02-29 23:23:24'),
|
||||||
|
('sct','sct','2024-02-29 23:23:25');
|
||||||
110
internal/adapter/grpc/grpc.go
Normal file
110
internal/adapter/grpc/grpc.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/application/core/domain"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/proto/golang/dolar"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a Adapter) NewHistory(ctx context.Context, request *dolar.AddDolarRequest) (*dolar.AddDolarResponse, error) {
|
||||||
|
newHistory := domain.NewHistory(
|
||||||
|
request.Institution.Name,
|
||||||
|
request.Institution.Parser,
|
||||||
|
request.Institution.Parser,
|
||||||
|
float64(request.Institution.Compra),
|
||||||
|
float64(request.Institution.Venta))
|
||||||
|
result, err := a.api.NewHistory(&newHistory)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &dolar.AddDolarResponse{Id: result.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) GetLatest(ctx context.Context, name *dolar.GetLatestRequest) (*dolar.GetLatestResponse, error) {
|
||||||
|
lastHistory, err := a.api.GetLatest(name.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dH := dolar.History{
|
||||||
|
Id: lastHistory.ID,
|
||||||
|
Name: lastHistory.Institution.Name,
|
||||||
|
Compra: float32(lastHistory.Compra),
|
||||||
|
Venta: float32(lastHistory.Venta),
|
||||||
|
Parser: lastHistory.Parser,
|
||||||
|
Parsed: lastHistory.Parsed,
|
||||||
|
}
|
||||||
|
return &dolar.GetLatestResponse{Actual: &dH}, nil
|
||||||
|
}
|
||||||
|
func (a Adapter) GetSince(ctx context.Context, req *dolar.GetSinceRequest) (*dolar.GetMultipleResponse, error) {
|
||||||
|
ch, err := a.api.GetSince(req.Name, req.Duration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
historiesList := []*dolar.History{}
|
||||||
|
for _, his := range ch {
|
||||||
|
h := dolar.History{
|
||||||
|
Id: his.ID,
|
||||||
|
Name: his.Institution.Name,
|
||||||
|
Compra: float32(his.Compra),
|
||||||
|
Venta: float32(his.Venta),
|
||||||
|
Parser: his.Parser,
|
||||||
|
Parsed: his.Parsed,
|
||||||
|
}
|
||||||
|
historiesList = append(historiesList, &h)
|
||||||
|
}
|
||||||
|
return &dolar.GetMultipleResponse{Histories: historiesList}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) GetInstByType(ctx context.Context, req *dolar.GetInstByTypeRequest) (*dolar.GetInstByTypeResponse, error) {
|
||||||
|
il, err := a.api.GetInstByType(req.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dolar.GetInstByTypeResponse{InstList: il}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) GetInstByName(ctx context.Context, req *dolar.GetInstByNameRequest) (*dolar.GetInstByNameResponse, error) {
|
||||||
|
inst, err := a.api.GetInstByName(req.InstName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
in := &dolar.Institution{
|
||||||
|
Id: inst.ID,
|
||||||
|
Name: inst.Name,
|
||||||
|
ShortName: inst.ShortName,
|
||||||
|
Created: inst.Created,
|
||||||
|
}
|
||||||
|
return &dolar.GetInstByNameResponse{Institution: in}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) TGBSubscribe(ctx context.Context, req *dolar.TGBSubscribeRequest) (*dolar.TGBSubsResponse, error) {
|
||||||
|
ok, err := a.api.TGBSubscribe(req.TgbId, req.InstName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dolar.TGBSubsResponse{Response: ok}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) TGBUnsubscribe(ctx context.Context, req *dolar.TGBUnsubscribeRequest) (*dolar.TGBSubsResponse, error) {
|
||||||
|
ok, err := a.api.TGBUnsubscribe(req.TgbId, req.InstName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dolar.TGBSubsResponse{Response: ok}, nil
|
||||||
|
}
|
||||||
|
func (a Adapter) TGBGetSubscribedUsers(ctx context.Context, req *dolar.TGBGetSubscribedUsersRequest) (*dolar.TGBGetSubscribedUsersResponse, error) {
|
||||||
|
userList, err := a.api.TGBGetSubscribedUsers(req.InstName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dolar.TGBGetSubscribedUsersResponse{TgbIds: userList}, nil
|
||||||
|
}
|
||||||
|
func (a Adapter) TGBGetSubscribedInsts(ctx context.Context, req *dolar.TGBGetSubscribedInstRequest) (*dolar.TGBGetSubscribedInstResponse, error) {
|
||||||
|
userList, err := a.api.TGBGetSubscribedInst(req.TgbId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dolar.TGBGetSubscribedInstResponse{InstName: userList}, nil
|
||||||
|
}
|
||||||
44
internal/adapter/grpc/server.go
Normal file
44
internal/adapter/grpc/server.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"git.maximotejeda.com/maximo/dolar/config"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/ports"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/proto/golang/dolar"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/reflection"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Adapter struct {
|
||||||
|
api ports.APIPort
|
||||||
|
port int
|
||||||
|
log *slog.Logger
|
||||||
|
dolar.UnimplementedDollarServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdapter(api ports.APIPort, port int) *Adapter {
|
||||||
|
log := slog.Default()
|
||||||
|
log = log.With("adapter", "dolar-grpc")
|
||||||
|
return &Adapter{api: api, port: port, log: log}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) Run() {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
listen, err := net.Listen("tcp", fmt.Sprintf(":%d", a.port))
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error("failed to listen", "port", a.port, "error", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
grpcServer := grpc.NewServer()
|
||||||
|
dolar.RegisterDollarServer(grpcServer, a)
|
||||||
|
if config.GetEnv() == "development" {
|
||||||
|
reflection.Register(grpcServer)
|
||||||
|
}
|
||||||
|
if err := grpcServer.Serve(listen); err != nil {
|
||||||
|
a.log.Error("failed to serve grpc on port", "port", a.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
65
internal/adapter/nats/nats.go
Normal file
65
internal/adapter/nats/nats.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package nats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/application/core/domain"
|
||||||
|
"github.com/nats-io/nats.go"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
type Adapter struct {
|
||||||
|
conn *nats.Conn
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAdapter(dataSourceURL string) (*Adapter, error) {
|
||||||
|
log := slog.Default()
|
||||||
|
log = log.With("adapter", "nats")
|
||||||
|
conn, err := nats.Connect(dataSourceURL)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("connecting nats", "error", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Adapter{conn: conn, log: log}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) ChangeRegistered(change *domain.Message) error {
|
||||||
|
if change == nil {
|
||||||
|
return fmt.Errorf("nil structure")
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(change)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshaling struct: %w", err)
|
||||||
|
}
|
||||||
|
err = a.conn.Publish("dolar-crawler", data)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = a.conn.Publish("dolar-bot-change", data)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Adapter) NewInstRegistered(change *domain.Message) error {
|
||||||
|
if change == nil {
|
||||||
|
return fmt.Errorf("nil structure")
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(change)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("marshaling struct: %w", err)
|
||||||
|
}
|
||||||
|
err = a.conn.Publish("dolar-crawler", data)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
err = a.conn.Publish("dolar-bot-change", data)
|
||||||
|
if err != nil {
|
||||||
|
a.log.Error(err.Error())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
68
internal/application/core/api/api.go
Normal file
68
internal/application/core/api/api.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/application/core/domain"
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/ports"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
db ports.DBPort
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApplication(db ports.DBPort) *Application {
|
||||||
|
log := slog.Default()
|
||||||
|
log = log.With("adapter", "application")
|
||||||
|
return &Application{
|
||||||
|
db: db,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Application) NewHistory(history *domain.History) (*domain.History, error) {
|
||||||
|
err := a.db.Save(history)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return history, nil
|
||||||
|
}
|
||||||
|
func (a Application) GetInstByType(name string) ([]string, error) {
|
||||||
|
list, err := a.db.GetInstByType(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
func (a Application) GetLatest(name string) (*domain.History, error) {
|
||||||
|
return a.db.GetLatest(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Application) GetSince(name string, duration int64) ([]*domain.History, error) {
|
||||||
|
hs, err := a.db.GetSince(name, duration)
|
||||||
|
a.log.Info("get since", "name", name, "duration", duration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return hs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Application) GetInstByName(name string) (*domain.Institution, error) {
|
||||||
|
return a.db.GetInstByName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Application) TGBSubscribe(tgb_id int64, inst_name string) (bool, error) {
|
||||||
|
return a.db.TGBSubscribe(tgb_id, inst_name)
|
||||||
|
}
|
||||||
|
func (a Application) TGBUnsubscribe(tgb_id int64, inst_name string) (bool, error) {
|
||||||
|
return a.db.TGBUnsubscribe(tgb_id, inst_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Application) TGBGetSubscribedUsers(inst_name string) ([]int64, error) {
|
||||||
|
return a.db.TGBGetSubscribedUsers(inst_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Application) TGBGetSubscribedInst(tgb_id int64) ([]string, error) {
|
||||||
|
return a.db.TGBGetSubscribedInst(tgb_id)
|
||||||
|
}
|
||||||
66
internal/application/core/domain/history.go
Normal file
66
internal/application/core/domain/history.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Institution struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ShortName string `json:"short_name"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type History struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Institution Institution `json:"institution"`
|
||||||
|
Compra float64 `json:"compra"`
|
||||||
|
Venta float64 `json:"venta"`
|
||||||
|
Parser string `json:"parser"`
|
||||||
|
Parsed int64 `json:"parsed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Change struct {
|
||||||
|
Before History `json:"before"`
|
||||||
|
After History `json:"after"`
|
||||||
|
}
|
||||||
|
type Message struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Data *Change `json:"data"`
|
||||||
|
Error error `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHistory(name, parser, shortname string, compra, venta float64) History {
|
||||||
|
return History{
|
||||||
|
Institution: Institution{
|
||||||
|
Name: name,
|
||||||
|
ShortName: shortname,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
},
|
||||||
|
Parser: parser,
|
||||||
|
Compra: compra,
|
||||||
|
Venta: venta,
|
||||||
|
Parsed: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInstitution(name, shortname string) Institution {
|
||||||
|
return Institution{
|
||||||
|
Name: name,
|
||||||
|
ShortName: shortname,
|
||||||
|
Created: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChange(before, after History) *Change {
|
||||||
|
return &Change{
|
||||||
|
Before: before,
|
||||||
|
After: after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMessage(message string, data *Change, err error) *Message {
|
||||||
|
return &Message{
|
||||||
|
Message: message,
|
||||||
|
Data: data,
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
18
internal/ports/api.go
Normal file
18
internal/ports/api.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package ports
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.maximotejeda.com/maximo/dolar/internal/application/core/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type APIPort interface {
|
||||||
|
NewHistory(history *domain.History) (*domain.History, error)
|
||||||
|
GetLatest(name string) (*domain.History, error)
|
||||||
|
GetSince(name string, duration int64) ([]*domain.History, error)
|
||||||
|
GetInstByType(name string) ([]string, error)
|
||||||
|
// TODO
|
||||||
|
GetInstByName(name string) (*domain.Institution, error)
|
||||||
|
TGBSubscribe(tgb_id int64, inst_name string) (bool, error)
|
||||||
|
TGBUnsubscribe(tgb_id int64, inst_name string) (bool, error)
|
||||||
|
TGBGetSubscribedUsers(inst_name string) ([]int64, error)
|
||||||
|
TGBGetSubscribedInst(tgb_id int64) ([]string, error)
|
||||||
|
}
|
||||||
16
internal/ports/db.go
Normal file
16
internal/ports/db.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package ports
|
||||||
|
|
||||||
|
import "git.maximotejeda.com/maximo/dolar/internal/application/core/domain"
|
||||||
|
|
||||||
|
type DBPort interface {
|
||||||
|
GetLatest(name string) (*domain.History, error)
|
||||||
|
GetSince(name string, duration int64) ([]*domain.History, error)
|
||||||
|
GetInstByType(name string) ([]string, error)
|
||||||
|
Save(*domain.History) error
|
||||||
|
// TODO
|
||||||
|
GetInstByName(name string) (*domain.Institution, error)
|
||||||
|
TGBSubscribe(tgb_id int64, inst_name string)(bool, error)
|
||||||
|
TGBUnsubscribe(tgb_id int64, inst_name string)(bool, error)
|
||||||
|
TGBGetSubscribedUsers(inst_name string) ([]int64, error)
|
||||||
|
TGBGetSubscribedInst(tgb_id int64)([]string, error)
|
||||||
|
}
|
||||||
8
internal/ports/nats.go
Normal file
8
internal/ports/nats.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package ports
|
||||||
|
|
||||||
|
import "git.maximotejeda.com/maximo/dolar/internal/application/core/domain"
|
||||||
|
|
||||||
|
type NATSPort interface {
|
||||||
|
ChangeRegistered(history *domain.Message) error
|
||||||
|
NewInstRegistered(history *domain.Message) error
|
||||||
|
}
|
||||||
61
k8s/deployment.yml.template
Normal file
61
k8s/deployment.yml.template
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: $REPONAME-grpc
|
||||||
|
labels:
|
||||||
|
app: $REPONAME-grpc
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: $REPONAME-grpc
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: $REPONAME-grpc
|
||||||
|
name: $REPONAME-grpc
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: $REPONAME-grpc
|
||||||
|
image: "$IMAGE"
|
||||||
|
env:
|
||||||
|
- name: ENV
|
||||||
|
value: "$ENV"
|
||||||
|
- name: NATS_SERVICE_URL
|
||||||
|
value: "$NATS_SERVICE_URL"
|
||||||
|
- name: DATA_SOURCE_URL
|
||||||
|
value: "$DATA_SOURCE_URL"
|
||||||
|
- name: APPLICATION_PORT
|
||||||
|
value: "$APPLICATION_PORT"
|
||||||
|
volumeMounts:
|
||||||
|
- name: db
|
||||||
|
mountPath: "/app/dolardb"
|
||||||
|
volumes:
|
||||||
|
- name: db
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: $REPONAME-grpc-pvc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: $REPONAME-grpc-pvc
|
||||||
|
spec:
|
||||||
|
storageClassName: nfs-csi
|
||||||
|
accessModes: [ReadWriteMany]
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: $REPONAME-grpc-svc
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
app: $REPONAME-grpc
|
||||||
|
ports:
|
||||||
|
- port: 80
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: $APPLICATION_PORT
|
||||||
|
name: grpc
|
||||||
|
type: LoadBalancer
|
||||||
107
proto/dolar/dolar.proto
Normal file
107
proto/dolar/dolar.proto
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
option go_package="github.com/maximotejeda/msvc-proto/golang/dolar";
|
||||||
|
|
||||||
|
message History {
|
||||||
|
int64 id = 1;
|
||||||
|
string name = 2;
|
||||||
|
float compra = 3;
|
||||||
|
float venta = 4;
|
||||||
|
string parser = 5;
|
||||||
|
int64 parsed = 6;
|
||||||
|
int64 inst_id = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Institution {
|
||||||
|
int64 id = 1;
|
||||||
|
string name = 2;
|
||||||
|
string short_name = 3;
|
||||||
|
int64 created = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetLatestRequest{
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetSinceRequest{
|
||||||
|
string name = 1;
|
||||||
|
int64 duration = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetLatestResponse{
|
||||||
|
History actual = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetMultipleResponse{
|
||||||
|
repeated History histories = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddDolarRequest{
|
||||||
|
History institution = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddDolarResponse{
|
||||||
|
int64 id = 1;
|
||||||
|
}
|
||||||
|
message AddInstitutionRequest{
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetInstByTypeRequest {
|
||||||
|
string name = 1;
|
||||||
|
}
|
||||||
|
message GetInstByTypeResponse{
|
||||||
|
repeated string inst_list = 1;
|
||||||
|
}
|
||||||
|
message GetInstByNameRequest {
|
||||||
|
string inst_name = 1;
|
||||||
|
}
|
||||||
|
message GetInstByNameResponse{
|
||||||
|
Institution institution = 1;
|
||||||
|
}
|
||||||
|
message QueryEmptyRequest{
|
||||||
|
reserved 1,2,3,4;
|
||||||
|
}
|
||||||
|
message TGBSubscribeRequest {
|
||||||
|
int64 tgb_id = 1;
|
||||||
|
string inst_name = 2;
|
||||||
|
}
|
||||||
|
message TGBUnsubscribeRequest {
|
||||||
|
int64 tgb_id = 1;
|
||||||
|
string inst_name = 2;
|
||||||
|
}
|
||||||
|
message TGBSubsResponse {
|
||||||
|
bool response = 1;
|
||||||
|
}
|
||||||
|
message TGBGetSubscribedUsersRequest {
|
||||||
|
string inst_name = 1;
|
||||||
|
}
|
||||||
|
message TGBGetSubscribedUsersResponse {
|
||||||
|
repeated int64 tgb_ids = 1;
|
||||||
|
}
|
||||||
|
message TGBGetSubscribedInstRequest {
|
||||||
|
int64 tgb_id = 1;
|
||||||
|
}
|
||||||
|
message TGBGetSubscribedInstResponse {
|
||||||
|
repeated string inst_name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
service Dollar {
|
||||||
|
rpc NewHistory(AddDolarRequest)
|
||||||
|
returns (AddDolarResponse){}
|
||||||
|
rpc GetLatest(GetLatestRequest)
|
||||||
|
returns (GetLatestResponse){}
|
||||||
|
rpc GetSince(GetSinceRequest)
|
||||||
|
returns(GetMultipleResponse){}
|
||||||
|
rpc GetInstByType(GetInstByTypeRequest)
|
||||||
|
returns (GetInstByTypeResponse){}
|
||||||
|
rpc GetInstByName(GetInstByNameRequest)
|
||||||
|
returns (GetInstByNameResponse){}
|
||||||
|
rpc TGBSubscribe(TGBSubscribeRequest)
|
||||||
|
returns(TGBSubsResponse){}
|
||||||
|
rpc TGBUnsubscribe(TGBUnsubscribeRequest)
|
||||||
|
returns (TGBSubsResponse){}
|
||||||
|
rpc TGBGetSubscribedUsers(TGBGetSubscribedUsersRequest)
|
||||||
|
returns (TGBGetSubscribedUsersResponse){}
|
||||||
|
rpc TGBGetSubscribedInsts(TGBGetSubscribedInstRequest)
|
||||||
|
returns (TGBGetSubscribedInstResponse){}
|
||||||
|
}
|
||||||
1572
proto/golang/dolar/dolar.pb.go
Normal file
1572
proto/golang/dolar/dolar.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
405
proto/golang/dolar/dolar_grpc.pb.go
Normal file
405
proto/golang/dolar/dolar_grpc.pb.go
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// - protoc-gen-go-grpc v1.3.0
|
||||||
|
// - protoc v4.25.1
|
||||||
|
// source: dolar/dolar.proto
|
||||||
|
|
||||||
|
package dolar
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
codes "google.golang.org/grpc/codes"
|
||||||
|
status "google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
// Requires gRPC-Go v1.32.0 or later.
|
||||||
|
const _ = grpc.SupportPackageIsVersion7
|
||||||
|
|
||||||
|
const (
|
||||||
|
Dollar_NewHistory_FullMethodName = "/Dollar/NewHistory"
|
||||||
|
Dollar_GetLatest_FullMethodName = "/Dollar/GetLatest"
|
||||||
|
Dollar_GetSince_FullMethodName = "/Dollar/GetSince"
|
||||||
|
Dollar_GetInstByType_FullMethodName = "/Dollar/GetInstByType"
|
||||||
|
Dollar_GetInstByName_FullMethodName = "/Dollar/GetInstByName"
|
||||||
|
Dollar_TGBSubscribe_FullMethodName = "/Dollar/TGBSubscribe"
|
||||||
|
Dollar_TGBUnsubscribe_FullMethodName = "/Dollar/TGBUnsubscribe"
|
||||||
|
Dollar_TGBGetSubscribedUsers_FullMethodName = "/Dollar/TGBGetSubscribedUsers"
|
||||||
|
Dollar_TGBGetSubscribedInsts_FullMethodName = "/Dollar/TGBGetSubscribedInsts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DollarClient is the client API for Dollar service.
|
||||||
|
//
|
||||||
|
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||||
|
type DollarClient interface {
|
||||||
|
NewHistory(ctx context.Context, in *AddDolarRequest, opts ...grpc.CallOption) (*AddDolarResponse, error)
|
||||||
|
GetLatest(ctx context.Context, in *GetLatestRequest, opts ...grpc.CallOption) (*GetLatestResponse, error)
|
||||||
|
GetSince(ctx context.Context, in *GetSinceRequest, opts ...grpc.CallOption) (*GetMultipleResponse, error)
|
||||||
|
GetInstByType(ctx context.Context, in *GetInstByTypeRequest, opts ...grpc.CallOption) (*GetInstByTypeResponse, error)
|
||||||
|
GetInstByName(ctx context.Context, in *GetInstByNameRequest, opts ...grpc.CallOption) (*GetInstByNameResponse, error)
|
||||||
|
TGBSubscribe(ctx context.Context, in *TGBSubscribeRequest, opts ...grpc.CallOption) (*TGBSubsResponse, error)
|
||||||
|
TGBUnsubscribe(ctx context.Context, in *TGBUnsubscribeRequest, opts ...grpc.CallOption) (*TGBSubsResponse, error)
|
||||||
|
TGBGetSubscribedUsers(ctx context.Context, in *TGBGetSubscribedUsersRequest, opts ...grpc.CallOption) (*TGBGetSubscribedUsersResponse, error)
|
||||||
|
TGBGetSubscribedInsts(ctx context.Context, in *TGBGetSubscribedInstRequest, opts ...grpc.CallOption) (*TGBGetSubscribedInstResponse, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type dollarClient struct {
|
||||||
|
cc grpc.ClientConnInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDollarClient(cc grpc.ClientConnInterface) DollarClient {
|
||||||
|
return &dollarClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) NewHistory(ctx context.Context, in *AddDolarRequest, opts ...grpc.CallOption) (*AddDolarResponse, error) {
|
||||||
|
out := new(AddDolarResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_NewHistory_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) GetLatest(ctx context.Context, in *GetLatestRequest, opts ...grpc.CallOption) (*GetLatestResponse, error) {
|
||||||
|
out := new(GetLatestResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_GetLatest_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) GetSince(ctx context.Context, in *GetSinceRequest, opts ...grpc.CallOption) (*GetMultipleResponse, error) {
|
||||||
|
out := new(GetMultipleResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_GetSince_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) GetInstByType(ctx context.Context, in *GetInstByTypeRequest, opts ...grpc.CallOption) (*GetInstByTypeResponse, error) {
|
||||||
|
out := new(GetInstByTypeResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_GetInstByType_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) GetInstByName(ctx context.Context, in *GetInstByNameRequest, opts ...grpc.CallOption) (*GetInstByNameResponse, error) {
|
||||||
|
out := new(GetInstByNameResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_GetInstByName_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) TGBSubscribe(ctx context.Context, in *TGBSubscribeRequest, opts ...grpc.CallOption) (*TGBSubsResponse, error) {
|
||||||
|
out := new(TGBSubsResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_TGBSubscribe_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) TGBUnsubscribe(ctx context.Context, in *TGBUnsubscribeRequest, opts ...grpc.CallOption) (*TGBSubsResponse, error) {
|
||||||
|
out := new(TGBSubsResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_TGBUnsubscribe_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) TGBGetSubscribedUsers(ctx context.Context, in *TGBGetSubscribedUsersRequest, opts ...grpc.CallOption) (*TGBGetSubscribedUsersResponse, error) {
|
||||||
|
out := new(TGBGetSubscribedUsersResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_TGBGetSubscribedUsers_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *dollarClient) TGBGetSubscribedInsts(ctx context.Context, in *TGBGetSubscribedInstRequest, opts ...grpc.CallOption) (*TGBGetSubscribedInstResponse, error) {
|
||||||
|
out := new(TGBGetSubscribedInstResponse)
|
||||||
|
err := c.cc.Invoke(ctx, Dollar_TGBGetSubscribedInsts_FullMethodName, in, out, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DollarServer is the server API for Dollar service.
|
||||||
|
// All implementations must embed UnimplementedDollarServer
|
||||||
|
// for forward compatibility
|
||||||
|
type DollarServer interface {
|
||||||
|
NewHistory(context.Context, *AddDolarRequest) (*AddDolarResponse, error)
|
||||||
|
GetLatest(context.Context, *GetLatestRequest) (*GetLatestResponse, error)
|
||||||
|
GetSince(context.Context, *GetSinceRequest) (*GetMultipleResponse, error)
|
||||||
|
GetInstByType(context.Context, *GetInstByTypeRequest) (*GetInstByTypeResponse, error)
|
||||||
|
GetInstByName(context.Context, *GetInstByNameRequest) (*GetInstByNameResponse, error)
|
||||||
|
TGBSubscribe(context.Context, *TGBSubscribeRequest) (*TGBSubsResponse, error)
|
||||||
|
TGBUnsubscribe(context.Context, *TGBUnsubscribeRequest) (*TGBSubsResponse, error)
|
||||||
|
TGBGetSubscribedUsers(context.Context, *TGBGetSubscribedUsersRequest) (*TGBGetSubscribedUsersResponse, error)
|
||||||
|
TGBGetSubscribedInsts(context.Context, *TGBGetSubscribedInstRequest) (*TGBGetSubscribedInstResponse, error)
|
||||||
|
mustEmbedUnimplementedDollarServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnimplementedDollarServer must be embedded to have forward compatible implementations.
|
||||||
|
type UnimplementedDollarServer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (UnimplementedDollarServer) NewHistory(context.Context, *AddDolarRequest) (*AddDolarResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method NewHistory not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) GetLatest(context.Context, *GetLatestRequest) (*GetLatestResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetLatest not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) GetSince(context.Context, *GetSinceRequest) (*GetMultipleResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetSince not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) GetInstByType(context.Context, *GetInstByTypeRequest) (*GetInstByTypeResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetInstByType not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) GetInstByName(context.Context, *GetInstByNameRequest) (*GetInstByNameResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method GetInstByName not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) TGBSubscribe(context.Context, *TGBSubscribeRequest) (*TGBSubsResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method TGBSubscribe not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) TGBUnsubscribe(context.Context, *TGBUnsubscribeRequest) (*TGBSubsResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method TGBUnsubscribe not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) TGBGetSubscribedUsers(context.Context, *TGBGetSubscribedUsersRequest) (*TGBGetSubscribedUsersResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method TGBGetSubscribedUsers not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) TGBGetSubscribedInsts(context.Context, *TGBGetSubscribedInstRequest) (*TGBGetSubscribedInstResponse, error) {
|
||||||
|
return nil, status.Errorf(codes.Unimplemented, "method TGBGetSubscribedInsts not implemented")
|
||||||
|
}
|
||||||
|
func (UnimplementedDollarServer) mustEmbedUnimplementedDollarServer() {}
|
||||||
|
|
||||||
|
// UnsafeDollarServer may be embedded to opt out of forward compatibility for this service.
|
||||||
|
// Use of this interface is not recommended, as added methods to DollarServer will
|
||||||
|
// result in compilation errors.
|
||||||
|
type UnsafeDollarServer interface {
|
||||||
|
mustEmbedUnimplementedDollarServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterDollarServer(s grpc.ServiceRegistrar, srv DollarServer) {
|
||||||
|
s.RegisterService(&Dollar_ServiceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_NewHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(AddDolarRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).NewHistory(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_NewHistory_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).NewHistory(ctx, req.(*AddDolarRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_GetLatest_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetLatestRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).GetLatest(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_GetLatest_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).GetLatest(ctx, req.(*GetLatestRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_GetSince_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetSinceRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).GetSince(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_GetSince_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).GetSince(ctx, req.(*GetSinceRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_GetInstByType_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetInstByTypeRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).GetInstByType(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_GetInstByType_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).GetInstByType(ctx, req.(*GetInstByTypeRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_GetInstByName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(GetInstByNameRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).GetInstByName(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_GetInstByName_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).GetInstByName(ctx, req.(*GetInstByNameRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_TGBSubscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(TGBSubscribeRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).TGBSubscribe(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_TGBSubscribe_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).TGBSubscribe(ctx, req.(*TGBSubscribeRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_TGBUnsubscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(TGBUnsubscribeRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).TGBUnsubscribe(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_TGBUnsubscribe_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).TGBUnsubscribe(ctx, req.(*TGBUnsubscribeRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_TGBGetSubscribedUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(TGBGetSubscribedUsersRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).TGBGetSubscribedUsers(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_TGBGetSubscribedUsers_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).TGBGetSubscribedUsers(ctx, req.(*TGBGetSubscribedUsersRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Dollar_TGBGetSubscribedInsts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||||
|
in := new(TGBGetSubscribedInstRequest)
|
||||||
|
if err := dec(in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if interceptor == nil {
|
||||||
|
return srv.(DollarServer).TGBGetSubscribedInsts(ctx, in)
|
||||||
|
}
|
||||||
|
info := &grpc.UnaryServerInfo{
|
||||||
|
Server: srv,
|
||||||
|
FullMethod: Dollar_TGBGetSubscribedInsts_FullMethodName,
|
||||||
|
}
|
||||||
|
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||||
|
return srv.(DollarServer).TGBGetSubscribedInsts(ctx, req.(*TGBGetSubscribedInstRequest))
|
||||||
|
}
|
||||||
|
return interceptor(ctx, in, info, handler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dollar_ServiceDesc is the grpc.ServiceDesc for Dollar service.
|
||||||
|
// It's only intended for direct use with grpc.RegisterService,
|
||||||
|
// and not to be introspected or modified (even as a copy)
|
||||||
|
var Dollar_ServiceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "Dollar",
|
||||||
|
HandlerType: (*DollarServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{
|
||||||
|
{
|
||||||
|
MethodName: "NewHistory",
|
||||||
|
Handler: _Dollar_NewHistory_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetLatest",
|
||||||
|
Handler: _Dollar_GetLatest_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetSince",
|
||||||
|
Handler: _Dollar_GetSince_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetInstByType",
|
||||||
|
Handler: _Dollar_GetInstByType_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "GetInstByName",
|
||||||
|
Handler: _Dollar_GetInstByName_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "TGBSubscribe",
|
||||||
|
Handler: _Dollar_TGBSubscribe_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "TGBUnsubscribe",
|
||||||
|
Handler: _Dollar_TGBUnsubscribe_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "TGBGetSubscribedUsers",
|
||||||
|
Handler: _Dollar_TGBGetSubscribedUsers_Handler,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
MethodName: "TGBGetSubscribedInsts",
|
||||||
|
Handler: _Dollar_TGBGetSubscribedInsts_Handler,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Streams: []grpc.StreamDesc{},
|
||||||
|
Metadata: "dolar/dolar.proto",
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user