From eed1cea48af530f0e1d86fc2a11598de271c2f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Mo=C5=84ko?= Date: Sun, 14 Jan 2024 22:08:35 +0100 Subject: [PATCH] bits and bolts --- cmd/config.go | 4 +- cmd/migrate.go | 8 +- cmd/provision.go | 6 +- cmd/root.go | 29 +---- cmd/serve.go | 12 +- go.mod | 50 +++++--- go.sum | 128 ++++++++++++--------- pkg/auth/provider.go | 16 +-- pkg/auth/provider_auth0.go | 29 ++--- pkg/auth/provider_local.go | 4 +- pkg/auth/provider_mock.go | 112 ++++++++++++++++++ pkg/{config/config.go => conf/conf.go} | 47 +++++++- pkg/dao/dao.go | 12 +- pkg/dao/dao_test.go | 151 +++++++++++++++++++++++++ pkg/dao/expense_extras.go | 4 +- pkg/graph/resolver.go | 4 +- pkg/graph/wallets.resolvers.go | 2 +- pkg/logz/logz.go | 40 ++++++- pkg/logz/logz_test.go | 32 +----- pkg/server/server.go | 8 +- 20 files changed, 517 insertions(+), 181 deletions(-) create mode 100644 pkg/auth/provider_mock.go rename pkg/{config/config.go => conf/conf.go} (50%) create mode 100644 pkg/dao/dao_test.go diff --git a/cmd/config.go b/cmd/config.go index f9b9e07..a3ccb4d 100644 --- a/cmd/config.go +++ b/cmd/config.go @@ -24,7 +24,7 @@ package cmd import ( "encoding/json" "fmt" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/spf13/cobra" ) @@ -34,7 +34,7 @@ var configCmd = &cobra.Command{ Aliases: []string{"conf"}, Short: "Config verifies configuration is complete", Run: func(cmd *cobra.Command, args []string) { - conf := config.New() + conf := conf.New() err := conf.Validate() cobra.CheckErr(err) diff --git a/cmd/migrate.go b/cmd/migrate.go index d9de9d6..606ab5e 100644 --- a/cmd/migrate.go +++ b/cmd/migrate.go @@ -25,7 +25,7 @@ import ( "errors" "github.com/golang-migrate/migrate/v4" "github.com/piotrekmonko/portfello/dbschema" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/spf13/cobra" "log" ) @@ -48,7 +48,7 @@ var upCmd = &cobra.Command{ Use: "up", Short: "Apply missing migrations", Run: func(cmd *cobra.Command, args []string) { - conf := config.New() + conf := conf.New() migrator, err := dbschema.NewMigrator(conf.DatabaseDSN) if err != nil { logFatalMigrate(err) @@ -66,7 +66,7 @@ var downCmd = &cobra.Command{ Use: "down", Short: "Revert one last migration", Run: func(cmd *cobra.Command, args []string) { - conf := config.New() + conf := conf.New() migrator, err := dbschema.NewMigrator(conf.DatabaseDSN) if err != nil { logFatalMigrate(err) @@ -84,7 +84,7 @@ var dropCmd = &cobra.Command{ Use: "drop", Short: "Drop database. WARNING: This will delete all data!", Run: func(cmd *cobra.Command, args []string) { - conf := config.New() + conf := conf.New() migrator, err := dbschema.NewMigrator(conf.DatabaseDSN) if err != nil { logFatalMigrate(err) diff --git a/cmd/provision.go b/cmd/provision.go index a333d5c..9ebbbf4 100644 --- a/cmd/provision.go +++ b/cmd/provision.go @@ -27,7 +27,7 @@ import ( "github.com/brianvoe/gofakeit/v6" "github.com/lithammer/shortuuid/v4" "github.com/piotrekmonko/portfello/pkg/auth" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/piotrekmonko/portfello/pkg/dao" "github.com/piotrekmonko/portfello/pkg/logz" "github.com/spf13/cobra" @@ -41,7 +41,7 @@ var provisionCmd = &cobra.Command{ Short: "Add objects to systems", Run: func(cmd *cobra.Command, args []string) { ctx := cmd.Context() - conf := config.New() + conf := conf.New() log := logz.NewLogger(&conf.Logging) db, dbq, err := dao.NewDAO(ctx, log, conf.DatabaseDSN) if err != nil { @@ -119,5 +119,5 @@ func provisionTestData(ctx context.Context, dbq *dao.DAO) error { } } - return q.Commit() + return q.Commit(ctx) } diff --git a/cmd/root.go b/cmd/root.go index ff4a494..6197bc6 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -23,12 +23,12 @@ package cmd import ( "fmt" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/piotrekmonko/portfello/pkg/logz" "go.uber.org/zap" "os" "github.com/spf13/cobra" - "github.com/spf13/viper" ) const baseVersion = "v1.0.0" @@ -82,30 +82,5 @@ func init() { // initConfig reads in config file and ENV variables if set. func initConfig() { - if cfgFile != "" { - // Use config file from the flag. - viper.SetConfigFile(cfgFile) - if err := viper.ReadInConfig(); err != nil { - cobra.CheckErr(fmt.Errorf("fatal error config file @ %s: %w", cfgFile, err)) - } - } - - // Find home directory. - home, err := os.UserHomeDir() - cobra.CheckErr(err) - - // Search config in home directory with name ".portfello" (without extension). - viper.AddConfigPath(home) - viper.AddConfigPath(".") - viper.SetConfigType("yaml") - - viper.AutomaticEnv() // read in environment variables that match - - // Loop through found config files until all are parsed - for _, configFile := range []string{".portfello", ".portfello-local"} { - viper.SetConfigName(configFile) - if err := viper.MergeInConfig(); err == nil { - _, _ = fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) - } - } + cobra.CheckErr(conf.InitConfig(cfgFile)) } diff --git a/cmd/serve.go b/cmd/serve.go index 362957a..507cec0 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -25,7 +25,7 @@ import ( "context" "errors" "github.com/piotrekmonko/portfello/pkg/auth" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/piotrekmonko/portfello/pkg/dao" "github.com/piotrekmonko/portfello/pkg/logz" "github.com/piotrekmonko/portfello/pkg/server" @@ -41,20 +41,20 @@ import ( var serveCmd = &cobra.Command{ Use: "serve", Short: "Start GraphQL server", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() - conf := config.New() + conf := conf.New() log := logz.NewLogger(&conf.Logging) db, dbQuerier, err := dao.NewDAO(ctx, log, conf.DatabaseDSN) if err != nil { - return + return err } defer db.Close() authProvider, err := auth.NewProvider(ctx, log, conf, dbQuerier) if err != nil { - return + return err } authService := auth.New(authProvider) @@ -73,6 +73,8 @@ var serveCmd = &cobra.Command{ defer closeCanc() cobra.CheckErr(httpSrv.Shutdown(closeCtx)) log.Infow(ctx, "Server stopped") + + return nil }, } diff --git a/go.mod b/go.mod index 974aff7..9573bc0 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/99designs/gqlgen v0.17.42 github.com/auth0/go-auth0 v1.4.0 github.com/auth0/go-jwt-middleware/v2 v2.2.0 - github.com/brianvoe/gofakeit/v6 v6.26.3 + github.com/brianvoe/gofakeit/v6 v6.26.4 github.com/eko/gocache/lib/v4 v4.1.5 github.com/eko/gocache/store/go_cache/v4 v4.2.1 github.com/go-acme/lego/v4 v4.14.2 @@ -17,7 +17,7 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 - github.com/sqlc-dev/sqlc v1.24.0 + github.com/sqlc-dev/sqlc v1.25.0 github.com/stretchr/testify v1.8.4 github.com/vektah/gqlparser/v2 v2.5.10 go.uber.org/zap v1.26.0 @@ -26,16 +26,15 @@ require ( require ( github.com/PuerkitoBio/rehttp v1.3.0 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect - github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytecodealliance/wasmtime-go/v14 v14.0.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/cubicdaiya/gonp v1.0.4 // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-jose/go-jose/v3 v3.0.1 // indirect @@ -51,23 +50,25 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 // indirect - github.com/jackc/pgx/v5 v5.5.1 // indirect + github.com/jackc/pgx/v5 v5.5.2 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/lib/pq v1.10.9 // indirect github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-sqlite3 v1.14.19 // indirect - github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect - github.com/pganalyze/pg_query_go/v4 v4.2.3 // indirect + github.com/pganalyze/pg_query_go/v4 v4.2.4-0.20231205012101-7463430c7b73 // indirect + github.com/pganalyze/pg_query_go/v5 v5.1.0 // indirect github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c // indirect github.com/pingcap/log v1.1.0 // indirect - github.com/pingcap/tidb/pkg/parser v0.0.0-20240102121832-237b2c7d5078 // indirect + github.com/pingcap/tidb/pkg/parser v0.0.0-20240113042725-d0c81e1f2aff // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect - github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/common v0.46.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/riza-io/grpc-go v0.2.0 // indirect @@ -81,26 +82,39 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/tetratelabs/wazero v1.6.0 // indirect github.com/urfave/cli/v2 v2.27.1 // indirect + github.com/wasilibs/go-pgquery v0.0.0-20240111082134-4f3a12da8e62 // indirect + github.com/wasilibs/wazerox v0.0.0-20240105014115-75455786b41e // indirect github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect + golang.org/x/crypto v0.18.0 // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.15.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/tools v0.17.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 // indirect google.golang.org/grpc v1.60.1 // indirect google.golang.org/protobuf v1.32.0 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/uint128 v1.3.0 // indirect + modernc.org/cc/v3 v3.41.0 // indirect + modernc.org/ccgo/v3 v3.16.15 // indirect + modernc.org/libc v1.40.2 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/sqlite v1.28.0 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index e7a25a5..9312bf9 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,6 @@ github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNg github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM= -github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= @@ -32,10 +30,8 @@ github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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.26.3 h1:3ljYrjPwsUNAUFdUIr2jVg5EhKdcke/ZLop7uVg1Er8= -github.com/brianvoe/gofakeit/v6 v6.26.3/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= -github.com/bytecodealliance/wasmtime-go/v14 v14.0.0 h1:ur7S3P+PAeJmgllhSrKnGQOAmmtUbLQxb/nw2NZiaEM= -github.com/bytecodealliance/wasmtime-go/v14 v14.0.0/go.mod h1:tqOVEUjnXY6aGpSfM9qdVRR6G//Yc513fFYUdzZb/DY= +github.com/brianvoe/gofakeit/v6 v6.26.4 h1:+7JwTAXxw46Hdo1hA/F92Wi7x8vTwbjdFtBWYdm8eII= +github.com/brianvoe/gofakeit/v6 v6.26.4/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= 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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= @@ -63,6 +59,8 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh 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.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/eko/gocache/lib/v4 v4.1.5 h1:CeMQmdIzwBKKLRjk3FCDXzNFsQTyqJ01JLI7Ib0C9r8= github.com/eko/gocache/lib/v4 v4.1.5/go.mod h1:XaNfCwW8KYW1bRZ/KoHA1TugnnkMz0/gT51NDIu7LSY= github.com/eko/gocache/store/go_cache/v4 v4.2.1 h1:3xSksOamzCf+YZXz9l67gr6jOj3AA4hnk0mV4z3Jwbs= @@ -85,26 +83,18 @@ github.com/golang-migrate/migrate/v4 v4.17.0 h1:rd40H3QXU0AA4IoLllFcEAEo9dYKRHYN github.com/golang-migrate/migrate/v4 v4.17.0/go.mod h1:+Cp2mtLP4/aXDTKb9wmXYitdrNx2HGs45rbWAo6OsKM= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/cel-go v0.18.2 h1:L0B6sNBSVmt0OyECi8v6VOS74KOc9W/tLiWKfZABvf4= github.com/google/cel-go v0.18.2/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -125,14 +115,16 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA= github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.1 h1:5I9etrGkLrN+2XPCsi6XLlV5DITbSL/xBZdmAxFcXPI= -github.com/jackc/pgx/v5 v5.5.1/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA= +github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -146,10 +138,10 @@ github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= -github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= 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/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -164,8 +156,10 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pganalyze/pg_query_go/v4 v4.2.3 h1:cNLqyiVMasV7YGWyYV+fkXyHp32gDfXVNCqoHztEGNk= -github.com/pganalyze/pg_query_go/v4 v4.2.3/go.mod h1:aEkDNOXNM5j0YGzaAapwJ7LB3dLNj+bvbWcLv1hOVqA= +github.com/pganalyze/pg_query_go/v4 v4.2.4-0.20231205012101-7463430c7b73 h1:vZEujakYrBzd7CWMwqTsDRU7bdRm4Aiu76X9K3P+qtI= +github.com/pganalyze/pg_query_go/v4 v4.2.4-0.20231205012101-7463430c7b73/go.mod h1:pRJ9rCcWIhDLAzymhH2iY5kEVEfU6UGrL1wBWuO7gmM= +github.com/pganalyze/pg_query_go/v5 v5.1.0 h1:MlxQqHZnvA3cbRQYyIrjxEjzo560P6MyTgtlaf3pmXg= +github.com/pganalyze/pg_query_go/v5 v5.1.0/go.mod h1:FsglvxidZsVN+Ltw3Ai6nTgPVcK2BPukH3jCDEqc1Ug= github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 h1:+FZIDR/D97YOPik4N4lPDaUcLDF/EQPogxtlHB2ZZRM= @@ -174,8 +168,8 @@ github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c h1:CgbKAHto5CQgW github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c/go.mod h1:4qGtCB0QK0wBzKtFEGDhxXnSnbQApw1gc9siScUl8ew= github.com/pingcap/log v1.1.0 h1:ELiPxACz7vdo1qAvvaWJg1NrYFoY6gqAh/+Uo6aXdD8= github.com/pingcap/log v1.1.0/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4= -github.com/pingcap/tidb/pkg/parser v0.0.0-20240102121832-237b2c7d5078 h1:+hofKYHC1Qw65oewSb1DEkLOT0HnUXvwOzD9Is7TZN4= -github.com/pingcap/tidb/pkg/parser v0.0.0-20240102121832-237b2c7d5078/go.mod h1:yRkiqLFwIqibYg2P7h4bclHjHcJiIFRLKhGRyBcKYus= +github.com/pingcap/tidb/pkg/parser v0.0.0-20240113042725-d0c81e1f2aff h1:cqQfZj2myN/JRMBNwaRlFAVvUwStWVCOW5IzVvMYQnA= +github.com/pingcap/tidb/pkg/parser v0.0.0-20240113042725-d0c81e1f2aff/go.mod h1:yRkiqLFwIqibYg2P7h4bclHjHcJiIFRLKhGRyBcKYus= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -186,8 +180,8 @@ github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+ github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= -github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common v0.46.0 h1:doXzt5ybi1HBKpsZOL0sSkaNHJJqkyfEWZGGqqScV0Y= +github.com/prometheus/common v0.46.0/go.mod h1:Tp0qkxpb9Jsg54QMe+EAmqXkSV7Evdy1BTn+g2pa/hQ= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= @@ -219,8 +213,8 @@ 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.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/sqlc-dev/sqlc v1.24.0 h1:hyVtU9uPYM5VQ6HidWCFFc6QBmn3jSuPqGXBmjvuOsU= -github.com/sqlc-dev/sqlc v1.24.0/go.mod h1:6MrlleFzjRAmi9Vw1zxq9W2X8KeypjNam9rLp2DFgfM= +github.com/sqlc-dev/sqlc v1.25.0 h1:+lI62q7IiLeEwM1tuX5dRmIKi2sdWY5Yd1d93VRRdQw= +github.com/sqlc-dev/sqlc v1.25.0/go.mod h1:f2/ok8PBTvvf4KPZuofiksVOB0OCKGLWp+wyxTHapp8= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -237,10 +231,16 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU 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/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g= +github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/vektah/gqlparser/v2 v2.5.10 h1:6zSM4azXC9u4Nxy5YmdmGu4uKamfwsdKTwp5zsEealU= github.com/vektah/gqlparser/v2 v2.5.10/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= +github.com/wasilibs/go-pgquery v0.0.0-20240111082134-4f3a12da8e62 h1:g36hOBSt/BlUop92fDLo7eSpNiGjb8DLTkY+zwSfyWs= +github.com/wasilibs/go-pgquery v0.0.0-20240111082134-4f3a12da8e62/go.mod h1:FP9vDxueQ0fVagGRsgDk81sdgEXvWc+XBG95K9d193I= +github.com/wasilibs/wazerox v0.0.0-20240105014115-75455786b41e h1:9h7OzkTRM/FD0Stn2JJEx09+Cx1sD3niOqdqPDm2e0o= +github.com/wasilibs/wazerox v0.0.0-20240105014115-75455786b41e/go.mod h1:IQNVyA4d1hWIe23mlMMuqXjyWMdndgSlNx6FqBkwPsM= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e h1:+SOyEddqYF09QP7vr7CgJ1eti3pY9Fn3LHO1M1r/0sI= github.com/xrash/smetrics v0.0.0-20231213231151-1d8dd44e695e/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -264,10 +264,10 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4= -golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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= @@ -280,15 +280,15 @@ 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-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= -golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -298,8 +298,9 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -316,28 +317,23 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= +google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 h1:OPXtXn7fNMaXwO3JvOmF1QyTc00jsSFFz1vXXBOdCDo= +google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -363,3 +359,31 @@ 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= +lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= +lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= +modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= +modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= +modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.40.2 h1:pzVHG9jwYZNWANfltHiU3HYfrzYIsX6ysRLJ93adZXA= +modernc.org/libc v1.40.2/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= +modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= diff --git a/pkg/auth/provider.go b/pkg/auth/provider.go index f27e5ac..ea85f31 100644 --- a/pkg/auth/provider.go +++ b/pkg/auth/provider.go @@ -3,7 +3,7 @@ package auth import ( "context" "fmt" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/piotrekmonko/portfello/pkg/dao" "github.com/piotrekmonko/portfello/pkg/logz" ) @@ -17,13 +17,15 @@ type Provider interface { } // NewProvider builds correct provider based on config. -func NewProvider(ctx context.Context, log logz.Logger, conf *config.Config, dao *dao.DAO) (Provider, error) { - switch conf.Auth.Provider { - case config.AuthProviderLocal: +func NewProvider(ctx context.Context, log logz.Logger, c *conf.Config, dao *dao.DAO) (Provider, error) { + switch c.Auth.Provider { + case conf.AuthProviderLocal: return NewLocalProvider(log.Named("auth"), dao), nil - case config.AuthProviderAuth0: - return NewAuth0Provider(ctx, log.Named("auth"), &conf.Auth) + case conf.AuthProviderAuth0: + return NewAuth0Provider(ctx, log.Named("auth"), &c.Auth) + case conf.AuthProviderMock: + return NewMockProvider() default: - return nil, fmt.Errorf("unsupported auth provider configuration valu: %s", conf.Auth.Provider) + return nil, fmt.Errorf("unsupported auth provider configuration valu: %s", c.Auth.Provider) } } diff --git a/pkg/auth/provider_auth0.go b/pkg/auth/provider_auth0.go index 08c4756..0b79d6a 100644 --- a/pkg/auth/provider_auth0.go +++ b/pkg/auth/provider_auth0.go @@ -7,7 +7,7 @@ import ( "github.com/auth0/go-jwt-middleware/v2/jwks" "github.com/auth0/go-jwt-middleware/v2/validator" "github.com/google/uuid" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/piotrekmonko/portfello/pkg/logz" "net/http" "net/url" @@ -26,15 +26,15 @@ func (c Auth0Claims) Validate(_ context.Context) error { type Auth0Provider struct { log logz.Logger - client *management.Management + manager *management.Management jwtValidator *validator.Validator jwtProvider *jwks.CachingProvider - config *config.Auth0 + config *conf.Auth0 } var _ Provider = (*Auth0Provider)(nil) -func NewAuth0Provider(ctx context.Context, log logz.Logger, conf *config.Auth0) (*Auth0Provider, error) { +func NewAuth0Provider(ctx context.Context, log logz.Logger, conf *conf.Auth0) (*Auth0Provider, error) { // Initialize auth0 management API client client, err := management.New( conf.Domain, @@ -72,7 +72,7 @@ func NewAuth0Provider(ctx context.Context, log logz.Logger, conf *config.Auth0) log: log.Named("prov.auth0"), jwtProvider: jwtProvider, jwtValidator: jwtValidator, - client: client, + manager: client, config: conf, }, nil } @@ -91,13 +91,13 @@ func (a *Auth0Provider) ValidateToken(ctx context.Context, token string) (string } func (a *Auth0Provider) GetUserByEmail(ctx context.Context, email string) (*User, error) { - matchingUsers, err := a.client.User.ListByEmail(ctx, email) + matchingUsers, err := a.manager.User.ListByEmail(ctx, email) if err != nil || len(matchingUsers) == 0 { return nil, a.log.Errorw(ctx, err, "cannot find user by email='%s'", email) } auth0User := matchingUsers[0] - roles, err := a.client.User.Roles(ctx, auth0User.GetID()) + roles, err := a.manager.User.Roles(ctx, auth0User.GetID()) if err != nil { return nil, a.log.Errorw(ctx, err, "cannot read user roles for email='%s'", email) } @@ -118,7 +118,7 @@ func (a *Auth0Provider) GetUserByEmail(ctx context.Context, email string) (*User func (a *Auth0Provider) ListUsers(ctx context.Context) ([]*User, int, error) { var wg sync.WaitGroup - uList, err := a.client.User.List(ctx, management.IncludeTotals(true)) + uList, err := a.manager.User.List(ctx, management.IncludeTotals(true)) if err != nil { return nil, -1, a.log.Errorw(ctx, err, "cannot list users") } @@ -137,9 +137,10 @@ func (a *Auth0Provider) ListUsers(ctx context.Context) ([]*User, int, error) { go func(userID string, index int) { defer wg.Done() - roles, err := a.client.User.Roles(ctx, userID) + roles, err := a.manager.User.Roles(ctx, userID) if err != nil { _ = a.log.Errorw(ctx, err, "cannot read user roles for userID='%s'", userID) + return } roleSlice := make(Roles, len(roles.Roles)) @@ -157,7 +158,7 @@ func (a *Auth0Provider) ListUsers(ctx context.Context) ([]*User, int, error) { } func (a *Auth0Provider) CreateUser(ctx context.Context, email string, name string, roles Roles) (*User, error) { - conn, err := a.client.Connection.Read(ctx, a.config.ConnectionID) + conn, err := a.manager.Connection.Read(ctx, a.config.ConnectionID) if err != nil { return nil, a.log.Errorw(ctx, err, "cannot fetch Auth0 db connection") } @@ -169,7 +170,7 @@ func (a *Auth0Provider) CreateUser(ctx context.Context, email string, name strin Name: &name, Password: &initialPassword, } - err = a.client.User.Create(ctx, userReq) + err = a.manager.User.Create(ctx, userReq) if err != nil { if mngmtErr, isMngmtErr := err.(management.Error); isMngmtErr && mngmtErr.Status() == http.StatusConflict { a.log.Warnw(ctx, "user(%s) already exists in Auth0", email) @@ -183,7 +184,7 @@ func (a *Auth0Provider) CreateUser(ctx context.Context, email string, name strin } } - existingRoles, err := a.client.Role.List(ctx) + existingRoles, err := a.manager.Role.List(ctx) if err != nil { return nil, a.log.Errorw(ctx, err, "cannot list roles") } @@ -202,7 +203,7 @@ func (a *Auth0Provider) CreateUser(ctx context.Context, email string, name strin } } - if err = a.client.User.AssignRoles(ctx, userReq.GetID(), auth0Roles); err != nil { + if err = a.manager.User.AssignRoles(ctx, userReq.GetID(), auth0Roles); err != nil { return nil, a.log.Errorw(ctx, err, "cannot assign roles to user(%s)", email) } @@ -214,7 +215,7 @@ func (a *Auth0Provider) AssignRoles(ctx context.Context, auth0UserID string, rol for i, role := range roles { auth0Roles[i] = &management.Role{Name: role.StrPtr()} } - if err := a.client.User.AssignRoles(ctx, auth0UserID, auth0Roles); err != nil { + if err := a.manager.User.AssignRoles(ctx, auth0UserID, auth0Roles); err != nil { return nil, a.log.Errorw(ctx, err, "cannot assign roles to user(%s)", auth0UserID) } diff --git a/pkg/auth/provider_local.go b/pkg/auth/provider_local.go index 86c7009..01dc984 100644 --- a/pkg/auth/provider_local.go +++ b/pkg/auth/provider_local.go @@ -71,7 +71,7 @@ func (p *LocalProvider) CreateUser(ctx context.Context, email string, name strin return nil, p.log.Errorw(ctx, err, "cannot retrieve user with email='%s'", email) } - return userFromLocal(usr), tx.Commit() + return userFromLocal(usr), tx.Commit(ctx) } func (p *LocalProvider) AssignRoles(ctx context.Context, email string, roles []RoleID) ([]RoleID, error) { @@ -91,7 +91,7 @@ func (p *LocalProvider) AssignRoles(ctx context.Context, email string, roles []R return nil, p.log.Errorw(ctx, err, "cannot retrieve user with email='%s'", email) } - return RolesFromString(usr.Roles), tx.Commit() + return RolesFromString(usr.Roles), tx.Commit(ctx) } func (p *LocalProvider) ValidateToken(ctx context.Context, token string) (userID string, err error) { diff --git a/pkg/auth/provider_mock.go b/pkg/auth/provider_mock.go new file mode 100644 index 0000000..3c7f243 --- /dev/null +++ b/pkg/auth/provider_mock.go @@ -0,0 +1,112 @@ +package auth + +import ( + "context" + "errors" + "fmt" + "strings" + "time" +) + +var ( + ErrUserNotFound = fmt.Errorf("user not found") +) + +// MockProvider is only for test use. +type MockProvider struct { + Users []*User +} + +var _ Provider = (*MockProvider)(nil) + +func NewMockProvider() (*MockProvider, error) { + return &MockProvider{Users: []*User{ + { + ID: "u1", + DisplayName: "User One", + Email: "user.one@example.com", + Roles: []RoleID{RoleSuperAdmin}, + CreatedAt: time.Now(), + Registration: nil, + key: nil, + }, + { + ID: "u2", + DisplayName: "User Two", + Email: "user.two@example.com", + Roles: []RoleID{RoleAdmin}, + CreatedAt: time.Now(), + Registration: nil, + key: nil, + }, + { + ID: "u3", + DisplayName: "User Three", + Email: "user.three@example.com", + Roles: []RoleID{RoleUser}, + CreatedAt: time.Now(), + Registration: nil, + key: nil, + }, + }}, nil +} + +func (m *MockProvider) GetUserByEmail(ctx context.Context, email string) (*User, error) { + for i := range m.Users { + if strings.ToLower(m.Users[i].GetEmail()) == strings.ToLower(email) { + return m.Users[i], nil + } + } + + return nil, ErrUserNotFound +} + +func (m *MockProvider) ListUsers(ctx context.Context) ([]*User, int, error) { + return m.Users, len(m.Users), nil +} + +func (m *MockProvider) CreateUser(ctx context.Context, email string, name string, roles Roles) (*User, error) { + existingUser, err := m.GetUserByEmail(ctx, email) + if !errors.Is(err, ErrUserNotFound) { + return nil, err + } else if existingUser != nil { + return existingUser, nil + } + + nu := &User{ + ID: email, + DisplayName: name, + Email: email, + Roles: roles, + CreatedAt: time.Now(), + Registration: nil, + key: nil, + } + m.Users = append(m.Users, nu) + return nu, nil +} + +func (m *MockProvider) AssignRoles(ctx context.Context, email string, roles []RoleID) ([]RoleID, error) { + usr, err := m.GetUserByEmail(ctx, email) + if err != nil { + return nil, err + } + + usr.Roles = roles + return usr.Roles, nil +} + +// ValidateToken expects token to be in format "mocktoken:". +func (m *MockProvider) ValidateToken(ctx context.Context, token string) (userID string, err error) { + parts := strings.Split(token, ":") + if len(parts) != 2 || parts[0] != "mocktoken" { + return "", fmt.Errorf("invalid token") + } + + usr, err := m.GetUserByEmail(ctx, parts[1]) + if err != nil { + return "", err + } + + return usr.ID, nil +} diff --git a/pkg/config/config.go b/pkg/conf/conf.go similarity index 50% rename from pkg/config/config.go rename to pkg/conf/conf.go index 6f5f12a..c9453b4 100644 --- a/pkg/config/config.go +++ b/pkg/conf/conf.go @@ -1,10 +1,12 @@ -package config +package conf import ( "fmt" "github.com/mitchellh/mapstructure" + "github.com/spf13/cobra" "github.com/spf13/viper" "log" + "os" ) type Config struct { @@ -22,6 +24,7 @@ type GraphQL struct { const ( AuthProviderLocal = "local" AuthProviderAuth0 = "auth0" + AuthProviderMock = "mock" ) type Auth0 struct { @@ -60,3 +63,45 @@ func (c *Config) Validate() error { return nil } + +// NewTestConfig returns configuration suitable for testing. +func NewTestConfig() *Config { + return &Config{ + DatabaseDSN: "", + Graph: GraphQL{}, + Auth: Auth0{}, + Logging: Logging{}, + } +} + +// InitConfig reads in config file and ENV variables if set. Should be called once at app startup. +func InitConfig(cfgFilePath string) error { + if cfgFilePath != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFilePath) + if err := viper.ReadInConfig(); err != nil { + return fmt.Errorf("fatal error config file @ %s: %w", cfgFilePath, err) + } + } + + // Find home directory. + home, err := os.UserHomeDir() + cobra.CheckErr(err) + + // Search config in home directory with name ".portfello" (without extension). + viper.AddConfigPath(home) + viper.AddConfigPath(".") + viper.SetConfigType("yaml") + + viper.AutomaticEnv() // read in environment variables that match + + // Loop through found config files until all are parsed + for _, configFile := range []string{".portfello", ".portfello-local"} { + viper.SetConfigName(configFile) + if err := viper.MergeInConfig(); err == nil { + _, _ = fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) + } + } + + return nil +} diff --git a/pkg/dao/dao.go b/pkg/dao/dao.go index 5e50d3a..cd84e2f 100644 --- a/pkg/dao/dao.go +++ b/pkg/dao/dao.go @@ -11,6 +11,8 @@ import ( type DAO struct { log logz.Logger db DBTX + // txDepth reports how many times a transaction was started and closed + txDepth int *Queries } @@ -39,31 +41,33 @@ type beginner interface { } func (q *DAO) BeginTx(ctx context.Context) (*DAO, func() error, error) { - txLog := q.log.With("tx", "true") tx, err := q.db.(beginner).BeginTx(ctx, nil) if err != nil { return nil, func() error { return nil }, q.log.Errorw(ctx, err, "error while starting transaction") } + txLog := q.log.With("tx", q.txDepth+1) return &DAO{ log: txLog, db: q.db, + txDepth: q.txDepth + 1, Queries: q.WithTx(tx), }, func() error { if errors.Is(tx.Rollback(), sql.ErrTxDone) { // This callback can be called as deferred, regardless if tx was committed or not, thus we should // silence sql.ErrTxDone error. q.Queries.db = q.db + q.log.Debugw(ctx, "transaction committed") return nil } - return txLog.Errorw(ctx, err, "error while rolling back transaction") + return q.log.Errorw(ctx, err, "error while rolling back transaction") }, nil } -func (q *DAO) Commit() error { +func (q *DAO) Commit(ctx context.Context) error { err := q.Queries.db.(*sql.Tx).Commit() q.Queries.db = q.db - return q.log.Errorw(context.Background(), err, "error while commiting transaction") + return q.log.Errorw(ctx, err, "error while committing transaction") } func NilStr(s string) sql.NullString { diff --git a/pkg/dao/dao_test.go b/pkg/dao/dao_test.go new file mode 100644 index 0000000..c32e7ad --- /dev/null +++ b/pkg/dao/dao_test.go @@ -0,0 +1,151 @@ +package dao + +import ( + "context" + "database/sql" + "github.com/piotrekmonko/portfello/pkg/logz" + "reflect" + "testing" +) + +func TestDAO_BeginTx_Rollback(t *testing.T) { + type fields struct { + log logz.Logger + db DBTX + txDepth int + Queries *Queries + } + type args struct { + ctx context.Context + } + tests := []struct { + name string + fields fields + args args + want *DAO + want1 func() error + wantErr bool + wantRollbackerErr error + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q := &DAO{ + log: tt.fields.log, + db: tt.fields.db, + txDepth: tt.fields.txDepth, + Queries: tt.fields.Queries, + } + tTx, rollbacker, err := q.BeginTx(tt.args.ctx) + if (err != nil) != tt.wantErr { + t.Errorf("BeginTx() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(tTx, tt.want) { + t.Errorf("BeginTx() got = %v, want %v", tTx, tt.want) + } + rollbackerErr := rollbacker() + if !reflect.DeepEqual(rollbackerErr, tt.wantRollbackerErr) { + t.Errorf("BeginTx() got1 = %v, want %v", rollbackerErr, tt.wantRollbackerErr) + } + }) + } +} + +func TestDAO_BeginTx_Commit(t *testing.T) { + type fields struct { + log logz.Logger + db DBTX + txDepth int + Queries *Queries + } + type args struct { + ctx context.Context + } + tests := []struct { + name string + fields fields + args args + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + q := &DAO{ + log: tt.fields.log, + db: tt.fields.db, + txDepth: tt.fields.txDepth, + Queries: tt.fields.Queries, + } + if err := q.Commit(tt.args.ctx); (err != nil) != tt.wantErr { + t.Errorf("Commit() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestNewDAO(t *testing.T) { + type args struct { + ctx context.Context + log logz.Logger + dsn string + } + tests := []struct { + name string + args args + want *sql.DB + want1 *DAO + wantErr bool + }{ + // TODO: Add test cases. + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1, err := NewDAO(tt.args.ctx, tt.args.log, tt.args.dsn) + if (err != nil) != tt.wantErr { + t.Errorf("NewDAO() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewDAO() got = %v, want %v", got, tt.want) + } + if !reflect.DeepEqual(got1, tt.want1) { + t.Errorf("NewDAO() got1 = %v, want %v", got1, tt.want1) + } + }) + } +} + +func TestNilStr(t *testing.T) { + tests := []struct { + name string + input string + want sql.NullString + }{ + { + name: "empty string", + input: "", + want: sql.NullString{ + String: "", + Valid: false, + }, + }, + { + name: "valid string", + input: "valid", + want: sql.NullString{ + String: "valid", + Valid: true, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NilStr(tt.input); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NilStr() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/dao/expense_extras.go b/pkg/dao/expense_extras.go index ff47a39..9032f50 100644 --- a/pkg/dao/expense_extras.go +++ b/pkg/dao/expense_extras.go @@ -2,9 +2,7 @@ package dao import "time" -func (e *Expense) IsOperation() { - return -} +func (e *Expense) IsOperation() {} func (e *Expense) GetID() string { return e.ID diff --git a/pkg/graph/resolver.go b/pkg/graph/resolver.go index a28a9c7..292c9f7 100644 --- a/pkg/graph/resolver.go +++ b/pkg/graph/resolver.go @@ -2,7 +2,7 @@ package graph import ( "github.com/piotrekmonko/portfello/pkg/auth" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/piotrekmonko/portfello/pkg/dao" ) @@ -11,7 +11,7 @@ import ( // It serves as dependency injection for your app, add any dependencies you require here. type Resolver struct { - Conf *config.Config + Conf *conf.Config DbDAO *dao.DAO AuthService *auth.Service } diff --git a/pkg/graph/wallets.resolvers.go b/pkg/graph/wallets.resolvers.go index 256b219..3f1f82e 100644 --- a/pkg/graph/wallets.resolvers.go +++ b/pkg/graph/wallets.resolvers.go @@ -49,7 +49,7 @@ func (r *mutationResolver) CreateWallet(ctx context.Context, input model.CreateW return nil, fmt.Errorf("cannot list user wallets: %w", err) } - return wallets, q.Commit() + return wallets, q.Commit(ctx) } // ListWallets is the resolver for the listWallets field. diff --git a/pkg/logz/logz.go b/pkg/logz/logz.go index 04cef4f..bed0208 100644 --- a/pkg/logz/logz.go +++ b/pkg/logz/logz.go @@ -3,9 +3,13 @@ package logz import ( "context" "fmt" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" + "github.com/stretchr/testify/assert" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "go.uber.org/zap/zaptest" + "strings" + "testing" ) type loggerKeyValuePairsCtx struct{} @@ -21,11 +25,12 @@ type Logger interface { With(args ...interface{}) Logger Debugw(ctx context.Context, msg string, keysAndValues ...interface{}) Infow(ctx context.Context, msg string, keysAndValues ...interface{}) + Infof(ctx context.Context, msg string, args ...interface{}) Warnw(ctx context.Context, msg string, keysAndValues ...interface{}) Errorw(ctx context.Context, err error, msg string, keysAndValues ...interface{}) error } -func NewLogger(c *config.Logging) Logger { +func NewLogger(c *conf.Logging) Logger { var cfg zap.Config if c.Format == "dev" { @@ -69,6 +74,10 @@ func (l *Log) Infow(ctx context.Context, msg string, keysAndValues ...interface{ l.SugaredLogger.With(FromCtx(ctx)...).Infow(msg, keysAndValues...) } +func (l *Log) Infof(ctx context.Context, msg string, args ...interface{}) { + l.SugaredLogger.With(FromCtx(ctx)...).Infof(msg, args...) +} + func (l *Log) Warnw(ctx context.Context, msg string, keysAndValues ...interface{}) { l.SugaredLogger.With(FromCtx(ctx)...).Warnw(msg, keysAndValues...) } @@ -121,3 +130,30 @@ func ParseFlag(level string) error { func SetVer(v string) { version = v } + +// TestLogger records output to internal Messages slice for later inspection. Use in your tests. +type TestLogger struct { + testing.TB + *Log + Messages []string +} + +func NewTestLogger(tb testing.TB) *TestLogger { + tl := &TestLogger{ + TB: tb, + Messages: make([]string, 0), + } + tl.Log = &Log{SugaredLogger: zaptest.NewLogger(tl).Sugar()} + return tl +} + +func (t *TestLogger) Logf(format string, args ...interface{}) { + m := fmt.Sprintf(format, args...) + m = m[strings.IndexByte(m, '\t')+1:] // strip the timestamp and its following tab + t.Messages = append(t.Messages, m) + t.TB.Log(m) +} + +func (t *TestLogger) AssertMessages(msgs ...string) { + assert.Equal(t.TB, msgs, t.Messages, "logged messages did not match") +} diff --git a/pkg/logz/logz_test.go b/pkg/logz/logz_test.go index 4ed5d27..f40b302 100644 --- a/pkg/logz/logz_test.go +++ b/pkg/logz/logz_test.go @@ -3,40 +3,12 @@ package logz import ( "context" "fmt" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/stretchr/testify/assert" - "go.uber.org/zap/zaptest" "reflect" - "strings" "testing" ) -type TestLogger struct { - testing.TB - *Log - Messages []string -} - -func NewTestLogger(tb testing.TB) *TestLogger { - tl := &TestLogger{ - TB: tb, - Messages: make([]string, 0), - } - tl.Log = &Log{SugaredLogger: zaptest.NewLogger(tl).Sugar()} - return tl -} - -func (t *TestLogger) Logf(format string, args ...interface{}) { - m := fmt.Sprintf(format, args...) - m = m[strings.IndexByte(m, '\t')+1:] // strip the timestamp and its following tab - t.Messages = append(t.Messages, m) - t.TB.Log(m) -} - -func (t *TestLogger) AssertMessages(msgs ...string) { - assert.Equal(t.TB, msgs, t.Messages, "logged messages did not match") -} - func TestFromCtx(t *testing.T) { tests := []struct { name string @@ -90,7 +62,7 @@ func TestLog_Levels(t *testing.T) { } func TestNewLogger(t *testing.T) { - logger := NewLogger(&config.Logging{ + logger := NewLogger(&conf.Logging{ Level: "debug", Format: "dev", }) diff --git a/pkg/server/server.go b/pkg/server/server.go index f777c2e..4dfe368 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -5,7 +5,7 @@ import ( "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/playground" "github.com/piotrekmonko/portfello/pkg/auth" - "github.com/piotrekmonko/portfello/pkg/config" + "github.com/piotrekmonko/portfello/pkg/conf" "github.com/piotrekmonko/portfello/pkg/dao" "github.com/piotrekmonko/portfello/pkg/graph" "github.com/piotrekmonko/portfello/pkg/logz" @@ -13,7 +13,7 @@ import ( "time" ) -func NewServer(ctx context.Context, log logz.Logger, conf *config.Config, dbQuerier *dao.DAO, authService *auth.Service) *http.Server { +func NewServer(ctx context.Context, log logz.Logger, conf *conf.Config, dbQuerier *dao.DAO, authService *auth.Service) *http.Server { graphResolver := &graph.Resolver{ Conf: conf, DbDAO: dbQuerier, @@ -35,10 +35,10 @@ func NewServer(ctx context.Context, log logz.Logger, conf *config.Config, dbQuer mux.Handle("/query", srv) if conf.Graph.EnablePlayground { - log.Infow(ctx, "connect to http://localhost:%s/ for GraphQL playground", conf.Graph.Port) + log.Infof(ctx, "connect to http://localhost:%s/ for GraphQL playground", conf.Graph.Port) mux.Handle("/", playground.Handler("GraphQL playground", "/query")) } - log.Infow(ctx, "serving on http://localhost:%s/", conf.Graph.Port) + log.Infof(ctx, "serving on http://localhost:%s/", conf.Graph.Port) return httpSrv }