From 25f412bc0d0af3a3a6ffb99b14292c34afc708e5 Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Sun, 31 Jan 2021 14:01:46 +0400 Subject: [PATCH 01/15] fix --- .env | 11 + .gitignore | 2 +- .golangci.yml | 118 +++ Dockerfile | 24 + Makefile | 25 + README.md | 117 ++- app/configuration.go | 58 ++ app/main.go | 64 ++ business-logic/domain/entity.go | 22 + business-logic/entity/repository/postgres.go | 63 ++ .../entity/translator/google-translator.go | 29 + business-logic/entity/usecase.go | 53 ++ docker-compose.yml | 31 + entrypoints/http/api/v1/router.go | 53 ++ entrypoints/http/probe/router.go | 24 + entrypoints/schedule/scheduler.go | 40 + go.mod | 12 + go.sum | 699 ++++++++++++++++++ infrastructure/httpserver/http.go | 41 + infrastructure/img/example-http-db.png | Bin 0 -> 46819 bytes infrastructure/img/layers.png | Bin 0 -> 52626 bytes infrastructure/postgres/postgres.go | 59 ++ infrastructure/rabbitmq/rabbitmq.go | 1 + migrations/000001_migrate_name.down.sql | 1 + migrations/000001_migrate_name.up.sql | 5 + mocks/EntityRepository.go | 52 ++ mocks/EntityTranslator.go | 34 + mocks/EntityUseCase.go | 15 + 28 files changed, 1649 insertions(+), 4 deletions(-) create mode 100644 .env create mode 100644 .golangci.yml create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 app/configuration.go create mode 100644 app/main.go create mode 100644 business-logic/domain/entity.go create mode 100644 business-logic/entity/repository/postgres.go create mode 100644 business-logic/entity/translator/google-translator.go create mode 100644 business-logic/entity/usecase.go create mode 100644 docker-compose.yml create mode 100644 entrypoints/http/api/v1/router.go create mode 100644 entrypoints/http/probe/router.go create mode 100644 entrypoints/schedule/scheduler.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 infrastructure/httpserver/http.go create mode 100644 infrastructure/img/example-http-db.png create mode 100644 infrastructure/img/layers.png create mode 100644 infrastructure/postgres/postgres.go create mode 100644 infrastructure/rabbitmq/rabbitmq.go create mode 100644 migrations/000001_migrate_name.down.sql create mode 100644 migrations/000001_migrate_name.up.sql create mode 100644 mocks/EntityRepository.go create mode 100644 mocks/EntityTranslator.go create mode 100644 mocks/EntityUseCase.go diff --git a/.env b/.env new file mode 100644 index 00000000..b9296a4a --- /dev/null +++ b/.env @@ -0,0 +1,11 @@ +GIN_MODE = release + +HTTP_API_PORT = 8080 +HTTP_PROBE_PORT = 8081 + +PG_URL = postgres://user:pass@localhost:5432/postgres +PG_POOL_MAX = 1 +PG_CONN_ATTEMPTS = 10 + +RMQ_URL = amqp://guest:guest@localhost:5672/ +RMQ_QUEUE_NAME = test_queue \ No newline at end of file diff --git a/.gitignore b/.gitignore index e10deff0..f78c4224 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ /.idea/ -/bin/ +/.bin/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..3ddf6748 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,118 @@ +linters-settings: + gci: + local-prefixes: github.com/evrone/go-service-template + govet: + check-shadowing: true + misspell: + locale: US + gofumpt: + extra-rules: true + dupl: + threshold: 100 + errorlint: + errorf: true + funlen: + lines: 60 + statements: 40 + gocognit: + min-complexity: 10 + gocyclo: + min-complexity: 10 + goconst: + min-len: 2 + min-occurrences: 2 + gocritic: + enabled-tags: + - diagnostic + - experimental + - opinionated + - performance + - style + disabled-checks: + - dupImport # https://github.com/go-critic/go-critic/issues/845 + golint: + min-confidence: 0 + lll: + line-length: 120 + maligned: + suggest-new: true + nestif: + min-complexity: 4 + nolintlint: + allow-leading-space: true + allow-unused: false + require-explanation: false + require-specific: false + +linters: + disable-all: true + enable: + - godot + - gofumpt + - gci + - misspell + - whitespace + +# - asciicheck +# - bodyclose +# - deadcode +# - dogsled +# - dupl +# - errcheck +# - errorlint +# - exhaustive +# - exportloopref +# - funlen +# - gochecknoglobals +# - gochecknoinits +# - gocognit +# - goconst +# - gocritic +# - gocyclo +# - godox +# - goerr113 +# - golint +# - gomnd +# - goprintffuncname +# - gosec +# - gosimple +# - govet +# - ineffassign +# - interfacer +# - lll +# - maligned +# - nakedret +# - nestif +# - nlreturn +# - noctx +# - nolintlint +# - paralleltest +# - prealloc +# - rowserrcheck +# - scopelint +# - sqlclosecheck +# - staticcheck +# - structcheck +# - stylecheck +# - testpackage +# - tparallel +# - unconvert +# - unparam +# - unused +# - varcheck +# - wrapcheck +# - wsl + +issues: + exclude-rules: + - path: _test\.go + linters: + - gomnd + - funlen + +#run: +# skip-dirs: +# - test/testdata_etc + +# skip-files: +# - .*_test.go \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..ebd3abe0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +# Step 1: Modules +FROM golang:1.15.7-alpine3.13 as modules +COPY go.mod go.sum /modules/ +RUN cd /modules && go mod download + + +# Step 2: Builder +FROM golang:1.15.7-alpine3.13 as builder +RUN apk add --no-cache ca-certificates + +COPY --from=modules /go/pkg /go/pkg + +COPY . /app +WORKDIR /app + +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -o /bin/app ./app + + +# Step 3: Final +FROM scratch +COPY --from=builder /bin/app /app +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ +CMD ["/app"] diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..800eea25 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +include .env +export + +run: + go mod download && GIN_MODE=debug CGO_ENABLED=0 go run ./app + +up: + docker-compose up --build -d --remove-orphans && docker-compose logs -f + +down: + docker-compose down --remove-orphans + +test: + go test -v -cover -race ./... + +mock: + mockery --all + +migrate: + migrate -path migrations -database '$(PG_URL)?sslmode=disable' up + +create: + migrate create -ext sql -dir migrations -seq 'migrate_name' + +.PHONY: run, up, down, test, mock, migrate, create \ No newline at end of file diff --git a/README.md b/README.md index 2d59fb19..5624e470 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,115 @@ -Readme ------------------------- +Шаблон микросервиса на Go +=========================================== +Цель: +1. Предоставить готовый шаблон для быстрого развертывания +2. Стандартизировать подходы к организации кода +3. Аккумулировать лучшие практики -Readme +С развитием любого проекта, логика работы приложения усложняется и запутывается. +Разработка новых фич замедляется. Если не планировать архитектуру, довольно быстро +приходит время, когда переписать сервис становится дешевле, чем делать изменения в +существующем. + + + +Clean Architecture +---- +### Главный принцип +Dependency Inversion (тот самый из SOLID) — принцип инверсии зависимостей, играет +ключевую роль в построении архитектуры приложения. Чтобы принцип начал работать, +нам нужно поделить приложение на слои. + +![Clean Architecture](infrastructure/img/layers.png) + +Итак, приложение делится на 2 слоя, внутренний и внешний: +1. **Бизнес-логика** (стандартная библиотека Go). +2. **Инструменты** (база данных, HTTP сервер, брокер сообщений, любые другие пакеты + и фреймворки). + +**Внутренний** слой с бизнес-логикой должен быть чистым, то есть: +* Не иметь в себе импортов пакетов из внешнего слоя. +* Не иметь в себе импортов любых других пакетов, кроме стандартной библиотеки. +* Делать обращения к внешнему слою через интерфейс(!). + +Бизнес-логика ничего не знает о Postgres или RabbitMQ. Бизнес-логика знает +интерфейс для работы с _абстрактной_ базой данных, _абстрактным_ брокером +сообщений или _абстрактным_ HTTP сервером. + +**Внешний** слой имеет другие ограничения: +* Все компоненты этого слоя не знают о существовании друг друга. +* Как из одного инструмента вызвать другой? Напрямую никак, только через + внутренний слой логики. +* Обращения к внутреннему слою происходят через интерфейс(!). +* Данные передаются в том формате, который удобен для бизнес-логики. + +**Например**, вам нужно обратиться из http хендлера к базе данных. И http, и +БД находятся во внешнем слое, значит они ничего не знают друг о друге. Связь +между ними осуществляется через бизнес-логику: +``` + Хендлер HTTP > Бизнес-логика + Бизнес-логика > База данных + Бизнес-логика < База данных + Хендлер HTTP < Бизнес-логика +``` +Стрелочками > и < показано пересечение границ слоев с помощью Интерфейсов. + +То же самое на картинке: + +![Example](infrastructure/img/example-http-db.png) + +### Терминология по слоям +Во внутреннем слое: +* **Entities** — объекты, которыми оперирует бизнес-логика. В коде всегда называются +по именам принятым в организации. +* **Use Cases** — бизнес-логика, манипулирующая внешним слоем через интерфейсы. + +Во внешнем слое просто перечень пакетов, которые реализуют интерфейсы (чтобы +бизнес-логика могла к ним обратится): + * Postgres + * HTTP + * RabbitMQ + * WebAPI + * Framework + * Любой другой package + +### Дополнительные слои +На самом здесь представлен упрощённый, адаптированный под микросервисы, вариант +Clean Architecture. Классический вариант [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) +разрабатывался для построения больших монолитных приложений и имеет 4 слоя абстракций. + +То есть, в оригинальной версии, внешний слой делится ещё на два, которые +так же имеют обратную инверсию зависимостей друг к другу (направленную во внутрь) +и общаются через интерфейсы. + +Внутренний слой так же делится на два (с разделением интерфейсов), в случае +сложной логики. +_______________________________ + +В нашей схеме мы оставляем во внутреннем круге 2 слоя (entity и usecase), потому +что это совпадает со структурой папок проекта. Но это очень тонкий слой, без +интерфейсов. + +Сложные инструменты из внешнего слоя рекомендуется делить на дополнительные +слои абстракции. Мы призываем руководствоваться здравым смыслом и добавлять слои +лишь в том случае, если это действительно необходимо. + +### Альтернативные подходы +Кроме Чистой архитектуры есть очень близкие по духу — Луковая архитектура и +Гексагональная (Порты и адаптеры). В основе всех подходов лежит базовый принцип +инверсии зависимостей. Все подходы преследуют цель уменьшить зацепление и +разграничить ответственность. + +Test Driven Design +------------------ +Domain Driven Design +-------------------- +12-factor app +------------- +Dependency Injection +-------------------- + +Полезные ссылки +--------------- +* [The Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) +* [Книга Чистая архитектура](https://www.ozon.ru/context/detail/id/144499396/) +* [12 факторов](https://12factor.net/ru/) diff --git a/app/configuration.go b/app/configuration.go new file mode 100644 index 00000000..c8432262 --- /dev/null +++ b/app/configuration.go @@ -0,0 +1,58 @@ +package main + +import ( + "log" + "os" + "strconv" +) + +type Conf struct { + HttpApiPort string + HttpProbePort string + PgURL string + PgPoolMax int + PgConnAttempts int + RmqURL string + RmqQueueName string +} + +func NewConfig() Conf { + return Conf{ + HttpApiPort: strEnv("HTTP_API_PORT"), + HttpProbePort: strEnv("HTTP_PROBE_PORT"), + PgURL: strEnv("PG_URL"), + PgPoolMax: intEnv("PG_POOL_MAX"), + PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), + RmqURL: strEnv("RMQ_URL"), + RmqQueueName: strEnv("RMQ_QUEUE_NAME"), + } +} + +func strEnv(env string) string { + value, ok := os.LookupEnv(env) + if !ok || len(value) == 0 { + log.Fatalf("environment variable not declared: %s", env) + } + + log.Println(env, "=", value) + + return value +} + +func intEnv(env string) int { + var intValue int + + value, ok := os.LookupEnv(env) + if !ok || len(value) == 0 { + log.Fatalf("environment variable not declared: %s", env) + } + + intValue, err := strconv.Atoi(value) + if err != nil { + log.Fatalf("typecast error to integer: %s", err) + } + + log.Println(env, "=", intValue) + + return intValue +} diff --git a/app/main.go b/app/main.go new file mode 100644 index 00000000..0ffa8bed --- /dev/null +++ b/app/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "log" + "os" + "os/signal" + "syscall" + + "github.com/evrone/go-service-template/business-logic/entity" + "github.com/evrone/go-service-template/business-logic/entity/repository" + "github.com/evrone/go-service-template/business-logic/entity/translator" + "github.com/evrone/go-service-template/entrypoints/http/api/v1" + "github.com/evrone/go-service-template/infrastructure/postgres" + + "github.com/evrone/go-service-template/infrastructure/httpserver" + + "github.com/evrone/go-service-template/entrypoints/http/probe" + + _ "github.com/golang-migrate/migrate/v4/database/postgres" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +func main() { + conf := NewConfig() + pg := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) + pgRepository := repository.NewPostgresEntityRepository(pg) + + translateApi := translator.NewGoogleTranslator() + + entityUseCase := entity.NewUseCase(pgRepository, translateApi) + + apiRouter := v1.NewApiRouter(entityUseCase) + apiServer := httpserver.NewServer(apiRouter, conf.HttpApiPort) + apiServer.Start() + + probeRouter := probe.NewHealthRouter() + probeServer := httpserver.NewServer(probeRouter, conf.HttpProbePort) + probeServer.Start() + + // Graceful shutdown + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) + + select { + case s := <-interrupt: + log.Println("main - Interrupt signal", s.String()) // TODO + case err := <-probeServer.Notify(): + log.Println("main - probeServer.Notify error", err.Error()) // TODO + case err := <-apiServer.Notify(): + log.Println("main - apiServer.Notify error", err.Error()) // TODO + } + + err := probeServer.Stop() + if err != nil { + log.Println("main - probeServer.Stop error") // TODO + } + + err = apiServer.Stop() + if err != nil { + log.Println("main - apiServer.Stop error") // TODO + } + + pg.Close() +} diff --git a/business-logic/domain/entity.go b/business-logic/domain/entity.go new file mode 100644 index 00000000..3e6fc8f3 --- /dev/null +++ b/business-logic/domain/entity.go @@ -0,0 +1,22 @@ +package domain + +import "context" + +type Entity struct { + Original string `json:"original" binding:"required"` + Translation string `json:"translation"` +} + +type EntityUseCase interface { + DoTranslate(entity Entity) (Entity, error) + History() ([]Entity, error) +} + +type EntityTranslator interface { + Translate(entity Entity) (Entity, error) +} + +type EntityRepository interface { + Store(ctx context.Context, entity Entity) error + GetHistory(context.Context) ([]Entity, error) +} diff --git a/business-logic/entity/repository/postgres.go b/business-logic/entity/repository/postgres.go new file mode 100644 index 00000000..fd925d32 --- /dev/null +++ b/business-logic/entity/repository/postgres.go @@ -0,0 +1,63 @@ +package repository + +import ( + "context" + + "github.com/evrone/go-service-template/infrastructure/postgres" + + "github.com/evrone/go-service-template/business-logic/domain" +) + +type postgresEntityRepository struct { + postgres.Postgres +} + +func NewPostgresEntityRepository(pg postgres.Postgres) domain.EntityRepository { + return &postgresEntityRepository{pg} +} + +func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Entity, error) { + sql, _, err := p.Builder. + Select("original, translation"). + From("history"). + ToSql() + if err != nil { + return nil, err + } + + rows, err := p.Pool.Query(ctx, sql) + if err != nil { + return nil, err + } + defer rows.Close() + + entities := make([]domain.Entity, 0, 64) + for rows.Next() { + e := domain.Entity{} + err = rows.Scan(&e.Original, &e.Translation) + if err != nil { + return nil, err + } + entities = append(entities, e) + } + + return entities, nil +} + +func (p *postgresEntityRepository) Store(ctx context.Context, entity domain.Entity) error { + sql, args, err := p.Builder. + Insert("history"). + Columns("original, translation"). + Values(entity.Original, entity.Translation). + ToSql() + if err != nil { + return err + } + + _, err = p.Pool.Exec(ctx, sql, args...) + if err != nil { + return err + } + + return nil +} diff --git a/business-logic/entity/translator/google-translator.go b/business-logic/entity/translator/google-translator.go new file mode 100644 index 00000000..0998d97f --- /dev/null +++ b/business-logic/entity/translator/google-translator.go @@ -0,0 +1,29 @@ +package translator + +import ( + translator "github.com/Conight/go-googletrans" + + "github.com/evrone/go-service-template/business-logic/domain" +) + +type GoogleTranslator struct{} + +func NewGoogleTranslator() *GoogleTranslator { + return &GoogleTranslator{} +} + +func (y *GoogleTranslator) Translate(entity domain.Entity) (domain.Entity, error) { + c := translator.Config{ + UserAgent: []string{"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"}, + ServiceUrls: []string{"translate.google.as"}, + } + t := translator.New(c) + result, err := t.Translate(entity.Original, "ru", "en") + if err != nil { + return domain.Entity{}, err + } + + entity.Translation = result.Text + + return entity, nil +} diff --git a/business-logic/entity/usecase.go b/business-logic/entity/usecase.go new file mode 100644 index 00000000..f3365c58 --- /dev/null +++ b/business-logic/entity/usecase.go @@ -0,0 +1,53 @@ +package entity + +import ( + "context" + "log" + + "github.com/evrone/go-service-template/business-logic/domain" +) + +type useCase struct { + repository domain.EntityRepository + translator domain.EntityTranslator +} + +func NewUseCase(repo domain.EntityRepository, api domain.EntityTranslator) domain.EntityUseCase { + return &useCase{repo, api} +} + +func (u *useCase) History() ([]domain.Entity, error) { + entities, err := u.repository.GetHistory(context.Background()) + if err != nil { + log.Print(err) + return nil, err + } + + return entities, nil +} + +func (u *useCase) DoTranslate(entity domain.Entity) (domain.Entity, error) { + entity, err := u.translator.Translate(entity) + if err != nil { + log.Print(err) + return domain.Entity{}, err + } + + err = u.repository.Store(context.Background(), entity) + if err != nil { + log.Print(err) + return domain.Entity{}, err + } + + //keys, err := u.repository.GetHistory(context.Background()) + //if err != nil { + // log.Print(err) + // return + //} + // + //for _, key := range keys { + // fmt.Println(key.Original, key.Translation) + //} + + return entity, nil +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..c3bff835 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3.9' +services: + +# app: +# build: . +# container_name: app +# image: app +# env_file: +# - ./.env +# environment: +# PG_URL: 'postgres://user:pass@db:5432/postgres' +# ports: +# - 8080:8080 # api +# - 8081:8081 # K8s probe +# depends_on: +# - db + + db: + container_name: postgres + image: postgres + volumes: + - pg-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: 'user' + POSTGRES_PASSWORD: 'pass' + POSTGRES_DB: 'postgres' + ports: + - 5432:5432 + +volumes: + pg-data: \ No newline at end of file diff --git a/entrypoints/http/api/v1/router.go b/entrypoints/http/api/v1/router.go new file mode 100644 index 00000000..fb9be2ee --- /dev/null +++ b/entrypoints/http/api/v1/router.go @@ -0,0 +1,53 @@ +package v1 + +import ( + "net/http" + + "github.com/evrone/go-service-template/business-logic/domain" + + "github.com/gin-gonic/gin" +) + +type apiRouter struct { + *gin.Engine + useCase domain.EntityUseCase +} + +func NewApiRouter(uc domain.EntityUseCase) http.Handler { + router := gin.Default() + api := &apiRouter{router, uc} + + v1 := router.Group("/api/v1") + { + v1.GET("/history", api.history) + v1.POST("/do-translate", api.doTranslate) + } + + return api +} + +func (a *apiRouter) history(c *gin.Context) { + entities, err := a.useCase.History() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, entities) +} + +func (a *apiRouter) doTranslate(c *gin.Context) { + var entity domain.Entity + if err := c.ShouldBindJSON(&entity); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + entity, err := a.useCase.DoTranslate(entity) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, entity) +} diff --git a/entrypoints/http/probe/router.go b/entrypoints/http/probe/router.go new file mode 100644 index 00000000..44ccfb31 --- /dev/null +++ b/entrypoints/http/probe/router.go @@ -0,0 +1,24 @@ +package probe + +import ( + "log" + "net/http" +) + +type healthRouter struct { + *http.ServeMux +} + +func NewHealthRouter() http.Handler { + mux := http.NewServeMux() + r := &healthRouter{mux} + + r.HandleFunc("/healthz", r.liveness) + + return r +} + +func (h *healthRouter) liveness(w http.ResponseWriter, r *http.Request) { + log.Println("It's alive!") + w.WriteHeader(http.StatusOK) +} diff --git a/entrypoints/schedule/scheduler.go b/entrypoints/schedule/scheduler.go new file mode 100644 index 00000000..eab2fe6b --- /dev/null +++ b/entrypoints/schedule/scheduler.go @@ -0,0 +1,40 @@ +package schedule + +import ( + "time" + + "github.com/evrone/go-service-template/business-logic/domain" +) + +type Scheduler struct { + useCase domain.EntityUseCase + interval time.Duration + stopSignal chan struct{} +} + +func NewScheduler(uc domain.EntityUseCase, seconds int) *Scheduler { + return &Scheduler{ + useCase: uc, + interval: time.Duration(seconds) * time.Second, + stopSignal: make(chan struct{}, 1), + } +} + +func (s *Scheduler) Start() { + go func() { + LOOP: + for range time.Tick(s.interval) { + select { + case <-s.stopSignal: + break LOOP + default: + } + + s.useCase.DoTranslate() + } + }() +} + +func (s *Scheduler) Stop() { + close(s.stopSignal) +} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..362b37bd --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/evrone/go-service-template + +go 1.15 + +require ( + github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb + github.com/Masterminds/squirrel v1.5.0 + github.com/gin-gonic/gin v1.6.3 + github.com/golang-migrate/migrate/v4 v4.14.1 + github.com/jackc/pgx/v4 v4.10.1 + github.com/stretchr/testify v1.7.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..e9168b4f --- /dev/null +++ b/go.sum @@ -0,0 +1,699 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg= +cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/spanner v1.9.0/go.mod h1:xvlEn0NZ5v1iJPYsBnUVRDNvccDxsBTEi16pJRKQVws= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= +github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb h1:MwdqXipO1SFnK2k1iIL8xlUkZC63KdZR6QB/DSIlYBk= +github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb/go.mod h1:J9k1GS4/KUFdJgWCp8Od4J3XXalm3pUPm8YYrMFzYTk= +github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= +github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= +github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +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/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dhui/dktest v0.3.3 h1:DBuH/9GFaWbDRa42qsut/hbQu+srAQ0rPWnUoiGX7CA= +github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8= +github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= +github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE= +github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.3.2/go.mod h1:LvCquS3HbBKwgl7KbX9KyqEIumJAbm1UMcTvGaIf3bM= +github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= +github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= +github.com/jackc/pgconn v1.8.0 h1:FmjZ0rOyXTr1wfWs45i4a9vjnjWUAGpMuQLD9OSs+lw= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +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/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0= +github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po= +github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ= +github.com/jackc/pgtype v1.6.2 h1:b3pDeuhbbzBYcg5kwNmNDun4pFUD/0AAr1kLXZLeNt8= +github.com/jackc/pgtype v1.6.2/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA= +github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o= +github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg= +github.com/jackc/pgx/v4 v4.10.1 h1:/6Q3ye4myIj6AaplUm+eRcz4OhK9HAvFf4ePsG40LJY= +github.com/jackc/pgx/v4 v4.10.1/go.mod h1:QlrWebbs3kqEZPHCTGyxecvzG6tvIsYu+A5b1raylkA= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +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/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= +github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= +github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= +github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= +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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= +github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0= +github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= +go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM= +golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200818005847-188abfa75333/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3 h1:sg8vLDNIxFPHTchfhH1E3AI32BL3f23oie38xUWnJM8= +google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= +modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= +modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= +modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= +modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= +modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= +modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/infrastructure/httpserver/http.go b/infrastructure/httpserver/http.go new file mode 100644 index 00000000..43370f71 --- /dev/null +++ b/infrastructure/httpserver/http.go @@ -0,0 +1,41 @@ +package httpserver + +import ( + "context" + "net" + "net/http" + "time" +) + +type Server struct { + server http.Server + errors chan error +} + +func NewServer(handler http.Handler, port string) *Server { + return &Server{ + server: http.Server{ + Addr: net.JoinHostPort("", port), + Handler: handler, + }, + errors: make(chan error, 1), + } +} + +func (s *Server) Start() { + go func() { + s.errors <- s.server.ListenAndServe() + close(s.errors) + }() +} + +func (s *Server) Notify() <-chan error { + return s.errors +} + +func (s *Server) Stop() error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + return s.server.Shutdown(ctx) +} diff --git a/infrastructure/img/example-http-db.png b/infrastructure/img/example-http-db.png new file mode 100644 index 0000000000000000000000000000000000000000..15e314f7803f2c21630e17569ce94e0f41b24f80 GIT binary patch literal 46819 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 QEQKJDq^O(-5W@=kKL^)&>i_@% literal 0 HcmV?d00001 diff --git a/infrastructure/img/layers.png b/infrastructure/img/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..69117ec80e01c8b28c7835bf8333d59565b73bed GIT binary patch literal 52626 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

    FD8{5$tw2Zt=sQx9j@(O`lgFY@=^cyO8gp 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 0 { + pool, errConn = pgxpool.ConnectConfig(context.Background(), poolConfig) + if errConn == nil { + break + } + + log.Printf("postgres is trying to connect, attempts left: %d", connAttempts) + + time.Sleep(time.Second) + + connAttempts-- + } + + if errConn != nil { + log.Fatalf("postgres connect error: %s", errConn) + } + + log.Print("postgres connected") + + builder := squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + + return Postgres{ + Pool: pool, + Builder: builder, + } +} + +func (p Postgres) Close() { + if p.Pool != nil { + p.Pool.Close() + } +} diff --git a/infrastructure/rabbitmq/rabbitmq.go b/infrastructure/rabbitmq/rabbitmq.go new file mode 100644 index 00000000..a4e247e1 --- /dev/null +++ b/infrastructure/rabbitmq/rabbitmq.go @@ -0,0 +1 @@ +package rabbitmq diff --git a/migrations/000001_migrate_name.down.sql b/migrations/000001_migrate_name.down.sql new file mode 100644 index 00000000..7f14ef9f --- /dev/null +++ b/migrations/000001_migrate_name.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS history; \ No newline at end of file diff --git a/migrations/000001_migrate_name.up.sql b/migrations/000001_migrate_name.up.sql new file mode 100644 index 00000000..886fc411 --- /dev/null +++ b/migrations/000001_migrate_name.up.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS history( + id serial PRIMARY KEY, + original VARCHAR(255), + translation VARCHAR(255) +); \ No newline at end of file diff --git a/mocks/EntityRepository.go b/mocks/EntityRepository.go new file mode 100644 index 00000000..2e1eb101 --- /dev/null +++ b/mocks/EntityRepository.go @@ -0,0 +1,52 @@ +// Code generated by mockery v2.5.1. DO NOT EDIT. + +package mocks + +import ( + context "context" + + domain "github.com/evrone/go-service-template/business-logic/domain" + mock "github.com/stretchr/testify/mock" +) + +// EntityRepository is an autogenerated mock type for the EntityRepository type +type EntityRepository struct { + mock.Mock +} + +// GetHistory provides a mock function with given fields: _a0 +func (_m *EntityRepository) GetHistory(_a0 context.Context) ([]domain.Entity, error) { + ret := _m.Called(_a0) + + var r0 []domain.Entity + if rf, ok := ret.Get(0).(func(context.Context) []domain.Entity); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]domain.Entity) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store provides a mock function with given fields: _a0, _a1 +func (_m *EntityRepository) Store(_a0 context.Context, _a1 domain.Entity) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, domain.Entity) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/mocks/EntityTranslator.go b/mocks/EntityTranslator.go new file mode 100644 index 00000000..a7a3ef92 --- /dev/null +++ b/mocks/EntityTranslator.go @@ -0,0 +1,34 @@ +// Code generated by mockery v2.5.1. DO NOT EDIT. + +package mocks + +import ( + domain "github.com/evrone/go-service-template/business-logic/domain" + mock "github.com/stretchr/testify/mock" +) + +// EntityTranslator is an autogenerated mock type for the EntityTranslator type +type EntityTranslator struct { + mock.Mock +} + +// Translate provides a mock function with given fields: _a0 +func (_m *EntityTranslator) Translate(_a0 domain.Entity) (domain.Entity, error) { + ret := _m.Called(_a0) + + var r0 domain.Entity + if rf, ok := ret.Get(0).(func(domain.Entity) domain.Entity); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(domain.Entity) + } + + var r1 error + if rf, ok := ret.Get(1).(func(domain.Entity) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/mocks/EntityUseCase.go b/mocks/EntityUseCase.go new file mode 100644 index 00000000..c6fe9e85 --- /dev/null +++ b/mocks/EntityUseCase.go @@ -0,0 +1,15 @@ +// Code generated by mockery v2.5.1. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// EntityUseCase is an autogenerated mock type for the EntityUseCase type +type EntityUseCase struct { + mock.Mock +} + +// DoTranslate provides a mock function with given fields: +func (_m *EntityUseCase) DoTranslate() { + _m.Called() +} From 70ff8a3f0d1868315fb43ea9ea1b945bf0a56e17 Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Wed, 3 Feb 2021 02:26:24 +0400 Subject: [PATCH 02/15] Logger --- .env | 20 +- .gitignore | 1 - Dockerfile | 2 +- Makefile | 6 +- README.md | 4 +- app/configuration.go | 58 --- app/main.go | 64 --- business-logic/entity/usecase.go | 53 --- cmd/app/main.go | 9 + go.mod | 13 +- go.sum | 435 +----------------- internal/app/app.go | 70 +++ internal/app/configuration.go | 64 +++ .../business-logic}/domain/entity.go | 4 +- internal/business-logic/domain/logger.go | 16 + .../entity/repository/postgres.go | 4 +- .../entity/translator/google-translator.go | 2 +- internal/business-logic/entity/usecase.go | 48 ++ .../entrypoints}/http/api/v1/router.go | 2 +- .../entrypoints}/http/probe/router.go | 2 - .../entrypoints}/schedule/scheduler.go | 4 +- mocks/EntityUseCase.go | 15 - ...tityRepository.go => entity_repository.go} | 10 +- ...tityTranslator.go => entity_translator.go} | 12 +- mocks/entity_use_case.go | 57 +++ {infrastructure => pkg}/httpserver/http.go | 0 .../img/example-http-db.png | Bin {infrastructure => pkg}/img/layers.png | Bin pkg/logger/logger.go | 97 ++++ pkg/logger/rollbar.go | 16 + pkg/logger/zap.go | 38 ++ {infrastructure => pkg}/postgres/postgres.go | 16 +- {infrastructure => pkg}/rabbitmq/rabbitmq.go | 0 33 files changed, 485 insertions(+), 657 deletions(-) delete mode 100644 app/configuration.go delete mode 100644 app/main.go delete mode 100644 business-logic/entity/usecase.go create mode 100644 cmd/app/main.go create mode 100644 internal/app/app.go create mode 100644 internal/app/configuration.go rename {business-logic => internal/business-logic}/domain/entity.go (95%) create mode 100644 internal/business-logic/domain/logger.go rename {business-logic => internal/business-logic}/entity/repository/postgres.go (89%) rename {business-logic => internal/business-logic}/entity/translator/google-translator.go (89%) create mode 100644 internal/business-logic/entity/usecase.go rename {entrypoints => internal/entrypoints}/http/api/v1/router.go (93%) rename {entrypoints => internal/entrypoints}/http/probe/router.go (90%) rename {entrypoints => internal/entrypoints}/schedule/scheduler.go (84%) delete mode 100644 mocks/EntityUseCase.go rename mocks/{EntityRepository.go => entity_repository.go} (76%) rename mocks/{EntityTranslator.go => entity_translator.go} (63%) create mode 100644 mocks/entity_use_case.go rename {infrastructure => pkg}/httpserver/http.go (100%) rename {infrastructure => pkg}/img/example-http-db.png (100%) rename {infrastructure => pkg}/img/layers.png (100%) create mode 100644 pkg/logger/logger.go create mode 100644 pkg/logger/rollbar.go create mode 100644 pkg/logger/zap.go rename {infrastructure => pkg}/postgres/postgres.go (68%) rename {infrastructure => pkg}/rabbitmq/rabbitmq.go (100%) diff --git a/.env b/.env index b9296a4a..1dc0700b 100644 --- a/.env +++ b/.env @@ -1,11 +1,15 @@ -GIN_MODE = release +GIN_MODE = release -HTTP_API_PORT = 8080 -HTTP_PROBE_PORT = 8081 +ZAP_LOG_LEVEL = debug +ROLLBAR_ACCESS_TOKEN = 97c783270fd14bf6a3c76ab524ee8839 +ROLLBAR_ENVIRONMENT = go-template-test -PG_URL = postgres://user:pass@localhost:5432/postgres -PG_POOL_MAX = 1 -PG_CONN_ATTEMPTS = 10 +HTTP_API_PORT = 8080 +HTTP_PROBE_PORT = 8081 -RMQ_URL = amqp://guest:guest@localhost:5672/ -RMQ_QUEUE_NAME = test_queue \ No newline at end of file +PG_URL = postgres://user:pass@localhost:5432/postgres +PG_POOL_MAX = 1 +PG_CONN_ATTEMPTS = 5 + +RMQ_URL = amqp://guest:guest@localhost:5672/ +RMQ_QUEUE_NAME = test_queue \ No newline at end of file diff --git a/.gitignore b/.gitignore index f78c4224..85e7c1df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /.idea/ -/.bin/ diff --git a/Dockerfile b/Dockerfile index ebd3abe0..9d73fc88 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ COPY . /app WORKDIR /app RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -o /bin/app ./app + go build -o /bin/app ./cmd/app # Step 3: Final diff --git a/Makefile b/Makefile index 800eea25..6dcc0f47 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ include .env export run: - go mod download && GIN_MODE=debug CGO_ENABLED=0 go run ./app + go mod download && GIN_MODE=debug CGO_ENABLED=0 go run ./cmd/app up: docker-compose up --build -d --remove-orphans && docker-compose logs -f @@ -14,12 +14,12 @@ test: go test -v -cover -race ./... mock: - mockery --all + mockery --all -r --case snake migrate: migrate -path migrations -database '$(PG_URL)?sslmode=disable' up create: - migrate create -ext sql -dir migrations -seq 'migrate_name' + migrate create -ext sql -dir migrations 'migrate_name' .PHONY: run, up, down, test, mock, migrate, create \ No newline at end of file diff --git a/README.md b/README.md index 5624e470..1453c14f 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Dependency Inversion (тот самый из SOLID) — принцип инве ключевую роль в построении архитектуры приложения. Чтобы принцип начал работать, нам нужно поделить приложение на слои. -![Clean Architecture](infrastructure/img/layers.png) +![Clean Architecture](pkg/img/layers.png) Итак, приложение делится на 2 слоя, внутренний и внешний: 1. **Бизнес-логика** (стандартная библиотека Go). @@ -55,7 +55,7 @@ Dependency Inversion (тот самый из SOLID) — принцип инве То же самое на картинке: -![Example](infrastructure/img/example-http-db.png) +![Example](pkg/img/example-http-db.png) ### Терминология по слоям Во внутреннем слое: diff --git a/app/configuration.go b/app/configuration.go deleted file mode 100644 index c8432262..00000000 --- a/app/configuration.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "log" - "os" - "strconv" -) - -type Conf struct { - HttpApiPort string - HttpProbePort string - PgURL string - PgPoolMax int - PgConnAttempts int - RmqURL string - RmqQueueName string -} - -func NewConfig() Conf { - return Conf{ - HttpApiPort: strEnv("HTTP_API_PORT"), - HttpProbePort: strEnv("HTTP_PROBE_PORT"), - PgURL: strEnv("PG_URL"), - PgPoolMax: intEnv("PG_POOL_MAX"), - PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), - RmqURL: strEnv("RMQ_URL"), - RmqQueueName: strEnv("RMQ_QUEUE_NAME"), - } -} - -func strEnv(env string) string { - value, ok := os.LookupEnv(env) - if !ok || len(value) == 0 { - log.Fatalf("environment variable not declared: %s", env) - } - - log.Println(env, "=", value) - - return value -} - -func intEnv(env string) int { - var intValue int - - value, ok := os.LookupEnv(env) - if !ok || len(value) == 0 { - log.Fatalf("environment variable not declared: %s", env) - } - - intValue, err := strconv.Atoi(value) - if err != nil { - log.Fatalf("typecast error to integer: %s", err) - } - - log.Println(env, "=", intValue) - - return intValue -} diff --git a/app/main.go b/app/main.go deleted file mode 100644 index 0ffa8bed..00000000 --- a/app/main.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "log" - "os" - "os/signal" - "syscall" - - "github.com/evrone/go-service-template/business-logic/entity" - "github.com/evrone/go-service-template/business-logic/entity/repository" - "github.com/evrone/go-service-template/business-logic/entity/translator" - "github.com/evrone/go-service-template/entrypoints/http/api/v1" - "github.com/evrone/go-service-template/infrastructure/postgres" - - "github.com/evrone/go-service-template/infrastructure/httpserver" - - "github.com/evrone/go-service-template/entrypoints/http/probe" - - _ "github.com/golang-migrate/migrate/v4/database/postgres" - _ "github.com/golang-migrate/migrate/v4/source/file" -) - -func main() { - conf := NewConfig() - pg := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) - pgRepository := repository.NewPostgresEntityRepository(pg) - - translateApi := translator.NewGoogleTranslator() - - entityUseCase := entity.NewUseCase(pgRepository, translateApi) - - apiRouter := v1.NewApiRouter(entityUseCase) - apiServer := httpserver.NewServer(apiRouter, conf.HttpApiPort) - apiServer.Start() - - probeRouter := probe.NewHealthRouter() - probeServer := httpserver.NewServer(probeRouter, conf.HttpProbePort) - probeServer.Start() - - // Graceful shutdown - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) - - select { - case s := <-interrupt: - log.Println("main - Interrupt signal", s.String()) // TODO - case err := <-probeServer.Notify(): - log.Println("main - probeServer.Notify error", err.Error()) // TODO - case err := <-apiServer.Notify(): - log.Println("main - apiServer.Notify error", err.Error()) // TODO - } - - err := probeServer.Stop() - if err != nil { - log.Println("main - probeServer.Stop error") // TODO - } - - err = apiServer.Stop() - if err != nil { - log.Println("main - apiServer.Stop error") // TODO - } - - pg.Close() -} diff --git a/business-logic/entity/usecase.go b/business-logic/entity/usecase.go deleted file mode 100644 index f3365c58..00000000 --- a/business-logic/entity/usecase.go +++ /dev/null @@ -1,53 +0,0 @@ -package entity - -import ( - "context" - "log" - - "github.com/evrone/go-service-template/business-logic/domain" -) - -type useCase struct { - repository domain.EntityRepository - translator domain.EntityTranslator -} - -func NewUseCase(repo domain.EntityRepository, api domain.EntityTranslator) domain.EntityUseCase { - return &useCase{repo, api} -} - -func (u *useCase) History() ([]domain.Entity, error) { - entities, err := u.repository.GetHistory(context.Background()) - if err != nil { - log.Print(err) - return nil, err - } - - return entities, nil -} - -func (u *useCase) DoTranslate(entity domain.Entity) (domain.Entity, error) { - entity, err := u.translator.Translate(entity) - if err != nil { - log.Print(err) - return domain.Entity{}, err - } - - err = u.repository.Store(context.Background(), entity) - if err != nil { - log.Print(err) - return domain.Entity{}, err - } - - //keys, err := u.repository.GetHistory(context.Background()) - //if err != nil { - // log.Print(err) - // return - //} - // - //for _, key := range keys { - // fmt.Println(key.Original, key.Translation) - //} - - return entity, nil -} diff --git a/cmd/app/main.go b/cmd/app/main.go new file mode 100644 index 00000000..d3a39ce7 --- /dev/null +++ b/cmd/app/main.go @@ -0,0 +1,9 @@ +package main + +import ( + "github.com/evrone/go-service-template/internal/app" +) + +func main() { + app.Run() +} diff --git a/go.mod b/go.mod index 362b37bd..56b2424c 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,16 @@ require ( github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb github.com/Masterminds/squirrel v1.5.0 github.com/gin-gonic/gin v1.6.3 - github.com/golang-migrate/migrate/v4 v4.14.1 + github.com/golang/protobuf v1.4.3 // indirect + github.com/google/go-cmp v0.5.1 // indirect github.com/jackc/pgx/v4 v4.10.1 - github.com/stretchr/testify v1.7.0 // indirect + github.com/lib/pq v1.8.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rollbar/rollbar-go v1.2.0 + github.com/stretchr/testify v1.7.0 + go.uber.org/zap v1.10.0 + golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect + golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + google.golang.org/protobuf v1.25.0 // indirect ) diff --git a/go.sum b/go.sum index e9168b4f..538624cb 100644 --- a/go.sum +++ b/go.sum @@ -1,102 +1,27 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg= -cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/spanner v1.9.0/go.mod h1:xvlEn0NZ5v1iJPYsBnUVRDNvccDxsBTEi16pJRKQVws= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb h1:MwdqXipO1SFnK2k1iIL8xlUkZC63KdZR6QB/DSIlYBk= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb/go.mod h1:J9k1GS4/KUFdJgWCp8Od4J3XXalm3pUPm8YYrMFzYTk= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dhui/dktest v0.3.3 h1:DBuH/9GFaWbDRa42qsut/hbQu+srAQ0rPWnUoiGX7CA= -github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ= -github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8= -github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -105,92 +30,31 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= -github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE= -github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0= -github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= -github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -199,7 +63,6 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= -github.com/jackc/pgconn v1.3.2/go.mod h1:LvCquS3HbBKwgl7KbX9KyqEIumJAbm1UMcTvGaIf3bM= github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= @@ -245,27 +108,17 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -278,40 +131,19 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= 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= @@ -319,8 +151,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rollbar/rollbar-go v1.2.0 h1:CUanFtVu0sa3QZ/fBlgevdGQGLWaE3D4HxoVSQohDfo= +github.com/rollbar/rollbar-go v1.2.0/go.mod h1:czC86b8U4xdUH7W2C6gomi2jutLm8qK0OtrF5WMvpcc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -330,323 +163,105 @@ github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoM github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0= -github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d h1:dOiJ2n2cMwGLce/74I/QHMbnpk5GfY7InR8rczoMqRM= -golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM= -golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200818005847-188abfa75333/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3 h1:sg8vLDNIxFPHTchfhH1E3AI32BL3f23oie38xUWnJM8= -google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -655,45 +270,19 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= -modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= -modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= -modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= -modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= -modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= -modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= -modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= -modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= -modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 00000000..e2d567c2 --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,70 @@ +package app + +import ( + "os" + "os/signal" + "syscall" + + "github.com/evrone/go-service-template/internal/business-logic/domain" + + "github.com/evrone/go-service-template/pkg/logger" + + "github.com/evrone/go-service-template/internal/business-logic/entity" + "github.com/evrone/go-service-template/internal/business-logic/entity/repository" + "github.com/evrone/go-service-template/internal/business-logic/entity/translator" + "github.com/evrone/go-service-template/internal/entrypoints/http/api/v1" + "github.com/evrone/go-service-template/pkg/postgres" + + "github.com/evrone/go-service-template/pkg/httpserver" + + "github.com/evrone/go-service-template/internal/entrypoints/http/probe" +) + +func Run() { + conf := NewConfig() + + zap := logger.NewZapLogger(conf.ZapLogLevel) + defer zap.Close() + rollbar := logger.NewRollbarLogger(conf.RollbarAccessToken, conf.RollbarEnvironment) + defer rollbar.Close() + domain.Logger = logger.NewAppLogger(zap, rollbar) + + pg := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) + pgRepository := repository.NewPostgresEntityRepository(pg) + defer pg.Close() + + googleTranslateAPI := translator.NewGoogleTranslator() + + entityUseCase := entity.NewUseCase(pgRepository, googleTranslateAPI) + + apiRouter := v1.NewApiRouter(entityUseCase) + apiServer := httpserver.NewServer(apiRouter, conf.HttpApiPort) + apiServer.Start() + + probeRouter := probe.NewHealthRouter() + probeServer := httpserver.NewServer(probeRouter, conf.HttpProbePort) + probeServer.Start() + + // Graceful shutdown + interrupt := make(chan os.Signal, 1) + signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) + + select { + case s := <-interrupt: + domain.Logger.Info("main - signal: " + s.String()) + case err := <-probeServer.Notify(): + domain.Logger.Error(err, "main - probeServer.Notify") + case err := <-apiServer.Notify(): + domain.Logger.Error(err, "main - apiServer.Notify") + } + + err := probeServer.Stop() + if err != nil { + domain.Logger.Error(err, "main - probeServer.Stop") + } + + err = apiServer.Stop() + if err != nil { + domain.Logger.Error(err, "main - apiServer.Stop") + } +} diff --git a/internal/app/configuration.go b/internal/app/configuration.go new file mode 100644 index 00000000..33ba5b11 --- /dev/null +++ b/internal/app/configuration.go @@ -0,0 +1,64 @@ +package app + +import ( + "log" + "os" + "strconv" +) + +type Conf struct { + ZapLogLevel string + RollbarAccessToken string + RollbarEnvironment string + HttpApiPort string + HttpProbePort string + PgURL string + PgPoolMax int + PgConnAttempts int + RmqURL string + RmqQueueName string +} + +func NewConfig() Conf { + return Conf{ + ZapLogLevel: strEnv("ZAP_LOG_LEVEL"), + RollbarAccessToken: strEnv("ROLLBAR_ACCESS_TOKEN"), + RollbarEnvironment: strEnv("ROLLBAR_ENVIRONMENT"), + HttpApiPort: strEnv("HTTP_API_PORT"), + HttpProbePort: strEnv("HTTP_PROBE_PORT"), + PgURL: strEnv("PG_URL"), + PgPoolMax: intEnv("PG_POOL_MAX"), + PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), + RmqURL: strEnv("RMQ_URL"), + RmqQueueName: strEnv("RMQ_QUEUE_NAME"), + } +} + +func strEnv(env string) string { + value, ok := os.LookupEnv(env) + if !ok || len(value) == 0 { + log.Fatalf("environment variable not declared: %s", env) + } + + log.Println(env, "=", value) + + return value +} + +func intEnv(env string) int { + var intValue int + + value, ok := os.LookupEnv(env) + if !ok || len(value) == 0 { + log.Fatalf("environment variable not declared: %s", env) + } + + intValue, err := strconv.Atoi(value) + if err != nil { + log.Fatalf("typecast error to integer: %s", err) + } + + log.Println(env, "=", intValue) + + return intValue +} diff --git a/business-logic/domain/entity.go b/internal/business-logic/domain/entity.go similarity index 95% rename from business-logic/domain/entity.go rename to internal/business-logic/domain/entity.go index 3e6fc8f3..ee6c2fa7 100644 --- a/business-logic/domain/entity.go +++ b/internal/business-logic/domain/entity.go @@ -1,6 +1,8 @@ package domain -import "context" +import ( + "context" +) type Entity struct { Original string `json:"original" binding:"required"` diff --git a/internal/business-logic/domain/logger.go b/internal/business-logic/domain/logger.go new file mode 100644 index 00000000..93fc6665 --- /dev/null +++ b/internal/business-logic/domain/logger.go @@ -0,0 +1,16 @@ +package domain + +var Logger Log + +type Log interface { + Debug(msg string, fields ...Field) + Info(msg string, fields ...Field) + Warn(msg string, fields ...Field) + Error(err error, msg string, fields ...Field) + Fatal(err error, msg string, fields ...Field) +} + +type Field struct { + Key string + Val interface{} +} diff --git a/business-logic/entity/repository/postgres.go b/internal/business-logic/entity/repository/postgres.go similarity index 89% rename from business-logic/entity/repository/postgres.go rename to internal/business-logic/entity/repository/postgres.go index fd925d32..c2d98d73 100644 --- a/business-logic/entity/repository/postgres.go +++ b/internal/business-logic/entity/repository/postgres.go @@ -3,9 +3,9 @@ package repository import ( "context" - "github.com/evrone/go-service-template/infrastructure/postgres" + "github.com/evrone/go-service-template/pkg/postgres" - "github.com/evrone/go-service-template/business-logic/domain" + "github.com/evrone/go-service-template/internal/business-logic/domain" ) type postgresEntityRepository struct { diff --git a/business-logic/entity/translator/google-translator.go b/internal/business-logic/entity/translator/google-translator.go similarity index 89% rename from business-logic/entity/translator/google-translator.go rename to internal/business-logic/entity/translator/google-translator.go index 0998d97f..bf841111 100644 --- a/business-logic/entity/translator/google-translator.go +++ b/internal/business-logic/entity/translator/google-translator.go @@ -3,7 +3,7 @@ package translator import ( translator "github.com/Conight/go-googletrans" - "github.com/evrone/go-service-template/business-logic/domain" + "github.com/evrone/go-service-template/internal/business-logic/domain" ) type GoogleTranslator struct{} diff --git a/internal/business-logic/entity/usecase.go b/internal/business-logic/entity/usecase.go new file mode 100644 index 00000000..452325c4 --- /dev/null +++ b/internal/business-logic/entity/usecase.go @@ -0,0 +1,48 @@ +package entity + +import ( + "context" + + "github.com/evrone/go-service-template/internal/business-logic/domain" +) + +type useCase struct { + repo domain.EntityRepository + translator domain.EntityTranslator +} + +func NewUseCase( + repository domain.EntityRepository, + translateAPI domain.EntityTranslator, +) domain.EntityUseCase { + return &useCase{ + repo: repository, + translator: translateAPI, + } +} + +func (u *useCase) History() ([]domain.Entity, error) { + entities, err := u.repo.GetHistory(context.Background()) + if err != nil { + domain.Logger.Error(err, "History - repo.GetHistory") + return nil, err + } + + return entities, nil +} + +func (u *useCase) DoTranslate(entity domain.Entity) (domain.Entity, error) { + entity, err := u.translator.Translate(entity) + if err != nil { + domain.Logger.Error(err, "DoTranslate - translator.Translate") + return domain.Entity{}, err + } + + err = u.repo.Store(context.Background(), entity) + if err != nil { + domain.Logger.Error(err, "DoTranslate - repo.Store") + return domain.Entity{}, err + } + + return entity, nil +} diff --git a/entrypoints/http/api/v1/router.go b/internal/entrypoints/http/api/v1/router.go similarity index 93% rename from entrypoints/http/api/v1/router.go rename to internal/entrypoints/http/api/v1/router.go index fb9be2ee..efc7209e 100644 --- a/entrypoints/http/api/v1/router.go +++ b/internal/entrypoints/http/api/v1/router.go @@ -3,7 +3,7 @@ package v1 import ( "net/http" - "github.com/evrone/go-service-template/business-logic/domain" + "github.com/evrone/go-service-template/internal/business-logic/domain" "github.com/gin-gonic/gin" ) diff --git a/entrypoints/http/probe/router.go b/internal/entrypoints/http/probe/router.go similarity index 90% rename from entrypoints/http/probe/router.go rename to internal/entrypoints/http/probe/router.go index 44ccfb31..aec950ae 100644 --- a/entrypoints/http/probe/router.go +++ b/internal/entrypoints/http/probe/router.go @@ -1,7 +1,6 @@ package probe import ( - "log" "net/http" ) @@ -19,6 +18,5 @@ func NewHealthRouter() http.Handler { } func (h *healthRouter) liveness(w http.ResponseWriter, r *http.Request) { - log.Println("It's alive!") w.WriteHeader(http.StatusOK) } diff --git a/entrypoints/schedule/scheduler.go b/internal/entrypoints/schedule/scheduler.go similarity index 84% rename from entrypoints/schedule/scheduler.go rename to internal/entrypoints/schedule/scheduler.go index eab2fe6b..8ac76d3b 100644 --- a/entrypoints/schedule/scheduler.go +++ b/internal/entrypoints/schedule/scheduler.go @@ -3,7 +3,7 @@ package schedule import ( "time" - "github.com/evrone/go-service-template/business-logic/domain" + "github.com/evrone/go-service-template/internal/business-logic/domain" ) type Scheduler struct { @@ -30,7 +30,7 @@ func (s *Scheduler) Start() { default: } - s.useCase.DoTranslate() + // s.useCase.DoTranslate() } }() } diff --git a/mocks/EntityUseCase.go b/mocks/EntityUseCase.go deleted file mode 100644 index c6fe9e85..00000000 --- a/mocks/EntityUseCase.go +++ /dev/null @@ -1,15 +0,0 @@ -// Code generated by mockery v2.5.1. DO NOT EDIT. - -package mocks - -import mock "github.com/stretchr/testify/mock" - -// EntityUseCase is an autogenerated mock type for the EntityUseCase type -type EntityUseCase struct { - mock.Mock -} - -// DoTranslate provides a mock function with given fields: -func (_m *EntityUseCase) DoTranslate() { - _m.Called() -} diff --git a/mocks/EntityRepository.go b/mocks/entity_repository.go similarity index 76% rename from mocks/EntityRepository.go rename to mocks/entity_repository.go index 2e1eb101..ae3db70d 100644 --- a/mocks/EntityRepository.go +++ b/mocks/entity_repository.go @@ -5,7 +5,7 @@ package mocks import ( context "context" - domain "github.com/evrone/go-service-template/business-logic/domain" + domain "github.com/evrone/go-service-template/internal/business-logic/domain" mock "github.com/stretchr/testify/mock" ) @@ -37,13 +37,13 @@ func (_m *EntityRepository) GetHistory(_a0 context.Context) ([]domain.Entity, er return r0, r1 } -// Store provides a mock function with given fields: _a0, _a1 -func (_m *EntityRepository) Store(_a0 context.Context, _a1 domain.Entity) error { - ret := _m.Called(_a0, _a1) +// Store provides a mock function with given fields: ctx, entity +func (_m *EntityRepository) Store(ctx context.Context, entity domain.Entity) error { + ret := _m.Called(ctx, entity) var r0 error if rf, ok := ret.Get(0).(func(context.Context, domain.Entity) error); ok { - r0 = rf(_a0, _a1) + r0 = rf(ctx, entity) } else { r0 = ret.Error(0) } diff --git a/mocks/EntityTranslator.go b/mocks/entity_translator.go similarity index 63% rename from mocks/EntityTranslator.go rename to mocks/entity_translator.go index a7a3ef92..d44b788a 100644 --- a/mocks/EntityTranslator.go +++ b/mocks/entity_translator.go @@ -3,7 +3,7 @@ package mocks import ( - domain "github.com/evrone/go-service-template/business-logic/domain" + domain "github.com/evrone/go-service-template/internal/business-logic/domain" mock "github.com/stretchr/testify/mock" ) @@ -12,20 +12,20 @@ type EntityTranslator struct { mock.Mock } -// Translate provides a mock function with given fields: _a0 -func (_m *EntityTranslator) Translate(_a0 domain.Entity) (domain.Entity, error) { - ret := _m.Called(_a0) +// Translate provides a mock function with given fields: entity +func (_m *EntityTranslator) Translate(entity domain.Entity) (domain.Entity, error) { + ret := _m.Called(entity) var r0 domain.Entity if rf, ok := ret.Get(0).(func(domain.Entity) domain.Entity); ok { - r0 = rf(_a0) + r0 = rf(entity) } else { r0 = ret.Get(0).(domain.Entity) } var r1 error if rf, ok := ret.Get(1).(func(domain.Entity) error); ok { - r1 = rf(_a0) + r1 = rf(entity) } else { r1 = ret.Error(1) } diff --git a/mocks/entity_use_case.go b/mocks/entity_use_case.go new file mode 100644 index 00000000..8f3efdc3 --- /dev/null +++ b/mocks/entity_use_case.go @@ -0,0 +1,57 @@ +// Code generated by mockery v2.5.1. DO NOT EDIT. + +package mocks + +import ( + domain "github.com/evrone/go-service-template/internal/business-logic/domain" + mock "github.com/stretchr/testify/mock" +) + +// EntityUseCase is an autogenerated mock type for the EntityUseCase type +type EntityUseCase struct { + mock.Mock +} + +// DoTranslate provides a mock function with given fields: entity +func (_m *EntityUseCase) DoTranslate(entity domain.Entity) (domain.Entity, error) { + ret := _m.Called(entity) + + var r0 domain.Entity + if rf, ok := ret.Get(0).(func(domain.Entity) domain.Entity); ok { + r0 = rf(entity) + } else { + r0 = ret.Get(0).(domain.Entity) + } + + var r1 error + if rf, ok := ret.Get(1).(func(domain.Entity) error); ok { + r1 = rf(entity) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// History provides a mock function with given fields: +func (_m *EntityUseCase) History() ([]domain.Entity, error) { + ret := _m.Called() + + var r0 []domain.Entity + if rf, ok := ret.Get(0).(func() []domain.Entity); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]domain.Entity) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/infrastructure/httpserver/http.go b/pkg/httpserver/http.go similarity index 100% rename from infrastructure/httpserver/http.go rename to pkg/httpserver/http.go diff --git a/infrastructure/img/example-http-db.png b/pkg/img/example-http-db.png similarity index 100% rename from infrastructure/img/example-http-db.png rename to pkg/img/example-http-db.png diff --git a/infrastructure/img/layers.png b/pkg/img/layers.png similarity index 100% rename from infrastructure/img/layers.png rename to pkg/img/layers.png diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 00000000..2334ea60 --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,97 @@ +package logger + +import ( + "fmt" + + "github.com/evrone/go-service-template/internal/business-logic/domain" + + "github.com/rollbar/rollbar-go" + + "go.uber.org/zap" +) + +type appLogger struct { + zap *ZapLogger + rollbar *RollbarLogger +} + +func NewAppLogger(zap *ZapLogger, rollbar *RollbarLogger) domain.Log { + return &appLogger{zap, rollbar} +} + +func (l *appLogger) Debug(msg string, fields ...domain.Field) { + if fields == nil { + l.rollbar.Message(rollbar.DEBUG, msg) + l.zap.Debug(msg) + return + } + + l.rollbar.MessageWithExtras(rollbar.DEBUG, msg, rollbarMap(fields)) + l.zap.Debug(msg, zapFields(fields)...) +} + +func (l *appLogger) Info(msg string, fields ...domain.Field) { + if fields == nil { + l.rollbar.Message(rollbar.INFO, msg) + l.zap.Info(msg) + return + } + + l.rollbar.MessageWithExtras(rollbar.INFO, msg, rollbarMap(fields)) + l.zap.Info(msg, zapFields(fields)...) +} + +func (l *appLogger) Warn(msg string, fields ...domain.Field) { + if fields == nil { + l.rollbar.Message(rollbar.WARN, msg) + l.zap.Warn(msg) + return + } + + l.rollbar.MessageWithExtras(rollbar.WARN, msg, rollbarMap(fields)) + l.zap.Warn(msg, zapFields(fields)...) +} + +func (l *appLogger) Error(err error, msg string, fields ...domain.Field) { + err = fmt.Errorf("%s: %w", msg, err) + + if fields == nil { + l.rollbar.ErrorWithStackSkip(rollbar.ERR, err, 4) + l.zap.Error(err.Error()) + return + } + + l.rollbar.ErrorWithStackSkipWithExtras(rollbar.ERR, err, 3, rollbarMap(fields)) + l.zap.Error(err.Error(), zapFields(fields)...) +} + +func (l *appLogger) Fatal(err error, msg string, fields ...domain.Field) { + err = fmt.Errorf("%s: %w", msg, err) + + if fields == nil { + l.rollbar.ErrorWithStackSkip(rollbar.CRIT, err, 4) + l.rollbar.Close() + l.zap.Fatal(err.Error()) // os.Exit() + return + } + + l.rollbar.ErrorWithStackSkipWithExtras(rollbar.CRIT, err, 3, rollbarMap(fields)) + l.rollbar.Close() + l.zap.Fatal(err.Error(), zapFields(fields)...) // os.Exit() +} + +func rollbarMap(fields []domain.Field) map[string]interface{} { + m := make(map[string]interface{}, len(fields)*2) + for _, field := range fields { + m[field.Key] = field.Val + } + return m +} + +func zapFields(fields []domain.Field) []zap.Field { + s := make([]zap.Field, 0, len(fields)) + for _, field := range fields { + s = append(s, zap.Reflect(field.Key, field.Val)) + } + return s +} diff --git a/pkg/logger/rollbar.go b/pkg/logger/rollbar.go new file mode 100644 index 00000000..8119de13 --- /dev/null +++ b/pkg/logger/rollbar.go @@ -0,0 +1,16 @@ +package logger + +import "github.com/rollbar/rollbar-go" + +type RollbarLogger struct { + *rollbar.Client +} + +func NewRollbarLogger(token, env string) *RollbarLogger { + client := rollbar.NewAsync(token, env, "", "", "") + return &RollbarLogger{client} +} + +func (r *RollbarLogger) Close() { + r.Client.Wait() +} diff --git a/pkg/logger/zap.go b/pkg/logger/zap.go new file mode 100644 index 00000000..0e6b0d80 --- /dev/null +++ b/pkg/logger/zap.go @@ -0,0 +1,38 @@ +package logger + +import ( + "log" + + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type ZapLogger struct { + *zap.Logger +} + +func NewZapLogger(logLevel string) *ZapLogger { + var ( + level zapcore.Level + logger *zap.Logger + ) + + err := level.UnmarshalText([]byte(logLevel)) + if err != nil { + log.Fatalf("zap init error: %s", err) + } + + config := zap.NewProductionConfig() + config.Level = zap.NewAtomicLevelAt(level) + + logger, err = config.Build() + if err != nil { + log.Fatalf("zap init error: %s", err) + } + + return &ZapLogger{logger} +} + +func (z *ZapLogger) Close() { + _ = z.Logger.Sync() +} diff --git a/infrastructure/postgres/postgres.go b/pkg/postgres/postgres.go similarity index 68% rename from infrastructure/postgres/postgres.go rename to pkg/postgres/postgres.go index d84ed8ea..da046bde 100644 --- a/infrastructure/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -2,9 +2,10 @@ package postgres import ( "context" - "log" "time" + "github.com/evrone/go-service-template/internal/business-logic/domain" + "github.com/Masterminds/squirrel" "github.com/jackc/pgx/v4/pgxpool" ) @@ -17,7 +18,7 @@ type Postgres struct { func NewPostgres(url string, maxPoolSize, connAttempts int) Postgres { poolConfig, err := pgxpool.ParseConfig(url) if err != nil { - log.Fatalf("postgres connect error: %s", err) + domain.Logger.Fatal(err, "postgres connect error") } poolConfig.MaxConns = int32(maxPoolSize) @@ -30,19 +31,20 @@ func NewPostgres(url string, maxPoolSize, connAttempts int) Postgres { if errConn == nil { break } + domain.Logger.Debug("postgres is trying to connect", + domain.Field{Key: "attempts left", Val: connAttempts}, + ) - log.Printf("postgres is trying to connect, attempts left: %d", connAttempts) - - time.Sleep(time.Second) + time.Sleep(time.Second * 2) connAttempts-- } if errConn != nil { - log.Fatalf("postgres connect error: %s", errConn) + domain.Logger.Fatal(errConn, "postgres connect error") } - log.Print("postgres connected") + domain.Logger.Info("postgres connected") builder := squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) diff --git a/infrastructure/rabbitmq/rabbitmq.go b/pkg/rabbitmq/rabbitmq.go similarity index 100% rename from infrastructure/rabbitmq/rabbitmq.go rename to pkg/rabbitmq/rabbitmq.go From 048b00b677b85a083d1b8315b95520640d6d6936 Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Tue, 9 Feb 2021 14:43:18 +0400 Subject: [PATCH 03/15] Swagger --- .env | 1 - .golangci.yml | 2 +- Dockerfile | 5 +- Makefile | 15 +- docker-compose.yml | 1 - docs/docs.go | 203 ++++++++++++++++++ docs/swagger.json | 141 ++++++++++++ docs/swagger.yaml | 96 +++++++++ go.mod | 10 +- go.sum | 132 ++++++++++++ internal/app/app.go | 62 +++--- internal/app/configuration.go | 2 - internal/business-logic/domain/entity.go | 24 --- internal/business-logic/entity/usecase.go | 48 ----- internal/delivery/http/v1/error.go | 6 + internal/delivery/http/v1/handler.go | 19 ++ internal/delivery/http/v1/translation.go | 75 +++++++ internal/delivery/http/v2/handler.go | 16 ++ internal/delivery/rmq/consumer/translation.go | 1 + internal/delivery/rmq/publisher/interface.go | 1 + .../delivery/rmq/publisher/translation.go | 1 + .../{business-logic => }/domain/logger.go | 0 internal/domain/translation.go | 8 + internal/entrypoints/http/api/v1/router.go | 53 ----- internal/entrypoints/http/probe/router.go | 22 -- internal/entrypoints/schedule/scheduler.go | 40 ---- internal/service/interface.go | 8 + internal/service/translation.go | 51 +++++ internal/subservice/repository/interface.go | 12 ++ .../repository/translation_postgres.go} | 22 +- internal/subservice/webapi/interface.go | 7 + .../webapi/translation_google.go} | 11 +- migrations/000001_migrate_name.up.sql | 2 + mocks/entity_repository.go | 52 ----- mocks/entity_translator.go | 34 --- mocks/entity_use_case.go | 57 ----- pkg/logger/logger.go | 6 +- pkg/postgres/postgres.go | 2 +- 38 files changed, 854 insertions(+), 394 deletions(-) create mode 100644 docs/docs.go create mode 100644 docs/swagger.json create mode 100644 docs/swagger.yaml delete mode 100644 internal/business-logic/domain/entity.go delete mode 100644 internal/business-logic/entity/usecase.go create mode 100644 internal/delivery/http/v1/error.go create mode 100644 internal/delivery/http/v1/handler.go create mode 100644 internal/delivery/http/v1/translation.go create mode 100644 internal/delivery/http/v2/handler.go create mode 100644 internal/delivery/rmq/consumer/translation.go create mode 100644 internal/delivery/rmq/publisher/interface.go create mode 100644 internal/delivery/rmq/publisher/translation.go rename internal/{business-logic => }/domain/logger.go (100%) create mode 100644 internal/domain/translation.go delete mode 100644 internal/entrypoints/http/api/v1/router.go delete mode 100644 internal/entrypoints/http/probe/router.go delete mode 100644 internal/entrypoints/schedule/scheduler.go create mode 100644 internal/service/interface.go create mode 100644 internal/service/translation.go create mode 100644 internal/subservice/repository/interface.go rename internal/{business-logic/entity/repository/postgres.go => subservice/repository/translation_postgres.go} (60%) create mode 100644 internal/subservice/webapi/interface.go rename internal/{business-logic/entity/translator/google-translator.go => subservice/webapi/translation_google.go} (61%) delete mode 100644 mocks/entity_repository.go delete mode 100644 mocks/entity_translator.go delete mode 100644 mocks/entity_use_case.go diff --git a/.env b/.env index 1dc0700b..670beb4a 100644 --- a/.env +++ b/.env @@ -5,7 +5,6 @@ ROLLBAR_ACCESS_TOKEN = 97c783270fd14bf6a3c76ab524ee8839 ROLLBAR_ENVIRONMENT = go-template-test HTTP_API_PORT = 8080 -HTTP_PROBE_PORT = 8081 PG_URL = postgres://user:pass@localhost:5432/postgres PG_POOL_MAX = 1 diff --git a/.golangci.yml b/.golangci.yml index 3ddf6748..05d17029 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -47,7 +47,6 @@ linters-settings: linters: disable-all: true enable: - - godot - gofumpt - gci - misspell @@ -102,6 +101,7 @@ linters: # - varcheck # - wrapcheck # - wsl +# - godot issues: exclude-rules: diff --git a/Dockerfile b/Dockerfile index 9d73fc88..b01b9026 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# Step 1: Modules +# Step 1: Modules caching FROM golang:1.15.7-alpine3.13 as modules COPY go.mod go.sum /modules/ RUN cd /modules && go mod download @@ -13,8 +13,7 @@ COPY --from=modules /go/pkg /go/pkg COPY . /app WORKDIR /app -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ - go build -o /bin/app ./cmd/app +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /bin/app ./cmd/app # Step 3: Final diff --git a/Makefile b/Makefile index 6dcc0f47..ee070c94 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ include .env export -run: +run: swag go mod download && GIN_MODE=debug CGO_ENABLED=0 go run ./cmd/app -up: +compose-up: docker-compose up --build -d --remove-orphans && docker-compose logs -f -down: +compose-down: docker-compose down --remove-orphans test: @@ -16,10 +16,13 @@ test: mock: mockery --all -r --case snake +migrate-create: + migrate create -ext sql -dir migrations 'migrate_name' + migrate: migrate -path migrations -database '$(PG_URL)?sslmode=disable' up -create: - migrate create -ext sql -dir migrations 'migrate_name' +swag: + swag init -g internal/app/app.go -.PHONY: run, up, down, test, mock, migrate, create \ No newline at end of file +.PHONY: run, compose-up, compose-down, test, mock, migrate-create, migrate, swag \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index c3bff835..52423b32 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,7 +11,6 @@ services: # PG_URL: 'postgres://user:pass@db:5432/postgres' # ports: # - 8080:8080 # api -# - 8081:8081 # K8s probe # depends_on: # - db diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 00000000..e2905734 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,203 @@ +// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT +// This file was generated by swaggo/swag + +package docs + +import ( + "bytes" + "encoding/json" + "strings" + + "github.com/alecthomas/template" + "github.com/swaggo/swag" +) + +var doc = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{.Description}}", + "title": "{{.Title}}", + "contact": {}, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/translation/do-translate": { + "post": { + "description": "Translate a text", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "translation" + ], + "summary": "Translate", + "operationId": "do-translate", + "parameters": [ + { + "description": "Set up translation", + "name": "input", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.translationInput" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Translation" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.errorResponse" + } + } + } + } + }, + "/translation/history": { + "get": { + "description": "Show all translation history", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "translation" + ], + "summary": "Show history", + "operationId": "history", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Translation" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.errorResponse" + } + } + } + } + } + }, + "definitions": { + "domain.Translation": { + "type": "object", + "properties": { + "destination": { + "type": "string", + "example": "en" + }, + "original": { + "type": "string", + "example": "текст для перевода" + }, + "source": { + "type": "string", + "example": "auto" + }, + "translation": { + "type": "string", + "example": "text for translation" + } + } + }, + "v1.errorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "message" + } + } + }, + "v1.translationInput": { + "type": "object", + "required": [ + "destination", + "original", + "source" + ], + "properties": { + "destination": { + "type": "string", + "example": "en" + }, + "original": { + "type": "string", + "example": "текст для перевода" + }, + "source": { + "type": "string", + "example": "auto" + } + } + } + } +}` + +type swaggerInfo struct { + Version string + Host string + BasePath string + Schemes []string + Title string + Description string +} + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = swaggerInfo{ + Version: "1.0", + Host: "localhost:8080", + BasePath: "/api/v1/", + Schemes: []string{}, + Title: "Go Service Template API", + Description: "Using a translation service as an example", +} + +type s struct{} + +func (s *s) ReadDoc() string { + sInfo := SwaggerInfo + sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1) + + t, err := template.New("swagger_info").Funcs(template.FuncMap{ + "marshal": func(v interface{}) string { + a, _ := json.Marshal(v) + return string(a) + }, + }).Parse(doc) + if err != nil { + return doc + } + + var tpl bytes.Buffer + if err := t.Execute(&tpl, sInfo); err != nil { + return doc + } + + return tpl.String() +} + +func init() { + swag.Register(swag.Name, &s{}) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 00000000..d3b87e10 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,141 @@ +{ + "swagger": "2.0", + "info": { + "description": "Using a translation service as an example", + "title": "Go Service Template API", + "contact": {}, + "version": "1.0" + }, + "host": "localhost:8080", + "basePath": "/api/v1/", + "paths": { + "/translation/do-translate": { + "post": { + "description": "Translate a text", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "translation" + ], + "summary": "Translate", + "operationId": "do-translate", + "parameters": [ + { + "description": "Set up translation", + "name": "input", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.translationInput" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Translation" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.errorResponse" + } + } + } + } + }, + "/translation/history": { + "get": { + "description": "Show all translation history", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "translation" + ], + "summary": "Show history", + "operationId": "history", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Translation" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.errorResponse" + } + } + } + } + } + }, + "definitions": { + "domain.Translation": { + "type": "object", + "properties": { + "destination": { + "type": "string", + "example": "en" + }, + "original": { + "type": "string", + "example": "текст для перевода" + }, + "source": { + "type": "string", + "example": "auto" + }, + "translation": { + "type": "string", + "example": "text for translation" + } + } + }, + "v1.errorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "message" + } + } + }, + "v1.translationInput": { + "type": "object", + "required": [ + "destination", + "original", + "source" + ], + "properties": { + "destination": { + "type": "string", + "example": "en" + }, + "original": { + "type": "string", + "example": "текст для перевода" + }, + "source": { + "type": "string", + "example": "auto" + } + } + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 00000000..43b67bf1 --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,96 @@ +basePath: /api/v1/ +definitions: + domain.Translation: + properties: + destination: + example: en + type: string + original: + example: текст для перевода + type: string + source: + example: auto + type: string + translation: + example: text for translation + type: string + type: object + v1.errorResponse: + properties: + error: + example: message + type: string + type: object + v1.translationInput: + properties: + destination: + example: en + type: string + original: + example: текст для перевода + type: string + source: + example: auto + type: string + required: + - destination + - original + - source + type: object +host: localhost:8080 +info: + contact: {} + description: Using a translation service as an example + title: Go Service Template API + version: "1.0" +paths: + /translation/do-translate: + post: + consumes: + - application/json + description: Translate a text + operationId: do-translate + parameters: + - description: Set up translation + in: body + name: input + required: true + schema: + $ref: '#/definitions/v1.translationInput' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.Translation' + "400": + description: Bad Request + schema: + $ref: '#/definitions/v1.errorResponse' + summary: Translate + tags: + - translation + /translation/history: + get: + consumes: + - application/json + description: Show all translation history + operationId: history + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/domain.Translation' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/v1.errorResponse' + summary: Show history + tags: + - translation +swagger: "2.0" diff --git a/go.mod b/go.mod index 56b2424c..9b6231ab 100644 --- a/go.mod +++ b/go.mod @@ -5,17 +5,23 @@ go 1.15 require ( github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb github.com/Masterminds/squirrel v1.5.0 + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 github.com/gin-gonic/gin v1.6.3 + github.com/go-openapi/spec v0.20.3 // indirect github.com/golang/protobuf v1.4.3 // indirect github.com/google/go-cmp v0.5.1 // indirect github.com/jackc/pgx/v4 v4.10.1 github.com/lib/pq v1.8.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rollbar/rollbar-go v1.2.0 github.com/stretchr/testify v1.7.0 + github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 + github.com/swaggo/gin-swagger v1.3.0 + github.com/swaggo/swag v1.7.0 go.uber.org/zap v1.10.0 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect + golang.org/x/tools v0.1.0 // indirect google.golang.org/protobuf v1.25.0 // indirect ) diff --git a/go.sum b/go.sum index 538624cb..06220727 100644 --- a/go.sum +++ b/go.sum @@ -3,25 +3,67 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb h1:MwdqXipO1SFnK2k1iIL8xlUkZC63KdZR6QB/DSIlYBk= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb/go.mod h1:J9k1GS4/KUFdJgWCp8Od4J3XXalm3pUPm8YYrMFzYTk= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= +github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= +github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= +github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= +github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= +github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= +github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= +github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= +github.com/go-openapi/spec v0.19.14 h1:r4fbYFo6N4ZelmSX8G6p+cv/hZRXzcuqQIADGT1iNKM= +github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA= +github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= +github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= +github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= +github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -36,6 +78,7 @@ github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= @@ -108,6 +151,10 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +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/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= @@ -119,6 +166,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -131,9 +180,17 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= +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/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -142,8 +199,14 @@ github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHX github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= 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= @@ -157,10 +220,14 @@ github.com/rollbar/rollbar-go v1.2.0/go.mod h1:czC86b8U4xdUH7W2C6gomi2jutLm8qK0O github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= @@ -172,12 +239,30 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM= +github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= +github.com/swaggo/gin-swagger v1.3.0 h1:eOmp7r57oUgZPw2dJOjcGNMse9cvXcI4tTqBcnZtPsI= +github.com/swaggo/gin-swagger v1.3.0/go.mod h1:oy1BRA6WvgtCp848lhxce7BnWH4C8Bxa0m5SkWx+cS0= +github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= +github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E= +github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q= +github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc= +github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4= +github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -196,6 +281,7 @@ golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaE golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -208,48 +294,82 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/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-20190222072716-a9d3bda3a223/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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e h1:t96dS3DO8DGjawSLJL/HIdz8CycAd2v07XxqB3UPTi0= +golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= @@ -275,13 +395,25 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= diff --git a/internal/app/app.go b/internal/app/app.go index e2d567c2..239151f6 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,49 +1,68 @@ package app import ( + "net/http" "os" "os/signal" "syscall" - "github.com/evrone/go-service-template/internal/business-logic/domain" + "github.com/gin-gonic/gin" + + "github.com/evrone/go-service-template/internal/delivery/http/v1" + "github.com/evrone/go-service-template/internal/delivery/http/v2" + + "github.com/evrone/go-service-template/internal/domain" "github.com/evrone/go-service-template/pkg/logger" - "github.com/evrone/go-service-template/internal/business-logic/entity" - "github.com/evrone/go-service-template/internal/business-logic/entity/repository" - "github.com/evrone/go-service-template/internal/business-logic/entity/translator" - "github.com/evrone/go-service-template/internal/entrypoints/http/api/v1" + "github.com/evrone/go-service-template/internal/service" + "github.com/evrone/go-service-template/internal/subservice/repository" + "github.com/evrone/go-service-template/internal/subservice/webapi" "github.com/evrone/go-service-template/pkg/postgres" "github.com/evrone/go-service-template/pkg/httpserver" - "github.com/evrone/go-service-template/internal/entrypoints/http/probe" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" ) +// @title Go Service Template API +// @version 1.0 +// @description Using a translation service as an example + +// @host localhost:8080 +// @BasePath /api/v1/ func Run() { conf := NewConfig() + // Logger zap := logger.NewZapLogger(conf.ZapLogLevel) defer zap.Close() rollbar := logger.NewRollbarLogger(conf.RollbarAccessToken, conf.RollbarEnvironment) defer rollbar.Close() domain.Logger = logger.NewAppLogger(zap, rollbar) + // Repository pg := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) pgRepository := repository.NewPostgresEntityRepository(pg) defer pg.Close() - googleTranslateAPI := translator.NewGoogleTranslator() + // WebAPI + googleTranslateAPI := webapi.NewGoogleTranslator() + + // Service + entityUseCase := service.NewUseCase(pgRepository, googleTranslateAPI) - entityUseCase := entity.NewUseCase(pgRepository, googleTranslateAPI) + // Router + handler := gin.Default() + handler.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger + handler.GET("/health", func(c *gin.Context) { c.Status(http.StatusOK) }) // K8s probe - apiRouter := v1.NewApiRouter(entityUseCase) - apiServer := httpserver.NewServer(apiRouter, conf.HttpApiPort) - apiServer.Start() + v1.NewAPIRouter(handler, entityUseCase) + v2.NewAPIRouter(handler) - probeRouter := probe.NewHealthRouter() - probeServer := httpserver.NewServer(probeRouter, conf.HttpProbePort) - probeServer.Start() + server := httpserver.NewServer(handler, conf.HttpApiPort) + server.Start() // Graceful shutdown interrupt := make(chan os.Signal, 1) @@ -52,19 +71,12 @@ func Run() { select { case s := <-interrupt: domain.Logger.Info("main - signal: " + s.String()) - case err := <-probeServer.Notify(): - domain.Logger.Error(err, "main - probeServer.Notify") - case err := <-apiServer.Notify(): - domain.Logger.Error(err, "main - apiServer.Notify") - } - - err := probeServer.Stop() - if err != nil { - domain.Logger.Error(err, "main - probeServer.Stop") + case err := <-server.Notify(): + domain.Logger.Error(err, "main - server.Notify") } - err = apiServer.Stop() + err := server.Stop() if err != nil { - domain.Logger.Error(err, "main - apiServer.Stop") + domain.Logger.Error(err, "main - server.Stop") } } diff --git a/internal/app/configuration.go b/internal/app/configuration.go index 33ba5b11..0b61e708 100644 --- a/internal/app/configuration.go +++ b/internal/app/configuration.go @@ -11,7 +11,6 @@ type Conf struct { RollbarAccessToken string RollbarEnvironment string HttpApiPort string - HttpProbePort string PgURL string PgPoolMax int PgConnAttempts int @@ -25,7 +24,6 @@ func NewConfig() Conf { RollbarAccessToken: strEnv("ROLLBAR_ACCESS_TOKEN"), RollbarEnvironment: strEnv("ROLLBAR_ENVIRONMENT"), HttpApiPort: strEnv("HTTP_API_PORT"), - HttpProbePort: strEnv("HTTP_PROBE_PORT"), PgURL: strEnv("PG_URL"), PgPoolMax: intEnv("PG_POOL_MAX"), PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), diff --git a/internal/business-logic/domain/entity.go b/internal/business-logic/domain/entity.go deleted file mode 100644 index ee6c2fa7..00000000 --- a/internal/business-logic/domain/entity.go +++ /dev/null @@ -1,24 +0,0 @@ -package domain - -import ( - "context" -) - -type Entity struct { - Original string `json:"original" binding:"required"` - Translation string `json:"translation"` -} - -type EntityUseCase interface { - DoTranslate(entity Entity) (Entity, error) - History() ([]Entity, error) -} - -type EntityTranslator interface { - Translate(entity Entity) (Entity, error) -} - -type EntityRepository interface { - Store(ctx context.Context, entity Entity) error - GetHistory(context.Context) ([]Entity, error) -} diff --git a/internal/business-logic/entity/usecase.go b/internal/business-logic/entity/usecase.go deleted file mode 100644 index 452325c4..00000000 --- a/internal/business-logic/entity/usecase.go +++ /dev/null @@ -1,48 +0,0 @@ -package entity - -import ( - "context" - - "github.com/evrone/go-service-template/internal/business-logic/domain" -) - -type useCase struct { - repo domain.EntityRepository - translator domain.EntityTranslator -} - -func NewUseCase( - repository domain.EntityRepository, - translateAPI domain.EntityTranslator, -) domain.EntityUseCase { - return &useCase{ - repo: repository, - translator: translateAPI, - } -} - -func (u *useCase) History() ([]domain.Entity, error) { - entities, err := u.repo.GetHistory(context.Background()) - if err != nil { - domain.Logger.Error(err, "History - repo.GetHistory") - return nil, err - } - - return entities, nil -} - -func (u *useCase) DoTranslate(entity domain.Entity) (domain.Entity, error) { - entity, err := u.translator.Translate(entity) - if err != nil { - domain.Logger.Error(err, "DoTranslate - translator.Translate") - return domain.Entity{}, err - } - - err = u.repo.Store(context.Background(), entity) - if err != nil { - domain.Logger.Error(err, "DoTranslate - repo.Store") - return domain.Entity{}, err - } - - return entity, nil -} diff --git a/internal/delivery/http/v1/error.go b/internal/delivery/http/v1/error.go new file mode 100644 index 00000000..bb7c26e9 --- /dev/null +++ b/internal/delivery/http/v1/error.go @@ -0,0 +1,6 @@ +package v1 + +// TODO +type errorResponse struct { + Error string `json:"error" example:"message"` +} diff --git a/internal/delivery/http/v1/handler.go b/internal/delivery/http/v1/handler.go new file mode 100644 index 00000000..53713041 --- /dev/null +++ b/internal/delivery/http/v1/handler.go @@ -0,0 +1,19 @@ +package v1 + +import ( + "github.com/evrone/go-service-template/internal/service" + "github.com/gin-gonic/gin" +) + +type router struct { + translationService service.Translation +} + +func NewAPIRouter(handler *gin.Engine, translationService service.Translation) { + r := &router{translationService} + + api := handler.Group("/api/v1") + { + r.translationRouts(api) + } +} diff --git a/internal/delivery/http/v1/translation.go b/internal/delivery/http/v1/translation.go new file mode 100644 index 00000000..e61ccbf1 --- /dev/null +++ b/internal/delivery/http/v1/translation.go @@ -0,0 +1,75 @@ +package v1 + +import ( + "net/http" + + "github.com/evrone/go-service-template/internal/domain" + + _ "github.com/evrone/go-service-template/docs" + "github.com/gin-gonic/gin" +) + +func (r *router) translationRouts(api *gin.RouterGroup) { + translation := api.Group("/translation") + { + translation.GET("/history", r.history) + translation.POST("/do-translate", r.doTranslate) + } +} + +// TODO + +// @Summary Show history +// @Description Show all translation history +// @ID history +// @Tags translation +// @Accept json +// @Produce json +// @Success 200 {array} domain.Translation +// @Failure 400 {object} errorResponse +// @Router /translation/history [get] +func (r *router) history(c *gin.Context) { + entities, err := r.translationService.History() + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, gin.H{"history": entities}) +} + +type translationInput struct { + Source string `json:"source" binding:"required" example:"auto"` + Destination string `json:"destination" binding:"required" example:"en"` + Original string `json:"original" binding:"required" example:"текст для перевода"` +} + +// @Summary Translate +// @Description Translate a text +// @ID do-translate +// @Tags translation +// @Accept json +// @Produce json +// @Param input body translationInput true "Set up translation" +// @Success 200 {object} domain.Translation +// @Failure 400 {object} errorResponse +// @Router /translation/do-translate [post] +func (r *router) doTranslate(c *gin.Context) { + var input translationInput + if err := c.ShouldBindJSON(&input); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + translationResponse, err := r.translationService.DoTranslate(domain.Translation{ + Source: input.Source, + Destination: input.Destination, + Original: input.Original, + }) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, translationResponse) +} diff --git a/internal/delivery/http/v2/handler.go b/internal/delivery/http/v2/handler.go new file mode 100644 index 00000000..8c1306b0 --- /dev/null +++ b/internal/delivery/http/v2/handler.go @@ -0,0 +1,16 @@ +package v2 + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type router struct{} + +func NewAPIRouter(handler *gin.Engine) { + api := handler.Group("/api/v2") + { + api.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, "Hello from v2") }) + } +} diff --git a/internal/delivery/rmq/consumer/translation.go b/internal/delivery/rmq/consumer/translation.go new file mode 100644 index 00000000..b78b46cb --- /dev/null +++ b/internal/delivery/rmq/consumer/translation.go @@ -0,0 +1 @@ +package consumer diff --git a/internal/delivery/rmq/publisher/interface.go b/internal/delivery/rmq/publisher/interface.go new file mode 100644 index 00000000..f5979501 --- /dev/null +++ b/internal/delivery/rmq/publisher/interface.go @@ -0,0 +1 @@ +package publisher diff --git a/internal/delivery/rmq/publisher/translation.go b/internal/delivery/rmq/publisher/translation.go new file mode 100644 index 00000000..f5979501 --- /dev/null +++ b/internal/delivery/rmq/publisher/translation.go @@ -0,0 +1 @@ +package publisher diff --git a/internal/business-logic/domain/logger.go b/internal/domain/logger.go similarity index 100% rename from internal/business-logic/domain/logger.go rename to internal/domain/logger.go diff --git a/internal/domain/translation.go b/internal/domain/translation.go new file mode 100644 index 00000000..17c71e1c --- /dev/null +++ b/internal/domain/translation.go @@ -0,0 +1,8 @@ +package domain + +type Translation struct { + Source string `json:"source" example:"auto"` + Destination string `json:"destination" example:"en"` + Original string `json:"original" example:"текст для перевода"` + Translation string `json:"translation" example:"text for translation"` +} diff --git a/internal/entrypoints/http/api/v1/router.go b/internal/entrypoints/http/api/v1/router.go deleted file mode 100644 index efc7209e..00000000 --- a/internal/entrypoints/http/api/v1/router.go +++ /dev/null @@ -1,53 +0,0 @@ -package v1 - -import ( - "net/http" - - "github.com/evrone/go-service-template/internal/business-logic/domain" - - "github.com/gin-gonic/gin" -) - -type apiRouter struct { - *gin.Engine - useCase domain.EntityUseCase -} - -func NewApiRouter(uc domain.EntityUseCase) http.Handler { - router := gin.Default() - api := &apiRouter{router, uc} - - v1 := router.Group("/api/v1") - { - v1.GET("/history", api.history) - v1.POST("/do-translate", api.doTranslate) - } - - return api -} - -func (a *apiRouter) history(c *gin.Context) { - entities, err := a.useCase.History() - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, entities) -} - -func (a *apiRouter) doTranslate(c *gin.Context) { - var entity domain.Entity - if err := c.ShouldBindJSON(&entity); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - entity, err := a.useCase.DoTranslate(entity) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - c.JSON(http.StatusOK, entity) -} diff --git a/internal/entrypoints/http/probe/router.go b/internal/entrypoints/http/probe/router.go deleted file mode 100644 index aec950ae..00000000 --- a/internal/entrypoints/http/probe/router.go +++ /dev/null @@ -1,22 +0,0 @@ -package probe - -import ( - "net/http" -) - -type healthRouter struct { - *http.ServeMux -} - -func NewHealthRouter() http.Handler { - mux := http.NewServeMux() - r := &healthRouter{mux} - - r.HandleFunc("/healthz", r.liveness) - - return r -} - -func (h *healthRouter) liveness(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) -} diff --git a/internal/entrypoints/schedule/scheduler.go b/internal/entrypoints/schedule/scheduler.go deleted file mode 100644 index 8ac76d3b..00000000 --- a/internal/entrypoints/schedule/scheduler.go +++ /dev/null @@ -1,40 +0,0 @@ -package schedule - -import ( - "time" - - "github.com/evrone/go-service-template/internal/business-logic/domain" -) - -type Scheduler struct { - useCase domain.EntityUseCase - interval time.Duration - stopSignal chan struct{} -} - -func NewScheduler(uc domain.EntityUseCase, seconds int) *Scheduler { - return &Scheduler{ - useCase: uc, - interval: time.Duration(seconds) * time.Second, - stopSignal: make(chan struct{}, 1), - } -} - -func (s *Scheduler) Start() { - go func() { - LOOP: - for range time.Tick(s.interval) { - select { - case <-s.stopSignal: - break LOOP - default: - } - - // s.useCase.DoTranslate() - } - }() -} - -func (s *Scheduler) Stop() { - close(s.stopSignal) -} diff --git a/internal/service/interface.go b/internal/service/interface.go new file mode 100644 index 00000000..26eef08c --- /dev/null +++ b/internal/service/interface.go @@ -0,0 +1,8 @@ +package service + +import "github.com/evrone/go-service-template/internal/domain" + +type Translation interface { + DoTranslate(entity domain.Translation) (domain.Translation, error) + History() ([]domain.Translation, error) +} diff --git a/internal/service/translation.go b/internal/service/translation.go new file mode 100644 index 00000000..a658acd9 --- /dev/null +++ b/internal/service/translation.go @@ -0,0 +1,51 @@ +package service + +import ( + "context" + + "github.com/evrone/go-service-template/internal/subservice/repository" + + "github.com/evrone/go-service-template/internal/subservice/webapi" + + "github.com/evrone/go-service-template/internal/domain" +) + +type useCase struct { + translationRepo repository.Translation + translationWebAPI webapi.Translation +} + +func NewUseCase(repo repository.Translation, api webapi.Translation) Translation { + return &useCase{ + translationRepo: repo, + translationWebAPI: api, + } +} + +func (u *useCase) History() ([]domain.Translation, error) { + entities, err := u.translationRepo.GetHistory(context.Background()) + if err != nil { + domain.Logger.Error(err, "History - translationRepo.GetHistory", + domain.Field{Key: "key", Val: "value"}, + ) + return nil, err + } + + return entities, nil +} + +func (u *useCase) DoTranslate(entity domain.Translation) (domain.Translation, error) { + entity, err := u.translationWebAPI.Translate(entity) + if err != nil { + domain.Logger.Error(err, "DoTranslate - translationWebAPI.Translate") + return domain.Translation{}, err + } + + err = u.translationRepo.Store(context.Background(), entity) + if err != nil { + domain.Logger.Error(err, "DoTranslate - translationRepo.Store") + return domain.Translation{}, err + } + + return entity, nil +} diff --git a/internal/subservice/repository/interface.go b/internal/subservice/repository/interface.go new file mode 100644 index 00000000..efd6a06c --- /dev/null +++ b/internal/subservice/repository/interface.go @@ -0,0 +1,12 @@ +package repository + +import ( + "context" + + "github.com/evrone/go-service-template/internal/domain" +) + +type Translation interface { + Store(ctx context.Context, entity domain.Translation) error + GetHistory(context.Context) ([]domain.Translation, error) +} diff --git a/internal/business-logic/entity/repository/postgres.go b/internal/subservice/repository/translation_postgres.go similarity index 60% rename from internal/business-logic/entity/repository/postgres.go rename to internal/subservice/repository/translation_postgres.go index c2d98d73..d65031b8 100644 --- a/internal/business-logic/entity/repository/postgres.go +++ b/internal/subservice/repository/translation_postgres.go @@ -3,22 +3,22 @@ package repository import ( "context" - "github.com/evrone/go-service-template/pkg/postgres" + "github.com/evrone/go-service-template/internal/domain" - "github.com/evrone/go-service-template/internal/business-logic/domain" + "github.com/evrone/go-service-template/pkg/postgres" ) type postgresEntityRepository struct { postgres.Postgres } -func NewPostgresEntityRepository(pg postgres.Postgres) domain.EntityRepository { +func NewPostgresEntityRepository(pg postgres.Postgres) Translation { return &postgresEntityRepository{pg} } -func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Entity, error) { +func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Translation, error) { sql, _, err := p.Builder. - Select("original, translation"). + Select("source, destination, original, translation"). From("history"). ToSql() if err != nil { @@ -31,10 +31,10 @@ func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Ent } defer rows.Close() - entities := make([]domain.Entity, 0, 64) + entities := make([]domain.Translation, 0, 64) for rows.Next() { - e := domain.Entity{} - err = rows.Scan(&e.Original, &e.Translation) + e := domain.Translation{} + err = rows.Scan(&e.Source, &e.Destination, &e.Original, &e.Translation) if err != nil { return nil, err } @@ -44,11 +44,11 @@ func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Ent return entities, nil } -func (p *postgresEntityRepository) Store(ctx context.Context, entity domain.Entity) error { +func (p *postgresEntityRepository) Store(ctx context.Context, entity domain.Translation) error { sql, args, err := p.Builder. Insert("history"). - Columns("original, translation"). - Values(entity.Original, entity.Translation). + Columns("source, destination, original, translation"). + Values(entity.Source, entity.Destination, entity.Original, entity.Translation). ToSql() if err != nil { return err diff --git a/internal/subservice/webapi/interface.go b/internal/subservice/webapi/interface.go new file mode 100644 index 00000000..e27705be --- /dev/null +++ b/internal/subservice/webapi/interface.go @@ -0,0 +1,7 @@ +package webapi + +import "github.com/evrone/go-service-template/internal/domain" + +type Translation interface { + Translate(entity domain.Translation) (domain.Translation, error) +} diff --git a/internal/business-logic/entity/translator/google-translator.go b/internal/subservice/webapi/translation_google.go similarity index 61% rename from internal/business-logic/entity/translator/google-translator.go rename to internal/subservice/webapi/translation_google.go index bf841111..fa161c9b 100644 --- a/internal/business-logic/entity/translator/google-translator.go +++ b/internal/subservice/webapi/translation_google.go @@ -1,9 +1,8 @@ -package translator +package webapi import ( translator "github.com/Conight/go-googletrans" - - "github.com/evrone/go-service-template/internal/business-logic/domain" + "github.com/evrone/go-service-template/internal/domain" ) type GoogleTranslator struct{} @@ -12,15 +11,15 @@ func NewGoogleTranslator() *GoogleTranslator { return &GoogleTranslator{} } -func (y *GoogleTranslator) Translate(entity domain.Entity) (domain.Entity, error) { +func (y *GoogleTranslator) Translate(entity domain.Translation) (domain.Translation, error) { c := translator.Config{ UserAgent: []string{"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"}, ServiceUrls: []string{"translate.google.as"}, } t := translator.New(c) - result, err := t.Translate(entity.Original, "ru", "en") + result, err := t.Translate(entity.Original, entity.Source, entity.Destination) if err != nil { - return domain.Entity{}, err + return domain.Translation{}, err } entity.Translation = result.Text diff --git a/migrations/000001_migrate_name.up.sql b/migrations/000001_migrate_name.up.sql index 886fc411..6c068bcd 100644 --- a/migrations/000001_migrate_name.up.sql +++ b/migrations/000001_migrate_name.up.sql @@ -1,5 +1,7 @@ CREATE TABLE IF NOT EXISTS history( id serial PRIMARY KEY, + source VARCHAR(255), + destination VARCHAR(255), original VARCHAR(255), translation VARCHAR(255) ); \ No newline at end of file diff --git a/mocks/entity_repository.go b/mocks/entity_repository.go deleted file mode 100644 index ae3db70d..00000000 --- a/mocks/entity_repository.go +++ /dev/null @@ -1,52 +0,0 @@ -// Code generated by mockery v2.5.1. DO NOT EDIT. - -package mocks - -import ( - context "context" - - domain "github.com/evrone/go-service-template/internal/business-logic/domain" - mock "github.com/stretchr/testify/mock" -) - -// EntityRepository is an autogenerated mock type for the EntityRepository type -type EntityRepository struct { - mock.Mock -} - -// GetHistory provides a mock function with given fields: _a0 -func (_m *EntityRepository) GetHistory(_a0 context.Context) ([]domain.Entity, error) { - ret := _m.Called(_a0) - - var r0 []domain.Entity - if rf, ok := ret.Get(0).(func(context.Context) []domain.Entity); ok { - r0 = rf(_a0) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]domain.Entity) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(_a0) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Store provides a mock function with given fields: ctx, entity -func (_m *EntityRepository) Store(ctx context.Context, entity domain.Entity) error { - ret := _m.Called(ctx, entity) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, domain.Entity) error); ok { - r0 = rf(ctx, entity) - } else { - r0 = ret.Error(0) - } - - return r0 -} diff --git a/mocks/entity_translator.go b/mocks/entity_translator.go deleted file mode 100644 index d44b788a..00000000 --- a/mocks/entity_translator.go +++ /dev/null @@ -1,34 +0,0 @@ -// Code generated by mockery v2.5.1. DO NOT EDIT. - -package mocks - -import ( - domain "github.com/evrone/go-service-template/internal/business-logic/domain" - mock "github.com/stretchr/testify/mock" -) - -// EntityTranslator is an autogenerated mock type for the EntityTranslator type -type EntityTranslator struct { - mock.Mock -} - -// Translate provides a mock function with given fields: entity -func (_m *EntityTranslator) Translate(entity domain.Entity) (domain.Entity, error) { - ret := _m.Called(entity) - - var r0 domain.Entity - if rf, ok := ret.Get(0).(func(domain.Entity) domain.Entity); ok { - r0 = rf(entity) - } else { - r0 = ret.Get(0).(domain.Entity) - } - - var r1 error - if rf, ok := ret.Get(1).(func(domain.Entity) error); ok { - r1 = rf(entity) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/mocks/entity_use_case.go b/mocks/entity_use_case.go deleted file mode 100644 index 8f3efdc3..00000000 --- a/mocks/entity_use_case.go +++ /dev/null @@ -1,57 +0,0 @@ -// Code generated by mockery v2.5.1. DO NOT EDIT. - -package mocks - -import ( - domain "github.com/evrone/go-service-template/internal/business-logic/domain" - mock "github.com/stretchr/testify/mock" -) - -// EntityUseCase is an autogenerated mock type for the EntityUseCase type -type EntityUseCase struct { - mock.Mock -} - -// DoTranslate provides a mock function with given fields: entity -func (_m *EntityUseCase) DoTranslate(entity domain.Entity) (domain.Entity, error) { - ret := _m.Called(entity) - - var r0 domain.Entity - if rf, ok := ret.Get(0).(func(domain.Entity) domain.Entity); ok { - r0 = rf(entity) - } else { - r0 = ret.Get(0).(domain.Entity) - } - - var r1 error - if rf, ok := ret.Get(1).(func(domain.Entity) error); ok { - r1 = rf(entity) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// History provides a mock function with given fields: -func (_m *EntityUseCase) History() ([]domain.Entity, error) { - ret := _m.Called() - - var r0 []domain.Entity - if rf, ok := ret.Get(0).(func() []domain.Entity); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).([]domain.Entity) - } - } - - var r1 error - if rf, ok := ret.Get(1).(func() error); ok { - r1 = rf() - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 2334ea60..7528f304 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -3,7 +3,7 @@ package logger import ( "fmt" - "github.com/evrone/go-service-template/internal/business-logic/domain" + "github.com/evrone/go-service-template/internal/domain" "github.com/rollbar/rollbar-go" @@ -21,23 +21,19 @@ func NewAppLogger(zap *ZapLogger, rollbar *RollbarLogger) domain.Log { func (l *appLogger) Debug(msg string, fields ...domain.Field) { if fields == nil { - l.rollbar.Message(rollbar.DEBUG, msg) l.zap.Debug(msg) return } - l.rollbar.MessageWithExtras(rollbar.DEBUG, msg, rollbarMap(fields)) l.zap.Debug(msg, zapFields(fields)...) } func (l *appLogger) Info(msg string, fields ...domain.Field) { if fields == nil { - l.rollbar.Message(rollbar.INFO, msg) l.zap.Info(msg) return } - l.rollbar.MessageWithExtras(rollbar.INFO, msg, rollbarMap(fields)) l.zap.Info(msg, zapFields(fields)...) } diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go index da046bde..48319960 100644 --- a/pkg/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/evrone/go-service-template/internal/business-logic/domain" + "github.com/evrone/go-service-template/internal/domain" "github.com/Masterminds/squirrel" "github.com/jackc/pgx/v4/pgxpool" From 8d6fa87aaeb024c26b75e43526bc5c390ec41cfc Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Tue, 16 Feb 2021 13:17:38 +0400 Subject: [PATCH 04/15] [GOT-20] Integration tests --- .circleci/bin/hadolint.sh | 6 + .circleci/bin/install_tools.sh | 24 + .circleci/bin/reviewdog.sh | 4 + .circleci/config.yml | 95 ++++ .env | 5 +- Dockerfile | 6 +- Dockerfile.prod | 11 + Makefile | 23 +- README.md | 34 +- docker-compose.yml | 24 +- docs/docs.go | 44 +- docs/swagger.json | 44 +- docs/swagger.yaml | 33 +- go.mod | 7 +- go.sum | 454 ++++++++++++++++++ integration_test/rest_api_test.go | 52 ++ internal/app/app.go | 34 +- internal/app/configuration.go | 4 + internal/app/migrate.go | 40 ++ internal/delivery/http/v1/error.go | 17 +- .../http/v1/{handler.go => router.go} | 2 +- internal/delivery/http/v1/translation.go | 38 +- .../http/v2/{handler.go => router.go} | 4 +- internal/domain/logger.go | 16 - .../{subservice => }/repository/interface.go | 0 .../repository/translation_postgres.go | 23 +- internal/service/interface.go | 2 +- internal/service/translation.go | 44 +- .../subservice/webapi/translation_google.go | 28 -- internal/{subservice => }/webapi/interface.go | 0 internal/webapi/translation_google.go | 32 ++ pkg/logger/interface.go | 14 + pkg/logger/logger.go | 93 ---- pkg/logger/logger_api.go | 31 ++ pkg/logger/loggers.go | 62 +++ pkg/postgres/postgres.go | 20 +- 36 files changed, 1070 insertions(+), 300 deletions(-) create mode 100755 .circleci/bin/hadolint.sh create mode 100755 .circleci/bin/install_tools.sh create mode 100755 .circleci/bin/reviewdog.sh create mode 100644 .circleci/config.yml create mode 100644 Dockerfile.prod create mode 100644 integration_test/rest_api_test.go create mode 100644 internal/app/migrate.go rename internal/delivery/http/v1/{handler.go => router.go} (77%) rename internal/delivery/http/v2/{handler.go => router.go} (70%) delete mode 100644 internal/domain/logger.go rename internal/{subservice => }/repository/interface.go (100%) rename internal/{subservice => }/repository/translation_postgres.go (54%) delete mode 100644 internal/subservice/webapi/translation_google.go rename internal/{subservice => }/webapi/interface.go (100%) create mode 100644 internal/webapi/translation_google.go create mode 100644 pkg/logger/interface.go delete mode 100644 pkg/logger/logger.go create mode 100644 pkg/logger/logger_api.go create mode 100644 pkg/logger/loggers.go diff --git a/.circleci/bin/hadolint.sh b/.circleci/bin/hadolint.sh new file mode 100755 index 00000000..af9a599a --- /dev/null +++ b/.circleci/bin/hadolint.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -ex + +git ls-files --exclude='Dockerfile*' --ignored | xargs ./bin/hadolint \ + | ./bin/reviewdog -efm="%f:%l %m" -diff="git diff ${DEFAULT_BRANCH}..HEAD" -reporter=github-pr-review diff --git a/.circleci/bin/install_tools.sh b/.circleci/bin/install_tools.sh new file mode 100755 index 00000000..f6504c25 --- /dev/null +++ b/.circleci/bin/install_tools.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -ex + +HADOLINT_VERSION=v1.22.1 +REVIEWDOG_VERSION=v0.11.0 +GOLANGCILINT_VERSION=v1.36.0 +DOTENV_LINTER_VERSION=v3.0.0 + +go get -u golang.org/x/lint/golint + +# Install reviewdog +wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh \ + | sh -s -- -b ./bin $REVIEWDOG_VERSION + +# Install golangci-lint +wget -O- -nv https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s $GOLANGCILINT_VERSION + +# Install hadolint +wget -q https://github.com/hadolint/hadolint/releases/download/$HADOLINT_VERSION/hadolint-Linux-x86_64 \ + -O ./bin/hadolint && chmod +x ./bin/hadolint + +# Install dotenv-linter +wget -q -O - https://git.io/JLbXn | sh -s -- -b bin $DOTENV_LINTER_VERSION diff --git a/.circleci/bin/reviewdog.sh b/.circleci/bin/reviewdog.sh new file mode 100755 index 00000000..da2a3d8e --- /dev/null +++ b/.circleci/bin/reviewdog.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +set -ex +./bin/reviewdog -diff="git diff ${DEFAULT_BRANCH}..HEAD" -reporter=github-pr-review diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..d8b34255 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,95 @@ +version: 2.1 + +parameters: + service_name: + type: string + default: "go-service-template" + image: + type: string + default: "evrone/go-service-template" + +orbs: + docker: circleci/docker@0.5.20 + +executors: + default: + docker: + - image: circleci/golang:1.15 + +aliases: + - &restore-mod-cache + keys: + - go-modules-v1-{{ checksum "go.sum" }} + - go-modules-v1- + + - &save-mod-cache + key: go-modules-v1-{{ checksum "go.sum" }} + paths: + - "/go/pkg/mod" + +jobs: + linters: + executor: + name: default + steps: + - checkout + - run: + name: Install tools + command: .circleci/bin/install_tools.sh + - run: + name: Run Reviewdog + command: .circleci/bin/reviewdog.sh + - run: + name: Run Hadolint + command: .circleci/bin/hadolint.sh + tests: + executor: + name: default + steps: + - checkout + - restore_cache: *restore-mod-cache + - run: + name: Tests + command: go test ./... + - save_cache: *save-mod-cache + +workflows: + ci: + jobs: + - linters + - tests + # Build staging image + - docker/publish: + context: evrone-erp + image: << pipeline.parameters.image >> + tag: "staging-${CIRCLE_SHA1}" + requires: + - linters + - tests + filters: + branches: + only: + - develop + # Build QA image + - docker/publish: + context: evrone-erp + image: << pipeline.parameters.image >> + tag: "qa-${CIRCLE_SHA1}" + requires: + - linters + - tests + filters: + branches: + only: + - master + - /release\/.*/ + # Build production image + - docker/publish: + context: evrone-erp + image: << pipeline.parameters.image >> + tag: "$CIRCLE_TAG" + filters: + tags: + only: /^v.*/ + branches: + ignore: /.*/ diff --git a/.env b/.env index 670beb4a..e3e1fc9f 100644 --- a/.env +++ b/.env @@ -1,5 +1,8 @@ GIN_MODE = release +SERVICE_NAME = go-service-template +SERVICE_VERSION = 0.0.0 + ZAP_LOG_LEVEL = debug ROLLBAR_ACCESS_TOKEN = 97c783270fd14bf6a3c76ab524ee8839 ROLLBAR_ENVIRONMENT = go-template-test @@ -8,7 +11,7 @@ HTTP_API_PORT = 8080 PG_URL = postgres://user:pass@localhost:5432/postgres PG_POOL_MAX = 1 -PG_CONN_ATTEMPTS = 5 +PG_CONN_ATTEMPTS = 10 RMQ_URL = amqp://guest:guest@localhost:5672/ RMQ_QUEUE_NAME = test_queue \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index b01b9026..54566211 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,11 +13,13 @@ COPY --from=modules /go/pkg /go/pkg COPY . /app WORKDIR /app -RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /bin/app ./cmd/app +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -tags migrate -o /bin/app ./cmd/app # Step 3: Final FROM scratch +COPY --from=builder /app/migrations /migrations COPY --from=builder /bin/app /app COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -CMD ["/app"] +CMD ["/app"] \ No newline at end of file diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 00000000..867a4b1b --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,11 @@ +# Step 1: Builder +FROM golang:1.15.7-alpine3.13 as builder +COPY . /app +WORKDIR /app +RUN go mod download -x && \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -x -o /bin/app ./cmd/app + +# Step 2: Final +FROM alpine:3.13 +COPY --from=builder /bin/app /app +CMD ["/app"] \ No newline at end of file diff --git a/Makefile b/Makefile index ee070c94..ff4338af 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,32 @@ include .env export +swag: + swag init -g internal/app/app.go + run: swag go mod download && GIN_MODE=debug CGO_ENABLED=0 go run ./cmd/app +run-with-migrate: + go mod download && GIN_MODE=debug CGO_ENABLED=0 go run -tags migrate ./cmd/app + +compose-up-db: + docker-compose up --build -d --remove-orphans db && docker-compose logs -f + compose-up: docker-compose up --build -d --remove-orphans && docker-compose logs -f compose-down: docker-compose down --remove-orphans +lint: + golangci-lint run + test: - go test -v -cover -race ./... + go test -v -cover -race ./internal/... + +integration-test: + go clean -testcache && go test ./integration_test/... mock: mockery --all -r --case snake @@ -22,7 +37,5 @@ migrate-create: migrate: migrate -path migrations -database '$(PG_URL)?sslmode=disable' up -swag: - swag init -g internal/app/app.go - -.PHONY: run, compose-up, compose-down, test, mock, migrate-create, migrate, swag \ No newline at end of file +.PHONY: swag, run, run-with-migrate, compose-up-db, compose-up, compose-down +.PHONY: lint, test, integration-test, mock, migrate-create, migrate \ No newline at end of file diff --git a/README.md b/README.md index 1453c14f..8ba89ae9 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,40 @@ Шаблон микросервиса на Go =========================================== -Цель: -1. Предоставить готовый шаблон для быстрого развертывания -2. Стандартизировать подходы к организации кода -3. Аккумулировать лучшие практики - С развитием любого проекта, логика работы приложения усложняется и запутывается. Разработка новых фич замедляется. Если не планировать архитектуру, довольно быстро приходит время, когда переписать сервис становится дешевле, чем делать изменения в существующем. +Цели: +1. Предоставить готовый шаблон для быстрого развертывания +2. Стандартизировать подходы к организации кода +3. Аккумулировать лучшие практики +Quick start +---- +Локальная разработка: +```sh +# DB container +$ make compose-up-db +# App local +$ make run +``` + +В контейнерах: +```sh +# DB and app containers +$ make compose-up +``` + +Линтеры, тесты: +```sh +# Linters +$ make lint +# Unit tests +$ make test +# REST API tests +$ make integration-test +``` Clean Architecture ---- diff --git a/docker-compose.yml b/docker-compose.yml index 52423b32..e18a7b2c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,18 +1,18 @@ version: '3.9' services: -# app: -# build: . -# container_name: app -# image: app -# env_file: -# - ./.env -# environment: -# PG_URL: 'postgres://user:pass@db:5432/postgres' -# ports: -# - 8080:8080 # api -# depends_on: -# - db + app: + build: . + container_name: app + image: app + env_file: + - .env + environment: + PG_URL: 'postgres://user:pass@db:5432/postgres' + ports: + - 8080:8080 + depends_on: + - db db: container_name: postgres diff --git a/docs/docs.go b/docs/docs.go index e2905734..8ea50cca 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -41,11 +41,11 @@ var doc = `{ "parameters": [ { "description": "Set up translation", - "name": "input", + "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v1.translationInput" + "$ref": "#/definitions/v1.doTranslateRequest" } } ], @@ -59,7 +59,7 @@ var doc = `{ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/v1.errorResponse" + "$ref": "#/definitions/v1.response" } } } @@ -83,16 +83,13 @@ var doc = `{ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.Translation" - } + "$ref": "#/definitions/v1.historyResponse" } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/v1.errorResponse" + "$ref": "#/definitions/v1.response" } } } @@ -121,16 +118,7 @@ var doc = `{ } } }, - "v1.errorResponse": { - "type": "object", - "properties": { - "error": { - "type": "string", - "example": "message" - } - } - }, - "v1.translationInput": { + "v1.doTranslateRequest": { "type": "object", "required": [ "destination", @@ -151,6 +139,26 @@ var doc = `{ "example": "auto" } } + }, + "v1.historyResponse": { + "type": "object", + "properties": { + "history": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Translation" + } + } + } + }, + "v1.response": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "message" + } + } } } }` diff --git a/docs/swagger.json b/docs/swagger.json index d3b87e10..2436b230 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -26,11 +26,11 @@ "parameters": [ { "description": "Set up translation", - "name": "input", + "name": "request", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/v1.translationInput" + "$ref": "#/definitions/v1.doTranslateRequest" } } ], @@ -44,7 +44,7 @@ "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/v1.errorResponse" + "$ref": "#/definitions/v1.response" } } } @@ -68,16 +68,13 @@ "200": { "description": "OK", "schema": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.Translation" - } + "$ref": "#/definitions/v1.historyResponse" } }, "400": { "description": "Bad Request", "schema": { - "$ref": "#/definitions/v1.errorResponse" + "$ref": "#/definitions/v1.response" } } } @@ -106,16 +103,7 @@ } } }, - "v1.errorResponse": { - "type": "object", - "properties": { - "error": { - "type": "string", - "example": "message" - } - } - }, - "v1.translationInput": { + "v1.doTranslateRequest": { "type": "object", "required": [ "destination", @@ -136,6 +124,26 @@ "example": "auto" } } + }, + "v1.historyResponse": { + "type": "object", + "properties": { + "history": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.Translation" + } + } + } + }, + "v1.response": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "message" + } + } } } } \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 43b67bf1..2fc36b94 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -15,13 +15,7 @@ definitions: example: text for translation type: string type: object - v1.errorResponse: - properties: - error: - example: message - type: string - type: object - v1.translationInput: + v1.doTranslateRequest: properties: destination: example: en @@ -37,6 +31,19 @@ definitions: - original - source type: object + v1.historyResponse: + properties: + history: + items: + $ref: '#/definitions/domain.Translation' + type: array + type: object + v1.response: + properties: + error: + example: message + type: string + type: object host: localhost:8080 info: contact: {} @@ -53,10 +60,10 @@ paths: parameters: - description: Set up translation in: body - name: input + name: request required: true schema: - $ref: '#/definitions/v1.translationInput' + $ref: '#/definitions/v1.doTranslateRequest' produces: - application/json responses: @@ -67,7 +74,7 @@ paths: "400": description: Bad Request schema: - $ref: '#/definitions/v1.errorResponse' + $ref: '#/definitions/v1.response' summary: Translate tags: - translation @@ -83,13 +90,11 @@ paths: "200": description: OK schema: - items: - $ref: '#/definitions/domain.Translation' - type: array + $ref: '#/definitions/v1.historyResponse' "400": description: Bad Request schema: - $ref: '#/definitions/v1.errorResponse' + $ref: '#/definitions/v1.response' summary: Show history tags: - translation diff --git a/go.mod b/go.mod index 9b6231ab..3f58774b 100644 --- a/go.mod +++ b/go.mod @@ -4,21 +4,26 @@ go 1.15 require ( github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb + github.com/Eun/go-convert v0.0.0-20200813135915-a28ba853dd5b // indirect + github.com/Eun/go-hit v0.5.20 github.com/Masterminds/squirrel v1.5.0 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 + github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e // indirect github.com/gin-gonic/gin v1.6.3 github.com/go-openapi/spec v0.20.3 // indirect + github.com/golang-migrate/migrate/v4 v4.14.1 github.com/golang/protobuf v1.4.3 // indirect - github.com/google/go-cmp v0.5.1 // indirect github.com/jackc/pgx/v4 v4.10.1 github.com/lib/pq v1.8.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rollbar/rollbar-go v1.2.0 github.com/stretchr/testify v1.7.0 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 github.com/swaggo/gin-swagger v1.3.0 github.com/swaggo/swag v1.7.0 + github.com/tidwall/pretty v1.0.5 // indirect go.uber.org/zap v1.10.0 golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect diff --git a/go.sum b/go.sum index 06220727..0c7a1a23 100644 --- a/go.sum +++ b/go.sum @@ -1,35 +1,118 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg= +cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/spanner v1.9.0/go.mod h1:xvlEn0NZ5v1iJPYsBnUVRDNvccDxsBTEi16pJRKQVws= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb h1:MwdqXipO1SFnK2k1iIL8xlUkZC63KdZR6QB/DSIlYBk= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb/go.mod h1:J9k1GS4/KUFdJgWCp8Od4J3XXalm3pUPm8YYrMFzYTk= +github.com/Eun/go-convert v0.0.0-20200421145326-bef6c56666ee h1:9oCc9EfVVSuy2WoHLAEYppJ5zX45+MQhAU1W30Uu3SI= +github.com/Eun/go-convert v0.0.0-20200421145326-bef6c56666ee/go.mod h1:cMqWKb0SQrV+L1Zve08CI1NQGPeRAjXuYTxYE/y6gcU= +github.com/Eun/go-convert v0.0.0-20200813135915-a28ba853dd5b h1:xUFPNiC2U7rKAiRfR3E5wrbRehcy96fGFaaUa7ZEDOY= +github.com/Eun/go-convert v0.0.0-20200813135915-a28ba853dd5b/go.mod h1:cMqWKb0SQrV+L1Zve08CI1NQGPeRAjXuYTxYE/y6gcU= +github.com/Eun/go-doppelgangerreader v0.0.0-20190911075941-30f1527f16b2 h1:RfkLLL7sQdxTMWRLo//6CZcAN3j5/laO8BooS9ctG2g= +github.com/Eun/go-doppelgangerreader v0.0.0-20190911075941-30f1527f16b2/go.mod h1:+o+i8cYK1XYOQo4ocUKNV4R9D5Y7MIAPJk2l5SEh93M= +github.com/Eun/go-hit v0.5.20 h1:vzZJAG8pF02OZSpljASBb8HlqK3b4f8pSPnOuX9Qip0= +github.com/Eun/go-hit v0.5.20/go.mod h1:UTxCIcWC8T2FxF0ul+uHUgQHOcsdbaKOarFsnPsOHOo= +github.com/Eun/go-testdoc v0.0.1/go.mod h1:uT+GeDi7TpqQx6MBkcfXD9nF15Q8IX+kTNEnUUPbuUo= +github.com/Eun/yaegi-template v1.5.16/go.mod h1:eyFQ1QHbKLNHKpUvdjt8+99ZR1ji7lVVbduSK1M5N/U= +github.com/Eun/yaegi-template v1.5.18/go.mod h1:iVHjge496SWL7hLf1euBZIO40Bk0R38g6lu8iyvpc30= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/aaw/maybe_tls v0.0.0-20160803104303-89c499bcc6aa/go.mod h1:I0wzMZvViQzmJjxK+AtfFAnqDCkQV/+r17PO1CCSYnU= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= +github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI= +github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1 h1:TEBmxO80TM04L8IuMWk77SGL1HomBmKTdzdJLLWznxI= +github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI= +github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e h1:OjdSMCht0ZVX7IH0nTdf00xEustvbtUGRgMh3gbdmOg= +github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= +github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= +github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= @@ -42,6 +125,9 @@ github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6 github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -72,32 +158,102 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-migrate/migrate v1.3.2 h1:QAlFV1QF9zdkzy/jujlBVkVu+L/+k18cg8tuY1/4JDY= +github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA= +github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE= +github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gookit/color v1.3.7 h1:wtJz526qll4+TxVcUUaMJOzrXDCiPr6jlyB5VSOBd2U= +github.com/gookit/color v1.3.7/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3 h1:l7vogWrq+zj8v5t/G69/eT13nAGs2H7cq+CI2nlnKdk= +github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3/go.mod h1:296z3W7Xsrp2mlIY88ruDKscuvrkL6zXCNRtaYVshzw= +github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA= +github.com/itchyny/gojq v0.12.1 h1:pQJrG8LXgEbZe9hvpfjKg7UlBfieQQydIw3YQq+7WIA= +github.com/itchyny/gojq v0.12.1/go.mod h1:Y5Lz0qoT54ii+ucY/K3yNDy19qzxZvWNBMBpKUDQR/4= +github.com/itchyny/timefmt-go v0.1.1 h1:rLpnm9xxb39PEEVzO0n4IRp0q6/RmBc7Dy/rE4HrA0U= +github.com/itchyny/timefmt-go v0.1.1/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -106,6 +262,7 @@ github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgO github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.3.2/go.mod h1:LvCquS3HbBKwgl7KbX9KyqEIumJAbm1UMcTvGaIf3bM= github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk= github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI= @@ -151,15 +308,28 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= 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/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= +github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -168,6 +338,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -180,6 +351,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.8.0 h1:9xohqzkUwzR4Ga4ivdTcawVS89YSDVxXMa3xJX3cGzg= github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lunixbochs/vtclean v1.0.0 h1:xu2sLAri4lGiovBDQKxl5mrXyESr3gUr5m5SM5+LVb8= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= @@ -187,9 +360,16 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN 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/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= +github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -197,6 +377,11 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -205,8 +390,20 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= +github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= +github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= 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= @@ -214,6 +411,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rollbar/rollbar-go v1.2.0 h1:CUanFtVu0sa3QZ/fBlgevdGQGLWaE3D4HxoVSQohDfo= github.com/rollbar/rollbar-go v1.2.0/go.mod h1:czC86b8U4xdUH7W2C6gomi2jutLm8qK0OtrF5WMvpcc= @@ -223,6 +422,7 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -230,11 +430,16 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0= +github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU= github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -249,6 +454,13 @@ github.com/swaggo/gin-swagger v1.3.0/go.mod h1:oy1BRA6WvgtCp848lhxce7BnWH4C8Bxa0 github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E= github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo= +github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/pretty v1.0.5 h1:RlE8xFRj9RrbHWXXNGa4MymvI1Ovlm70omjXQ1sWgFU= +github.com/tidwall/pretty v1.0.5/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/traefik/yaegi v0.9.8/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= +github.com/traefik/yaegi v0.9.10/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -262,12 +474,29 @@ github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCB github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= +go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -277,70 +506,162 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190225153610-fe579d43d832/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -348,20 +669,63 @@ golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200818005847-188abfa75333/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201026223136-e84cfc6dd5ca/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e h1:t96dS3DO8DGjawSLJL/HIdz8CycAd2v07XxqB3UPTi0= golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= @@ -374,14 +738,76 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -390,6 +816,7 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -398,12 +825,17 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= @@ -414,7 +846,29 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= +modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= +modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= +modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= +modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= +modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= +modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= +modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= +modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/integration_test/rest_api_test.go b/integration_test/rest_api_test.go new file mode 100644 index 00000000..97a6ac63 --- /dev/null +++ b/integration_test/rest_api_test.go @@ -0,0 +1,52 @@ +package rest_api_test + +import ( + "net/http" + "os" + "testing" + + . "github.com/Eun/go-hit" +) + +func TestMain(m *testing.M) { + code := m.Run() + os.Exit(code) +} + +func TestDoTranslate(t *testing.T) { + body := `{ + "destination": "en", + "original": "текст для перевода", + "source": "auto" + }` + Test(t, + Description("DoTranslate Success"), + Post("http://localhost:8080/api/v1/translation/do-translate"), + Send().Headers("Content-Type").Add("application/json"), + Send().Body().String(body), + Expect().Status().Equal(http.StatusOK), + Expect().Body().JSON().JQ(".translation").Equal("text for translation"), + ) + + body = `{ + "destination": "en", + "original": "текст для перевода", + }` + Test(t, + Description("DoTranslate Fail"), + Post("http://localhost:8080/api/v1/translation/do-translate"), + Send().Headers("Content-Type").Add("application/json"), + Send().Body().String(body), + Expect().Status().Equal(http.StatusBadRequest), + Expect().Body().JSON().JQ(".error").Equal("invalid request body"), + ) +} + +func TestHistory(t *testing.T) { + Test(t, + Description("History Success"), + Get("http://localhost:8080/api/v1/translation/history"), + Expect().Status().Equal(http.StatusOK), + Expect().Body().String().Contains(`{"history":[{`), + ) +} diff --git a/internal/app/app.go b/internal/app/app.go index 239151f6..e12db2ed 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -6,18 +6,16 @@ import ( "os/signal" "syscall" + v2 "github.com/evrone/go-service-template/internal/delivery/http/v2" + "github.com/gin-gonic/gin" "github.com/evrone/go-service-template/internal/delivery/http/v1" - "github.com/evrone/go-service-template/internal/delivery/http/v2" - - "github.com/evrone/go-service-template/internal/domain" - "github.com/evrone/go-service-template/pkg/logger" + "github.com/evrone/go-service-template/internal/repository" "github.com/evrone/go-service-template/internal/service" - "github.com/evrone/go-service-template/internal/subservice/repository" - "github.com/evrone/go-service-template/internal/subservice/webapi" + "github.com/evrone/go-service-template/internal/webapi" "github.com/evrone/go-service-template/pkg/postgres" "github.com/evrone/go-service-template/pkg/httpserver" @@ -40,26 +38,26 @@ func Run() { defer zap.Close() rollbar := logger.NewRollbarLogger(conf.RollbarAccessToken, conf.RollbarEnvironment) defer rollbar.Close() - domain.Logger = logger.NewAppLogger(zap, rollbar) + logger.NewAppLogger(zap, rollbar, conf.ServiceName, conf.ServiceVersion) // Repository - pg := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) - pgRepository := repository.NewPostgresEntityRepository(pg) - defer pg.Close() + postgresDB := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) + translationRepository := repository.NewTranslationRepository(postgresDB) + defer postgresDB.Close() // WebAPI - googleTranslateAPI := webapi.NewGoogleTranslator() + translationWebAPI := webapi.NewTranslationWebAPI() // Service - entityUseCase := service.NewUseCase(pgRepository, googleTranslateAPI) + translationService := service.NewTranslationService(translationRepository, translationWebAPI) - // Router + // REST handler := gin.Default() handler.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger handler.GET("/health", func(c *gin.Context) { c.Status(http.StatusOK) }) // K8s probe - v1.NewAPIRouter(handler, entityUseCase) - v2.NewAPIRouter(handler) + v1.NewRouter(handler, translationService) + v2.NewRouter(handler) server := httpserver.NewServer(handler, conf.HttpApiPort) server.Start() @@ -70,13 +68,13 @@ func Run() { select { case s := <-interrupt: - domain.Logger.Info("main - signal: " + s.String()) + logger.Info("app - Run - signal: " + s.String()) case err := <-server.Notify(): - domain.Logger.Error(err, "main - server.Notify") + logger.Error(err, "app - Run - server.Notify") } err := server.Stop() if err != nil { - domain.Logger.Error(err, "main - server.Stop") + logger.Error(err, "app - Run - server.Stop") } } diff --git a/internal/app/configuration.go b/internal/app/configuration.go index 0b61e708..73ed7a2b 100644 --- a/internal/app/configuration.go +++ b/internal/app/configuration.go @@ -7,6 +7,8 @@ import ( ) type Conf struct { + ServiceName string + ServiceVersion string ZapLogLevel string RollbarAccessToken string RollbarEnvironment string @@ -20,6 +22,8 @@ type Conf struct { func NewConfig() Conf { return Conf{ + ServiceName: strEnv("SERVICE_NAME"), + ServiceVersion: strEnv("SERVICE_VERSION"), ZapLogLevel: strEnv("ZAP_LOG_LEVEL"), RollbarAccessToken: strEnv("ROLLBAR_ACCESS_TOKEN"), RollbarEnvironment: strEnv("ROLLBAR_ENVIRONMENT"), diff --git a/internal/app/migrate.go b/internal/app/migrate.go new file mode 100644 index 00000000..ca18d830 --- /dev/null +++ b/internal/app/migrate.go @@ -0,0 +1,40 @@ +// +build migrate + +package app + +import ( + "errors" + "log" + "time" + + "github.com/golang-migrate/migrate/v4" + _ "github.com/golang-migrate/migrate/v4/database/postgres" + _ "github.com/golang-migrate/migrate/v4/source/file" +) + +func init() { + var err error + var m *migrate.Migrate + + attempts := 10 + for attempts > 0 { + m, err = migrate.New("file://migrations", + "postgres://user:pass@db:5432/postgres?sslmode=disable") + if err == nil { + break + } + log.Printf("Migrate: postgres is trying to connect, attempts left: %d", attempts) + time.Sleep(time.Second * 1) + attempts-- + } + + if err != nil { + log.Fatalf("Migrate: postgres connect error: %s", err) + } + + err = m.Up() + if err != nil && !errors.Is(err, migrate.ErrNoChange) { + log.Fatalf("Migrate: up error: %s", err) + } + m.Close() +} diff --git a/internal/delivery/http/v1/error.go b/internal/delivery/http/v1/error.go index bb7c26e9..054b7150 100644 --- a/internal/delivery/http/v1/error.go +++ b/internal/delivery/http/v1/error.go @@ -1,6 +1,19 @@ package v1 -// TODO -type errorResponse struct { +import ( + "github.com/evrone/go-service-template/pkg/logger" + "github.com/gin-gonic/gin" +) + +type response struct { Error string `json:"error" example:"message"` } + +func errorResponse(c *gin.Context, code int, err error, msg string) { + logger.Error(err, "http - v1 - errorResponse", + logger.Field{Key: "path", Val: c.FullPath()}, + logger.Field{Key: "request_method", Val: c.Request.Method}, + logger.Field{Key: "response_code", Val: code}, + ) + c.AbortWithStatusJSON(code, response{msg}) +} diff --git a/internal/delivery/http/v1/handler.go b/internal/delivery/http/v1/router.go similarity index 77% rename from internal/delivery/http/v1/handler.go rename to internal/delivery/http/v1/router.go index 53713041..355f2ba8 100644 --- a/internal/delivery/http/v1/handler.go +++ b/internal/delivery/http/v1/router.go @@ -9,7 +9,7 @@ type router struct { translationService service.Translation } -func NewAPIRouter(handler *gin.Engine, translationService service.Translation) { +func NewRouter(handler *gin.Engine, translationService service.Translation) { r := &router{translationService} api := handler.Group("/api/v1") diff --git a/internal/delivery/http/v1/translation.go b/internal/delivery/http/v1/translation.go index e61ccbf1..a9c8f666 100644 --- a/internal/delivery/http/v1/translation.go +++ b/internal/delivery/http/v1/translation.go @@ -17,7 +17,9 @@ func (r *router) translationRouts(api *gin.RouterGroup) { } } -// TODO +type historyResponse struct { + History []domain.Translation `json:"history"` +} // @Summary Show history // @Description Show all translation history @@ -25,20 +27,20 @@ func (r *router) translationRouts(api *gin.RouterGroup) { // @Tags translation // @Accept json // @Produce json -// @Success 200 {array} domain.Translation -// @Failure 400 {object} errorResponse +// @Success 200 {object} historyResponse +// @Failure 400 {object} response // @Router /translation/history [get] func (r *router) history(c *gin.Context) { - entities, err := r.translationService.History() + translations, err := r.translationService.History() if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + errorResponse(c, http.StatusBadRequest, err, "database problems") return } - c.JSON(http.StatusOK, gin.H{"history": entities}) + c.JSON(http.StatusOK, historyResponse{translations}) } -type translationInput struct { +type doTranslateRequest struct { Source string `json:"source" binding:"required" example:"auto"` Destination string `json:"destination" binding:"required" example:"en"` Original string `json:"original" binding:"required" example:"текст для перевода"` @@ -50,26 +52,26 @@ type translationInput struct { // @Tags translation // @Accept json // @Produce json -// @Param input body translationInput true "Set up translation" +// @Param request body doTranslateRequest true "Set up translation" // @Success 200 {object} domain.Translation -// @Failure 400 {object} errorResponse +// @Failure 400 {object} response // @Router /translation/do-translate [post] func (r *router) doTranslate(c *gin.Context) { - var input translationInput - if err := c.ShouldBindJSON(&input); err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + var request doTranslateRequest + if err := c.ShouldBindJSON(&request); err != nil { + errorResponse(c, http.StatusBadRequest, err, "invalid request body") return } - translationResponse, err := r.translationService.DoTranslate(domain.Translation{ - Source: input.Source, - Destination: input.Destination, - Original: input.Original, + translation, err := r.translationService.Translate(domain.Translation{ + Source: request.Source, + Destination: request.Destination, + Original: request.Original, }) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + errorResponse(c, http.StatusBadRequest, err, "translation service problems") return } - c.JSON(http.StatusOK, translationResponse) + c.JSON(http.StatusOK, translation) } diff --git a/internal/delivery/http/v2/handler.go b/internal/delivery/http/v2/router.go similarity index 70% rename from internal/delivery/http/v2/handler.go rename to internal/delivery/http/v2/router.go index 8c1306b0..fde4ab86 100644 --- a/internal/delivery/http/v2/handler.go +++ b/internal/delivery/http/v2/router.go @@ -8,8 +8,8 @@ import ( type router struct{} -func NewAPIRouter(handler *gin.Engine) { - api := handler.Group("/api/v2") +func NewRouter(handler *gin.Engine) { + api := handler.Group("/api/test") { api.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, "Hello from v2") }) } diff --git a/internal/domain/logger.go b/internal/domain/logger.go deleted file mode 100644 index 93fc6665..00000000 --- a/internal/domain/logger.go +++ /dev/null @@ -1,16 +0,0 @@ -package domain - -var Logger Log - -type Log interface { - Debug(msg string, fields ...Field) - Info(msg string, fields ...Field) - Warn(msg string, fields ...Field) - Error(err error, msg string, fields ...Field) - Fatal(err error, msg string, fields ...Field) -} - -type Field struct { - Key string - Val interface{} -} diff --git a/internal/subservice/repository/interface.go b/internal/repository/interface.go similarity index 100% rename from internal/subservice/repository/interface.go rename to internal/repository/interface.go diff --git a/internal/subservice/repository/translation_postgres.go b/internal/repository/translation_postgres.go similarity index 54% rename from internal/subservice/repository/translation_postgres.go rename to internal/repository/translation_postgres.go index d65031b8..c8842c0d 100644 --- a/internal/subservice/repository/translation_postgres.go +++ b/internal/repository/translation_postgres.go @@ -2,32 +2,33 @@ package repository import ( "context" + "fmt" "github.com/evrone/go-service-template/internal/domain" "github.com/evrone/go-service-template/pkg/postgres" ) -type postgresEntityRepository struct { - postgres.Postgres +type translationRepository struct { + *postgres.Postgres } -func NewPostgresEntityRepository(pg postgres.Postgres) Translation { - return &postgresEntityRepository{pg} +func NewTranslationRepository(pg *postgres.Postgres) Translation { + return &translationRepository{pg} } -func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Translation, error) { +func (p *translationRepository) GetHistory(ctx context.Context) ([]domain.Translation, error) { sql, _, err := p.Builder. Select("source, destination, original, translation"). From("history"). ToSql() if err != nil { - return nil, err + return nil, fmt.Errorf("translationRepository - GetHistory - p.Builder: %w", err) } rows, err := p.Pool.Query(ctx, sql) if err != nil { - return nil, err + return nil, fmt.Errorf("translationRepository - GetHistory - p.Pool.Query: %w", err) } defer rows.Close() @@ -36,7 +37,7 @@ func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Tra e := domain.Translation{} err = rows.Scan(&e.Source, &e.Destination, &e.Original, &e.Translation) if err != nil { - return nil, err + return nil, fmt.Errorf("translationRepository - GetHistory - rows.Scan: %w", err) } entities = append(entities, e) } @@ -44,19 +45,19 @@ func (p *postgresEntityRepository) GetHistory(ctx context.Context) ([]domain.Tra return entities, nil } -func (p *postgresEntityRepository) Store(ctx context.Context, entity domain.Translation) error { +func (p *translationRepository) Store(ctx context.Context, entity domain.Translation) error { sql, args, err := p.Builder. Insert("history"). Columns("source, destination, original, translation"). Values(entity.Source, entity.Destination, entity.Original, entity.Translation). ToSql() if err != nil { - return err + return fmt.Errorf("translationRepository - Store - p.Builder: %w", err) } _, err = p.Pool.Exec(ctx, sql, args...) if err != nil { - return err + return fmt.Errorf("translationRepository - Store - p.Pool.Exec: %w", err) } return nil diff --git a/internal/service/interface.go b/internal/service/interface.go index 26eef08c..474f5e54 100644 --- a/internal/service/interface.go +++ b/internal/service/interface.go @@ -3,6 +3,6 @@ package service import "github.com/evrone/go-service-template/internal/domain" type Translation interface { - DoTranslate(entity domain.Translation) (domain.Translation, error) + Translate(entity domain.Translation) (domain.Translation, error) History() ([]domain.Translation, error) } diff --git a/internal/service/translation.go b/internal/service/translation.go index a658acd9..c5543d08 100644 --- a/internal/service/translation.go +++ b/internal/service/translation.go @@ -2,50 +2,46 @@ package service import ( "context" + "fmt" - "github.com/evrone/go-service-template/internal/subservice/repository" + "github.com/evrone/go-service-template/internal/repository" - "github.com/evrone/go-service-template/internal/subservice/webapi" + "github.com/evrone/go-service-template/internal/webapi" "github.com/evrone/go-service-template/internal/domain" ) -type useCase struct { - translationRepo repository.Translation - translationWebAPI webapi.Translation +type translationService struct { + repository repository.Translation + webAPI webapi.Translation } -func NewUseCase(repo repository.Translation, api webapi.Translation) Translation { - return &useCase{ - translationRepo: repo, - translationWebAPI: api, +func NewTranslationService(repository repository.Translation, webAPI webapi.Translation) Translation { + return &translationService{ + repository: repository, + webAPI: webAPI, } } -func (u *useCase) History() ([]domain.Translation, error) { - entities, err := u.translationRepo.GetHistory(context.Background()) +func (u *translationService) History() ([]domain.Translation, error) { + translations, err := u.repository.GetHistory(context.Background()) if err != nil { - domain.Logger.Error(err, "History - translationRepo.GetHistory", - domain.Field{Key: "key", Val: "value"}, - ) - return nil, err + return nil, fmt.Errorf("translationService - History - u.repository.GetHistory: %w", err) } - return entities, nil + return translations, nil } -func (u *useCase) DoTranslate(entity domain.Translation) (domain.Translation, error) { - entity, err := u.translationWebAPI.Translate(entity) +func (u *translationService) Translate(translation domain.Translation) (domain.Translation, error) { + translation, err := u.webAPI.Translate(translation) if err != nil { - domain.Logger.Error(err, "DoTranslate - translationWebAPI.Translate") - return domain.Translation{}, err + return domain.Translation{}, fmt.Errorf("translationService - Translate - u.webAPI.Translate: %w", err) } - err = u.translationRepo.Store(context.Background(), entity) + err = u.repository.Store(context.Background(), translation) if err != nil { - domain.Logger.Error(err, "DoTranslate - translationRepo.Store") - return domain.Translation{}, err + return domain.Translation{}, fmt.Errorf("translationService - Translate - u.repository.Store: %w", err) } - return entity, nil + return translation, nil } diff --git a/internal/subservice/webapi/translation_google.go b/internal/subservice/webapi/translation_google.go deleted file mode 100644 index fa161c9b..00000000 --- a/internal/subservice/webapi/translation_google.go +++ /dev/null @@ -1,28 +0,0 @@ -package webapi - -import ( - translator "github.com/Conight/go-googletrans" - "github.com/evrone/go-service-template/internal/domain" -) - -type GoogleTranslator struct{} - -func NewGoogleTranslator() *GoogleTranslator { - return &GoogleTranslator{} -} - -func (y *GoogleTranslator) Translate(entity domain.Translation) (domain.Translation, error) { - c := translator.Config{ - UserAgent: []string{"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"}, - ServiceUrls: []string{"translate.google.as"}, - } - t := translator.New(c) - result, err := t.Translate(entity.Original, entity.Source, entity.Destination) - if err != nil { - return domain.Translation{}, err - } - - entity.Translation = result.Text - - return entity, nil -} diff --git a/internal/subservice/webapi/interface.go b/internal/webapi/interface.go similarity index 100% rename from internal/subservice/webapi/interface.go rename to internal/webapi/interface.go diff --git a/internal/webapi/translation_google.go b/internal/webapi/translation_google.go new file mode 100644 index 00000000..995caded --- /dev/null +++ b/internal/webapi/translation_google.go @@ -0,0 +1,32 @@ +package webapi + +import ( + "fmt" + + translator "github.com/Conight/go-googletrans" + "github.com/evrone/go-service-template/internal/domain" +) + +type translationWebAPI struct { + conf translator.Config +} + +func NewTranslationWebAPI() Translation { + conf := translator.Config{ + UserAgent: []string{"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"}, + ServiceUrls: []string{"translate.google.com"}, + } + return &translationWebAPI{conf} +} + +func (t *translationWebAPI) Translate(translation domain.Translation) (domain.Translation, error) { + trans := translator.New(t.conf) + result, err := trans.Translate(translation.Original, translation.Source, translation.Destination) + if err != nil { + return domain.Translation{}, fmt.Errorf("translationWebAPI - Translate - trans.Translate: %w", err) + } + + translation.Translation = result.Text + + return translation, nil +} diff --git a/pkg/logger/interface.go b/pkg/logger/interface.go new file mode 100644 index 00000000..8611f7e0 --- /dev/null +++ b/pkg/logger/interface.go @@ -0,0 +1,14 @@ +package logger + +type Logger interface { + debug(msg string, fields ...Field) + info(msg string, fields ...Field) + warn(msg string, fields ...Field) + error(err error, msg string, fields ...Field) + fatal(err error, msg string, fields ...Field) +} + +type Field struct { + Key string + Val interface{} +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go deleted file mode 100644 index 7528f304..00000000 --- a/pkg/logger/logger.go +++ /dev/null @@ -1,93 +0,0 @@ -package logger - -import ( - "fmt" - - "github.com/evrone/go-service-template/internal/domain" - - "github.com/rollbar/rollbar-go" - - "go.uber.org/zap" -) - -type appLogger struct { - zap *ZapLogger - rollbar *RollbarLogger -} - -func NewAppLogger(zap *ZapLogger, rollbar *RollbarLogger) domain.Log { - return &appLogger{zap, rollbar} -} - -func (l *appLogger) Debug(msg string, fields ...domain.Field) { - if fields == nil { - l.zap.Debug(msg) - return - } - - l.zap.Debug(msg, zapFields(fields)...) -} - -func (l *appLogger) Info(msg string, fields ...domain.Field) { - if fields == nil { - l.zap.Info(msg) - return - } - - l.zap.Info(msg, zapFields(fields)...) -} - -func (l *appLogger) Warn(msg string, fields ...domain.Field) { - if fields == nil { - l.rollbar.Message(rollbar.WARN, msg) - l.zap.Warn(msg) - return - } - - l.rollbar.MessageWithExtras(rollbar.WARN, msg, rollbarMap(fields)) - l.zap.Warn(msg, zapFields(fields)...) -} - -func (l *appLogger) Error(err error, msg string, fields ...domain.Field) { - err = fmt.Errorf("%s: %w", msg, err) - - if fields == nil { - l.rollbar.ErrorWithStackSkip(rollbar.ERR, err, 4) - l.zap.Error(err.Error()) - return - } - - l.rollbar.ErrorWithStackSkipWithExtras(rollbar.ERR, err, 3, rollbarMap(fields)) - l.zap.Error(err.Error(), zapFields(fields)...) -} - -func (l *appLogger) Fatal(err error, msg string, fields ...domain.Field) { - err = fmt.Errorf("%s: %w", msg, err) - - if fields == nil { - l.rollbar.ErrorWithStackSkip(rollbar.CRIT, err, 4) - l.rollbar.Close() - l.zap.Fatal(err.Error()) // os.Exit() - return - } - - l.rollbar.ErrorWithStackSkipWithExtras(rollbar.CRIT, err, 3, rollbarMap(fields)) - l.rollbar.Close() - l.zap.Fatal(err.Error(), zapFields(fields)...) // os.Exit() -} - -func rollbarMap(fields []domain.Field) map[string]interface{} { - m := make(map[string]interface{}, len(fields)*2) - for _, field := range fields { - m[field.Key] = field.Val - } - return m -} - -func zapFields(fields []domain.Field) []zap.Field { - s := make([]zap.Field, 0, len(fields)) - for _, field := range fields { - s = append(s, zap.Reflect(field.Key, field.Val)) - } - return s -} diff --git a/pkg/logger/logger_api.go b/pkg/logger/logger_api.go new file mode 100644 index 00000000..a8207f00 --- /dev/null +++ b/pkg/logger/logger_api.go @@ -0,0 +1,31 @@ +package logger + +var appLogger Logger + +func NewAppLogger(zap *ZapLogger, rollbar *RollbarLogger, serviceName, serviceVersion string) { + fields := []Field{ + {"service-name", serviceName}, + {"service-version", serviceVersion}, + } + appLogger = &loggers{zap, rollbar, fields} +} + +func Debug(msg string, fields ...Field) { + appLogger.debug(msg, fields...) +} + +func Info(msg string, fields ...Field) { + appLogger.info(msg, fields...) +} + +func Warn(msg string, fields ...Field) { + appLogger.warn(msg, fields...) +} + +func Error(err error, msg string, fields ...Field) { + appLogger.error(err, msg, fields...) +} + +func Fatal(err error, msg string, fields ...Field) { + appLogger.fatal(err, msg, fields...) +} diff --git a/pkg/logger/loggers.go b/pkg/logger/loggers.go new file mode 100644 index 00000000..cc80086e --- /dev/null +++ b/pkg/logger/loggers.go @@ -0,0 +1,62 @@ +package logger + +import ( + "fmt" + + "github.com/rollbar/rollbar-go" + + "go.uber.org/zap" +) + +type loggers struct { + zap *ZapLogger + rollbar *RollbarLogger + defaultFields []Field +} + +func (l *loggers) debug(msg string, fields ...Field) { + fields = append(l.defaultFields, fields...) + l.zap.Debug(msg, zapFields(fields)...) +} + +func (l *loggers) info(msg string, fields ...Field) { + fields = append(l.defaultFields, fields...) + l.zap.Info(msg, zapFields(fields)...) +} + +func (l *loggers) warn(msg string, fields ...Field) { + fields = append(l.defaultFields, fields...) + l.rollbar.MessageWithExtras(rollbar.WARN, msg, rollbarMap(fields)) + l.zap.Warn(msg, zapFields(fields)...) +} + +func (l *loggers) error(err error, msg string, fields ...Field) { + err = fmt.Errorf("%s: %w", msg, err) + fields = append(l.defaultFields, fields...) + l.rollbar.ErrorWithStackSkipWithExtras(rollbar.ERR, err, 3, rollbarMap(fields)) + l.zap.Error(err.Error(), zapFields(fields)...) +} + +func (l *loggers) fatal(err error, msg string, fields ...Field) { + err = fmt.Errorf("%s: %w", msg, err) + fields = append(l.defaultFields, fields...) + l.rollbar.ErrorWithStackSkipWithExtras(rollbar.CRIT, err, 3, rollbarMap(fields)) + l.rollbar.Close() + l.zap.Fatal(err.Error(), zapFields(fields)...) // os.Exit() +} + +func rollbarMap(fields []Field) map[string]interface{} { + m := make(map[string]interface{}, len(fields)*2) + for _, field := range fields { + m[field.Key] = field.Val + } + return m +} + +func zapFields(fields []Field) []zap.Field { + s := make([]zap.Field, 0, len(fields)) + for _, field := range fields { + s = append(s, zap.Reflect(field.Key, field.Val)) + } + return s +} diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go index 48319960..e8753f91 100644 --- a/pkg/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/evrone/go-service-template/internal/domain" + "github.com/evrone/go-service-template/pkg/logger" "github.com/Masterminds/squirrel" "github.com/jackc/pgx/v4/pgxpool" @@ -15,10 +15,10 @@ type Postgres struct { Builder squirrel.StatementBuilderType } -func NewPostgres(url string, maxPoolSize, connAttempts int) Postgres { +func NewPostgres(url string, maxPoolSize, connAttempts int) *Postgres { poolConfig, err := pgxpool.ParseConfig(url) if err != nil { - domain.Logger.Fatal(err, "postgres connect error") + logger.Fatal(err, "postgres connect error") } poolConfig.MaxConns = int32(maxPoolSize) @@ -31,30 +31,30 @@ func NewPostgres(url string, maxPoolSize, connAttempts int) Postgres { if errConn == nil { break } - domain.Logger.Debug("postgres is trying to connect", - domain.Field{Key: "attempts left", Val: connAttempts}, + logger.Info("postgres is trying to connect", + logger.Field{Key: "attempts left", Val: connAttempts}, ) - time.Sleep(time.Second * 2) + time.Sleep(time.Second) connAttempts-- } if errConn != nil { - domain.Logger.Fatal(errConn, "postgres connect error") + logger.Fatal(errConn, "postgres connect error") } - domain.Logger.Info("postgres connected") + logger.Info("postgres connected") builder := squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) - return Postgres{ + return &Postgres{ Pool: pool, Builder: builder, } } -func (p Postgres) Close() { +func (p *Postgres) Close() { if p.Pool != nil { p.Pool.Close() } From 6ab26e5357bd91e49ea3b481c92feb1222c0022b Mon Sep 17 00:00:00 2001 From: Pavel Argentov Date: Wed, 17 Feb 2021 18:36:30 +0300 Subject: [PATCH 05/15] [] CI tuning --- .circleci/config.yml | 70 ++++++++++++++++++++++---------------------- .reviewdog.yml | 12 ++++++++ 2 files changed, 47 insertions(+), 35 deletions(-) create mode 100644 .reviewdog.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index d8b34255..9e07f39d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -58,38 +58,38 @@ workflows: jobs: - linters - tests - # Build staging image - - docker/publish: - context: evrone-erp - image: << pipeline.parameters.image >> - tag: "staging-${CIRCLE_SHA1}" - requires: - - linters - - tests - filters: - branches: - only: - - develop - # Build QA image - - docker/publish: - context: evrone-erp - image: << pipeline.parameters.image >> - tag: "qa-${CIRCLE_SHA1}" - requires: - - linters - - tests - filters: - branches: - only: - - master - - /release\/.*/ - # Build production image - - docker/publish: - context: evrone-erp - image: << pipeline.parameters.image >> - tag: "$CIRCLE_TAG" - filters: - tags: - only: /^v.*/ - branches: - ignore: /.*/ + # # Build staging image + # - docker/publish: + # context: evrone-erp + # image: << pipeline.parameters.image >> + # tag: "staging-${CIRCLE_SHA1}" + # requires: + # - linters + # - tests + # filters: + # branches: + # only: + # - develop + # # Build QA image + # - docker/publish: + # context: evrone-erp + # image: << pipeline.parameters.image >> + # tag: "qa-${CIRCLE_SHA1}" + # requires: + # - linters + # - tests + # filters: + # branches: + # only: + # - master + # - /release\/v\d.*/ + # # Build production image + # - docker/publish: + # context: evrone-erp + # image: << pipeline.parameters.image >> + # tag: "$CIRCLE_TAG" + # filters: + # tags: + # only: /^v.*/ + # branches: + # ignore: /.*/ diff --git a/.reviewdog.yml b/.reviewdog.yml new file mode 100644 index 00000000..28276f36 --- /dev/null +++ b/.reviewdog.yml @@ -0,0 +1,12 @@ +runner: + dotenv-linter: + cmd: './bin/dotenv-linter' + + golangci-lint: + cmd: "./bin/golangci-lint run --out-format=line-number --enable-all ./..." + + golint: + cmd: 'golint ./...' + + govet: + cmd: 'go vet ./...' From ea508a76034603ba77b05e68cde02847bed72031 Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Wed, 17 Feb 2021 19:46:17 +0400 Subject: [PATCH 06/15] [GOT-9] Linters --- .golangci.yml | 583 +++++++++++++++--- Makefile | 2 +- docs/docs.go | 2 +- docs/swagger.json | 2 +- docs/swagger.yaml | 2 +- internal/app/app.go | 26 +- internal/app/configuration.go | 8 +- internal/app/migrate.go | 2 +- internal/delivery/http/v1/error.go | 3 +- internal/delivery/http/v1/router.go | 4 +- internal/delivery/http/v1/translation.go | 12 +- internal/delivery/http/v2/router.go | 3 +- internal/delivery/rmq/consumer/translation.go | 1 - internal/delivery/rmq/publisher/interface.go | 1 - .../delivery/rmq/publisher/translation.go | 1 - internal/domain/translation.go | 2 + internal/repository/interface.go | 1 + internal/repository/translation_postgres.go | 4 +- internal/service/interface.go | 1 + internal/service/translation.go | 4 +- internal/webapi/interface.go | 1 + internal/webapi/translation_google.go | 3 + pkg/httpserver/http.go | 3 +- pkg/logger/interface.go | 1 + pkg/logger/logger_api.go | 2 +- pkg/logger/loggers.go | 5 +- pkg/logger/rollbar.go | 1 + pkg/logger/zap.go | 2 +- pkg/postgres/postgres.go | 12 +- pkg/rabbitmq/rabbitmq.go | 1 - 30 files changed, 558 insertions(+), 137 deletions(-) delete mode 100644 internal/delivery/rmq/consumer/translation.go delete mode 100644 internal/delivery/rmq/publisher/interface.go delete mode 100644 internal/delivery/rmq/publisher/translation.go delete mode 100644 pkg/rabbitmq/rabbitmq.go diff --git a/.golangci.yml b/.golangci.yml index 05d17029..10458630 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,118 +1,521 @@ +# This file contains all available configuration options +# with their default values. + +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 1m + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: false + + # default is true. Enables skipping of directories: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs-use-default: true + # + # which dirs to skip: they won't be analyzed; + # can use regexp here: generated.*, regexp is applied on full path; + skip-dirs: + - docs # autogenerated + + # which files to skip: they will be analyzed, but issues from them + # won't be reported. Default value is empty list, but there is + # no need to include all autogenerated files, we confidently recognize + # autogenerated files. If it's not please let us know. + #skip-files: + # - ".*\\.my\\.go$" + # - lib/bad.go + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + format: colored-line-number + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + +# This file contains all available configuration options +# with their default values. + +# all available settings of specific linters linters-settings: - gci: - local-prefixes: github.com/evrone/go-service-template - govet: - check-shadowing: true - misspell: - locale: US - gofumpt: - extra-rules: true + dogsled: + # checks assignments with too many blank identifiers; default is 2 + max-blank-identifiers: 2 dupl: - threshold: 100 - errorlint: - errorf: true + # tokens count to trigger issue, 150 by default + threshold: 150 + errcheck: + # report about not checking of errors in type assetions: `a := b.(MyStruct)`; + check-type-assertions: true + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + check-blank: true + # path to a file containing a list of functions to exclude from checking + # see https://github.com/kisielk/errcheck#excluding-functions for details + #exclude: /path/to/file.txt + exhaustive: + # check switch statements in generated files also + check-generated: false + # indicates that switch statements are to be considered exhaustive if a + # 'default' case is present, even if all enum members aren't listed in the + # switch + default-signifies-exhaustive: false funlen: - lines: 60 + lines: 80 statements: 40 + gci: + # put imports beginning with prefix after 3rd-party packages; + # only support one prefix + # if not set, use goimports.local-prefixes + local-prefixes: github.com/evrone/go-service-template gocognit: - min-complexity: 10 - gocyclo: - min-complexity: 10 + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 15 + nestif: + # minimal complexity of if statements to report, 5 by default + min-complexity: 4 goconst: - min-len: 2 - min-occurrences: 2 + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 3 gocritic: + # Which checks should be enabled; can't be combined with 'disabled-checks'; + # See https://go-critic.github.io/overview#checks-overview + # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` + # By default list of stable checks is used. + enabled-checks: + - nestingreduce + - ruleguard + - truncatecmp + - unnamedresult + + # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty + disabled-checks: + - regexpMust + + # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. + # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". enabled-tags: + - performance + - style - diagnostic + # - security + disabled-tags: - experimental - opinionated - - performance - - style - disabled-checks: - - dupImport # https://github.com/go-critic/go-critic/issues/845 + + # Settings passed to gocritic. + # The settings key is the name of a supported gocritic checker. + # The list of supported checkers can be find in https://go-critic.github.io/overview. + settings: + captLocal: # must be valid enabled check name + # whether to restrict checker to params only (default true) + paramsOnly: true + elseif: + # whether to skip balanced if-else pairs (default true) + skipBalanced: true + hugeParam: + # size in bytes that makes the warning trigger (default 80) + sizeThreshold: 120 + nestingReduce: + # min number of statements inside a branch to trigger a warning (default 5) + bodyWidth: 5 + rangeExprCopy: + # size in bytes that makes the warning trigger (default 512) + sizeThreshold: 512 + # whether to check test functions (default true) + skipTestFuncs: true + rangeValCopy: + # size in bytes that makes the warning trigger (default 128) + sizeThreshold: 128 + # whether to check test functions (default true) + skipTestFuncs: true + ruleguard: + # path to a gorules file for the ruleguard checker + rules: '' + truncateCmp: + # whether to skip int/uint/uintptr types (default true) + skipArchDependent: true + underef: + # whether to skip (*x).method() calls where x is a pointer receiver (default true) + skipRecvDeref: true + unnamedResult: + # whether to check exported functions + checkExported: true + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 15 + godot: + # comments to be checked: `declarations`, `toplevel`, or `all` + scope: declarations + # list of regexps for excluding particular comment lines from check + exclude: + # example: exclude comments which contain numbers + # - '[0-9]+' + # check that each sentence starts with a capital letter + capital: true + godox: + # report any comments starting with keywords, this is useful for TODO or FIXME comments that + # might be left in the code accidentally and should be resolved before merging + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + - NOTE + - TODO + - FIXME + - OPTIMIZE # marks code that should be optimized before merging + - HACK # marks hack-arounds that should be removed before merging + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + goheader: + values: + const: + # define here const type values in format k:v, for example: + # COMPANY: MY COMPANY + regexp: + # define here regexp type values, for example + # AUTHOR: .*@mycompany\.com + template: # | + # put here copyright header template for source code files, for example: + # Note: {{ YEAR }} is a builtin value that returns the year relative to the current machine time. + # + # {{ AUTHOR }} {{ COMPANY }} {{ YEAR }} + # SPDX-License-Identifier: Apache-2.0 + + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at: + + # http://www.apache.org/licenses/LICENSE-2.0 + + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + template-path: + # also as alternative of directive 'template' you may put the path to file with the template source + goimports: + # put imports beginning with prefix after 3rd-party packages; + # it's a comma-separated list of prefixes + local-prefixes: github.com/evrone/evrone-erp-ns golint: - min-confidence: 0 + # minimal confidence for issues, default is 0.8 + min-confidence: 0.7 + gomnd: + settings: + mnd: + # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. + checks: argument,case,condition,operation,return,assign + ignored-numbers: + - 10 + - 100 + - 1000 + - 2 + - 3 + # ignored-files: magic_.*.go + # ignored-functions: math.* + gomodguard: + allowed: + modules: # List of allowed modules + # - gopkg.in/yaml.v2 + domains: # List of allowed module domains + # - golang.org + blocked: + modules: # List of blocked modules + # - github.com/uudashr/go-module: # Blocked module + # recommendations: # Recommended modules that should be used instead (Optional) + # - golang.org/x/mod + # reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional) + versions: # List of blocked module version constraints + # - github.com/mitchellh/go-homedir: # Blocked module with version constraint + # version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons + # reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional) + local_replace_directives: false # Set to true to raise lint issues for packages that are loaded from a local path via replace directive + govet: + # report about shadowed variables + check-shadowing: true + + # settings per analyzer + settings: + printf: # analyzer name, run `go tool vet help` to see all analyzers + funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer + - Infof + - Warnf + - Errorf + - Fatalf + - Prependf + - Appendf + + # enable or disable analyzers by name + # enable: + # - atomicalign + enable-all: true + disable: + - shadow + - gocheknoinits + disable-all: false + depguard: + list-type: blacklist + include-go-root: false + packages: + - github.com/sirupsen/logrus + packages-with-error-message: + # specify an error message to output when a blacklisted package is used + - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" + ifshort: + # Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax. + # Has higher priority than max-decl-chars. + max-decl-lines: 1 + # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax. + max-decl-chars: 30 lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option line-length: 120 + # tab width in spaces. Default to 1. + tab-width: 3 maligned: + # print struct with more effective memory layout or not, false by default suggest-new: true - nestif: - min-complexity: 4 + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + ignore-words: + - evrone + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 40 + prealloc: + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + predeclared: + # comma-separated list of predeclared identifiers to not report on + ignore: "" + # include method names and field names (i.e., qualified names) in checks + q: false nolintlint: + # Enable to ensure that nolint directives are all used. Default is true. + allow-unused: true # generate some false-positive + # Disable to ensure that nolint directives don't have a leading space. Default is true. allow-leading-space: true - allow-unused: false - require-explanation: false + # Exclude following linters from requiring an explanation. Default is []. + allow-no-explanation: [] + # Enable to require an explanation of nonzero length after each nolint directive. Default is false. + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. require-specific: false + rowserrcheck: + packages: + - github.com/jmoiron/sqlx + testpackage: + # regexp pattern to skip files + skip-regexp: (export|internal)_test\.go + thelper: + # The following configurations enable all checks. It can be omitted because all checks are enabled by default. + # You can enable only required checks deleting unnecessary checks. + test: + first: true + name: true + begin: true + benchmark: + first: true + name: true + begin: true + tb: + first: true + name: true + begin: true + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + wsl: + # If true append is only allowed to be cuddled if appending value is + # matching variables, fields or types on line above. Default is true. + strict-append: true + # Allow calls and assignments to be cuddled as long as the lines have any + # matching variables, fields or types. Default is true. + allow-assign-and-call: true + # Allow multiline assignments to be cuddled. Default is true. + allow-multiline-assign: true + # Allow declarations (var) to be cuddled. + allow-cuddle-declarations: false + # Allow trailing comments in ending of blocks + allow-trailing-comment: false + # Force newlines in end of case at this limit (0 = never). + force-case-trailing-whitespace: 0 + # Force cuddling of err checks with err var assignment + force-err-cuddling: false + # Allow leading comments to be separated with empty liens + allow-separated-leading-comment: false + gofumpt: + # Choose whether or not to use the extra rules that are disabled + # by default + extra-rules: false + errorlint: + # Report non-wrapping error creation using fmt.Errorf + errorf: true + makezero: + # Allow only slices initialized with a length of zero. Default is false. + always: false + forbidigo: + # Forbid the following identifiers + forbid: + - fmt.Print.* # too much log noise + - log.Print.* # don't use default logger + # Exclude godoc examples from forbidigo checks. Default is true. + exclude_godoc_examples: false + + # The custom section can be used to define linter plugins to be loaded at runtime. See README doc + # for more info. + custom: + # Each custom linter should have a unique name. + example: + # The path to the plugin *.so. Can be absolute or local. Required for each custom linter + # path: /path/to/example.so + # The description of the linter. Optional, just for documentation purposes. + # description: This is an example usage of a plugin linter. + # Intended to point to the repo location of the linter. Optional, just for documentation purposes. + # original-url: github.com/golangci/example-linter linters: - disable-all: true - enable: - - gofumpt - - gci - - misspell - - whitespace - -# - asciicheck -# - bodyclose -# - deadcode -# - dogsled -# - dupl -# - errcheck -# - errorlint -# - exhaustive -# - exportloopref -# - funlen -# - gochecknoglobals -# - gochecknoinits -# - gocognit -# - goconst -# - gocritic -# - gocyclo -# - godox -# - goerr113 -# - golint -# - gomnd -# - goprintffuncname -# - gosec -# - gosimple -# - govet -# - ineffassign -# - interfacer -# - lll -# - maligned -# - nakedret -# - nestif -# - nlreturn -# - noctx -# - nolintlint -# - paralleltest -# - prealloc -# - rowserrcheck -# - scopelint -# - sqlclosecheck -# - staticcheck -# - structcheck -# - stylecheck -# - testpackage -# - tparallel -# - unconvert -# - unparam -# - unused -# - varcheck -# - wrapcheck -# - wsl -# - godot + enable-all: true + disable: + - exhaustivestruct + - maligned + - testpackage + - prealloc + - ifshort + disable-all: false + fast: false + issues: + # List of regexps of issue texts to exclude, empty list by default. + # But independently from this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. To list all + # excluded by default patterns execute `golangci-lint run --help` + exclude: + - abcdef + + # Excluding configuration per-path, per-linter, per-text and per-source exclude-rules: + # Exclude some linters from running on tests files. - path: _test\.go linters: - - gomnd - - funlen + - gocyclo + - gocognit + - errcheck + - dupl + - gosec + + # Exclude known linters from partially hard-vendored code, + # which is impossible to exclude via "nolint" comments. + - path: internal/hmac/ + text: "weak cryptographic primitive" + linters: + - gosec + + # Exclude some staticcheck messages + - linters: + - staticcheck + text: "SA9003:" + + - linters: + - golint + text: "should have comment or be unexported" + + # Exclude lll issues for long lines with go:generate + - linters: + - lll + source: "^//go:generate " + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + + # The default value is false. If set to true exclude and exclude-rules + # regular expressions become case sensitive. + exclude-case-sensitive: false + + # The list of ids of default excludes to include or disable. By default it's empty. + include: + - EXC0002 # disable excluding of issues about comments from golint + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Show only new issues created after git revision `REV` + # new-from-rev: REV + + # Show only new issues created in git patch with set file path. + # new-from-patch: path/to/patch/file + + # Fix found issues (if it's supported by the linter) + fix: true + +severity: + # Default value is empty string. + # Set the default severity for issues. If severity rules are defined and the issues + # do not match or no severity is provided to the rule this will be the default + # severity applied. Severities should match the supported severity names of the + # selected out format. + # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity + # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity + # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + default-severity: error -#run: -# skip-dirs: -# - test/testdata_etc + # The default value is false. + # If set to true severity-rules regular expressions become case sensitive. + case-sensitive: false -# skip-files: -# - .*_test.go \ No newline at end of file + # Default value is empty list. + # When a list of severity rules are provided, severity information will be added to lint + # issues. Severity rules have the same filtering capability as exclude rules except you + # are allowed to specify one matcher per severity rule. + # Only affects out formats that support setting severity information. + rules: + - linters: + - dupl + severity: info diff --git a/Makefile b/Makefile index ff4338af..25551803 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ run: swag go mod download && GIN_MODE=debug CGO_ENABLED=0 go run ./cmd/app run-with-migrate: - go mod download && GIN_MODE=debug CGO_ENABLED=0 go run -tags migrate ./cmd/app + go mod download && CGO_ENABLED=0 go run -tags migrate ./cmd/app compose-up-db: docker-compose up --build -d --remove-orphans db && docker-compose logs -f diff --git a/docs/docs.go b/docs/docs.go index 8ea50cca..5d64e3b1 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -179,7 +179,7 @@ var SwaggerInfo = swaggerInfo{ BasePath: "/api/v1/", Schemes: []string{}, Title: "Go Service Template API", - Description: "Using a translation service as an example", + Description: "Using a translation service as an example.", } type s struct{} diff --git a/docs/swagger.json b/docs/swagger.json index 2436b230..db5ce542 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "description": "Using a translation service as an example", + "description": "Using a translation service as an example.", "title": "Go Service Template API", "contact": {}, "version": "1.0" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 2fc36b94..cd27a764 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -47,7 +47,7 @@ definitions: host: localhost:8080 info: contact: {} - description: Using a translation service as an example + description: Using a translation service as an example. title: Go Service Template API version: "1.0" paths: diff --git a/internal/app/app.go b/internal/app/app.go index e12db2ed..0f118658 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -1,3 +1,4 @@ +// Package app configures and runs application. package app import ( @@ -6,22 +7,18 @@ import ( "os/signal" "syscall" - v2 "github.com/evrone/go-service-template/internal/delivery/http/v2" - "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" - "github.com/evrone/go-service-template/internal/delivery/http/v1" - "github.com/evrone/go-service-template/pkg/logger" - + v1 "github.com/evrone/go-service-template/internal/delivery/http/v1" + v2 "github.com/evrone/go-service-template/internal/delivery/http/v2" "github.com/evrone/go-service-template/internal/repository" "github.com/evrone/go-service-template/internal/service" "github.com/evrone/go-service-template/internal/webapi" - "github.com/evrone/go-service-template/pkg/postgres" - "github.com/evrone/go-service-template/pkg/httpserver" - - swaggerFiles "github.com/swaggo/files" - ginSwagger "github.com/swaggo/gin-swagger" + "github.com/evrone/go-service-template/pkg/logger" + "github.com/evrone/go-service-template/pkg/postgres" ) // @title Go Service Template API @@ -30,21 +27,26 @@ import ( // @host localhost:8080 // @BasePath /api/v1/ + +// Run like main, runs application. func Run() { conf := NewConfig() // Logger zap := logger.NewZapLogger(conf.ZapLogLevel) defer zap.Close() + rollbar := logger.NewRollbarLogger(conf.RollbarAccessToken, conf.RollbarEnvironment) defer rollbar.Close() + logger.NewAppLogger(zap, rollbar, conf.ServiceName, conf.ServiceVersion) // Repository postgresDB := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) - translationRepository := repository.NewTranslationRepository(postgresDB) defer postgresDB.Close() + translationRepository := repository.NewTranslationRepository(postgresDB) + // WebAPI translationWebAPI := webapi.NewTranslationWebAPI() @@ -59,7 +61,7 @@ func Run() { v1.NewRouter(handler, translationService) v2.NewRouter(handler) - server := httpserver.NewServer(handler, conf.HttpApiPort) + server := httpserver.NewServer(handler, conf.HTTPAPIPort) server.Start() // Graceful shutdown diff --git a/internal/app/configuration.go b/internal/app/configuration.go index 73ed7a2b..4e7e6b16 100644 --- a/internal/app/configuration.go +++ b/internal/app/configuration.go @@ -12,7 +12,7 @@ type Conf struct { ZapLogLevel string RollbarAccessToken string RollbarEnvironment string - HttpApiPort string + HTTPAPIPort string PgURL string PgPoolMax int PgConnAttempts int @@ -27,7 +27,7 @@ func NewConfig() Conf { ZapLogLevel: strEnv("ZAP_LOG_LEVEL"), RollbarAccessToken: strEnv("ROLLBAR_ACCESS_TOKEN"), RollbarEnvironment: strEnv("ROLLBAR_ENVIRONMENT"), - HttpApiPort: strEnv("HTTP_API_PORT"), + HTTPAPIPort: strEnv("HTTP_API_PORT"), PgURL: strEnv("PG_URL"), PgPoolMax: intEnv("PG_POOL_MAX"), PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), @@ -42,8 +42,6 @@ func strEnv(env string) string { log.Fatalf("environment variable not declared: %s", env) } - log.Println(env, "=", value) - return value } @@ -60,7 +58,5 @@ func intEnv(env string) int { log.Fatalf("typecast error to integer: %s", err) } - log.Println(env, "=", intValue) - return intValue } diff --git a/internal/app/migrate.go b/internal/app/migrate.go index ca18d830..3011918a 100644 --- a/internal/app/migrate.go +++ b/internal/app/migrate.go @@ -24,7 +24,7 @@ func init() { break } log.Printf("Migrate: postgres is trying to connect, attempts left: %d", attempts) - time.Sleep(time.Second * 1) + time.Sleep(time.Second) attempts-- } diff --git a/internal/delivery/http/v1/error.go b/internal/delivery/http/v1/error.go index 054b7150..fe49b72e 100644 --- a/internal/delivery/http/v1/error.go +++ b/internal/delivery/http/v1/error.go @@ -1,8 +1,9 @@ package v1 import ( - "github.com/evrone/go-service-template/pkg/logger" "github.com/gin-gonic/gin" + + "github.com/evrone/go-service-template/pkg/logger" ) type response struct { diff --git a/internal/delivery/http/v1/router.go b/internal/delivery/http/v1/router.go index 355f2ba8..abe335c6 100644 --- a/internal/delivery/http/v1/router.go +++ b/internal/delivery/http/v1/router.go @@ -1,8 +1,10 @@ +// Package v1 implements routing paths. Each services in own file. package v1 import ( - "github.com/evrone/go-service-template/internal/service" "github.com/gin-gonic/gin" + + "github.com/evrone/go-service-template/internal/service" ) type router struct { diff --git a/internal/delivery/http/v1/translation.go b/internal/delivery/http/v1/translation.go index a9c8f666..f9ed5aac 100644 --- a/internal/delivery/http/v1/translation.go +++ b/internal/delivery/http/v1/translation.go @@ -3,10 +3,11 @@ package v1 import ( "net/http" - "github.com/evrone/go-service-template/internal/domain" + "github.com/gin-gonic/gin" + // Swagger docs. _ "github.com/evrone/go-service-template/docs" - "github.com/gin-gonic/gin" + "github.com/evrone/go-service-template/internal/domain" ) func (r *router) translationRouts(api *gin.RouterGroup) { @@ -29,11 +30,12 @@ type historyResponse struct { // @Produce json // @Success 200 {object} historyResponse // @Failure 400 {object} response -// @Router /translation/history [get] +// @Router /translation/history [get]. func (r *router) history(c *gin.Context) { translations, err := r.translationService.History() if err != nil { errorResponse(c, http.StatusBadRequest, err, "database problems") + return } @@ -55,11 +57,12 @@ type doTranslateRequest struct { // @Param request body doTranslateRequest true "Set up translation" // @Success 200 {object} domain.Translation // @Failure 400 {object} response -// @Router /translation/do-translate [post] +// @Router /translation/do-translate [post]. func (r *router) doTranslate(c *gin.Context) { var request doTranslateRequest if err := c.ShouldBindJSON(&request); err != nil { errorResponse(c, http.StatusBadRequest, err, "invalid request body") + return } @@ -70,6 +73,7 @@ func (r *router) doTranslate(c *gin.Context) { }) if err != nil { errorResponse(c, http.StatusBadRequest, err, "translation service problems") + return } diff --git a/internal/delivery/http/v2/router.go b/internal/delivery/http/v2/router.go index fde4ab86..cac799c2 100644 --- a/internal/delivery/http/v2/router.go +++ b/internal/delivery/http/v2/router.go @@ -1,3 +1,4 @@ +// Package v2 is example version 2 HTTP API package v2 import ( @@ -6,8 +7,6 @@ import ( "github.com/gin-gonic/gin" ) -type router struct{} - func NewRouter(handler *gin.Engine) { api := handler.Group("/api/test") { diff --git a/internal/delivery/rmq/consumer/translation.go b/internal/delivery/rmq/consumer/translation.go deleted file mode 100644 index b78b46cb..00000000 --- a/internal/delivery/rmq/consumer/translation.go +++ /dev/null @@ -1 +0,0 @@ -package consumer diff --git a/internal/delivery/rmq/publisher/interface.go b/internal/delivery/rmq/publisher/interface.go deleted file mode 100644 index f5979501..00000000 --- a/internal/delivery/rmq/publisher/interface.go +++ /dev/null @@ -1 +0,0 @@ -package publisher diff --git a/internal/delivery/rmq/publisher/translation.go b/internal/delivery/rmq/publisher/translation.go deleted file mode 100644 index f5979501..00000000 --- a/internal/delivery/rmq/publisher/translation.go +++ /dev/null @@ -1 +0,0 @@ -package publisher diff --git a/internal/domain/translation.go b/internal/domain/translation.go index 17c71e1c..df64bff4 100644 --- a/internal/domain/translation.go +++ b/internal/domain/translation.go @@ -1,3 +1,5 @@ +// Package domain defines main entities for business logic (services), data base mapping and +// HTTP response objects if suitable. Each logic group entities in own file. package domain type Translation struct { diff --git a/internal/repository/interface.go b/internal/repository/interface.go index efd6a06c..fe2cef28 100644 --- a/internal/repository/interface.go +++ b/internal/repository/interface.go @@ -1,3 +1,4 @@ +// Package repository implements databases for services. For each service in its own file. package repository import ( diff --git a/internal/repository/translation_postgres.go b/internal/repository/translation_postgres.go index c8842c0d..5b820813 100644 --- a/internal/repository/translation_postgres.go +++ b/internal/repository/translation_postgres.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/evrone/go-service-template/internal/domain" - "github.com/evrone/go-service-template/pkg/postgres" ) @@ -33,12 +32,15 @@ func (p *translationRepository) GetHistory(ctx context.Context) ([]domain.Transl defer rows.Close() entities := make([]domain.Translation, 0, 64) + for rows.Next() { e := domain.Translation{} + err = rows.Scan(&e.Source, &e.Destination, &e.Original, &e.Translation) if err != nil { return nil, fmt.Errorf("translationRepository - GetHistory - rows.Scan: %w", err) } + entities = append(entities, e) } diff --git a/internal/service/interface.go b/internal/service/interface.go index 474f5e54..f6d78df2 100644 --- a/internal/service/interface.go +++ b/internal/service/interface.go @@ -1,3 +1,4 @@ +// Package service implements application business logic. Each logic group in own file. package service import "github.com/evrone/go-service-template/internal/domain" diff --git a/internal/service/translation.go b/internal/service/translation.go index c5543d08..80b701e6 100644 --- a/internal/service/translation.go +++ b/internal/service/translation.go @@ -4,11 +4,9 @@ import ( "context" "fmt" + "github.com/evrone/go-service-template/internal/domain" "github.com/evrone/go-service-template/internal/repository" - "github.com/evrone/go-service-template/internal/webapi" - - "github.com/evrone/go-service-template/internal/domain" ) type translationService struct { diff --git a/internal/webapi/interface.go b/internal/webapi/interface.go index e27705be..95975804 100644 --- a/internal/webapi/interface.go +++ b/internal/webapi/interface.go @@ -1,3 +1,4 @@ +// Package webapi implements google translator API. package webapi import "github.com/evrone/go-service-template/internal/domain" diff --git a/internal/webapi/translation_google.go b/internal/webapi/translation_google.go index 995caded..eb990ea1 100644 --- a/internal/webapi/translation_google.go +++ b/internal/webapi/translation_google.go @@ -4,6 +4,7 @@ import ( "fmt" translator "github.com/Conight/go-googletrans" + "github.com/evrone/go-service-template/internal/domain" ) @@ -16,11 +17,13 @@ func NewTranslationWebAPI() Translation { UserAgent: []string{"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"}, ServiceUrls: []string{"translate.google.com"}, } + return &translationWebAPI{conf} } func (t *translationWebAPI) Translate(translation domain.Translation) (domain.Translation, error) { trans := translator.New(t.conf) + result, err := trans.Translate(translation.Original, translation.Source, translation.Destination) if err != nil { return domain.Translation{}, fmt.Errorf("translationWebAPI - Translate - trans.Translate: %w", err) diff --git a/pkg/httpserver/http.go b/pkg/httpserver/http.go index 43370f71..6b553ed8 100644 --- a/pkg/httpserver/http.go +++ b/pkg/httpserver/http.go @@ -1,3 +1,4 @@ +// Package httpserver implements HTTP server. package httpserver import ( @@ -34,7 +35,7 @@ func (s *Server) Notify() <-chan error { } func (s *Server) Stop() error { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) //nolint:gomnd // it's magic defer cancel() return s.server.Shutdown(ctx) diff --git a/pkg/logger/interface.go b/pkg/logger/interface.go index 8611f7e0..e06b849f 100644 --- a/pkg/logger/interface.go +++ b/pkg/logger/interface.go @@ -1,3 +1,4 @@ +// Package logger implements abstract logger. package logger type Logger interface { diff --git a/pkg/logger/logger_api.go b/pkg/logger/logger_api.go index a8207f00..fb58beec 100644 --- a/pkg/logger/logger_api.go +++ b/pkg/logger/logger_api.go @@ -1,6 +1,6 @@ package logger -var appLogger Logger +var appLogger Logger //nolint:gochecknoglobals // it's necessary func NewAppLogger(zap *ZapLogger, rollbar *RollbarLogger, serviceName, serviceVersion string) { fields := []Field{ diff --git a/pkg/logger/loggers.go b/pkg/logger/loggers.go index cc80086e..7dd6dbe0 100644 --- a/pkg/logger/loggers.go +++ b/pkg/logger/loggers.go @@ -4,7 +4,6 @@ import ( "fmt" "github.com/rollbar/rollbar-go" - "go.uber.org/zap" ) @@ -32,6 +31,7 @@ func (l *loggers) warn(msg string, fields ...Field) { func (l *loggers) error(err error, msg string, fields ...Field) { err = fmt.Errorf("%s: %w", msg, err) + fields = append(l.defaultFields, fields...) l.rollbar.ErrorWithStackSkipWithExtras(rollbar.ERR, err, 3, rollbarMap(fields)) l.zap.Error(err.Error(), zapFields(fields)...) @@ -39,6 +39,7 @@ func (l *loggers) error(err error, msg string, fields ...Field) { func (l *loggers) fatal(err error, msg string, fields ...Field) { err = fmt.Errorf("%s: %w", msg, err) + fields = append(l.defaultFields, fields...) l.rollbar.ErrorWithStackSkipWithExtras(rollbar.CRIT, err, 3, rollbarMap(fields)) l.rollbar.Close() @@ -50,6 +51,7 @@ func rollbarMap(fields []Field) map[string]interface{} { for _, field := range fields { m[field.Key] = field.Val } + return m } @@ -58,5 +60,6 @@ func zapFields(fields []Field) []zap.Field { for _, field := range fields { s = append(s, zap.Reflect(field.Key, field.Val)) } + return s } diff --git a/pkg/logger/rollbar.go b/pkg/logger/rollbar.go index 8119de13..b21226c9 100644 --- a/pkg/logger/rollbar.go +++ b/pkg/logger/rollbar.go @@ -8,6 +8,7 @@ type RollbarLogger struct { func NewRollbarLogger(token, env string) *RollbarLogger { client := rollbar.NewAsync(token, env, "", "", "") + return &RollbarLogger{client} } diff --git a/pkg/logger/zap.go b/pkg/logger/zap.go index 0e6b0d80..e8dc84e5 100644 --- a/pkg/logger/zap.go +++ b/pkg/logger/zap.go @@ -34,5 +34,5 @@ func NewZapLogger(logLevel string) *ZapLogger { } func (z *ZapLogger) Close() { - _ = z.Logger.Sync() + _ = z.Logger.Sync() //nolint:errcheck //can't log it } diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go index e8753f91..65762726 100644 --- a/pkg/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -1,13 +1,14 @@ +// Package postgres implements postgres connection. package postgres import ( "context" "time" - "github.com/evrone/go-service-template/pkg/logger" - "github.com/Masterminds/squirrel" "github.com/jackc/pgx/v4/pgxpool" + + "github.com/evrone/go-service-template/pkg/logger" ) type Postgres struct { @@ -23,14 +24,17 @@ func NewPostgres(url string, maxPoolSize, connAttempts int) *Postgres { poolConfig.MaxConns = int32(maxPoolSize) - var errConn error - var pool *pgxpool.Pool + var ( + errConn error + pool *pgxpool.Pool + ) for connAttempts > 0 { pool, errConn = pgxpool.ConnectConfig(context.Background(), poolConfig) if errConn == nil { break } + logger.Info("postgres is trying to connect", logger.Field{Key: "attempts left", Val: connAttempts}, ) diff --git a/pkg/rabbitmq/rabbitmq.go b/pkg/rabbitmq/rabbitmq.go deleted file mode 100644 index a4e247e1..00000000 --- a/pkg/rabbitmq/rabbitmq.go +++ /dev/null @@ -1 +0,0 @@ -package rabbitmq From 13d6c7ba65a0cfe642017c9850a001ea514e3766 Mon Sep 17 00:00:00 2001 From: Pavel Argentov Date: Wed, 17 Feb 2021 18:36:30 +0300 Subject: [PATCH 07/15] [] CI tuning --- .circleci/config.yml | 70 ++++++++++++++++++++++---------------------- .reviewdog.yml | 12 ++++++++ 2 files changed, 47 insertions(+), 35 deletions(-) create mode 100644 .reviewdog.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index d8b34255..9e07f39d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -58,38 +58,38 @@ workflows: jobs: - linters - tests - # Build staging image - - docker/publish: - context: evrone-erp - image: << pipeline.parameters.image >> - tag: "staging-${CIRCLE_SHA1}" - requires: - - linters - - tests - filters: - branches: - only: - - develop - # Build QA image - - docker/publish: - context: evrone-erp - image: << pipeline.parameters.image >> - tag: "qa-${CIRCLE_SHA1}" - requires: - - linters - - tests - filters: - branches: - only: - - master - - /release\/.*/ - # Build production image - - docker/publish: - context: evrone-erp - image: << pipeline.parameters.image >> - tag: "$CIRCLE_TAG" - filters: - tags: - only: /^v.*/ - branches: - ignore: /.*/ + # # Build staging image + # - docker/publish: + # context: evrone-erp + # image: << pipeline.parameters.image >> + # tag: "staging-${CIRCLE_SHA1}" + # requires: + # - linters + # - tests + # filters: + # branches: + # only: + # - develop + # # Build QA image + # - docker/publish: + # context: evrone-erp + # image: << pipeline.parameters.image >> + # tag: "qa-${CIRCLE_SHA1}" + # requires: + # - linters + # - tests + # filters: + # branches: + # only: + # - master + # - /release\/v\d.*/ + # # Build production image + # - docker/publish: + # context: evrone-erp + # image: << pipeline.parameters.image >> + # tag: "$CIRCLE_TAG" + # filters: + # tags: + # only: /^v.*/ + # branches: + # ignore: /.*/ diff --git a/.reviewdog.yml b/.reviewdog.yml new file mode 100644 index 00000000..28276f36 --- /dev/null +++ b/.reviewdog.yml @@ -0,0 +1,12 @@ +runner: + dotenv-linter: + cmd: './bin/dotenv-linter' + + golangci-lint: + cmd: "./bin/golangci-lint run --out-format=line-number --enable-all ./..." + + golint: + cmd: 'golint ./...' + + govet: + cmd: 'go vet ./...' From a84f2c125ce1b77f27f7b3664bdba4305f4a6214 Mon Sep 17 00:00:00 2001 From: Pavel Argentov Date: Wed, 17 Feb 2021 18:56:00 +0300 Subject: [PATCH 08/15] [] CI: DB & dockerize --- .circleci/config.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9e07f39d..fb9ca097 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,6 +15,14 @@ executors: default: docker: - image: circleci/golang:1.15 + with-services: + docker: + - image: circleci/golang:1.15 + - image: circleci/postgres:11-alpine + environment: + POSTGRES_USER: postgres + POSTGRES_DB: template_test + POSTGRES_PASSWORD: '' aliases: - &restore-mod-cache @@ -44,10 +52,13 @@ jobs: command: .circleci/bin/hadolint.sh tests: executor: - name: default + name: with-services steps: - checkout - restore_cache: *restore-mod-cache + # Wait for database + - run: dockerize -wait tcp://localhost:5432 -timeout 1m + # APP DB init - run: name: Tests command: go test ./... From e8a71be56cd3293a2795fd39864740b14924732a Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Thu, 18 Feb 2021 03:37:01 +0400 Subject: [PATCH 09/15] [GOT-21] CircleCI --- .circleci/bin/install_tools.sh | 2 +- .circleci/config.yml | 22 +++++++++++----------- .env | 27 ++++++++++----------------- .reviewdog.yml | 10 ++-------- Dockerfile | 7 ++----- docker-compose.yml | 8 ++++---- docs/docs.go | 2 +- docs/swagger.json | 2 +- docs/swagger.yaml | 2 +- go.mod | 6 ------ go.sum | 24 ++++++++++++++++++++++-- internal/app/configuration.go | 22 +++++++++------------- 12 files changed, 64 insertions(+), 70 deletions(-) diff --git a/.circleci/bin/install_tools.sh b/.circleci/bin/install_tools.sh index f6504c25..eff27b8d 100755 --- a/.circleci/bin/install_tools.sh +++ b/.circleci/bin/install_tools.sh @@ -4,7 +4,7 @@ set -ex HADOLINT_VERSION=v1.22.1 REVIEWDOG_VERSION=v0.11.0 -GOLANGCILINT_VERSION=v1.36.0 +GOLANGCILINT_VERSION=v1.37.0 DOTENV_LINTER_VERSION=v3.0.0 go get -u golang.org/x/lint/golint diff --git a/.circleci/config.yml b/.circleci/config.yml index fb9ca097..730e4bbe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,14 +15,14 @@ executors: default: docker: - image: circleci/golang:1.15 - with-services: - docker: - - image: circleci/golang:1.15 - - image: circleci/postgres:11-alpine - environment: - POSTGRES_USER: postgres - POSTGRES_DB: template_test - POSTGRES_PASSWORD: '' +# with-services: +# docker: +# - image: circleci/golang:1.15 +# - image: circleci/postgres:11-alpine +# environment: +# POSTGRES_USER: postgres +# POSTGRES_DB: template_test +# POSTGRES_PASSWORD: '' aliases: - &restore-mod-cache @@ -52,16 +52,16 @@ jobs: command: .circleci/bin/hadolint.sh tests: executor: - name: with-services + name: default steps: - checkout - restore_cache: *restore-mod-cache # Wait for database - - run: dockerize -wait tcp://localhost:5432 -timeout 1m +# - run: dockerize -wait tcp://localhost:5432 -timeout 1m # APP DB init - run: name: Tests - command: go test ./... + command: go test -v -cover -race ./internal/... - save_cache: *save-mod-cache workflows: diff --git a/.env b/.env index e3e1fc9f..fa0c9476 100644 --- a/.env +++ b/.env @@ -1,17 +1,10 @@ -GIN_MODE = release - -SERVICE_NAME = go-service-template -SERVICE_VERSION = 0.0.0 - -ZAP_LOG_LEVEL = debug -ROLLBAR_ACCESS_TOKEN = 97c783270fd14bf6a3c76ab524ee8839 -ROLLBAR_ENVIRONMENT = go-template-test - -HTTP_API_PORT = 8080 - -PG_URL = postgres://user:pass@localhost:5432/postgres -PG_POOL_MAX = 1 -PG_CONN_ATTEMPTS = 10 - -RMQ_URL = amqp://guest:guest@localhost:5672/ -RMQ_QUEUE_NAME = test_queue \ No newline at end of file +GOT_GIN_MODE=release +GOT_HTTP_API_PORT=8080 +GOT_PG_CONN_ATTEMPTS=10 +GOT_PG_POOL_MAX=1 +GOT_PG_URL=postgres://user:pass@localhost:5432/postgres +GOT_ROLLBAR_ACCESS_TOKEN=97c783270fd14bf6a3c76ab524ee8839 +GOT_ROLLBAR_ENVIRONMENT=go-template-test +GOT_SERVICE_NAME=go-service-template +GOT_SERVICE_VERSION=0.0.0 +GOT_ZAP_LOG_LEVEL=debug diff --git a/.reviewdog.yml b/.reviewdog.yml index 28276f36..a3081e2b 100644 --- a/.reviewdog.yml +++ b/.reviewdog.yml @@ -1,12 +1,6 @@ runner: dotenv-linter: - cmd: './bin/dotenv-linter' + cmd: "./bin/dotenv-linter" golangci-lint: - cmd: "./bin/golangci-lint run --out-format=line-number --enable-all ./..." - - golint: - cmd: 'golint ./...' - - govet: - cmd: 'go vet ./...' + cmd: "./bin/golangci-lint run --out-format=line-number -c .golangci.yml ./..." \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 54566211..43719d5d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,15 @@ # Step 1: Modules caching FROM golang:1.15.7-alpine3.13 as modules COPY go.mod go.sum /modules/ -RUN cd /modules && go mod download +WORKDIR /modules +RUN go mod download # Step 2: Builder FROM golang:1.15.7-alpine3.13 as builder -RUN apk add --no-cache ca-certificates - COPY --from=modules /go/pkg /go/pkg - COPY . /app WORKDIR /app - RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -tags migrate -o /bin/app ./cmd/app diff --git a/docker-compose.yml b/docker-compose.yml index e18a7b2c..f5d0c278 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: env_file: - .env environment: - PG_URL: 'postgres://user:pass@db:5432/postgres' + GOT_PG_URL: 'postgres://user:pass@db:5432/postgres' ports: - 8080:8080 depends_on: @@ -20,9 +20,9 @@ services: volumes: - pg-data:/var/lib/postgresql/data environment: - POSTGRES_USER: 'user' - POSTGRES_PASSWORD: 'pass' - POSTGRES_DB: 'postgres' + GOT_POSTGRES_USER: 'user' + GOT_POSTGRES_PASSWORD: 'pass' + GOT_POSTGRES_DB: 'postgres' ports: - 5432:5432 diff --git a/docs/docs.go b/docs/docs.go index 5d64e3b1..8ea50cca 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -179,7 +179,7 @@ var SwaggerInfo = swaggerInfo{ BasePath: "/api/v1/", Schemes: []string{}, Title: "Go Service Template API", - Description: "Using a translation service as an example.", + Description: "Using a translation service as an example", } type s struct{} diff --git a/docs/swagger.json b/docs/swagger.json index db5ce542..2436b230 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "description": "Using a translation service as an example.", + "description": "Using a translation service as an example", "title": "Go Service Template API", "contact": {}, "version": "1.0" diff --git a/docs/swagger.yaml b/docs/swagger.yaml index cd27a764..2fc36b94 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -47,7 +47,7 @@ definitions: host: localhost:8080 info: contact: {} - description: Using a translation service as an example. + description: Using a translation service as an example title: Go Service Template API version: "1.0" paths: diff --git a/go.mod b/go.mod index 3f58774b..4fa8f2ad 100644 --- a/go.mod +++ b/go.mod @@ -12,21 +12,15 @@ require ( github.com/gin-gonic/gin v1.6.3 github.com/go-openapi/spec v0.20.3 // indirect github.com/golang-migrate/migrate/v4 v4.14.1 - github.com/golang/protobuf v1.4.3 // indirect github.com/jackc/pgx/v4 v4.10.1 - github.com/lib/pq v1.8.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.8 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/rollbar/rollbar-go v1.2.0 - github.com/stretchr/testify v1.7.0 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 github.com/swaggo/gin-swagger v1.3.0 github.com/swaggo/swag v1.7.0 github.com/tidwall/pretty v1.0.5 // indirect go.uber.org/zap v1.10.0 - golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 // indirect golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect golang.org/x/tools v0.1.0 // indirect - google.golang.org/protobuf v1.25.0 // indirect ) diff --git a/go.sum b/go.sum index 0c7a1a23..ca39168e 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,7 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -55,12 +56,14 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/aaw/maybe_tls v0.0.0-20160803104303-89c499bcc6aa h1:6yJyU8MlPBB2enGJdPciPlr8P+PC0nhCFHnSHYMirZI= github.com/aaw/maybe_tls v0.0.0-20160803104303-89c499bcc6aa/go.mod h1:I0wzMZvViQzmJjxK+AtfFAnqDCkQV/+r17PO1CCSYnU= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -86,6 +89,7 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -101,10 +105,15 @@ 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/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dhui/dktest v0.3.3 h1:DBuH/9GFaWbDRa42qsut/hbQu+srAQ0rPWnUoiGX7CA= github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ= +github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:iWPIG7pWIsCwT6ZtHnTUpoVMnete7O/pzd9HFE3+tn8= github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -165,9 +174,8 @@ github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PL github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/golang-migrate/migrate v1.3.2 h1:QAlFV1QF9zdkzy/jujlBVkVu+L/+k18cg8tuY1/4JDY= -github.com/golang-migrate/migrate v3.5.4+incompatible h1:R7OzwvCJTCgwapPCiX6DyBiu2czIUMDCB118gFTKTUA= github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE= github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= @@ -237,6 +245,7 @@ github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -320,6 +329,7 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= @@ -390,6 +400,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= @@ -400,7 +411,9 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= @@ -431,6 +444,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV 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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0= github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU= @@ -541,6 +555,7 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -672,6 +687,7 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -793,6 +809,7 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200911024640-645f7a48b24f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3 h1:sg8vLDNIxFPHTchfhH1E3AI32BL3f23oie38xUWnJM8= google.golang.org/genproto v0.0.0-20201030142918-24207fddd1c3/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -807,6 +824,7 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -848,6 +866,7 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclp gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -856,6 +875,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= diff --git a/internal/app/configuration.go b/internal/app/configuration.go index 4e7e6b16..66e7269f 100644 --- a/internal/app/configuration.go +++ b/internal/app/configuration.go @@ -16,23 +16,19 @@ type Conf struct { PgURL string PgPoolMax int PgConnAttempts int - RmqURL string - RmqQueueName string } func NewConfig() Conf { return Conf{ - ServiceName: strEnv("SERVICE_NAME"), - ServiceVersion: strEnv("SERVICE_VERSION"), - ZapLogLevel: strEnv("ZAP_LOG_LEVEL"), - RollbarAccessToken: strEnv("ROLLBAR_ACCESS_TOKEN"), - RollbarEnvironment: strEnv("ROLLBAR_ENVIRONMENT"), - HTTPAPIPort: strEnv("HTTP_API_PORT"), - PgURL: strEnv("PG_URL"), - PgPoolMax: intEnv("PG_POOL_MAX"), - PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), - RmqURL: strEnv("RMQ_URL"), - RmqQueueName: strEnv("RMQ_QUEUE_NAME"), + ServiceName: strEnv("GOT_SERVICE_NAME"), + ServiceVersion: strEnv("GOT_SERVICE_VERSION"), + ZapLogLevel: strEnv("GOT_ZAP_LOG_LEVEL"), + RollbarAccessToken: strEnv("GOT_ROLLBAR_ACCESS_TOKEN"), + RollbarEnvironment: strEnv("GOT_ROLLBAR_ENVIRONMENT"), + HTTPAPIPort: strEnv("GOT_HTTP_API_PORT"), + PgURL: strEnv("GOT_PG_URL"), + PgPoolMax: intEnv("GOT_PG_POOL_MAX"), + PgConnAttempts: intEnv("GOT_PG_CONN_ATTEMPTS"), } } From a79acad47b70562aa5c7c5688e075bbeaf27bc5b Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Thu, 18 Feb 2021 22:42:59 +0400 Subject: [PATCH 10/15] [GOT-22] Integration tests CircleCI --- .circleci/bin/hadolint.sh | 6 -- .../bin/{install_tools.sh => install.sh} | 6 +- .circleci/bin/reviewdog.sh | 4 -- .circleci/bin/reviewdog_dotenv.sh | 7 +++ .circleci/bin/reviewdog_golangci-lint.sh | 7 +++ .circleci/bin/reviewdog_hadolint.sh | 7 +++ .circleci/config.yml | 55 +++++++++++++------ .env | 8 +-- .reviewdog.yml | 6 -- Dockerfile | 6 +- Dockerfile.prod | 2 +- Makefile | 24 +++++--- README.md | 22 ++++---- docker-compose.yml | 31 +++++++---- go.mod | 3 +- go.sum | 2 + integration-test/Dockerfile | 17 ++++++ .../rest_api_test.go | 48 ++++++++++++++-- internal/app/migrate.go | 29 ++++++++-- ...l => 20210221023242_migrate_name.down.sql} | 0 ...sql => 20210221023242_migrate_name.up.sql} | 0 21 files changed, 200 insertions(+), 90 deletions(-) delete mode 100755 .circleci/bin/hadolint.sh rename .circleci/bin/{install_tools.sh => install.sh} (93%) delete mode 100755 .circleci/bin/reviewdog.sh create mode 100755 .circleci/bin/reviewdog_dotenv.sh create mode 100755 .circleci/bin/reviewdog_golangci-lint.sh create mode 100755 .circleci/bin/reviewdog_hadolint.sh delete mode 100644 .reviewdog.yml create mode 100644 integration-test/Dockerfile rename {integration_test => integration-test}/rest_api_test.go (50%) rename migrations/{000001_migrate_name.down.sql => 20210221023242_migrate_name.down.sql} (100%) rename migrations/{000001_migrate_name.up.sql => 20210221023242_migrate_name.up.sql} (100%) diff --git a/.circleci/bin/hadolint.sh b/.circleci/bin/hadolint.sh deleted file mode 100755 index af9a599a..00000000 --- a/.circleci/bin/hadolint.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -git ls-files --exclude='Dockerfile*' --ignored | xargs ./bin/hadolint \ - | ./bin/reviewdog -efm="%f:%l %m" -diff="git diff ${DEFAULT_BRANCH}..HEAD" -reporter=github-pr-review diff --git a/.circleci/bin/install_tools.sh b/.circleci/bin/install.sh similarity index 93% rename from .circleci/bin/install_tools.sh rename to .circleci/bin/install.sh index eff27b8d..a41f09b3 100755 --- a/.circleci/bin/install_tools.sh +++ b/.circleci/bin/install.sh @@ -2,13 +2,11 @@ set -ex -HADOLINT_VERSION=v1.22.1 REVIEWDOG_VERSION=v0.11.0 GOLANGCILINT_VERSION=v1.37.0 +HADOLINT_VERSION=v1.22.1 DOTENV_LINTER_VERSION=v3.0.0 -go get -u golang.org/x/lint/golint - # Install reviewdog wget -O - -q https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh \ | sh -s -- -b ./bin $REVIEWDOG_VERSION @@ -21,4 +19,4 @@ wget -q https://github.com/hadolint/hadolint/releases/download/$HADOLINT_VERSION -O ./bin/hadolint && chmod +x ./bin/hadolint # Install dotenv-linter -wget -q -O - https://git.io/JLbXn | sh -s -- -b bin $DOTENV_LINTER_VERSION +wget -q -O - https://git.io/JLbXn | sh -s -- -b bin $DOTENV_LINTER_VERSION \ No newline at end of file diff --git a/.circleci/bin/reviewdog.sh b/.circleci/bin/reviewdog.sh deleted file mode 100755 index da2a3d8e..00000000 --- a/.circleci/bin/reviewdog.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env bash - -set -ex -./bin/reviewdog -diff="git diff ${DEFAULT_BRANCH}..HEAD" -reporter=github-pr-review diff --git a/.circleci/bin/reviewdog_dotenv.sh b/.circleci/bin/reviewdog_dotenv.sh new file mode 100755 index 00000000..04aa1bb2 --- /dev/null +++ b/.circleci/bin/reviewdog_dotenv.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -ex + +# Exit code always 0 +dotenv-linter \ +| reviewdog -f=dotenv-linter -diff="git diff ${DEFAULT_BRANCH}..HEAD" -reporter=github-pr-review diff --git a/.circleci/bin/reviewdog_golangci-lint.sh b/.circleci/bin/reviewdog_golangci-lint.sh new file mode 100755 index 00000000..c199073c --- /dev/null +++ b/.circleci/bin/reviewdog_golangci-lint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -ex + +# Exit code always 0 +golangci-lint run --out-format=line-number \ +| reviewdog -f=golangci-lint -diff="git diff ${DEFAULT_BRANCH}..HEAD" -reporter=github-pr-review diff --git a/.circleci/bin/reviewdog_hadolint.sh b/.circleci/bin/reviewdog_hadolint.sh new file mode 100755 index 00000000..7980d0af --- /dev/null +++ b/.circleci/bin/reviewdog_hadolint.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -ex + +# Exit code always 0 +git ls-files --exclude='Dockerfile*' --ignored | xargs hadolint \ +| reviewdog -efm="%f:%l %m" -diff="git diff ${DEFAULT_BRANCH}..HEAD" -reporter=github-pr-review diff --git a/.circleci/config.yml b/.circleci/config.yml index 730e4bbe..05dd46c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -15,20 +15,11 @@ executors: default: docker: - image: circleci/golang:1.15 -# with-services: -# docker: -# - image: circleci/golang:1.15 -# - image: circleci/postgres:11-alpine -# environment: -# POSTGRES_USER: postgres -# POSTGRES_DB: template_test -# POSTGRES_PASSWORD: '' aliases: - &restore-mod-cache keys: - go-modules-v1-{{ checksum "go.sum" }} - - go-modules-v1- - &save-mod-cache key: go-modules-v1-{{ checksum "go.sum" }} @@ -43,32 +34,60 @@ jobs: - checkout - run: name: Install tools - command: .circleci/bin/install_tools.sh + command: .circleci/bin/install.sh - run: - name: Run Reviewdog - command: .circleci/bin/reviewdog.sh + name: PATH for working without a relative path + command: echo "export PATH=$PATH:/home/circleci/project/bin" >> $BASH_ENV - run: - name: Run Hadolint - command: .circleci/bin/hadolint.sh + name: Dotenv > Reviewdog + command: .circleci/bin/reviewdog_dotenv.sh + - run: + name: Hadolint > Reviewdog + command: .circleci/bin/reviewdog_hadolint.sh + - run: + name: Golangci-lint > Reviewdog + command: .circleci/bin/reviewdog_golangci-lint.sh + - run: + name: Dotenv + command: dotenv-linter + - run: + name: Hadolint + command: git ls-files --exclude='Dockerfile*' --ignored | xargs hadolint + - run: + name: Golangci-lint + command: golangci-lint run + tests: executor: name: default steps: - checkout - restore_cache: *restore-mod-cache - # Wait for database -# - run: dockerize -wait tcp://localhost:5432 -timeout 1m - # APP DB init - run: name: Tests - command: go test -v -cover -race ./internal/... + command: go test -v -race ./internal/... - save_cache: *save-mod-cache + integration-tests: + executor: + name: default + steps: + - checkout + - setup_remote_docker: + version: 19.03.13 + - run: + name: Integration tests + command: docker-compose up --build -d && docker attach integration + - run: + name: Logs + command: docker-compose logs + workflows: ci: jobs: - linters - tests + - integration-tests # # Build staging image # - docker/publish: # context: evrone-erp diff --git a/.env b/.env index fa0c9476..ecbee4d8 100644 --- a/.env +++ b/.env @@ -1,10 +1,10 @@ -GOT_GIN_MODE=release +GIN_MODE=release GOT_HTTP_API_PORT=8080 -GOT_PG_CONN_ATTEMPTS=10 +GOT_PG_CONN_ATTEMPTS=20 GOT_PG_POOL_MAX=1 GOT_PG_URL=postgres://user:pass@localhost:5432/postgres -GOT_ROLLBAR_ACCESS_TOKEN=97c783270fd14bf6a3c76ab524ee8839 -GOT_ROLLBAR_ENVIRONMENT=go-template-test +GOT_ROLLBAR_ACCESS_TOKEN= +GOT_ROLLBAR_ENVIRONMENT= GOT_SERVICE_NAME=go-service-template GOT_SERVICE_VERSION=0.0.0 GOT_ZAP_LOG_LEVEL=debug diff --git a/.reviewdog.yml b/.reviewdog.yml deleted file mode 100644 index a3081e2b..00000000 --- a/.reviewdog.yml +++ /dev/null @@ -1,6 +0,0 @@ -runner: - dotenv-linter: - cmd: "./bin/dotenv-linter" - - golangci-lint: - cmd: "./bin/golangci-lint run --out-format=line-number -c .golangci.yml ./..." \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 43719d5d..aedf380f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,17 @@ # Step 1: Modules caching -FROM golang:1.15.7-alpine3.13 as modules +FROM golang:1.16.0-alpine3.13 as modules COPY go.mod go.sum /modules/ WORKDIR /modules RUN go mod download - # Step 2: Builder -FROM golang:1.15.7-alpine3.13 as builder +FROM golang:1.16.0-alpine3.13 as builder COPY --from=modules /go/pkg /go/pkg COPY . /app WORKDIR /app RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -tags migrate -o /bin/app ./cmd/app - # Step 3: Final FROM scratch COPY --from=builder /app/migrations /migrations diff --git a/Dockerfile.prod b/Dockerfile.prod index 867a4b1b..fe70f526 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,5 +1,5 @@ # Step 1: Builder -FROM golang:1.15.7-alpine3.13 as builder +FROM golang:1.16.0-alpine3.13 as builder COPY . /app WORKDIR /app RUN go mod download -x && \ diff --git a/Makefile b/Makefile index 25551803..0057520f 100644 --- a/Makefile +++ b/Makefile @@ -5,10 +5,7 @@ swag: swag init -g internal/app/app.go run: swag - go mod download && GIN_MODE=debug CGO_ENABLED=0 go run ./cmd/app - -run-with-migrate: - go mod download && CGO_ENABLED=0 go run -tags migrate ./cmd/app + go mod tidy && go mod download && GIN_MODE=debug CGO_ENABLED=0 go run -tags migrate ./cmd/app compose-up-db: docker-compose up --build -d --remove-orphans db && docker-compose logs -f @@ -19,14 +16,23 @@ compose-up: compose-down: docker-compose down --remove-orphans -lint: +docker-rm-volume: + docker volume rm go-service-template_pg-data + +linter-golangci: golangci-lint run +linter-hadolint: + git ls-files --exclude='Dockerfile*' --ignored | xargs hadolint + +linter-dotenv: + dotenv-linter + test: go test -v -cover -race ./internal/... -integration-test: - go clean -testcache && go test ./integration_test/... +test-integration: + go clean -testcache && HOST=localhost:8080 go test -v ./integration-test/... mock: mockery --all -r --case snake @@ -37,5 +43,5 @@ migrate-create: migrate: migrate -path migrations -database '$(PG_URL)?sslmode=disable' up -.PHONY: swag, run, run-with-migrate, compose-up-db, compose-up, compose-down -.PHONY: lint, test, integration-test, mock, migrate-create, migrate \ No newline at end of file +.PHONY: swag, run, compose-up-db, compose-up, compose-down, docker-rm-pg-data, linter-golangci, linter-hadolint +.PHONY: linter-dotenv, test, test-integration, mock, migrate-create, migrate \ No newline at end of file diff --git a/README.md b/README.md index 8ba89ae9..ebf6ebba 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Шаблон микросервиса на Go +Шаблон сервиса на Go =========================================== С развитием любого проекта, логика работы приложения усложняется и запутывается. Разработка новых фич замедляется. Если не планировать архитектуру, довольно быстро @@ -16,26 +16,24 @@ Quick start ```sh # DB container $ make compose-up-db -# App local +# Run app with migrations $ make run ``` -В контейнерах: +В контейнерах (можно запускать в ci): ```sh -# DB and app containers +# DB + app + migrations + integration tests $ make compose-up ``` -Линтеры, тесты: -```sh -# Linters -$ make lint -# Unit tests -$ make test -# REST API tests -$ make integration-test +Для работы RollBar нужно установить переменные в .env: +``` +GOT_ROLLBAR_ACCESS_TOKEN= +GOT_ROLLBAR_ENVIRONMENT= ``` + + Clean Architecture ---- ### Главный принцип diff --git a/docker-compose.yml b/docker-compose.yml index f5d0c278..b9ecf50c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,18 @@ version: '3.9' services: + db: + container_name: postgres + image: postgres + volumes: + - pg-data:/var/lib/postgresql/data + environment: + POSTGRES_USER: 'user' + POSTGRES_PASSWORD: 'pass' + POSTGRES_DB: 'postgres' + ports: + - 5432:5432 + app: build: . container_name: app @@ -14,17 +26,16 @@ services: depends_on: - db - db: - container_name: postgres - image: postgres - volumes: - - pg-data:/var/lib/postgresql/data + integration: + build: + context: . + dockerfile: integration-test/Dockerfile + container_name: integration + image: integration environment: - GOT_POSTGRES_USER: 'user' - GOT_POSTGRES_PASSWORD: 'pass' - GOT_POSTGRES_DB: 'postgres' - ports: - - 5432:5432 + HOST: 'app:8080' + depends_on: + - app volumes: pg-data: \ No newline at end of file diff --git a/go.mod b/go.mod index 4fa8f2ad..8b4761ae 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/evrone/go-service-template -go 1.15 +go 1.16 require ( github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb @@ -21,6 +21,7 @@ require ( github.com/swaggo/swag v1.7.0 github.com/tidwall/pretty v1.0.5 // indirect go.uber.org/zap v1.10.0 + golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect golang.org/x/tools v0.1.0 // indirect ) diff --git a/go.sum b/go.sum index ca39168e..4f307970 100644 --- a/go.sum +++ b/go.sum @@ -557,6 +557,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= diff --git a/integration-test/Dockerfile b/integration-test/Dockerfile new file mode 100644 index 00000000..211e99e8 --- /dev/null +++ b/integration-test/Dockerfile @@ -0,0 +1,17 @@ +# Step 1: Modules caching +FROM golang:1.16.0-alpine3.13 as modules +COPY go.mod go.sum /modules/ +WORKDIR /modules +RUN go mod download + +# Step 2: Builder +FROM golang:1.16.0-alpine3.13 as builder +COPY --from=modules /go/pkg /go/pkg +COPY . /app +WORKDIR /app + +RUN go env -w CGO_ENABLED=0 +RUN go env -w GOOS=linux +RUN go env -w GOARCH=amd64 + +CMD ["go", "test", "-v", "./integration-test/..."] \ No newline at end of file diff --git a/integration_test/rest_api_test.go b/integration-test/rest_api_test.go similarity index 50% rename from integration_test/rest_api_test.go rename to integration-test/rest_api_test.go index 97a6ac63..010a8fc6 100644 --- a/integration_test/rest_api_test.go +++ b/integration-test/rest_api_test.go @@ -1,18 +1,56 @@ package rest_api_test import ( + "log" "net/http" "os" "testing" + "time" . "github.com/Eun/go-hit" ) +var basePath string + func TestMain(m *testing.M) { + host, ok := os.LookupEnv("HOST") + if !ok || len(host) == 0 { + log.Fatalf("environment variable not declared: HOST") + } + + err := healthCheck(host, 20) + if err != nil { + log.Fatalf("Integration tests: host %s is not available: %s", host, err) + } + + log.Printf("Integration tests: host %s is available", host) + + basePath = "http://" + host + "/api/v1" + code := m.Run() os.Exit(code) } +func healthCheck(host string, attempts int) error { + var err error + healthPath := "http://" + host + "/health" + + for attempts > 0 { + err = Do(Get(healthPath), Expect().Status().Equal(http.StatusOK)) + if err == nil { + return nil + } + + log.Printf("Integration tests: url %s is not available, attempts left: %d", healthPath, attempts) + + time.Sleep(time.Second) + + attempts-- + } + + return err +} + func TestDoTranslate(t *testing.T) { body := `{ "destination": "en", @@ -21,7 +59,7 @@ func TestDoTranslate(t *testing.T) { }` Test(t, Description("DoTranslate Success"), - Post("http://localhost:8080/api/v1/translation/do-translate"), + Post(basePath+"/translation/do-translate"), Send().Headers("Content-Type").Add("application/json"), Send().Body().String(body), Expect().Status().Equal(http.StatusOK), @@ -29,12 +67,12 @@ func TestDoTranslate(t *testing.T) { ) body = `{ - "destination": "en", - "original": "текст для перевода", + "destination": "en", + "original": "текст для перевода" }` Test(t, Description("DoTranslate Fail"), - Post("http://localhost:8080/api/v1/translation/do-translate"), + Post(basePath+"/translation/do-translate"), Send().Headers("Content-Type").Add("application/json"), Send().Body().String(body), Expect().Status().Equal(http.StatusBadRequest), @@ -45,7 +83,7 @@ func TestDoTranslate(t *testing.T) { func TestHistory(t *testing.T) { Test(t, Description("History Success"), - Get("http://localhost:8080/api/v1/translation/history"), + Get(basePath+"/translation/history"), Expect().Status().Equal(http.StatusOK), Expect().Body().String().Contains(`{"history":[{`), ) diff --git a/internal/app/migrate.go b/internal/app/migrate.go index 3011918a..f3200d4c 100644 --- a/internal/app/migrate.go +++ b/internal/app/migrate.go @@ -5,6 +5,7 @@ package app import ( "errors" "log" + "os" "time" "github.com/golang-migrate/migrate/v4" @@ -13,16 +14,25 @@ import ( ) func init() { - var err error - var m *migrate.Migrate + databaseURL, ok := os.LookupEnv("GOT_PG_URL") + if !ok || len(databaseURL) == 0 { + log.Fatalf("migrate: environment variable not declared: GOT_PG_URL") + } + + databaseURL += "?sslmode=disable" + + var ( + attempts = 20 + err error + m *migrate.Migrate + ) - attempts := 10 for attempts > 0 { - m, err = migrate.New("file://migrations", - "postgres://user:pass@db:5432/postgres?sslmode=disable") + m, err = migrate.New("file://migrations", databaseURL) if err == nil { break } + log.Printf("Migrate: postgres is trying to connect, attempts left: %d", attempts) time.Sleep(time.Second) attempts-- @@ -33,8 +43,15 @@ func init() { } err = m.Up() + defer m.Close() if err != nil && !errors.Is(err, migrate.ErrNoChange) { log.Fatalf("Migrate: up error: %s", err) } - m.Close() + + if errors.Is(err, migrate.ErrNoChange) { + log.Printf("Migrate: no change") + return + } + + log.Printf("Migrate: up success") } diff --git a/migrations/000001_migrate_name.down.sql b/migrations/20210221023242_migrate_name.down.sql similarity index 100% rename from migrations/000001_migrate_name.down.sql rename to migrations/20210221023242_migrate_name.down.sql diff --git a/migrations/000001_migrate_name.up.sql b/migrations/20210221023242_migrate_name.up.sql similarity index 100% rename from migrations/000001_migrate_name.up.sql rename to migrations/20210221023242_migrate_name.up.sql From eb440ea439b2e8b6f81f7e89faaed870f0b15a2d Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Fri, 26 Feb 2021 04:23:51 +0400 Subject: [PATCH 11/15] [GOT-23] Strict linters --- .circleci/config.yml | 2 +- .golangci.yml | 584 +++++------------------------- go.mod | 11 +- go.sum | 77 +--- integration-test/rest_api_test.go | 5 +- internal/app/app.go | 4 +- internal/app/configuration.go | 4 +- internal/service/translation.go | 4 +- pkg/logger/loggers.go | 2 +- pkg/logger/zap.go | 2 +- 10 files changed, 116 insertions(+), 579 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 05dd46c2..ded19710 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,7 +14,7 @@ orbs: executors: default: docker: - - image: circleci/golang:1.15 + - image: circleci/golang:1.16 aliases: - &restore-mod-cache diff --git a/.golangci.yml b/.golangci.yml index 10458630..f01fdb4c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,521 +1,135 @@ -# This file contains all available configuration options -# with their default values. - -# options for analysis running -run: - # default concurrency is a available CPU number - concurrency: 4 - - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 1m - - # exit code when at least one issue was found, default is 1 - issues-exit-code: 1 - - # include test files or not, default is true - tests: false - - # default is true. Enables skipping of directories: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs-use-default: true - # - # which dirs to skip: they won't be analyzed; - # can use regexp here: generated.*, regexp is applied on full path; - skip-dirs: - - docs # autogenerated - - # which files to skip: they will be analyzed, but issues from them - # won't be reported. Default value is empty list, but there is - # no need to include all autogenerated files, we confidently recognize - # autogenerated files. If it's not please let us know. - #skip-files: - # - ".*\\.my\\.go$" - # - lib/bad.go - -# output configuration options -output: - # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - format: colored-line-number - - # print lines of code with issue, default is true - print-issued-lines: true - - # print linter name in the end of issue text, default is true - print-linter-name: true - -# This file contains all available configuration options -# with their default values. - -# all available settings of specific linters linters-settings: - dogsled: - # checks assignments with too many blank identifiers; default is 2 - max-blank-identifiers: 2 + gci: + local-prefixes: github.com/evrone/go-service-template dupl: - # tokens count to trigger issue, 150 by default - threshold: 150 + threshold: 100 + errorlint: + errorf: true errcheck: - # report about not checking of errors in type assetions: `a := b.(MyStruct)`; check-type-assertions: true - # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; check-blank: true - # path to a file containing a list of functions to exclude from checking - # see https://github.com/kisielk/errcheck#excluding-functions for details - #exclude: /path/to/file.txt exhaustive: - # check switch statements in generated files also check-generated: false - # indicates that switch statements are to be considered exhaustive if a - # 'default' case is present, even if all enum members aren't listed in the - # switch default-signifies-exhaustive: false funlen: - lines: 80 + lines: 60 statements: 40 - gci: - # put imports beginning with prefix after 3rd-party packages; - # only support one prefix - # if not set, use goimports.local-prefixes - local-prefixes: github.com/evrone/go-service-template gocognit: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 15 - nestif: - # minimal complexity of if statements to report, 5 by default - min-complexity: 4 + min-complexity: 10 + gocyclo: + min-complexity: 10 goconst: - # minimal length of string constant, 3 by default - min-len: 3 - # minimal occurrences count to trigger, 3 by default - min-occurrences: 3 + min-len: 2 + min-occurrences: 2 gocritic: - # Which checks should be enabled; can't be combined with 'disabled-checks'; - # See https://go-critic.github.io/overview#checks-overview - # To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run` - # By default list of stable checks is used. - enabled-checks: - - nestingreduce - - ruleguard - - truncatecmp - - unnamedresult - - # Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty - disabled-checks: - - regexpMust - - # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks. - # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". enabled-tags: - - performance - - style - diagnostic - # - security - disabled-tags: - experimental - opinionated - - # Settings passed to gocritic. - # The settings key is the name of a supported gocritic checker. - # The list of supported checkers can be find in https://go-critic.github.io/overview. - settings: - captLocal: # must be valid enabled check name - # whether to restrict checker to params only (default true) - paramsOnly: true - elseif: - # whether to skip balanced if-else pairs (default true) - skipBalanced: true - hugeParam: - # size in bytes that makes the warning trigger (default 80) - sizeThreshold: 120 - nestingReduce: - # min number of statements inside a branch to trigger a warning (default 5) - bodyWidth: 5 - rangeExprCopy: - # size in bytes that makes the warning trigger (default 512) - sizeThreshold: 512 - # whether to check test functions (default true) - skipTestFuncs: true - rangeValCopy: - # size in bytes that makes the warning trigger (default 128) - sizeThreshold: 128 - # whether to check test functions (default true) - skipTestFuncs: true - ruleguard: - # path to a gorules file for the ruleguard checker - rules: '' - truncateCmp: - # whether to skip int/uint/uintptr types (default true) - skipArchDependent: true - underef: - # whether to skip (*x).method() calls where x is a pointer receiver (default true) - skipRecvDeref: true - unnamedResult: - # whether to check exported functions - checkExported: true - gocyclo: - # minimal code complexity to report, 30 by default (but we recommend 10-20) - min-complexity: 15 - godot: - # comments to be checked: `declarations`, `toplevel`, or `all` - scope: declarations - # list of regexps for excluding particular comment lines from check - exclude: - # example: exclude comments which contain numbers - # - '[0-9]+' - # check that each sentence starts with a capital letter - capital: true - godox: - # report any comments starting with keywords, this is useful for TODO or FIXME comments that - # might be left in the code accidentally and should be resolved before merging - keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting - - NOTE - - TODO - - FIXME - - OPTIMIZE # marks code that should be optimized before merging - - HACK # marks hack-arounds that should be removed before merging - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - goheader: - values: - const: - # define here const type values in format k:v, for example: - # COMPANY: MY COMPANY - regexp: - # define here regexp type values, for example - # AUTHOR: .*@mycompany\.com - template: # | - # put here copyright header template for source code files, for example: - # Note: {{ YEAR }} is a builtin value that returns the year relative to the current machine time. - # - # {{ AUTHOR }} {{ COMPANY }} {{ YEAR }} - # SPDX-License-Identifier: Apache-2.0 - - # Licensed under the Apache License, Version 2.0 (the "License"); - # you may not use this file except in compliance with the License. - # You may obtain a copy of the License at: - - # http://www.apache.org/licenses/LICENSE-2.0 - - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - template-path: - # also as alternative of directive 'template' you may put the path to file with the template source - goimports: - # put imports beginning with prefix after 3rd-party packages; - # it's a comma-separated list of prefixes - local-prefixes: github.com/evrone/evrone-erp-ns + - performance + - style + disabled-checks: + - dupImport + - unnecessaryBlock golint: - # minimal confidence for issues, default is 0.8 min-confidence: 0.7 - gomnd: - settings: - mnd: - # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. - checks: argument,case,condition,operation,return,assign - ignored-numbers: - - 10 - - 100 - - 1000 - - 2 - - 3 - # ignored-files: magic_.*.go - # ignored-functions: math.* - gomodguard: - allowed: - modules: # List of allowed modules - # - gopkg.in/yaml.v2 - domains: # List of allowed module domains - # - golang.org - blocked: - modules: # List of blocked modules - # - github.com/uudashr/go-module: # Blocked module - # recommendations: # Recommended modules that should be used instead (Optional) - # - golang.org/x/mod - # reason: "`mod` is the official go.mod parser library." # Reason why the recommended module should be used (Optional) - versions: # List of blocked module version constraints - # - github.com/mitchellh/go-homedir: # Blocked module with version constraint - # version: "< 1.1.0" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons - # reason: "testing if blocked version constraint works." # Reason why the version constraint exists. (Optional) - local_replace_directives: false # Set to true to raise lint issues for packages that are loaded from a local path via replace directive + gofumpt: + extra-rules: true govet: - # report about shadowed variables check-shadowing: true - - # settings per analyzer - settings: - printf: # analyzer name, run `go tool vet help` to see all analyzers - funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer - - Infof - - Warnf - - Errorf - - Fatalf - - Prependf - - Appendf - - # enable or disable analyzers by name - # enable: - # - atomicalign - enable-all: true - disable: - - shadow - - gocheknoinits - disable-all: false - depguard: - list-type: blacklist - include-go-root: false - packages: - - github.com/sirupsen/logrus - packages-with-error-message: - # specify an error message to output when a blacklisted package is used - - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log" - ifshort: - # Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax. - # Has higher priority than max-decl-chars. - max-decl-lines: 1 - # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax. - max-decl-chars: 30 lll: - # max line length, lines longer will be reported. Default is 120. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option line-length: 120 - # tab width in spaces. Default to 1. - tab-width: 3 maligned: - # print struct with more effective memory layout or not, false by default suggest-new: true misspell: - # Correct spellings using locale preferences for US or UK. - # Default is to use a neutral variety of English. - # Setting locale to US will correct the British spelling of 'colour' to 'color'. locale: US - ignore-words: - - evrone - nakedret: - # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 - max-func-lines: 40 - prealloc: - # For most programs usage of prealloc will be a premature optimization. - - # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. - # True by default. - simple: true - range-loops: true # Report preallocation suggestions on range loops, true by default - for-loops: false # Report preallocation suggestions on for loops, false by default - predeclared: - # comma-separated list of predeclared identifiers to not report on - ignore: "" - # include method names and field names (i.e., qualified names) in checks - q: false + nestif: + min-complexity: 4 nolintlint: - # Enable to ensure that nolint directives are all used. Default is true. - allow-unused: true # generate some false-positive - # Disable to ensure that nolint directives don't have a leading space. Default is true. - allow-leading-space: true - # Exclude following linters from requiring an explanation. Default is []. - allow-no-explanation: [] - # Enable to require an explanation of nonzero length after each nolint directive. Default is false. require-explanation: true - # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. - require-specific: false - rowserrcheck: - packages: - - github.com/jmoiron/sqlx - testpackage: - # regexp pattern to skip files - skip-regexp: (export|internal)_test\.go - thelper: - # The following configurations enable all checks. It can be omitted because all checks are enabled by default. - # You can enable only required checks deleting unnecessary checks. - test: - first: true - name: true - begin: true - benchmark: - first: true - name: true - begin: true - tb: - first: true - name: true - begin: true - unparam: - # Inspect exported functions, default is false. Set to true if no external program/library imports your code. - # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find external interfaces. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - unused: - # treat code as a program (not a library) and report unused exported identifiers; default is false. - # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: - # if it's called for subdir of a project it can't find funcs usages. All text editor integrations - # with golangci-lint call it on a directory with the changed file. - check-exported: false - whitespace: - multi-if: false # Enforces newlines (or comments) after every multi-line if statement - multi-func: false # Enforces newlines (or comments) after every multi-line function signature - wsl: - # If true append is only allowed to be cuddled if appending value is - # matching variables, fields or types on line above. Default is true. - strict-append: true - # Allow calls and assignments to be cuddled as long as the lines have any - # matching variables, fields or types. Default is true. - allow-assign-and-call: true - # Allow multiline assignments to be cuddled. Default is true. - allow-multiline-assign: true - # Allow declarations (var) to be cuddled. - allow-cuddle-declarations: false - # Allow trailing comments in ending of blocks - allow-trailing-comment: false - # Force newlines in end of case at this limit (0 = never). - force-case-trailing-whitespace: 0 - # Force cuddling of err checks with err var assignment - force-err-cuddling: false - # Allow leading comments to be separated with empty liens - allow-separated-leading-comment: false - gofumpt: - # Choose whether or not to use the extra rules that are disabled - # by default - extra-rules: false - errorlint: - # Report non-wrapping error creation using fmt.Errorf - errorf: true - makezero: - # Allow only slices initialized with a length of zero. Default is false. - always: false - forbidigo: - # Forbid the following identifiers - forbid: - - fmt.Print.* # too much log noise - - log.Print.* # don't use default logger - # Exclude godoc examples from forbidigo checks. Default is true. - exclude_godoc_examples: false - - # The custom section can be used to define linter plugins to be loaded at runtime. See README doc - # for more info. - custom: - # Each custom linter should have a unique name. - example: - # The path to the plugin *.so. Can be absolute or local. Required for each custom linter - # path: /path/to/example.so - # The description of the linter. Optional, just for documentation purposes. - # description: This is an example usage of a plugin linter. - # Intended to point to the repo location of the linter. Optional, just for documentation purposes. - # original-url: github.com/golangci/example-linter + require-specific: true linters: - enable-all: true - disable: - - exhaustivestruct - - maligned - - testpackage - - prealloc - - ifshort - disable-all: false - fast: false - + disable-all: true + enable: + - asciicheck + - bodyclose + - cyclop + - deadcode + - depguard + - dogsled + - dupl + - durationcheck + - errcheck + - errorlint + - exhaustive + - exportloopref + - forbidigo + - funlen + - gci + - gochecknoglobals + - gochecknoinits + - gocognit + - goconst + - gocritic + - gocyclo + - godot + - godox + - goerr113 + - gofmt + - gofumpt + - goimports + - golint + - gomnd + - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - lll + - makezero + - misspell + - nakedret + - nestif + - nlreturn + - noctx + - nolintlint + - paralleltest + - predeclared + - revive + - rowserrcheck + - scopelint + - sqlclosecheck + - staticcheck + - structcheck + - stylecheck + - tparallel + - thelper + - typecheck + - unconvert + - unparam + - unused + - varcheck + - wrapcheck + - wsl + - whitespace + +# disable: +# - exhaustivestruct +# - ifshort +# - goheader +# - maligned +# - prealloc +# - testpackage issues: - # List of regexps of issue texts to exclude, empty list by default. - # But independently from this option we use default exclude patterns, - # it can be disabled by `exclude-use-default: false`. To list all - # excluded by default patterns execute `golangci-lint run --help` - exclude: - - abcdef - - # Excluding configuration per-path, per-linter, per-text and per-source exclude-rules: - # Exclude some linters from running on tests files. - - path: _test\.go - linters: - - gocyclo - - gocognit - - errcheck - - dupl - - gosec - - # Exclude known linters from partially hard-vendored code, - # which is impossible to exclude via "nolint" comments. - - path: internal/hmac/ - text: "weak cryptographic primitive" + - path: integration-test linters: - - gosec + - paralleltest - # Exclude some staticcheck messages - - linters: - - staticcheck - text: "SA9003:" - - - linters: - - golint - text: "should have comment or be unexported" - - # Exclude lll issues for long lines with go:generate - - linters: - - lll - source: "^//go:generate " - - # Independently from option `exclude` we use default exclude patterns, - # it can be disabled by this option. To list all - # excluded by default patterns execute `golangci-lint run --help`. - # Default value for this option is true. - exclude-use-default: false - - # The default value is false. If set to true exclude and exclude-rules - # regular expressions become case sensitive. - exclude-case-sensitive: false - - # The list of ids of default excludes to include or disable. By default it's empty. - include: - - EXC0002 # disable excluding of issues about comments from golint - - # Maximum issues count per one linter. Set to 0 to disable. Default is 50. - max-issues-per-linter: 0 - - # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. - max-same-issues: 0 - - # Show only new issues: if there are unstaged changes or untracked files, - # only those changes are analyzed, else only changes in HEAD~ are analyzed. - # It's a super-useful option for integration of golangci-lint into existing - # large codebase. It's not practical to fix all existing issues at the moment - # of integration: much better don't allow issues in new code. - # Default is false. - new: false - - # Show only new issues created after git revision `REV` - # new-from-rev: REV - - # Show only new issues created in git patch with set file path. - # new-from-patch: path/to/patch/file - - # Fix found issues (if it's supported by the linter) - fix: true - -severity: - # Default value is empty string. - # Set the default severity for issues. If severity rules are defined and the issues - # do not match or no severity is provided to the rule this will be the default - # severity applied. Severities should match the supported severity names of the - # selected out format. - # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity - # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity - # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message - default-severity: error - - # The default value is false. - # If set to true severity-rules regular expressions become case sensitive. - case-sensitive: false - - # Default value is empty list. - # When a list of severity rules are provided, severity information will be added to lint - # issues. Severity rules have the same filtering capability as exclude rules except you - # are allowed to specify one matcher per severity rule. - # Only affects out formats that support setting severity information. - rules: - - linters: - - dupl - severity: info +run: + skip-dirs: + - docs \ No newline at end of file diff --git a/go.mod b/go.mod index 8b4761ae..d1a92320 100644 --- a/go.mod +++ b/go.mod @@ -4,24 +4,15 @@ go 1.16 require ( github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb - github.com/Eun/go-convert v0.0.0-20200813135915-a28ba853dd5b // indirect github.com/Eun/go-hit v0.5.20 github.com/Masterminds/squirrel v1.5.0 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 - github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e // indirect github.com/gin-gonic/gin v1.6.3 - github.com/go-openapi/spec v0.20.3 // indirect github.com/golang-migrate/migrate/v4 v4.14.1 github.com/jackc/pgx/v4 v4.10.1 - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.8 // indirect github.com/rollbar/rollbar-go v1.2.0 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 github.com/swaggo/gin-swagger v1.3.0 github.com/swaggo/swag v1.7.0 - github.com/tidwall/pretty v1.0.5 // indirect - go.uber.org/zap v1.10.0 - golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect - golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect - golang.org/x/tools v0.1.0 // indirect + go.uber.org/zap v1.16.0 ) diff --git a/go.sum b/go.sum index 4f307970..aedc08b0 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,6 @@ github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb h1:MwdqXipO github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb/go.mod h1:J9k1GS4/KUFdJgWCp8Od4J3XXalm3pUPm8YYrMFzYTk= github.com/Eun/go-convert v0.0.0-20200421145326-bef6c56666ee h1:9oCc9EfVVSuy2WoHLAEYppJ5zX45+MQhAU1W30Uu3SI= github.com/Eun/go-convert v0.0.0-20200421145326-bef6c56666ee/go.mod h1:cMqWKb0SQrV+L1Zve08CI1NQGPeRAjXuYTxYE/y6gcU= -github.com/Eun/go-convert v0.0.0-20200813135915-a28ba853dd5b h1:xUFPNiC2U7rKAiRfR3E5wrbRehcy96fGFaaUa7ZEDOY= -github.com/Eun/go-convert v0.0.0-20200813135915-a28ba853dd5b/go.mod h1:cMqWKb0SQrV+L1Zve08CI1NQGPeRAjXuYTxYE/y6gcU= github.com/Eun/go-doppelgangerreader v0.0.0-20190911075941-30f1527f16b2 h1:RfkLLL7sQdxTMWRLo//6CZcAN3j5/laO8BooS9ctG2g= github.com/Eun/go-doppelgangerreader v0.0.0-20190911075941-30f1527f16b2/go.mod h1:+o+i8cYK1XYOQo4ocUKNV4R9D5Y7MIAPJk2l5SEh93M= github.com/Eun/go-hit v0.5.20 h1:vzZJAG8pF02OZSpljASBb8HlqK3b4f8pSPnOuX9Qip0= @@ -71,8 +69,6 @@ github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QN github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI= github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1 h1:TEBmxO80TM04L8IuMWk77SGL1HomBmKTdzdJLLWznxI= github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI= -github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e h1:OjdSMCht0ZVX7IH0nTdf00xEustvbtUGRgMh3gbdmOg= -github.com/araddon/dateparse v0.0.0-20210207001429-0eec95c9db7e/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= @@ -93,13 +89,10 @@ github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPK github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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= @@ -122,7 +115,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= @@ -140,25 +132,17 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= github.com/go-openapi/spec v0.19.14 h1:r4fbYFo6N4ZelmSX8G6p+cv/hZRXzcuqQIADGT1iNKM= github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA= -github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ= -github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.11 h1:RFTu/dlFySpyVvJDfp/7674JY4SDglYWKztbiIGFpmc= github.com/go-openapi/swag v0.19.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= @@ -217,7 +201,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -319,11 +302,8 @@ github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -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/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -340,14 +320,11 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o 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/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 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/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= @@ -367,9 +344,6 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= 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/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -377,8 +351,6 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -388,15 +360,12 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= @@ -422,24 +391,19 @@ 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rollbar/rollbar-go v1.2.0 h1:CUanFtVu0sa3QZ/fBlgevdGQGLWaE3D4HxoVSQohDfo= github.com/rollbar/rollbar-go v1.2.0/go.mod h1:czC86b8U4xdUH7W2C6gomi2jutLm8qK0OtrF5WMvpcc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= @@ -448,10 +412,8 @@ github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0= github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU= -github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -471,22 +433,17 @@ github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6A github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.0.5 h1:RlE8xFRj9RrbHWXXNGa4MymvI1Ovlm70omjXQ1sWgFU= -github.com/tidwall/pretty v1.0.5/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/traefik/yaegi v0.9.8/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= github.com/traefik/yaegi v0.9.10/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q= github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc= github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4= github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= @@ -505,7 +462,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -517,8 +473,9 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -529,7 +486,6 @@ golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899 h1:DZhuSZLsGlFL4CmhA8BcRA0mnthyA/nZ00AqCUo7vHg= golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -551,14 +507,11 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -608,8 +561,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201029221708-28c70e62bb1d/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180227000427-d7d64896b5ff/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -668,28 +619,18 @@ golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY= golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -712,7 +653,6 @@ golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -746,13 +686,10 @@ golang.org/x/tools v0.0.0-20200818005847-188abfa75333/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20201026223136-e84cfc6dd5ca/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e h1:t96dS3DO8DGjawSLJL/HIdz8CycAd2v07XxqB3UPTi0= golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -840,7 +777,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -856,25 +792,18 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= diff --git a/integration-test/rest_api_test.go b/integration-test/rest_api_test.go index 010a8fc6..0bc3a533 100644 --- a/integration-test/rest_api_test.go +++ b/integration-test/rest_api_test.go @@ -10,11 +10,11 @@ import ( . "github.com/Eun/go-hit" ) -var basePath string +var basePath string //nolint:gochecknoglobals // it's necessary func TestMain(m *testing.M) { host, ok := os.LookupEnv("HOST") - if !ok || len(host) == 0 { + if !ok || host == "" { log.Fatalf("environment variable not declared: HOST") } @@ -33,6 +33,7 @@ func TestMain(m *testing.M) { func healthCheck(host string, attempts int) error { var err error + healthPath := "http://" + host + "/health" for attempts > 0 { diff --git a/internal/app/app.go b/internal/app/app.go index 0f118658..5ec1dd20 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -54,7 +54,9 @@ func Run() { translationService := service.NewTranslationService(translationRepository, translationWebAPI) // REST - handler := gin.Default() + handler := gin.New() + handler.Use(gin.Logger()) + handler.Use(gin.Recovery()) handler.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger handler.GET("/health", func(c *gin.Context) { c.Status(http.StatusOK) }) // K8s probe diff --git a/internal/app/configuration.go b/internal/app/configuration.go index 66e7269f..30a1ad2e 100644 --- a/internal/app/configuration.go +++ b/internal/app/configuration.go @@ -34,7 +34,7 @@ func NewConfig() Conf { func strEnv(env string) string { value, ok := os.LookupEnv(env) - if !ok || len(value) == 0 { + if !ok || value == "" { log.Fatalf("environment variable not declared: %s", env) } @@ -45,7 +45,7 @@ func intEnv(env string) int { var intValue int value, ok := os.LookupEnv(env) - if !ok || len(value) == 0 { + if !ok || value == "" { log.Fatalf("environment variable not declared: %s", env) } diff --git a/internal/service/translation.go b/internal/service/translation.go index 80b701e6..eac81d04 100644 --- a/internal/service/translation.go +++ b/internal/service/translation.go @@ -14,9 +14,9 @@ type translationService struct { webAPI webapi.Translation } -func NewTranslationService(repository repository.Translation, webAPI webapi.Translation) Translation { +func NewTranslationService(repo repository.Translation, webAPI webapi.Translation) Translation { return &translationService{ - repository: repository, + repository: repo, webAPI: webAPI, } } diff --git a/pkg/logger/loggers.go b/pkg/logger/loggers.go index 7dd6dbe0..0200e5b4 100644 --- a/pkg/logger/loggers.go +++ b/pkg/logger/loggers.go @@ -47,7 +47,7 @@ func (l *loggers) fatal(err error, msg string, fields ...Field) { } func rollbarMap(fields []Field) map[string]interface{} { - m := make(map[string]interface{}, len(fields)*2) + m := make(map[string]interface{}, len(fields)*2) //nolint:gomnd // fields number always 2 for _, field := range fields { m[field.Key] = field.Val } diff --git a/pkg/logger/zap.go b/pkg/logger/zap.go index e8dc84e5..917080e8 100644 --- a/pkg/logger/zap.go +++ b/pkg/logger/zap.go @@ -34,5 +34,5 @@ func NewZapLogger(logLevel string) *ZapLogger { } func (z *ZapLogger) Close() { - _ = z.Logger.Sync() //nolint:errcheck //can't log it + _ = z.Logger.Sync() //nolint:errcheck // can't log it } From f2ce594391d0f68a1deba3a6865661cb1982228a Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Sun, 28 Feb 2021 23:04:03 +0400 Subject: [PATCH 12/15] [GOT-12] rabbitmq rpc client, rpc server --- .circleci/bin/install.sh | 2 +- .circleci/config.yml | 6 +- .env | 21 +- .golangci.yml | 5 +- Makefile | 43 ++-- README.md | 16 +- docker-compose.yml | 15 +- go.mod | 4 + go.sum | 146 +++++++++++- .../{ => rest_api}/rest_api_test.go | 2 +- internal/app/app.go | 55 ++++- internal/app/configuration.go | 24 +- internal/app/migrate.go | 4 +- internal/delivery/amqp_rpc/router.go | 22 ++ internal/delivery/amqp_rpc/translation.go | 35 +++ internal/repository/translation_postgres.go | 28 +-- internal/service/translation.go | 22 +- internal/webapi/translation_google.go | 10 +- pkg/httpserver/http.go | 20 +- pkg/rmq/connect.go | 121 ++++++++++ pkg/rmq/errors.go | 15 ++ pkg/rmq/rpc_client.go | 215 ++++++++++++++++++ pkg/rmq/rpc_server.go | 124 ++++++++++ 23 files changed, 854 insertions(+), 101 deletions(-) rename integration-test/{ => rest_api}/rest_api_test.go (97%) create mode 100644 internal/delivery/amqp_rpc/router.go create mode 100644 internal/delivery/amqp_rpc/translation.go create mode 100644 pkg/rmq/connect.go create mode 100644 pkg/rmq/errors.go create mode 100644 pkg/rmq/rpc_client.go create mode 100644 pkg/rmq/rpc_server.go diff --git a/.circleci/bin/install.sh b/.circleci/bin/install.sh index a41f09b3..6fd66ba4 100755 --- a/.circleci/bin/install.sh +++ b/.circleci/bin/install.sh @@ -3,7 +3,7 @@ set -ex REVIEWDOG_VERSION=v0.11.0 -GOLANGCILINT_VERSION=v1.37.0 +GOLANGCILINT_VERSION=v1.37.1 HADOLINT_VERSION=v1.22.1 DOTENV_LINTER_VERSION=v3.0.0 diff --git a/.circleci/config.yml b/.circleci/config.yml index ded19710..0cee37fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -20,6 +20,7 @@ aliases: - &restore-mod-cache keys: - go-modules-v1-{{ checksum "go.sum" }} + - go-modules-v1- - &save-mod-cache key: go-modules-v1-{{ checksum "go.sum" }} @@ -77,10 +78,7 @@ jobs: version: 19.03.13 - run: name: Integration tests - command: docker-compose up --build -d && docker attach integration - - run: - name: Logs - command: docker-compose logs + command: docker-compose up --build --abort-on-container-exit --exit-code-from integration workflows: ci: diff --git a/.env b/.env index ecbee4d8..3a64153d 100644 --- a/.env +++ b/.env @@ -1,10 +1,13 @@ GIN_MODE=release -GOT_HTTP_API_PORT=8080 -GOT_PG_CONN_ATTEMPTS=20 -GOT_PG_POOL_MAX=1 -GOT_PG_URL=postgres://user:pass@localhost:5432/postgres -GOT_ROLLBAR_ACCESS_TOKEN= -GOT_ROLLBAR_ENVIRONMENT= -GOT_SERVICE_NAME=go-service-template -GOT_SERVICE_VERSION=0.0.0 -GOT_ZAP_LOG_LEVEL=debug +HTTP_API_PORT=8080 +PG_CONN_ATTEMPTS=20 +PG_POOL_MAX=1 +PG_URL=postgres://user:pass@localhost:5432/postgres +RMQ_EXCHANGE_NAME=exchange_name +RMQ_QUEUE_NAME=queue_name +RMQ_URL=amqp://guest:guest@localhost:5672/ +ROLLBAR_ACCESS_TOKEN= +ROLLBAR_ENVIRONMENT= +SERVICE_NAME=go-service-template +SERVICE_VERSION=0.0.0 +ZAP_LOG_LEVEL=debug diff --git a/.golangci.yml b/.golangci.yml index f01fdb4c..7398a6bc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -12,10 +12,10 @@ linters-settings: check-generated: false default-signifies-exhaustive: false funlen: - lines: 60 + lines: 65 statements: 40 gocognit: - min-complexity: 10 + min-complexity: 15 gocyclo: min-complexity: 10 goconst: @@ -87,7 +87,6 @@ linters: - gosimple - govet - ineffassign - - interfacer - lll - makezero - misspell diff --git a/Makefile b/Makefile index 0057520f..3b217777 100644 --- a/Makefile +++ b/Makefile @@ -1,21 +1,39 @@ include .env export -swag: - swag init -g internal/app/app.go - -run: swag - go mod tidy && go mod download && GIN_MODE=debug CGO_ENABLED=0 go run -tags migrate ./cmd/app - -compose-up-db: - docker-compose up --build -d --remove-orphans db && docker-compose logs -f +.PHONY: compose-up +.PHONY: compose-up-integration-test +.PHONY: compose-down +.PHONY: swag +.PHONY: run +.PHONY: docker-rm-volume +.PHONY: linter-golangci +.PHONY: linter-hadolint +.PHONY: linter-dotenv +.PHONY: test +.PHONY: integration-test +.PHONY: mock +.PHONY: migrate-create +.PHONY: migrate-up compose-up: - docker-compose up --build -d --remove-orphans && docker-compose logs -f + docker-compose up --build -d postgres rabbitmq && docker-compose logs -f + +compose-up-integration-test: + docker-compose up --build --abort-on-container-exit --exit-code-from integration compose-down: docker-compose down --remove-orphans +swag: + swag init -g internal/app/app.go + +#run: swag +# go mod tidy && go mod download && GIN_MODE=debug CGO_ENABLED=0 go run -tags migrate ./cmd/app + +run: + go mod tidy && go mod download && GIN_MODE=release CGO_ENABLED=0 go run -tags migrate ./cmd/app + docker-rm-volume: docker volume rm go-service-template_pg-data @@ -31,7 +49,7 @@ linter-dotenv: test: go test -v -cover -race ./internal/... -test-integration: +integration-test: go clean -testcache && HOST=localhost:8080 go test -v ./integration-test/... mock: @@ -40,8 +58,5 @@ mock: migrate-create: migrate create -ext sql -dir migrations 'migrate_name' -migrate: +migrate-up: migrate -path migrations -database '$(PG_URL)?sslmode=disable' up - -.PHONY: swag, run, compose-up-db, compose-up, compose-down, docker-rm-pg-data, linter-golangci, linter-hadolint -.PHONY: linter-dotenv, test, test-integration, mock, migrate-create, migrate \ No newline at end of file diff --git a/README.md b/README.md index ebf6ebba..6b868b0e 100644 --- a/README.md +++ b/README.md @@ -14,22 +14,22 @@ Quick start ---- Локальная разработка: ```sh -# DB container -$ make compose-up-db +# Postgres, RabbitMQ +$ make compose-up # Run app with migrations $ make run ``` -В контейнерах (можно запускать в ci): +Интеграционные тесты (можно запускать в ci): ```sh -# DB + app + migrations + integration tests -$ make compose-up +# DB, app + migrations, integration tests +$ make compose-up-integration-test ``` -Для работы RollBar нужно установить переменные в .env: +Для работы RollBar установить переменные в .env: ``` -GOT_ROLLBAR_ACCESS_TOKEN= -GOT_ROLLBAR_ENVIRONMENT= +ROLLBAR_ACCESS_TOKEN= +ROLLBAR_ENVIRONMENT= ``` diff --git a/docker-compose.yml b/docker-compose.yml index b9ecf50c..94d672cc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3.9' services: - db: + postgres: container_name: postgres image: postgres volumes: @@ -13,6 +13,13 @@ services: ports: - 5432:5432 + rabbitmq: + container_name: rabbitmq + image: rabbitmq:3.8.12-management + ports: + - 5672:5672 + - 15672:15672 + app: build: . container_name: app @@ -20,11 +27,13 @@ services: env_file: - .env environment: - GOT_PG_URL: 'postgres://user:pass@db:5432/postgres' + PG_URL: 'postgres://user:pass@postgres:5432/postgres' + RMQ_URL: 'amqp://guest:guest@rabbitmq:5672/' ports: - 8080:8080 depends_on: - - db + - postgres + - rabbitmq integration: build: diff --git a/go.mod b/go.mod index d1a92320..6babb736 100644 --- a/go.mod +++ b/go.mod @@ -9,8 +9,12 @@ require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 github.com/gin-gonic/gin v1.6.3 github.com/golang-migrate/migrate/v4 v4.14.1 + github.com/google/uuid v1.1.2 github.com/jackc/pgx/v4 v4.10.1 + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/pkg/errors v0.9.1 github.com/rollbar/rollbar-go v1.2.0 + github.com/streadway/amqp v1.0.0 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 github.com/swaggo/gin-swagger v1.3.0 github.com/swaggo/swag v1.7.0 diff --git a/go.sum b/go.sum index aedc08b0..04ffb4f9 100644 --- a/go.sum +++ b/go.sum @@ -13,31 +13,40 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg= +cloud.google.com/go v0.64.0 h1:xVP3LPvMjGT4J0a55y02Gw5y/dkY/rxGz58sfK1jqIo= cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1 h1:ukjixP1wl0LpnZ6LWtZJ0mX5tBmjp1f8Sqer8Z2OMUU= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/spanner v1.9.0 h1:WXuGWhUp5i7MeUMzMrJlodqJvSGtU0Cdw6BdHGgCgVo= cloud.google.com/go/spanner v1.9.0/go.mod h1:xvlEn0NZ5v1iJPYsBnUVRDNvccDxsBTEi16pJRKQVws= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/ClickHouse/clickhouse-go v1.3.12 h1:HvD2NhKPLSeO3Ots6YV0ePgs4l3wO0bLqa9Uk1yeMOs= github.com/ClickHouse/clickhouse-go v1.3.12/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb h1:MwdqXipO1SFnK2k1iIL8xlUkZC63KdZR6QB/DSIlYBk= github.com/Conight/go-googletrans v0.0.0-20200929083318-176776d061cb/go.mod h1:J9k1GS4/KUFdJgWCp8Od4J3XXalm3pUPm8YYrMFzYTk= @@ -47,8 +56,10 @@ github.com/Eun/go-doppelgangerreader v0.0.0-20190911075941-30f1527f16b2 h1:RfkLL github.com/Eun/go-doppelgangerreader v0.0.0-20190911075941-30f1527f16b2/go.mod h1:+o+i8cYK1XYOQo4ocUKNV4R9D5Y7MIAPJk2l5SEh93M= github.com/Eun/go-hit v0.5.20 h1:vzZJAG8pF02OZSpljASBb8HlqK3b4f8pSPnOuX9Qip0= github.com/Eun/go-hit v0.5.20/go.mod h1:UTxCIcWC8T2FxF0ul+uHUgQHOcsdbaKOarFsnPsOHOo= +github.com/Eun/go-testdoc v0.0.1 h1:a0sl5HFWsyT3FLGpJektXNwXbl9gfLOYo0p5RGISDEQ= github.com/Eun/go-testdoc v0.0.1/go.mod h1:uT+GeDi7TpqQx6MBkcfXD9nF15Q8IX+kTNEnUUPbuUo= github.com/Eun/yaegi-template v1.5.16/go.mod h1:eyFQ1QHbKLNHKpUvdjt8+99ZR1ji7lVVbduSK1M5N/U= +github.com/Eun/yaegi-template v1.5.18 h1:oDtMPkrl1B0bu8mV0LDNiMsSGOrs/rRQwV9PuGCMnoM= github.com/Eun/yaegi-template v1.5.18/go.mod h1:iVHjge496SWL7hLf1euBZIO40Bk0R38g6lu8iyvpc30= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= @@ -65,38 +76,59 @@ github.com/aaw/maybe_tls v0.0.0-20160803104303-89c499bcc6aa h1:6yJyU8MlPBB2enGJd github.com/aaw/maybe_tls v0.0.0-20160803104303-89c499bcc6aa/go.mod h1:I0wzMZvViQzmJjxK+AtfFAnqDCkQV/+r17PO1CCSYnU= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230 h1:5ultmol0yeX75oh1hY78uAFn3dupBQ/QUNxERCkiaUQ= github.com/apache/arrow/go/arrow v0.0.0-20200601151325-b2287a20f230/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= github.com/araddon/dateparse v0.0.0-20190622164848-0fb0a474d195/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI= github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1 h1:TEBmxO80TM04L8IuMWk77SGL1HomBmKTdzdJLLWznxI= github.com/araddon/dateparse v0.0.0-20200409225146-d820a6159ab1/go.mod h1:SLqhdZcd+dF3TEVL2RMoob5bBP5R1P1qkox+HtCBgGI= +github.com/aws/aws-sdk-go v1.17.7 h1:/4+rDPe0W95KBmNGYCG+NUvdL8ssPYBMxL+aSCg6nIA= github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= +github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051 h1:eApuUG8W2EtBVwxqLlY2wgoqDYOg3WvIHGvW4fUbbow= github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1 h1:pASeJT3R3YyVn+94qEPk0SnU1OQ20Jd/T+SPKy9xehY= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7 h1:6pwm8kMQKCmgUg0ZHTm5+/YvRK0s3THD/28+T6/kk4A= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 h1:XNT/Zf5l++1Pyg08/HV04ppB0gKxAqtZQBRYiYrUuYk= github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= +github.com/dave/jennifer v1.4.1 h1:XyqG6cn5RQsTj3qlWQTKlRGAyrTcsk1kUmWdZBzRjDw= github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= 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/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec h1:NfhRXXFDPxcF5Cwo06DzeIaE7uuJtAUhsDwH3LNsjos= github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dhui/dktest v0.3.3 h1:DBuH/9GFaWbDRa42qsut/hbQu+srAQ0rPWnUoiGX7CA= github.com/dhui/dktest v0.3.3/go.mod h1:EML9sP4sqJELHn4jV7B0TY8oF6077nk83/tz7M56jcQ= @@ -108,13 +140,19 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8= github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/fake-gcs-server v1.17.0 h1:OeH75kBZcZa3ZE+zz/mFdJ2btt9FgqfjI7gIh9+5fvk= github.com/fsouza/fake-gcs-server v1.17.0/go.mod h1:D1rTE4YCyHFNa99oyJJ5HyclvN/0uQR+pM/VdlL83bw= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc= github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w= @@ -126,8 +164,10 @@ github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6 github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= @@ -152,9 +192,13 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= +github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4 h1:vF83LI8tAakwEwvWZtrIEx7pOySacl2TOxx6eXk4ePo= github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -162,10 +206,13 @@ github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-migrate/migrate/v4 v4.14.1 h1:qmRd/rNGjM1r3Ve5gHd5ZplytrD02UcItYNxJ3iUHHE= github.com/golang-migrate/migrate/v4 v4.14.1/go.mod h1:l7Ks0Au6fYHuUIxUhQ0rcVX1uLlJg54C/VvW7tvxSz0= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -173,6 +220,7 @@ github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFU github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -191,9 +239,12 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v1.11.0 h1:O7CEyB8Cb3/DmtxODGtLHcEvpr81Jm5qLg/hsHnxA2A= github.com/google/flatbuffers v1.11.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -204,10 +255,15 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -215,32 +271,43 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99 h1:Ak8CrdlwwXwAZxzS66vgPt4U8yUZX7JwLvVR58FN5jM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gookit/color v1.3.7 h1:wtJz526qll4+TxVcUUaMJOzrXDCiPr6jlyB5VSOBd2U= github.com/gookit/color v1.3.7/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg= github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3 h1:l7vogWrq+zj8v5t/G69/eT13nAGs2H7cq+CI2nlnKdk= github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3/go.mod h1:296z3W7Xsrp2mlIY88ruDKscuvrkL6zXCNRtaYVshzw= +github.com/itchyny/go-flags v1.5.0 h1:Z5q2ist2sfDjDlExVPBrMqlsEDxDR2h4zuOElB0OEYI= github.com/itchyny/go-flags v1.5.0/go.mod h1:lenkYuCobuxLBAd/HGFE4LRoW8D3B6iXRQfWYJ+MNbA= github.com/itchyny/gojq v0.12.1 h1:pQJrG8LXgEbZe9hvpfjKg7UlBfieQQydIw3YQq+7WIA= github.com/itchyny/gojq v0.12.1/go.mod h1:Y5Lz0qoT54ii+ucY/K3yNDy19qzxZvWNBMBpKUDQR/4= @@ -300,7 +367,9 @@ github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3 h1:JnPg/5Q9xVJGfjsO5CPUOjnJps1JaRUm8I9FXVCFK94= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -308,23 +377,31 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= github.com/k0kubun/pp v2.3.0+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/kisielk/errcheck v1.2.0 h1:reN85Pxc5larApoH1keMBiu2GWtPqXQ1nc9gx+jOU+E= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 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/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ktrysmt/go-bitbucket v0.6.4 h1:C8dUGp0qkwncKtAnozHCbbqhptefzEd1I0sfnuy9rYQ= github.com/ktrysmt/go-bitbucket v0.6.4/go.mod h1:9u0v3hsd2rqCHRIpbir1oP7F58uo5dq19sBYvuMoyQ4= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= @@ -344,13 +421,15 @@ github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/markbates/pkger v0.15.1 h1:3MPelV53RnGSW07izx5xGxl4e/sdRD6zqseIk0rMASY= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= @@ -359,9 +438,12 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238 h1:+MZW2uvHgN8kYvksEN3f7eFL2wpzk0GxmlFsMybWc7E= github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -371,49 +453,69 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mutecomm/go-sqlcipher/v4 v4.4.0 h1:sV1tWCWGAVlPhNGT95Q+z/txFxuhAYWwHD1afF5bMZg= github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY= +github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8 h1:P48LjvUQpTReR3TQRbxSeSBsMXzfK0uol7eRcr7VBYQ= github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= +github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba h1:fhFP5RliM2HW/8XdcO5QngSfFli9GcRIpMXvypTQt6E= github.com/neo4j/neo4j-go-driver v1.8.1-0.20200803113522-b626aa943eba/go.mod h1:ncO5VaFWh0Nrt+4KT4mOZboaczBZcLuHrG+/sUeP8gI= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= 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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 h1:HQagqIiBmr8YXawX/le3+O26N+vPPC1PtjaF3mwnook= github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rollbar/rollbar-go v1.2.0 h1:CUanFtVu0sa3QZ/fBlgevdGQGLWaE3D4HxoVSQohDfo= github.com/rollbar/rollbar-go v1.2.0/go.mod h1:czC86b8U4xdUH7W2C6gomi2jutLm8qK0OtrF5WMvpcc= +github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0 h1:uPRuwkWF4J6fGsJ2R0Gn2jB1EQiav9k3S6CSdygQJXY= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY= github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce h1:CGR1hXCOeoZ1aJhCs8qdKJuEu3xoZnxsLcYoh5Bnr+4= github.com/snowflakedb/glog v0.0.0-20180824191149-f5055e6f21ce/go.mod h1:EB/w24pR5VKI60ecFnKqXzxX3dOorz1rnVicQTQrGM0= +github.com/snowflakedb/gosnowflake v1.3.5 h1:/Ep0cXv4/3o+iXQvh+6CDjHCRPk2AM42l/AMR9PM94Q= github.com/snowflakedb/gosnowflake v1.3.5/go.mod h1:13Ky+lxzIm3VqNDZJdyvu9MCGy+WgRdYFdXp96UcLZU= +github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -434,6 +536,7 @@ github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1C github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/traefik/yaegi v0.9.8/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= +github.com/traefik/yaegi v0.9.10 h1:/b8qQg8/2D2yaTT3b49QtpFiiOBGBYblDldqO7Tu2R8= github.com/traefik/yaegi v0.9.10/go.mod h1:FAYnRlZyuVlEkvnkHq3bvJ1lW5be6XuwgLdkYgYG6Lk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -443,22 +546,32 @@ github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljT github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4= github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/xanzy/go-gitlab v0.15.0 h1:rWtwKTgEnXyNUGrOArN7yyc3THRkpYcKXIXia9abywQ= github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= +github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= +github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= +go.mongodb.org/mongo-driver v1.1.0 h1:aeOqSrhl9eDRAap/3T5pCfMBEBxZ0vuXBP+RMtp2KX8= go.mongodb.org/mongo-driver v1.1.0/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -498,8 +611,10 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6 h1:QE6XYQK6naiK1EPAe1g/ILLxN5RBoH5xkJk3CqlMI/Y= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -513,6 +628,7 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= @@ -567,6 +683,7 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -576,6 +693,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180224232135-f6cff0780e54/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -631,6 +749,7 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -708,6 +827,7 @@ google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.0.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= @@ -716,6 +836,7 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -780,12 +901,19 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -799,6 +927,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -808,18 +937,33 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +modernc.org/b v1.0.0 h1:vpvqeyp17ddcQWF29Czawql4lDdABCDRbXRAS4+aF2o= modernc.org/b v1.0.0/go.mod h1:uZWcZfRj1BpYzfN9JTerzlNUnnPsV9O2ZA8JsRcubNg= +modernc.org/db v1.0.0 h1:2c6NdCfaLnshSvY7OU09cyAY0gYXUZj4lmg5ItHyucg= modernc.org/db v1.0.0/go.mod h1:kYD/cO29L/29RM0hXYl4i3+Q5VojL31kTUVpVJDw0s8= +modernc.org/file v1.0.0 h1:9/PdvjVxd5+LcWUQIfapAWRGOkDLK90rloa8s/au06A= modernc.org/file v1.0.0/go.mod h1:uqEokAEn1u6e+J45e54dsEA/pw4o7zLrA2GwyntZzjw= +modernc.org/fileutil v1.0.0 h1:Z1AFLZwl6BO8A5NldQg/xTSjGLetp+1Ubvl4alfGx8w= modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= +modernc.org/golex v1.0.0 h1:wWpDlbK8ejRfSyi0frMyhilD3JBvtcx2AdGDnU+JtsE= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= +modernc.org/internal v1.0.0 h1:XMDsFDcBDsibbBnHB2xzljZ+B1yrOVLEFkKL2u15Glw= modernc.org/internal v1.0.0/go.mod h1:VUD/+JAkhCpvkUitlEOnhpVxCgsBI90oTzSCRcqQVSM= +modernc.org/lldb v1.0.0 h1:6vjDJxQEfhlOLwl4bhpwIz00uyFK4EmSYcbwqwbynsc= modernc.org/lldb v1.0.0/go.mod h1:jcRvJGWfCGodDZz8BPwiKMJxGJngQ/5DrRapkQnLob8= +modernc.org/mathutil v1.0.0 h1:93vKjrJopTPrtTNpZ8XIovER7iCIH1QU7wNbOQXC60I= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= +modernc.org/ql v1.0.0 h1:bIQ/trWNVjQPlinI6jdOQsi195SIturGo3mp5hsDqVU= modernc.org/ql v1.0.0/go.mod h1:xGVyrLIatPcO2C1JvI/Co8c0sr6y91HKFNy4pt9JXEY= +modernc.org/sortutil v1.1.0 h1:oP3U4uM+NT/qBQcbg/K2iqAX0Nx7B1b6YZtq3Gk/PjM= modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k= +modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/zappy v1.0.0 h1:dPVaP+3ueIUv4guk8PuZ2wiUGcJ1WUVvIheeSSTD0yk= modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= +rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/integration-test/rest_api_test.go b/integration-test/rest_api/rest_api_test.go similarity index 97% rename from integration-test/rest_api_test.go rename to integration-test/rest_api/rest_api_test.go index 0bc3a533..fe4230cd 100644 --- a/integration-test/rest_api_test.go +++ b/integration-test/rest_api/rest_api_test.go @@ -34,7 +34,7 @@ func TestMain(m *testing.M) { func healthCheck(host string, attempts int) error { var err error - healthPath := "http://" + host + "/health" + healthPath := "http://" + host + "/healthz" for attempts > 0 { err = Do(Get(healthPath), Expect().Status().Equal(http.StatusOK)) diff --git a/internal/app/app.go b/internal/app/app.go index 5ec1dd20..25f4c696 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -11,6 +11,7 @@ import ( swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" + amqprpc "github.com/evrone/go-service-template/internal/delivery/amqp_rpc" v1 "github.com/evrone/go-service-template/internal/delivery/http/v1" v2 "github.com/evrone/go-service-template/internal/delivery/http/v2" "github.com/evrone/go-service-template/internal/repository" @@ -19,6 +20,7 @@ import ( "github.com/evrone/go-service-template/pkg/httpserver" "github.com/evrone/go-service-template/pkg/logger" "github.com/evrone/go-service-template/pkg/postgres" + "github.com/evrone/go-service-template/pkg/rmq" ) // @title Go Service Template API @@ -29,7 +31,7 @@ import ( // @BasePath /api/v1/ // Run like main, runs application. -func Run() { +func Run() { //nolint:funlen // it's ok conf := NewConfig() // Logger @@ -53,18 +55,41 @@ func Run() { // Service translationService := service.NewTranslationService(translationRepository, translationWebAPI) + // RabbitMQ Client + rmqClient := rmq.NewClient("rpc_client", "rpc_server") + + // RabbitMQ Server + rmqRouter := amqprpc.NewRouter(translationService) + rmqServer := rmq.NewServer(rmqRouter, "rpc_server") + + //nolint:gocritic // example + // Example RabbitMQ - RemoteCall + //go func() { + // type historyResponse struct { + // History []domain.Translation `json:"history"` + // } + // + // for i := 0; i < 100; i++ { + // var history historyResponse + // + // err := rmqClient.RemoteCall("getHistory", nil, &history) + // if err != nil { + // log.Println("Error!", err) + // } + // } + //}() + // REST handler := gin.New() handler.Use(gin.Logger()) handler.Use(gin.Recovery()) handler.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger - handler.GET("/health", func(c *gin.Context) { c.Status(http.StatusOK) }) // K8s probe + handler.GET("/healthz", func(c *gin.Context) { c.Status(http.StatusOK) }) // K8s probe v1.NewRouter(handler, translationService) v2.NewRouter(handler) - server := httpserver.NewServer(handler, conf.HTTPAPIPort) - server.Start() + httpServer := httpserver.NewServer(handler, conf.HTTPAPIPort) // Graceful shutdown interrupt := make(chan os.Signal, 1) @@ -73,12 +98,26 @@ func Run() { select { case s := <-interrupt: logger.Info("app - Run - signal: " + s.String()) - case err := <-server.Notify(): - logger.Error(err, "app - Run - server.Notify") + case err := <-httpServer.Notify(): + logger.Error(err, "app - Run - httpServer.Notify") + case err := <-rmqClient.Notify(): + logger.Error(err, "app - Run - rmqClient.Notify") + case err := <-rmqServer.Notify(): + logger.Error(err, "app - Run - rmqServer.Notify") + } + + err := httpServer.Shutdown() + if err != nil { + logger.Error(err, "app - Run - httpServer.Shutdown") + } + + err = rmqClient.Shutdown() + if err != nil { + logger.Error(err, "app - Run - rmqClient.Shutdown") } - err := server.Stop() + err = rmqServer.Shutdown() if err != nil { - logger.Error(err, "app - Run - server.Stop") + logger.Error(err, "app - Run - rmqServer.Shutdown") } } diff --git a/internal/app/configuration.go b/internal/app/configuration.go index 30a1ad2e..0d94b000 100644 --- a/internal/app/configuration.go +++ b/internal/app/configuration.go @@ -10,6 +10,9 @@ type Conf struct { ServiceName string ServiceVersion string ZapLogLevel string + RmqExchangeName string + RmqQueueName string + RmqURL string RollbarAccessToken string RollbarEnvironment string HTTPAPIPort string @@ -20,15 +23,18 @@ type Conf struct { func NewConfig() Conf { return Conf{ - ServiceName: strEnv("GOT_SERVICE_NAME"), - ServiceVersion: strEnv("GOT_SERVICE_VERSION"), - ZapLogLevel: strEnv("GOT_ZAP_LOG_LEVEL"), - RollbarAccessToken: strEnv("GOT_ROLLBAR_ACCESS_TOKEN"), - RollbarEnvironment: strEnv("GOT_ROLLBAR_ENVIRONMENT"), - HTTPAPIPort: strEnv("GOT_HTTP_API_PORT"), - PgURL: strEnv("GOT_PG_URL"), - PgPoolMax: intEnv("GOT_PG_POOL_MAX"), - PgConnAttempts: intEnv("GOT_PG_CONN_ATTEMPTS"), + ServiceName: strEnv("SERVICE_NAME"), + ServiceVersion: strEnv("SERVICE_VERSION"), + ZapLogLevel: strEnv("ZAP_LOG_LEVEL"), + RmqExchangeName: strEnv("RMQ_EXCHANGE_NAME"), + RmqQueueName: strEnv("RMQ_QUEUE_NAME"), + RmqURL: strEnv("RMQ_URL"), + RollbarAccessToken: strEnv("ROLLBAR_ACCESS_TOKEN"), + RollbarEnvironment: strEnv("ROLLBAR_ENVIRONMENT"), + HTTPAPIPort: strEnv("HTTP_API_PORT"), + PgURL: strEnv("PG_URL"), + PgPoolMax: intEnv("PG_POOL_MAX"), + PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), } } diff --git a/internal/app/migrate.go b/internal/app/migrate.go index f3200d4c..ed731400 100644 --- a/internal/app/migrate.go +++ b/internal/app/migrate.go @@ -14,9 +14,9 @@ import ( ) func init() { - databaseURL, ok := os.LookupEnv("GOT_PG_URL") + databaseURL, ok := os.LookupEnv("PG_URL") if !ok || len(databaseURL) == 0 { - log.Fatalf("migrate: environment variable not declared: GOT_PG_URL") + log.Fatalf("migrate: environment variable not declared: PG_URL") } databaseURL += "?sslmode=disable" diff --git a/internal/delivery/amqp_rpc/router.go b/internal/delivery/amqp_rpc/router.go new file mode 100644 index 00000000..c2e38ae2 --- /dev/null +++ b/internal/delivery/amqp_rpc/router.go @@ -0,0 +1,22 @@ +package amqprpc + +import ( + "github.com/evrone/go-service-template/internal/service" + "github.com/evrone/go-service-template/pkg/rmq" +) + +type router struct { + translationService service.Translation + routerMap map[string]rmq.CallHandler +} + +func NewRouter(translationService service.Translation) map[string]rmq.CallHandler { + r := &router{ + translationService: translationService, + routerMap: make(map[string]rmq.CallHandler), + } + + r.translationRoutes() + + return r.routerMap +} diff --git a/internal/delivery/amqp_rpc/translation.go b/internal/delivery/amqp_rpc/translation.go new file mode 100644 index 00000000..0b1e473f --- /dev/null +++ b/internal/delivery/amqp_rpc/translation.go @@ -0,0 +1,35 @@ +package amqprpc + +import ( + "encoding/json" + + "github.com/pkg/errors" + "github.com/streadway/amqp" + + "github.com/evrone/go-service-template/internal/domain" + "github.com/evrone/go-service-template/pkg/rmq" +) + +func (r *router) translationRoutes() { + r.routerMap["getHistory"] = r.getHistory() +} + +type historyResponse struct { + History []domain.Translation `json:"history"` +} + +func (r *router) getHistory() rmq.CallHandler { + return func(d *amqp.Delivery) ([]byte, error) { + translations, err := r.translationService.History() + if err != nil { + return nil, errors.Wrap(err, "amqp_rpc - router - getHistory - r.translationService.History") + } + + response, err := json.Marshal(historyResponse{translations}) + if err != nil { + return nil, errors.Wrap(err, "amqp_rpc - router - getHistory - json.Marshal") + } + + return response, nil + } +} diff --git a/internal/repository/translation_postgres.go b/internal/repository/translation_postgres.go index 5b820813..e3a6d041 100644 --- a/internal/repository/translation_postgres.go +++ b/internal/repository/translation_postgres.go @@ -8,26 +8,26 @@ import ( "github.com/evrone/go-service-template/pkg/postgres" ) -type translationRepository struct { +type TranslationRepository struct { *postgres.Postgres } -func NewTranslationRepository(pg *postgres.Postgres) Translation { - return &translationRepository{pg} +func NewTranslationRepository(pg *postgres.Postgres) *TranslationRepository { + return &TranslationRepository{pg} } -func (p *translationRepository) GetHistory(ctx context.Context) ([]domain.Translation, error) { - sql, _, err := p.Builder. +func (r *TranslationRepository) GetHistory(ctx context.Context) ([]domain.Translation, error) { + sql, _, err := r.Builder. Select("source, destination, original, translation"). From("history"). ToSql() if err != nil { - return nil, fmt.Errorf("translationRepository - GetHistory - p.Builder: %w", err) + return nil, fmt.Errorf("TranslationRepository - GetHistory - r.Builder: %w", err) } - rows, err := p.Pool.Query(ctx, sql) + rows, err := r.Pool.Query(ctx, sql) if err != nil { - return nil, fmt.Errorf("translationRepository - GetHistory - p.Pool.Query: %w", err) + return nil, fmt.Errorf("TranslationRepository - GetHistory - r.Pool.Query: %w", err) } defer rows.Close() @@ -38,7 +38,7 @@ func (p *translationRepository) GetHistory(ctx context.Context) ([]domain.Transl err = rows.Scan(&e.Source, &e.Destination, &e.Original, &e.Translation) if err != nil { - return nil, fmt.Errorf("translationRepository - GetHistory - rows.Scan: %w", err) + return nil, fmt.Errorf("TranslationRepository - GetHistory - rows.Scan: %w", err) } entities = append(entities, e) @@ -47,19 +47,19 @@ func (p *translationRepository) GetHistory(ctx context.Context) ([]domain.Transl return entities, nil } -func (p *translationRepository) Store(ctx context.Context, entity domain.Translation) error { - sql, args, err := p.Builder. +func (r *TranslationRepository) Store(ctx context.Context, entity domain.Translation) error { + sql, args, err := r.Builder. Insert("history"). Columns("source, destination, original, translation"). Values(entity.Source, entity.Destination, entity.Original, entity.Translation). ToSql() if err != nil { - return fmt.Errorf("translationRepository - Store - p.Builder: %w", err) + return fmt.Errorf("TranslationRepository - Store - r.Builder: %w", err) } - _, err = p.Pool.Exec(ctx, sql, args...) + _, err = r.Pool.Exec(ctx, sql, args...) if err != nil { - return fmt.Errorf("translationRepository - Store - p.Pool.Exec: %w", err) + return fmt.Errorf("TranslationRepository - Store - r.Pool.Exec: %w", err) } return nil diff --git a/internal/service/translation.go b/internal/service/translation.go index eac81d04..6810a383 100644 --- a/internal/service/translation.go +++ b/internal/service/translation.go @@ -9,36 +9,36 @@ import ( "github.com/evrone/go-service-template/internal/webapi" ) -type translationService struct { +type TranslationService struct { repository repository.Translation webAPI webapi.Translation } -func NewTranslationService(repo repository.Translation, webAPI webapi.Translation) Translation { - return &translationService{ +func NewTranslationService(repo repository.Translation, webAPI webapi.Translation) *TranslationService { + return &TranslationService{ repository: repo, webAPI: webAPI, } } -func (u *translationService) History() ([]domain.Translation, error) { - translations, err := u.repository.GetHistory(context.Background()) +func (s *TranslationService) History() ([]domain.Translation, error) { + translations, err := s.repository.GetHistory(context.Background()) if err != nil { - return nil, fmt.Errorf("translationService - History - u.repository.GetHistory: %w", err) + return nil, fmt.Errorf("TranslationService - History - s.repository.GetHistory: %w", err) } return translations, nil } -func (u *translationService) Translate(translation domain.Translation) (domain.Translation, error) { - translation, err := u.webAPI.Translate(translation) +func (s *TranslationService) Translate(translation domain.Translation) (domain.Translation, error) { + translation, err := s.webAPI.Translate(translation) if err != nil { - return domain.Translation{}, fmt.Errorf("translationService - Translate - u.webAPI.Translate: %w", err) + return domain.Translation{}, fmt.Errorf("TranslationService - Translate - s.webAPI.Translate: %w", err) } - err = u.repository.Store(context.Background(), translation) + err = s.repository.Store(context.Background(), translation) if err != nil { - return domain.Translation{}, fmt.Errorf("translationService - Translate - u.repository.Store: %w", err) + return domain.Translation{}, fmt.Errorf("TranslationService - Translate - s.repository.Store: %w", err) } return translation, nil diff --git a/internal/webapi/translation_google.go b/internal/webapi/translation_google.go index eb990ea1..8b7ee642 100644 --- a/internal/webapi/translation_google.go +++ b/internal/webapi/translation_google.go @@ -8,25 +8,25 @@ import ( "github.com/evrone/go-service-template/internal/domain" ) -type translationWebAPI struct { +type TranslationWebAPI struct { conf translator.Config } -func NewTranslationWebAPI() Translation { +func NewTranslationWebAPI() *TranslationWebAPI { conf := translator.Config{ UserAgent: []string{"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"}, ServiceUrls: []string{"translate.google.com"}, } - return &translationWebAPI{conf} + return &TranslationWebAPI{conf} } -func (t *translationWebAPI) Translate(translation domain.Translation) (domain.Translation, error) { +func (t *TranslationWebAPI) Translate(translation domain.Translation) (domain.Translation, error) { trans := translator.New(t.conf) result, err := trans.Translate(translation.Original, translation.Source, translation.Destination) if err != nil { - return domain.Translation{}, fmt.Errorf("translationWebAPI - Translate - trans.Translate: %w", err) + return domain.Translation{}, fmt.Errorf("TranslationWebAPI - Translate - trans.Translate: %w", err) } translation.Translation = result.Text diff --git a/pkg/httpserver/http.go b/pkg/httpserver/http.go index 6b553ed8..10aab077 100644 --- a/pkg/httpserver/http.go +++ b/pkg/httpserver/http.go @@ -10,31 +10,35 @@ import ( type Server struct { server http.Server - errors chan error + notify chan error } func NewServer(handler http.Handler, port string) *Server { - return &Server{ + s := &Server{ server: http.Server{ Addr: net.JoinHostPort("", port), Handler: handler, }, - errors: make(chan error, 1), + notify: make(chan error, 1), } + + s.start() + + return s } -func (s *Server) Start() { +func (s *Server) start() { go func() { - s.errors <- s.server.ListenAndServe() - close(s.errors) + s.notify <- s.server.ListenAndServe() + close(s.notify) }() } func (s *Server) Notify() <-chan error { - return s.errors + return s.notify } -func (s *Server) Stop() error { +func (s *Server) Shutdown() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) //nolint:gomnd // it's magic defer cancel() diff --git a/pkg/rmq/connect.go b/pkg/rmq/connect.go new file mode 100644 index 00000000..1d34e1ad --- /dev/null +++ b/pkg/rmq/connect.go @@ -0,0 +1,121 @@ +package rmq + +import ( + "log" + "time" + + "github.com/pkg/errors" + "github.com/streadway/amqp" + + "github.com/evrone/go-service-template/pkg/logger" +) + +type Connection struct { + url string + waitTime time.Duration + attempts int + consumerExchange string + + connection *amqp.Connection + channel *amqp.Channel + delivery <-chan amqp.Delivery +} + +func newConnection(consumerExchange string) *Connection { + conn := &Connection{ + url: "amqp://guest:guest@rabbitmq:5672/", + waitTime: 5 * time.Second, //nolint:gomnd // will be config + attempts: 10, //nolint:gomnd // will be config + consumerExchange: consumerExchange, + } + + err := conn.attemptConnect() + if err != nil { + logger.Fatal(err, "rmq - newConnection - conn.attemptConnect") + } + + return conn +} + +func (c *Connection) attemptConnect() error { + var err error + for i := c.attempts; i > 0; i-- { + if err = c.connect(); err == nil { + break + } + + log.Printf("RabbitMQ is trying to connect, attempts left: %d", i) + time.Sleep(c.waitTime) + } + + if err != nil { + return errors.Wrap(err, "rmq - attemptConnect - c.connect") + } + + return nil +} + +func (c *Connection) connect() error { + var err error + + c.connection, err = amqp.Dial(c.url) + if err != nil { + return errors.Wrap(err, "amqp.Dial") + } + + c.channel, err = c.connection.Channel() + if err != nil { + return errors.Wrap(err, "c.connection.Channel") + } + + err = c.channel.ExchangeDeclare( + c.consumerExchange, + "fanout", + false, + false, + false, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.connection.Channel") + } + + queue, err := c.channel.QueueDeclare( + "", + false, + false, + true, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.channel.QueueDeclare") + } + + err = c.channel.QueueBind( + queue.Name, + "", + c.consumerExchange, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.channel.QueueBind") + } + + c.delivery, err = c.channel.Consume( + queue.Name, + "", + false, + false, + false, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.channel.Consume") + } + + return nil +} diff --git a/pkg/rmq/errors.go b/pkg/rmq/errors.go new file mode 100644 index 00000000..f6800664 --- /dev/null +++ b/pkg/rmq/errors.go @@ -0,0 +1,15 @@ +package rmq + +type Error string + +func (e Error) Error() string { return string(e) } + +const ( + ErrTimeout = Error("timeout") + ErrInternalServer = Error("internal server error") + ErrBadHandler = Error("unregistered handler") +) + +const ( + Success = "success" +) diff --git a/pkg/rmq/rpc_client.go b/pkg/rmq/rpc_client.go new file mode 100644 index 00000000..41caddf5 --- /dev/null +++ b/pkg/rmq/rpc_client.go @@ -0,0 +1,215 @@ +package rmq + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/google/uuid" + "github.com/pkg/errors" + "github.com/streadway/amqp" +) + +type Message struct { + Queue string + Priority uint8 + ContentType string + Body []byte + ReplyTo string + CorrelationID string +} + +type pendingCall struct { + done chan struct{} + status string + body []byte +} + +type Client struct { + conn *Connection + serverExchange string + timeout time.Duration + error chan error + stop chan struct{} + + mx *sync.RWMutex + calls map[string]*pendingCall +} + +func NewClient(clientExchange, serverExchange string) *Client { + c := &Client{ + conn: newConnection(clientExchange), + serverExchange: serverExchange, + timeout: 2 * time.Second, //nolint:gomnd // will be config + error: make(chan error), + stop: make(chan struct{}), + mx: &sync.RWMutex{}, + calls: make(map[string]*pendingCall), + } + + go c.consumer() + + return c +} + +func (c *Client) publish(corrID, handler string, request interface{}) error { + var ( + requestBody []byte + err error + ) + + if request != nil { + requestBody, err = json.Marshal(request) + if err != nil { + return errors.New("json.Marshal") + } + } + + err = c.conn.channel.Publish(c.serverExchange, "", false, false, + amqp.Publishing{ + ContentType: "application/json", + CorrelationId: corrID, + ReplyTo: c.conn.consumerExchange, + Type: handler, + Body: requestBody, + }) + if err != nil { + return errors.Wrap(err, "c.channel.Publish") + } + + return nil +} + +func (c *Client) RemoteCall(handler string, request, response interface{}) error { //nolint:cyclop // complex func + select { + case <-c.stop: + time.Sleep(c.timeout) + select { + case <-c.stop: + return errors.New("rmq - Client - RemoteCall - connection closed") + default: + } + default: + } + + corrID := uuid.New().String() + + err := c.publish(corrID, handler, request) + if err != nil { + return errors.Wrap(err, "rmq - Client - RemoteCall - c.publish") + } + + call := &pendingCall{done: make(chan struct{})} + + c.addCall(corrID, call) + defer c.deleteCall(corrID) + + select { + case <-time.After(c.timeout): + return ErrTimeout + case <-call.done: + } + + if call.status == Success { + err = json.Unmarshal(call.body, &response) + if err != nil { + return errors.Wrap(err, "rmq - Client - RemoteCall - json.Unmarshal") + } + + return nil + } + + if call.status == ErrBadHandler.Error() { + return ErrBadHandler + } + + if call.status == ErrInternalServer.Error() { + return ErrInternalServer + } + + return nil +} + +func (c *Client) consumer() { + for { + select { + case <-c.stop: + return + case d, opened := <-c.conn.delivery: + if !opened { + c.reconnect() + + return + } + + _ = d.Ack(false) //nolint:errcheck // don't need this + + c.getCall(&d) + } + } +} + +func (c *Client) reconnect() { + close(c.stop) + + err := c.conn.attemptConnect() + if err != nil { + c.error <- err + close(c.error) + + return + } + + c.stop = make(chan struct{}) + + go c.consumer() +} + +func (c *Client) getCall(d *amqp.Delivery) { + c.mx.RLock() + call, ok := c.calls[d.CorrelationId] + c.mx.RUnlock() + + if !ok { + return + } + + call.status = d.Type + call.body = d.Body + close(call.done) +} + +func (c *Client) addCall(corrID string, call *pendingCall) { + c.mx.Lock() + c.calls[corrID] = call + c.mx.Unlock() +} + +func (c *Client) deleteCall(corrID string) { + c.mx.Lock() + delete(c.calls, corrID) + c.mx.Unlock() +} + +func (c *Client) Notify() <-chan error { + return c.error +} + +func (c *Client) Shutdown() error { + select { + case <-c.error: + return nil + default: + } + + close(c.stop) + time.Sleep(c.timeout) + + err := c.conn.connection.Close() + if err != nil { + return fmt.Errorf("rmq - Client - Shutdown - c.connection.Close: %w", err) + } + + return nil +} diff --git a/pkg/rmq/rpc_server.go b/pkg/rmq/rpc_server.go new file mode 100644 index 00000000..dcae0f68 --- /dev/null +++ b/pkg/rmq/rpc_server.go @@ -0,0 +1,124 @@ +package rmq + +import ( + "fmt" + "time" + + "github.com/streadway/amqp" + + "github.com/evrone/go-service-template/pkg/logger" +) + +type CallHandler func(*amqp.Delivery) ([]byte, error) + +type Server struct { + conn *Connection + timeout time.Duration + error chan error + stop chan struct{} + router map[string]CallHandler +} + +func NewServer(router map[string]CallHandler, serverExchange string) *Server { + s := &Server{ + conn: newConnection(serverExchange), + timeout: 2 * time.Second, //nolint:gomnd // will be config + error: make(chan error), + stop: make(chan struct{}), + router: router, + } + + go s.consumer() + + return s +} + +func (s *Server) consumer() { + for { + select { + case <-s.stop: + return + case d, opened := <-s.conn.delivery: + if !opened { + s.reconnect() + + return + } + + _ = d.Ack(false) //nolint:errcheck // don't need this + + s.serveCall(&d) + } + } +} + +func (s *Server) serveCall(d *amqp.Delivery) { + callHandler, ok := s.router[d.Type] + if !ok { + s.publish(d, nil, ErrBadHandler.Error()) + + return + } + + body, err := callHandler(d) + if err != nil { + s.publish(d, nil, ErrInternalServer.Error()) + + logger.Error(err, "rmq - Server - serveCall - callHandler") + + return + } + + s.publish(d, body, Success) +} + +func (s *Server) publish(d *amqp.Delivery, body []byte, status string) { + err := s.conn.channel.Publish(d.ReplyTo, "", false, false, + amqp.Publishing{ + ContentType: "application/json", + CorrelationId: d.CorrelationId, + Type: status, + Body: body, + }) + if err != nil { + logger.Error(err, "rmq - Server - publish - s.conn.channel.Publish") + } +} + +func (s *Server) reconnect() { + close(s.stop) + + err := s.conn.attemptConnect() + if err != nil { + s.error <- err + close(s.error) + + return + } + + s.stop = make(chan struct{}) + + go s.consumer() +} + +func (s *Server) Notify() <-chan error { + return s.error +} + +func (s *Server) Shutdown() error { + select { + case <-s.error: + return nil + default: + } + + close(s.stop) + time.Sleep(s.timeout) + + err := s.conn.connection.Close() + if err != nil { + return fmt.Errorf("rmq - Server - Shutdown - s.connection.Close: %w", err) + } + + return nil +} From 3add3140208563e74ded57fd2a702a2a746d6c6f Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Tue, 23 Mar 2021 19:38:31 +0400 Subject: [PATCH 13/15] [GOT-24] Config --- .env | 12 +- .env.example | 5 + .gitignore | 1 + .golangci.yml | 10 ++ Dockerfile | 5 +- Dockerfile.prod | 2 +- Makefile | 43 +++-- README.md | 8 +- cmd/app/main.go | 26 ++- config/config.go | 35 ++++ config/config.yml | 17 ++ docker-compose.yml | 2 - docs/docs.go | 2 +- docs/swagger.json | 2 +- docs/swagger.yaml | 2 +- go.mod | 3 + go.sum | 10 +- integration-test/Dockerfile | 4 +- integration-test/integration_test.go | 139 ++++++++++++++++ integration-test/rest_api/rest_api_test.go | 91 ----------- internal/app/app.go | 101 +++--------- internal/app/configuration.go | 64 -------- internal/delivery/amqp_rpc/router.go | 8 +- internal/delivery/amqp_rpc/translation.go | 13 +- internal/delivery/http/v1/router.go | 23 +++ internal/delivery/http/v1/translation.go | 2 - internal/delivery/http/v2/router.go | 15 -- internal/{repository => repo}/interface.go | 4 +- .../translation_postgres.go | 22 +-- internal/service/translation.go | 20 +-- pkg/httpserver/options.go | 26 +++ pkg/httpserver/{http.go => server.go} | 25 ++- pkg/logger/{logger_api.go => logger.go} | 6 +- pkg/postgres/options.go | 15 ++ pkg/postgres/postgres.go | 59 +++---- pkg/rabbitmq/rmq_rpc/client/options.go | 23 +++ .../rmq_rpc/client}/rpc_client.go | 67 +++++--- pkg/rabbitmq/rmq_rpc/connection.go | 115 +++++++++++++ pkg/{rmq => rabbitmq/rmq_rpc}/errors.go | 2 +- pkg/rabbitmq/rmq_rpc/server/options.go | 23 +++ pkg/rabbitmq/rmq_rpc/server/rpc_server.go | 151 ++++++++++++++++++ pkg/rmq/connect.go | 121 -------------- pkg/rmq/rpc_server.go | 124 -------------- 43 files changed, 805 insertions(+), 643 deletions(-) create mode 100644 .env.example create mode 100644 config/config.go create mode 100644 config/config.yml create mode 100644 integration-test/integration_test.go delete mode 100644 integration-test/rest_api/rest_api_test.go delete mode 100644 internal/app/configuration.go delete mode 100644 internal/delivery/http/v2/router.go rename internal/{repository => repo}/interface.go (67%) rename internal/{repository => repo}/translation_postgres.go (54%) create mode 100644 pkg/httpserver/options.go rename pkg/httpserver/{http.go => server.go} (57%) rename pkg/logger/{logger_api.go => logger.go} (77%) create mode 100644 pkg/postgres/options.go create mode 100644 pkg/rabbitmq/rmq_rpc/client/options.go rename pkg/{rmq => rabbitmq/rmq_rpc/client}/rpc_client.go (65%) create mode 100644 pkg/rabbitmq/rmq_rpc/connection.go rename pkg/{rmq => rabbitmq/rmq_rpc}/errors.go (94%) create mode 100644 pkg/rabbitmq/rmq_rpc/server/options.go create mode 100644 pkg/rabbitmq/rmq_rpc/server/rpc_server.go delete mode 100644 pkg/rmq/connect.go delete mode 100644 pkg/rmq/rpc_server.go diff --git a/.env b/.env index 3a64153d..76cde1b0 100644 --- a/.env +++ b/.env @@ -1,13 +1,5 @@ +DISABLE_SWAGGER_HTTP_HANDLER=true GIN_MODE=release -HTTP_API_PORT=8080 -PG_CONN_ATTEMPTS=20 -PG_POOL_MAX=1 +LOG_ROLLBAR_TOKEN= PG_URL=postgres://user:pass@localhost:5432/postgres -RMQ_EXCHANGE_NAME=exchange_name -RMQ_QUEUE_NAME=queue_name RMQ_URL=amqp://guest:guest@localhost:5672/ -ROLLBAR_ACCESS_TOKEN= -ROLLBAR_ENVIRONMENT= -SERVICE_NAME=go-service-template -SERVICE_VERSION=0.0.0 -ZAP_LOG_LEVEL=debug diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..76cde1b0 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +DISABLE_SWAGGER_HTTP_HANDLER=true +GIN_MODE=release +LOG_ROLLBAR_TOKEN= +PG_URL=postgres://user:pass@localhost:5432/postgres +RMQ_URL=amqp://guest:guest@localhost:5672/ diff --git a/.gitignore b/.gitignore index 85e7c1df..99e53607 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /.idea/ +.env \ No newline at end of file diff --git a/.golangci.yml b/.golangci.yml index 7398a6bc..aa3db7ba 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -35,6 +35,15 @@ linters-settings: min-confidence: 0.7 gofumpt: extra-rules: true + gomnd: + settings: + mnd: + checks: + - argument + - case + - condition + - operation + - return govet: check-shadowing: true lll: @@ -128,6 +137,7 @@ issues: - path: integration-test linters: - paralleltest + - godot run: skip-dirs: diff --git a/Dockerfile b/Dockerfile index aedf380f..8ee02f28 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,11 @@ # Step 1: Modules caching -FROM golang:1.16.0-alpine3.13 as modules +FROM golang:1.16.2-alpine3.13 as modules COPY go.mod go.sum /modules/ WORKDIR /modules RUN go mod download # Step 2: Builder -FROM golang:1.16.0-alpine3.13 as builder +FROM golang:1.16.2-alpine3.13 as builder COPY --from=modules /go/pkg /go/pkg COPY . /app WORKDIR /app @@ -14,6 +14,7 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ # Step 3: Final FROM scratch +COPY --from=builder /app/config /config COPY --from=builder /app/migrations /migrations COPY --from=builder /bin/app /app COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ diff --git a/Dockerfile.prod b/Dockerfile.prod index fe70f526..52b844f6 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,5 +1,5 @@ # Step 1: Builder -FROM golang:1.16.0-alpine3.13 as builder +FROM golang:1.16.2-alpine3.13 as builder COPY . /app WORKDIR /app RUN go mod download -x && \ diff --git a/Makefile b/Makefile index 3b217777..f16c49da 100644 --- a/Makefile +++ b/Makefile @@ -1,62 +1,59 @@ include .env export -.PHONY: compose-up -.PHONY: compose-up-integration-test -.PHONY: compose-down -.PHONY: swag -.PHONY: run -.PHONY: docker-rm-volume -.PHONY: linter-golangci -.PHONY: linter-hadolint -.PHONY: linter-dotenv -.PHONY: test -.PHONY: integration-test -.PHONY: mock -.PHONY: migrate-create -.PHONY: migrate-up - compose-up: docker-compose up --build -d postgres rabbitmq && docker-compose logs -f +.PHONY: compose-up compose-up-integration-test: docker-compose up --build --abort-on-container-exit --exit-code-from integration +.PHONY: compose-up-integration-test compose-down: docker-compose down --remove-orphans +.PHONY: compose-down -swag: - swag init -g internal/app/app.go - -#run: swag -# go mod tidy && go mod download && GIN_MODE=debug CGO_ENABLED=0 go run -tags migrate ./cmd/app +swag-v1: + swag init -g internal/delivery/http/v1/router.go +.PHONY: swag-v1 -run: - go mod tidy && go mod download && GIN_MODE=release CGO_ENABLED=0 go run -tags migrate ./cmd/app +run: swag-v1 + go mod tidy && go mod download && \ + DISABLE_SWAGGER_HTTP_HANDLER='' GIN_MODE=debug CGO_ENABLED=0 go run -tags migrate ./cmd/app +.PHONY: run docker-rm-volume: docker volume rm go-service-template_pg-data +.PHONY: docker-rm-volume linter-golangci: golangci-lint run +.PHONY: linter-golangci linter-hadolint: git ls-files --exclude='Dockerfile*' --ignored | xargs hadolint +.PHONY: linter-hadolint linter-dotenv: dotenv-linter +.PHONY: linter-dotenv test: go test -v -cover -race ./internal/... +.PHONY: test integration-test: - go clean -testcache && HOST=localhost:8080 go test -v ./integration-test/... + go clean -testcache && go test -v ./integration-test/... +.PHONY: integration-test mock: mockery --all -r --case snake +.PHONY: mock migrate-create: migrate create -ext sql -dir migrations 'migrate_name' +.PHONY: migrate-create migrate-up: migrate -path migrations -database '$(PG_URL)?sslmode=disable' up +.PHONY: migrate-up diff --git a/README.md b/README.md index 6b868b0e..05b3b860 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Quick start ---- +Скопировать и переименовать файл `.env.example` в `.env`. + Локальная разработка: ```sh # Postgres, RabbitMQ @@ -26,12 +28,6 @@ $ make run $ make compose-up-integration-test ``` -Для работы RollBar установить переменные в .env: -``` -ROLLBAR_ACCESS_TOKEN= -ROLLBAR_ENVIRONMENT= -``` - Clean Architecture diff --git a/cmd/app/main.go b/cmd/app/main.go index d3a39ce7..79e137c1 100644 --- a/cmd/app/main.go +++ b/cmd/app/main.go @@ -1,9 +1,33 @@ package main import ( + "log" + + "github.com/ilyakaznacheev/cleanenv" + + "github.com/evrone/go-service-template/config" "github.com/evrone/go-service-template/internal/app" + "github.com/evrone/go-service-template/pkg/logger" ) func main() { - app.Run() + // Configuration + var cfg config.Config + + err := cleanenv.ReadConfig("./config/config.yml", &cfg) + if err != nil { + log.Fatalf("Config error: %s", err) + } + + // Logger + zap := logger.NewZapLogger(cfg.Log.ZapLevel) + defer zap.Close() + + rollbar := logger.NewRollbarLogger(cfg.Log.RollbarToken, cfg.Log.RollbarEnv) + defer rollbar.Close() + + logger.NewAppLogger(zap, rollbar, cfg.App.Name, cfg.App.Version) + + // Run + app.Run(&cfg) } diff --git a/config/config.go b/config/config.go new file mode 100644 index 00000000..c6a822c2 --- /dev/null +++ b/config/config.go @@ -0,0 +1,35 @@ +package config + +type Config struct { + App `yaml:"app"` + HTTP `yaml:"http"` + Log `yaml:"logger"` + PG `yaml:"postgres"` + RMQ `yaml:"rabbitmq"` +} + +type App struct { + Name string `env-required:"true" yaml:"name" env:"APP_NAME"` + Version string `env-required:"true" yaml:"version" env:"APP_VERSION"` +} + +type HTTP struct { + Port string `env-required:"true" yaml:"port" env:"HTTP_PORT"` +} + +type Log struct { + ZapLevel string `env-required:"true" yaml:"zap_level" env:"LOG_ZAP_LEVEL"` + RollbarEnv string `env-required:"true" yaml:"rollbar_env" env:"LOG_ROLLBAR_ENV"` + RollbarToken string `env-required:"true" env:"LOG_ROLLBAR_TOKEN"` +} + +type PG struct { + PoolMax int `env-required:"true" yaml:"pool_max" env:"PG_POOL_MAX"` + URL string `env-required:"true" env:"PG_URL"` +} + +type RMQ struct { + ServerExchange string `env-required:"true" yaml:"rpc_server_exchange" env:"RMQ_RPC_SERVER"` + ClientExchange string `env-required:"true" yaml:"rpc_client_exchange" env:"RMQ_RPC_CLIENT"` + URL string `env-required:"true" env:"RMQ_URL"` +} diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 00000000..2e08a363 --- /dev/null +++ b/config/config.yml @@ -0,0 +1,17 @@ +app: + name: 'go-service-template' + version: '0.0.0' + +http: + port: '8080' + +logger: + zap_level: 'debug' + rollbar_env: 'go-service-template' + +postgres: + pool_max: 2 + +rabbitmq: + rpc_server_exchange: 'rpc_server' + rpc_client_exchange: 'rpc_client' diff --git a/docker-compose.yml b/docker-compose.yml index 94d672cc..81e2ebc3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,8 +41,6 @@ services: dockerfile: integration-test/Dockerfile container_name: integration image: integration - environment: - HOST: 'app:8080' depends_on: - app diff --git a/docs/docs.go b/docs/docs.go index 8ea50cca..18b22367 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -176,7 +176,7 @@ type swaggerInfo struct { var SwaggerInfo = swaggerInfo{ Version: "1.0", Host: "localhost:8080", - BasePath: "/api/v1/", + BasePath: "/api/v1", Schemes: []string{}, Title: "Go Service Template API", Description: "Using a translation service as an example", diff --git a/docs/swagger.json b/docs/swagger.json index 2436b230..f7f7af67 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -7,7 +7,7 @@ "version": "1.0" }, "host": "localhost:8080", - "basePath": "/api/v1/", + "basePath": "/api/v1", "paths": { "/translation/do-translate": { "post": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 2fc36b94..67ba6be7 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,4 +1,4 @@ -basePath: /api/v1/ +basePath: /api/v1 definitions: domain.Translation: properties: diff --git a/go.mod b/go.mod index 6babb736..de2976b5 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/gin-gonic/gin v1.6.3 github.com/golang-migrate/migrate/v4 v4.14.1 github.com/google/uuid v1.1.2 + github.com/ilyakaznacheev/cleanenv v1.2.5 github.com/jackc/pgx/v4 v4.10.1 github.com/mattn/go-colorable v0.1.8 // indirect github.com/pkg/errors v0.9.1 @@ -19,4 +20,6 @@ require ( github.com/swaggo/gin-swagger v1.3.0 github.com/swaggo/swag v1.7.0 go.uber.org/zap v1.16.0 + gopkg.in/yaml.v2 v2.4.0 // indirect + olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 // indirect ) diff --git a/go.sum b/go.sum index 04ffb4f9..4153451d 100644 --- a/go.sum +++ b/go.sum @@ -305,6 +305,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6 h1:UDMh68UUwekSh5iP2OMhRRZJiiBccgV7axzUG8vi56c= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ilyakaznacheev/cleanenv v1.2.5 h1:/SlcF9GaIvefWqFJzsccGG/NJdoaAwb7Mm7ImzhO3DM= +github.com/ilyakaznacheev/cleanenv v1.2.5/go.mod h1:/i3yhzwZ3s7hacNERGFwvlhwXMDcaqwIzmayEhbRplk= github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3 h1:l7vogWrq+zj8v5t/G69/eT13nAGs2H7cq+CI2nlnKdk= github.com/itchyny/astgen-go v0.0.0-20210113000433-0da0671862a3/go.mod h1:296z3W7Xsrp2mlIY88ruDKscuvrkL6zXCNRtaYVshzw= github.com/itchyny/go-flags v1.5.0 h1:Z5q2ist2sfDjDlExVPBrMqlsEDxDR2h4zuOElB0OEYI= @@ -371,6 +373,8 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -921,8 +925,9 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/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-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= @@ -961,6 +966,9 @@ modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/zappy v1.0.0 h1:dPVaP+3ueIUv4guk8PuZ2wiUGcJ1WUVvIheeSSTD0yk= modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4= +olympos.io/encoding/edn v0.0.0-20200308123125-93e3b8dd0e24/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3 h1:slmdOY3vp8a7KQbHkL+FLbvbkgMqmXojpFUO/jENuqQ= +olympos.io/encoding/edn v0.0.0-20201019073823-d3554ca0b0a3/go.mod h1:oVgVk4OWVDi43qWBEyGhXgYxt7+ED4iYNpTngSLX2Iw= rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= diff --git a/integration-test/Dockerfile b/integration-test/Dockerfile index 211e99e8..c45620a1 100644 --- a/integration-test/Dockerfile +++ b/integration-test/Dockerfile @@ -1,11 +1,11 @@ # Step 1: Modules caching -FROM golang:1.16.0-alpine3.13 as modules +FROM golang:1.16.2-alpine3.13 as modules COPY go.mod go.sum /modules/ WORKDIR /modules RUN go mod download # Step 2: Builder -FROM golang:1.16.0-alpine3.13 as builder +FROM golang:1.16.2-alpine3.13 as builder COPY --from=modules /go/pkg /go/pkg COPY . /app WORKDIR /app diff --git a/integration-test/integration_test.go b/integration-test/integration_test.go new file mode 100644 index 00000000..8e525eeb --- /dev/null +++ b/integration-test/integration_test.go @@ -0,0 +1,139 @@ +package integration_test + +import ( + "log" + "net/http" + "os" + "testing" + "time" + + . "github.com/Eun/go-hit" + + "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc/client" +) + +const ( + // Attempts connection + host = "app:8080" + healthPath = "http://" + host + "/healthz" + attempts = 20 + + // HTTP REST + basePath = "http://" + host + "/api/v1" + + // RabbitMQ RPC + rmqURL = "amqp://guest:guest@rabbitmq:5672/" + rpcServerExchange = "rpc_server" + rpcClientExchange = "rpc_client" + requests = 10 +) + +func TestMain(m *testing.M) { + err := healthCheck(attempts) + if err != nil { + log.Fatalf("Integration tests: host %s is not available: %s", host, err) + } + + log.Printf("Integration tests: host %s is available", host) + + code := m.Run() + os.Exit(code) +} + +func healthCheck(attempts int) error { + var err error + + for attempts > 0 { + err = Do(Get(healthPath), Expect().Status().Equal(http.StatusOK)) + if err == nil { + return nil + } + + log.Printf("Integration tests: url %s is not available, attempts left: %d", healthPath, attempts) + + time.Sleep(time.Second) + + attempts-- + } + + return err +} + +// HTTP POST: /translation/do-translate. +func TestHTTPDoTranslate(t *testing.T) { + body := `{ + "destination": "en", + "original": "текст для перевода", + "source": "auto" + }` + Test(t, + Description("DoTranslate Success"), + Post(basePath+"/translation/do-translate"), + Send().Headers("Content-Type").Add("application/json"), + Send().Body().String(body), + Expect().Status().Equal(http.StatusOK), + Expect().Body().JSON().JQ(".translation").Equal("text for translation"), + ) + + body = `{ + "destination": "en", + "original": "текст для перевода" + }` + Test(t, + Description("DoTranslate Fail"), + Post(basePath+"/translation/do-translate"), + Send().Headers("Content-Type").Add("application/json"), + Send().Body().String(body), + Expect().Status().Equal(http.StatusBadRequest), + Expect().Body().JSON().JQ(".error").Equal("invalid request body"), + ) +} + +// HTTP GET: /translation/history. +func TestHTTPHistory(t *testing.T) { + Test(t, + Description("History Success"), + Get(basePath+"/translation/history"), + Expect().Status().Equal(http.StatusOK), + Expect().Body().String().Contains(`{"history":[{`), + ) +} + +// RabbitMQ RPC Client: getHistory. +func TestRMQClientRPC(t *testing.T) { + rmqClient, err := client.NewClient(rmqURL, rpcServerExchange, rpcClientExchange) + if err != nil { + t.Fatal("RabbitMQ RPC Client - init error - client.NewClient") + } + + defer func() { + err = rmqClient.Shutdown() + if err != nil { + t.Fatal("RabbitMQ RPC Client - shutdown error - rmqClient.RemoteCall", err) + } + }() + + type Translation struct { + Source string `json:"source"` + Destination string `json:"destination"` + Original string `json:"original"` + Translation string `json:"translation"` + } + + type historyResponse struct { + History []Translation `json:"history"` + } + + for i := 0; i < requests; i++ { + var history historyResponse + + err = rmqClient.RemoteCall("getHistory", nil, &history) + if err != nil { + t.Fatal("RabbitMQ RPC Client - remote call error - rmqClient.RemoteCall", err) + } + + if history.History[0].Original != "текст для перевода" { + t.Fatal("Original != текст для перевода") + } + } +} diff --git a/integration-test/rest_api/rest_api_test.go b/integration-test/rest_api/rest_api_test.go deleted file mode 100644 index fe4230cd..00000000 --- a/integration-test/rest_api/rest_api_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package rest_api_test - -import ( - "log" - "net/http" - "os" - "testing" - "time" - - . "github.com/Eun/go-hit" -) - -var basePath string //nolint:gochecknoglobals // it's necessary - -func TestMain(m *testing.M) { - host, ok := os.LookupEnv("HOST") - if !ok || host == "" { - log.Fatalf("environment variable not declared: HOST") - } - - err := healthCheck(host, 20) - if err != nil { - log.Fatalf("Integration tests: host %s is not available: %s", host, err) - } - - log.Printf("Integration tests: host %s is available", host) - - basePath = "http://" + host + "/api/v1" - - code := m.Run() - os.Exit(code) -} - -func healthCheck(host string, attempts int) error { - var err error - - healthPath := "http://" + host + "/healthz" - - for attempts > 0 { - err = Do(Get(healthPath), Expect().Status().Equal(http.StatusOK)) - if err == nil { - return nil - } - - log.Printf("Integration tests: url %s is not available, attempts left: %d", healthPath, attempts) - - time.Sleep(time.Second) - - attempts-- - } - - return err -} - -func TestDoTranslate(t *testing.T) { - body := `{ - "destination": "en", - "original": "текст для перевода", - "source": "auto" - }` - Test(t, - Description("DoTranslate Success"), - Post(basePath+"/translation/do-translate"), - Send().Headers("Content-Type").Add("application/json"), - Send().Body().String(body), - Expect().Status().Equal(http.StatusOK), - Expect().Body().JSON().JQ(".translation").Equal("text for translation"), - ) - - body = `{ - "destination": "en", - "original": "текст для перевода" - }` - Test(t, - Description("DoTranslate Fail"), - Post(basePath+"/translation/do-translate"), - Send().Headers("Content-Type").Add("application/json"), - Send().Body().String(body), - Expect().Status().Equal(http.StatusBadRequest), - Expect().Body().JSON().JQ(".error").Equal("invalid request body"), - ) -} - -func TestHistory(t *testing.T) { - Test(t, - Description("History Success"), - Get(basePath+"/translation/history"), - Expect().Status().Equal(http.StatusOK), - Expect().Body().String().Contains(`{"history":[{`), - ) -} diff --git a/internal/app/app.go b/internal/app/app.go index 25f4c696..98b701e5 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -2,120 +2,71 @@ package app import ( - "net/http" "os" "os/signal" "syscall" "github.com/gin-gonic/gin" - swaggerFiles "github.com/swaggo/files" - ginSwagger "github.com/swaggo/gin-swagger" + "github.com/evrone/go-service-template/config" amqprpc "github.com/evrone/go-service-template/internal/delivery/amqp_rpc" v1 "github.com/evrone/go-service-template/internal/delivery/http/v1" - v2 "github.com/evrone/go-service-template/internal/delivery/http/v2" - "github.com/evrone/go-service-template/internal/repository" + "github.com/evrone/go-service-template/internal/repo" "github.com/evrone/go-service-template/internal/service" "github.com/evrone/go-service-template/internal/webapi" "github.com/evrone/go-service-template/pkg/httpserver" "github.com/evrone/go-service-template/pkg/logger" "github.com/evrone/go-service-template/pkg/postgres" - "github.com/evrone/go-service-template/pkg/rmq" + "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc/server" ) -// @title Go Service Template API -// @version 1.0 -// @description Using a translation service as an example - -// @host localhost:8080 -// @BasePath /api/v1/ - -// Run like main, runs application. -func Run() { //nolint:funlen // it's ok - conf := NewConfig() - - // Logger - zap := logger.NewZapLogger(conf.ZapLogLevel) - defer zap.Close() - - rollbar := logger.NewRollbarLogger(conf.RollbarAccessToken, conf.RollbarEnvironment) - defer rollbar.Close() - - logger.NewAppLogger(zap, rollbar, conf.ServiceName, conf.ServiceVersion) - +// Run creates objects via constructors. +func Run(cfg *config.Config) { // Repository - postgresDB := postgres.NewPostgres(conf.PgURL, conf.PgPoolMax, conf.PgConnAttempts) - defer postgresDB.Close() - - translationRepository := repository.NewTranslationRepository(postgresDB) - - // WebAPI - translationWebAPI := webapi.NewTranslationWebAPI() + pg, err := postgres.NewPostgres(cfg.PG.URL, postgres.MaxPoolSize(cfg.PG.PoolMax)) + if err != nil { + logger.Fatal(err, "app - Run - postgres.NewPostgres") + } + defer pg.Close() // Service - translationService := service.NewTranslationService(translationRepository, translationWebAPI) - - // RabbitMQ Client - rmqClient := rmq.NewClient("rpc_client", "rpc_server") + translationService := service.NewTranslationService( + repo.NewTranslationRepo(pg), + webapi.NewTranslationWebAPI(), + ) - // RabbitMQ Server + // RabbitMQ RPC Server rmqRouter := amqprpc.NewRouter(translationService) - rmqServer := rmq.NewServer(rmqRouter, "rpc_server") - //nolint:gocritic // example - // Example RabbitMQ - RemoteCall - //go func() { - // type historyResponse struct { - // History []domain.Translation `json:"history"` - // } - // - // for i := 0; i < 100; i++ { - // var history historyResponse - // - // err := rmqClient.RemoteCall("getHistory", nil, &history) - // if err != nil { - // log.Println("Error!", err) - // } - // } - //}() + rmqServer, err := server.NewServer(cfg.RMQ.URL, cfg.RMQ.ServerExchange, rmqRouter) + if err != nil { + logger.Fatal(err, "app - Run - rmqServer - server.NewServer") + } - // REST + // HTTP handler := gin.New() - handler.Use(gin.Logger()) - handler.Use(gin.Recovery()) - handler.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // Swagger - handler.GET("/healthz", func(c *gin.Context) { c.Status(http.StatusOK) }) // K8s probe - v1.NewRouter(handler, translationService) - v2.NewRouter(handler) + httpServer := httpserver.NewServer(handler, httpserver.Port(cfg.HTTP.Port)) - httpServer := httpserver.NewServer(handler, conf.HTTPAPIPort) - - // Graceful shutdown + // Waiting signal interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM) select { case s := <-interrupt: logger.Info("app - Run - signal: " + s.String()) - case err := <-httpServer.Notify(): + case err = <-httpServer.Notify(): logger.Error(err, "app - Run - httpServer.Notify") - case err := <-rmqClient.Notify(): - logger.Error(err, "app - Run - rmqClient.Notify") - case err := <-rmqServer.Notify(): + case err = <-rmqServer.Notify(): logger.Error(err, "app - Run - rmqServer.Notify") } - err := httpServer.Shutdown() + // Shutdown + err = httpServer.Shutdown() if err != nil { logger.Error(err, "app - Run - httpServer.Shutdown") } - err = rmqClient.Shutdown() - if err != nil { - logger.Error(err, "app - Run - rmqClient.Shutdown") - } - err = rmqServer.Shutdown() if err != nil { logger.Error(err, "app - Run - rmqServer.Shutdown") diff --git a/internal/app/configuration.go b/internal/app/configuration.go deleted file mode 100644 index 0d94b000..00000000 --- a/internal/app/configuration.go +++ /dev/null @@ -1,64 +0,0 @@ -package app - -import ( - "log" - "os" - "strconv" -) - -type Conf struct { - ServiceName string - ServiceVersion string - ZapLogLevel string - RmqExchangeName string - RmqQueueName string - RmqURL string - RollbarAccessToken string - RollbarEnvironment string - HTTPAPIPort string - PgURL string - PgPoolMax int - PgConnAttempts int -} - -func NewConfig() Conf { - return Conf{ - ServiceName: strEnv("SERVICE_NAME"), - ServiceVersion: strEnv("SERVICE_VERSION"), - ZapLogLevel: strEnv("ZAP_LOG_LEVEL"), - RmqExchangeName: strEnv("RMQ_EXCHANGE_NAME"), - RmqQueueName: strEnv("RMQ_QUEUE_NAME"), - RmqURL: strEnv("RMQ_URL"), - RollbarAccessToken: strEnv("ROLLBAR_ACCESS_TOKEN"), - RollbarEnvironment: strEnv("ROLLBAR_ENVIRONMENT"), - HTTPAPIPort: strEnv("HTTP_API_PORT"), - PgURL: strEnv("PG_URL"), - PgPoolMax: intEnv("PG_POOL_MAX"), - PgConnAttempts: intEnv("PG_CONN_ATTEMPTS"), - } -} - -func strEnv(env string) string { - value, ok := os.LookupEnv(env) - if !ok || value == "" { - log.Fatalf("environment variable not declared: %s", env) - } - - return value -} - -func intEnv(env string) int { - var intValue int - - value, ok := os.LookupEnv(env) - if !ok || value == "" { - log.Fatalf("environment variable not declared: %s", env) - } - - intValue, err := strconv.Atoi(value) - if err != nil { - log.Fatalf("typecast error to integer: %s", err) - } - - return intValue -} diff --git a/internal/delivery/amqp_rpc/router.go b/internal/delivery/amqp_rpc/router.go index c2e38ae2..e7c7358d 100644 --- a/internal/delivery/amqp_rpc/router.go +++ b/internal/delivery/amqp_rpc/router.go @@ -2,18 +2,18 @@ package amqprpc import ( "github.com/evrone/go-service-template/internal/service" - "github.com/evrone/go-service-template/pkg/rmq" + "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc/server" ) type router struct { translationService service.Translation - routerMap map[string]rmq.CallHandler + routerMap map[string]server.CallHandler } -func NewRouter(translationService service.Translation) map[string]rmq.CallHandler { +func NewRouter(translationService service.Translation) map[string]server.CallHandler { r := &router{ translationService: translationService, - routerMap: make(map[string]rmq.CallHandler), + routerMap: make(map[string]server.CallHandler), } r.translationRoutes() diff --git a/internal/delivery/amqp_rpc/translation.go b/internal/delivery/amqp_rpc/translation.go index 0b1e473f..b9d1c66a 100644 --- a/internal/delivery/amqp_rpc/translation.go +++ b/internal/delivery/amqp_rpc/translation.go @@ -1,13 +1,11 @@ package amqprpc import ( - "encoding/json" - "github.com/pkg/errors" "github.com/streadway/amqp" "github.com/evrone/go-service-template/internal/domain" - "github.com/evrone/go-service-template/pkg/rmq" + "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc/server" ) func (r *router) translationRoutes() { @@ -18,17 +16,14 @@ type historyResponse struct { History []domain.Translation `json:"history"` } -func (r *router) getHistory() rmq.CallHandler { - return func(d *amqp.Delivery) ([]byte, error) { +func (r *router) getHistory() server.CallHandler { + return func(d *amqp.Delivery) (interface{}, error) { translations, err := r.translationService.History() if err != nil { return nil, errors.Wrap(err, "amqp_rpc - router - getHistory - r.translationService.History") } - response, err := json.Marshal(historyResponse{translations}) - if err != nil { - return nil, errors.Wrap(err, "amqp_rpc - router - getHistory - json.Marshal") - } + response := historyResponse{translations} return response, nil } diff --git a/internal/delivery/http/v1/router.go b/internal/delivery/http/v1/router.go index abe335c6..92fef33e 100644 --- a/internal/delivery/http/v1/router.go +++ b/internal/delivery/http/v1/router.go @@ -2,11 +2,24 @@ package v1 import ( + "net/http" + "github.com/gin-gonic/gin" + swaggerFiles "github.com/swaggo/files" + ginSwagger "github.com/swaggo/gin-swagger" + // Swagger docs. + _ "github.com/evrone/go-service-template/docs" "github.com/evrone/go-service-template/internal/service" ) +// Swagger spec: +// @title Go Service Template API +// @description Using a translation service as an example +// @version 1.0 +// @host localhost:8080 +// @BasePath /api/v1 + type router struct { translationService service.Translation } @@ -14,6 +27,16 @@ type router struct { func NewRouter(handler *gin.Engine, translationService service.Translation) { r := &router{translationService} + handler.Use(gin.Logger()) + handler.Use(gin.Recovery()) + + // Swagger + swaggerHandler := ginSwagger.DisablingWrapHandler(swaggerFiles.Handler, "DISABLE_SWAGGER_HTTP_HANDLER") + handler.GET("/swagger/*any", swaggerHandler) + + // K8s probe + handler.GET("/healthz", func(c *gin.Context) { c.Status(http.StatusOK) }) + api := handler.Group("/api/v1") { r.translationRouts(api) diff --git a/internal/delivery/http/v1/translation.go b/internal/delivery/http/v1/translation.go index f9ed5aac..48711ca5 100644 --- a/internal/delivery/http/v1/translation.go +++ b/internal/delivery/http/v1/translation.go @@ -5,8 +5,6 @@ import ( "github.com/gin-gonic/gin" - // Swagger docs. - _ "github.com/evrone/go-service-template/docs" "github.com/evrone/go-service-template/internal/domain" ) diff --git a/internal/delivery/http/v2/router.go b/internal/delivery/http/v2/router.go deleted file mode 100644 index cac799c2..00000000 --- a/internal/delivery/http/v2/router.go +++ /dev/null @@ -1,15 +0,0 @@ -// Package v2 is example version 2 HTTP API -package v2 - -import ( - "net/http" - - "github.com/gin-gonic/gin" -) - -func NewRouter(handler *gin.Engine) { - api := handler.Group("/api/test") - { - api.GET("/health", func(c *gin.Context) { c.JSON(http.StatusOK, "Hello from v2") }) - } -} diff --git a/internal/repository/interface.go b/internal/repo/interface.go similarity index 67% rename from internal/repository/interface.go rename to internal/repo/interface.go index fe2cef28..90d73235 100644 --- a/internal/repository/interface.go +++ b/internal/repo/interface.go @@ -1,5 +1,5 @@ -// Package repository implements databases for services. For each service in its own file. -package repository +// Package repo implements databases for services. For each service in its own file. +package repo import ( "context" diff --git a/internal/repository/translation_postgres.go b/internal/repo/translation_postgres.go similarity index 54% rename from internal/repository/translation_postgres.go rename to internal/repo/translation_postgres.go index e3a6d041..320b53d3 100644 --- a/internal/repository/translation_postgres.go +++ b/internal/repo/translation_postgres.go @@ -1,4 +1,4 @@ -package repository +package repo import ( "context" @@ -8,26 +8,26 @@ import ( "github.com/evrone/go-service-template/pkg/postgres" ) -type TranslationRepository struct { +type TranslationRepo struct { *postgres.Postgres } -func NewTranslationRepository(pg *postgres.Postgres) *TranslationRepository { - return &TranslationRepository{pg} +func NewTranslationRepo(pg *postgres.Postgres) *TranslationRepo { + return &TranslationRepo{pg} } -func (r *TranslationRepository) GetHistory(ctx context.Context) ([]domain.Translation, error) { +func (r *TranslationRepo) GetHistory(ctx context.Context) ([]domain.Translation, error) { sql, _, err := r.Builder. Select("source, destination, original, translation"). From("history"). ToSql() if err != nil { - return nil, fmt.Errorf("TranslationRepository - GetHistory - r.Builder: %w", err) + return nil, fmt.Errorf("TranslationRepo - GetHistory - r.Builder: %w", err) } rows, err := r.Pool.Query(ctx, sql) if err != nil { - return nil, fmt.Errorf("TranslationRepository - GetHistory - r.Pool.Query: %w", err) + return nil, fmt.Errorf("TranslationRepo - GetHistory - r.Pool.Query: %w", err) } defer rows.Close() @@ -38,7 +38,7 @@ func (r *TranslationRepository) GetHistory(ctx context.Context) ([]domain.Transl err = rows.Scan(&e.Source, &e.Destination, &e.Original, &e.Translation) if err != nil { - return nil, fmt.Errorf("TranslationRepository - GetHistory - rows.Scan: %w", err) + return nil, fmt.Errorf("TranslationRepo - GetHistory - rows.Scan: %w", err) } entities = append(entities, e) @@ -47,19 +47,19 @@ func (r *TranslationRepository) GetHistory(ctx context.Context) ([]domain.Transl return entities, nil } -func (r *TranslationRepository) Store(ctx context.Context, entity domain.Translation) error { +func (r *TranslationRepo) Store(ctx context.Context, entity domain.Translation) error { sql, args, err := r.Builder. Insert("history"). Columns("source, destination, original, translation"). Values(entity.Source, entity.Destination, entity.Original, entity.Translation). ToSql() if err != nil { - return fmt.Errorf("TranslationRepository - Store - r.Builder: %w", err) + return fmt.Errorf("TranslationRepo - Store - r.Builder: %w", err) } _, err = r.Pool.Exec(ctx, sql, args...) if err != nil { - return fmt.Errorf("TranslationRepository - Store - r.Pool.Exec: %w", err) + return fmt.Errorf("TranslationRepo - Store - r.Pool.Exec: %w", err) } return nil diff --git a/internal/service/translation.go b/internal/service/translation.go index 6810a383..df3da81c 100644 --- a/internal/service/translation.go +++ b/internal/service/translation.go @@ -5,26 +5,26 @@ import ( "fmt" "github.com/evrone/go-service-template/internal/domain" - "github.com/evrone/go-service-template/internal/repository" + "github.com/evrone/go-service-template/internal/repo" "github.com/evrone/go-service-template/internal/webapi" ) type TranslationService struct { - repository repository.Translation - webAPI webapi.Translation + repo repo.Translation + webAPI webapi.Translation } -func NewTranslationService(repo repository.Translation, webAPI webapi.Translation) *TranslationService { +func NewTranslationService(repository repo.Translation, webAPI webapi.Translation) *TranslationService { return &TranslationService{ - repository: repo, - webAPI: webAPI, + repo: repository, + webAPI: webAPI, } } func (s *TranslationService) History() ([]domain.Translation, error) { - translations, err := s.repository.GetHistory(context.Background()) + translations, err := s.repo.GetHistory(context.Background()) if err != nil { - return nil, fmt.Errorf("TranslationService - History - s.repository.GetHistory: %w", err) + return nil, fmt.Errorf("TranslationService - History - s.repo.GetHistory: %w", err) } return translations, nil @@ -36,9 +36,9 @@ func (s *TranslationService) Translate(translation domain.Translation) (domain.T return domain.Translation{}, fmt.Errorf("TranslationService - Translate - s.webAPI.Translate: %w", err) } - err = s.repository.Store(context.Background(), translation) + err = s.repo.Store(context.Background(), translation) if err != nil { - return domain.Translation{}, fmt.Errorf("TranslationService - Translate - s.repository.Store: %w", err) + return domain.Translation{}, fmt.Errorf("TranslationService - Translate - s.repo.Store: %w", err) } return translation, nil diff --git a/pkg/httpserver/options.go b/pkg/httpserver/options.go new file mode 100644 index 00000000..fa1dee12 --- /dev/null +++ b/pkg/httpserver/options.go @@ -0,0 +1,26 @@ +package httpserver + +import ( + "net" + "time" +) + +type Option func(*Server) + +func Port(port string) Option { + return func(s *Server) { + s.server.Addr = net.JoinHostPort("", port) + } +} + +func ReadTimeout(timeout time.Duration) Option { + return func(s *Server) { + s.server.ReadTimeout = timeout + } +} + +func WriteTimeout(timeout time.Duration) Option { + return func(s *Server) { + s.server.WriteTimeout = timeout + } +} diff --git a/pkg/httpserver/http.go b/pkg/httpserver/server.go similarity index 57% rename from pkg/httpserver/http.go rename to pkg/httpserver/server.go index 10aab077..74a5ac0c 100644 --- a/pkg/httpserver/http.go +++ b/pkg/httpserver/server.go @@ -3,25 +3,34 @@ package httpserver import ( "context" - "net" "net/http" "time" ) type Server struct { - server http.Server + server *http.Server notify chan error } -func NewServer(handler http.Handler, port string) *Server { +func NewServer(handler http.Handler, opts ...Option) *Server { + httpServer := &http.Server{ + Handler: handler, + // Default + ReadTimeout: 5 * time.Second, + WriteTimeout: 5 * time.Second, + Addr: ":80", + } + s := &Server{ - server: http.Server{ - Addr: net.JoinHostPort("", port), - Handler: handler, - }, + server: httpServer, notify: make(chan error, 1), } + // Set options + for _, opt := range opts { + opt(s) + } + s.start() return s @@ -39,7 +48,7 @@ func (s *Server) Notify() <-chan error { } func (s *Server) Shutdown() error { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) //nolint:gomnd // it's magic + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) //nolint:gomnd // it's magic defer cancel() return s.server.Shutdown(ctx) diff --git a/pkg/logger/logger_api.go b/pkg/logger/logger.go similarity index 77% rename from pkg/logger/logger_api.go rename to pkg/logger/logger.go index fb58beec..c4b4bd81 100644 --- a/pkg/logger/logger_api.go +++ b/pkg/logger/logger.go @@ -2,10 +2,10 @@ package logger var appLogger Logger //nolint:gochecknoglobals // it's necessary -func NewAppLogger(zap *ZapLogger, rollbar *RollbarLogger, serviceName, serviceVersion string) { +func NewAppLogger(zap *ZapLogger, rollbar *RollbarLogger, appName, appVersion string) { fields := []Field{ - {"service-name", serviceName}, - {"service-version", serviceVersion}, + {"app-name", appName}, + {"app-version", appVersion}, } appLogger = &loggers{zap, rollbar, fields} } diff --git a/pkg/postgres/options.go b/pkg/postgres/options.go new file mode 100644 index 00000000..d4cb5b01 --- /dev/null +++ b/pkg/postgres/options.go @@ -0,0 +1,15 @@ +package postgres + +type Option func(*Postgres) + +func MaxPoolSize(size int) Option { + return func(c *Postgres) { + c.maxPoolSize = size + } +} + +func ConnAttempts(attempts int) Option { + return func(c *Postgres) { + c.connAttempts = attempts + } +} diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go index 65762726..7c88e9c0 100644 --- a/pkg/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -3,59 +3,60 @@ package postgres import ( "context" + "log" "time" "github.com/Masterminds/squirrel" "github.com/jackc/pgx/v4/pgxpool" - - "github.com/evrone/go-service-template/pkg/logger" + "github.com/pkg/errors" ) type Postgres struct { - Pool *pgxpool.Pool - Builder squirrel.StatementBuilderType + maxPoolSize int + connAttempts int + Builder squirrel.StatementBuilderType + Pool *pgxpool.Pool } -func NewPostgres(url string, maxPoolSize, connAttempts int) *Postgres { +func NewPostgres(url string, opts ...Option) (*Postgres, error) { + pg := &Postgres{ + // Default + maxPoolSize: 1, + connAttempts: 10, + } + + // Set options + for _, opt := range opts { + opt(pg) + } + + pg.Builder = squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) + poolConfig, err := pgxpool.ParseConfig(url) if err != nil { - logger.Fatal(err, "postgres connect error") + return nil, errors.Wrap(err, "postgres - NewPostgres - pgxpool.ParseConfig") } - poolConfig.MaxConns = int32(maxPoolSize) + poolConfig.MaxConns = int32(pg.maxPoolSize) - var ( - errConn error - pool *pgxpool.Pool - ) - - for connAttempts > 0 { - pool, errConn = pgxpool.ConnectConfig(context.Background(), poolConfig) - if errConn == nil { + for pg.connAttempts > 0 { + pg.Pool, err = pgxpool.ConnectConfig(context.Background(), poolConfig) + if err == nil { break } - logger.Info("postgres is trying to connect", - logger.Field{Key: "attempts left", Val: connAttempts}, - ) + log.Printf("Postgres is trying to connect, attempts left: %d", pg.connAttempts) time.Sleep(time.Second) - connAttempts-- + pg.connAttempts-- } - if errConn != nil { - logger.Fatal(errConn, "postgres connect error") + if err != nil { + return nil, errors.Wrap(err, "postgres - NewPostgres - connAttempts == 0") } - logger.Info("postgres connected") - - builder := squirrel.StatementBuilder.PlaceholderFormat(squirrel.Dollar) - - return &Postgres{ - Pool: pool, - Builder: builder, - } + return pg, nil } func (p *Postgres) Close() { diff --git a/pkg/rabbitmq/rmq_rpc/client/options.go b/pkg/rabbitmq/rmq_rpc/client/options.go new file mode 100644 index 00000000..118305a5 --- /dev/null +++ b/pkg/rabbitmq/rmq_rpc/client/options.go @@ -0,0 +1,23 @@ +package client + +import "time" + +type Option func(*Client) + +func Timeout(timeout time.Duration) Option { + return func(c *Client) { + c.timeout = timeout + } +} + +func ConnWaitTime(timeout time.Duration) Option { + return func(c *Client) { + c.conn.WaitTime = timeout + } +} + +func ConnAttempts(attempts int) Option { + return func(c *Client) { + c.conn.Attempts = attempts + } +} diff --git a/pkg/rmq/rpc_client.go b/pkg/rabbitmq/rmq_rpc/client/rpc_client.go similarity index 65% rename from pkg/rmq/rpc_client.go rename to pkg/rabbitmq/rmq_rpc/client/rpc_client.go index 41caddf5..35032b00 100644 --- a/pkg/rmq/rpc_client.go +++ b/pkg/rabbitmq/rmq_rpc/client/rpc_client.go @@ -1,4 +1,4 @@ -package rmq +package client import ( "encoding/json" @@ -9,6 +9,8 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" "github.com/streadway/amqp" + + rmqrpc "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc" ) type Message struct { @@ -27,30 +29,49 @@ type pendingCall struct { } type Client struct { - conn *Connection + conn *rmqrpc.Connection serverExchange string - timeout time.Duration error chan error stop chan struct{} mx *sync.RWMutex calls map[string]*pendingCall + + timeout time.Duration } -func NewClient(clientExchange, serverExchange string) *Client { +func NewClient(url, serverExchange, clientExchange string, opts ...Option) (*Client, error) { + cfg := rmqrpc.Config{ + URL: url, + // Default + WaitTime: 5 * time.Second, + Attempts: 10, + } + c := &Client{ - conn: newConnection(clientExchange), + conn: rmqrpc.NewConnection(clientExchange, cfg), serverExchange: serverExchange, - timeout: 2 * time.Second, //nolint:gomnd // will be config error: make(chan error), stop: make(chan struct{}), mx: &sync.RWMutex{}, calls: make(map[string]*pendingCall), + // Default + timeout: 2 * time.Second, + } + + // Set options + for _, opt := range opts { + opt(c) + } + + err := c.conn.AttemptConnect() + if err != nil { + return nil, errors.Wrap(err, "rmq_rpc client - NewClient - c.conn.AttemptConnect") } go c.consumer() - return c + return c, nil } func (c *Client) publish(corrID, handler string, request interface{}) error { @@ -66,16 +87,16 @@ func (c *Client) publish(corrID, handler string, request interface{}) error { } } - err = c.conn.channel.Publish(c.serverExchange, "", false, false, + err = c.conn.Channel.Publish(c.serverExchange, "", false, false, amqp.Publishing{ ContentType: "application/json", CorrelationId: corrID, - ReplyTo: c.conn.consumerExchange, + ReplyTo: c.conn.ConsumerExchange, Type: handler, Body: requestBody, }) if err != nil { - return errors.Wrap(err, "c.channel.Publish") + return errors.Wrap(err, "c.Channel.Publish") } return nil @@ -87,7 +108,7 @@ func (c *Client) RemoteCall(handler string, request, response interface{}) error time.Sleep(c.timeout) select { case <-c.stop: - return errors.New("rmq - Client - RemoteCall - connection closed") + return errors.New("rmq_rpc client - Client - RemoteCall - Connection closed") default: } default: @@ -97,7 +118,7 @@ func (c *Client) RemoteCall(handler string, request, response interface{}) error err := c.publish(corrID, handler, request) if err != nil { - return errors.Wrap(err, "rmq - Client - RemoteCall - c.publish") + return errors.Wrap(err, "rmq_rpc client - Client - RemoteCall - c.publish") } call := &pendingCall{done: make(chan struct{})} @@ -107,25 +128,25 @@ func (c *Client) RemoteCall(handler string, request, response interface{}) error select { case <-time.After(c.timeout): - return ErrTimeout + return rmqrpc.ErrTimeout case <-call.done: } - if call.status == Success { + if call.status == rmqrpc.Success { err = json.Unmarshal(call.body, &response) if err != nil { - return errors.Wrap(err, "rmq - Client - RemoteCall - json.Unmarshal") + return errors.Wrap(err, "rmq_rpc client - Client - RemoteCall - json.Unmarshal") } return nil } - if call.status == ErrBadHandler.Error() { - return ErrBadHandler + if call.status == rmqrpc.ErrBadHandler.Error() { + return rmqrpc.ErrBadHandler } - if call.status == ErrInternalServer.Error() { - return ErrInternalServer + if call.status == rmqrpc.ErrInternalServer.Error() { + return rmqrpc.ErrInternalServer } return nil @@ -136,7 +157,7 @@ func (c *Client) consumer() { select { case <-c.stop: return - case d, opened := <-c.conn.delivery: + case d, opened := <-c.conn.Delivery: if !opened { c.reconnect() @@ -153,7 +174,7 @@ func (c *Client) consumer() { func (c *Client) reconnect() { close(c.stop) - err := c.conn.attemptConnect() + err := c.conn.AttemptConnect() if err != nil { c.error <- err close(c.error) @@ -206,9 +227,9 @@ func (c *Client) Shutdown() error { close(c.stop) time.Sleep(c.timeout) - err := c.conn.connection.Close() + err := c.conn.Connection.Close() if err != nil { - return fmt.Errorf("rmq - Client - Shutdown - c.connection.Close: %w", err) + return fmt.Errorf("rmq_rpc client - Client - Shutdown - c.Connection.Close: %w", err) } return nil diff --git a/pkg/rabbitmq/rmq_rpc/connection.go b/pkg/rabbitmq/rmq_rpc/connection.go new file mode 100644 index 00000000..22d96c99 --- /dev/null +++ b/pkg/rabbitmq/rmq_rpc/connection.go @@ -0,0 +1,115 @@ +package rmqrpc + +import ( + "log" + "time" + + "github.com/pkg/errors" + "github.com/streadway/amqp" +) + +type Config struct { + URL string + WaitTime time.Duration + Attempts int +} + +type Connection struct { + ConsumerExchange string + Config + Connection *amqp.Connection + Channel *amqp.Channel + Delivery <-chan amqp.Delivery +} + +func NewConnection(consumerExchange string, cfg Config) *Connection { + conn := &Connection{ + ConsumerExchange: consumerExchange, + Config: cfg, + } + + return conn +} + +func (c *Connection) AttemptConnect() error { + var err error + for i := c.Attempts; i > 0; i-- { + if err = c.connect(); err == nil { + break + } + + log.Printf("RabbitMQ is trying to connect, attempts left: %d", i) + time.Sleep(c.WaitTime) + } + + if err != nil { + return errors.Wrap(err, "rmq_rpc - AttemptConnect - c.connect") + } + + return nil +} + +func (c *Connection) connect() error { + var err error + + c.Connection, err = amqp.Dial(c.URL) + if err != nil { + return errors.Wrap(err, "amqp.Dial") + } + + c.Channel, err = c.Connection.Channel() + if err != nil { + return errors.Wrap(err, "c.Connection.Channel") + } + + err = c.Channel.ExchangeDeclare( + c.ConsumerExchange, + "fanout", + false, + false, + false, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.Connection.Channel") + } + + queue, err := c.Channel.QueueDeclare( + "", + false, + false, + true, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.Channel.QueueDeclare") + } + + err = c.Channel.QueueBind( + queue.Name, + "", + c.ConsumerExchange, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.Channel.QueueBind") + } + + c.Delivery, err = c.Channel.Consume( + queue.Name, + "", + false, + false, + false, + false, + nil, + ) + if err != nil { + return errors.Wrap(err, "c.Channel.Consume") + } + + return nil +} diff --git a/pkg/rmq/errors.go b/pkg/rabbitmq/rmq_rpc/errors.go similarity index 94% rename from pkg/rmq/errors.go rename to pkg/rabbitmq/rmq_rpc/errors.go index f6800664..6ce24804 100644 --- a/pkg/rmq/errors.go +++ b/pkg/rabbitmq/rmq_rpc/errors.go @@ -1,4 +1,4 @@ -package rmq +package rmqrpc type Error string diff --git a/pkg/rabbitmq/rmq_rpc/server/options.go b/pkg/rabbitmq/rmq_rpc/server/options.go new file mode 100644 index 00000000..0b381b2f --- /dev/null +++ b/pkg/rabbitmq/rmq_rpc/server/options.go @@ -0,0 +1,23 @@ +package server + +import "time" + +type Option func(*Server) + +func Timeout(timeout time.Duration) Option { + return func(s *Server) { + s.timeout = timeout + } +} + +func ConnWaitTime(timeout time.Duration) Option { + return func(s *Server) { + s.conn.WaitTime = timeout + } +} + +func ConnAttempts(attempts int) Option { + return func(s *Server) { + s.conn.Attempts = attempts + } +} diff --git a/pkg/rabbitmq/rmq_rpc/server/rpc_server.go b/pkg/rabbitmq/rmq_rpc/server/rpc_server.go new file mode 100644 index 00000000..c7b72d3b --- /dev/null +++ b/pkg/rabbitmq/rmq_rpc/server/rpc_server.go @@ -0,0 +1,151 @@ +package server + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/pkg/errors" + "github.com/streadway/amqp" + + "github.com/evrone/go-service-template/pkg/logger" + rmqrpc "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc" +) + +type CallHandler func(*amqp.Delivery) (interface{}, error) + +type Server struct { + conn *rmqrpc.Connection + error chan error + stop chan struct{} + router map[string]CallHandler + + timeout time.Duration +} + +func NewServer(url, serverExchange string, router map[string]CallHandler, opts ...Option) (*Server, error) { + cfg := rmqrpc.Config{ + URL: url, + // Default + WaitTime: 5 * time.Second, + Attempts: 10, + } + + s := &Server{ + conn: rmqrpc.NewConnection(serverExchange, cfg), + error: make(chan error), + stop: make(chan struct{}), + router: router, + // Default + timeout: 2 * time.Second, + } + + // Set options + for _, opt := range opts { + opt(s) + } + + err := s.conn.AttemptConnect() + if err != nil { + return nil, errors.Wrap(err, "rmq_rpc server - NewServer - s.conn.AttemptConnect") + } + + go s.consumer() + + return s, nil +} + +func (s *Server) consumer() { + for { + select { + case <-s.stop: + return + case d, opened := <-s.conn.Delivery: + if !opened { + s.reconnect() + + return + } + + _ = d.Ack(false) //nolint:errcheck // don't need this + + s.serveCall(&d) + } + } +} + +func (s *Server) serveCall(d *amqp.Delivery) { + callHandler, ok := s.router[d.Type] + if !ok { + s.publish(d, nil, rmqrpc.ErrBadHandler.Error()) + + return + } + + response, err := callHandler(d) + if err != nil { + s.publish(d, nil, rmqrpc.ErrInternalServer.Error()) + + logger.Error(err, "rmq_rpc server - Server - serveCall - callHandler") + + return + } + + body, err := json.Marshal(response) + if err != nil { + logger.Error(err, "rmq_rpc server - Server - serveCall - json.Marshal") + } + + s.publish(d, body, rmqrpc.Success) +} + +func (s *Server) publish(d *amqp.Delivery, body []byte, status string) { + err := s.conn.Channel.Publish(d.ReplyTo, "", false, false, + amqp.Publishing{ + ContentType: "application/json", + CorrelationId: d.CorrelationId, + Type: status, + Body: body, + }) + if err != nil { + logger.Error(err, "rmq_rpc server - Server - publish - s.conn.Channel.Publish") + } +} + +func (s *Server) reconnect() { + close(s.stop) + + err := s.conn.AttemptConnect() + if err != nil { + s.error <- err + close(s.error) + + return + } + + s.stop = make(chan struct{}) + + go s.consumer() +} + +func (s *Server) Notify() <-chan error { + return s.error +} + +func (s *Server) Shutdown() error { + select { + case <-s.error: + return nil + default: + } + + close(s.stop) + time.Sleep(s.timeout) + + err := s.conn.Connection.Close() + if err != nil { + return fmt.Errorf("rmq_rpc server - Server - Shutdown - s.Connection.Close: %w", err) + } + + return nil +} diff --git a/pkg/rmq/connect.go b/pkg/rmq/connect.go deleted file mode 100644 index 1d34e1ad..00000000 --- a/pkg/rmq/connect.go +++ /dev/null @@ -1,121 +0,0 @@ -package rmq - -import ( - "log" - "time" - - "github.com/pkg/errors" - "github.com/streadway/amqp" - - "github.com/evrone/go-service-template/pkg/logger" -) - -type Connection struct { - url string - waitTime time.Duration - attempts int - consumerExchange string - - connection *amqp.Connection - channel *amqp.Channel - delivery <-chan amqp.Delivery -} - -func newConnection(consumerExchange string) *Connection { - conn := &Connection{ - url: "amqp://guest:guest@rabbitmq:5672/", - waitTime: 5 * time.Second, //nolint:gomnd // will be config - attempts: 10, //nolint:gomnd // will be config - consumerExchange: consumerExchange, - } - - err := conn.attemptConnect() - if err != nil { - logger.Fatal(err, "rmq - newConnection - conn.attemptConnect") - } - - return conn -} - -func (c *Connection) attemptConnect() error { - var err error - for i := c.attempts; i > 0; i-- { - if err = c.connect(); err == nil { - break - } - - log.Printf("RabbitMQ is trying to connect, attempts left: %d", i) - time.Sleep(c.waitTime) - } - - if err != nil { - return errors.Wrap(err, "rmq - attemptConnect - c.connect") - } - - return nil -} - -func (c *Connection) connect() error { - var err error - - c.connection, err = amqp.Dial(c.url) - if err != nil { - return errors.Wrap(err, "amqp.Dial") - } - - c.channel, err = c.connection.Channel() - if err != nil { - return errors.Wrap(err, "c.connection.Channel") - } - - err = c.channel.ExchangeDeclare( - c.consumerExchange, - "fanout", - false, - false, - false, - false, - nil, - ) - if err != nil { - return errors.Wrap(err, "c.connection.Channel") - } - - queue, err := c.channel.QueueDeclare( - "", - false, - false, - true, - false, - nil, - ) - if err != nil { - return errors.Wrap(err, "c.channel.QueueDeclare") - } - - err = c.channel.QueueBind( - queue.Name, - "", - c.consumerExchange, - false, - nil, - ) - if err != nil { - return errors.Wrap(err, "c.channel.QueueBind") - } - - c.delivery, err = c.channel.Consume( - queue.Name, - "", - false, - false, - false, - false, - nil, - ) - if err != nil { - return errors.Wrap(err, "c.channel.Consume") - } - - return nil -} diff --git a/pkg/rmq/rpc_server.go b/pkg/rmq/rpc_server.go deleted file mode 100644 index dcae0f68..00000000 --- a/pkg/rmq/rpc_server.go +++ /dev/null @@ -1,124 +0,0 @@ -package rmq - -import ( - "fmt" - "time" - - "github.com/streadway/amqp" - - "github.com/evrone/go-service-template/pkg/logger" -) - -type CallHandler func(*amqp.Delivery) ([]byte, error) - -type Server struct { - conn *Connection - timeout time.Duration - error chan error - stop chan struct{} - router map[string]CallHandler -} - -func NewServer(router map[string]CallHandler, serverExchange string) *Server { - s := &Server{ - conn: newConnection(serverExchange), - timeout: 2 * time.Second, //nolint:gomnd // will be config - error: make(chan error), - stop: make(chan struct{}), - router: router, - } - - go s.consumer() - - return s -} - -func (s *Server) consumer() { - for { - select { - case <-s.stop: - return - case d, opened := <-s.conn.delivery: - if !opened { - s.reconnect() - - return - } - - _ = d.Ack(false) //nolint:errcheck // don't need this - - s.serveCall(&d) - } - } -} - -func (s *Server) serveCall(d *amqp.Delivery) { - callHandler, ok := s.router[d.Type] - if !ok { - s.publish(d, nil, ErrBadHandler.Error()) - - return - } - - body, err := callHandler(d) - if err != nil { - s.publish(d, nil, ErrInternalServer.Error()) - - logger.Error(err, "rmq - Server - serveCall - callHandler") - - return - } - - s.publish(d, body, Success) -} - -func (s *Server) publish(d *amqp.Delivery, body []byte, status string) { - err := s.conn.channel.Publish(d.ReplyTo, "", false, false, - amqp.Publishing{ - ContentType: "application/json", - CorrelationId: d.CorrelationId, - Type: status, - Body: body, - }) - if err != nil { - logger.Error(err, "rmq - Server - publish - s.conn.channel.Publish") - } -} - -func (s *Server) reconnect() { - close(s.stop) - - err := s.conn.attemptConnect() - if err != nil { - s.error <- err - close(s.error) - - return - } - - s.stop = make(chan struct{}) - - go s.consumer() -} - -func (s *Server) Notify() <-chan error { - return s.error -} - -func (s *Server) Shutdown() error { - select { - case <-s.error: - return nil - default: - } - - close(s.stop) - time.Sleep(s.timeout) - - err := s.conn.connection.Close() - if err != nil { - return fmt.Errorf("rmq - Server - Shutdown - s.connection.Close: %w", err) - } - - return nil -} From 08d158744f6f86fde93f9b72704f65da9de6462a Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Tue, 30 Mar 2021 15:06:33 +0400 Subject: [PATCH 14/15] [GOT-25] refactoring --- .golangci.yml | 3 +- README.md | 29 +++++----------- internal/app/migrate.go | 9 +++-- pkg/httpserver/options.go | 6 ++++ pkg/httpserver/server.go | 30 ++++++++++------- pkg/postgres/options.go | 8 +++++ pkg/postgres/postgres.go | 22 +++++++++---- .../client/{rpc_client.go => client.go} | 33 ++++++++++--------- .../server/{rpc_server.go => server.go} | 26 ++++++++------- 9 files changed, 98 insertions(+), 68 deletions(-) rename pkg/rabbitmq/rmq_rpc/client/{rpc_client.go => client.go} (92%) rename pkg/rabbitmq/rmq_rpc/server/{rpc_server.go => server.go} (87%) diff --git a/.golangci.yml b/.golangci.yml index aa3db7ba..4b5d4bd6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -108,7 +108,6 @@ linters: - predeclared - revive - rowserrcheck - - scopelint - sqlclosecheck - staticcheck - structcheck @@ -120,7 +119,6 @@ linters: - unparam - unused - varcheck - - wrapcheck - wsl - whitespace @@ -131,6 +129,7 @@ linters: # - maligned # - prealloc # - testpackage +# - wrapcheck issues: exclude-rules: diff --git a/README.md b/README.md index 05b3b860..c8e1a2e3 100644 --- a/README.md +++ b/README.md @@ -62,12 +62,12 @@ Dependency Inversion (тот самый из SOLID) — принцип инве **Например**, вам нужно обратиться из http хендлера к базе данных. И http, и БД находятся во внешнем слое, значит они ничего не знают друг о друге. Связь -между ними осуществляется через бизнес-логику: +между ними осуществляется через бизнес-логику (_service_): ``` - Хендлер HTTP > Бизнес-логика - Бизнес-логика > База данных - Бизнес-логика < База данных - Хендлер HTTP < Бизнес-логика + delivery HTTP > service + service > repo Postrges + service < repo Postrges + delivery HTTP < service ``` Стрелочками > и < показано пересечение границ слоев с помощью Интерфейсов. @@ -78,8 +78,10 @@ Dependency Inversion (тот самый из SOLID) — принцип инве ### Терминология по слоям Во внутреннем слое: * **Entities** — объекты, которыми оперирует бизнес-логика. В коде всегда называются -по именам принятым в организации. -* **Use Cases** — бизнес-логика, манипулирующая внешним слоем через интерфейсы. +по именам принятым в организации. Находятся в папке `internal/domain`. +* **Use Cases** — в терминах Clean Architecture это бизнес-логика, манипулирующая внешним слоем через интерфейсы. +Находится в папке `internal/service`. Называть бизнес-логику словом _service_ не очень идеоматично с точки зрения Чистой + Архитектуры, но слово _service_ больше подходит для названия пакета, чем _usecase_. Во внешнем слое просто перечень пакетов, которые реализуют интерфейсы (чтобы бизнес-логика могла к ним обратится): @@ -103,10 +105,6 @@ Clean Architecture. Классический вариант [Clean Architecture] сложной логики. _______________________________ -В нашей схеме мы оставляем во внутреннем круге 2 слоя (entity и usecase), потому -что это совпадает со структурой папок проекта. Но это очень тонкий слой, без -интерфейсов. - Сложные инструменты из внешнего слоя рекомендуется делить на дополнительные слои абстракции. Мы призываем руководствоваться здравым смыслом и добавлять слои лишь в том случае, если это действительно необходимо. @@ -117,15 +115,6 @@ _______________________________ инверсии зависимостей. Все подходы преследуют цель уменьшить зацепление и разграничить ответственность. -Test Driven Design ------------------- -Domain Driven Design --------------------- -12-factor app -------------- -Dependency Injection --------------------- - Полезные ссылки --------------- * [The Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) diff --git a/internal/app/migrate.go b/internal/app/migrate.go index ed731400..272604c6 100644 --- a/internal/app/migrate.go +++ b/internal/app/migrate.go @@ -13,6 +13,11 @@ import ( _ "github.com/golang-migrate/migrate/v4/source/file" ) +const ( + defaultAttempts = 20 + defaultTimeout = time.Second +) + func init() { databaseURL, ok := os.LookupEnv("PG_URL") if !ok || len(databaseURL) == 0 { @@ -22,7 +27,7 @@ func init() { databaseURL += "?sslmode=disable" var ( - attempts = 20 + attempts = defaultAttempts err error m *migrate.Migrate ) @@ -34,7 +39,7 @@ func init() { } log.Printf("Migrate: postgres is trying to connect, attempts left: %d", attempts) - time.Sleep(time.Second) + time.Sleep(defaultTimeout) attempts-- } diff --git a/pkg/httpserver/options.go b/pkg/httpserver/options.go index fa1dee12..38088cdd 100644 --- a/pkg/httpserver/options.go +++ b/pkg/httpserver/options.go @@ -24,3 +24,9 @@ func WriteTimeout(timeout time.Duration) Option { s.server.WriteTimeout = timeout } } + +func ShutdownTimeout(timeout time.Duration) Option { + return func(s *Server) { + s.shutdownTimeout = timeout + } +} diff --git a/pkg/httpserver/server.go b/pkg/httpserver/server.go index 74a5ac0c..9efcf0c4 100644 --- a/pkg/httpserver/server.go +++ b/pkg/httpserver/server.go @@ -7,26 +7,34 @@ import ( "time" ) +const ( + defaultReadTimeout = 5 * time.Second + defaultWriteTimeout = 5 * time.Second + defaultAddr = ":80" + defaultShutdownTimeout = 3 * time.Second +) + type Server struct { - server *http.Server - notify chan error + server *http.Server + notify chan error + shutdownTimeout time.Duration } func NewServer(handler http.Handler, opts ...Option) *Server { httpServer := &http.Server{ - Handler: handler, - // Default - ReadTimeout: 5 * time.Second, - WriteTimeout: 5 * time.Second, - Addr: ":80", + Handler: handler, + ReadTimeout: defaultReadTimeout, + WriteTimeout: defaultWriteTimeout, + Addr: defaultAddr, } s := &Server{ - server: httpServer, - notify: make(chan error, 1), + server: httpServer, + notify: make(chan error, 1), + shutdownTimeout: defaultShutdownTimeout, } - // Set options + // Custom options for _, opt := range opts { opt(s) } @@ -48,7 +56,7 @@ func (s *Server) Notify() <-chan error { } func (s *Server) Shutdown() error { - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) //nolint:gomnd // it's magic + ctx, cancel := context.WithTimeout(context.Background(), s.shutdownTimeout) defer cancel() return s.server.Shutdown(ctx) diff --git a/pkg/postgres/options.go b/pkg/postgres/options.go index d4cb5b01..49f50f3f 100644 --- a/pkg/postgres/options.go +++ b/pkg/postgres/options.go @@ -1,5 +1,7 @@ package postgres +import "time" + type Option func(*Postgres) func MaxPoolSize(size int) Option { @@ -13,3 +15,9 @@ func ConnAttempts(attempts int) Option { c.connAttempts = attempts } } + +func ConnTimeout(timeout time.Duration) Option { + return func(c *Postgres) { + c.connTimeout = timeout + } +} diff --git a/pkg/postgres/postgres.go b/pkg/postgres/postgres.go index 7c88e9c0..8b634cde 100644 --- a/pkg/postgres/postgres.go +++ b/pkg/postgres/postgres.go @@ -11,21 +11,29 @@ import ( "github.com/pkg/errors" ) +const ( + defaultMaxPoolSize = 1 + defaultConnAttempts = 10 + defaultConnTimeout = time.Second +) + type Postgres struct { maxPoolSize int connAttempts int - Builder squirrel.StatementBuilderType - Pool *pgxpool.Pool + connTimeout time.Duration + + Builder squirrel.StatementBuilderType + Pool *pgxpool.Pool } func NewPostgres(url string, opts ...Option) (*Postgres, error) { pg := &Postgres{ - // Default - maxPoolSize: 1, - connAttempts: 10, + maxPoolSize: defaultMaxPoolSize, + connAttempts: defaultConnAttempts, + connTimeout: defaultConnTimeout, } - // Set options + // Custom options for _, opt := range opts { opt(pg) } @@ -47,7 +55,7 @@ func NewPostgres(url string, opts ...Option) (*Postgres, error) { log.Printf("Postgres is trying to connect, attempts left: %d", pg.connAttempts) - time.Sleep(time.Second) + time.Sleep(pg.connTimeout) pg.connAttempts-- } diff --git a/pkg/rabbitmq/rmq_rpc/client/rpc_client.go b/pkg/rabbitmq/rmq_rpc/client/client.go similarity index 92% rename from pkg/rabbitmq/rmq_rpc/client/rpc_client.go rename to pkg/rabbitmq/rmq_rpc/client/client.go index 35032b00..a2e3bee3 100644 --- a/pkg/rabbitmq/rmq_rpc/client/rpc_client.go +++ b/pkg/rabbitmq/rmq_rpc/client/client.go @@ -13,6 +13,12 @@ import ( rmqrpc "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc" ) +const ( + defaultWaitTime = 5 * time.Second + defaultAttempts = 10 + defaultTimeout = 2 * time.Second +) + type Message struct { Queue string Priority uint8 @@ -34,7 +40,7 @@ type Client struct { error chan error stop chan struct{} - mx *sync.RWMutex + sync.RWMutex calls map[string]*pendingCall timeout time.Duration @@ -42,10 +48,9 @@ type Client struct { func NewClient(url, serverExchange, clientExchange string, opts ...Option) (*Client, error) { cfg := rmqrpc.Config{ - URL: url, - // Default - WaitTime: 5 * time.Second, - Attempts: 10, + URL: url, + WaitTime: defaultWaitTime, + Attempts: defaultAttempts, } c := &Client{ @@ -53,13 +58,11 @@ func NewClient(url, serverExchange, clientExchange string, opts ...Option) (*Cli serverExchange: serverExchange, error: make(chan error), stop: make(chan struct{}), - mx: &sync.RWMutex{}, calls: make(map[string]*pendingCall), - // Default - timeout: 2 * time.Second, + timeout: defaultTimeout, } - // Set options + // Custom options for _, opt := range opts { opt(c) } @@ -188,9 +191,9 @@ func (c *Client) reconnect() { } func (c *Client) getCall(d *amqp.Delivery) { - c.mx.RLock() + c.RLock() call, ok := c.calls[d.CorrelationId] - c.mx.RUnlock() + c.RUnlock() if !ok { return @@ -202,15 +205,15 @@ func (c *Client) getCall(d *amqp.Delivery) { } func (c *Client) addCall(corrID string, call *pendingCall) { - c.mx.Lock() + c.Lock() c.calls[corrID] = call - c.mx.Unlock() + c.Unlock() } func (c *Client) deleteCall(corrID string) { - c.mx.Lock() + c.Lock() delete(c.calls, corrID) - c.mx.Unlock() + c.Unlock() } func (c *Client) Notify() <-chan error { diff --git a/pkg/rabbitmq/rmq_rpc/server/rpc_server.go b/pkg/rabbitmq/rmq_rpc/server/server.go similarity index 87% rename from pkg/rabbitmq/rmq_rpc/server/rpc_server.go rename to pkg/rabbitmq/rmq_rpc/server/server.go index c7b72d3b..afcbc2a2 100644 --- a/pkg/rabbitmq/rmq_rpc/server/rpc_server.go +++ b/pkg/rabbitmq/rmq_rpc/server/server.go @@ -12,6 +12,12 @@ import ( rmqrpc "github.com/evrone/go-service-template/pkg/rabbitmq/rmq_rpc" ) +const ( + defaultWaitTime = 5 * time.Second + defaultAttempts = 10 + defaultTimeout = 2 * time.Second +) + type CallHandler func(*amqp.Delivery) (interface{}, error) type Server struct { @@ -25,22 +31,20 @@ type Server struct { func NewServer(url, serverExchange string, router map[string]CallHandler, opts ...Option) (*Server, error) { cfg := rmqrpc.Config{ - URL: url, - // Default - WaitTime: 5 * time.Second, - Attempts: 10, + URL: url, + WaitTime: defaultWaitTime, + Attempts: defaultAttempts, } s := &Server{ - conn: rmqrpc.NewConnection(serverExchange, cfg), - error: make(chan error), - stop: make(chan struct{}), - router: router, - // Default - timeout: 2 * time.Second, + conn: rmqrpc.NewConnection(serverExchange, cfg), + error: make(chan error), + stop: make(chan struct{}), + router: router, + timeout: defaultTimeout, } - // Set options + // Custom options for _, opt := range opts { opt(s) } From fdf96cefa693f60c3d0f67d4ed0b3e12db4f3380 Mon Sep 17 00:00:00 2001 From: Misha Nepryakhin Date: Fri, 2 Apr 2021 13:35:09 +0400 Subject: [PATCH 15/15] [GOT-26] readme --- .circleci/config.yml | 35 ------ README.md | 109 ++++++++++++------ config/config.yml | 2 +- internal/app/app.go | 4 +- internal/repo/interface.go | 13 --- internal/service/interface.go | 9 -- internal/service/interfaces.go | 22 ++++ .../repo/translation_postgres.go | 0 internal/service/translation.go | 12 +- .../webapi/translation_google.go | 0 internal/webapi/interface.go | 8 -- 11 files changed, 104 insertions(+), 110 deletions(-) delete mode 100644 internal/repo/interface.go delete mode 100644 internal/service/interface.go create mode 100644 internal/service/interfaces.go rename internal/{ => service}/repo/translation_postgres.go (100%) rename internal/{ => service}/webapi/translation_google.go (100%) delete mode 100644 internal/webapi/interface.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 0cee37fa..6d44c4ac 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -86,38 +86,3 @@ workflows: - linters - tests - integration-tests - # # Build staging image - # - docker/publish: - # context: evrone-erp - # image: << pipeline.parameters.image >> - # tag: "staging-${CIRCLE_SHA1}" - # requires: - # - linters - # - tests - # filters: - # branches: - # only: - # - develop - # # Build QA image - # - docker/publish: - # context: evrone-erp - # image: << pipeline.parameters.image >> - # tag: "qa-${CIRCLE_SHA1}" - # requires: - # - linters - # - tests - # filters: - # branches: - # only: - # - master - # - /release\/v\d.*/ - # # Build production image - # - docker/publish: - # context: evrone-erp - # image: << pipeline.parameters.image >> - # tag: "$CIRCLE_TAG" - # filters: - # tags: - # only: /^v.*/ - # branches: - # ignore: /.*/ diff --git a/README.md b/README.md index c8e1a2e3..732ad91a 100644 --- a/README.md +++ b/README.md @@ -45,56 +45,95 @@ Dependency Inversion (тот самый из SOLID) — принцип инве и фреймворки). **Внутренний** слой с бизнес-логикой должен быть чистым, то есть: -* Не иметь в себе импортов пакетов из внешнего слоя. -* Не иметь в себе импортов любых других пакетов, кроме стандартной библиотеки. -* Делать обращения к внешнему слою через интерфейс(!). +- Не иметь в себе импортов пакетов из внешнего слоя. +- Не иметь в себе импортов любых других пакетов, кроме стандартной библиотеки. +- Делать обращения к внешнему слою через интерфейс(!). Бизнес-логика ничего не знает о Postgres или RabbitMQ. Бизнес-логика знает интерфейс для работы с _абстрактной_ базой данных, _абстрактным_ брокером -сообщений или _абстрактным_ HTTP сервером. +сообщений или _абстрактным_ web api. **Внешний** слой имеет другие ограничения: -* Все компоненты этого слоя не знают о существовании друг друга. -* Как из одного инструмента вызвать другой? Напрямую никак, только через +- Все компоненты этого слоя не знают о существовании друг друга. +- Как из одного инструмента вызвать другой? Напрямую никак, только через внутренний слой логики. -* Обращения к внутреннему слою происходят через интерфейс(!). -* Данные передаются в том формате, который удобен для бизнес-логики. +- Обращения к внутреннему слою происходят через интерфейс(!). +- Данные передаются в том формате, который удобен для бизнес-логики. **Например**, вам нужно обратиться из http хендлера к базе данных. И http, и БД находятся во внешнем слое, значит они ничего не знают друг о друге. Связь -между ними осуществляется через бизнес-логику (_service_): +между ними осуществляется через `service` (бизнес-логику): ``` - delivery HTTP > service - service > repo Postrges - service < repo Postrges - delivery HTTP < service + HTTP (delivery) > service + service > Postrges (repo) + service < Postrges (repo) + HTTP (delivery) < service ``` -Стрелочками > и < показано пересечение границ слоев с помощью Интерфейсов. +Стрелочками > и < показано пересечение границ слоев через Интерфейсы. То же самое на картинке: ![Example](pkg/img/example-http-db.png) -### Терминология по слоям -Во внутреннем слое: -* **Entities** — объекты, которыми оперирует бизнес-логика. В коде всегда называются -по именам принятым в организации. Находятся в папке `internal/domain`. -* **Use Cases** — в терминах Clean Architecture это бизнес-логика, манипулирующая внешним слоем через интерфейсы. -Находится в папке `internal/service`. Называть бизнес-логику словом _service_ не очень идеоматично с точки зрения Чистой - Архитектуры, но слово _service_ больше подходит для названия пакета, чем _usecase_. +### Терминология Чистой Архитектуры +- **Entities** — объекты, которыми оперирует бизнес-логика. В коде называются +по именам принятым в организации. Находятся в папке `internal/domain`. Domain намекает на то что мы придерживаемся + принципов DDD (domain driven design), это отчасти так, но без фанатизма. В терминах MVC entity это модели. Удобно + когда модели называются по именам предметной области. + -Во внешнем слое просто перечень пакетов, которые реализуют интерфейсы (чтобы -бизнес-логика могла к ним обратится): - * Postgres - * HTTP - * RabbitMQ - * WebAPI - * Framework - * Любой другой package +- **Use Cases** — это бизнес-логика, манипулирующая внешними пакетами через интерфейсы. +Находится в папке `internal/service`. Называть бизнес-логику словом _service_ не очень идеоматично с точки зрения Чистой + Архитектуры, но слово _service_ удобнее использовать для названия пакета, чем _usecase_. + +Слой с которым непосредственно взаимодействует бизнес-логика обычно называют инфраструктурным слоем - _infrastructure_. +Это могут быть репозитории `internal/service/repo`, внешние webapi `internal/service/webapi`, любые pkg и другие +микросервисы. В шаблоне инфраструктурные пакеты находятся внутри каталога `internal/service`. + +Как называть точки входа, вопрос открыт. Варианты: +- delivery +- transport +- controllers +- adaptors +- gateways +- input +- entry_points +- primary + +### Dependency Injection +Для того чтобы убрать зависимость бизнес-логики от внешних пакетов, используется инъекция зависимостей. + +Через конструктор NewService мы делаем инъекцию зависимости в структуру бизнес-логики. Таким образом бизнес-логика +становится независимой (и даже переносимой). Мы можем подменить реализацию интерфейса и при этом не вносить правки в +пакет `service`. + +```go +package service + +import ( + // Nothing! +) + +type Repository interface { + Get() +} + +type Service struct { + repo Repository +} + +func NewService(r Repository) *Service{ + return &Service{r} +} + +func (s *Service) Do() { + s.repo.Get() +} +``` +Это так же позволит нам делать автогенерацию моков (например с помощью библиотеки _mockery_) и легко писать юнит-тесты. ### Дополнительные слои -На самом здесь представлен упрощённый, адаптированный под микросервисы, вариант -Clean Architecture. Классический вариант [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) +Классический вариант [Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) разрабатывался для построения больших монолитных приложений и имеет 4 слоя абстракций. То есть, в оригинальной версии, внешний слой делится ещё на два, которые @@ -106,7 +145,7 @@ Clean Architecture. Классический вариант [Clean Architecture] _______________________________ Сложные инструменты из внешнего слоя рекомендуется делить на дополнительные -слои абстракции. Мы призываем руководствоваться здравым смыслом и добавлять слои +слои абстракции. Следует руководствоваться здравым смыслом и добавлять слои лишь в том случае, если это действительно необходимо. ### Альтернативные подходы @@ -117,6 +156,6 @@ _______________________________ Полезные ссылки --------------- -* [The Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) -* [Книга Чистая архитектура](https://www.ozon.ru/context/detail/id/144499396/) -* [12 факторов](https://12factor.net/ru/) +- [The Clean Architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) +- [Книга Чистая архитектура](https://www.ozon.ru/context/detail/id/144499396/) +- [12 факторов](https://12factor.net/ru/) diff --git a/config/config.yml b/config/config.yml index 2e08a363..75def32a 100644 --- a/config/config.yml +++ b/config/config.yml @@ -1,6 +1,6 @@ app: name: 'go-service-template' - version: '0.0.0' + version: '1.0.0' http: port: '8080' diff --git a/internal/app/app.go b/internal/app/app.go index 98b701e5..54246562 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -11,9 +11,9 @@ import ( "github.com/evrone/go-service-template/config" amqprpc "github.com/evrone/go-service-template/internal/delivery/amqp_rpc" v1 "github.com/evrone/go-service-template/internal/delivery/http/v1" - "github.com/evrone/go-service-template/internal/repo" "github.com/evrone/go-service-template/internal/service" - "github.com/evrone/go-service-template/internal/webapi" + "github.com/evrone/go-service-template/internal/service/repo" + "github.com/evrone/go-service-template/internal/service/webapi" "github.com/evrone/go-service-template/pkg/httpserver" "github.com/evrone/go-service-template/pkg/logger" "github.com/evrone/go-service-template/pkg/postgres" diff --git a/internal/repo/interface.go b/internal/repo/interface.go deleted file mode 100644 index 90d73235..00000000 --- a/internal/repo/interface.go +++ /dev/null @@ -1,13 +0,0 @@ -// Package repo implements databases for services. For each service in its own file. -package repo - -import ( - "context" - - "github.com/evrone/go-service-template/internal/domain" -) - -type Translation interface { - Store(ctx context.Context, entity domain.Translation) error - GetHistory(context.Context) ([]domain.Translation, error) -} diff --git a/internal/service/interface.go b/internal/service/interface.go deleted file mode 100644 index f6d78df2..00000000 --- a/internal/service/interface.go +++ /dev/null @@ -1,9 +0,0 @@ -// Package service implements application business logic. Each logic group in own file. -package service - -import "github.com/evrone/go-service-template/internal/domain" - -type Translation interface { - Translate(entity domain.Translation) (domain.Translation, error) - History() ([]domain.Translation, error) -} diff --git a/internal/service/interfaces.go b/internal/service/interfaces.go new file mode 100644 index 00000000..c9784753 --- /dev/null +++ b/internal/service/interfaces.go @@ -0,0 +1,22 @@ +// Package service implements application business logic. Each logic group in own file. +package service + +import ( + "context" + + "github.com/evrone/go-service-template/internal/domain" +) + +type Translation interface { + Translate(domain.Translation) (domain.Translation, error) + History() ([]domain.Translation, error) +} + +type TranslationRepo interface { + Store(context.Context, domain.Translation) error + GetHistory(context.Context) ([]domain.Translation, error) +} + +type TranslationWebAPI interface { + Translate(domain.Translation) (domain.Translation, error) +} diff --git a/internal/repo/translation_postgres.go b/internal/service/repo/translation_postgres.go similarity index 100% rename from internal/repo/translation_postgres.go rename to internal/service/repo/translation_postgres.go diff --git a/internal/service/translation.go b/internal/service/translation.go index df3da81c..4ec01930 100644 --- a/internal/service/translation.go +++ b/internal/service/translation.go @@ -5,19 +5,17 @@ import ( "fmt" "github.com/evrone/go-service-template/internal/domain" - "github.com/evrone/go-service-template/internal/repo" - "github.com/evrone/go-service-template/internal/webapi" ) type TranslationService struct { - repo repo.Translation - webAPI webapi.Translation + repo TranslationRepo + webAPI TranslationWebAPI } -func NewTranslationService(repository repo.Translation, webAPI webapi.Translation) *TranslationService { +func NewTranslationService(r TranslationRepo, w TranslationWebAPI) *TranslationService { return &TranslationService{ - repo: repository, - webAPI: webAPI, + repo: r, + webAPI: w, } } diff --git a/internal/webapi/translation_google.go b/internal/service/webapi/translation_google.go similarity index 100% rename from internal/webapi/translation_google.go rename to internal/service/webapi/translation_google.go diff --git a/internal/webapi/interface.go b/internal/webapi/interface.go deleted file mode 100644 index 95975804..00000000 --- a/internal/webapi/interface.go +++ /dev/null @@ -1,8 +0,0 @@ -// Package webapi implements google translator API. -package webapi - -import "github.com/evrone/go-service-template/internal/domain" - -type Translation interface { - Translate(entity domain.Translation) (domain.Translation, error) -}