diff --git a/.github/workflows/build-base.yml b/.github/workflows/build-base.yml new file mode 100644 index 0000000..495d3f2 --- /dev/null +++ b/.github/workflows/build-base.yml @@ -0,0 +1,25 @@ +name: "Build Base" + +on: + workflow_call: +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + go: ["1.21"] + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + - uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go }} + - name: Install Task + uses: arduino/setup-task@v1 + with: + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} + - name: Build + run: | + task build \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..ef5be34 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,20 @@ +name: "Build" + +on: + push: + paths: + - "cmd/**" + - "pkg/**" + - "main.go" + branches: + - master + pull_request: + paths: + - "cmd/**" + - "pkg/**" + - "main.go" + branches: + - master +jobs: + build: + uses: "./.github/workflows/build-base.yml" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index de0aad2..5ccda09 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,19 +1,25 @@ +name: 'GolangCI Lint' + on: - pull_request: + push: + paths: + - 'pkg/**' + - 'main.go' branches: - master - - develop - workflow_call: - push: + pull_request: + paths: + - 'pkg/**' + - 'main.go' branches: - master - - develop jobs: - golangci-lint: + lint: runs-on: ubuntu-latest steps: - - name: Check out code into the Go module directory - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + fetch-depth: 1 - name: golangci-lint uses: reviewdog/action-golangci-lint@v2 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 66e0952..9a698cd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,45 +1,39 @@ -name: 'Tests' +name: "Testing" on: push: + paths: + - "pkg/**" + - "main.go" branches: - master - - develop pull_request: + paths: + - "pkg/**" + - "main.go" branches: - master - - develop + jobs: unit-tests: strategy: matrix: - go: [ 1.20 ] + go: ["1.21"] runs-on: ubuntu-latest steps: - - name: Checkout code - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + - uses: actions/setup-go@v4 with: go-version: ${{ matrix.go }} - - uses: actions/cache@v3 + - name: Install Task + uses: arduino/setup-task@v1 with: - path: | - ~/.cache/go-build - ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Set up gotestfmt - run: go install gotesttools/gotestfmt@latest + version: 3.x + repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Run tests run: | + go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest cp config.example.yml config.yml - go test -covermode=atomic -coverprofile=coverage.txt -timeout 5m -json -v ./... | gotestfmt -showteststatus - - uses: codecov/codecov-action@v1 - with: - token: ${{ secrets.CODECOV_TOKEN }} - file: ./coverage.txt - flags: tests - name: codecov-umbrella - fail_ci_if_error: true - verbose: true + task test diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..2a31997 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,97 @@ +run: + concurrency: 16 + timeout: 5m + issues-exit-code: 1 + tests: true + +skip-dirs: + - docs/ + +skip-files: + - "*_gen\\.go$" + +modules-download-mode: mod +allow-parallel-runners: true +go: '1.21' + +output: + format: colored-line-number + print-issued-lines: true + print-linter-name: true + uniq-by-line: true + sort-results: true + +linters: + disable-all: true + enable: + - asasalint + - bidichk + - bodyclose + - decorder + - dupl + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - exportloopref + - forbidigo + - gofumpt + - ginkgolinter + - gocheckcompilerdirectives + - gochecknoglobals + - gocognit + - goconst + - gocritic + - gocyclo + - godox + - goerr113 + - goheader + - goimports + - gomodguard + - goprintffuncname + - gosec + - gosimple + - gosmopolitan + - govet + - grouper + - importas + - ineffassign + - interfacebloat + - loggercheck + - maintidx + - makezero + - mirror + - misspell + - nakedret + - nestif + - nilerr + - nilnil + - noctx + - nolintlint + - nonamedreturns + - nosprintfhostport + - paralleltest + - prealloc + - predeclared + - promlinter + - reassign + - revive + - rowserrcheck + - sqlclosecheck + - staticcheck + - stylecheck + - tenv + - testableexamples + - testpackage + - thelper + - tparallel + - unconvert + - unparam + - unused + - usestdlibvars + - wastedassign + - whitespace + - zerologlint + - prealloc + - perfsprint diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..e407294 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,32 @@ +{ + "recommendations": [ + "task.vscode-task", + "rogalmic.bash-debug", + "mads-hartmann.bash-ide-vscode", + "ms-vscode-remote.remote-containers", + "ms-azuretools.vscode-docker", + "p1c2u.docker-compose", + "usernamehw.errorlens", + "github.vscode-github-actions", + "eamodio.gitlens", + "golang.go", + "mongodb.mongodb-vscode", + "christian-kohler.path-intellisense", + "uniquevision.vscode-plpgsql-lsp", + "foxundermoon.shell-format", + "timonwong.shellcheck", + "mtxr.sqltools", + "JordanHury.sqltools-cassandra", + "mtxr.sqltools-driver-pg", + "wayou.vscode-todo-highlight", + "tooltitudeteam.tooltitude", + "pflannery.vscode-versionlens", + "zxh404.vscode-proto3", + "redhat.vscode-yaml", + "ms-vscode.test-adapter-converter", + "GitHub.vscode-pull-request-github", + "ms-vscode.remote-repositories", + "ms-vsliveshare.vsliveshare", + "aaron-bond.better-comments" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index bdd8a84..119f2a1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,7 +2,7 @@ "version": "0.2.0", "configurations": [ { - "name": "Debug Boilerplate Server", + "name": "Debug Server", "type": "go", "request": "attach", "mode": "remote", @@ -11,19 +11,12 @@ "host": "127.0.0.1" }, { - "name": "Debug Boilerplate", + "name": "Debug", "type": "go", "request": "launch", "mode": "debug", "program": "main.go", - "args": [ - "serve", - "--env", - "development", - "--log-level", - "debug", - "--log-pretty-print" - ] + "args": ["serve"] }, { "name": "Attach to Process", diff --git a/.vscode/settings.json b/.vscode/settings.json index 2171082..0893320 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,16 @@ { - "go.inferGopath": false, + "editor.quickSuggestions": { + "comments": true, + "strings": true, + "other": true + }, + "[yaml]": { + "editor.defaultFormatter": "redhat.vscode-yaml" + }, + "git.allowForcePush": true, + "git.alwaysSignOff": true, + "git.autofetch": true, + "git.ignoreLimitWarning": true, "go.testEnvVars": { "CC": "gcc", "CXX": "g++" @@ -8,13 +19,20 @@ "CC": "gcc", "CXX": "g++" }, - "go.docsTool": "guru", + "go.diagnostic.vulncheck": "Imports", + "go.disableConcurrentTests": false, + "go.coverOnSave": true, + "go.coverOnSingleTest": true, + "go.coverOnSingleTestFile": true, + "go.inlayHints.assignVariableTypes": true, + "go.inlayHints.compositeLiteralFields": true, + "go.inlayHints.constantValues": true, + "go.inlayHints.compositeLiteralTypes": false, + "go.inlayHints.parameterNames": true, "go.useLanguageServer": true, - "go.autocompleteUnimportedPackages": true, - "go.gotoSymbol.includeImports": true, - "go.gotoSymbol.includeGoroot": true, + "go.inferGopath": true, "go.vetOnSave": "workspace", - "go.coverMode": "atomic", + "go.coverMode": "default", "go.addTags": { "tags": "json,bson,yaml", "promptForTags": true @@ -32,13 +50,95 @@ "runtest": true }, "gopls": { - "expandWorkspaceToModule": true, - "gofumpt": true, - "build.memoryMode": "Normal" + "ui.codelenses": { + "gc_details": true, + "upgrade_dependency": true, + "generate": true, + "regenerate_cgo": true, + "test": true, + "tidy": true, + "run_govulncheck": true + }, + "ui.diagnostic.analyses": { + "asmdecl": true, + "nilness": true, + "assign": true, + "atomic": true, + "atomicalign": true, + "bools": true, + "buildtags": true, + "cgocall": true, + "composites": true, + "copylocks": true, + "buildtag": true, + "errorsas": true, + "fieldalignment": true, + "deepequalerrors": true, + "fillreturns": true, + "fillstruct": true, + "nilfunc": true, + "ifaceassert": true, + "httpresponse": true, + "infertypeargs": true, + "lostcancel": true, + "loopclosure": true, + "printf": true, + "simplifycompositelit": true, + "stdmethods": true, + "shadow": true, + "structtag": true, + "timeformat": true, + "unmarshal": true, + "unreachable": true, + "unusedwrite": true, + "unusedvariable": true, + "unsafeptr": true, + "unusedresult": true, + "embed": true, + "nonewvars": true, + "noresultvalues": true, + "shift": true, + "simplifyrange": true, + "simplifyslice": true, + "sortslice": true, + "stringintconv": true, + "stubmethods": true, + "testinggoroutine": true, + "tests": true, + "undeclaredname": true, + "unusedparams": false + }, + "ui.semanticTokens": true, + "ui.completion.experimentalPostfixCompletions": true, + "ui.completion.usePlaceholders": false, + "ui.completion.matcher": "Fuzzy", + "ui.diagnostic.staticcheck": true, + "ui.diagnostic.annotations": { + "bounds": true, + "escape": true, + "inline": true, + "nil": true + }, + "ui.navigation.importShortcut": "Link", + "ui.noSemanticNumber": true, + "ui.navigation.symbolStyle": "Full", + "ui.noSemanticString": true, + "ui.documentation.linksInHover": false, + "ui.navigation.symbolMatcher": "FastFuzzy", + "ui.documentation.hoverKind": "FullDocumentation", + "ui.documentation.linkTarget": "pkg.go.dev", + "build.memoryMode": "Normal", + "build.allowImplicitNetworkAccess": true, + "ui.completion.completionBudget": "500ms", + "ui.navigation.symbolScope": "all", + "ui.diagnostic.diagnosticsDelay": "600ms", + "build.allowModfileModifications": true, + "gofumpt": true }, "go.lintTool": "golangci-lint", "go.toolsManagement.autoUpdate": true, "go.coverageOptions": "showCoveredCodeOnly", + "go.survey.prompt": false, "go.editorContextMenuCommands": { "removeTags": true, "fillStruct": true, @@ -48,5 +148,25 @@ "generateTestForPackage": true, "benchmarkAtCursor": true }, - "go.testExplorer.showDynamicSubtestsInEditor": true + "go.inlayHints.functionTypeParameters": true, + "go.inlayHints.rangeVariableTypes": true, + "go.installDependenciesWhenBuilding": true, + "go.testExplorer.showOutput": true, + "go.testExplorer.packageDisplayMode": "nested", + "go.testExplorer.showDynamicSubtestsInEditor": true, + "go.terminal.activateEnvironment": true, + "go.testTimeout": "30s", + "go.removeTags": { + "tags": "", + "options": "", + "promptForTags": false + }, + "go.showWelcome": false, + "go.playground": { + "openbrowser": true, + "share": true, + "run": true + }, + "go.testExplorer.enable": true, + "go.testExplorer.concatenateMessages": true } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f9ad5ac..7456364 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,14 +2,7 @@ "version": "2.0.0", "tasks": [ { - "label": "Boilerplate: Production Build", - "type": "shell", - "command": "make", - "args": ["build", "ENV=production"], - "group": "build" - }, - { - "label": "Boilerplate: Hot Reload", + "label": "App: Hot Reload", "type": "shell", "command": "air", "isBackground": true, @@ -17,14 +10,7 @@ "problemMatcher": [] }, { - "label": "Boilerplate: Development Build", - "type": "shell", - "command": "make", - "args": ["build", "ENV=development"], - "group": "build" - }, - { - "label": "Boilerplate: Race Test", + "label": "App: Race Test", "type": "shell", "command": "go", "args": [ @@ -39,14 +25,14 @@ "group": "test" }, { - "label": "Boilerplate: Test", + "label": "App: Test", "type": "shell", "command": "go", "args": ["test", "-v", "-covermode=atomic", "-timeout", "5m", "./..."], "group": "test" }, { - "label": "Boilerplate: Format", + "label": "App: Format", "type": "shell", "command": "gofumpt", "args": ["-l", "-w", "."], @@ -54,7 +40,7 @@ "problemMatcher": [] }, { - "label": "Boilerplate: Lint", + "label": "App: Lint", "type": "shell", "command": "golangci-lint", "args": ["run"], diff --git a/Makefile b/Makefile index 02b7a94..f02ffc5 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,3 @@ -PLATFORM ?= linux/arm64,linux/amd64 -ENV ?= development -RACE ?= 0 -GOPATH ?= $(HOME)/go -APP_NAME ?= boilerplate -VERSION ?= dev - -.PHONY: run -run: - @CXX=g++ CC=gcc go run ./main.go - .PHONY: build build: ifeq ($(ENV),production) @@ -19,32 +8,3 @@ else @echo "Target ${ENV} is not supported" endif @cp ./config.example.yml bin/config.yml - -.PHONY: test -test: -ifeq ($(RACE), 1) - @CC=gcc CXX=g++ go test ./... -race -covermode=atomic -coverprofile=coverage.txt -timeout 5m -else - @CC=gcc CXX=g++ go test ./... -covermode=atomic -coverprofile=coverage.txt -timeout 1m -endif - -.PHONY: buildx -buildx: - @docker buildx build --target production --build-arg APP_NAME=$(APP_NAME) --build-arg VERSION="$(VERSION)" --platform "$(PLATFORM)" -t "brossquad/$(APP_NAME):$(VERSION)" --file ./Dockerfile . - -.PHONY: tidy -tidy: - @rm -f go.sum - @go mod tidy - -.PHONY: clean -clean: - @rm -rf ./bin - -.PHONY: lint -lint: - @golangci-lint run - -.PHONY: fmt -fmt: - @gofumpt -l -w . diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..d8d80b1 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,70 @@ +version: '3' + +tasks: + build: + cmds: + - go build -ldflags="-s -w" -o bin/app + - cp config.example.yml bin/config.yml + package: + requires: + vars: [GOOS, GOARCH] + preconditions: + - test -f bin/app + - test -f bin/config.yml + cmds: + - tar -czf app-{{.GOOS }}-{{.GOARCH }}.tar.gz bin/* + fmt: + cmds: + - gofumpt -l -w . + lint: + cmds: + - golangci-lint run --color "always" -v -j 8 + sec: + cmds: + - gosec ./... + tidy: + cmds: + - rm -f go.sum + - go mod tidy + update: + cmds: + - go get -u ./... # Updates regular packages + - go get -u -t ./... # Updates Test packages + cli-tools: + cmds: + - go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest + - go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + - go install github.com/securego/gosec/v2/cmd/gosec@latest + - go install github.com/uudashr/gopkgs/v2/cmd/gopkgs@latest + - go install github.com/ramya-rao-a/go-outline@latest + - go install github.com/cweill/gotests/gotests@latest + - go install github.com/fatih/gomodifytags@latest + - go install github.com/daixiang0/gci@latest + - go install github.com/josharian/impl@latest + - go install github.com/haya14busa/goplay/cmd/goplay@latest + - go install github.com/go-delve/delve/cmd/dlv@latest + - go install mvdan.cc/gofumpt@latest + - go install github.com/swaggo/swag/cmd/swag@latest + - go install github.com/cosmtrek/air@latest + test: + cmds: + - go test -covermode=atomic -race -coverprofile=coverage.txt -timeout 5m -json -v ./... | gotestfmt -showteststatus + env: + GOMAXPROCS: 4 + setup: + cmds: + - cp config.example.yml config.yml + run: + cmds: + - go run main.go {{ .CLI_ARGS }} + swagger-gen: + cmds: + - swag init --outputTypes="go" --parseDependency + docker-build: + requires: + vars: [VERSION, TARGET] + cmds: + - docker buildx build --target {{ .TARGET }} --build-arg VERSION="{{ .VERSION }}" --platform -t "brossquad/app:{{ .VERSION }}" --file ./Dockerfile . + clean: + cmds: + - rm -rf bin/ \ No newline at end of file diff --git a/app/commands/serve.go b/app/commands/serve.go new file mode 100644 index 0000000..d295d67 --- /dev/null +++ b/app/commands/serve.go @@ -0,0 +1,29 @@ +package commands + +import ( + "time" + + "github.com/spf13/cobra" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/container" + "github.com/BrosSquad/GoFiber-Boilerplate/app/http" + "github.com/BrosSquad/GoFiber-Boilerplate/core/constants" + corehttp "github.com/BrosSquad/GoFiber-Boilerplate/core/http" +) + +func Serve() *cobra.Command { + return &cobra.Command{ + Use: "serve", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + di := ctx.Value(constants.ContainerContextKey).(*container.Container) + app := http.CreateApplication(ctx, di, true) + + cfg := di.GetConfig().HTTP + go corehttp.RunServer(cfg.Addr, cfg.Port, app) + + <-ctx.Done() + return app.ShutdownWithTimeout(10 * time.Second) + }, + } +} diff --git a/app/config/config.go b/app/config/config.go new file mode 100644 index 0000000..eb36a96 --- /dev/null +++ b/app/config/config.go @@ -0,0 +1,55 @@ +package config + +import ( + "github.com/spf13/viper" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/constants" + utilsconfig "github.com/nano-interactive/go-utils/config" +) + +type ( + Logging struct { + Level string `mapstructure:"level" json:"level" yaml:"level"` + PrettyPrint bool `mapstructure:"pretty_print" json:"pretty_print" yaml:"pretty_print"` + } + + HTTP struct { + Addr string `mapstructure:"addr" json:"addr" yaml:"addr"` + Port int `mapstructure:"port" json:"port" yaml:"port"` + } + + Config struct { + Logging Logging `mapstructure:"logging" json:"logging" yaml:"logging"` + HTTP HTTP `mapstructure:"http" json:"http" yaml:"http"` + } +) + +func New() (Config, error) { + cfg := utilsconfig.Config{ + ProjectName: constants.AppName, + Name: "config", + Type: "yaml", + Paths: []string{ + "$XDG_CONFIG_HOME/" + constants.AppName, + "/etc/" + constants.AppName, + ".", + }, + } + + v, err := utilsconfig.NewWithModifier(cfg) + if err != nil { + return Config{}, err + } + + return NewWithViper(v) +} + +func NewWithViper(v *viper.Viper) (Config, error) { + c := Config{} + + if err := v.Unmarshal(&c); err != nil { + return Config{}, err + } + + return c, nil +} diff --git a/app/constants/constants.go b/app/constants/constants.go new file mode 100644 index 0000000..5bc5f98 --- /dev/null +++ b/app/constants/constants.go @@ -0,0 +1,6 @@ +package constants + +const ( + AppName = "app" + AppDescription = "Go Fiber Boilerplate" +) diff --git a/app/container/container.go b/app/container/container.go new file mode 100644 index 0000000..1720c6b --- /dev/null +++ b/app/container/container.go @@ -0,0 +1,27 @@ +package container + +import ( + "context" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/config" +) + +type Container struct { + config *config.Config + ctx context.Context +} + +func New(ctx context.Context, config config.Config) *Container { + return &Container{ + ctx: ctx, + config: &config, + } +} + +func (c *Container) GetConfig() config.Config { + return *c.config +} + +func (c *Container) Close() error { + return nil +} diff --git a/app/container/logger.go b/app/container/logger.go new file mode 100644 index 0000000..ba7ac1f --- /dev/null +++ b/app/container/logger.go @@ -0,0 +1,25 @@ +package container + +import ( + "io" + "os" + + appLogger "github.com/nano-interactive/go-utils/logging" + "github.com/rs/zerolog" +) + +func (c *Container) GetLogger() zerolog.Logger { + var stdout io.Writer = os.Stdout + + if c.config.Logging.PrettyPrint { + stdout = zerolog.NewConsoleWriter() + } + + writer := stdout + + return appLogger.New(c.config.Logging.Level, c.config.Logging.PrettyPrint). + Output(writer). + With(). + Stack(). + Logger() +} diff --git a/app/dto/dto.go b/app/dto/dto.go new file mode 100644 index 0000000..2932164 --- /dev/null +++ b/app/dto/dto.go @@ -0,0 +1,5 @@ +package dto + +type ErrorResponse struct { + Message any `json:"message,omitempty"` +} diff --git a/app/http/handlers.go b/app/http/handlers.go new file mode 100644 index 0000000..68fd54f --- /dev/null +++ b/app/http/handlers.go @@ -0,0 +1,12 @@ +package http + +import ( + "github.com/gofiber/fiber/v2" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/container" + "github.com/BrosSquad/GoFiber-Boilerplate/app/http/helloworld" +) + +func routes(app fiber.Router, c *container.Container) { + app.Get("/", helloworld.HelloWorld(c.GetLogger())) +} diff --git a/pkg/http/handlers/hello_world/hello_world.go b/app/http/helloworld/handler.go similarity index 92% rename from pkg/http/handlers/hello_world/hello_world.go rename to app/http/helloworld/handler.go index 660aecd..3c8abaa 100644 --- a/pkg/http/handlers/hello_world/hello_world.go +++ b/app/http/helloworld/handler.go @@ -1,4 +1,4 @@ -package hello_world +package helloworld import ( "github.com/gofiber/fiber/v2" diff --git a/app/http/helloworld/handler_test.go b/app/http/helloworld/handler_test.go new file mode 100644 index 0000000..c034452 --- /dev/null +++ b/app/http/helloworld/handler_test.go @@ -0,0 +1,15 @@ +package helloworld_test + +// func TestHelloWorldHandler(t *testing.T) { +// t.Parallel() +// assert := require.New(t) + +// app, _ := testing_utils.CreateApplication() +// _, loggerAssert := testing_utils.NewTestLogger(t, zerolog.InfoLevel) + +// app.Get("/", hello_world.HelloWorld(loggerAssert.Logger())) + +// res := testing_utils.Get(app, "/") + +// assert.Equal(http.StatusOK, res.StatusCode) +// } diff --git a/app/http/http.go b/app/http/http.go new file mode 100644 index 0000000..8618fc2 --- /dev/null +++ b/app/http/http.go @@ -0,0 +1,14 @@ +package http + +import ( + "context" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/constants" + "github.com/BrosSquad/GoFiber-Boilerplate/app/container" + corehttp "github.com/BrosSquad/GoFiber-Boilerplate/core/http" + "github.com/gofiber/fiber/v2" +) + +func CreateApplication(ctx context.Context, c *container.Container, displayInfo bool) *fiber.App { + return corehttp.CreateApplication(ctx, constants.AppName, c, displayInfo, routes) +} diff --git a/app/testutils/testing.go b/app/testutils/testing.go new file mode 100644 index 0000000..f71fb93 --- /dev/null +++ b/app/testutils/testing.go @@ -0,0 +1,78 @@ +package testutils + +import ( + "context" + "testing" + "time" + + "github.com/gofiber/fiber/v2" + nanotesting "github.com/nano-interactive/go-utils/testing" + nanofibertesting "github.com/nano-interactive/go-utils/testing/fiber" + "github.com/samber/lo" + "github.com/spf13/viper" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/config" + "github.com/BrosSquad/GoFiber-Boilerplate/app/container" + "github.com/BrosSquad/GoFiber-Boilerplate/app/http" +) + +func GetConfig(tb testing.TB) config.Config { + tb.Helper() + return nanotesting.GetConfig[config.Config](tb, func(v *viper.Viper) (config.Config, error) { + cfg, err := config.NewWithViper(v) + if err != nil { + return config.Config{}, err + } + + cfg.Logging.PrettyPrint = true + + return cfg, nil + }) +} + +func AppTester[T any](tb testing.TB, items ...any) *nanofibertesting.GoFiberSender[T] { + tb.Helper() + sender, _, _ := App[T](tb, false, items...) + return sender +} + +func App[T any](tb testing.TB, followRedirects bool, items ...any) (*nanofibertesting.GoFiberSender[T], *container.Container, *fiber.App) { + tb.Helper() + + creator := nanotesting.AppCreaterFunc[*fiber.App, *container.Container](func(ctx context.Context, v *viper.Viper) (*fiber.App, *container.Container) { + conf, found := lo.Find(items, func(l any) bool { + _, ok := l.(config.Config) + + return ok + }) + + cfg, err := config.NewWithViper(v) + if err != nil { + tb.Errorf("Failed to parse config file: %v", v) + tb.FailNow() + } + + if found { + cfg = conf.(config.Config) + } + di := container.New(ctx, cfg) + app := http.CreateApplication(ctx, di, false) + + tb.Cleanup(func() { + if err := app.ShutdownWithTimeout(10 * time.Second); err != nil { + tb.Errorf("Failed to close APP Server: %v", err) + } + + if err := di.Close(); err != nil { + tb.Errorf("Failed to close DI Container: %v", err) + } + }) + + return nil, nil + }) + + app, di := nanotesting.CreateApplication[*fiber.App, *container.Container](tb, creator) + sender := nanofibertesting.New[T](tb, app, followRedirects) + + return sender, di, app +} diff --git a/cmd/base/base.go b/cmd/base/base.go deleted file mode 100644 index 196d524..0000000 --- a/cmd/base/base.go +++ /dev/null @@ -1,46 +0,0 @@ -package base - -import ( - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/config" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/container" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/logging" -) - -var ( - ViperConfig *viper.Viper - Environment config.Env - - EnvironmentStr string - ConfigName string - ConfigType string - LoggingLevel string - - FiberLogo bool - LogPrettyPrint bool -) - -func LoadConfig(*cobra.Command, []string) error { - logging.ConfigureDefaultLogger(LoggingLevel, LogPrettyPrint) - - v, err := config.New(EnvironmentStr, ConfigName, ConfigType) - if err != nil { - log.Fatal().Err(err).Msg("Failed to load Configuration") - return err - } - - ViperConfig = v - - return nil -} - -func GetContainer() *container.Container { - return container.New(ViperConfig, LogPrettyPrint, LoggingLevel, Environment) -} - -func CloseResources(*cobra.Command, []string) error { - return nil -} diff --git a/cmd/cmd.go b/cmd/cmd.go deleted file mode 100644 index 10445c2..0000000 --- a/cmd/cmd.go +++ /dev/null @@ -1,43 +0,0 @@ -package cmd - -import ( - "context" - - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - - "github.com/BrosSquad/GoFiber-Boilerplate/cmd/base" - "github.com/BrosSquad/GoFiber-Boilerplate/cmd/commands" -) - -func registerCommands(root *cobra.Command) { - root.AddCommand(commands.Serve()) -} - -func Execute(version string) { - rootCmd := &cobra.Command{ - Use: "boilerplate", - Short: "boilerplate", - Long: `Go Fiber Boilerplate`, - PersistentPreRunE: base.LoadConfig, - PersistentPostRunE: base.CloseResources, - Version: version, - } - - flags := rootCmd.PersistentFlags() - - flags.StringVarP(&base.EnvironmentStr, "env", "e", "production", "Running EnvironmentStr (Production|Development|Testing)") - flags.StringVarP(&base.ConfigType, "config-type", "t", "yaml", "Configuration Type (yaml|json|toml)") - flags.StringVarP(&base.ConfigName, "config-name", "c", "config", "Configuration name") - flags.StringVarP(&base.LoggingLevel, "log-level", "l", "info", "Logging Level (Trace|Debug|Info|Warn|Error|Fatal)") - flags.BoolVarP(&base.FiberLogo, "fiber-logo", "f", false, "Display Fiber Information") - flags.BoolVarP(&base.LogPrettyPrint, "log-pretty-print", "p", false, "Pretty print STDOUT/STDERR logs") - - registerCommands(rootCmd) - - if err := rootCmd.ExecuteContext(context.Background()); err != nil { - log.Fatal(). - Err(err). - Msg("Error while running command") - } -} diff --git a/cmd/commands/serve.go b/cmd/commands/serve.go deleted file mode 100644 index e65ea7a..0000000 --- a/cmd/commands/serve.go +++ /dev/null @@ -1,67 +0,0 @@ -package commands - -import ( - "context" - "os" - "os/signal" - - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - - "github.com/BrosSquad/GoFiber-Boilerplate/cmd/base" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/constants" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/container" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http/handlers" -) - -func startHttpServer(ctx context.Context, c *container.Container) { - app := http.CreateApplication( - ctx, - c, - constants.AppName, - base.Environment, - base.FiberLogo, - base.ViperConfig.GetBool("http.enable_monitor"), - handlers.Error( - c.GetLogger(), - c.GetTranslator(), - ), - ) - - http.RunServer( - base.ViperConfig.GetString("http.host"), - base.ViperConfig.GetInt("http.port"), - app, - ) -} - -func Serve() *cobra.Command { - return &cobra.Command{ - Use: "serve", - RunE: func(command *cobra.Command, args []string) error { - c := base.GetContainer() - - defer func(c *container.Container) { - err := c.Close() - if err != nil { - log.Error().Err(err).Msg("Failed to close DI Container") - } - }(c) - - ctx, cancel := context.WithCancel(command.Context()) - sig := make(chan os.Signal, 1) - signal.Notify(sig, os.Interrupt) - - defer cancel() - - log.Info().Msg("Starting HTTP Server") - go startHttpServer(ctx, c) - - <-sig - cancel() - - return nil - }, - } -} diff --git a/config.example.yml b/config.example.yml index bd9ddad..f33f87c 100644 --- a/config.example.yml +++ b/config.example.yml @@ -1,26 +1,3 @@ http: - bind: '0.0.0.0' + addr: '0.0.0.0' port: 8080 - domain: localhost - enable_monitor: true - -cors: - headers: - - '*' - origins: - - '*' - methods: - - '*' - -databases: - redis: - host: redis - port: 6379 - username: '' - password: '' - session: - db: 1 - csrf: - db: 2 - queue: - db: 3 diff --git a/core/cmd/cmd.go b/core/cmd/cmd.go new file mode 100644 index 0000000..f1f7c5e --- /dev/null +++ b/core/cmd/cmd.go @@ -0,0 +1,62 @@ +package cmd + +import ( + "context" + "os" + "os/signal" + "syscall" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/rs/zerolog/pkgerrors" + "github.com/spf13/cobra" + + appLogger "github.com/nano-interactive/go-utils/logging" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/config" + "github.com/BrosSquad/GoFiber-Boilerplate/app/container" + "github.com/BrosSquad/GoFiber-Boilerplate/core/constants" +) + +func Execute(version string, cmds []*cobra.Command, use, short, long string) { + rootCmd := &cobra.Command{ + Use: use, + Short: short, + Long: long, + Version: version, + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + ctx := context.Background() + cfg, err := config.New() + if err != nil { + return err + } + + appLogger.ConfigureDefaultLogger(cfg.Logging.Level, cfg.Logging.PrettyPrint) + //nolint:all + zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack + + cnt := container.New(ctx, cfg) + ctx = context.WithValue(ctx, constants.ContainerContextKey, cnt) + ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) + ctx = context.WithValue(ctx, constants.CancelContextKey, cancel) + + cmd.SetContext(ctx) + return nil + }, + PersistentPostRunE: func(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + cancel := ctx.Value(constants.CancelContextKey).(context.CancelFunc) + cnt := ctx.Value(constants.ContainerContextKey).(*container.Container) + cancel() + return cnt.Close() + }, + } + + rootCmd.AddCommand(cmds...) + + if err := rootCmd.ExecuteContext(context.Background()); err != nil { + log.Fatal(). + Err(err). + Msg("Error while running command") + } +} diff --git a/core/constants/context.go b/core/constants/context.go new file mode 100644 index 0000000..bfce0cb --- /dev/null +++ b/core/constants/context.go @@ -0,0 +1,11 @@ +package constants + +type ContextKey string + +const ( + RequestIDContextKey ContextKey = "request_id" + CancelFuncContextKey ContextKey = "cancel" + CancelWillBeCalledContextKey ContextKey = "cancelFnWillBeCalled" + ContainerContextKey ContextKey = "container" + CancelContextKey ContextKey = "cancel" +) diff --git a/pkg/http/middleware/context.go b/core/http/context.go similarity index 85% rename from pkg/http/middleware/context.go rename to core/http/context.go index 84ca2a2..6c07975 100644 --- a/pkg/http/middleware/context.go +++ b/core/http/context.go @@ -1,11 +1,11 @@ -package middleware +package http import ( "context" "github.com/gofiber/fiber/v2" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/constants" + "github.com/BrosSquad/GoFiber-Boilerplate/core/constants" ) func Context(base context.Context) fiber.Handler { diff --git a/pkg/http/middleware/context_test.go b/core/http/context_test.go similarity index 72% rename from pkg/http/middleware/context_test.go rename to core/http/context_test.go index 31a4120..393cb61 100644 --- a/pkg/http/middleware/context_test.go +++ b/core/http/context_test.go @@ -1,4 +1,4 @@ -package middleware +package http_test import ( "context" @@ -9,25 +9,22 @@ import ( "github.com/stretchr/testify/require" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/constants" + "github.com/BrosSquad/GoFiber-Boilerplate/core/constants" + "github.com/BrosSquad/GoFiber-Boilerplate/core/http" ) func TestContextMiddleware(t *testing.T) { t.Parallel() - assert := require.New(t) + assert := require.New(t) app := fiber.New() - - app.Use(Context(context.Background())) - + app.Use(http.Context(context.Background())) app.Get("/", func(ctx *fiber.Ctx) error { return ctx.SendStatus(fiber.StatusOK) }) h := app.Handler() - ctx := &fasthttp.RequestCtx{} - h(ctx) assert.NotNil(ctx.UserValue(constants.CancelFuncContextKey)) diff --git a/core/http/error.go b/core/http/error.go new file mode 100644 index 0000000..0243d44 --- /dev/null +++ b/core/http/error.go @@ -0,0 +1,51 @@ +package http + +import ( + "errors" + + "github.com/gofiber/fiber/v2" + "github.com/invopop/validation" + "github.com/rs/zerolog" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type ErrorResponse struct { + Message any `json:"message,omitempty"` +} + +func Error(logger zerolog.Logger) fiber.ErrorHandler { + return func(c *fiber.Ctx, err error) error { + c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8) + + if errors.Is(err, primitive.ErrInvalidHex) { + return c.Status(fiber.StatusBadRequest).JSON(ErrorResponse{ + Message: "Invalid JSON Payload, check your input", + }) + } + + var fiberErr *fiber.Error + + if errors.As(err, &fiberErr) { + return c.Status(fiberErr.Code).JSON(ErrorResponse{ + Message: fiberErr.Message, + }) + } + + { + var validationErr validation.Errors + if errors.As(err, &validationErr) { + return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{ + "errors": validationErr, + }) + } + } + + logger.Error().Err(err). + Str("path", c.Route().Path). + Msg("Failed to process request") + + return c.Status(fiber.StatusInternalServerError).JSON(ErrorResponse{ + Message: "An error has occurred!", + }) + } +} diff --git a/core/http/error_test.go b/core/http/error_test.go new file mode 100644 index 0000000..f70cee1 --- /dev/null +++ b/core/http/error_test.go @@ -0,0 +1,96 @@ +package http_test + +// func setupErrorHandlerApp(t *testing.T) (*fiber.App, *zltest.Tester) { +// // v, translations := testing_utils.GetValidator() + +// logger, loggerTest := nanotesting.NewAppTestLogger(t, zerolog.InfoLevel) + +// app := fiber.New(fiber.Config{ +// ErrorHandler: handlers.Error(logger, nil), +// }) + +// return app, v, loggerTest +// } + +// func TestErrorHandler_ReturnFiberError(t *testing.T) { +// t.Parallel() +// assert := require.New(t) + +// app, _, _ := setupErrorHandlerApp(t) + +// app.Get("/", func(ctx *fiber.Ctx) error { +// return fiber.ErrBadGateway +// }) + +// m := struct { +// Message string `json:"message"` +// }{} +// res := testing_utils.Get(app, "/") + +// assert.EqualValues(fiber.StatusBadGateway, res.StatusCode) +// assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) +// assert.Nil(json.NewDecoder(res.Body).Decode(&m)) +// assert.NotEmpty(m.Message) +// } + +// func TestErrorHandler_InvalidPayloadError(t *testing.T) { +// t.Parallel() +// assert := require.New(t) + +// app, _, _ := setupErrorHandlerApp(t) +// app.Get("/", func(ctx *fiber.Ctx) error { +// return handlers.ErrInvalidPayload +// }) + +// res := testing_utils.Get(app, "/") + +// m := struct { +// Message string `json:"message"` +// }{} + +// assert.EqualValues(fiber.StatusBadRequest, res.StatusCode) +// assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) +// assert.Nil(json.NewDecoder(res.Body).Decode(&m)) +// assert.NotEmpty(m.Message) +// assert.Equal(handlers.ErrInvalidPayload.Error(), m.Message) +// } + +// func TestErrorHandler_ValidationError(t *testing.T) { +// t.Parallel() +// assert := require.New(t) +// app, _, _ := setupErrorHandlerApp(t) +// app.Get("/", func(ctx *fiber.Ctx) error { +// return validator.ValidationErrors{} +// }) +// res, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) +// assert.Nil(err) +// assert.EqualValues(fiber.StatusUnprocessableEntity, res.StatusCode) +// assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) +// } + +// func TestErrorHandler_InvalidValidationError(t *testing.T) { +// t.Parallel() +// assert := require.New(t) + +// app, _, _ := setupErrorHandlerApp(t) +// app.Get("/", func(ctx *fiber.Ctx) error { +// return &validator.InvalidValidationError{} +// }) +// res, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) +// assert.Nil(err) +// assert.EqualValues(fiber.StatusUnprocessableEntity, res.StatusCode) +// assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) +// } + +// func TestErrorHandler_AnyError(t *testing.T) { +// t.Parallel() +// assert := require.New(t) +// app, _, _ := setupErrorHandlerApp(t) +// app.Get("/", func(ctx *fiber.Ctx) error { +// return errors.New("any other error") +// }) +// res, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) +// assert.Nil(err) +// assert.EqualValues(fiber.StatusInternalServerError, res.StatusCode) +// assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) +// } diff --git a/core/http/http.go b/core/http/http.go new file mode 100644 index 0000000..3be7066 --- /dev/null +++ b/core/http/http.go @@ -0,0 +1,77 @@ +package http + +import ( + "context" + "encoding/json" + "fmt" + "net" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/container" + "github.com/BrosSquad/GoFiber-Boilerplate/core/constants" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" + "github.com/gofiber/fiber/v2/middleware/requestid" + fiberutils "github.com/gofiber/fiber/v2/utils" + "github.com/nano-interactive/go-utils" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +type Routes func(fiber.Router, *container.Container) + +func RunServer(ip string, port int, app *fiber.App) { + addr := fmt.Sprintf("%s:%d", ip, port) + + listener, err := net.Listen("tcp", addr) + if err != nil { + log. + Fatal(). + Err(err). + Msg("Error while creating net.Listener for HTTP Server") + } + + err = app.Listener(listener) + + if err != nil { + log. + Fatal(). + Err(err). + Msg("Cannot start Fiber HTTP Server") + } +} + +func CreateApplication(ctx context.Context, appName string, c *container.Container, displayInfo bool, routes Routes) *fiber.App { + var ( + jsonEncoder fiberutils.JSONMarshal = json.Marshal + jsonDecoder fiberutils.JSONUnmarshal = json.Unmarshal + ) + + staticConfig := fiber.Config{ + StrictRouting: true, + EnablePrintRoutes: false, + Prefork: false, + DisableStartupMessage: !displayInfo, + DisableDefaultDate: true, + DisableHeaderNormalizing: false, + DisablePreParseMultipartForm: true, + AppName: appName, + ErrorHandler: Error(zerolog.Nop()), + JSONEncoder: jsonEncoder, + JSONDecoder: jsonDecoder, + } + + app := fiber.New(staticConfig) + + app.Use(recover.New()) + app.Use(Context(ctx)) + app.Use(requestid.New(requestid.Config{ + Generator: func() string { + return utils.RandomString(32) + }, + ContextKey: constants.RequestIDContextKey, + })) + + routes(app, c) + + return app +} diff --git a/pkg/http/handlers/handlers.go b/core/http/notfound.go similarity index 75% rename from pkg/http/handlers/handlers.go rename to core/http/notfound.go index e851d6d..87d0b8b 100644 --- a/pkg/http/handlers/handlers.go +++ b/core/http/notfound.go @@ -1,17 +1,9 @@ -package handlers +package http import ( - "errors" - "github.com/gofiber/fiber/v2" ) -var ErrInvalidPayload = errors.New("Invalid Payload") - -type ErrorResponse struct { - Message interface{} `json:"message,omitempty"` -} - const pageNotFoundMessage = "Page is not found" func NotFound() fiber.Handler { diff --git a/core/http/notfound_test.go b/core/http/notfound_test.go new file mode 100644 index 0000000..9f9052b --- /dev/null +++ b/core/http/notfound_test.go @@ -0,0 +1,30 @@ +package http_test + +// func setupNotFoundApplication() *fiber.App { +// app := fiber.New() + +// app.Use(handlers.NotFound()) + +// return app +// } + +// func TestNotFound_JsonResponse(t *testing.T) { +// t.Parallel() +// assert := require.New(t) + +// app := setupNotFoundApplication() + +// res := testing_utils.Get(app, "/", testing_utils.WithHeaders(http.Header{ +// fiber.HeaderAccept: []string{fiber.MIMEApplicationJSONCharsetUTF8}, +// })) + +// assert.Equal(http.StatusNotFound, res.StatusCode) +// assert.Equal(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) + +// var errRes handlers.ErrorResponse +// _ = json.NewDecoder(res.Body).Decode(&errRes) + +// assert.EqualValues(handlers.ErrorResponse{ +// Message: "Page is not found", +// }, errRes) +// } diff --git a/go.mod b/go.mod index 4a25ed2..c50d9e4 100644 --- a/go.mod +++ b/go.mod @@ -1,54 +1,64 @@ module github.com/BrosSquad/GoFiber-Boilerplate -go 1.20 +go 1.21 require ( - github.com/go-playground/locales v0.14.1 - github.com/go-playground/universal-translator v0.18.1 - github.com/go-playground/validator/v10 v10.11.2 - github.com/goccy/go-json v0.10.0 - github.com/gofiber/fiber/v2 v2.42.0 - github.com/minio/simdjson-go v0.4.4 - github.com/rs/zerolog v1.29.0 - github.com/rzajac/zltest v0.12.0 - github.com/spf13/cobra v1.6.1 - github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.2 - github.com/valyala/fasthttp v1.44.0 + github.com/goccy/go-json v0.10.2 + github.com/gofiber/fiber/v2 v2.50.0 + github.com/invopop/validation v0.3.0 + github.com/nano-interactive/go-utils v1.5.3 + github.com/rs/zerolog v1.31.0 + github.com/samber/lo v1.38.1 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.17.0 + github.com/stretchr/testify v1.8.4 + github.com/valyala/fasthttp v1.50.0 + go.mongodb.org/mongo-driver v1.12.1 ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/andybalholm/brotli v1.0.6 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/gocql/gocql v1.6.0 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.3.1 // indirect + github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.16.0 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.2 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect - github.com/philhofer/fwd v1.1.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.4 // indirect - github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect - github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect - github.com/spf13/afero v1.9.4 // indirect - github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/rzajac/zltest v0.12.0 // indirect + github.com/sagikazarmark/locafero v0.3.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.10.0 // indirect + github.com/spf13/cast v1.5.1 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.2 // indirect - github.com/tinylib/msgp v1.1.8 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + github.com/tanimutomo/sqlfile v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + github.com/xdg-go/pbkdf2 v1.0.0 // indirect + github.com/xdg-go/scram v1.1.2 // indirect + github.com/xdg-go/stringprep v1.0.4 // indirect + github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index c6c11c6..0b0ba14 100644 --- a/go.sum +++ b/go.sum @@ -38,9 +38,17 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 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/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= +github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/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/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/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= @@ -50,35 +58,34 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 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.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= -github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= -github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= -github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gocql/gocql v1.6.0 h1:IdFdOTbnpbd0pDhl4REKQDM+Q0SzKXQ1Yh+YZZ8T/qU= +github.com/gocql/gocql v1.6.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofiber/fiber/v2 v2.42.0 h1:Fnp7ybWvS+sjNQsFvkhf4G8OhXswvB6Vee8hM/LyS+8= -github.com/gofiber/fiber/v2 v2.42.0/go.mod h1:3+SGNjqMh5VQH5Vz2Wdi43zTIV16ktlFd3x3R6O1Zlc= +github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= +github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= 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= @@ -104,6 +111,10 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq 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/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/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/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -116,6 +127,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/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/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -131,97 +143,101 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/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/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +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/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/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/invopop/validation v0.3.0 h1:o260kbjXzoBO/ypXDSSrCLL7SxEFUXBsX09YTE9AxZw= +github.com/invopop/validation v0.3.0/go.mod h1:qIBG6APYLp2Wu3/96p3idYjP8ffTKVmQBfKiZbw0Hts= 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/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= -github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4= -github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/minio/simdjson-go v0.4.4 h1:Jd3PTBSLtWGNvJA6WC9wOwZ/ic5Y41jAj9BXr6uyfv8= -github.com/minio/simdjson-go v0.4.4/go.mod h1:eoNz0DcLQRyEDeaPr4Ru6JpjlZPzbA0IodxVJk8lO8E= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= -github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/nano-interactive/go-utils v1.5.3 h1:gmi0bkj9d2zxrVsk0h0cfQ+HjFF9xvxP2+Fl687sIuM= +github.com/nano-interactive/go-utils v1.5.3/go.mod h1:z+wk/heUyiXGMKcR+ZjMcZnXST8e61nsL9mKgDj9Emw= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI= -github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= -github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= +github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= github.com/rzajac/zltest v0.12.0 h1:9WPX0UhhXG66iuRT9+jYSl9SAGyl+PmKsC4+UGKLJVU= github.com/rzajac/zltest v0.12.0/go.mod h1:wZSsCw1RyFaEIfUOCUw8DiicX4U6yB1IZdOg8Uj0yDI= -github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= -github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= -github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= -github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= -github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= -github.com/spf13/afero v1.9.4 h1:Sd43wM1IWz/s1aVXdOBkjJvuP8UdyqioeE4AmM0QsBs= -github.com/spf13/afero v1.9.4/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= +github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= -github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= +github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= +github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/objx v0.5.1 h1:4VhoImhV/Bm0ToFkXFi8hXNXwpDRZ/ynw3amt82mzq0= +github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= +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= @@ -229,42 +245,55 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= -github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/tanimutomo/sqlfile v1.0.0 h1:8Nnkd1ra7vBDb7yrv4zvvEdto0vO5Yuegv87C6Tcyp8= +github.com/tanimutomo/sqlfile v1.0.0/go.mod h1:vdHiTAUB+JJn9lSFzv4iLGm54ghBOo444TthhTSvP4E= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR5Q= -github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= +github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= 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/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= +go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= 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.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/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= @@ -275,6 +304,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 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/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 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= @@ -300,7 +331,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -335,8 +365,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/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= @@ -358,7 +386,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -396,20 +425,15 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= 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= @@ -418,9 +442,10 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/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= @@ -465,7 +490,6 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= @@ -474,7 +498,6 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= 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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -569,8 +592,11 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index a49d5eb..34c1fca 100644 --- a/main.go +++ b/main.go @@ -1,11 +1,17 @@ package main import ( - "github.com/BrosSquad/GoFiber-Boilerplate/cmd" + "github.com/spf13/cobra" + + "github.com/BrosSquad/GoFiber-Boilerplate/app/commands" + "github.com/BrosSquad/GoFiber-Boilerplate/app/constants" + "github.com/BrosSquad/GoFiber-Boilerplate/core/cmd" ) const Version = "0.0.1" func main() { - cmd.Execute(Version) + cmd.Execute(Version, []*cobra.Command{ + commands.Serve(), + }, constants.AppName, constants.AppName, constants.AppDescription) } diff --git a/pkg/config/config.go b/pkg/config/config.go deleted file mode 100644 index 81b11ee..0000000 --- a/pkg/config/config.go +++ /dev/null @@ -1,35 +0,0 @@ -package config - -import ( - "github.com/spf13/viper" -) - -func New(envStr, configName, configTypeStr string) (*viper.Viper, error) { - env, err := ParseEnvironment(envStr) - if err != nil { - return nil, err - } - - configType, err := ParseConfigType(configTypeStr) - if err != nil { - return nil, err - } - - v := viper.New() - - v.SetConfigName(configName) - v.SetConfigType(string(configType)) - - if env == Production { - v.AddConfigPath("/etc/sitemap") - v.AddConfigPath(".") - } else { - v.AddConfigPath(".") - } - - if err := v.ReadInConfig(); err != nil { - return nil, err - } - - return v, nil -} diff --git a/pkg/config/config_type.go b/pkg/config/config_type.go deleted file mode 100644 index aca5d8f..0000000 --- a/pkg/config/config_type.go +++ /dev/null @@ -1,27 +0,0 @@ -package config - -import ( - "errors" - "strings" -) - -type ConfigType string - -const ( - JSON ConfigType = "json" - YAML ConfigType = "yaml" - TOML ConfigType = "toml" -) - -func ParseConfigType(configType string) (ConfigType, error) { - switch strings.ToLower(configType) { - case "json": - return JSON, nil - case "yaml", "": // Empty string as default - return YAML, nil - case "toml": - return TOML, nil - default: - return "", errors.New("Invalid Configuration Type: JSON, YAML, TOML or \"\"(empty string), Given: " + configType) - } -} diff --git a/pkg/config/env.go b/pkg/config/env.go deleted file mode 100644 index 2b97197..0000000 --- a/pkg/config/env.go +++ /dev/null @@ -1,27 +0,0 @@ -package config - -import ( - "errors" - "strings" -) - -type Env uint8 - -const ( - Testing Env = iota - Development - Production -) - -func ParseEnvironment(env string) (Env, error) { - switch strings.ToLower(env) { - case "prod", "production": - return Production, nil - case "dev", "development", "develop": - return Development, nil - case "testing", "test": - return Testing, nil - default: - return 0, errors.New("Invalid Environment") - } -} diff --git a/pkg/config/env_test.go b/pkg/config/env_test.go deleted file mode 100644 index d7b82b3..0000000 --- a/pkg/config/env_test.go +++ /dev/null @@ -1 +0,0 @@ -package config_test diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go deleted file mode 100644 index 8737b43..0000000 --- a/pkg/constants/constants.go +++ /dev/null @@ -1,15 +0,0 @@ -package constants - -const ( - AppName = "GoFiber-Boilerplate" - RequestIdKey = "request_id" -) - -const ( - TestUserAgent = "GoFiber-Boilerplate/Testing 1.0" -) - -const ( - CancelFuncContextKey = "cancel" - CancelWillBeCalledContextKey = "cancelFnWillBeCalled" -) diff --git a/pkg/container/container.go b/pkg/container/container.go deleted file mode 100644 index ff779be..0000000 --- a/pkg/container/container.go +++ /dev/null @@ -1,36 +0,0 @@ -package container - -import ( - ut "github.com/go-playground/universal-translator" - "github.com/go-playground/validator/v10" - "github.com/spf13/viper" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/config" -) - -type Container struct { - config *viper.Viper - loggingLevel string - loggingPrettyPrint bool - environment config.Env - - validator *validator.Validate - translator ut.Translator -} - -func New(config *viper.Viper, loggingPrettyPrint bool, loggingLevel string, env config.Env) *Container { - return &Container{ - config: config, - loggingLevel: loggingLevel, - loggingPrettyPrint: loggingPrettyPrint, - environment: env, - } -} - -func (c *Container) GetEnvironment() config.Env { - return c.environment -} - -func (c *Container) Close() error { - return nil -} diff --git a/pkg/container/logger.go b/pkg/container/logger.go deleted file mode 100644 index 7984611..0000000 --- a/pkg/container/logger.go +++ /dev/null @@ -1,11 +0,0 @@ -package container - -import ( - "github.com/rs/zerolog" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/logging" -) - -func (c *Container) GetLogger() zerolog.Logger { - return logging.New(c.loggingLevel, c.loggingPrettyPrint) -} diff --git a/pkg/container/validator.go b/pkg/container/validator.go deleted file mode 100644 index 2a6821a..0000000 --- a/pkg/container/validator.go +++ /dev/null @@ -1,43 +0,0 @@ -package container - -import ( - "github.com/go-playground/locales/en" - ut "github.com/go-playground/universal-translator" - "github.com/go-playground/validator/v10" - entranslations "github.com/go-playground/validator/v10/translations/en" - "github.com/rs/zerolog/log" -) - -func (c *Container) GetValidator() *validator.Validate { - if c.validator == nil { - c.validator = validator.New() - - if err := entranslations.RegisterDefaultTranslations(c.validator, c.GetTranslator()); err != nil { - log. - Fatal(). - Err(err). - Msg("Error while registering english translations") - } - } - - return c.validator -} - -func (c *Container) GetTranslator() ut.Translator { - if c.translator == nil { - english := en.New() - uni := ut.New(english, english) - - translator, found := uni.GetTranslator("en") - - if !found { - log. - Fatal(). - Msg("Locale is not found: en") - } - - c.translator = translator - } - - return c.translator -} diff --git a/pkg/http/handlers.go b/pkg/http/handlers.go deleted file mode 100644 index 519fe83..0000000 --- a/pkg/http/handlers.go +++ /dev/null @@ -1,14 +0,0 @@ -package http - -import ( - "github.com/gofiber/fiber/v2" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/config" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/container" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http/handlers/hello_world" -) - -func registerHandlers(app *fiber.App, c *container.Container, environment config.Env) { - app.Get("/", hello_world.HelloWorld(c.GetLogger())) -} diff --git a/pkg/http/handlers/error.go b/pkg/http/handlers/error.go deleted file mode 100644 index b9df1c0..0000000 --- a/pkg/http/handlers/error.go +++ /dev/null @@ -1,46 +0,0 @@ -package handlers - -import ( - "net/http" - - ut "github.com/go-playground/universal-translator" - "github.com/go-playground/validator/v10" - "github.com/gofiber/fiber/v2" - "github.com/rs/zerolog" -) - -func Error(logger zerolog.Logger, translator ut.Translator) fiber.ErrorHandler { - return func(ctx *fiber.Ctx, err error) error { - ctx.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8) - code := fiber.StatusInternalServerError - - logger.Error(). - Err(err). - Msg("An error has occurred in application") - - if e, ok := err.(*fiber.Error); ok { - return ctx.Status(e.Code).JSON(ErrorResponse{ - Message: e.Message, - }) - } - - if err == ErrInvalidPayload { - return ctx.Status(http.StatusBadRequest).JSON(ErrorResponse{ - Message: ErrInvalidPayload.Error(), - }) - } - - if _, ok := err.(*validator.InvalidValidationError); ok { - return ctx.Status(fiber.StatusUnprocessableEntity). - JSON(ErrorResponse{Message: "Data is invalid"}) - } - - if err, ok := err.(validator.ValidationErrors); ok { - return ctx.Status(fiber.StatusUnprocessableEntity). - JSON(err.Translate(translator)) - } - - return ctx.Status(code). - JSON(ErrorResponse{Message: "An error has occurred!"}) - } -} diff --git a/pkg/http/handlers/error_test.go b/pkg/http/handlers/error_test.go deleted file mode 100644 index a975191..0000000 --- a/pkg/http/handlers/error_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package handlers_test - -import ( - "encoding/json" - "errors" - "net/http" - "net/http/httptest" - "testing" - - "github.com/go-playground/validator/v10" - "github.com/gofiber/fiber/v2" - "github.com/rs/zerolog" - "github.com/rzajac/zltest" - "github.com/stretchr/testify/require" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http/handlers" - "github.com/BrosSquad/GoFiber-Boilerplate/testing_utils" -) - -func setupErrorHandlerApp(t *testing.T) (*fiber.App, *validator.Validate, *zltest.Tester) { - v, translations := testing_utils.GetValidator() - - logger, loggerTest := testing_utils.NewTestLogger(t, zerolog.InfoLevel) - - app := fiber.New(fiber.Config{ - ErrorHandler: handlers.Error(logger, translations), - }) - - return app, v, loggerTest -} - -func TestErrorHandler_ReturnFiberError(t *testing.T) { - t.Parallel() - assert := require.New(t) - - app, _, _ := setupErrorHandlerApp(t) - - app.Get("/", func(ctx *fiber.Ctx) error { - return fiber.ErrBadGateway - }) - - m := struct { - Message string `json:"message"` - }{} - res := testing_utils.Get(app, "/") - - assert.EqualValues(fiber.StatusBadGateway, res.StatusCode) - assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) - assert.Nil(json.NewDecoder(res.Body).Decode(&m)) - assert.NotEmpty(m.Message) -} - -func TestErrorHandler_InvalidPayloadError(t *testing.T) { - t.Parallel() - assert := require.New(t) - - app, _, _ := setupErrorHandlerApp(t) - app.Get("/", func(ctx *fiber.Ctx) error { - return handlers.ErrInvalidPayload - }) - - res := testing_utils.Get(app, "/") - - m := struct { - Message string `json:"message"` - }{} - - assert.EqualValues(fiber.StatusBadRequest, res.StatusCode) - assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) - assert.Nil(json.NewDecoder(res.Body).Decode(&m)) - assert.NotEmpty(m.Message) - assert.Equal(handlers.ErrInvalidPayload.Error(), m.Message) -} - -func TestErrorHandler_ValidationError(t *testing.T) { - t.Parallel() - assert := require.New(t) - app, _, _ := setupErrorHandlerApp(t) - app.Get("/", func(ctx *fiber.Ctx) error { - return validator.ValidationErrors{} - }) - res, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - assert.Nil(err) - assert.EqualValues(fiber.StatusUnprocessableEntity, res.StatusCode) - assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) -} - -func TestErrorHandler_InvalidValidationError(t *testing.T) { - t.Parallel() - assert := require.New(t) - - app, _, _ := setupErrorHandlerApp(t) - app.Get("/", func(ctx *fiber.Ctx) error { - return &validator.InvalidValidationError{} - }) - res, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - assert.Nil(err) - assert.EqualValues(fiber.StatusUnprocessableEntity, res.StatusCode) - assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) -} - -func TestErrorHandler_AnyError(t *testing.T) { - t.Parallel() - assert := require.New(t) - app, _, _ := setupErrorHandlerApp(t) - app.Get("/", func(ctx *fiber.Ctx) error { - return errors.New("any other error") - }) - res, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - assert.Nil(err) - assert.EqualValues(fiber.StatusInternalServerError, res.StatusCode) - assert.EqualValues(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) -} diff --git a/pkg/http/handlers/handlers_test.go b/pkg/http/handlers/handlers_test.go deleted file mode 100644 index 8f1ea64..0000000 --- a/pkg/http/handlers/handlers_test.go +++ /dev/null @@ -1,42 +0,0 @@ -package handlers_test - -import ( - "encoding/json" - "net/http" - "testing" - - "github.com/gofiber/fiber/v2" - "github.com/stretchr/testify/require" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http/handlers" - "github.com/BrosSquad/GoFiber-Boilerplate/testing_utils" -) - -func setupNotFoundApplication() *fiber.App { - app := fiber.New() - - app.Use(handlers.NotFound()) - - return app -} - -func TestNotFound_JsonResponse(t *testing.T) { - t.Parallel() - assert := require.New(t) - - app := setupNotFoundApplication() - - res := testing_utils.Get(app, "/", testing_utils.WithHeaders(http.Header{ - fiber.HeaderAccept: []string{fiber.MIMEApplicationJSONCharsetUTF8}, - })) - - assert.Equal(http.StatusNotFound, res.StatusCode) - assert.Equal(fiber.MIMEApplicationJSON, res.Header.Get(fiber.HeaderContentType)) - - var errRes handlers.ErrorResponse - _ = json.NewDecoder(res.Body).Decode(&errRes) - - assert.EqualValues(handlers.ErrorResponse{ - Message: "Page is not found", - }, errRes) -} diff --git a/pkg/http/handlers/hello_world/hello_world_test.go b/pkg/http/handlers/hello_world/hello_world_test.go deleted file mode 100644 index 5abc478..0000000 --- a/pkg/http/handlers/hello_world/hello_world_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package hello_world_test - -import ( - "net/http" - "testing" - - "github.com/rs/zerolog" - "github.com/stretchr/testify/require" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http/handlers/hello_world" - "github.com/BrosSquad/GoFiber-Boilerplate/testing_utils" -) - -func TestHelloWorldHandler(t *testing.T) { - t.Parallel() - assert := require.New(t) - - app, _ := testing_utils.CreateApplication() - _, loggerAssert := testing_utils.NewTestLogger(t, zerolog.InfoLevel) - - app.Get("/", hello_world.HelloWorld(loggerAssert.Logger())) - - res := testing_utils.Get(app, "/") - - assert.Equal(http.StatusOK, res.StatusCode) -} diff --git a/pkg/http/http.go b/pkg/http/http.go deleted file mode 100644 index 1226163..0000000 --- a/pkg/http/http.go +++ /dev/null @@ -1,119 +0,0 @@ -package http - -import ( - "context" - "fmt" - "net" - - "github.com/goccy/go-json" - "github.com/minio/simdjson-go" - "github.com/rs/zerolog/log" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" - "github.com/gofiber/fiber/v2/middleware/monitor" - "github.com/gofiber/fiber/v2/middleware/pprof" - "github.com/gofiber/fiber/v2/middleware/recover" - "github.com/gofiber/fiber/v2/middleware/requestid" - fiber_utils "github.com/gofiber/fiber/v2/utils" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/config" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/constants" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/container" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http/middleware" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/utils" -) - -func CreateApplication(ctx context.Context, c *container.Container, appName string, environment config.Env, displayInfo, enableMonitor bool, errorHandler fiber.ErrorHandler) *fiber.App { - var ( - jsonEncoder fiber_utils.JSONMarshal = json.Marshal - jsonDecoder fiber_utils.JSONUnmarshal - ) - - if simdjson.SupportedCPU() { - jsonDecoder = func(data []byte, v interface{}) error { - parsed, err := simdjson.Parse(data, nil) - if err != nil { - return err - } - - parsed.Iter() - return nil - } - } else { - log.Warn(). - Str("app", appName). - Str("env", string(environment)). - Msg("simdjson is not supported on this CPU, application performance might suffer") - - jsonDecoder = json.Unmarshal - } - - staticConfig := fiber.Config{ - StrictRouting: true, - EnablePrintRoutes: displayInfo, - Prefork: false, - DisableStartupMessage: !displayInfo, - DisableDefaultDate: true, - DisableHeaderNormalizing: false, - DisablePreParseMultipartForm: true, - AppName: appName, - ErrorHandler: errorHandler, - JSONEncoder: jsonEncoder, - JSONDecoder: jsonDecoder, - } - - app := fiber.New(staticConfig) - - switch environment { - case config.Development: - app.Use(pprof.New()) - case config.Production: - app.Use(recover.New()) - } - - app.Use(middleware.Context(ctx)) - app.Use(requestid.New(requestid.Config{ - Generator: func() string { - return utils.RandomString(32) - }, - ContextKey: constants.RequestIdKey, - })) - - if environment == config.Development { - app.Use(logger.New(logger.Config{ - TimeZone: "UTC", - })) - } - - if enableMonitor { - app.Get("/monitor", monitor.New(monitor.Config{ - Title: constants.AppName + " Monitor", - })) - } - - registerHandlers(app, c, environment) - - return app -} - -func RunServer(ip string, port int, app *fiber.App) { - addr := fmt.Sprintf("%s:%d", ip, port) - - listener, err := net.Listen("tcp4", addr) - if err != nil { - log. - Fatal(). - Err(err). - Msg("Error while creating net.Listener for HTTP Server") - } - - err = app.Listener(listener) - - if err != nil { - log. - Fatal(). - Err(err). - Msg("Cannot start Fiber HTTP Server") - } -} diff --git a/pkg/http/middleware/middleware.go b/pkg/http/middleware/middleware.go deleted file mode 100644 index c870d7c..0000000 --- a/pkg/http/middleware/middleware.go +++ /dev/null @@ -1 +0,0 @@ -package middleware diff --git a/pkg/logging/logging.go b/pkg/logging/logging.go deleted file mode 100644 index 8e82d9a..0000000 --- a/pkg/logging/logging.go +++ /dev/null @@ -1,59 +0,0 @@ -package logging - -import ( - "io" - "os" - "time" - - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" -) - -const DateTimeFormat = "2006-01-02 15:04:05" - -func ConfigureDefaultLogger(level string, prettyPrint bool) { - zerologLevel, err := zerolog.ParseLevel(level) - if err != nil { - panic("Failed to parse logging level: " + level) - } - - zerolog.SetGlobalLevel(zerologLevel) - zerolog.TimeFieldFormat = DateTimeFormat - zerolog.DurationFieldUnit = time.Microsecond - zerolog.TimestampFunc = time.Now().UTC - - var w io.Writer - - if prettyPrint { - w = zerolog.NewConsoleWriter() - } else { - w = os.Stdout - } - - log.Logger = log.Output(w) -} - -func New(level string, prettyPrint bool) zerolog.Logger { - var logger zerolog.Logger - - zerologLevel, err := zerolog.ParseLevel(level) - if err != nil { - panic("Failed to parse logging level: " + level) - } - - var w io.Writer - - if prettyPrint { - w = zerolog.NewConsoleWriter() - } else { - w = os.Stdout - } - - logger = zerolog.New(w). - With(). - Timestamp(). - Logger(). - Level(zerologLevel) - - return logger -} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go deleted file mode 100644 index 5262d9b..0000000 --- a/pkg/utils/utils.go +++ /dev/null @@ -1,182 +0,0 @@ -package utils - -import ( - "crypto/rand" - "encoding/base64" - "io/fs" - "net" - "net/http" - "os" - "path/filepath" - "reflect" - "unicode" - "unsafe" -) - -// #nosec G103 -// UnsafeBytes returns a byte pointer without allocation -func UnsafeBytes(s string) []byte { - var bs []byte - - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) - bh.Data = sh.Data - bh.Len = sh.Len - bh.Cap = sh.Len - - return bs -} - -// #nosec G103 -// UnsafeString returns a string pointer without allocation -func UnsafeString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - -func IsSuccess(status int) bool { - return status >= http.StatusOK && status < http.StatusMultipleChoices -} - -func IsInt(s string) bool { - if len(s) > 0 && s[0] == '0' { - return false - } - - for _, c := range s { - if !unicode.IsDigit(c) { - return false - } - } - - return true -} - -func Getenv(env string, def ...string) string { - item, exists := os.LookupEnv(env) - - if !exists && len(def) > 0 { - return def[0] - } - - return item -} - -func RandomString(n int32) string { - buffer := make([]byte, n) - - _, err := rand.Read(buffer) - if err != nil { - return "" - } - - return base64.RawURLEncoding.EncodeToString(buffer) -} - -func GetAbsolutePath(path string) (string, error) { - var err error - - if !filepath.IsAbs(path) { - path, err = filepath.Abs(path) - - if err != nil { - return "", err - } - - return path, nil - } - - return path, err -} - -func CreateDirectoryFromFile(path string, perm fs.FileMode) (string, error) { - p, err := GetAbsolutePath(path) - if err != nil { - return "", err - } - - directory := filepath.Dir(p) - - if err := os.MkdirAll(directory, perm); err != nil { - return "", err - } - - return p, nil -} - -func CreateFile(path string, flags int, dirMode, mode fs.FileMode) (file *os.File, err error) { - path, err = CreateDirectoryFromFile(path, dirMode|fs.ModeDir) - - if err != nil { - return nil, err - } - - if _, err = os.Stat(path); err != nil { - if !os.IsNotExist(err) { - return nil, err - } - - file, err = os.Create(path) - - if err != nil { - return nil, err - } - - if err := file.Chmod(mode); err != nil { - return nil, err - } - - if err := file.Close(); err != nil { - return nil, err - } - } - - file, err = os.OpenFile(path, flags, mode) - - return -} - -func CreateLogFile(path string) (file *os.File, err error) { - file, err = CreateFile(path, os.O_WRONLY|os.O_APPEND, 0o744, fs.FileMode(0o744)|os.ModeAppend) - - return -} - -func FileExists(path string) bool { - _, err := os.Stat(path) - if err != nil { - return false - } - - return true -} - -func CreateDirectory(path string, perm fs.FileMode) (string, error) { - p, err := GetAbsolutePath(path) - if err != nil { - return "", err - } - - if err := os.MkdirAll(p, perm); err != nil { - return "", err - } - - return p, nil -} - -func GetLocalIP() string { - addrs, err := net.InterfaceAddrs() - if err != nil { - return "" - } - - for _, address := range addrs { - ipnet, ok := address.(*net.IPNet) - - // check the address type and if it is not a loopback the display it - if ok && !ipnet.IP.IsLoopback() && ipnet.IP.To4() != nil { - return ipnet.IP.String() - } - } - - return "" -} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go deleted file mode 100644 index ba77d6a..0000000 --- a/pkg/utils/utils_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package utils - -import ( - "crypto/rand" - "encoding/base64" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" -) - -func TestCreateLogFile(t *testing.T) { - t.Parallel() - assert := require.New(t) - - path := "./test-logs/log.json" - - defer os.RemoveAll("./test-logs") - - file, err := CreateLogFile(path) - - assert.NoError(err) - assert.NotNil(file) - assert.FileExists(path) -} - -func TestFileExists(t *testing.T) { - t.Parallel() - assert := require.New(t) - - t.Run("FileExists", func(t *testing.T) { - file, err := os.Create("./test-exists.txt") - - assert.NoError(err) - - defer os.Remove("./test-exists.txt") - defer file.Close() - - assert.True(FileExists("./test-exists.txt")) - }) - - t.Run("File_Does_Not_Exists", func(t *testing.T) { - assert.False(FileExists("./test-does-not-exists.txt")) - }) -} - -func TestCreateDirectory(t *testing.T) { - t.Parallel() - assert := require.New(t) - - defer os.RemoveAll("./test-dir") - - path, err := CreateDirectory("./test-dir", 0o744) - - assert.NoError(err) - abs, _ := filepath.Abs("./test-dir") - - assert.Equal(abs, path) -} - -func TestRandomString(t *testing.T) { - t.Parallel() - - l := 32 - - str := RandomString(int32(l)) - - if base64.RawURLEncoding.EncodedLen(l) != len(str) { - t.Fatalf("Expected length: %d Given %d", l, len(str)) - } -} - -func BenchmarkRandomString(b *testing.B) { - for i := 0; i < b.N; i++ { - RandomString(32) - } -} - -func random(b *testing.B) string { - buffer := make([]byte, 32) - - n, err := rand.Read(buffer) - if err != nil { - b.Errorf("error while generating random buffer %v", err) - return "" - } - - if n != 32 { - b.Errorf("expected length 32, given %d", n) - return "" - } - - return base64.RawURLEncoding.EncodeToString(buffer) -} - -func BenchmarkCryptoRandomString(b *testing.B) { - for i := 0; i < b.N; i++ { - random(b) - } -} - -func TestIsSuccess(t *testing.T) { - t.Parallel() - assert := require.New(t) - - type Data struct { - Value int - Expected bool - } - - data := [400]Data{} - - for i := 0; i < 400; i++ { - data[i] = Data{ - Value: 100 + i, - Expected: 100+i >= 200 && 100+i < 300, - } - } - - for _, item := range data { - assert.Equal(item.Expected, IsSuccess(item.Value)) - } -} - -func TestUnsafeBytes(t *testing.T) { - t.Parallel() - assert := require.New(t) - - bytes := []byte("Hello World") - - unsafeBytes := UnsafeBytes("Hello World") - - assert.EqualValues(bytes, unsafeBytes) -} - -func TestUnsafeString(t *testing.T) { - t.Parallel() - assert := require.New(t) - - bytes := []byte("Hello World") - - str := UnsafeString(bytes) - - assert.EqualValues("Hello World", str) -} - -func TestGetenv(t *testing.T) { - t.Parallel() - assert := require.New(t) - - t.Run("DefaultValue", func(t *testing.T) { - value := Getenv("HELLO_ENV") - assert.Empty(value) - - value = Getenv("HELLO_ENV", "some_default_value") - - assert.NotEmpty(value) - assert.Equal("some_default_value", value) - }) - - t.Run("WithEnvSet", func(t *testing.T) { - os.Setenv("HELLO_ENV", "value") - - value := Getenv("HELLO_ENV") - assert.NotEmpty(value) - assert.Equal("value", value) - - value = Getenv("HELLO_ENV", "hello_world") - assert.NotEmpty(value) - assert.Equal("value", value) - }) -} - -func TestIsInt(t *testing.T) { - t.Parallel() - assert := require.New(t) - - t.Run("IsInt", func(t *testing.T) { - assert.True(IsInt("23445555")) - }) - - t.Run("NotAnInt", func(t *testing.T) { - assert.False(IsInt("fjkhadskjdfhasjd")) - }) - - t.Run("CannotStartWith0", func(t *testing.T) { - assert.False(IsInt("023355")) - }) -} diff --git a/testing_utils/log.go b/testing_utils/log.go deleted file mode 100644 index c9958dc..0000000 --- a/testing_utils/log.go +++ /dev/null @@ -1,17 +0,0 @@ -package testing_utils - -import ( - "testing" - - "github.com/rs/zerolog" - "github.com/rzajac/zltest" -) - -func NewTestLogger(t *testing.T, level zerolog.Level) (zerolog.Logger, *zltest.Tester) { - tst := zltest.New(t) - testWriter := zerolog.NewTestWriter(t) - - logger := zerolog.New(zerolog.MultiLevelWriter(tst, testWriter)).Level(level) - - return logger, tst -} diff --git a/testing_utils/requests.go b/testing_utils/requests.go deleted file mode 100644 index 1e47735..0000000 --- a/testing_utils/requests.go +++ /dev/null @@ -1,156 +0,0 @@ -package testing_utils - -import ( - "bytes" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - - "github.com/gofiber/fiber/v2" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/constants" -) - -func getBody[T any](headers http.Header, body T) io.Reader { - switch headers.Get(fiber.HeaderContentType) { - case fiber.MIMEApplicationJSON, fiber.MIMEApplicationJSONCharsetUTF8: - jsonStr, err := json.Marshal(body) - if err != nil { - panic(err) - } - - return bytes.NewReader(jsonStr) - default: - return nil - } -} - -type RequestModifier func(*http.Request) *http.Request - -func WithHeaders(headers http.Header) RequestModifier { - return func(req *http.Request) *http.Request { - if headers.Get(fiber.HeaderContentType) == "" { - headers.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8) - } - - if headers.Get(fiber.HeaderAccept) == "" { - headers.Set(fiber.HeaderAccept, fiber.MIMEApplicationJSONCharsetUTF8) - } - - if headers.Get(fiber.HeaderUserAgent) == "" { - headers.Set(fiber.HeaderUserAgent, constants.TestUserAgent) - } - - req.Header = headers - - return req - } -} - -func WithBody[T any](body T) RequestModifier { - return func(req *http.Request) *http.Request { - newReq := httptest.NewRequest(req.Method, req.URL.Path, getBody(req.Header, body)) - newReq.Header = req.Header - newReq.URL.RawQuery = req.URL.Query().Encode() - - for _, cookie := range req.Cookies() { - newReq.AddCookie(cookie) - } - - return newReq - } -} - -func WithCookies(cookies []*http.Cookie) RequestModifier { - return func(req *http.Request) *http.Request { - for _, cookie := range cookies { - req.AddCookie(cookie) - } - - return req - } -} - -func MakeRequest[T any](method, uri string, modifiers ...RequestModifier) *http.Request { - var defaults []func(*http.Request) *http.Request - - switch method { - case http.MethodPost, http.MethodPut, http.MethodPatch: - defaults = []func(*http.Request) *http.Request{ - WithHeaders(http.Header{}), - WithBody[any](nil), - } - default: - defaults = []func(*http.Request) *http.Request{ - WithHeaders(http.Header{}), - } - } - - req := httptest.NewRequest(method, uri, nil) - - for _, modifier := range defaults { - req = modifier(req) - } - - for _, modifier := range modifiers { - req = modifier(req) - } - - return req -} - -func Get(app *fiber.App, uri string, modifiers ...RequestModifier) *http.Response { - req := MakeRequest[any](http.MethodGet, uri, modifiers...) - - res, err := app.Test(req, -1) - if err != nil { - panic("Cannot get response") - } - - return res -} - -func Post[T any](app *fiber.App, uri string, modifiers ...RequestModifier) *http.Response { - req := MakeRequest[T](http.MethodPost, uri, modifiers...) - - res, err := app.Test(req, -1) - if err != nil { - panic("Cannot get response") - } - - return res -} - -func Put[T any](app *fiber.App, uri string, modifiers ...RequestModifier) *http.Response { - req := MakeRequest[T](http.MethodPut, uri, modifiers...) - - res, err := app.Test(req, -1) - if err != nil { - panic("Cannot get response") - } - - return res -} - -func Patch[T any](app *fiber.App, uri string, modifiers ...RequestModifier) *http.Response { - req := MakeRequest[T](http.MethodPatch, uri, modifiers...) - - res, err := app.Test(req, -1) - if err != nil { - panic("Cannot get response") - } - - return res -} - -func Delete(app *fiber.App, uri string, modifiers ...RequestModifier) *http.Response { - req := MakeRequest[any](http.MethodDelete, uri, modifiers...) - - res, err := app.Test(req, -1) - if err != nil { - panic("Cannot get response") - } - - return res -} diff --git a/testing_utils/testing.go b/testing_utils/testing.go deleted file mode 100644 index 32e9656..0000000 --- a/testing_utils/testing.go +++ /dev/null @@ -1,74 +0,0 @@ -package testing_utils - -import ( - "context" - "errors" - "os" - "path/filepath" - - "github.com/go-playground/locales/en" - ut "github.com/go-playground/universal-translator" - "github.com/go-playground/validator/v10" - "github.com/gofiber/fiber/v2" - "github.com/spf13/viper" - - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/config" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/constants" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/container" - httpapp "github.com/BrosSquad/GoFiber-Boilerplate/pkg/http" - "github.com/BrosSquad/GoFiber-Boilerplate/pkg/utils" -) - -func GetValidator() (*validator.Validate, ut.Translator) { - v := validator.New() - english := en.New() - uni := ut.New(english, english) - englishTranslations, _ := uni.GetTranslator("en") - - return v, englishTranslations -} - -func CreateApplication() (*fiber.App, *container.Container) { - wd, err := os.Getwd() - if err != nil { - panic(err) - } - - configPath, err := findConfig(wd) - if err != nil { - panic(err) - } - - cfg := viper.New() - - cfg.SetConfigName("config") - cfg.AddConfigPath(configPath) - cfg.SetConfigType("yaml") - - if err := cfg.ReadInConfig(); err != nil { - panic(err) - } - - c := container.New(cfg, false, "info", config.Testing) - return httpapp.CreateApplication(context.Background(), c, constants.AppName, config.Testing, false, false, nil), c -} - -func findConfig(workingDir string) (string, error) { - for entries, err := os.ReadDir(workingDir); err == nil; { - for _, entry := range entries { - if !entry.IsDir() && entry.Name() == "config.yml" { - return workingDir, nil - } - } - - workingDir, err = utils.GetAbsolutePath(filepath.Join(workingDir, "..")) - - if err != nil { - return "", err - } - - entries, err = os.ReadDir(workingDir) - } - - return "", errors.New("config file not found") -}