diff --git a/.devcontainer/.env b/.devcontainer/.env index ed61c9e..28d56a2 100644 --- a/.devcontainer/.env +++ b/.devcontainer/.env @@ -4,14 +4,12 @@ COCKROACH_HOST=crdb:26257 COCKROACH_URL="postgresql://root@crdb:26257/location_api_dev?sslmode=disable" # location-api config -LOCATIONAPI_CRDB_URI="postgresql://root@crdb:26257/location_api_dev?sslmode=disable" ATLAS_DB_URI="postgresql://root@crdb:26257/atlas_migrations?sslmode=disable" +LOCATIONAPI_CRDB_URI="postgresql://root@crdb:26257/location_api_dev?sslmode=disable" +LOCATIONAPI_EVENTS_PUBLISHER_NATS_CREDSFILE="/workspaces/location-api/.devcontainer/nsc/nkeys/creds/LOCAL/LOC/USER.creds" -NATS_URL="nats://nats:4222" -NATS_CREDS="/tmp/user.creds" - -NKEYS_PATH="/workspace/.devcontainer/nsc/nkeys" -NSC_HOME="/workspace/.devcontainer/nsc/nats" +NKEYS_PATH="/workspaces/location-api/.devcontainer/nsc/nkeys" +NSC_HOME="/workspaces/location-api/.devcontainer/nsc/nats" # postgresql client config PGHOST=crdb diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 70b7684..26c307c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -22,9 +22,16 @@ RUN export DEBIAN_FRONTEND=noninteractive \ uuid-runtime \ postgresql-client +# Install NATS Tooling +RUN curl -o /tmp/install.sh https://raw.githubusercontent.com/nats-io/nsc/main/install.sh \ + && chmod +x /tmp/install.sh \ + && /tmp/install.sh -d /usr/local/bin -s - \ + && rm -f /tmp/install.sh USER vscode - -RUN go install ariga.io/atlas/cmd/atlas@latest +ENV NATS_CLI_VERSION=0.0.35 +RUN go install -v github.com/cweill/gotests/gotests@v1.6.0 \ + && go install github.com/nats-io/natscli/nats@v${NATS_CLI_VERSION} \ + && go install github.com/nats-io/nkeys/nk@latest USER root diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ac76f8b..03e8dc0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,6 +12,7 @@ "editor.defaultFormatter": "golang.go" }, "go.buildTags": "testtools", + "go.formatTool": "goimports", "go.lintTool": "golangci-lint", "gopls": { "formatting.gofumpt": true, diff --git a/.devcontainer/nats/nats-server.conf b/.devcontainer/nats/nats-server.conf new file mode 100644 index 0000000..40a7b51 --- /dev/null +++ b/.devcontainer/nats/nats-server.conf @@ -0,0 +1,32 @@ +server_name: nats + + # Client port of 4222 on all interfaces + port: 4222 + + # HTTP monitoring port + monitor_port: 8222 + + # # This is for clustering multiple servers together. + # cluster { + # name: "cluster1" + # listen: 0.0.0.0:6222 + # routes = [nats://127.0.0.1:6222] + # cluster_advertise: nats-server:6222 + # connect_retries: 0 + # } + + jetstream: enabled + jetstream { + store_dir: /data/jetstream + max_mem: 10M + max_file: 1G + } + + debug: true + logtime: true + + max_payload: 4MB + lame_duck_grace_period: 10s + lame_duck_duration: 30s + + include "resolver.conf" diff --git a/.devcontainer/scripts/app-entrypoint.sh b/.devcontainer/scripts/app-entrypoint.sh new file mode 100755 index 0000000..3a52cd4 --- /dev/null +++ b/.devcontainer/scripts/app-entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -e + +.devcontainer/scripts/nats_account.sh + +sleep infinity diff --git a/.devcontainer/scripts/nats_init.sh b/.devcontainer/scripts/nats_init.sh new file mode 100755 index 0000000..b5a0c5f --- /dev/null +++ b/.devcontainer/scripts/nats_init.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +set -e + +# script to bootstrap a nats operator environment + +if nsc describe operator; then + echo "operator exists, not overwriting config" + exit 0 +fi + +echo "Cleaning up NATS environment" +rm -rf /nsc/* + +echo "Creating NATS operator" +nsc add operator --generate-signing-key --sys --name LOCAL +nsc edit operator -u 'nats://nats:4222' +nsc list operators +nsc describe operator + +export OPERATOR_SIGNING_KEY_ID=`nsc describe operator -J | jq -r '.nats.signing_keys | first'` + +echo "Creating NATS account for location-api" +nsc add account -n LOC -K ${OPERATOR_SIGNING_KEY_ID} +nsc edit account LOC --sk generate --js-mem-storage -1 --js-disk-storage -1 --js-streams -1 --js-consumer -1 +nsc describe account LOC + +export ACCOUNTS_SIGNING_KEY_ID=`nsc describe account LOC -J | jq -r '.nats.signing_keys | first'` + +echo "Creating NATS user for location-api" +nsc add user -n USER -K ${ACCOUNTS_SIGNING_KEY_ID} +nsc describe user USER + +echo "Generating NATS resolver.conf" +nsc generate config --mem-resolver --sys-account SYS --config-file /nats/resolver.conf --force diff --git a/.github/workflows/test-go.yml b/.github/workflows/test-go.yml index 0865e2c..d98fa64 100644 --- a/.github/workflows/test-go.yml +++ b/.github/workflows/test-go.yml @@ -50,4 +50,4 @@ jobs: run: go install ariga.io/atlas/cmd/atlas@latest - name: Run go tests for ${{ matrix.ci-database }} - run: METADATAAPI_TESTDB_URI="${{ matrix.env-database-uri }}" go test -race -coverprofile=coverage.txt -covermode=atomic -tags testtools ./... + run: LOCATIONAPI_TESTDB_URI="${{ matrix.env-database-uri }}" go test -race -coverprofile=coverage.txt -covermode=atomic -tags testtools ./... diff --git a/.gitignore b/.gitignore index 326a346..a3f89cc 100644 --- a/.gitignore +++ b/.gitignore @@ -15,9 +15,14 @@ *.out # Dependency directories (remove the comment below to include it) -# vendor/ +vendor/ # Go workspace file go.work -location-api \ No newline at end of file +# binary +location-api + +# devcontainer artifacts +.devcontainer/nats/resolver.conf +.devcontainer/nsc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..950f351 --- /dev/null +++ b/Makefile @@ -0,0 +1,92 @@ +all: lint tests binary ## Runs lint, tests, and builds the binary + +PHONY: help all test coverage lint golint clean vendor docker-up docker-down unit-test +GOOS=linux +DB=location_api +DEV_DB=${DB}_dev +TEST_DB=${DB}_test +DEV_URI="postgresql://root@crdb:26257/${DEV_DB}?sslmode=disable" +TEST_URI="postgresql://root@crdb:26257/${TEST_DB}?sslmode=disable" + +APP_NAME=location-api +PID_FILE=/tmp/loc.pid + +help: Makefile ## Print help + @grep -h "##" $(MAKEFILE_LIST) | grep -v grep | sed -e 's/:.*##/#/' | column -c 2 -t -s# + +tests: | unit-tests + +unit-tests: ## Runs unit tests + @echo --- Running unit tests... + @date --rfc-3339=seconds + @go test -race -cover -failfast -tags testtools -p 1 -v ./... + +coverage: ## Generates coverage report + @echo --- Generating coverage report... + @date --rfc-3339=seconds + @go test -race -coverprofile=coverage.out -covermode=atomic -tags testtools -p 1 ./... + @go tool cover -func=coverage.out + @go tool cover -html=coverage.out + +lint: golint ## Runs linting + +golint: + @echo --- Running golint... + @date --rfc-3339=seconds + @golangci-lint run + +clean: ## Clean up all the things + @echo --- Cleaning... + @date --rfc-3339=seconds + @rm -rf ./bin/ + @rm -rf coverage.out + @go clean -testcache + +binary: | vendor generate ## Builds the binary + @echo --- Building binary... + @date --rfc-3339=seconds + @go build -o bin/${APP_NAME} main.go + +vendor: ## Vendors dependencies + @echo --- Downloading dependencies... + @date --rfc-3339=seconds + @go mod tidy + @go mod download + +testclient:| background-run .testclient kill-running ## Regenerates the test client in graphclient + +.testclient: + @echo --- Generating test graph client... + @date --rfc-3339=seconds + @go generate ./internal/graphclient + +dev-nats: ## Initializes nats + @echo --- Initializing nats + @date --rfc-3339=seconds + @.devcontainer/scripts/nats_account.sh + +generate: background-run .generate kill-running ## Generates code + +.generate: + @echo --- Generating code... + @date --rfc-3339=seconds + @go generate ./... + +go-run: ## Runs the app + @echo --- Running binary... + @date --rfc-3339=seconds + @go run main.go serve --dev + +background-run: ## Runs in the app in the background + @date --rfc-3339=seconds + @if [ ! -f "${PID_FILE}" ]; then \ + echo --- Running binary in the background...; \ + go run main.go serve --dev --oidc=false --pid-file=${PID_FILE} & \ + else \ + echo --- Binary already running in the background...; \ + fi + +kill-running: ## Kills the running binary from pid file + @echo --- Killing background binary... + @date --rfc-3339=seconds + @kill $$(cat ${PID_FILE}) diff --git a/cmd/serve.go b/cmd/serve.go index 0d4282d..e7b4680 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -2,12 +2,17 @@ package cmd import ( "context" + "os" + "strconv" + "syscall" "entgo.io/ent/dialect" entsql "entgo.io/ent/dialect/sql" + "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" "github.com/spf13/cobra" "github.com/spf13/viper" + "go.infratographer.com/permissions-api/pkg/permissions" "go.infratographer.com/x/crdbx" "go.infratographer.com/x/echojwtx" "go.infratographer.com/x/echox" @@ -22,18 +27,28 @@ import ( "go.infratographer.com/location-api/internal/graphapi" ) -var defaultListenAddr = ":7906" +var defaultListenAddr = ":7909" var ( enablePlayground bool serveDevMode bool + pidFileName = "" ) var serveCmd = &cobra.Command{ Use: "serve", Short: "Start the location API", - Run: func(cmd *cobra.Command, args []string) { - serve(cmd.Context()) + RunE: func(cmd *cobra.Command, args []string) error { + if pidFileName != "" { + if err := writePidFile(pidFileName); err != nil { + logger.Error("failed to write pid file", zap.Error(err)) + return err + } + + defer os.Remove(pidFileName) + } + + return serve(cmd.Context()) }, } @@ -43,13 +58,15 @@ func init() { echox.MustViperFlags(viper.GetViper(), serveCmd.Flags(), defaultListenAddr) echojwtx.MustViperFlags(viper.GetViper(), serveCmd.Flags()) events.MustViperFlagsForPublisher(viper.GetViper(), serveCmd.Flags(), appName) + permissions.MustViperFlags(viper.GetViper(), serveCmd.Flags()) // only available as a CLI arg because it shouldn't be something that could accidentially end up in a config file or env var serveCmd.Flags().BoolVar(&serveDevMode, "dev", false, "dev mode: enables playground, disables all auth checks, sets CORS to allow all, pretty logging, etc.") serveCmd.Flags().BoolVar(&enablePlayground, "playground", false, "enable the graph playground") + serveCmd.Flags().StringVar(&pidFileName, "pid-file", "", "path to the pid file") } -func serve(ctx context.Context) { +func serve(ctx context.Context) error { if serveDevMode { enablePlayground = true config.AppConfig.Logging.Debug = true @@ -90,15 +107,18 @@ func serve(ctx context.Context) { } client := ent.NewClient(cOpts...) + defer client.Close() + var middleware []echo.MiddlewareFunc + + // jwt auth middleware if viper.GetBool("oidc.enabled") { auth, err := echojwtx.NewAuth(ctx, config.AppConfig.OIDC) if err != nil { logger.Fatal("failed to initialize jwt authentication", zap.Error(err)) } - auth.JWTConfig.Skipper = echox.SkipDefaultEndpoints - config.AppConfig.Server = config.AppConfig.Server.WithMiddleware(auth.Middleware()) + middleware = append(middleware, auth.Middleware()) } srv, err := echox.NewServer(logger.Desugar(), config.AppConfig.Server, versionx.BuildDetails()) @@ -106,15 +126,51 @@ func serve(ctx context.Context) { logger.Fatal("failed to initialize new server", zap.Error(err)) } + perms, err := permissions.New(config.AppConfig.Permissions, + permissions.WithLogger(logger), + permissions.WithDefaultChecker(permissions.DefaultAllowChecker), + ) + if err != nil { + logger.Fatal("failed to initialize permissions", zap.Error(err)) + } + + middleware = append(middleware, perms.Middleware()) + r := graphapi.NewResolver(client, logger.Named("resolvers")) - handler := r.Handler(enablePlayground, nil) + handler := r.Handler(enablePlayground, middleware...) srv.AddHandler(handler) // TODO: we should have a database check // srv.AddReadinessCheck("database", r.DatabaseCheck) - if err := srv.Run(); err != nil { + if err = srv.RunWithContext(ctx); err != nil { logger.Fatal("failed to run server", zap.Error(err)) } + + return err +} + +// Write a pid file, but first make sure it doesn't exist with a running pid. +func writePidFile(pidFile string) error { + // Read in the pid file as a slice of bytes. + if piddata, err := os.ReadFile(pidFile); err == nil { + // Convert the file contents to an integer. + if pid, err := strconv.Atoi(string(piddata)); err == nil { + // Look for the pid in the process list. + if process, err := os.FindProcess(pid); err == nil { + // Send the process a signal zero kill. + if err := process.Signal(syscall.Signal(0)); err == nil { + // We only get an error if the pid isn't running, or it's not ours. + return err + } + } + } + } + + logger.Debugw("writing pid file", "pid-file", pidFile) + + // If we get here, then the pidfile didn't exist, + // or the pid in it doesn't belong to the user running this app. + return os.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())), 0o664) // nolint: gomnd } diff --git a/go.mod b/go.mod index 83400e1..e724090 100644 --- a/go.mod +++ b/go.mod @@ -7,20 +7,29 @@ require ( entgo.io/ent v0.12.3 github.com/99designs/gqlgen v0.17.32-0.20230521032519-c313bf3d2899 github.com/Yamashou/gqlgenc v0.13.5 + github.com/brianvoe/gofakeit/v6 v6.22.0 github.com/hashicorp/go-multierror v1.1.1 github.com/labstack/echo/v4 v4.10.2 + github.com/lib/pq v1.10.9 + github.com/mattn/go-sqlite3 v1.14.16 github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.16.0 + github.com/stretchr/testify v1.8.4 + github.com/testcontainers/testcontainers-go/modules/postgres v0.20.1 github.com/vektah/gqlparser/v2 v2.5.2-0.20230422221642-25e09f9d292d github.com/wundergraph/graphql-go-tools v1.63.0 - go.infratographer.com/x v0.3.0 + go.infratographer.com/permissions-api v0.1.14 + go.infratographer.com/x v0.3.2 go.uber.org/zap v1.24.0 ) require ( ariga.io/atlas v0.10.2-0.20230427182402-87a07dfb83bf // indirect + filippo.io/edwards25519 v1.0.0 // indirect + github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/MicahParks/keyfunc/v2 v2.0.3 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ThreeDotsLabs/watermill v1.2.0 // indirect github.com/ThreeDotsLabs/watermill-nats/v2 v2.0.0 // indirect github.com/XSAM/otelsql v0.23.0 // indirect @@ -31,12 +40,21 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cockroachdb/cockroach-go/v2 v2.3.3 // indirect + github.com/containerd/containerd v1.6.19 // indirect + github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v23.0.6+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/garsue/watermillzap v1.2.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/inflect v0.19.0 // indirect github.com/gofrs/uuid v4.3.0+incompatible // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang-jwt/jwt/v5 v5.0.0 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -48,6 +66,7 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.1 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl/v2 v2.13.0 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.14.0 // indirect @@ -60,43 +79,56 @@ require ( github.com/jaevor/go-nanoid v1.3.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.16.5 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect + github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/echo-contrib v0.15.0 // indirect github.com/labstack/echo-jwt/v4 v4.2.0 // indirect github.com/labstack/gommon v0.4.0 // indirect - github.com/lib/pq v1.10.9 // indirect github.com/lithammer/shortuuid/v3 v3.0.7 // 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.18 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/moby/patternmatcher v0.5.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/nats-io/nats.go v1.26.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/nats-io/jwt/v2 v2.4.1 // indirect + github.com/nats-io/nats-server/v2 v2.9.17 // indirect + github.com/nats-io/nats.go v1.27.1 // indirect github.com/nats-io/nkeys v0.4.4 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc3 // indirect + github.com/opencontainers/runc v1.1.7 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pressly/goose/v3 v3.11.2 // indirect github.com/prometheus/client_golang v1.14.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.40.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.4.2 // indirect + github.com/testcontainers/testcontainers-go v0.20.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/vmihailenco/msgpack/v5 v5.0.0-beta.9 // indirect github.com/vmihailenco/tagparser v0.1.2 // indirect github.com/zclconf/go-cty v1.8.0 // indirect go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.42.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 // indirect go.opentelemetry.io/otel v1.16.0 // indirect go.opentelemetry.io/otel/exporters/jaeger v1.16.0 // indirect go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.16.0 // indirect @@ -108,19 +140,23 @@ require ( go.opentelemetry.io/otel/sdk v1.16.0 // indirect go.opentelemetry.io/otel/trace v1.16.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect + go.step.sm/crypto v0.31.2 // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/crypto v0.9.0 // indirect golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/mod v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sys v0.8.0 // indirect golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.8.1-0.20230428195545-5283a0178901 // indirect + google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect - google.golang.org/grpc v1.55.0 // indirect + google.golang.org/grpc v1.56.1 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8ad74da..3fdf09f 100644 --- a/go.sum +++ b/go.sum @@ -42,14 +42,21 @@ entgo.io/contrib v0.4.5 h1:BFaOHwFLE8WZjVJadP0XHCIaxgcC1BAtUvAyw7M/GHk= entgo.io/contrib v0.4.5/go.mod h1:wpZyq2DJgthugFvDBlaqMXj9mV4/9ebyGEn7xlTVQqE= entgo.io/ent v0.12.3 h1:N5lO2EOrHpCH5HYfiMOCHYbo+oh5M8GjT0/cx5x6xkk= entgo.io/ent v0.12.3/go.mod h1:AigGGx+tbrBBYHAzGOg8ND661E5cxx1Uiu5o/otJ6Yg= +filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= +filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= github.com/99designs/gqlgen v0.17.32-0.20230521032519-c313bf3d2899 h1:hpIR0DjkME2c1Wia6aT+wH6Fuqbod09uePvUBdA2scE= github.com/99designs/gqlgen v0.17.32-0.20230521032519-c313bf3d2899/go.mod h1:5j5Ak84e9FTYtH3aaNhK+FoYzXdUAY9CahQcWDqOwR8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 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/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/MicahParks/keyfunc/v2 v2.0.3 h1:uKUEOc+knRO0UoucONisgNPiT85V2s/W5c0FQYsd9kc= github.com/MicahParks/keyfunc/v2 v2.0.3/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4OeE/yHVMteCkw9k= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.9.7 h1:mKNHW/Xvv1aFH87Jb6ERDzXTJTLPlmzfZ28VBFD/bfg= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ThreeDotsLabs/watermill v1.2.0 h1:TU3TML1dnQ/ifK09F2+4JQk2EKhmhXe7Qv7eb5ZpTS8= github.com/ThreeDotsLabs/watermill v1.2.0/go.mod h1:IuVxGk/kgCN0cex2S94BLglUiB0PwOm8hbUhm6g2Nx4= @@ -71,10 +78,11 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6 github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/brianvoe/gofakeit/v6 v6.21.0 h1:tNkm9yxEbpuPK8Bx39tT4sSc5i9SUGiciLdNix+VDQY= +github.com/brianvoe/gofakeit/v6 v6.22.0 h1:BzOsDot1o3cufTfOk+fWKE9nFYojyDV+XHdCWL2+uyE= +github.com/brianvoe/gofakeit/v6 v6.22.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -98,15 +106,29 @@ github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/cockroachdb/cockroach-go/v2 v2.3.3 h1:fNmtG6XhoA1DhdDCIu66YyGSsNb1szj4CaAsbDxRmy4= github.com/cockroachdb/cockroach-go/v2 v2.3.3/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= +github.com/containerd/containerd v1.6.19 h1:F0qgQPrG0P2JPgwpxWxYavrVeXAG0ezUIB9Z/4FTUAU= +github.com/containerd/containerd v1.6.19/go.mod h1:HZCDMn4v/Xl2579/MvtOC2M206i+JJ6VxFWU/NetrGY= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= +github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 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/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v23.0.6+incompatible h1:aBD4np894vatVX99UTx/GyOUOK4uEcROwA3+bQhEcoU= +github.com/docker/docker v23.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 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= @@ -116,6 +138,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -140,6 +164,8 @@ github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= @@ -233,6 +259,8 @@ github.com/hashicorp/hcl/v2 v2.13.0 h1:0Apadu1w6M11dyGFxWnmhhcMjkbAiKCv7G1r/2QgC github.com/hashicorp/hcl/v2 v2.13.0/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= 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/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -292,6 +320,7 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm 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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= @@ -305,7 +334,8 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU= github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4= github.com/labstack/echo-jwt/v4 v4.2.0 h1:odSISV9JgcSCuhgQSV/6Io3i7nUmfM/QkBeR5GVJj5c= @@ -337,30 +367,48 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/ github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= 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/moby/patternmatcher v0.5.0 h1:YCZgJOeULcxLw1Q+sVR636pmS7sPEn1Qo2iAN6M7DBo= +github.com/moby/patternmatcher v0.5.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/nats-io/jwt/v2 v2.4.1 h1:Y35W1dgbbz2SQUYDPCaclXcuqleVmpbRa7646Jf2EX4= +github.com/nats-io/jwt/v2 v2.4.1/go.mod h1:24BeQtRwxRV8ruvC4CojXlx/WQ/VjuwlYiH+vu/+ibI= github.com/nats-io/nats-server/v2 v2.9.17 h1:gFpUQ3hqIDJrnqog+Bl5vaXg+RhhYEZIElasEuRn2tw= -github.com/nats-io/nats.go v1.26.0 h1:fWJTYPnZ8DzxIaqIHOAMfColuznchnd5Ab5dbJpgPIE= -github.com/nats-io/nats.go v1.26.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= +github.com/nats-io/nats-server/v2 v2.9.17/go.mod h1:eQysm3xDZmIjfkjr7DuD9DjRFpnxQc2vKVxtEg0Dp6s= +github.com/nats-io/nats.go v1.27.1 h1:OuYnal9aKVSnOzLQIzf7554OXMCG7KbaTkCSBHRcSoo= +github.com/nats-io/nats.go v1.27.1/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= +github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= +github.com/opencontainers/runc v1.1.7/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -398,6 +446,9 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= @@ -428,8 +479,13 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/testcontainers/testcontainers-go v0.20.1 h1:mK15UPJ8c5P+NsQKmkqzs/jMdJt6JMs5vlw2y4j92c0= +github.com/testcontainers/testcontainers-go v0.20.1/go.mod h1:zb+NOlCQBkZ7RQp4QI+YMIHyO2CQ/qsXzNF5eLJ24SY= +github.com/testcontainers/testcontainers-go/modules/postgres v0.20.1 h1:PkAq2/sxchYxLiepcshIUnMzmhlecakGOCTtKEuZCA0= +github.com/testcontainers/testcontainers-go/modules/postgres v0.20.1/go.mod h1:c9mDiyvz7se25wEvvkx/8ok1YIIsQE9ACItnim7C0xw= 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/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -453,8 +509,10 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA= github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.infratographer.com/x v0.3.0 h1:bvYxdbCBGSKASVG0Rl9sTi2LzrFwuJMBPkTu1wycyH4= -go.infratographer.com/x v0.3.0/go.mod h1:w+uknSNnOrJlHiOdDPV08e5EpU47SwWXZ/vOVWBovPA= +go.infratographer.com/permissions-api v0.1.14 h1:I2VEH02LKd8zbnp+aMSucr8Ci4rhffbdN+/5QZpk7j0= +go.infratographer.com/permissions-api v0.1.14/go.mod h1:aFTC2ZzPEyQpqPp6zTw90lH2tlKSyj9M6cms9JhFQg4= +go.infratographer.com/x v0.3.2 h1:AxHY77AGhWcRNcO7ENP/4Cj0xg6KGKpxFn/yZn+rPOs= +go.infratographer.com/x v0.3.2/go.mod h1:GvOhGwi/1Dp5qAQSudHUdLfFmiXzzc27KBfkH0nxnEQ= 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= @@ -463,6 +521,8 @@ 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.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.42.0 h1:sYefIhrd/A3fO8rmr0vy2tgCLoR8CsbMqwbcUa70x00= go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho v0.42.0/go.mod h1:5Ll2ndRzg9UNUrj1n+v4ZCcrD/SYy7BnVrlCQXECowA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0 h1:pginetY7+onl4qN1vl0xW/V/v6OBZ0vVdH+esuJgvmM= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.42.0/go.mod h1:XiYsayHc36K3EByOO6nbAXnAWbrUxdjUROCEeeROOH8= go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= @@ -488,6 +548,8 @@ go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLk go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.step.sm/crypto v0.31.2 h1:GJX4A15zXxxcbuS++g2SvETTitAUClGIfg5QnKlscDs= +go.step.sm/crypto v0.31.2/go.mod h1:gFQ/XlQIIiFRfZrXglqKbrX9bgC1HmsASErev9sZN4A= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -609,6 +671,7 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -623,6 +686,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -665,10 +729,12 @@ 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-20210616094352-59db8d763f22/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-20211103235746-7861aae1554b/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-20220715151400-c0bba94af5f8/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -680,6 +746,7 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR 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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols= 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= @@ -739,6 +806,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -747,6 +815,7 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f 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= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= @@ -845,8 +914,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.55.0 h1:3Oj82/tFSCeUrRTg/5E/7d/W5A1tj6Ky1ABAuZuv5ag= -google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= +google.golang.org/grpc v1.56.1 h1:z0dNfjIl0VpaZ9iSVjA6daGatAYwPGstTjt5vkRMFkQ= +google.golang.org/grpc v1.56.1/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -871,6 +940,7 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:a 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/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= +gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -880,6 +950,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/config/config.go b/internal/config/config.go index 4acabe4..f55c7c7 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,6 +2,7 @@ package config import ( + "go.infratographer.com/permissions-api/pkg/permissions" "go.infratographer.com/x/crdbx" "go.infratographer.com/x/echojwtx" "go.infratographer.com/x/echox" @@ -12,12 +13,13 @@ import ( // AppConfig contains the application configuration structure. var AppConfig struct { - OIDC echojwtx.AuthConfig - CRDB crdbx.Config - Logging loggingx.Config - Events EventsConfig - Server echox.Config - Tracing otelx.Config + CRDB crdbx.Config + Logging loggingx.Config + Events EventsConfig + OIDC echojwtx.AuthConfig + Permissions permissions.Config + Server echox.Config + Tracing otelx.Config } // EventsConfig stores the configuration for a location-api event publisher diff --git a/internal/ent/generated/client.go b/internal/ent/generated/client.go index eddabdb..eee052b 100644 --- a/internal/ent/generated/client.go +++ b/internal/ent/generated/client.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/ent.go b/internal/ent/generated/ent.go index 5cb1e8d..f47c065 100644 --- a/internal/ent/generated/ent.go +++ b/internal/ent/generated/ent.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/enttest/enttest.go b/internal/ent/generated/enttest/enttest.go index 224f3a4..2f70039 100644 --- a/internal/ent/generated/enttest/enttest.go +++ b/internal/ent/generated/enttest/enttest.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/eventhooks/hooks.go b/internal/ent/generated/eventhooks/hooks.go index 4af5701..a1651dc 100644 --- a/internal/ent/generated/eventhooks/hooks.go +++ b/internal/ent/generated/eventhooks/hooks.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/gql_collection.go b/internal/ent/generated/gql_collection.go index 12cbb50..0d75643 100644 --- a/internal/ent/generated/gql_collection.go +++ b/internal/ent/generated/gql_collection.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/gql_edge.go b/internal/ent/generated/gql_edge.go index 90459e2..830bd85 100644 --- a/internal/ent/generated/gql_edge.go +++ b/internal/ent/generated/gql_edge.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/gql_mutation_input.go b/internal/ent/generated/gql_mutation_input.go index fcbf04c..ac30f20 100644 --- a/internal/ent/generated/gql_mutation_input.go +++ b/internal/ent/generated/gql_mutation_input.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/gql_node.go b/internal/ent/generated/gql_node.go index 60ad62d..268ff95 100644 --- a/internal/ent/generated/gql_node.go +++ b/internal/ent/generated/gql_node.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/gql_pagination.go b/internal/ent/generated/gql_pagination.go index 3760342..2f8b80f 100644 --- a/internal/ent/generated/gql_pagination.go +++ b/internal/ent/generated/gql_pagination.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/gql_transaction.go b/internal/ent/generated/gql_transaction.go index 437d3e6..64c8df8 100644 --- a/internal/ent/generated/gql_transaction.go +++ b/internal/ent/generated/gql_transaction.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/gql_where_input.go b/internal/ent/generated/gql_where_input.go index af0eb3a..0508df4 100644 --- a/internal/ent/generated/gql_where_input.go +++ b/internal/ent/generated/gql_where_input.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/hook/hook.go b/internal/ent/generated/hook/hook.go index 4b0089e..8784b3e 100644 --- a/internal/ent/generated/hook/hook.go +++ b/internal/ent/generated/hook/hook.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/intercept/intercept.go b/internal/ent/generated/intercept/intercept.go index bd4d26f..577c8a1 100644 --- a/internal/ent/generated/intercept/intercept.go +++ b/internal/ent/generated/intercept/intercept.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/interfaces.go b/internal/ent/generated/interfaces.go index d16af05..e084974 100644 --- a/internal/ent/generated/interfaces.go +++ b/internal/ent/generated/interfaces.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/location.go b/internal/ent/generated/location.go index 7df0b1f..905ef86 100644 --- a/internal/ent/generated/location.go +++ b/internal/ent/generated/location.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/location/location.go b/internal/ent/generated/location/location.go index 878149d..961f05f 100644 --- a/internal/ent/generated/location/location.go +++ b/internal/ent/generated/location/location.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/location/where.go b/internal/ent/generated/location/where.go index bfc1fd5..f527c2b 100644 --- a/internal/ent/generated/location/where.go +++ b/internal/ent/generated/location/where.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/location_create.go b/internal/ent/generated/location_create.go index f07af77..6ef2c9b 100644 --- a/internal/ent/generated/location_create.go +++ b/internal/ent/generated/location_create.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/location_delete.go b/internal/ent/generated/location_delete.go index 1b28ac5..e13be85 100644 --- a/internal/ent/generated/location_delete.go +++ b/internal/ent/generated/location_delete.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/location_query.go b/internal/ent/generated/location_query.go index e9bf729..c076c99 100644 --- a/internal/ent/generated/location_query.go +++ b/internal/ent/generated/location_query.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/location_update.go b/internal/ent/generated/location_update.go index 69df5a3..dd840f7 100644 --- a/internal/ent/generated/location_update.go +++ b/internal/ent/generated/location_update.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/migrate/migrate.go b/internal/ent/generated/migrate/migrate.go index 955dc5f..e84f885 100644 --- a/internal/ent/generated/migrate/migrate.go +++ b/internal/ent/generated/migrate/migrate.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/migrate/schema.go b/internal/ent/generated/migrate/schema.go index 03a43c4..130b10f 100644 --- a/internal/ent/generated/migrate/schema.go +++ b/internal/ent/generated/migrate/schema.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/mutation.go b/internal/ent/generated/mutation.go index 4cbee89..5640b98 100644 --- a/internal/ent/generated/mutation.go +++ b/internal/ent/generated/mutation.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/predicate/predicate.go b/internal/ent/generated/predicate/predicate.go index 98fb261..4f3631c 100644 --- a/internal/ent/generated/predicate/predicate.go +++ b/internal/ent/generated/predicate/predicate.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/runtime.go b/internal/ent/generated/runtime.go index f09473b..3ab0df2 100644 --- a/internal/ent/generated/runtime.go +++ b/internal/ent/generated/runtime.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/runtime/runtime.go b/internal/ent/generated/runtime/runtime.go index 2c91b85..2fd4c54 100644 --- a/internal/ent/generated/runtime/runtime.go +++ b/internal/ent/generated/runtime/runtime.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/ent/generated/tx.go b/internal/ent/generated/tx.go index b68888c..4b56dca 100644 --- a/internal/ent/generated/tx.go +++ b/internal/ent/generated/tx.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff --git a/internal/graphapi/auth_test.go b/internal/graphapi/auth_test.go new file mode 100644 index 0000000..05d3be1 --- /dev/null +++ b/internal/graphapi/auth_test.go @@ -0,0 +1,84 @@ +package graphapi_test + +import ( + "context" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.infratographer.com/permissions-api/pkg/permissions" + "go.infratographer.com/x/echojwtx" + "go.infratographer.com/x/gidx" + "go.infratographer.com/x/testing/auth" +) + +func TestGetLocationWithAuth(t *testing.T) { + oauthCLI, issuer, oAuthClose := auth.OAuthTestClient("urn:test:location", "") + defer oAuthClose() + + srv, err := newTestServer( + withAuthConfig( + &echojwtx.AuthConfig{ + Issuer: issuer, + }, + ), + withPermissions( + permissions.WithDefaultChecker(permissions.DefaultAllowChecker), + ), + ) + + require.NoError(t, err) + require.NotNil(t, srv) + + defer srv.Close() + + ctx := context.Background() + loc1 := (&LocationBuilder{ + OwnerID: gidx.MustNewID("testown"), + }).MustNew(ctx) + + resp, err := graphTestClient( + withHTTPClient(oauthCLI), + withSrvURL(srv.URL+"/query"), + ).GetLocationByID(ctx, loc1.ID) + + require.NoError(t, err) + require.NotNil(t, resp) + assert.Equal(t, loc1.ID, resp.Location.ID) +} + +func TestGetLoadbalancerNoAuth(t *testing.T) { + _, issuer, oAuthClose := auth.OAuthTestClient("urn:test:loadbalancer", "") + defer oAuthClose() + + srv, err := newTestServer( + withAuthConfig( + &echojwtx.AuthConfig{ + Issuer: issuer, + }, + ), + withPermissions( + permissions.WithDefaultChecker(permissions.DefaultAllowChecker), + ), + ) + + require.NoError(t, err) + require.NotNil(t, srv) + + defer srv.Close() + + ctx := context.Background() + loc1 := (&LocationBuilder{ + OwnerID: gidx.MustNewID("testown"), + }).MustNew(ctx) + + resp, err := graphTestClient( + withHTTPClient(http.DefaultClient), + withSrvURL(srv.URL+"/query"), + ).GetLocationByID(ctx, loc1.ID) + + require.Error(t, err, "Expected an authorization error") + require.Nil(t, resp) + assert.ErrorContains(t, err, `{"networkErrors":{"code":401`) +} diff --git a/internal/graphapi/entity.resolvers.go b/internal/graphapi/entity.resolvers.go index c1fae58..da22405 100644 --- a/internal/graphapi/entity.resolvers.go +++ b/internal/graphapi/entity.resolvers.go @@ -7,9 +7,8 @@ package graphapi import ( "context" - "go.infratographer.com/x/gidx" - "go.infratographer.com/location-api/internal/ent/generated" + "go.infratographer.com/x/gidx" ) // FindLocationByID is the resolver for the findLocationByID field. diff --git a/internal/graphapi/gen_server.go b/internal/graphapi/gen_server.go index f9b1c72..b10111d 100644 --- a/internal/graphapi/gen_server.go +++ b/internal/graphapi/gen_server.go @@ -761,7 +761,6 @@ extend type Location { owner: ResourceOwner! } `, BuiltIn: false}, - {Name: "../../schema/owner.graphql", Input: ``, BuiltIn: false}, {Name: "../../federation/directives.graphql", Input: ` directive @composeDirective(name: String!) repeatable on SCHEMA directive @extends on OBJECT | INTERFACE diff --git a/internal/graphapi/location.resolvers.go b/internal/graphapi/location.resolvers.go index c0dc614..267ff0b 100644 --- a/internal/graphapi/location.resolvers.go +++ b/internal/graphapi/location.resolvers.go @@ -10,6 +10,7 @@ import ( "entgo.io/contrib/entgql" "go.infratographer.com/location-api/internal/ent/generated" "go.infratographer.com/location-api/internal/ent/generated/location" + "go.infratographer.com/permissions-api/pkg/permissions" "go.infratographer.com/x/gidx" ) @@ -20,7 +21,10 @@ func (r *locationResolver) Owner(ctx context.Context, obj *generated.Location) ( // LocationCreate is the resolver for the locationCreate field. func (r *mutationResolver) LocationCreate(ctx context.Context, input generated.CreateLocationInput) (*LocationCreatePayload, error) { - // TODO: auth check + if err := permissions.CheckAccess(ctx, input.OwnerID, actionLocationCreate); err != nil { + return nil, err + } + loc, err := r.client.Location.Create().SetInput(input).Save(ctx) if err != nil { return nil, err @@ -31,8 +35,12 @@ func (r *mutationResolver) LocationCreate(ctx context.Context, input generated.C // LocationDelete is the resolver for the locationDelete field. func (r *mutationResolver) LocationDelete(ctx context.Context, id gidx.PrefixedID) (*LocationDeletePayload, error) { - // TODO: auth check // TODO: check metadata for references to this location + + if err := permissions.CheckAccess(ctx, id, actionLocationDelete); err != nil { + return nil, err + } + if err := r.client.Location.DeleteOneID(id).Exec(ctx); err != nil { return nil, err } @@ -42,7 +50,10 @@ func (r *mutationResolver) LocationDelete(ctx context.Context, id gidx.PrefixedI // LocationUpdate is the resolver for the locationUpdate field. func (r *mutationResolver) LocationUpdate(ctx context.Context, id gidx.PrefixedID, input generated.UpdateLocationInput) (*LocationUpdatePayload, error) { - // TODO: auth check + if err := permissions.CheckAccess(ctx, id, actionLocationUpdate); err != nil { + return nil, err + } + loc, err := r.client.Location.UpdateOneID(id).SetInput(input).Save(ctx) if err != nil { return nil, err @@ -53,12 +64,19 @@ func (r *mutationResolver) LocationUpdate(ctx context.Context, id gidx.PrefixedI // Location is the resolver for the location field. func (r *queryResolver) Location(ctx context.Context, id gidx.PrefixedID) (*generated.Location, error) { - // TODO: auth check + if err := permissions.CheckAccess(ctx, id, actionLocationGet); err != nil { + return nil, err + } + return r.client.Location.Get(ctx, id) } // Locations is the resolver for the locations field. func (r *resourceOwnerResolver) Locations(ctx context.Context, obj *ResourceOwner, after *entgql.Cursor[gidx.PrefixedID], first *int, before *entgql.Cursor[gidx.PrefixedID], last *int, orderBy *generated.LocationOrder, where *generated.LocationWhereInput) (*generated.LocationConnection, error) { + if err := permissions.CheckAccess(ctx, obj.ID, actionLocationGet); err != nil { + return nil, err + } + return r.client.Location.Query().Where(location.OwnerID(obj.ID)).Paginate(ctx, after, first, before, last, generated.WithLocationOrder(orderBy), generated.WithLocationFilter(where.Filter)) } @@ -68,7 +86,5 @@ func (r *Resolver) Mutation() MutationResolver { return &mutationResolver{r} } // ResourceOwner returns ResourceOwnerResolver implementation. func (r *Resolver) ResourceOwner() ResourceOwnerResolver { return &resourceOwnerResolver{r} } -type ( - mutationResolver struct{ *Resolver } - resourceOwnerResolver struct{ *Resolver } -) +type mutationResolver struct{ *Resolver } +type resourceOwnerResolver struct{ *Resolver } diff --git a/internal/graphapi/location_test.go b/internal/graphapi/location_test.go new file mode 100644 index 0000000..ef9b3da --- /dev/null +++ b/internal/graphapi/location_test.go @@ -0,0 +1,61 @@ +package graphapi_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.infratographer.com/permissions-api/pkg/permissions" + "go.infratographer.com/x/gidx" + + "go.infratographer.com/location-api/internal/ent/generated" +) + +func TestQuery_IPAddress(t *testing.T) { + client := graphTestClient() + ctx := context.Background() + + // Permit request + ctx = context.WithValue(ctx, permissions.CheckerCtxKey, permissions.DefaultAllowChecker) + + loc1 := (&LocationBuilder{}).MustNew(ctx) + + testCases := []struct { + name string + queryID gidx.PrefixedID + expected *generated.Location + errorMsg string + }{ + { + name: "happy path location", + queryID: loc1.ID, + expected: loc1, + }, + { + name: "invalid-id", + queryID: gidx.MustNewID("testing"), + errorMsg: "location not found", + }, + } + + for _, tc := range testCases { + t.Run("Get "+tc.name, func(t *testing.T) { + resp, err := client.GetLocationByID(ctx, tc.queryID) + + if tc.errorMsg != "" { + require.Error(t, err) + assert.ErrorContains(t, err, tc.errorMsg) + assert.Nil(t, resp) + + return + } + + require.NoError(t, err) + require.NotNil(t, resp) + require.NotNil(t, resp.Location) + }) + } +} + +// TODO: test crud operations diff --git a/internal/graphapi/models_test.go b/internal/graphapi/models_test.go new file mode 100644 index 0000000..8a4a850 --- /dev/null +++ b/internal/graphapi/models_test.go @@ -0,0 +1,33 @@ +package graphapi_test + +import ( + "context" + + "github.com/brianvoe/gofakeit/v6" + "go.infratographer.com/x/gidx" + + ent "go.infratographer.com/location-api/internal/ent/generated" +) + +type LocationBuilder struct { + Name string + Description *string + OwnerID gidx.PrefixedID +} + +func (l *LocationBuilder) MustNew(ctx context.Context) *ent.Location { + if l.Name == "" { + l.Name = gofakeit.AppName() + } + + if l.Description == nil { + desc := gofakeit.HipsterSentence(10) + l.Description = &desc + } + + if l.OwnerID == "" { + l.OwnerID = gidx.MustNewID(ownerPrefix) + } + + return EntClient.Location.Create().SetName(l.Name).SetDescription(*l.Description).SetOwnerID(l.OwnerID).SaveX(ctx) +} diff --git a/internal/graphapi/permissions.go b/internal/graphapi/permissions.go new file mode 100644 index 0000000..4485a58 --- /dev/null +++ b/internal/graphapi/permissions.go @@ -0,0 +1,8 @@ +package graphapi + +const ( + actionLocationCreate = "location_create" + actionLocationUpdate = "location_update" + actionLocationDelete = "location_delete" + actionLocationGet = "location_get" +) diff --git a/internal/graphapi/resolver.go b/internal/graphapi/resolver.go index b838ea0..de2f660 100644 --- a/internal/graphapi/resolver.go +++ b/internal/graphapi/resolver.go @@ -18,7 +18,6 @@ import ( // // It serves as dependency injection for your app, add any dependencies you require here. - var ( graphPath = "query" playgroundPath = "playground" @@ -49,7 +48,7 @@ type Handler struct { } // Handler returns an http handler for a graph resolver -func (r *Resolver) Handler(withPlayground bool, middleware []echo.MiddlewareFunc) *Handler { +func (r *Resolver) Handler(withPlayground bool, middleware ...echo.MiddlewareFunc) *Handler { srv := handler.NewDefaultServer( NewExecutableSchema( Config{ @@ -62,8 +61,8 @@ func (r *Resolver) Handler(withPlayground bool, middleware []echo.MiddlewareFunc h := &Handler{ r: r, - middleware: middleware, graphqlHandler: srv, + middleware: middleware, } if withPlayground { @@ -85,6 +84,7 @@ func (h *Handler) Handler() http.HandlerFunc { // Routes ... func (h *Handler) Routes(e *echo.Group) { e.Use(h.middleware...) + e.POST(graphFullPath, h.graphRequest) if h.playground != nil { @@ -110,4 +110,4 @@ func (h *Handler) Routes(e *echo.Group) { func (h *Handler) graphRequest(ctx echo.Context) error { h.graphqlHandler.ServeHTTP(ctx.Response(), ctx.Request()) return nil -} \ No newline at end of file +} diff --git a/internal/graphapi/testdata/postgres_init.sh b/internal/graphapi/testdata/postgres_init.sh new file mode 100755 index 0000000..81ec23a --- /dev/null +++ b/internal/graphapi/testdata/postgres_init.sh @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + create extension btree_gin; +EOSQL diff --git a/internal/graphapi/tools_test.go b/internal/graphapi/tools_test.go new file mode 100644 index 0000000..dd0d1a4 --- /dev/null +++ b/internal/graphapi/tools_test.go @@ -0,0 +1,248 @@ +package graphapi_test + +import ( + "context" + "log" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + + "entgo.io/ent/dialect" + "github.com/99designs/gqlgen/graphql/handler" + "github.com/labstack/echo/v4" + _ "github.com/lib/pq" + _ "github.com/mattn/go-sqlite3" + "github.com/testcontainers/testcontainers-go/modules/postgres" + "go.uber.org/zap" + + "go.infratographer.com/permissions-api/pkg/permissions" + "go.infratographer.com/x/echojwtx" + "go.infratographer.com/x/echox" + "go.infratographer.com/x/events" + "go.infratographer.com/x/goosex" + "go.infratographer.com/x/testing/eventtools" + + "go.infratographer.com/x/testing/containersx" + + "go.infratographer.com/location-api/db" + ent "go.infratographer.com/location-api/internal/ent/generated" + "go.infratographer.com/location-api/internal/graphapi" + "go.infratographer.com/location-api/internal/testclient" +) + +const ( + locationPrefix = "testloc" + ownerPrefix = "testtnt" + nodePrefix = "testnod" +) + +var ( + TestDBURI = os.Getenv("LOCATIONAPI_TESTDB_URI") + EntClient *ent.Client + DBContainer *containersx.DBContainer +) + +func TestMain(m *testing.M) { + // setup the database if needed + setupDB() + // run the tests + code := m.Run() + // teardown the database + teardownDB() + // return the test response code + os.Exit(code) +} + +func parseDBURI(ctx context.Context) (string, string, *containersx.DBContainer) { + switch { + // if you don't pass in a database we default to an in memory sqlite + case TestDBURI == "": + return dialect.SQLite, "file:ent?mode=memory&cache=shared&_fk=1", nil + case strings.HasPrefix(TestDBURI, "sqlite://"): + return dialect.SQLite, strings.TrimPrefix(TestDBURI, "sqlite://"), nil + case strings.HasPrefix(TestDBURI, "postgres://"), strings.HasPrefix(TestDBURI, "postgresql://"): + return dialect.Postgres, TestDBURI, nil + case strings.HasPrefix(TestDBURI, "docker://"): + dbImage := strings.TrimPrefix(TestDBURI, "docker://") + + switch { + case strings.HasPrefix(dbImage, "cockroach"), strings.HasPrefix(dbImage, "cockroachdb"), strings.HasPrefix(dbImage, "crdb"): + cntr, err := containersx.NewCockroachDB(ctx, dbImage) + errPanic("error starting db test container", err) + + return dialect.Postgres, cntr.URI, cntr + case strings.HasPrefix(dbImage, "postgres"): + cntr, err := containersx.NewPostgresDB(ctx, dbImage, + postgres.WithInitScripts(filepath.Join("testdata", "postgres_init.sh")), + ) + errPanic("error starting db test container", err) + + return dialect.Postgres, cntr.URI, cntr + default: + panic("invalid testcontainer URI, uri: " + TestDBURI) + } + + default: + panic("invalid DB URI, uri: " + TestDBURI) + } +} + +func setupDB() { + // don't setup the datastore if we already have one + if EntClient != nil { + return + } + + ctx := context.Background() + + dia, uri, cntr := parseDBURI(ctx) + + nats, err := eventtools.NewNatsServer() + if err != nil { + errPanic("failed to start nats server", err) + } + + pub, err := events.NewPublisher(nats.PublisherConfig) + if err != nil { + errPanic("failed to create events publisher", err) + } + + c, err := ent.Open(dia, uri, ent.Debug(), ent.EventsPublisher(pub)) + if err != nil { + errPanic("failed terminating test db container after failing to connect to the db", cntr.Container.Terminate(ctx)) + errPanic("failed opening connection to database:", err) + } + + switch dia { + case dialect.SQLite: + // Run automatic migrations for SQLite + errPanic("failed creating db scema", c.Schema.Create(ctx)) + case dialect.Postgres: + log.Println("Running database migrations") + goosex.MigrateUp(uri, db.Migrations) + } + + EntClient = c +} + +func teardownDB() { + ctx := context.Background() + + if EntClient != nil { + errPanic("teardown failed to close database connection", EntClient.Close()) + } + + if DBContainer != nil { + errPanic("teardown failed to terminate test db container", DBContainer.Container.Terminate(ctx)) + } +} + +func errPanic(msg string, err error) { + if err != nil { + log.Panicf("%s err: %s", msg, err.Error()) + } +} + +type graphClient struct { + srvURL string + httpClient *http.Client +} + +type graphClientOptions func(*graphClient) + +func withSrvURL(url string) graphClientOptions { + return func(g *graphClient) { + g.srvURL = url + } +} + +func withHTTPClient(httpcli *http.Client) graphClientOptions { + return func(g *graphClient) { + g.httpClient = httpcli + } +} + +func graphTestClient(options ...graphClientOptions) testclient.TestClient { + g := &graphClient{ + srvURL: "graph", + httpClient: &http.Client{Transport: localRoundTripper{handler: handler.NewDefaultServer( + graphapi.NewExecutableSchema( + graphapi.Config{Resolvers: graphapi.NewResolver(EntClient, zap.NewNop().Sugar())}, + ))}}, + } + + for _, opt := range options { + opt(g) + } + + return testclient.NewClient(g.httpClient, g.srvURL) +} + +// localRoundTripper is an http.RoundTripper that executes HTTP transactions +// by using handler directly, instead of going over an HTTP connection. +type localRoundTripper struct { + handler http.Handler +} + +func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + w := httptest.NewRecorder() + l.handler.ServeHTTP(w, req) + + return w.Result(), nil +} + +type testServerConfig struct { + echoConfig echox.Config + handlerMiddleware []echo.MiddlewareFunc +} + +type testServerOption func(*testServerConfig) error + +func withAuthConfig(authConfig *echojwtx.AuthConfig) testServerOption { + return func(tsc *testServerConfig) error { + auth, err := echojwtx.NewAuth(context.Background(), *authConfig) + if err != nil { + return err + } + + tsc.echoConfig = tsc.echoConfig.WithMiddleware(auth.Middleware()) + + return nil + } +} + +func withPermissions(options ...permissions.Option) testServerOption { + return func(tsc *testServerConfig) error { + perms, err := permissions.New(permissions.Config{}, options...) + if err != nil { + return err + } + + tsc.handlerMiddleware = append(tsc.handlerMiddleware, perms.Middleware()) + + return nil + } +} + +func newTestServer(options ...testServerOption) (*httptest.Server, error) { + tsc := new(testServerConfig) + + for _, opt := range options { + if err := opt(tsc); err != nil { + return nil, err + } + } + + srv, err := echox.NewServer(zap.NewNop(), tsc.echoConfig.WithMiddleware(tsc.handlerMiddleware...), nil) + if err != nil { + return nil, err + } + + r := graphapi.NewResolver(EntClient, zap.NewNop().Sugar()) + srv.AddHandler(r.Handler(false)) + + return httptest.NewServer(srv.Handler()), nil +}