FIRST COMMIT
All checks were successful
dev test / test (push) Successful in 39s
dev test / vulnCheck (push) Successful in 39s
dev test / Ci-Lint (push) Successful in 24s

This commit is contained in:
maximo tejeda 2024-12-14 10:40:22 -04:00
commit e5d160da8c
28 changed files with 3052 additions and 0 deletions

14
.dir-locals.el Normal file
View File

@ -0,0 +1,14 @@
b((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")))
))

106
.github/workflows/image_creation.yml vendored Normal file
View File

@ -0,0 +1,106 @@
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 }}"
INFO_DATA_SOURCE_URL: "${{ vars.INFO_DATA_SOURCE_URL }}"
PHOTO_DATA_SOURCE_URL: "${{ vars.PHOTO_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

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/.env
/bin/
/db/

16
Dockerfile Normal file
View File

@ -0,0 +1,16 @@
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/cedulados-grpc \
-ldflags "-X main.Shortsha=${SHORTSHA} \
-X main.Version=${version} \
-X main.Aarch=${TARGETARCH}" ./cmd
FROM alpine AS runner
COPY --from=builder /app/bin/cedulados-grpc /usr/bin/
WORKDIR /app
ENTRYPOINT /usr/bin/cedulados-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=cedulados-grpc
VERSION=0.0.0_1
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=192.168.0.151:32000
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/cedulados/cedulados.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

33
cmd/main.go Normal file
View File

@ -0,0 +1,33 @@
package main
import (
"log/slog"
"git.maximotejeda.com/maximo/cedulados/config"
"git.maximotejeda.com/maximo/cedulados/internal/adapter/db"
"git.maximotejeda.com/maximo/cedulados/internal/adapter/grpc"
"git.maximotejeda.com/maximo/cedulados/internal/application/core/api"
)
func main() {
log := slog.Default()
log.With("adapter", "main")
infoDBAdapter, err := db.NewAdapter(config.GetInfoDataSourceURL())
if err != nil {
log.Error("failed to connect to database.", "error", err)
panic(err)
}
photoDBAdapter, err := db.NewAdapter(config.GetPhotoDataSourceURL())
if err != nil {
log.Error("failed to connect to database.", "error", err)
panic(err)
}
application := api.NewApplication(infoDBAdapter, photoDBAdapter)
grpcAdapter := grpc.NewAdapter(application, config.GetApplicationPort())
log.Info("grpc server running", "port", config.GetApplicationPort())
grpcAdapter.Run()
}

38
config/config.go Normal file
View File

@ -0,0 +1,38 @@
package config
import (
"log"
"os"
"strconv"
)
func GetEnv() string {
return getEnvironmentValue("ENV")
}
func GetInfoDataSourceURL() string {
return getEnvironmentValue("INFO_DATA_SOURCE_URL")
}
func GetPhotoDataSourceURL() string {
return getEnvironmentValue("PHOTO_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)
}

28
go.mod Normal file
View File

@ -0,0 +1,28 @@
module git.maximotejeda.com/maximo/cedulados
go 1.22.0
require (
google.golang.org/grpc v1.63.2
google.golang.org/protobuf v1.33.0
modernc.org/sqlite v1.29.8
)
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/mattn/go-isatty v0.0.20 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // 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.49.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // 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-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
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/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
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/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/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.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.6.0/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.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
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/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
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.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
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.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.29.8 h1:nGKglNx9K5v0As+zF0/Gcl1kMkmaU1XynYyq92PbsC8=
modernc.org/sqlite v1.29.8/go.mod h1:lQPm27iqa4UNZpmr4Aor0MH0HkCLbt1huYDfWylLZFk=
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=

View File

@ -0,0 +1,259 @@
package db
import (
"context"
"database/sql"
"errors"
"fmt"
"git.maximotejeda.com/maximo/cedulados/internal/application/core/domain"
)
// ByID
// Query knowing id
func (a Adapter) ByID(ctx context.Context, id int64) (*domain.Cedulado, error) {
stmt, err := a.db.PrepareContext(
ctx,
`
SELECT
c.id, c.mun_ced, c.seq_ced, c.ver_ced,
COALESCE(c.nombres, ""), COALESCE(c.apellido1 ""), COALESCE(c.apellido2 ""),
COALESCE(c.telefono, ""), COALESCE(c.fecha_nac, ""),
COALESCE(c.calle, "") || ' ' || COALESCE(c.casa, "") || ' ' || COALESCE(c.edificio, "") || ' ' || COALESCE(c.apto,"") || ', ' || COALESCE(s.descripcion, "") || ', ' || COALESCE(m.descripcion, "") || ', ' || COALESCE(cs.descripcion, ""),
COALESCE(c.sexo, "")
FROM cedulados AS c
JOIN ciudad_seccion cs ON
c.cod_ciudad = cs.cod_ciudad
JOIN municipio m ON
c.cod_municipio = m.cod_municipio
JOIN sector_paraje s ON
c.cod_sector = s.cod_sector
WHERE c.id = ?;
`)
if err != nil {
panic(err)
}
defer stmt.Close()
info := &domain.Cedulado{}
err = stmt.QueryRow(id).Scan(
&info.ID, &info.MunCed, &info.SeqCed, &info.VerCed,
&info.Nombres, &info.Apellido1, &info.Apellido2,
&info.Telefono, &info.FechaNac, &info.Direccion,
&info.Sexo,
)
if err != nil {
return nil, err
}
return info, nil
}
// ByCedula
// Query by cedula
func (a Adapter) ByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Cedulado, error) {
stmt, err := a.db.PrepareContext(
ctx,
`
SELECT
c.id, c.mun_ced, c.seq_ced, c.ver_ced,
COALESCE(c.nombres, ""), COALESCE(c.apellido1,""), COALESCE(c.apellido2, ""),
COALESCE(c.telefono,""), COALESCE(c.fecha_nac, ""),
COALESCE(c.calle, "") || ' ' || COALESCE(c.casa, "") || ' ' || COALESCE(c.edificio, "") || ' ' || COALESCE(c.apto,"") || ', ' || COALESCE(s.descripcion, "") || ', ' || COALESCE(m.descripcion, "") || ', ' || COALESCE(cs.descripcion, ""),
COALESCE(c.sexo,"")
FROM cedulados AS c
JOIN ciudad_seccion cs ON
c.cod_ciudad = cs.cod_ciudad AND c.cod_municipio = cs.cod_municipio
JOIN municipio m ON
c.cod_municipio = m.cod_municipio
JOIN sector_paraje s ON
c.cod_sector = s.cod_sector AND c.cod_municipio = s.cod_municipio
WHERE c.mun_ced = ? AND c.seq_ced = ? AND c.ver_ced = ?;
`)
if err != nil {
panic(err)
}
defer stmt.Close()
info := &domain.Cedulado{}
err = stmt.QueryRow(cedula.MunCed, cedula.SeqCed, cedula.VerCed).Scan(
&info.ID, &info.MunCed, &info.SeqCed, &info.VerCed,
&info.Nombres, &info.Apellido1, &info.Apellido2,
&info.Telefono, &info.FechaNac, &info.Direccion,
&info.Sexo,
)
if err != nil {
return nil, err
}
return info, nil
}
// GetByNameLastName
// Query by name and lastname 1 and 2
func (a Adapter) GetByNameLastName(ctx context.Context, nombre, apellido1, apellido2 string, page int64) (*domain.MultipleResults, error) {
limit := 10 + (10 * page)
result := &domain.MultipleResults{Data: []*domain.Cedulado{}}
stmt, err := a.db.PrepareContext(
ctx,
`
SELECT
c.id, c.mun_ced, c.seq_ced, c.ver_ced,
COALESCE(c.nombres, ""), COALESCE(c.apellido1, ""), COALESCE(c.apellido2, ""),
COALESCE(c.telefono, ""), COALESCE(c.fecha_nac, ""),
COALESCE(c.calle, "") || ' ' || COALESCE(c.casa, "") || ' ' || COALESCE(c.edificio, "") || ' ' || COALESCE(c.apto,"") || ', ' || COALESCE(s.descripcion, "") || ', ' || COALESCE(m.descripcion, "") || ', ' || COALESCE(cs.descripcion, ""),
COALESCE(c.sexo,"")
FROM cedulados AS c
JOIN ciudad_seccion cs ON
c.cod_ciudad = cs.cod_ciudad AND c.cod_municipio = cs.cod_municipio
JOIN municipio m ON
c.cod_municipio = m.cod_municipio
JOIN sector_paraje s ON
c.cod_sector = s.cod_sector AND c.cod_municipio = s.cod_municipio
WHERE c.nombres LIKE ? AND c.apellido1 LIKE ? AND c.apellido2 LIKE ? LIMIT ? OFFSET ?;
`)
if err != nil {
panic(err)
}
defer stmt.Close()
rows, err := stmt.QueryContext(ctx, "%"+nombre+"%", "%"+apellido1+"%", "%"+apellido2+"%", limit, page*10)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
info := &domain.Cedulado{}
if err := rows.Scan(
&info.ID, &info.MunCed, &info.SeqCed, &info.VerCed,
&info.Nombres, &info.Apellido1, &info.Apellido2,
&info.Telefono, &info.FechaNac, &info.Direccion,
&info.Sexo,
); err != nil {
fmt.Printf("%s", err)
continue
}
result.Data = append(result.Data, info)
}
if err := a.db.QueryRow("SELECT COUNT(*) FROM cedulados WHERE nombres LIKE ? AND apellido1 LIKE ? AND apellido2 LIKE ?", "%"+nombre+"%", "%"+apellido1+"%", "%"+apellido2+"%").Scan(&result.Total); err != nil {
if errors.Is(err, sql.ErrNoRows) {
result.Total = 0
}
fmt.Println(err)
}
result.Page = page + 1
return result, nil
}
// GetByFTS
// use unstructured text to search on the tables
func (a Adapter) GetByFTS(ctx context.Context, text string, page int64) (*domain.MultipleResults, error) {
limit := 10 + (10 * page)
results := &domain.MultipleResults{Data: []*domain.Cedulado{}}
stmt, err := a.db.PrepareContext(
ctx,
`
SELECT
rowid, mun_ced, seq_ced, ver_ced,
COALESCE(nombres, ""), COALESCE(apellido1, ""), COALESCE(apellido2, "")
FROM vcedulados
WHERE vcedulados MATCH ?
LIMIT ? OFFSET ?
`)
if err != nil {
panic(err)
}
defer stmt.Close()
rows, err := stmt.QueryContext(ctx, text, limit, page*10)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
info := &domain.Cedulado{}
if err := rows.Scan(
&info.ID, &info.MunCed, &info.SeqCed, &info.VerCed,
&info.Nombres, &info.Apellido1, &info.Apellido2,
); err != nil {
fmt.Printf("%s", err)
continue
}
// fmt.Println(info)
results.Data = append(results.Data, info)
}
if err := a.db.QueryRow(
`
SELECT
COUNT(*)
FROM vcedulados
WHERE vcedulados MATCH ?
`, text).Scan(&results.Total); err != nil {
if errors.Is(err, sql.ErrNoRows) {
results.Total = 0
}
fmt.Println(err)
}
results.Page = page + 1
return results, nil
}
// ByNameAndLocation
// Use name lastname1 and 2 plus location
func (a Adapter) ByNameAndLocation(ctx context.Context, nombre, apellido1, apellido2, municipio string, page int64) (*domain.MultipleResults, error) {
limit := 10 + (10 * page)
results := &domain.MultipleResults{Data: []*domain.Cedulado{}}
stmt, err := a.db.PrepareContext(
ctx,
`
SELECT
c.id, c.mun_ced, c.seq_ced, c.ver_ced,
COALESCE(c.nombres, ""), COALESCE(c.apellido1, ""), COALESCE(c.apellido2, ""),
COALESCE(c.telefono, ""), COALESCE(c.fecha_nac, ""),
COALESCE(c.calle, "") || ' ' || COALESCE(c.casa, "") || ' ' || COALESCE(c.edificio, "") || ' ' || COALESCE(c.apto,"") || ', ' || COALESCE(s.descripcion, "") || ', ' || COALESCE(m.descripcion, "") || ', ' || COALESCE(cs.descripcion, ""),
COALESCE(c.sexo,"")
FROM cedulados AS c
JOIN ciudad_seccion cs ON
c.cod_ciudad = cs.cod_ciudad AND c.cod_municipio = cs.cod_municipio
JOIN municipio m ON
c.cod_municipio = m.cod_municipio
JOIN sector_paraje s ON
c.cod_sector = s.cod_sector AND c.cod_municipio = s.cod_municipio
WHERE c.nombres LIKE ? AND c.apellido1 LIKE ? AND c.apellido2 LIKE ? AND m.descripcion LIKE ? LIMIT ? OFFSET ?;
`)
if err != nil {
panic(err)
}
defer stmt.Close()
rows, err := stmt.QueryContext(ctx, "%"+nombre+"%", "%"+apellido1+"%", "%"+apellido2+"%", municipio, limit, page*10)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
info := &domain.Cedulado{}
if err := rows.Scan(
&info.ID, &info.MunCed, &info.SeqCed, &info.VerCed,
&info.Nombres, &info.Apellido1, &info.Apellido2,
&info.Telefono, &info.FechaNac, &info.Direccion,
&info.Sexo,
); err != nil {
fmt.Printf("%s", err)
continue
}
// fmt.Println(info)
results.Data = append(results.Data, info)
}
if err := a.db.QueryRow(
`SELECT COUNT(*) FROM cedulados c
JOIN municipio m ON
c.cod_municipio = m.cod_municipio
WHERE c.nombres LIKE ? AND c.apellido1 LIKE ? AND c.apellido2 LIKE ? AND m.descripcion LIKE ?`,
"%"+nombre+"%", "%"+apellido1+"%", "%"+apellido2+"%", municipio,
).Scan(&results.Total); err != nil {
if errors.Is(err, sql.ErrNoRows) {
results.Total = 0
}
fmt.Println(err)
}
results.Page = page + 1
return results, nil
}

34
internal/adapter/db/db.go Normal file
View File

@ -0,0 +1,34 @@
package db
import (
"context"
"database/sql"
"fmt"
"log/slog"
"time"
_ "modernc.org/sqlite"
)
type Adapter struct {
db *sql.DB
log *slog.Logger
}
func NewAdapter(dataSourceURL string) (*Adapter, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
log := slog.Default().With("adapter", "db")
pragmas := "cache=shared&mode=ro&_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)
return &Adapter{db: db, log: log}, nil
}

View File

@ -0,0 +1,81 @@
package db
import (
"context"
"fmt"
"git.maximotejeda.com/maximo/cedulados/internal/application/core/domain"
)
// QueryFotoByCedula
// Query a foto by its cedula
func (a Adapter) QueryFotoByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Foto, error) {
stmt, err := a.db.PrepareContext(
ctx,
`
SELECT imagen AS i
FROM fotos
WHERE mun_ced = ?
AND seq_ced = ?
AND ver_ced = ?
LIMIT 1
`)
if err != nil {
panic(err)
}
defer stmt.Close()
i := []byte{}
err = stmt.QueryRowContext(ctx, cedula.MunCed, cedula.SeqCed, cedula.VerCed).Scan(&i)
if err != nil {
return nil, err
}
foto := &domain.Foto{
MunCed: cedula.MunCed,
SeqCed: cedula.SeqCed,
VerCed: cedula.VerCed,
Imagen: i,
}
return foto, nil
}
// QueryFotoById
// Query fotos by a knowing id
func (a Adapter) QueryFotoById(ctx context.Context, id int64) (*domain.Foto, error) {
foto := &domain.Foto{}
stmt, err := a.db.PrepareContext(
ctx,
`
SELECT mun_ced, seq_ced, ver_ced, imagen AS i
FROM fotos
WHERE id = ?
`)
if err != nil {
panic(err)
}
defer stmt.Close()
err = stmt.QueryRow(id).Scan(&foto.MunCed, &foto.SeqCed, &foto.VerCed, foto.Imagen)
if err != nil {
return nil, err
}
return foto, nil
}
// QueryAllCedulas
// query a list of cedulas
func (a Adapter) QueryAllCedulas(ctx context.Context, cedulas []*domain.Cedula) ([]*domain.Foto, error) {
if len(cedulas) == 0 {
return nil, fmt.Errorf("empty cedulas list")
}
images := []*domain.Foto{}
for _, v := range cedulas {
img, err := a.QueryFotoByCedula(ctx, v)
if err != nil {
continue
}
images = append(images, img)
}
return images, nil
}

View File

@ -0,0 +1,5 @@
CREATE VIRTUAL TABLE IF NOT EXISTS vcedulados USING(nombres, apellido1, apellido2, mun_ced, seq_ced, ver_ced, content=cedulados, content_rowid=id);
-- Tiggers to update index on first run
CREATE TRIGGER ai_cedulados AFTER INSERT ON cedulados BEGIN
INSERT INTO vcedulados(rowid, nombres, apellido1, apellido2, mun_ced, seq_ced, ver_ced) VALUES (new.nombres, new.apellido1, new.apellido2, new.mun_ced, new.seq_ced, new.ver_ced);
END;

View File

@ -0,0 +1,159 @@
package grpc
import (
"context"
"fmt"
"git.maximotejeda.com/maximo/cedulados/internal/application/core/domain"
"git.maximotejeda.com/maximo/cedulados/proto/golang/cedulados"
)
func (a Adapter) CeduladosByCedula(ctx context.Context, req *cedulados.QueryByCedulaRequest) (*cedulados.QueryResponse, error) {
cedula := &domain.Cedula{
MunCed: req.Cedula.MunCed,
SeqCed: req.Cedula.SeqCed,
VerCed: req.Cedula.VerCed,
}
c, err := a.api.ByCedula(ctx, cedula)
if err != nil {
return nil, err
}
cedulado := converCeduladoFromDomain(c)
if cedulado == nil {
return nil, fmt.Errorf("error nil structure")
}
return &cedulados.QueryResponse{Cedulado: cedulado}, nil
}
func (a Adapter) CeduladosGetByNameLastName(ctx context.Context, req *cedulados.QueryByNameLastNameRequest) (*cedulados.QueryMultipleResponse, error) {
cL, err := a.api.GetByNameLastName(ctx, req.Name, req.Apellido_1, req.Apellido_2, req.Page)
if err != nil {
return nil, err
}
list := &cedulados.QueryMultipleResponse{}
for _, it := range cL.Data {
c := converCeduladoFromDomain(it)
if c == nil {
continue
}
list.Cedulados =append(list.Cedulados, c)
}
list.Page = cL.Page
list.Total = int64(cL.Total)
return list, nil
}
func (a Adapter) CeduladosByFTS(ctx context.Context, req *cedulados.QueryByFTSRequest) (*cedulados.QueryMultipleResponse, error) {
cL, err := a.api.GetByFTS(ctx, req.Name, req.Page)
if err != nil {
return nil, err
}
list := &cedulados.QueryMultipleResponse{}
for _, it := range cL.Data {
c := converCeduladoFromDomain(it)
if c == nil {
continue
}
list.Cedulados =append(list.Cedulados, c)
}
list.Page = cL.Page
list.Total = int64(cL.Total)
return list, nil
}
func (a Adapter) CeduladosByNameAndLocation(ctx context.Context, req *cedulados.QueryByNamesLocationRequest) (*cedulados.QueryMultipleResponse, error) {
cL, err := a.api.ByNameAndLocation(ctx, req.Name, req.Apellido_1, req.Apellido_2, req.Municipio, req.Page)
if err != nil {
return nil, err
}
list := &cedulados.QueryMultipleResponse{}
for _, it := range cL.Data {
c := converCeduladoFromDomain(it)
if c == nil {
continue
}
list.Cedulados =append(list.Cedulados, c)
}
list.Page = cL.Page
list.Total = int64(cL.Total)
return list, nil
}
func (a Adapter) QueryFotoByCedula(ctx context.Context, req *cedulados.QueryFotoByCedulaRequest) (*cedulados.QueryFotoByCedulaResponse, error) {
cedula := domain.Cedula{
MunCed: req.Cedula.MunCed,
SeqCed: req.Cedula.SeqCed,
VerCed: req.Cedula.VerCed,
}
f, err := a.api.QueryFotoByCedula(ctx, &cedula)
if err != nil {
return nil, err
}
foto := cedulados.Foto{
MunCed: cedula.MunCed,
SeqCed: cedula.SeqCed,
VerCed: cedula.VerCed,
Foto: f.Imagen,
}
return &cedulados.QueryFotoByCedulaResponse{Foto: &foto}, nil
}
func (a Adapter) QueryFotoById(ctx context.Context, req *cedulados.QueryFotoByIdRequest) (*cedulados.QueryFotoByIdResponse, error) {
f, err := a.api.QueryFotoById(ctx, req.Id)
if err != nil {
return nil, err
}
foto := cedulados.Foto{
MunCed: f.MunCed,
SeqCed: f.SeqCed,
VerCed: f.VerCed,
Foto: f.Imagen,
}
return &cedulados.QueryFotoByIdResponse{Foto: &foto}, nil
}
func (a Adapter) QueryFotoAllCedulas(ctx context.Context, req *cedulados.QueryFotoAllCedulasRequest) (*cedulados.QueryFotoAllCedulasResponse, error) {
listDcedulas := []*domain.Cedula{}
for _, i := range req.Cedulas{
cedula := &domain.Cedula{
MunCed: i.MunCed,
SeqCed: i.SeqCed,
VerCed: i.VerCed,
}
listDcedulas = append(listDcedulas, cedula)
}
fL, err := a.api.QueryAllCedulas(ctx, listDcedulas)
if err != nil {
return nil, err
}
cFL := []*cedulados.Foto{}
for _, f := range fL{
foto := &cedulados.Foto{
MunCed: f.MunCed,
SeqCed: f.SeqCed,
VerCed: f.VerCed,
Foto: f.Imagen,
}
cFL = append(cFL, foto)
}
return &cedulados.QueryFotoAllCedulasResponse{Fotos: cFL}, nil
}
func converCeduladoFromDomain(c *domain.Cedulado)(converted *cedulados.Cedulado){
if c == nil {
return nil
}
return &cedulados.Cedulado{
Id: c.ID,
Nombres: c.Nombres,
Apellido_1: c.Apellido1,
Apellido_2: c.Apellido2,
Cedula: &cedulados.Cedula{
MunCed: c.MunCed,
SeqCed: c.SeqCed,
VerCed: c.VerCed,
},
Sexo: c.Sexo,
Direccion: c.Direccion,
Telefono: c.Telefono,
FechaNac: c.FechaNac,
}
}

View File

@ -0,0 +1,44 @@
package grpc
import (
"fmt"
"log/slog"
"net"
"git.maximotejeda.com/maximo/cedulados/config"
"git.maximotejeda.com/maximo/cedulados/internal/ports"
"git.maximotejeda.com/maximo/cedulados/proto/golang/cedulados"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
type Adapter struct {
api ports.APIPorts
port int
log *slog.Logger
cedulados.UnimplementedQueryCeduladoServer
}
func NewAdapter(api ports.APIPorts, port int) *Adapter {
log := slog.Default()
log = log.With("adapter", "cedulados-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()
cedulados.RegisterQueryCeduladoServer(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,61 @@
package api
import (
"context"
"log/slog"
"git.maximotejeda.com/maximo/cedulados/internal/application/core/domain"
"git.maximotejeda.com/maximo/cedulados/internal/ports"
)
type Application struct {
infoDB ports.DBPorts
fotoDB ports.DBPorts
log *slog.Logger
}
func NewApplication(infoDB, fotoDB ports.DBPorts) *Application {
log := slog.Default()
log = log.With("adapter", "application")
return &Application{
infoDB: infoDB,
fotoDB: fotoDB,
log: log,
}
}
// ByCedula
// Query db with a cedula complete info
func (a Application) ByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Cedulado, error) {
return a.infoDB.ByCedula(ctx, cedula)
}
// GetByNameLastName
// Query db by name, lastname
func (a Application) GetByNameLastName(ctx context.Context, nombre, apellido1, apellido2 string, page int64) (*domain.MultipleResults, error) {
return a.infoDB.GetByNameLastName(ctx, nombre, apellido1, apellido2, page)
}
// GetByFTS
// make a full text search
func (a Application) GetByFTS(ctx context.Context, parametros string, page int64) (*domain.MultipleResults, error) {
return a.infoDB.GetByFTS(ctx, parametros, page)
}
// ByNameAndLocation
// search coincidents names in a location
func (a Application) ByNameAndLocation(ctx context.Context, nombre, apellido1, apellido2, municipio string, page int64) (*domain.MultipleResults, error) {
return a.infoDB.ByNameAndLocation(ctx, nombre, apellido1, apellido2, municipio, page)
}
// QueryFotoByCedula
// query foto db with full cedula info
func (a Application) QueryFotoByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Foto, error) {
return a.fotoDB.QueryFotoByCedula(ctx, cedula)
}
// QueryFotoById
// knowing id of a record get the photo
func (a Application) QueryFotoById(ctx context.Context, id int64) (*domain.Foto, error) {
return a.fotoDB.QueryFotoById(ctx, id)
}
// QueryAllCedulas
// Query a list of cedulas
func (a Application) QueryAllCedulas(ctx context.Context, cedulas []*domain.Cedula) ([]*domain.Foto, error) {
return a.fotoDB.QueryAllCedulas(ctx, cedulas)
}

View File

@ -0,0 +1,19 @@
package domain
type Cedulado struct {
Cedula
ID int64 `json:"id"`
Nombres string `json:"nombres"`
Apellido1 string `json:"apellido1"`
Apellido2 string `json:"apellido2"`
Sexo string `json:"sexo"`
Direccion string `json:"direccion"`
Telefono string `json:"telefono"`
FechaNac string `json:"fecha_nac"`
}
type MultipleResults struct {
Data []*Cedulado `json:"data"`
Total int64 `json:"total"`
Page int64 `json:"page"`
}

View File

@ -0,0 +1,7 @@
package domain
type Cedula struct {
MunCed string `json:"mun_ced"`
SeqCed string `json:"seq_ced"`
VerCed string `json:"ver_ced"`
}

View File

@ -0,0 +1,12 @@
package domain
type imagen []byte
type Foto struct {
ID int64 `json:"id"`
MunCed string `json:"mun_ced"`
SeqCed string `json:"seq_ced"`
VerCed string `json:"ver_ced"`
Sequencia int64 `json:"sequencia"`
Imagen imagen `json:"imagen"`
}

17
internal/ports/api.go Normal file
View File

@ -0,0 +1,17 @@
package ports
import (
"context"
"git.maximotejeda.com/maximo/cedulados/internal/application/core/domain"
)
type APIPorts interface {
ByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Cedulado, error)
GetByNameLastName(ctx context.Context, nombre, apellido1, apellido2 string, page int64) (*domain.MultipleResults, error)
GetByFTS(ctx context.Context, parametros string, page int64) (*domain.MultipleResults, error)
ByNameAndLocation(ctx context.Context, nombre, apellido1, apellido2, municipio string, page int64) (*domain.MultipleResults, error)
QueryFotoByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Foto, error)
QueryFotoById(ctx context.Context, id int64) (*domain.Foto, error)
QueryAllCedulas(ctx context.Context, cedulas []*domain.Cedula) ([]*domain.Foto, error)
}

17
internal/ports/db.go Normal file
View File

@ -0,0 +1,17 @@
package ports
import (
"context"
"git.maximotejeda.com/maximo/cedulados/internal/application/core/domain"
)
type DBPorts interface {
ByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Cedulado, error)
GetByNameLastName(ctx context.Context, nombre, apellido1, apellido2 string, page int64) (*domain.MultipleResults, error)
GetByFTS(ctx context.Context, parametros string, page int64) (*domain.MultipleResults, error)
ByNameAndLocation(ctx context.Context, nombre, apellido1, apellido2, municipio string, page int64) (*domain.MultipleResults, error)
QueryFotoByCedula(ctx context.Context, cedula *domain.Cedula) (*domain.Foto, error)
QueryFotoById(ctx context.Context, id int64) (*domain.Foto, error)
QueryAllCedulas(ctx context.Context, cedulas []*domain.Cedula) ([]*domain.Foto, error)
}

4
internal/ports/nats.go Normal file
View File

@ -0,0 +1,4 @@
package ports
type NATSPort interface {
}

64
k8s/deployment.yml Normal file
View File

@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: cedulados-grpc
labels:
app: cedulados-grpc
spec:
replicas: 2
selector:
matchLabels:
app: cedulados-grpc
template:
metadata:
labels:
app: cedulados-grpc
name: cedulados-grpc
spec:
containers:
- name: cedulados-grpc
image: localhost:32000/cedulados-grpc:latest
env:
- name: ENV
value: "production"
- name: NATS_SERVICE_URL
value: "nats://nats-svc:4222"
- name: INFO_DATA_SOURCE_URL
value: "db/cedulados.db"
- name: PHOTO_DATA_SOURCE_URL
value: "db/fotos.db"
- name: APPLICATION_PORT
value: "8080"
volumeMounts:
- name: db
mountPath: "/app/db"
volumes:
- name: db
persistentVolumeClaim:
claimName: cedulados-grpc-pvc
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cedulados-grpc-pvc
spec:
storageClassName: nfs-csi
accessModes: [ReadWriteMany]
resources:
requests:
storage: 200Gi
---
apiVersion: v1
kind: Service
metadata:
name: cedulados-grpc-svc
spec:
selector:
app: cedulados-grpc
ports:
- port: 80
protocol: TCP
targetPort: 8080
name: grpc
type: LoadBalancer

View File

@ -0,0 +1,64 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: $REPONAME-grpc
labels:
app: $REPONAME-grpc
spec:
replicas: 2
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: "production"
- name: NATS_SERVICE_URL
value: "$NATS_SERVICE_URL"
- name: INFO_DATA_SOURCE_URL
value: "$INFO_DATA_SOURCE_URL"
- name: PHOTO_DATA_SOURCE_URL
value: "$PHOTO_DATA_SOURCE_URL"
- name: APPLICATION_PORT
value: "$APPLICATION_PORT"
volumeMounts:
- name: db
mountPath: "/app/db"
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: 200Gi
---
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

View File

@ -0,0 +1,101 @@
syntax = "proto3";
option go_package="github.com/maximotejeda/msvc-proto/golang/cedulados";
message Cedula {
string mun_ced = 1;
string seq_ced = 2;
string ver_ced = 3;
}
message Cedulado {
int64 id = 1;
string nombres = 2;
string apellido_1 = 3;
string apellido_2 = 4;
Cedula cedula = 5;
string Sexo = 6;
string Direccion = 7;
string Telefono = 8;
string FechaNac = 9;
}
message Foto {
int64 id = 1;
string mun_ced = 2;
string seq_ced = 3;
string ver_ced = 4;
int64 sequencia = 5;
bytes foto = 6;
}
message QueryByCedulaRequest {
Cedula cedula = 1;
}
message QueryResponse {
Cedulado cedulado = 1;
}
message QueryMultipleResponse {
repeated Cedulado cedulados = 1;
int64 total = 2;
int64 page = 3;
string viewing = 4;
}
message QueryByFTSRequest {
string name = 1; // name like maximo tejeda 002 for full text search
int64 page = 2;
}
message QueryByNameLastNameRequest {
string name = 1;
string apellido_1 = 2;
string apellido_2 = 3;
int64 page = 4;
}
message QueryByNamesLocationRequest {
string name = 1;
string apellido_1 = 2;
string apellido_2 = 3;
string municipio = 4;
int64 page = 5;
}
message QueryFotoByCedulaRequest{
Cedula cedula = 2;
}
message QueryFotoByCedulaResponse{
Foto foto = 1;
}
message QueryFotoByIdRequest{
int64 id = 1;
}
message QueryFotoByIdResponse{
Foto foto = 1;
}
message QueryFotoAllCedulasRequest{
repeated Cedula cedulas = 1;
}
message QueryFotoAllCedulasResponse{
repeated Foto fotos = 1;
}
service QueryCedulado{
rpc CeduladosByCedula(QueryByCedulaRequest)
returns (QueryResponse){}
rpc CeduladosByFTS(QueryByFTSRequest)
returns (QueryMultipleResponse){}
rpc CeduladosByNameLastName(QueryByNameLastNameRequest)
returns (QueryMultipleResponse){}
rpc CeduladosByNameAndLocation(QueryByNamesLocationRequest)
returns (QueryMultipleResponse){}
rpc QueryFotoByCedula(QueryFotoByCedulaRequest)
returns (QueryFotoByCedulaResponse){}
rpc QueryFotoById(QueryFotoByIdRequest)
returns (QueryFotoByIdResponse){}
rpc QueryFotosAllCedulas(QueryFotoAllCedulasRequest)
returns (QueryFotoAllCedulasResponse){}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,331 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc v4.25.1
// source: cedulados/cedulados.proto
package cedulados
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 (
QueryCedulado_CeduladosByCedula_FullMethodName = "/QueryCedulado/CeduladosByCedula"
QueryCedulado_CeduladosByFTS_FullMethodName = "/QueryCedulado/CeduladosByFTS"
QueryCedulado_CeduladosByNameLastName_FullMethodName = "/QueryCedulado/CeduladosByNameLastName"
QueryCedulado_CeduladosByNameAndLocation_FullMethodName = "/QueryCedulado/CeduladosByNameAndLocation"
QueryCedulado_QueryFotoByCedula_FullMethodName = "/QueryCedulado/QueryFotoByCedula"
QueryCedulado_QueryFotoById_FullMethodName = "/QueryCedulado/QueryFotoById"
QueryCedulado_QueryFotosAllCedulas_FullMethodName = "/QueryCedulado/QueryFotosAllCedulas"
)
// QueryCeduladoClient is the client API for QueryCedulado 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 QueryCeduladoClient interface {
CeduladosByCedula(ctx context.Context, in *QueryByCedulaRequest, opts ...grpc.CallOption) (*QueryResponse, error)
CeduladosByFTS(ctx context.Context, in *QueryByFTSRequest, opts ...grpc.CallOption) (*QueryMultipleResponse, error)
CeduladosByNameLastName(ctx context.Context, in *QueryByNameLastNameRequest, opts ...grpc.CallOption) (*QueryMultipleResponse, error)
CeduladosByNameAndLocation(ctx context.Context, in *QueryByNamesLocationRequest, opts ...grpc.CallOption) (*QueryMultipleResponse, error)
QueryFotoByCedula(ctx context.Context, in *QueryFotoByCedulaRequest, opts ...grpc.CallOption) (*QueryFotoByCedulaResponse, error)
QueryFotoById(ctx context.Context, in *QueryFotoByIdRequest, opts ...grpc.CallOption) (*QueryFotoByIdResponse, error)
QueryFotosAllCedulas(ctx context.Context, in *QueryFotoAllCedulasRequest, opts ...grpc.CallOption) (*QueryFotoAllCedulasResponse, error)
}
type queryCeduladoClient struct {
cc grpc.ClientConnInterface
}
func NewQueryCeduladoClient(cc grpc.ClientConnInterface) QueryCeduladoClient {
return &queryCeduladoClient{cc}
}
func (c *queryCeduladoClient) CeduladosByCedula(ctx context.Context, in *QueryByCedulaRequest, opts ...grpc.CallOption) (*QueryResponse, error) {
out := new(QueryResponse)
err := c.cc.Invoke(ctx, QueryCedulado_CeduladosByCedula_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryCeduladoClient) CeduladosByFTS(ctx context.Context, in *QueryByFTSRequest, opts ...grpc.CallOption) (*QueryMultipleResponse, error) {
out := new(QueryMultipleResponse)
err := c.cc.Invoke(ctx, QueryCedulado_CeduladosByFTS_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryCeduladoClient) CeduladosByNameLastName(ctx context.Context, in *QueryByNameLastNameRequest, opts ...grpc.CallOption) (*QueryMultipleResponse, error) {
out := new(QueryMultipleResponse)
err := c.cc.Invoke(ctx, QueryCedulado_CeduladosByNameLastName_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryCeduladoClient) CeduladosByNameAndLocation(ctx context.Context, in *QueryByNamesLocationRequest, opts ...grpc.CallOption) (*QueryMultipleResponse, error) {
out := new(QueryMultipleResponse)
err := c.cc.Invoke(ctx, QueryCedulado_CeduladosByNameAndLocation_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryCeduladoClient) QueryFotoByCedula(ctx context.Context, in *QueryFotoByCedulaRequest, opts ...grpc.CallOption) (*QueryFotoByCedulaResponse, error) {
out := new(QueryFotoByCedulaResponse)
err := c.cc.Invoke(ctx, QueryCedulado_QueryFotoByCedula_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryCeduladoClient) QueryFotoById(ctx context.Context, in *QueryFotoByIdRequest, opts ...grpc.CallOption) (*QueryFotoByIdResponse, error) {
out := new(QueryFotoByIdResponse)
err := c.cc.Invoke(ctx, QueryCedulado_QueryFotoById_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *queryCeduladoClient) QueryFotosAllCedulas(ctx context.Context, in *QueryFotoAllCedulasRequest, opts ...grpc.CallOption) (*QueryFotoAllCedulasResponse, error) {
out := new(QueryFotoAllCedulasResponse)
err := c.cc.Invoke(ctx, QueryCedulado_QueryFotosAllCedulas_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// QueryCeduladoServer is the server API for QueryCedulado service.
// All implementations must embed UnimplementedQueryCeduladoServer
// for forward compatibility
type QueryCeduladoServer interface {
CeduladosByCedula(context.Context, *QueryByCedulaRequest) (*QueryResponse, error)
CeduladosByFTS(context.Context, *QueryByFTSRequest) (*QueryMultipleResponse, error)
CeduladosByNameLastName(context.Context, *QueryByNameLastNameRequest) (*QueryMultipleResponse, error)
CeduladosByNameAndLocation(context.Context, *QueryByNamesLocationRequest) (*QueryMultipleResponse, error)
QueryFotoByCedula(context.Context, *QueryFotoByCedulaRequest) (*QueryFotoByCedulaResponse, error)
QueryFotoById(context.Context, *QueryFotoByIdRequest) (*QueryFotoByIdResponse, error)
QueryFotosAllCedulas(context.Context, *QueryFotoAllCedulasRequest) (*QueryFotoAllCedulasResponse, error)
mustEmbedUnimplementedQueryCeduladoServer()
}
// UnimplementedQueryCeduladoServer must be embedded to have forward compatible implementations.
type UnimplementedQueryCeduladoServer struct {
}
func (UnimplementedQueryCeduladoServer) CeduladosByCedula(context.Context, *QueryByCedulaRequest) (*QueryResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CeduladosByCedula not implemented")
}
func (UnimplementedQueryCeduladoServer) CeduladosByFTS(context.Context, *QueryByFTSRequest) (*QueryMultipleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CeduladosByFTS not implemented")
}
func (UnimplementedQueryCeduladoServer) CeduladosByNameLastName(context.Context, *QueryByNameLastNameRequest) (*QueryMultipleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CeduladosByNameLastName not implemented")
}
func (UnimplementedQueryCeduladoServer) CeduladosByNameAndLocation(context.Context, *QueryByNamesLocationRequest) (*QueryMultipleResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method CeduladosByNameAndLocation not implemented")
}
func (UnimplementedQueryCeduladoServer) QueryFotoByCedula(context.Context, *QueryFotoByCedulaRequest) (*QueryFotoByCedulaResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryFotoByCedula not implemented")
}
func (UnimplementedQueryCeduladoServer) QueryFotoById(context.Context, *QueryFotoByIdRequest) (*QueryFotoByIdResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryFotoById not implemented")
}
func (UnimplementedQueryCeduladoServer) QueryFotosAllCedulas(context.Context, *QueryFotoAllCedulasRequest) (*QueryFotoAllCedulasResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method QueryFotosAllCedulas not implemented")
}
func (UnimplementedQueryCeduladoServer) mustEmbedUnimplementedQueryCeduladoServer() {}
// UnsafeQueryCeduladoServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to QueryCeduladoServer will
// result in compilation errors.
type UnsafeQueryCeduladoServer interface {
mustEmbedUnimplementedQueryCeduladoServer()
}
func RegisterQueryCeduladoServer(s grpc.ServiceRegistrar, srv QueryCeduladoServer) {
s.RegisterService(&QueryCedulado_ServiceDesc, srv)
}
func _QueryCedulado_CeduladosByCedula_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryByCedulaRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryCeduladoServer).CeduladosByCedula(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: QueryCedulado_CeduladosByCedula_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryCeduladoServer).CeduladosByCedula(ctx, req.(*QueryByCedulaRequest))
}
return interceptor(ctx, in, info, handler)
}
func _QueryCedulado_CeduladosByFTS_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryByFTSRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryCeduladoServer).CeduladosByFTS(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: QueryCedulado_CeduladosByFTS_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryCeduladoServer).CeduladosByFTS(ctx, req.(*QueryByFTSRequest))
}
return interceptor(ctx, in, info, handler)
}
func _QueryCedulado_CeduladosByNameLastName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryByNameLastNameRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryCeduladoServer).CeduladosByNameLastName(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: QueryCedulado_CeduladosByNameLastName_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryCeduladoServer).CeduladosByNameLastName(ctx, req.(*QueryByNameLastNameRequest))
}
return interceptor(ctx, in, info, handler)
}
func _QueryCedulado_CeduladosByNameAndLocation_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryByNamesLocationRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryCeduladoServer).CeduladosByNameAndLocation(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: QueryCedulado_CeduladosByNameAndLocation_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryCeduladoServer).CeduladosByNameAndLocation(ctx, req.(*QueryByNamesLocationRequest))
}
return interceptor(ctx, in, info, handler)
}
func _QueryCedulado_QueryFotoByCedula_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryFotoByCedulaRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryCeduladoServer).QueryFotoByCedula(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: QueryCedulado_QueryFotoByCedula_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryCeduladoServer).QueryFotoByCedula(ctx, req.(*QueryFotoByCedulaRequest))
}
return interceptor(ctx, in, info, handler)
}
func _QueryCedulado_QueryFotoById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryFotoByIdRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryCeduladoServer).QueryFotoById(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: QueryCedulado_QueryFotoById_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryCeduladoServer).QueryFotoById(ctx, req.(*QueryFotoByIdRequest))
}
return interceptor(ctx, in, info, handler)
}
func _QueryCedulado_QueryFotosAllCedulas_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(QueryFotoAllCedulasRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(QueryCeduladoServer).QueryFotosAllCedulas(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: QueryCedulado_QueryFotosAllCedulas_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(QueryCeduladoServer).QueryFotosAllCedulas(ctx, req.(*QueryFotoAllCedulasRequest))
}
return interceptor(ctx, in, info, handler)
}
// QueryCedulado_ServiceDesc is the grpc.ServiceDesc for QueryCedulado service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var QueryCedulado_ServiceDesc = grpc.ServiceDesc{
ServiceName: "QueryCedulado",
HandlerType: (*QueryCeduladoServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "CeduladosByCedula",
Handler: _QueryCedulado_CeduladosByCedula_Handler,
},
{
MethodName: "CeduladosByFTS",
Handler: _QueryCedulado_CeduladosByFTS_Handler,
},
{
MethodName: "CeduladosByNameLastName",
Handler: _QueryCedulado_CeduladosByNameLastName_Handler,
},
{
MethodName: "CeduladosByNameAndLocation",
Handler: _QueryCedulado_CeduladosByNameAndLocation_Handler,
},
{
MethodName: "QueryFotoByCedula",
Handler: _QueryCedulado_QueryFotoByCedula_Handler,
},
{
MethodName: "QueryFotoById",
Handler: _QueryCedulado_QueryFotoById_Handler,
},
{
MethodName: "QueryFotosAllCedulas",
Handler: _QueryCedulado_QueryFotosAllCedulas_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "cedulados/cedulados.proto",
}