This commit is contained in:
2024-04-20 16:45:56 +03:00
commit 937c8d8eab
34 changed files with 3048 additions and 0 deletions

127
.drone.yaml Normal file
View File

@@ -0,0 +1,127 @@
---
kind: pipeline
type: docker
name: CI
steps:
- name: LINT
image: golangci/golangci-lint
volumes:
- name: cache
path: /go
environment:
GOMODCACHE: '/go/mod'
GOCACHE: '/go/cache'
commands:
- go fmt ./...
- golangci-lint run ./... --timeout=10m --verbose
- name: TEST
image: golang
volumes:
- name: cache
path: /go
environment:
GOMODCACHE: '/go/mod'
GOCACHE: '/go/cache'
commands:
- go test ./...
depends_on:
- LINT
- name: BUILD
image: plugins/docker
workspace:
path: /
settings:
repo: registry.spasskiy.site/gerlad
dockerfile: ./Dockerfile
tags: [ "latest" ]
username:
from_secret: docker_username
password:
from_secret: docker_password
registry: registry.spasskiy.site
depends_on:
- LINT
- TEST
- name: NOTIFY_TG
image: appleboy/drone-telegram
settings:
token:
from_secret: telegram_token
to: "-605857722"
message: >
{{#success build.status}}
*[Gerald]* ✅ Build *{{build.number}}* on branch *{{commit.branch}}* tested successfully.
{{else}}
*[Gerlad]* 🚧 Build *{{build.number}}* on branch *{{commit.branch}}* failed.
{{/success}}
*msg:* {{commit.message}}
depends_on:
- LINT
- TEST
- BUILD
volumes:
- name: cache
host:
path: /var/lib/cache
trigger:
event:
- push
---
kind: pipeline
type: docker
name: CD-STAGE
steps:
- name: DEPLOY-PROD
image: appleboy/drone-ssh:1.6.4
environment:
POSTGRES_DSN:
from_secret: POSTGRES_DSN
APNS_KEY:
from_secret: APNS_KEY
settings:
host: 68.183.14.242
username: root
password:
from_secret: ssh_password
script:
- cd ~/gerald && git checkout master && git pull
- cd ~/gerlad/deploy && docker-compose pull gerlad
- docker-compose up -d pg traefik
- POSTGRES_DSN=$POSTGRES_DSN docker-compose up -d --force-recreate --no-deps --build gerald
when:
target:
- production
- name: NOTIFY-TG
image: appleboy/drone-telegram
settings:
token:
from_secret: telegram_token
to: "-605857722"
message: >
{{#success build.status}}
*[2phon]* 🚀 Build {{build.number}} *deployed* on *production*.
{{else}}
*[2phon]* ❌ Build {{build.number}} failed to deploy on *production*.
{{/success}}
*commit msg:* {{commit.message}}
depends_on:
- DEPLOY-PROD
when:
target:
- production
trigger:
event:
- promote
target:
- staging
- production

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.dist/
.idea/
.DS_Store
/dist/marlboro

10
Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM golang as builder
ENV CGO_ENABLED=0 GOOS=linux
WORKDIR /app
ADD . .
RUN go build -o build ./cmd
FROM alpine as runner
WORKDIR /app
COPY --from=builder /app/build /app/build
ENTRYPOINT ["/app/build"]

34
Makefile Normal file
View File

@@ -0,0 +1,34 @@
#!make
PROJECT_NAME=gerald
spec_path = ./api/$(PROJECT_NAME).yaml
models_pkg = $(PROJECT_NAME)/internal/interfaces/rest/models
build:
CGO_ENABLED=0 go build -o ./dist/$(PROJECT_NAME) $(PROJECT_NAME)/cmd
generate:
rm -rf ./internal/interfaces/rest/models/*
rm -rf ./internal/interfaces/rest/restapi/operations
# download go-swagger in not exist
go get github.com/go-swagger/go-swagger/cmd/swagger
# generate swagger models and server handlers
go run github.com/go-swagger/go-swagger/cmd/swagger generate model --spec=$(spec_path) --target=./internal/interfaces/rest
go run github.com/go-swagger/go-swagger/cmd/swagger generate server --skip-models --existing-models=$(models_pkg) --exclude-main --spec=$(spec_path) --target=./internal/interfaces/rest
# clean up the generated files
rm -rf ./gen
go mod tidy
serve:
./dist/$(PROJECT_NAME)
test:
go test -v ./...
lint:
go fmt ./...
golangci-lint run ./... --timeout 5m0s -v
run: build serve
r: build serve

79
api/gerald.yaml Normal file
View File

@@ -0,0 +1,79 @@
swagger: '2.0'
info:
description: "gerald - feedback application server"
version: 0.0.1
title: gerald
basePath: /
consumes:
- application/json
produces:
- application/json
paths:
/feedback:
post:
summary: "Create a feedback"
tags:
- "feedback"
description: "Create a feedback"
operationId: "createFeedback"
parameters:
- in: body
name: body
description: "Feedback object that needs to be created"
required: true
schema:
$ref: "#/definitions/Feedback"
- in: query
name: session_id
description: "Session ID"
required: true
type: string
responses:
200:
description: "successful operation"
403:
description: "Forbidden"
/feedbacks:
get:
summary: "Get all feedbacks"
tags:
- "feedback"
description: "Get all feedbacks"
operationId: "getFeedbacks"
parameters:
- in: query
name: session_id
description: "Session ID"
required: true
type: string
responses:
200:
description: "successful operation"
schema:
type: array
items:
$ref: "#/definitions/Feedback"
403:
description: "Forbidden"
definitions:
Feedback:
type: object
properties:
user_id:
type: string
description: to have more context about the user
project_id:
type: string
description: to have more context about the project
type:
type: string
enum:
- feature_request
- feedback
- support
text:
type: string

35
cmd/main.go Normal file
View File

@@ -0,0 +1,35 @@
package main
import (
"gerald/internal/adapters/config"
"gerald/internal/adapters/logger"
"gerald/internal/interfaces/rest"
"gerald/internal/services"
"gerald/repo/pg"
"go.uber.org/fx"
)
func main() {
fx.New(
// COMMON
fx.Provide(config.GetConfig),
fx.Provide(logger.NewLogger),
// SERVICES
fx.Provide(services.NewFeedbackService),
// SERVER
fx.Provide(rest.NewServer),
fx.Invoke(rest.StartHTTPServer),
// DB
fx.Provide(
fx.Annotate(
pg.NewClient,
fx.OnStop(pg.Close),
),
pg.NewDB,
),
fx.Invoke(pg.CreateSchema),
).Run()
}

View File

@@ -0,0 +1,17 @@
http:
middlewares:
cors:
headers:
accessControlAllowMethods: ["GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"]
accessControlAllowOriginList: "*"
accessControlMaxAge: 100
accessControlAllowCredentials: true
accessControlAllowHeaders: "*"
accessControlExposeHeaders: ["Authorization"]
forceSTSHeader: true
frameDeny: true
sslRedirect: true
sslForceHost: true
stsPreload: true
stsseconds: "31536000"
customFrameOptionsValue: "SAMEORIGIN"

View File

@@ -0,0 +1,86 @@
x-custom: &envs
SERVING_PORT: 8080
MIGRATION_MODE: "on"
LOGGING_LEVEL: debug
POSTGRES_DSN: "host=pg port=2345 dbname=gerald user=gerald password=wR7gP2kD9qLxVzT3mBc sslmode=disable"
x-custom-pg: &envs-pg
POSTGRES_USER: "gerald"
POSTGRES_PASSWORD: "wR7gP2kD9qLxVzT3mBc"
POSTGRES_DB: "gerald"
services:
traefik:
image: traefik
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
ports:
- 80:80
- 443:443
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik.yml:/traefik.yml
- ./custom/:/custom/
- traefik-ssl-sert:/ssl-certs
- /var/log:/var/log/
networks:
- front
- back
labels:
- traefik.enable=true
- traefik.http.routers.traefik.entrypoints=websecure
- traefik.http.routers.traefik.rule=Host(`gerald-tr.octagon-lab.com`)
- traefik.http.routers.traefik.tls=true
- traefik.http.routers.traefik.tls.certresolver=letsEncrypt
- traefik.http.routers.traefik.service=api@internal
- traefik.http.services.traefik.loadbalancer.server.port=8080
- traefik.http.middlewares.tr-auth.basicauth.users=admin:$$apr1$$.gRkGUr0$$wu//p7fgWjDM1rlyQOGbW1,user:$$apr1$$J1cX/7u6$$tyPWYdAVYqACdWJy29FMn1
- traefik.http.routers.traefik.middlewares=tr-auth
gerald:
hostname: gerald
restart: unless-stopped
image: registry.spasskiy.site/gerald:latest
environment:
APP_NAME: "gerald"
<<: *envs
networks:
- back
labels:
- traefik.enable=true
- traefik.http.routers.gerald.service=gerald
- traefik.http.routers.gerald.entrypoints=websecure
- traefik.http.routers.gerald.tls=true
- traefik.http.routers.gerald.tls.certresolver=letsEncrypt
- traefik.http.routers.gerald.rule=Host(`api.2phon.com`) || Host(`2phon.oct-app.com`)
- traefik.http.services.gerald.loadbalancer.server.port=8080
- traefik.http.middlewares.gerald-ratelimit.ratelimit.average=5
- traefik.http.middlewares.gerald-ratelimit.ratelimit.burst=10
- traefik.http.middlewares.gerald-ratelimit.ratelimit.period=1s
- traefik.http.routers.gerald.middlewares=cors@file, gerald-ratelimit
pg:
image: postgres
restart: unless-stopped
environment:
<<: *envs-pg
PGDATA: /var/lib/postgresql/data/pgdata
networks:
- back
- front
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
driver: local
traefik-ssl-sert:
driver: local
networks:
front:
back:

43
deploy/traefik.yml Normal file
View File

@@ -0,0 +1,43 @@
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: "websecure"
scheme: "https"
permanent: true
websecure:
address: ":443"
postgres:
address: ":5432"
providers:
file:
directory: custom/
watch: true
docker:
endpoint: "unix:///var/run/docker.sock"
watch: true
certificatesResolvers:
letsEncrypt:
acme:
email: gwookiee@gmail.com
storage: /ssl-certs/acme.json
httpChallenge:
entryPoint: web
tlsChallenge: {}
api:
dashboard: true
log:
filePath: "/var/log/traefik.log"
accessLog:
filePath: "/var/log/traefik_access.log"
bufferingSize: 100
pilot:
token: "b07f1173-3ea3-47b8-b993-c286a3fa2e42"

51
go.mod Normal file
View File

@@ -0,0 +1,51 @@
module gerald
go 1.22
require (
github.com/go-openapi/errors v0.20.4
github.com/go-openapi/loads v0.21.2
github.com/go-openapi/runtime v0.26.0
github.com/go-openapi/spec v0.20.9
github.com/go-openapi/strfmt v0.21.7
github.com/go-openapi/swag v0.22.4
github.com/go-openapi/validate v0.22.1
github.com/google/uuid v1.3.0
github.com/jessevdk/go-flags v1.5.0
github.com/rs/zerolog v1.32.0
go.uber.org/fx v1.21.0
golang.org/x/net v0.10.0
gorm.io/driver/postgres v1.5.7
gorm.io/gorm v1.25.9
)
require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.8.4 // indirect
go.mongodb.org/mongo-driver v1.11.3 // indirect
go.uber.org/dig v1.17.1 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.26.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

258
go.sum Normal file
View File

@@ -0,0 +1,258 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M=
github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro=
github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw=
github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc=
github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8=
github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k=
github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU=
github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y=
go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc=
go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
go.uber.org/fx v1.21.0 h1:qqD6k7PyFHONffW5speYx403ywanuASqU4Rqdpc22XY=
go.uber.org/fx v1.21.0/go.mod h1:HT2M7d7RHo+ebKGh9NRcrsrHHfpZ60nW3QRubMRfv48=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM=
gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
gorm.io/gorm v1.25.9 h1:wct0gxZIELDk8+ZqF/MVnHLkA1rvYlBWUMv2EdsK1g8=
gorm.io/gorm v1.25.9/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

View File

@@ -0,0 +1,30 @@
package config
import (
"os"
"strconv"
)
var (
config *Config
)
type Config struct {
ApplicationName string
ApplicationPort int64
LoggingLevel string
PostgresDSN string
}
func GetConfig() *Config {
port, _ := strconv.ParseInt(os.Getenv("SERVING_PORT"), 10, 64)
config = &Config{
ApplicationName: os.Getenv("APP_NAME"),
ApplicationPort: port,
LoggingLevel: os.Getenv("LOGGING_LEVEL"),
PostgresDSN: os.Getenv("POSTGRES_DSN"),
}
return config
}

View File

@@ -0,0 +1,61 @@
package logger
import (
"github.com/rs/zerolog"
"net/http"
"time"
)
// responseData contains the data from the response.
type responseData struct {
status int
}
// loggingResponseWriter is a wrapper around http.ResponseWriter that keeps track of its HTTP status code and check response data errors
type loggingResponseWriter struct {
http.ResponseWriter
responseData *responseData
}
func (r *loggingResponseWriter) WriteHeader(statusCode int) {
r.ResponseWriter.WriteHeader(statusCode)
r.responseData.status = statusCode
}
// HTTPLoggerFunc create middleware that get status code and error message from response writer with Adapted logger
func HTTPLoggerFunc(logger zerolog.Logger, handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
ipAddress := r.Header.Get("X-Real-Ip")
if ipAddress == "" {
ipAddress = r.Header.Get("X-Forwarded-For")
}
responseData := &responseData{}
ww := loggingResponseWriter{
ResponseWriter: w,
responseData: responseData,
}
handler.ServeHTTP(&ww, r)
switch responseData.status {
case 200, 201, 202, 204:
logger.Info().
Dur("ms", time.Since(start)).
Str("url", r.URL.String()).
Str("mtd", r.Method).
Int("sts", responseData.status).
Str("ip", ipAddress).Send()
default:
logger.Error().
Dur("ms", time.Since(start)).
Str("url", r.URL.String()).
Int("sts", responseData.status).
Str("mtd", r.Method).
Str("ip", ipAddress).
Str("tkn", r.Header.Get("X-API-Key")).Send()
}
})
}

40
internal/adapters/logger/log.go Executable file
View File

@@ -0,0 +1,40 @@
package logger
import (
"gerald/internal/adapters/config"
"os"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/pkgerrors"
)
func NewLogger(config *config.Config) zerolog.Logger {
level, err := zerolog.ParseLevel(config.LoggingLevel)
if err != nil {
panic(err)
}
//zerolog global settings
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
zerolog.LevelFieldName = "lvl"
zerolog.MessageFieldName = "msg"
zerolog.DurationFieldUnit = time.Millisecond
zerolog.DurationFieldInteger = true
//zerolog output settings
output := zerolog.ConsoleWriter{
Out: os.Stderr,
NoColor: false,
TimeFormat: time.Stamp,
PartsOrder: []string{
zerolog.TimestampFieldName,
zerolog.LevelFieldName,
zerolog.MessageFieldName,
zerolog.CallerFieldName,
},
}
//create adapted logger with output settings
return zerolog.New(output).Level(level).With().Timestamp().Logger()
}

View File

@@ -0,0 +1,27 @@
package handlers
import (
"gerald/internal/interfaces/rest/restapi/operations/feedback"
"gerald/internal/services"
"github.com/go-openapi/runtime/middleware"
)
type FeedbackHandler struct {
feedback *services.FeedbackService
}
func NewFeedbackHandler(
feedback *services.FeedbackService,
) *FeedbackHandler {
return &FeedbackHandler{feedback: feedback}
}
func (h *FeedbackHandler) CreateFeedback(params feedback.CreateFeedbackParams, _ interface{}) middleware.Responder {
err := h.feedback.Create(params.SessionID, params.Body)
if err != nil {
return feedback.NewCreateFeedbackForbidden()
}
return feedback.NewCreateFeedbackOK()
}

View File

@@ -0,0 +1,117 @@
// Code generated by go-swagger; DO NOT EDIT.
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"encoding/json"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// Feedback feedback
//
// swagger:model Feedback
type Feedback struct {
// to have more context about the project
ProjectID string `json:"project_id,omitempty"`
// text
Text string `json:"text,omitempty"`
// type
// Enum: [feature_request feedback support]
Type string `json:"type,omitempty"`
// to have more context about the user
UserID string `json:"user_id,omitempty"`
}
// Validate validates this feedback
func (m *Feedback) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateType(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
var feedbackTypeTypePropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["feature_request","feedback","support"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
feedbackTypeTypePropEnum = append(feedbackTypeTypePropEnum, v)
}
}
const (
// FeedbackTypeFeatureRequest captures enum value "feature_request"
FeedbackTypeFeatureRequest string = "feature_request"
// FeedbackTypeFeedback captures enum value "feedback"
FeedbackTypeFeedback string = "feedback"
// FeedbackTypeSupport captures enum value "support"
FeedbackTypeSupport string = "support"
)
// prop value enum
func (m *Feedback) validateTypeEnum(path, location string, value string) error {
if err := validate.EnumCase(path, location, value, feedbackTypeTypePropEnum, true); err != nil {
return err
}
return nil
}
func (m *Feedback) validateType(formats strfmt.Registry) error {
if swag.IsZero(m.Type) { // not required
return nil
}
// value enum
if err := m.validateTypeEnum("type", "body", m.Type); err != nil {
return err
}
return nil
}
// ContextValidate validates this feedback based on context it is used
func (m *Feedback) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *Feedback) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *Feedback) UnmarshalBinary(b []byte) error {
var res Feedback
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,80 @@
// This file is safe to edit. Once it exists it will not be overwritten
package restapi
import (
"crypto/tls"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"gerald/internal/interfaces/rest/restapi/operations"
)
//go:generate swagger generate server --target ../../rest --name Gerald --spec ../../../../api/gerald.yaml --principal interface{} --skip-models --exclude-main
func configureFlags(api *operations.GeraldAPI) {
// api.CommandLineOptionsGroups = []swag.CommandLineOptionsGroup{ ... }
}
func configureAPI(api *operations.GeraldAPI) http.Handler {
// configure the api here
api.ServeError = errors.ServeError
// Set your custom logger if needed. Default one is log.Printf
// Expected interface func(string, ...interface{})
//
// Example:
// api.Logger = log.Printf
api.UseSwaggerUI()
// To continue using redoc as your UI, uncomment the following line
// api.UseRedoc()
api.JSONConsumer = runtime.JSONConsumer()
api.JSONProducer = runtime.JSONProducer()
if api.CreateFeedbackHandler == nil {
api.CreateFeedbackHandler = operations.CreateFeedbackHandlerFunc(func(params operations.CreateFeedbackParams) middleware.Responder {
return middleware.NotImplemented("operation operations.CreateFeedback has not yet been implemented")
})
}
if api.GetFeedbacksHandler == nil {
api.GetFeedbacksHandler = operations.GetFeedbacksHandlerFunc(func(params operations.GetFeedbacksParams) middleware.Responder {
return middleware.NotImplemented("operation operations.GetFeedbacks has not yet been implemented")
})
}
api.PreServerShutdown = func() {}
api.ServerShutdown = func() {}
return setupGlobalMiddleware(api.Serve(setupMiddlewares))
}
// The TLS configuration before HTTPS server starts.
func configureTLS(tlsConfig *tls.Config) {
// Make all necessary changes to the TLS configuration here.
}
// As soon as server is initialized but not run yet, this function will be called.
// If you need to modify a config, store server instance to stop it individually later, this is the place.
// This function can be called multiple times, depending on the number of serving schemes.
// scheme value will be set accordingly: "http", "https" or "unix".
func configureServer(s *http.Server, scheme, addr string) {
}
// The middleware configuration is for the handler executors. These do not apply to the swagger.json document.
// The middleware executes after routing but before authentication, binding and validation.
func setupMiddlewares(handler http.Handler) http.Handler {
return handler
}
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
// So this is a good place to plug in a panic handling middleware, logging and metrics.
func setupGlobalMiddleware(handler http.Handler) http.Handler {
return handler
}

View File

@@ -0,0 +1,19 @@
// Code generated by go-swagger; DO NOT EDIT.
// Package restapi gerald
//
// gerald - feedback application server
// Schemes:
// http
// Host: localhost
// BasePath: /
// Version: 0.0.1
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// swagger:meta
package restapi

View File

@@ -0,0 +1,244 @@
// Code generated by go-swagger; DO NOT EDIT.
package restapi
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"encoding/json"
)
var (
// SwaggerJSON embedded version of the swagger document used at generation time
SwaggerJSON json.RawMessage
// FlatSwaggerJSON embedded flattened version of the swagger document used at generation time
FlatSwaggerJSON json.RawMessage
)
func init() {
SwaggerJSON = json.RawMessage([]byte(`{
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"swagger": "2.0",
"info": {
"description": "gerald - feedback application server",
"title": "gerald",
"version": "0.0.1"
},
"basePath": "/",
"paths": {
"/feedback": {
"post": {
"description": "Create a feedback",
"tags": [
"feedback"
],
"summary": "Create a feedback",
"operationId": "createFeedback",
"parameters": [
{
"description": "Feedback object that needs to be created",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Feedback"
}
},
{
"type": "string",
"description": "Session ID",
"name": "session_id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "successful operation"
},
"403": {
"description": "Forbidden"
}
}
}
},
"/feedbacks": {
"get": {
"description": "Get all feedbacks",
"tags": [
"feedback"
],
"summary": "Get all feedbacks",
"operationId": "getFeedbacks",
"parameters": [
{
"type": "string",
"description": "Session ID",
"name": "session_id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Feedback"
}
}
},
"403": {
"description": "Forbidden"
}
}
}
}
},
"definitions": {
"Feedback": {
"type": "object",
"properties": {
"project_id": {
"description": "to have more context about the project",
"type": "string"
},
"text": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"feature_request",
"feedback",
"support"
]
},
"user_id": {
"description": "to have more context about the user",
"type": "string"
}
}
}
}
}`))
FlatSwaggerJSON = json.RawMessage([]byte(`{
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"swagger": "2.0",
"info": {
"description": "gerald - feedback application server",
"title": "gerald",
"version": "0.0.1"
},
"basePath": "/",
"paths": {
"/feedback": {
"post": {
"description": "Create a feedback",
"tags": [
"feedback"
],
"summary": "Create a feedback",
"operationId": "createFeedback",
"parameters": [
{
"description": "Feedback object that needs to be created",
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Feedback"
}
},
{
"type": "string",
"description": "Session ID",
"name": "session_id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "successful operation"
},
"403": {
"description": "Forbidden"
}
}
}
},
"/feedbacks": {
"get": {
"description": "Get all feedbacks",
"tags": [
"feedback"
],
"summary": "Get all feedbacks",
"operationId": "getFeedbacks",
"parameters": [
{
"type": "string",
"description": "Session ID",
"name": "session_id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Feedback"
}
}
},
"403": {
"description": "Forbidden"
}
}
}
}
},
"definitions": {
"Feedback": {
"type": "object",
"properties": {
"project_id": {
"description": "to have more context about the project",
"type": "string"
},
"text": {
"type": "string"
},
"type": {
"type": "string",
"enum": [
"feature_request",
"feedback",
"support"
]
},
"user_id": {
"description": "to have more context about the user",
"type": "string"
}
}
}
}
}`))
}

View File

@@ -0,0 +1,58 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// CreateFeedbackHandlerFunc turns a function with the right signature into a create feedback handler
type CreateFeedbackHandlerFunc func(CreateFeedbackParams) middleware.Responder
// Handle executing the request and returning a response
func (fn CreateFeedbackHandlerFunc) Handle(params CreateFeedbackParams) middleware.Responder {
return fn(params)
}
// CreateFeedbackHandler interface for that can handle valid create feedback params
type CreateFeedbackHandler interface {
Handle(CreateFeedbackParams) middleware.Responder
}
// NewCreateFeedback creates a new http.Handler for the create feedback operation
func NewCreateFeedback(ctx *middleware.Context, handler CreateFeedbackHandler) *CreateFeedback {
return &CreateFeedback{Context: ctx, Handler: handler}
}
/*
CreateFeedback swagger:route POST /feedback feedback createFeedback
# Create a feedback
Create a feedback
*/
type CreateFeedback struct {
Context *middleware.Context
Handler CreateFeedbackHandler
}
func (o *CreateFeedback) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewCreateFeedbackParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,118 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/validate"
"gerald/internal/interfaces/rest/models"
)
// NewCreateFeedbackParams creates a new CreateFeedbackParams object
//
// There are no default values defined in the spec.
func NewCreateFeedbackParams() CreateFeedbackParams {
return CreateFeedbackParams{}
}
// CreateFeedbackParams contains all the bound params for the create feedback operation
// typically these are obtained from a http.Request
//
// swagger:parameters createFeedback
type CreateFeedbackParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*Feedback object that needs to be created
Required: true
In: body
*/
Body *models.Feedback
/*Session ID
Required: true
In: query
*/
SessionID string
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewCreateFeedbackParams() beforehand.
func (o *CreateFeedbackParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
qs := runtime.Values(r.URL.Query())
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.Feedback
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body", ""))
} else {
res = append(res, errors.NewParseError("body", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
ctx := validate.WithOperationRequest(r.Context())
if err := body.ContextValidate(ctx, route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "body", ""))
}
qSessionID, qhkSessionID, _ := qs.GetOK("session_id")
if err := o.bindSessionID(qSessionID, qhkSessionID, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindSessionID binds and validates parameter SessionID from query.
func (o *CreateFeedbackParams) bindSessionID(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("session_id", "query", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// AllowEmptyValue: false
if err := validate.RequiredString("session_id", "query", raw); err != nil {
return err
}
o.SessionID = raw
return nil
}

View File

@@ -0,0 +1,62 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
)
// CreateFeedbackOKCode is the HTTP code returned for type CreateFeedbackOK
const CreateFeedbackOKCode int = 200
/*
CreateFeedbackOK successful operation
swagger:response createFeedbackOK
*/
type CreateFeedbackOK struct {
}
// NewCreateFeedbackOK creates CreateFeedbackOK with default headers values
func NewCreateFeedbackOK() *CreateFeedbackOK {
return &CreateFeedbackOK{}
}
// WriteResponse to the client
func (o *CreateFeedbackOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(200)
}
// CreateFeedbackForbiddenCode is the HTTP code returned for type CreateFeedbackForbidden
const CreateFeedbackForbiddenCode int = 403
/*
CreateFeedbackForbidden Forbidden
swagger:response createFeedbackForbidden
*/
type CreateFeedbackForbidden struct {
}
// NewCreateFeedbackForbidden creates CreateFeedbackForbidden with default headers values
func NewCreateFeedbackForbidden() *CreateFeedbackForbidden {
return &CreateFeedbackForbidden{}
}
// WriteResponse to the client
func (o *CreateFeedbackForbidden) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(403)
}

View File

@@ -0,0 +1,100 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// CreateFeedbackURL generates an URL for the create feedback operation
type CreateFeedbackURL struct {
SessionID string
_basePath string
// avoid unkeyed usage
_ struct{}
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CreateFeedbackURL) WithBasePath(bp string) *CreateFeedbackURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *CreateFeedbackURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *CreateFeedbackURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/feedback"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
qs := make(url.Values)
sessionIDQ := o.SessionID
if sessionIDQ != "" {
qs.Set("session_id", sessionIDQ)
}
_result.RawQuery = qs.Encode()
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *CreateFeedbackURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *CreateFeedbackURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *CreateFeedbackURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on CreateFeedbackURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on CreateFeedbackURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *CreateFeedbackURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,58 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// GetFeedbacksHandlerFunc turns a function with the right signature into a get feedbacks handler
type GetFeedbacksHandlerFunc func(GetFeedbacksParams) middleware.Responder
// Handle executing the request and returning a response
func (fn GetFeedbacksHandlerFunc) Handle(params GetFeedbacksParams) middleware.Responder {
return fn(params)
}
// GetFeedbacksHandler interface for that can handle valid get feedbacks params
type GetFeedbacksHandler interface {
Handle(GetFeedbacksParams) middleware.Responder
}
// NewGetFeedbacks creates a new http.Handler for the get feedbacks operation
func NewGetFeedbacks(ctx *middleware.Context, handler GetFeedbacksHandler) *GetFeedbacks {
return &GetFeedbacks{Context: ctx, Handler: handler}
}
/*
GetFeedbacks swagger:route GET /feedbacks feedback getFeedbacks
# Get all feedbacks
Get all feedbacks
*/
type GetFeedbacks struct {
Context *middleware.Context
Handler GetFeedbacksHandler
}
func (o *GetFeedbacks) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
*r = *rCtx
}
var Params = NewGetFeedbacksParams()
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,82 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/validate"
)
// NewGetFeedbacksParams creates a new GetFeedbacksParams object
//
// There are no default values defined in the spec.
func NewGetFeedbacksParams() GetFeedbacksParams {
return GetFeedbacksParams{}
}
// GetFeedbacksParams contains all the bound params for the get feedbacks operation
// typically these are obtained from a http.Request
//
// swagger:parameters getFeedbacks
type GetFeedbacksParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*Session ID
Required: true
In: query
*/
SessionID string
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewGetFeedbacksParams() beforehand.
func (o *GetFeedbacksParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
qs := runtime.Values(r.URL.Query())
qSessionID, qhkSessionID, _ := qs.GetOK("session_id")
if err := o.bindSessionID(qSessionID, qhkSessionID, route.Formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
// bindSessionID binds and validates parameter SessionID from query.
func (o *GetFeedbacksParams) bindSessionID(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("session_id", "query", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// AllowEmptyValue: false
if err := validate.RequiredString("session_id", "query", raw); err != nil {
return err
}
o.SessionID = raw
return nil
}

View File

@@ -0,0 +1,87 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"gerald/internal/interfaces/rest/models"
)
// GetFeedbacksOKCode is the HTTP code returned for type GetFeedbacksOK
const GetFeedbacksOKCode int = 200
/*
GetFeedbacksOK successful operation
swagger:response getFeedbacksOK
*/
type GetFeedbacksOK struct {
/*
In: Body
*/
Payload []*models.Feedback `json:"body,omitempty"`
}
// NewGetFeedbacksOK creates GetFeedbacksOK with default headers values
func NewGetFeedbacksOK() *GetFeedbacksOK {
return &GetFeedbacksOK{}
}
// WithPayload adds the payload to the get feedbacks o k response
func (o *GetFeedbacksOK) WithPayload(payload []*models.Feedback) *GetFeedbacksOK {
o.Payload = payload
return o
}
// SetPayload sets the payload to the get feedbacks o k response
func (o *GetFeedbacksOK) SetPayload(payload []*models.Feedback) {
o.Payload = payload
}
// WriteResponse to the client
func (o *GetFeedbacksOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
payload := o.Payload
if payload == nil {
// return empty array
payload = make([]*models.Feedback, 0, 50)
}
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
// GetFeedbacksForbiddenCode is the HTTP code returned for type GetFeedbacksForbidden
const GetFeedbacksForbiddenCode int = 403
/*
GetFeedbacksForbidden Forbidden
swagger:response getFeedbacksForbidden
*/
type GetFeedbacksForbidden struct {
}
// NewGetFeedbacksForbidden creates GetFeedbacksForbidden with default headers values
func NewGetFeedbacksForbidden() *GetFeedbacksForbidden {
return &GetFeedbacksForbidden{}
}
// WriteResponse to the client
func (o *GetFeedbacksForbidden) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
rw.WriteHeader(403)
}

View File

@@ -0,0 +1,100 @@
// Code generated by go-swagger; DO NOT EDIT.
package feedback
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// GetFeedbacksURL generates an URL for the get feedbacks operation
type GetFeedbacksURL struct {
SessionID string
_basePath string
// avoid unkeyed usage
_ struct{}
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *GetFeedbacksURL) WithBasePath(bp string) *GetFeedbacksURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *GetFeedbacksURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *GetFeedbacksURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/feedbacks"
_basePath := o._basePath
if _basePath == "" {
_basePath = "/"
}
_result.Path = golangswaggerpaths.Join(_basePath, _path)
qs := make(url.Values)
sessionIDQ := o.SessionID
if sessionIDQ != "" {
qs.Set("session_id", sessionIDQ)
}
_result.RawQuery = qs.Encode()
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *GetFeedbacksURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *GetFeedbacksURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *GetFeedbacksURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on GetFeedbacksURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on GetFeedbacksURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *GetFeedbacksURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,315 @@
// Code generated by go-swagger; DO NOT EDIT.
package operations
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"fmt"
"net/http"
"strings"
"github.com/go-openapi/errors"
"github.com/go-openapi/loads"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/runtime/security"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"gerald/internal/interfaces/rest/restapi/operations/feedback"
)
// NewGeraldAPI creates a new Gerald instance
func NewGeraldAPI(spec *loads.Document) *GeraldAPI {
return &GeraldAPI{
handlers: make(map[string]map[string]http.Handler),
formats: strfmt.Default,
defaultConsumes: "application/json",
defaultProduces: "application/json",
customConsumers: make(map[string]runtime.Consumer),
customProducers: make(map[string]runtime.Producer),
PreServerShutdown: func() {},
ServerShutdown: func() {},
spec: spec,
useSwaggerUI: false,
ServeError: errors.ServeError,
BasicAuthenticator: security.BasicAuth,
APIKeyAuthenticator: security.APIKeyAuth,
BearerAuthenticator: security.BearerAuth,
JSONConsumer: runtime.JSONConsumer(),
JSONProducer: runtime.JSONProducer(),
FeedbackCreateFeedbackHandler: feedback.CreateFeedbackHandlerFunc(func(params feedback.CreateFeedbackParams) middleware.Responder {
return middleware.NotImplemented("operation feedback.CreateFeedback has not yet been implemented")
}),
FeedbackGetFeedbacksHandler: feedback.GetFeedbacksHandlerFunc(func(params feedback.GetFeedbacksParams) middleware.Responder {
return middleware.NotImplemented("operation feedback.GetFeedbacks has not yet been implemented")
}),
}
}
/*GeraldAPI gerald - feedback application server */
type GeraldAPI struct {
spec *loads.Document
context *middleware.Context
handlers map[string]map[string]http.Handler
formats strfmt.Registry
customConsumers map[string]runtime.Consumer
customProducers map[string]runtime.Producer
defaultConsumes string
defaultProduces string
Middleware func(middleware.Builder) http.Handler
useSwaggerUI bool
// BasicAuthenticator generates a runtime.Authenticator from the supplied basic auth function.
// It has a default implementation in the security package, however you can replace it for your particular usage.
BasicAuthenticator func(security.UserPassAuthentication) runtime.Authenticator
// APIKeyAuthenticator generates a runtime.Authenticator from the supplied token auth function.
// It has a default implementation in the security package, however you can replace it for your particular usage.
APIKeyAuthenticator func(string, string, security.TokenAuthentication) runtime.Authenticator
// BearerAuthenticator generates a runtime.Authenticator from the supplied bearer token auth function.
// It has a default implementation in the security package, however you can replace it for your particular usage.
BearerAuthenticator func(string, security.ScopedTokenAuthentication) runtime.Authenticator
// JSONConsumer registers a consumer for the following mime types:
// - application/json
JSONConsumer runtime.Consumer
// JSONProducer registers a producer for the following mime types:
// - application/json
JSONProducer runtime.Producer
// FeedbackCreateFeedbackHandler sets the operation handler for the create feedback operation
FeedbackCreateFeedbackHandler feedback.CreateFeedbackHandler
// FeedbackGetFeedbacksHandler sets the operation handler for the get feedbacks operation
FeedbackGetFeedbacksHandler feedback.GetFeedbacksHandler
// ServeError is called when an error is received, there is a default handler
// but you can set your own with this
ServeError func(http.ResponseWriter, *http.Request, error)
// PreServerShutdown is called before the HTTP(S) server is shutdown
// This allows for custom functions to get executed before the HTTP(S) server stops accepting traffic
PreServerShutdown func()
// ServerShutdown is called when the HTTP(S) server is shut down and done
// handling all active connections and does not accept connections any more
ServerShutdown func()
// Custom command line argument groups with their descriptions
CommandLineOptionsGroups []swag.CommandLineOptionsGroup
// User defined logger function.
Logger func(string, ...interface{})
}
// UseRedoc for documentation at /docs
func (o *GeraldAPI) UseRedoc() {
o.useSwaggerUI = false
}
// UseSwaggerUI for documentation at /docs
func (o *GeraldAPI) UseSwaggerUI() {
o.useSwaggerUI = true
}
// SetDefaultProduces sets the default produces media type
func (o *GeraldAPI) SetDefaultProduces(mediaType string) {
o.defaultProduces = mediaType
}
// SetDefaultConsumes returns the default consumes media type
func (o *GeraldAPI) SetDefaultConsumes(mediaType string) {
o.defaultConsumes = mediaType
}
// SetSpec sets a spec that will be served for the clients.
func (o *GeraldAPI) SetSpec(spec *loads.Document) {
o.spec = spec
}
// DefaultProduces returns the default produces media type
func (o *GeraldAPI) DefaultProduces() string {
return o.defaultProduces
}
// DefaultConsumes returns the default consumes media type
func (o *GeraldAPI) DefaultConsumes() string {
return o.defaultConsumes
}
// Formats returns the registered string formats
func (o *GeraldAPI) Formats() strfmt.Registry {
return o.formats
}
// RegisterFormat registers a custom format validator
func (o *GeraldAPI) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) {
o.formats.Add(name, format, validator)
}
// Validate validates the registrations in the GeraldAPI
func (o *GeraldAPI) Validate() error {
var unregistered []string
if o.JSONConsumer == nil {
unregistered = append(unregistered, "JSONConsumer")
}
if o.JSONProducer == nil {
unregistered = append(unregistered, "JSONProducer")
}
if o.FeedbackCreateFeedbackHandler == nil {
unregistered = append(unregistered, "feedback.CreateFeedbackHandler")
}
if o.FeedbackGetFeedbacksHandler == nil {
unregistered = append(unregistered, "feedback.GetFeedbacksHandler")
}
if len(unregistered) > 0 {
return fmt.Errorf("missing registration: %s", strings.Join(unregistered, ", "))
}
return nil
}
// ServeErrorFor gets a error handler for a given operation id
func (o *GeraldAPI) ServeErrorFor(operationID string) func(http.ResponseWriter, *http.Request, error) {
return o.ServeError
}
// AuthenticatorsFor gets the authenticators for the specified security schemes
func (o *GeraldAPI) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator {
return nil
}
// Authorizer returns the registered authorizer
func (o *GeraldAPI) Authorizer() runtime.Authorizer {
return nil
}
// ConsumersFor gets the consumers for the specified media types.
// MIME type parameters are ignored here.
func (o *GeraldAPI) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer {
result := make(map[string]runtime.Consumer, len(mediaTypes))
for _, mt := range mediaTypes {
switch mt {
case "application/json":
result["application/json"] = o.JSONConsumer
}
if c, ok := o.customConsumers[mt]; ok {
result[mt] = c
}
}
return result
}
// ProducersFor gets the producers for the specified media types.
// MIME type parameters are ignored here.
func (o *GeraldAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
result := make(map[string]runtime.Producer, len(mediaTypes))
for _, mt := range mediaTypes {
switch mt {
case "application/json":
result["application/json"] = o.JSONProducer
}
if p, ok := o.customProducers[mt]; ok {
result[mt] = p
}
}
return result
}
// HandlerFor gets a http.Handler for the provided operation method and path
func (o *GeraldAPI) HandlerFor(method, path string) (http.Handler, bool) {
if o.handlers == nil {
return nil, false
}
um := strings.ToUpper(method)
if _, ok := o.handlers[um]; !ok {
return nil, false
}
if path == "/" {
path = ""
}
h, ok := o.handlers[um][path]
return h, ok
}
// Context returns the middleware context for the gerald API
func (o *GeraldAPI) Context() *middleware.Context {
if o.context == nil {
o.context = middleware.NewRoutableContext(o.spec, o, nil)
}
return o.context
}
func (o *GeraldAPI) initHandlerCache() {
o.Context() // don't care about the result, just that the initialization happened
if o.handlers == nil {
o.handlers = make(map[string]map[string]http.Handler)
}
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)
}
o.handlers["POST"]["/feedback"] = feedback.NewCreateFeedback(o.context, o.FeedbackCreateFeedbackHandler)
if o.handlers["GET"] == nil {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/feedbacks"] = feedback.NewGetFeedbacks(o.context, o.FeedbackGetFeedbacksHandler)
}
// Serve creates a http handler to serve the API over HTTP
// can be used directly in http.ListenAndServe(":8000", api.Serve(nil))
func (o *GeraldAPI) Serve(builder middleware.Builder) http.Handler {
o.Init()
if o.Middleware != nil {
return o.Middleware(builder)
}
if o.useSwaggerUI {
return o.context.APIHandlerSwaggerUI(builder)
}
return o.context.APIHandler(builder)
}
// Init allows you to just initialize the handler cache, you can then recompose the middleware as you see fit
func (o *GeraldAPI) Init() {
if len(o.handlers) == 0 {
o.initHandlerCache()
}
}
// RegisterConsumer allows you to add (or override) a consumer for a media type.
func (o *GeraldAPI) RegisterConsumer(mediaType string, consumer runtime.Consumer) {
o.customConsumers[mediaType] = consumer
}
// RegisterProducer allows you to add (or override) a producer for a media type.
func (o *GeraldAPI) RegisterProducer(mediaType string, producer runtime.Producer) {
o.customProducers[mediaType] = producer
}
// AddMiddlewareFor adds a http middleware to existing handler
func (o *GeraldAPI) AddMiddlewareFor(method, path string, builder middleware.Builder) {
um := strings.ToUpper(method)
if path == "/" {
path = ""
}
o.Init()
if h, ok := o.handlers[um][path]; ok {
o.handlers[um][path] = builder(h)
}
}

View File

@@ -0,0 +1,507 @@
// Code generated by go-swagger; DO NOT EDIT.
package restapi
import (
"context"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"sync"
"sync/atomic"
"syscall"
"time"
"github.com/go-openapi/runtime/flagext"
"github.com/go-openapi/swag"
flags "github.com/jessevdk/go-flags"
"golang.org/x/net/netutil"
"gerald/internal/interfaces/rest/restapi/operations"
)
const (
schemeHTTP = "http"
schemeHTTPS = "https"
schemeUnix = "unix"
)
var defaultSchemes []string
func init() {
defaultSchemes = []string{
schemeHTTP,
}
}
// NewServer creates a new api gerald server but does not configure it
func NewServer(api *operations.GeraldAPI) *Server {
s := new(Server)
s.shutdown = make(chan struct{})
s.api = api
s.interrupt = make(chan os.Signal, 1)
return s
}
// ConfigureAPI configures the API and handlers.
func (s *Server) ConfigureAPI() {
if s.api != nil {
s.handler = configureAPI(s.api)
}
}
// ConfigureFlags configures the additional flags defined by the handlers. Needs to be called before the parser.Parse
func (s *Server) ConfigureFlags() {
if s.api != nil {
configureFlags(s.api)
}
}
// Server for the gerald API
type Server struct {
EnabledListeners []string `long:"scheme" description:"the listeners to enable, this can be repeated and defaults to the schemes in the swagger spec"`
CleanupTimeout time.Duration `long:"cleanup-timeout" description:"grace period for which to wait before killing idle connections" default:"10s"`
GracefulTimeout time.Duration `long:"graceful-timeout" description:"grace period for which to wait before shutting down the server" default:"15s"`
MaxHeaderSize flagext.ByteSize `long:"max-header-size" description:"controls the maximum number of bytes the server will read parsing the request header's keys and values, including the request line. It does not limit the size of the request body." default:"1MiB"`
SocketPath flags.Filename `long:"socket-path" description:"the unix socket to listen on" default:"/var/run/gerald.sock"`
domainSocketL net.Listener
Host string `long:"host" description:"the IP to listen on" default:"localhost" env:"HOST"`
Port int `long:"port" description:"the port to listen on for insecure connections, defaults to a random value" env:"PORT"`
ListenLimit int `long:"listen-limit" description:"limit the number of outstanding requests"`
KeepAlive time.Duration `long:"keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)" default:"3m"`
ReadTimeout time.Duration `long:"read-timeout" description:"maximum duration before timing out read of the request" default:"30s"`
WriteTimeout time.Duration `long:"write-timeout" description:"maximum duration before timing out write of the response" default:"60s"`
httpServerL net.Listener
TLSHost string `long:"tls-host" description:"the IP to listen on for tls, when not specified it's the same as --host" env:"TLS_HOST"`
TLSPort int `long:"tls-port" description:"the port to listen on for secure connections, defaults to a random value" env:"TLS_PORT"`
TLSCertificate flags.Filename `long:"tls-certificate" description:"the certificate to use for secure connections" env:"TLS_CERTIFICATE"`
TLSCertificateKey flags.Filename `long:"tls-key" description:"the private key to use for secure connections" env:"TLS_PRIVATE_KEY"`
TLSCACertificate flags.Filename `long:"tls-ca" description:"the certificate authority file to be used with mutual tls auth" env:"TLS_CA_CERTIFICATE"`
TLSListenLimit int `long:"tls-listen-limit" description:"limit the number of outstanding requests"`
TLSKeepAlive time.Duration `long:"tls-keep-alive" description:"sets the TCP keep-alive timeouts on accepted connections. It prunes dead TCP connections ( e.g. closing laptop mid-download)"`
TLSReadTimeout time.Duration `long:"tls-read-timeout" description:"maximum duration before timing out read of the request"`
TLSWriteTimeout time.Duration `long:"tls-write-timeout" description:"maximum duration before timing out write of the response"`
httpsServerL net.Listener
api *operations.GeraldAPI
handler http.Handler
hasListeners bool
shutdown chan struct{}
shuttingDown int32
interrupted bool
interrupt chan os.Signal
}
// Logf logs message either via defined user logger or via system one if no user logger is defined.
func (s *Server) Logf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
} else {
log.Printf(f, args...)
}
}
// Fatalf logs message either via defined user logger or via system one if no user logger is defined.
// Exits with non-zero status after printing
func (s *Server) Fatalf(f string, args ...interface{}) {
if s.api != nil && s.api.Logger != nil {
s.api.Logger(f, args...)
os.Exit(1)
} else {
log.Fatalf(f, args...)
}
}
// SetAPI configures the server with the specified API. Needs to be called before Serve
func (s *Server) SetAPI(api *operations.GeraldAPI) {
if api == nil {
s.api = nil
s.handler = nil
return
}
s.api = api
s.handler = configureAPI(api)
}
func (s *Server) hasScheme(scheme string) bool {
schemes := s.EnabledListeners
if len(schemes) == 0 {
schemes = defaultSchemes
}
for _, v := range schemes {
if v == scheme {
return true
}
}
return false
}
// Serve the api
func (s *Server) Serve() (err error) {
if !s.hasListeners {
if err = s.Listen(); err != nil {
return err
}
}
// set default handler, if none is set
if s.handler == nil {
if s.api == nil {
return errors.New("can't create the default handler, as no api is set")
}
s.SetHandler(s.api.Serve(nil))
}
wg := new(sync.WaitGroup)
once := new(sync.Once)
signalNotify(s.interrupt)
go handleInterrupt(once, s)
servers := []*http.Server{}
if s.hasScheme(schemeUnix) {
domainSocket := new(http.Server)
domainSocket.MaxHeaderBytes = int(s.MaxHeaderSize)
domainSocket.Handler = s.handler
if int64(s.CleanupTimeout) > 0 {
domainSocket.IdleTimeout = s.CleanupTimeout
}
configureServer(domainSocket, "unix", string(s.SocketPath))
servers = append(servers, domainSocket)
wg.Add(1)
s.Logf("Serving gerald at unix://%s", s.SocketPath)
go func(l net.Listener) {
defer wg.Done()
if err := domainSocket.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving gerald at unix://%s", s.SocketPath)
}(s.domainSocketL)
}
if s.hasScheme(schemeHTTP) {
httpServer := new(http.Server)
httpServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpServer.ReadTimeout = s.ReadTimeout
httpServer.WriteTimeout = s.WriteTimeout
httpServer.SetKeepAlivesEnabled(int64(s.KeepAlive) > 0)
if s.ListenLimit > 0 {
s.httpServerL = netutil.LimitListener(s.httpServerL, s.ListenLimit)
}
if int64(s.CleanupTimeout) > 0 {
httpServer.IdleTimeout = s.CleanupTimeout
}
httpServer.Handler = s.handler
configureServer(httpServer, "http", s.httpServerL.Addr().String())
servers = append(servers, httpServer)
wg.Add(1)
s.Logf("Serving gerald at http://%s", s.httpServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpServer.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving gerald at http://%s", l.Addr())
}(s.httpServerL)
}
if s.hasScheme(schemeHTTPS) {
httpsServer := new(http.Server)
httpsServer.MaxHeaderBytes = int(s.MaxHeaderSize)
httpsServer.ReadTimeout = s.TLSReadTimeout
httpsServer.WriteTimeout = s.TLSWriteTimeout
httpsServer.SetKeepAlivesEnabled(int64(s.TLSKeepAlive) > 0)
if s.TLSListenLimit > 0 {
s.httpsServerL = netutil.LimitListener(s.httpsServerL, s.TLSListenLimit)
}
if int64(s.CleanupTimeout) > 0 {
httpsServer.IdleTimeout = s.CleanupTimeout
}
httpsServer.Handler = s.handler
// Inspired by https://blog.bracebin.com/achieving-perfect-ssl-labs-score-with-go
httpsServer.TLSConfig = &tls.Config{
// Causes servers to use Go's default ciphersuite preferences,
// which are tuned to avoid attacks. Does nothing on clients.
PreferServerCipherSuites: true,
// Only use curves which have assembly implementations
// https://github.com/golang/go/tree/master/src/crypto/elliptic
CurvePreferences: []tls.CurveID{tls.CurveP256},
// Use modern tls mode https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
NextProtos: []string{"h2", "http/1.1"},
// https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols
MinVersion: tls.VersionTLS12,
// These ciphersuites support Forward Secrecy: https://en.wikipedia.org/wiki/Forward_secrecy
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
}
// build standard config from server options
if s.TLSCertificate != "" && s.TLSCertificateKey != "" {
httpsServer.TLSConfig.Certificates = make([]tls.Certificate, 1)
httpsServer.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(string(s.TLSCertificate), string(s.TLSCertificateKey))
if err != nil {
return err
}
}
if s.TLSCACertificate != "" {
// include specified CA certificate
caCert, caCertErr := os.ReadFile(string(s.TLSCACertificate))
if caCertErr != nil {
return caCertErr
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(caCert)
if !ok {
return fmt.Errorf("cannot parse CA certificate")
}
httpsServer.TLSConfig.ClientCAs = caCertPool
httpsServer.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
// call custom TLS configurator
configureTLS(httpsServer.TLSConfig)
if len(httpsServer.TLSConfig.Certificates) == 0 && httpsServer.TLSConfig.GetCertificate == nil {
// after standard and custom config are passed, this ends up with no certificate
if s.TLSCertificate == "" {
if s.TLSCertificateKey == "" {
s.Fatalf("the required flags `--tls-certificate` and `--tls-key` were not specified")
}
s.Fatalf("the required flag `--tls-certificate` was not specified")
}
if s.TLSCertificateKey == "" {
s.Fatalf("the required flag `--tls-key` was not specified")
}
// this happens with a wrong custom TLS configurator
s.Fatalf("no certificate was configured for TLS")
}
configureServer(httpsServer, "https", s.httpsServerL.Addr().String())
servers = append(servers, httpsServer)
wg.Add(1)
s.Logf("Serving gerald at https://%s", s.httpsServerL.Addr())
go func(l net.Listener) {
defer wg.Done()
if err := httpsServer.Serve(l); err != nil && err != http.ErrServerClosed {
s.Fatalf("%v", err)
}
s.Logf("Stopped serving gerald at https://%s", l.Addr())
}(tls.NewListener(s.httpsServerL, httpsServer.TLSConfig))
}
wg.Add(1)
go s.handleShutdown(wg, &servers)
wg.Wait()
return nil
}
// Listen creates the listeners for the server
func (s *Server) Listen() error {
if s.hasListeners { // already done this
return nil
}
if s.hasScheme(schemeHTTPS) {
// Use http host if https host wasn't defined
if s.TLSHost == "" {
s.TLSHost = s.Host
}
// Use http listen limit if https listen limit wasn't defined
if s.TLSListenLimit == 0 {
s.TLSListenLimit = s.ListenLimit
}
// Use http tcp keep alive if https tcp keep alive wasn't defined
if int64(s.TLSKeepAlive) == 0 {
s.TLSKeepAlive = s.KeepAlive
}
// Use http read timeout if https read timeout wasn't defined
if int64(s.TLSReadTimeout) == 0 {
s.TLSReadTimeout = s.ReadTimeout
}
// Use http write timeout if https write timeout wasn't defined
if int64(s.TLSWriteTimeout) == 0 {
s.TLSWriteTimeout = s.WriteTimeout
}
}
if s.hasScheme(schemeUnix) {
domSockListener, err := net.Listen("unix", string(s.SocketPath))
if err != nil {
return err
}
s.domainSocketL = domSockListener
}
if s.hasScheme(schemeHTTP) {
listener, err := net.Listen("tcp", net.JoinHostPort(s.Host, strconv.Itoa(s.Port)))
if err != nil {
return err
}
h, p, err := swag.SplitHostPort(listener.Addr().String())
if err != nil {
return err
}
s.Host = h
s.Port = p
s.httpServerL = listener
}
if s.hasScheme(schemeHTTPS) {
tlsListener, err := net.Listen("tcp", net.JoinHostPort(s.TLSHost, strconv.Itoa(s.TLSPort)))
if err != nil {
return err
}
sh, sp, err := swag.SplitHostPort(tlsListener.Addr().String())
if err != nil {
return err
}
s.TLSHost = sh
s.TLSPort = sp
s.httpsServerL = tlsListener
}
s.hasListeners = true
return nil
}
// Shutdown server and clean up resources
func (s *Server) Shutdown() error {
if atomic.CompareAndSwapInt32(&s.shuttingDown, 0, 1) {
close(s.shutdown)
}
return nil
}
func (s *Server) handleShutdown(wg *sync.WaitGroup, serversPtr *[]*http.Server) {
// wg.Done must occur last, after s.api.ServerShutdown()
// (to preserve old behaviour)
defer wg.Done()
<-s.shutdown
servers := *serversPtr
ctx, cancel := context.WithTimeout(context.TODO(), s.GracefulTimeout)
defer cancel()
// first execute the pre-shutdown hook
s.api.PreServerShutdown()
shutdownChan := make(chan bool)
for i := range servers {
server := servers[i]
go func() {
var success bool
defer func() {
shutdownChan <- success
}()
if err := server.Shutdown(ctx); err != nil {
// Error from closing listeners, or context timeout:
s.Logf("HTTP server Shutdown: %v", err)
} else {
success = true
}
}()
}
// Wait until all listeners have successfully shut down before calling ServerShutdown
success := true
for range servers {
success = success && <-shutdownChan
}
if success {
s.api.ServerShutdown()
}
}
// GetHandler returns a handler useful for testing
func (s *Server) GetHandler() http.Handler {
return s.handler
}
// SetHandler allows for setting a http handler on this server
func (s *Server) SetHandler(handler http.Handler) {
s.handler = handler
}
// UnixListener returns the domain socket listener
func (s *Server) UnixListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.domainSocketL, nil
}
// HTTPListener returns the http listener
func (s *Server) HTTPListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpServerL, nil
}
// TLSListener returns the https listener
func (s *Server) TLSListener() (net.Listener, error) {
if !s.hasListeners {
if err := s.Listen(); err != nil {
return nil, err
}
}
return s.httpsServerL, nil
}
func handleInterrupt(once *sync.Once, s *Server) {
once.Do(func() {
for range s.interrupt {
if s.interrupted {
s.Logf("Server already shutting down")
continue
}
s.interrupted = true
s.Logf("Shutting down... ")
if err := s.Shutdown(); err != nil {
s.Logf("HTTP server Shutdown: %v", err)
}
}
})
}
func signalNotify(interrupt chan<- os.Signal) {
signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
}

View File

@@ -0,0 +1,65 @@
package rest
import (
"context"
"gerald/internal/adapters/config"
"gerald/internal/adapters/logger"
"gerald/internal/interfaces/rest/restapi"
"gerald/internal/interfaces/rest/restapi/operations"
"github.com/go-openapi/loads"
"github.com/rs/zerolog"
"go.uber.org/fx"
"net/http"
)
type Server struct {
config *config.Config
logger zerolog.Logger
}
func NewServer(
config *config.Config,
logger zerolog.Logger,
) *Server {
return &Server{
config: config,
logger: logger,
}
}
func (s *Server) SetupMiddleware(handler http.Handler) http.Handler {
return logger.HTTPLoggerFunc(s.logger, handler)
}
func StartHTTPServer(s *Server, lifecycle fx.Lifecycle) {
swaggerSpec, err := loads.Embedded(restapi.SwaggerJSON, restapi.FlatSwaggerJSON)
if err != nil {
return
}
api := operations.NewGeraldAPI(swaggerSpec)
api.UseSwaggerUI()
//
// Add global middleware here
server := restapi.NewServer(api)
handler := s.SetupMiddleware(api.Serve(nil))
server.SetHandler(handler)
server.Port = int(s.config.ApplicationPort)
go func() {
err = server.Serve()
if err != nil {
s.logger.Fatal().Err(err).Msg("Cannot start HTTP server")
}
}()
lifecycle.Append(fx.Hook{
OnStop: func(ctx context.Context) error {
return server.Shutdown()
},
})
}

View File

@@ -0,0 +1,22 @@
package models
import "github.com/google/uuid"
type Feedback struct {
UUID string `gorm:"primaryKey"`
UserUUID string `gorm:"type:varchar(255)"`
SessionUUID string `gorm:"type:varchar(255)"`
Type string `gorm:"type:varchar(255)"`
Text string `gorm:"type:varchar(300)"`
}
func NewFeedback(userUUID, sessionUUID, ticketType, text string) *Feedback {
return &Feedback{
UUID: uuid.New().String(),
UserUUID: userUUID,
SessionUUID: sessionUUID,
Type: ticketType,
Text: text,
}
}

View File

@@ -0,0 +1,25 @@
package services
import (
apiModels "gerald/internal/interfaces/rest/models"
"gerald/internal/models"
"gerald/repo/pg"
)
type FeedbackService struct {
db *pg.DB
}
func NewFeedbackService(db *pg.DB) *FeedbackService {
return &FeedbackService{
db: db,
}
}
func (f *FeedbackService) Create(sessionID string, feedback *apiModels.Feedback) error {
return f.db.Ticket.Create(models.NewFeedback(feedback.UserID, sessionID, feedback.Type, feedback.Text))
}
func (f *FeedbackService) GetBySessionID(sessionID string) ([]*models.Feedback, error) {
return f.db.Ticket.GetBySessionID(sessionID)
}

54
repo/pg/client.go Normal file
View File

@@ -0,0 +1,54 @@
package pg
import (
"gerald/internal/adapters/config"
"gerald/internal/models"
"github.com/rs/zerolog"
"gorm.io/driver/postgres"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
)
type DB struct {
Ticket *Feedback
logger zerolog.Logger
}
func NewDB(
db *gorm.DB,
logger zerolog.Logger,
) *DB {
return &DB{
Ticket: NewFeedback(db),
logger: logger,
}
}
func NewClient(cfg *config.Config, logger zerolog.Logger) *gorm.DB {
db, err := gorm.Open(postgres.Open(cfg.PostgresDSN), &gorm.Config{
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
})
if err != nil {
logger.Fatal().Err(err).Msg("Cannot connect to database")
}
return db
}
func Close(db *gorm.DB) error {
dbConn, _ := db.DB()
return dbConn.Close()
}
func CreateSchema(db *gorm.DB) error {
err := db.AutoMigrate(
&models.Feedback{},
)
if err != nil {
return err
}
return nil
}

33
repo/pg/feedback.go Normal file
View File

@@ -0,0 +1,33 @@
package pg
import (
"gerald/internal/models"
"gorm.io/gorm"
)
type Feedback struct {
db *gorm.DB
}
func NewFeedback(db *gorm.DB) *Feedback {
return &Feedback{
db: db,
}
}
func (t *Feedback) Create(ticket *models.Feedback) error {
return t.db.Create(ticket).Error
}
func (t *Feedback) GetBySessionID(sessionID string) ([]*models.Feedback, error) {
var tickets []*models.Feedback
res := t.db.
Where("session_uuid = ?", sessionID).
Find(&tickets)
if res.Error != nil {
return nil, res.Error
}
return tickets, nil
}