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

This commit is contained in:
maximo tejeda 2024-12-02 16:07:48 -04:00
commit 4b60ebc7a7
26 changed files with 3577 additions and 0 deletions

14
.dir-locals.el Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}

View 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');

View 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');

View 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
}

View 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)
}
}

View 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
}

View 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)
}

View 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
View 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
View 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
View 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
}

View 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
View 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){}
}

File diff suppressed because it is too large Load Diff

View 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",
}