diff --git a/_tools/go.mod b/_tools/go.mod index 80ce1ed..730b775 100644 --- a/_tools/go.mod +++ b/_tools/go.mod @@ -4,6 +4,7 @@ go 1.21.8 require ( github.com/golangci/golangci-lint v1.57.2 + github.com/sqlc-dev/sqlc v1.26.0 golang.org/x/tools v0.19.0 gotest.tools/gotestsum v1.11.0 ) @@ -11,6 +12,7 @@ require ( require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/4meepo/tagalign v1.3.3 // indirect github.com/Abirdcfly/dupword v0.0.14 // indirect github.com/Antonboom/errname v0.1.12 // indirect @@ -25,6 +27,7 @@ require ( github.com/alexkohler/nakedret/v2 v2.0.4 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -42,11 +45,14 @@ require ( github.com/charithe/durationcheck v0.0.10 // indirect github.com/chavacava/garif v0.1.0 // indirect github.com/ckaznocha/intrange v0.1.1 // indirect + github.com/cubicdaiya/gonp v1.0.4 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect + github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect github.com/daixiang0/gci v0.12.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/dnephin/pflag v1.0.7 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fatih/structtag v1.2.0 // indirect @@ -55,6 +61,7 @@ require ( github.com/fzipp/gocyclo v0.6.0 // indirect github.com/ghostiam/protogetter v0.3.5 // indirect github.com/go-critic/go-critic v0.11.2 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect github.com/go-toolsmith/astequal v1.2.0 // indirect @@ -66,26 +73,35 @@ require ( github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gofrs/flock v0.8.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect github.com/golangci/misspell v0.4.1 // indirect github.com/golangci/plugin-module-register v0.1.1 // indirect github.com/golangci/revgrep v0.5.2 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect github.com/gostaticanalysis/comment v1.4.2 // indirect github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgx/v5 v5.5.5 // indirect + github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jgautheron/goconst v1.7.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect github.com/jjti/go-spancheck v0.5.3 // indirect github.com/julz/importas v0.1.0 // indirect @@ -113,11 +129,17 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moricho/tparallel v0.3.1 // indirect github.com/nakabonne/nestif v0.3.1 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/nunnatsa/ginkgolinter v0.16.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml/v2 v2.2.0 // 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-20231103154709-4f00ece106b1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.4.8 // indirect github.com/prometheus/client_golang v1.17.0 // indirect @@ -128,7 +150,9 @@ require ( github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.2 // indirect + github.com/riza-io/grpc-go v0.2.0 // indirect github.com/ryancurrah/gomodguard v1.3.1 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sagikazarmark/locafero v0.3.0 // indirect @@ -152,12 +176,14 @@ require ( github.com/spf13/viper v1.17.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect github.com/tetafro/godot v1.4.16 // indirect + github.com/tetratelabs/wazero v1.7.0 // indirect github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect github.com/timonwong/loggercheck v0.9.4 // indirect github.com/tomarrell/wrapcheck/v2 v2.8.3 // indirect @@ -165,6 +191,7 @@ require ( github.com/ultraware/funlen v0.1.0 // indirect github.com/ultraware/whitespace v0.1.0 // indirect github.com/uudashr/gocognit v1.1.2 // indirect + github.com/wasilibs/go-pgquery v0.0.0-20240319230125-b9b2e95c69a7 // indirect github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect @@ -172,22 +199,36 @@ require ( gitlab.com/bosi/decorder v0.4.1 // indirect go-simpler.org/musttag v0.9.0 // indirect go-simpler.org/sloglint v0.5.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.uber.org/automaxprocs v1.5.3 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect + golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/grpc v1.62.1 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.4.7 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.41.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/sqlite v1.29.5 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect mvdan.cc/gofumpt v0.6.0 // indirect mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 // indirect ) diff --git a/_tools/go.sum b/_tools/go.sum index 2c69cae..baa75a4 100644 --- a/_tools/go.sum +++ b/_tools/go.sum @@ -35,6 +35,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= github.com/4meepo/tagalign v1.3.3 h1:ZsOxcwGD/jP4U/aw7qeWu58i7dwYemfy5Y+IF1ACoNw= github.com/4meepo/tagalign v1.3.3/go.mod h1:Q9c1rYMZJc9dPRkbQPpcBNCLEmY2njbAsXhQOZFE2dE= github.com/Abirdcfly/dupword v0.0.14 h1:3U4ulkc8EUo+CaT105/GJ1BQwtgyj6+VaBVbAX11Ba8= @@ -74,10 +76,13 @@ github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pO github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= +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/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s= github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -119,8 +124,12 @@ github.com/ckaznocha/intrange v0.1.1/go.mod h1:RWffCw/vKBwHeOEwWdCikAtY0q4gGt8Vh github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cubicdaiya/gonp v1.0.4 h1:ky2uIAJh81WiLcGKBVD5R7KsM/36W6IqqTy6Bo6rGws= +github.com/cubicdaiya/gonp v1.0.4/go.mod h1:iWGuP/7+JVTn02OWhRemVbMmG1DOUnmrGTYYACpOI0I= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= +github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso= +github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= github.com/daixiang0/gci v0.12.3 h1:yOZI7VAxAGPQmkb1eqt5g/11SUlwoat1fSblGLmdiQc= github.com/daixiang0/gci v0.12.3/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -131,6 +140,8 @@ github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42 github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= +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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -166,6 +177,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= @@ -224,6 +237,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/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/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g= @@ -240,6 +255,8 @@ github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNF github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -270,6 +287,8 @@ github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0Z github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= @@ -291,6 +310,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -298,10 +319,20 @@ github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSo github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 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/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= +github.com/jackc/pgx/v5 v5.5.5/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/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= +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/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jjti/go-spancheck v0.5.3 h1:vfq4s2IB8T3HvbpiwDTYgVPj1Ze/ZSXrTtaZRTc7CuM= @@ -370,6 +401,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 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= @@ -390,6 +423,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= @@ -411,6 +446,18 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9 github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +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= +github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg= +github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c h1:CgbKAHto5CQgWM9fSBIvaxsJHuGP0uM74HXtv3MyyGQ= +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-20231103154709-4f00ece106b1 h1:SwGY3zMnK4wO85vvRIqrR3Yh6VpIC9pydG0QNOUPHCY= +github.com/pingcap/tidb/pkg/parser v0.0.0-20231103154709-4f00ece106b1/go.mod h1:yRkiqLFwIqibYg2P7h4bclHjHcJiIFRLKhGRyBcKYus= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -455,9 +502,13 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/riza-io/grpc-go v0.2.0 h1:2HxQKFVE7VuYstcJ8zqpN84VnAoJ4dCL6YFhJewNcHQ= +github.com/riza-io/grpc-go v0.2.0/go.mod h1:2bDvR9KkKC3KhtlSHfR3dAXjUMT86kg4UfWFyVGWqi8= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= @@ -481,6 +532,7 @@ github.com/sashamelentyev/usestdlibvars v1.25.0 h1:IK8SI2QyFzy/2OD2PYnhy84dpfNo9 github.com/sashamelentyev/usestdlibvars v1.25.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= github.com/securego/gosec/v2 v2.19.0 h1:gl5xMkOI0/E6Hxx0XCY2XujA3V7SNSefA8sC+3f1gnk= github.com/securego/gosec/v2 v2.19.0/go.mod h1:hOkDcHz9J/XIgIlPDXalxjeVYsHxoWUc5zJSHxcB8YM= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= @@ -511,10 +563,14 @@ 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.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0+yVI= +github.com/sqlc-dev/sqlc v1.26.0 h1:bW6TA1vVdi2lfqsEddN5tSznRMYcWez7hf+AOqSiEp8= +github.com/sqlc-dev/sqlc v1.26.0/go.mod h1:k2F3RWilLCup3D0XufrzZENCyXjtplALmHDmOt4v5bs= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc= github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -543,6 +599,8 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpR github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetratelabs/wazero v1.7.0 h1:jg5qPydno59wqjpGrHph81lbtHzTrWzwwtD4cD88+hQ= +github.com/tetratelabs/wazero v1.7.0/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4= @@ -557,6 +615,8 @@ github.com/ultraware/whitespace v0.1.0 h1:O1HKYoh0kIeqE8sFqZf1o0qbORXUCOQFrlaQyZ github.com/ultraware/whitespace v0.1.0/go.mod h1:/se4r3beMFNmewJ4Xmz0nMQ941GJt+qmSHGP9emHYe0= github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI= github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k= +github.com/wasilibs/go-pgquery v0.0.0-20240319230125-b9b2e95c69a7 h1:sqqLVb63En4uTKFKBWSJ7c1aIFonhM1yn35/+KchOf4= +github.com/wasilibs/go-pgquery v0.0.0-20240319230125-b9b2e95c69a7/go.mod h1:ZAUjWnxivykc22k0TKFZylOV0WlVQ9nWMExfGFIBuF4= github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= @@ -585,12 +645,21 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -602,6 +671,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -816,6 +887,7 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -918,6 +990,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -930,6 +1006,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= 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= @@ -942,6 +1020,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -953,14 +1032,19 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 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/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-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/gotestsum v1.11.0 h1:A88/QWw7acMjZH1dMe6KZFhw32odUOIjCiAU/Q4n3mI= @@ -976,6 +1060,22 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.4.7 h1:9MDAWxMoSnB6QoSqiVr7P5mtkT9pOc1kSxchzPCnqJs= honnef.co/go/tools v0.4.7/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= +modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +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/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= +modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 h1:zCr3iRRgdk5eIikZNDphGcM6KGVTx3Yu+/Uu9Es254w= diff --git a/_tools/tools.go b/_tools/tools.go index 3fa1120..70b2b8a 100644 --- a/_tools/tools.go +++ b/_tools/tools.go @@ -4,6 +4,7 @@ package tools import ( // Build + _ "github.com/sqlc-dev/sqlc/cmd/sqlc" _ "golang.org/x/tools/cmd/stringer" // Test / lint diff --git a/cmd/import.go b/cmd/import.go index 4310d3a..8faa137 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -2,10 +2,12 @@ package cmd import ( "context" + "database/sql" "fmt" "github.com/firehydrant/signals-migrator/console" "github.com/firehydrant/signals-migrator/pager" + "github.com/firehydrant/signals-migrator/store" "github.com/firehydrant/signals-migrator/tfrender" "github.com/urfave/cli/v2" ) @@ -45,11 +47,6 @@ var ImportCommand = &cli.Command{ } func importAction(ctx *cli.Context) error { - tfr, err := tfrender.New(ctx.String("output-dir")) - if err != nil { - return fmt.Errorf("initializing Terraform render space: %w", err) - } - providerName := ctx.String("provider") provider, err := pager.NewPager(providerName, ctx.String("provider-api-key"), ctx.String("provider-app-id")) if err != nil { @@ -60,22 +57,24 @@ func importAction(ctx *cli.Context) error { return fmt.Errorf("initializing FireHydrant client: %w", err) } - users, err := importUsers(ctx.Context, tfr, provider, fh) - if err != nil { + if err := importUsers(ctx.Context, provider, fh); err != nil { return fmt.Errorf("importing users: %w", err) } - console.Infof("Imported users from %s to FireHydrant.\n", providerName) + console.Infof("Imported users from %s.\n", providerName) - teams, err := importTeams(ctx.Context, tfr, users, provider, fh) - if err != nil { + if err := importTeams(ctx.Context, provider, fh); err != nil { return fmt.Errorf("importing teams: %w", err) } - console.Infof("Imported %d teams from %s to FireHydrant.\n", len(teams), providerName) + console.Infof("Imported teams from %s.\n", providerName) - return fmt.Errorf("not implemented") + tfr, err := tfrender.New(ctx.String("output-dir")) + if err != nil { + return fmt.Errorf("initializing Terraform render space: %w", err) + } + return tfr.Write(ctx.Context) } -func importTeams(ctx context.Context, tfr *tfrender.TFRender, users map[string]*pager.User, provider pager.Pager, fh *pager.FireHydrant) (map[string]*pager.Team, error) { +func importTeams(ctx context.Context, provider pager.Pager, fh *pager.FireHydrant) error { // Get all of the teams registered from Pager Provider (e.g. PagerDuty) var err error var providerTeams []*pager.Team @@ -83,7 +82,7 @@ func importTeams(ctx context.Context, tfr *tfrender.TFRender, users map[string]* providerTeams, err = provider.ListTeams(ctx) }, "Fetching all teams from provider...") if err != nil { - return nil, fmt.Errorf("unable to fetch teamsfrom provider: %w", err) + return fmt.Errorf("unable to fetch teamsfrom provider: %w", err) } console.Successf("Found %d teams from provider.\n", len(providerTeams)) @@ -93,12 +92,10 @@ func importTeams(ctx context.Context, tfr *tfrender.TFRender, users map[string]* fhTeams, err = fh.ListTeams(ctx) }, "Fetching all teams from FireHydrant...") if err != nil { - return nil, fmt.Errorf("unable to match users to FireHydrant: %w", err) + return fmt.Errorf("unable to match users to FireHydrant: %w", err) } console.Successf("Found %d teams on FireHydrant.\n", len(fhTeams)) - matchedTeams := map[string]*pager.Team{} - toCreateTeams := []*pager.Team{} // Now, for every team we found in Pager provider, we prompt console for one of three choices: // 1. Create a new team in FireHydrant // 2. Match with an existing team in FireHydrant @@ -107,46 +104,72 @@ func importTeams(ctx context.Context, tfr *tfrender.TFRender, users map[string]* &pager.Team{Slug: "[<] Skip"}, &pager.Team{Slug: "[+] Create"}, }, fhTeams...) - for _, team := range providerTeams { + for _, extTeam := range providerTeams { i, t, err := console.Selectf(options, func(u *pager.Team) string { return u.String() - }, "For the team '%s' from provider:", team.String()) + }, "For the team '%s' from provider:", extTeam.String()) if err != nil { - return nil, fmt.Errorf("selecting match for '%s': %w", team.String(), err) + return fmt.Errorf("selecting match for '%s': %w", extTeam.String(), err) } switch i { case 0: - console.Warnf("[SKIPPED] '%s' will not be imported to FireHydrant.\n", team.String()) + console.Warnf("[< SKIPPED] '%s' will not be imported to FireHydrant.\n", extTeam.String()) continue case 1: - console.Successf("[ CREATE] '%s' will be created in FireHydrant.\n", team.String()) - toCreateTeams = append(toCreateTeams, team) + console.Successf("[+ CREATE] '%s' will be created in FireHydrant.\n", extTeam.String()) + if err := store.Query.InsertExtTeam(ctx, store.InsertExtTeamParams{ + ID: extTeam.ID, + Name: extTeam.Name, + Slug: extTeam.Slug, + FhTeamID: sql.NullString{Valid: false}, + }); err != nil { + return fmt.Errorf("unable to insert team '%s' into database: %w", extTeam.String(), err) + } continue default: - console.Infof("[MATCHED] '%s' => '%s'.\n", team.String(), t.String()) - matchedTeams[team.Resource.ID] = t + if err := store.Query.InsertExtTeam(ctx, store.InsertExtTeamParams{ + ID: extTeam.ID, + Name: extTeam.Name, + Slug: extTeam.Slug, + FhTeamID: sql.NullString{Valid: true, String: t.ID}, + }); err != nil { + return fmt.Errorf("unable to insert team '%s' into database: %w", extTeam.String(), err) + } else { + console.Infof("[= MATCHED]\n '%s'\n => '%s'.\n", extTeam.String(), t.String()) + } } } - console.Successf("Found %d teams with existing match and %d teams to be created\n", len(matchedTeams), len(toCreateTeams)) - for _, team := range toCreateTeams { - if err := provider.PopulateTeamMembers(ctx, team); err != nil { - return nil, fmt.Errorf("unable to populate members of '%s': %w", team.String(), err) - } - // TODO: populate schedules + allTeams, err := store.Query.ListExtTeams(ctx) + if err != nil { + return fmt.Errorf("unable to list all teams: %w", err) } - for _, team := range matchedTeams { - if err := provider.PopulateTeamMembers(ctx, team); err != nil { - return nil, fmt.Errorf("unable to populate members of '%s': %w", team.String(), err) + for _, extTeam := range allTeams { + t := &pager.Team{ + Resource: pager.Resource{ + ID: extTeam.ID, + Name: extTeam.Name, + }, + Slug: extTeam.Slug, + } + if err := provider.PopulateTeamMembers(ctx, t); err != nil { + return fmt.Errorf("unable to populate team members for '%s': %w", extTeam.Name, err) + } + + for _, member := range t.Members { + if err := store.Query.InsertExtMembership(ctx, store.InsertExtMembershipParams{ + TeamID: extTeam.ID, + UserID: member.ID, + }); err != nil { + return fmt.Errorf("unable to insert team member '%s' into database: %w", member.String(), err) + } } - // TODO: populate schedules } - // TODO: write to Terraform - return nil, nil + return nil } -func importUsers(ctx context.Context, tfr *tfrender.TFRender, provider pager.Pager, fh *pager.FireHydrant) (map[string]*pager.User, error) { +func importUsers(ctx context.Context, provider pager.Pager, fh *pager.FireHydrant) error { // Get all of the users registered from Pager Provider (e.g. PagerDuty) var err error var providerUsers []*pager.User @@ -154,21 +177,30 @@ func importUsers(ctx context.Context, tfr *tfrender.TFRender, provider pager.Pag providerUsers, err = provider.ListUsers(ctx) }, "Fetching all users from provider...") if err != nil { - return nil, fmt.Errorf("unable to fetch users from provider: %w", err) + return fmt.Errorf("unable to fetch users from provider: %w", err) } console.Successf("Found %d users from provider.\n", len(providerUsers)) + for _, user := range providerUsers { + if err := store.Query.InsertExtUser(ctx, store.InsertExtUserParams{ + ID: user.ID, + Name: user.Name, + Email: user.Email, + FhUserID: sql.NullString{Valid: false}, + }); err != nil { + return fmt.Errorf("unable to insert user '%s' into database: %w", user.Email, err) + } + } // Find out which users do not already have a FireHydrant account - var users map[string]*pager.User var unmatchedUsers []*pager.User console.Spin(func() { - users, unmatchedUsers, err = fh.MatchUsers(ctx, providerUsers) + unmatchedUsers, err = fh.MatchUsers(ctx, providerUsers) if err != nil { return } }, "Matching users with existing FireHydrant users...") if err != nil { - return nil, fmt.Errorf("unable to match users to FireHydrant: %w", err) + return fmt.Errorf("unable to match users to FireHydrant: %w", err) } // Prompt console to match users manually if necessary. @@ -176,31 +208,29 @@ func importUsers(ctx context.Context, tfr *tfrender.TFRender, provider pager.Pag console.Warnf("Found %d users which require manual mapping to FireHydrant.\n", len(unmatchedUsers)) options, err := fh.ListUsers(ctx) if err != nil { - return nil, fmt.Errorf("unable to list users from FireHydrant: %w", err) + return fmt.Errorf("unable to list users from FireHydrant: %w", err) } console.Warnf("Please select from %d FireHydrant users to match.\n", len(options)) // Prepend options with a choice to skip options = append([]*pager.User{&pager.User{Email: "[<] Skip"}}, options...) - for _, user := range unmatchedUsers { - i, u, err := console.Selectf(options, func(u *pager.User) string { + for _, extUser := range unmatchedUsers { + i, fhUser, err := console.Selectf(options, func(u *pager.User) string { return u.String() - }, "Select a FireHydrant user to match with '%s'", user.String()) + }, "Select a FireHydrant user to match with '%s'", extUser.String()) if err != nil { - return nil, fmt.Errorf("selecting match for '%s': %w", user.String(), err) + return fmt.Errorf("selecting match for '%s': %w", extUser.String(), err) } if i == 0 { - console.Warnf("[SKIPPED] '%s' will not be imported to FireHydrant.\n", user.String()) + console.Warnf("[< SKIPPED] '%s' will not be imported to FireHydrant.\n", extUser.String()) continue } - console.Infof("[MATCHED] '%s' => '%s'.\n", user.String(), u.String()) - users[user.Email] = u + if err := fh.PairUsers(ctx, fhUser.ID, extUser.ID); err != nil { + return fmt.Errorf("pairing '%s' with '%s': %w", extUser.String(), fhUser.String(), err) + } else { + console.Successf("[= MATCHED]\n '%s'\n => '%s'.\n", extUser.String(), fhUser.String()) + } } } - - // Render user information to TFRender. - if err := tfr.DataFireHydrantUsers(users); err != nil { - return nil, fmt.Errorf("writing users to Terraform: %w", err) - } - return users, nil + return nil } diff --git a/go.mod b/go.mod index 9090138..14240de 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/urfave/cli/v2 v2.27.1 github.com/victorops/go-victorops v1.0.7 github.com/zclconf/go-cty v1.13.0 + modernc.org/sqlite v1.29.5 ) // Remove once PR is merged: https://github.com/firehydrant/terraform-provider-firehydrant/pull/151 @@ -32,12 +33,15 @@ require ( github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/dghubble/sling v1.4.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.5.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.2.1 // indirect github.com/hashicorp/go-retryablehttp v0.5.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/terraform-plugin-log v0.7.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect @@ -50,16 +54,24 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/stretchr/testify v1.9.0 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/mod v0.13.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/term v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.17.0 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.41.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index 40d3db3..ceef778 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ 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/dghubble/sling v1.4.0 h1:/n8MRosVTthvMbwlNZgLx579OGVjUOy3GNEv5BIqAWY= github.com/dghubble/sling v1.4.0/go.mod h1:0r40aNsU9EdDUVBNhfCstAtFgutjgJGYbO1oNzkMoM8= +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/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= @@ -46,6 +48,10 @@ 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/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +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.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gosimple/slug v1.14.0 h1:RtTL/71mJNDfpUbCOmnf/XFkzKRtD6wL6Uy+3akm4Es= github.com/gosimple/slug v1.14.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= @@ -57,6 +63,8 @@ github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8Ym github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-retryablehttp v0.5.1 h1:Vsx5XKPqPs3M6sM4U4GWyUqFS8aBiL9U5gkgvpkg4SE= github.com/hashicorp/go-retryablehttp v0.5.1/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4= github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk= github.com/hashicorp/terraform-plugin-log v0.7.0 h1:SDxJUyT8TwN4l5b5/VkiTIaQgY6R+Y2BQ0sRZftGKQs= @@ -84,6 +92,8 @@ github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+Ei github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -96,6 +106,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.22 h1:0h+YoXSyipf6XQGyIaDg6z5jwRik1JSm+sQetnD7vGY= github.com/opsgenie/opsgenie-go-sdk-v2 v1.2.22/go.mod h1:4OjcxgwdXzezqytxN534MooNmrxRD50geWZxTD7845s= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -103,6 +115,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= @@ -130,8 +144,8 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0= github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -155,10 +169,26 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk= +modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY= +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/sqlite v1.29.5 h1:8l/SQKAjDtZFo9lkJLdk8g9JEOeYRG4/ghStDCCTiTE= +modernc.org/sqlite v1.29.5/go.mod h1:S02dvcmm7TnTRvGhv8IGYyLnIt7AS2KPaB1F/71p75U= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/pager/firehydrant.go b/pager/firehydrant.go index df7ce12..7ffbd9f 100644 --- a/pager/firehydrant.go +++ b/pager/firehydrant.go @@ -2,9 +2,10 @@ package pager import ( "context" + "database/sql" "fmt" - "log" + "github.com/firehydrant/signals-migrator/store" "github.com/firehydrant/terraform-provider-firehydrant/firehydrant" ) @@ -28,14 +29,26 @@ func NewFireHydrant(apiKey string, apiURL string) (*FireHydrant, error) { func (f *FireHydrant) ListTeams(ctx context.Context) ([]*Team, error) { teams := []*Team{} - opts := &firehydrant.TeamQuery{} + stored, err := store.Query.ListFhTeams(ctx) + if err == nil && len(stored) > 0 { + for _, t := range stored { + teams = append(teams, &Team{ + Slug: t.Slug, + Resource: Resource{ + ID: t.ID, + Name: t.Name, + }, + }) + } + return teams, nil + } + opts := &firehydrant.TeamQuery{} for { resp, err := f.client.Teams().List(ctx, opts) if err != nil { return nil, fmt.Errorf("fetching teams from FireHydrant: %w", err) } - log.Printf("%+v", resp.Pagination) for _, t := range resp.Teams { teams = append(teams, f.toTeam(t)) } @@ -44,6 +57,16 @@ func (f *FireHydrant) ListTeams(ctx context.Context) ([]*Team, error) { } opts.Page = resp.Pagination.Next } + + for _, t := range teams { + if err := store.Query.InsertFhTeam(ctx, store.InsertFhTeamParams{ + ID: t.ID, + Name: t.Name, + Slug: t.Slug, + }); err != nil { + return nil, fmt.Errorf("storing teams to database: %w", err) + } + } return teams, nil } @@ -58,25 +81,25 @@ func (f *FireHydrant) toTeam(team firehydrant.TeamResponse) *Team { } } -// TODO(wilsonehusin): cache / memoize responses -func (f *FireHydrant) FetchUsers(ctx context.Context) (map[string]*User, error) { - users, err := f.ListUsers(ctx) - if err != nil { - return nil, fmt.Errorf("fetching users from FireHydrant: %w", err) - } - usersByEmail := map[string]*User{} - for _, u := range users { - usersByEmail[u.Email] = u - } - return usersByEmail, nil -} - // ListUsers retrieves all users from within a FireHydrant organization, based on // the provided API key access. func (f *FireHydrant) ListUsers(ctx context.Context) ([]*User, error) { users := []*User{} - opts := firehydrant.GetUserParams{} + stored, err := store.Query.ListFhUsers(ctx) + if err == nil && len(stored) > 0 { + for _, u := range stored { + users = append(users, &User{ + Email: u.Email, + Resource: Resource{ + ID: u.ID, + Name: u.Name, + }, + }) + } + return users, nil + } + opts := firehydrant.GetUserParams{} for { resp, err := f.client.GetUsers(ctx, opts) if err != nil { @@ -90,6 +113,16 @@ func (f *FireHydrant) ListUsers(ctx context.Context) ([]*User, error) { } opts.Page = resp.Pagination.Next } + + for _, u := range users { + if err := store.Query.InsertFhUser(ctx, store.InsertFhUserParams{ + ID: u.ID, + Email: u.Email, + Name: u.Name, + }); err != nil { + return nil, fmt.Errorf("storing users to database: %w", err) + } + } return users, nil } @@ -104,25 +137,32 @@ func (f *FireHydrant) toUser(user firehydrant.User) *User { } // MatchUsers attempts to pair users in the parameter with its FireHydrant User counterpart. -// Returns: -// - mapping of user's email to their FireHydrant User counterpart. -// - a list of users which were not successfully matched. -func (f *FireHydrant) MatchUsers(ctx context.Context, users []*User) (map[string]*User, []*User, error) { - fhUsers, err := f.FetchUsers(ctx) +// Returns: a list of users which were not successfully matched. +func (f *FireHydrant) MatchUsers(ctx context.Context, users []*User) ([]*User, error) { + // Calling ListUsers just to make sure DB store exists. + _, err := f.ListUsers(ctx) if err != nil { - return nil, nil, fmt.Errorf("fetching users from FireHydrant: %w", err) + return nil, fmt.Errorf("fetching FireHydrant users: %w", err) } - matchedUsers := map[string]*User{} unmatchedUsers := []*User{} - for _, user := range users { - if u, ok := fhUsers[user.Email]; ok { - matchedUsers[user.Email] = u + fhUser, err := store.Query.GetFhUserByEmail(ctx, user.Email) + if err == nil { + if err := f.PairUsers(ctx, fhUser.ID, user.ID); err != nil { + return nil, fmt.Errorf("pairing users: %w", err) + } } else { unmatchedUsers = append(unmatchedUsers, user) } } - return matchedUsers, unmatchedUsers, nil + return unmatchedUsers, nil +} + +func (f *FireHydrant) PairUsers(ctx context.Context, fhUserID string, extUserID string) error { + return store.Query.LinkExtUser(ctx, store.LinkExtUserParams{ + FhUserID: sql.NullString{Valid: true, String: fhUserID}, + ID: extUserID, + }) } diff --git a/pager/resource.go b/pager/resource.go index 03e621c..2a86d47 100644 --- a/pager/resource.go +++ b/pager/resource.go @@ -1,9 +1,11 @@ package pager +// TODO: [noted with apologies] +// This file existed before the pivot to using SQLite. Now that we're using +// SQLite, we can instead make use of store/models.go instead of this file. + import ( "fmt" - "strings" - "time" "github.com/gosimple/slug" ) @@ -47,10 +49,6 @@ func (t *Team) String() string { return fmt.Sprintf("%s %s (%s)", t.Resource.ID, t.Resource.Name, t.Slug) } -func (t *Team) TFSlug() string { - return strings.ReplaceAll(t.Slug, "-", "_") -} - // User refers to a user within a Pager service, may also be referred // to as member or contact. type User struct { @@ -73,27 +71,3 @@ func (u *User) String() string { func (u *User) Slug() string { return slug.Make(u.Email) } - -func (u *User) TFSlug() string { - return strings.ReplaceAll(u.Slug(), "-", "_") -} - -// ScheduleStrategy refers to the strategy used to determine the scheduling -// order of users within a team. -type ScheduleStrategy int - -//go:generate stringer -type=ScheduleStrategy -const ( - Daily ScheduleStrategy = iota - Weekly - Fortnightly -) - -// Schedule refers to collection of on-call shifts within a team. It dictates -// the parameters on how shifts are scheduled automatically by pager provider. -type Schedule struct { - Strategy ScheduleStrategy - TimeZone string - HandoffTime time.Time - HandoffDay time.Weekday -} diff --git a/sqlc.go b/sqlc.go new file mode 100644 index 0000000..656ee5c --- /dev/null +++ b/sqlc.go @@ -0,0 +1,3 @@ +package main + +//go:generate sqlc generate diff --git a/sqlc.yaml b/sqlc.yaml new file mode 100644 index 0000000..3758f03 --- /dev/null +++ b/sqlc.yaml @@ -0,0 +1,10 @@ +version: "2" +sql: + - schema: "store/schema.sql" + queries: "store/queries.sql" + engine: sqlite + gen: + go: + package: "store" + out: "store" + emit_json_tags: true diff --git a/store/db.go b/store/db.go new file mode 100644 index 0000000..e31caef --- /dev/null +++ b/store/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 + +package store + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/store/model_attr.go b/store/model_attr.go new file mode 100644 index 0000000..9626737 --- /dev/null +++ b/store/model_attr.go @@ -0,0 +1,52 @@ +package store + +import ( + "strings" + + "github.com/gosimple/slug" +) + +func (r *ListExtTeamsRow) ExtTeam() *ExtTeam { + return &ExtTeam{ + ID: r.ID, + Name: r.Name, + Slug: r.Slug, + } +} + +func (r *ListExtTeamsRow) FhTeam() *FhTeam { + var id, name, slug string + if r.FhTeamID.Valid { + id = r.FhTeamID.String + } + if r.FhTeamName.Valid { + name = r.FhTeamName.String + } + if r.FhTeamSlug.Valid { + slug = r.FhTeamSlug.String + } + return &FhTeam{ + ID: id, + Name: name, + Slug: slug, + } +} + +func (t *ExtTeam) TFSlug() string { + if t.Slug == "" { + return strings.ReplaceAll(slug.Make(t.Name), "-", "_") + } + return strings.ReplaceAll(t.Slug, "-", "_") +} + +func (t *FhTeam) TFSlug() string { + if t.Slug == "" { + return strings.ReplaceAll(slug.Make(t.Name), "-", "_") + } + return strings.ReplaceAll(t.Slug, "-", "_") +} + +func (u *FhUser) TFSlug() string { + username := strings.Split(u.Email, "@")[0] + return strings.ReplaceAll(slug.Make(username), "-", "_") +} diff --git a/store/models.go b/store/models.go new file mode 100644 index 0000000..c9aeafe --- /dev/null +++ b/store/models.go @@ -0,0 +1,40 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 + +package store + +import ( + "database/sql" +) + +type ExtMembership struct { + UserID string `json:"user_id"` + TeamID string `json:"team_id"` +} + +type ExtTeam struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + FhTeamID sql.NullString `json:"fh_team_id"` +} + +type ExtUser struct { + ID string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + FhUserID sql.NullString `json:"fh_user_id"` +} + +type FhTeam struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` +} + +type FhUser struct { + ID string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` +} diff --git a/store/open.go b/store/open.go new file mode 100644 index 0000000..f743395 --- /dev/null +++ b/store/open.go @@ -0,0 +1,28 @@ +//go:build !dev + +package store + +import ( + "context" + "database/sql" + "time" +) + +func openDB() *Queries { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + db, err := sql.Open("sqlite", ":memory:") + if err != nil { + panic(err) + } + _, err = db.ExecContext(ctx, `PRAGMA foreign_keys = true;`) + if err != nil { + panic(err) + } + _, err = db.ExecContext(ctx, schema) + if err != nil { + panic(err) + } + return New(db) +} diff --git a/store/open_dev.go b/store/open_dev.go new file mode 100644 index 0000000..20e0843 --- /dev/null +++ b/store/open_dev.go @@ -0,0 +1,81 @@ +//go:build dev + +package store + +import ( + "context" + "database/sql" + "log" + "os" + "path/filepath" + "strings" + "time" + + "github.com/fatih/color" +) + +type loggedQueries struct { + q *sql.DB +} + +func (q *loggedQueries) log(t time.Duration, queryStr string) { + qInfo := strings.SplitN(queryStr, "\n", 2) + name := strings.TrimSpace(qInfo[0]) + query := "" + if len(qInfo) > 1 { + query = " " + strings.ReplaceAll(qInfo[1], "\n", "\n ") + } + log.Printf( + "%s %s\n%s", + color.HiCyanString("%s", t.String()), + color.HiMagentaString("%s", name), + color.WhiteString("%s", query), + ) +} + +func (q *loggedQueries) ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error) { + t := time.Now() + defer func() { q.log(time.Since(t), query) }() + return q.q.ExecContext(ctx, query, args...) +} + +func (q *loggedQueries) PrepareContext(ctx context.Context, query string) (*sql.Stmt, error) { + t := time.Now() + defer func() { q.log(time.Since(t), query) }() + return q.q.PrepareContext(ctx, query) +} + +func (q *loggedQueries) QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error) { + t := time.Now() + defer func() { q.log(time.Since(t), query) }() + return q.q.QueryContext(ctx, query, args...) +} + +func (q *loggedQueries) QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row { + t := time.Now() + defer func() { q.log(time.Since(t), query) }() + return q.q.QueryRowContext(ctx, query, args...) +} + +func openDB() *Queries { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + f := filepath.Join(os.TempDir(), "signals-migrator.db") + log.Printf("using db file %s", f) + + db, err := sql.Open("sqlite", f) + if err != nil { + panic(err) + } + dbtx := &loggedQueries{q: db} + _, err = dbtx.ExecContext(ctx, `PRAGMA foreign_keys = true;`) + if err != nil { + panic(err) + } + _, err = dbtx.ExecContext(ctx, schema) + if err != nil { + panic(err) + } + return New(dbtx) +} diff --git a/store/queries.sql b/store/queries.sql new file mode 100644 index 0000000..4570cdf --- /dev/null +++ b/store/queries.sql @@ -0,0 +1,41 @@ +-- name: ListFhUsers :many +SELECT * FROM fh_users; + +-- name: GetFhUserByEmail :one +SELECT * FROM fh_users WHERE email = ?; + +-- name: InsertFhUser :exec +INSERT INTO fh_users (id, name, email) VALUES (?, ?, ?); + +-- name: ListFhTeams :many +SELECT * FROM fh_teams; + +-- name: InsertFhTeam :exec +INSERT INTO fh_teams (id, name, slug) VALUES (?, ?, ?); + +-- name: InsertExtUser :exec +INSERT INTO ext_users (id, name, email, fh_user_id) VALUES (?, ?, ?, ?); + +-- name: ListExtTeams :many +SELECT ext_teams.*, fh_teams.name as fh_team_name, fh_teams.slug as fh_team_slug FROM ext_teams + LEFT JOIN fh_teams ON fh_teams.id = ext_teams.fh_team_id; + +-- name: InsertExtTeam :exec +INSERT INTO ext_teams (id, name, slug, fh_team_id) VALUES (?, ?, ?, ?); + +-- name: LinkExtUser :exec +UPDATE ext_users SET fh_user_id = ? WHERE id = ?; + +-- name: LinkExtTeam :exec +UPDATE ext_teams SET fh_team_id = ? WHERE id = ?; + +-- name: InsertExtMembership :exec +INSERT INTO ext_memberships (user_id, team_id) VALUES (?, ?); + +-- name: ListFhMembersByExtTeamID :many +SELECT fh_users.* FROM ext_memberships + JOIN ext_teams ON ext_teams.id = ext_memberships.team_id + JOIN ext_users ON ext_users.id = ext_memberships.user_id + JOIN fh_users ON fh_users.id = ext_users.fh_user_id + LEFT JOIN fh_teams ON fh_teams.id = ext_teams.fh_team_id +WHERE ext_teams.id = ?; diff --git a/store/queries.sql.go b/store/queries.sql.go new file mode 100644 index 0000000..d814d44 --- /dev/null +++ b/store/queries.sql.go @@ -0,0 +1,266 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.26.0 +// source: queries.sql + +package store + +import ( + "context" + "database/sql" +) + +const getFhUserByEmail = `-- name: GetFhUserByEmail :one +SELECT id, name, email FROM fh_users WHERE email = ? +` + +func (q *Queries) GetFhUserByEmail(ctx context.Context, email string) (FhUser, error) { + row := q.db.QueryRowContext(ctx, getFhUserByEmail, email) + var i FhUser + err := row.Scan(&i.ID, &i.Name, &i.Email) + return i, err +} + +const insertExtMembership = `-- name: InsertExtMembership :exec +INSERT INTO ext_memberships (user_id, team_id) VALUES (?, ?) +` + +type InsertExtMembershipParams struct { + UserID string `json:"user_id"` + TeamID string `json:"team_id"` +} + +func (q *Queries) InsertExtMembership(ctx context.Context, arg InsertExtMembershipParams) error { + _, err := q.db.ExecContext(ctx, insertExtMembership, arg.UserID, arg.TeamID) + return err +} + +const insertExtTeam = `-- name: InsertExtTeam :exec +INSERT INTO ext_teams (id, name, slug, fh_team_id) VALUES (?, ?, ?, ?) +` + +type InsertExtTeamParams struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + FhTeamID sql.NullString `json:"fh_team_id"` +} + +func (q *Queries) InsertExtTeam(ctx context.Context, arg InsertExtTeamParams) error { + _, err := q.db.ExecContext(ctx, insertExtTeam, + arg.ID, + arg.Name, + arg.Slug, + arg.FhTeamID, + ) + return err +} + +const insertExtUser = `-- name: InsertExtUser :exec +INSERT INTO ext_users (id, name, email, fh_user_id) VALUES (?, ?, ?, ?) +` + +type InsertExtUserParams struct { + ID string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` + FhUserID sql.NullString `json:"fh_user_id"` +} + +func (q *Queries) InsertExtUser(ctx context.Context, arg InsertExtUserParams) error { + _, err := q.db.ExecContext(ctx, insertExtUser, + arg.ID, + arg.Name, + arg.Email, + arg.FhUserID, + ) + return err +} + +const insertFhTeam = `-- name: InsertFhTeam :exec +INSERT INTO fh_teams (id, name, slug) VALUES (?, ?, ?) +` + +type InsertFhTeamParams struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` +} + +func (q *Queries) InsertFhTeam(ctx context.Context, arg InsertFhTeamParams) error { + _, err := q.db.ExecContext(ctx, insertFhTeam, arg.ID, arg.Name, arg.Slug) + return err +} + +const insertFhUser = `-- name: InsertFhUser :exec +INSERT INTO fh_users (id, name, email) VALUES (?, ?, ?) +` + +type InsertFhUserParams struct { + ID string `json:"id"` + Name string `json:"name"` + Email string `json:"email"` +} + +func (q *Queries) InsertFhUser(ctx context.Context, arg InsertFhUserParams) error { + _, err := q.db.ExecContext(ctx, insertFhUser, arg.ID, arg.Name, arg.Email) + return err +} + +const linkExtTeam = `-- name: LinkExtTeam :exec +UPDATE ext_teams SET fh_team_id = ? WHERE id = ? +` + +type LinkExtTeamParams struct { + FhTeamID sql.NullString `json:"fh_team_id"` + ID string `json:"id"` +} + +func (q *Queries) LinkExtTeam(ctx context.Context, arg LinkExtTeamParams) error { + _, err := q.db.ExecContext(ctx, linkExtTeam, arg.FhTeamID, arg.ID) + return err +} + +const linkExtUser = `-- name: LinkExtUser :exec +UPDATE ext_users SET fh_user_id = ? WHERE id = ? +` + +type LinkExtUserParams struct { + FhUserID sql.NullString `json:"fh_user_id"` + ID string `json:"id"` +} + +func (q *Queries) LinkExtUser(ctx context.Context, arg LinkExtUserParams) error { + _, err := q.db.ExecContext(ctx, linkExtUser, arg.FhUserID, arg.ID) + return err +} + +const listExtTeams = `-- name: ListExtTeams :many +SELECT ext_teams.id, ext_teams.name, ext_teams.slug, ext_teams.fh_team_id, fh_teams.name as fh_team_name, fh_teams.slug as fh_team_slug FROM ext_teams + LEFT JOIN fh_teams ON fh_teams.id = ext_teams.fh_team_id +` + +type ListExtTeamsRow struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + FhTeamID sql.NullString `json:"fh_team_id"` + FhTeamName sql.NullString `json:"fh_team_name"` + FhTeamSlug sql.NullString `json:"fh_team_slug"` +} + +func (q *Queries) ListExtTeams(ctx context.Context) ([]ListExtTeamsRow, error) { + rows, err := q.db.QueryContext(ctx, listExtTeams) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ListExtTeamsRow + for rows.Next() { + var i ListExtTeamsRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.Slug, + &i.FhTeamID, + &i.FhTeamName, + &i.FhTeamSlug, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listFhMembersByExtTeamID = `-- name: ListFhMembersByExtTeamID :many +SELECT fh_users.id, fh_users.name, fh_users.email FROM ext_memberships + JOIN ext_teams ON ext_teams.id = ext_memberships.team_id + JOIN ext_users ON ext_users.id = ext_memberships.user_id + JOIN fh_users ON fh_users.id = ext_users.fh_user_id + LEFT JOIN fh_teams ON fh_teams.id = ext_teams.fh_team_id +WHERE ext_teams.id = ? +` + +func (q *Queries) ListFhMembersByExtTeamID(ctx context.Context, id string) ([]FhUser, error) { + rows, err := q.db.QueryContext(ctx, listFhMembersByExtTeamID, id) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FhUser + for rows.Next() { + var i FhUser + if err := rows.Scan(&i.ID, &i.Name, &i.Email); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listFhTeams = `-- name: ListFhTeams :many +SELECT id, name, slug FROM fh_teams +` + +func (q *Queries) ListFhTeams(ctx context.Context) ([]FhTeam, error) { + rows, err := q.db.QueryContext(ctx, listFhTeams) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FhTeam + for rows.Next() { + var i FhTeam + if err := rows.Scan(&i.ID, &i.Name, &i.Slug); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listFhUsers = `-- name: ListFhUsers :many +SELECT id, name, email FROM fh_users +` + +func (q *Queries) ListFhUsers(ctx context.Context) ([]FhUser, error) { + rows, err := q.db.QueryContext(ctx, listFhUsers) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FhUser + for rows.Next() { + var i FhUser + if err := rows.Scan(&i.ID, &i.Name, &i.Email); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/store/schema.sql b/store/schema.sql new file mode 100644 index 0000000..f303914 --- /dev/null +++ b/store/schema.sql @@ -0,0 +1,33 @@ +CREATE TABLE IF NOT EXISTS fh_users ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + email TEXT NOT NULL +) STRICT; + +CREATE TABLE IF NOT EXISTS ext_users ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + email TEXT NOT NULL, + fh_user_id TEXT REFERENCES fh_users(id) +) STRICT; + +CREATE TABLE IF NOT EXISTS fh_teams ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + slug TEXT NOT NULL +) STRICT; + +CREATE TABLE IF NOT EXISTS ext_teams ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + slug TEXT NOT NULL, + fh_team_id TEXT REFERENCES fh_teams(id) +) STRICT; + +CREATE TABLE IF NOT EXISTS ext_memberships ( + user_id TEXT NOT NULL, + team_id TEXT NOT NULL, + PRIMARY KEY (user_id, team_id), + FOREIGN KEY (user_id) REFERENCES ext_users(id), + FOREIGN KEY (team_id) REFERENCES ext_teams(id) +) STRICT; diff --git a/store/store.go b/store/store.go new file mode 100644 index 0000000..7ae6137 --- /dev/null +++ b/store/store.go @@ -0,0 +1,12 @@ +package store + +import ( + _ "embed" + + _ "modernc.org/sqlite" +) + +//go:embed schema.sql +var schema string + +var Query = openDB() diff --git a/tfrender/tfrender.go b/tfrender/tfrender.go index 725a8b1..c1e4e11 100644 --- a/tfrender/tfrender.go +++ b/tfrender/tfrender.go @@ -1,10 +1,12 @@ package tfrender import ( + "context" "fmt" "os" - "github.com/firehydrant/signals-migrator/pager" + "github.com/firehydrant/signals-migrator/store" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/zclconf/go-cty/cty" ) @@ -26,6 +28,7 @@ func New(dir string) (*TFRender, error) { f := hclwrite.NewEmptyFile() root := f.Body() provider := root.AppendNewBlock("terraform", nil).Body().AppendNewBlock("required_providers", nil).Body() + // TODO: add provider information return &TFRender{ f: f, @@ -35,15 +38,101 @@ func New(dir string) (*TFRender, error) { }, nil } -func (r *TFRender) DataFireHydrantUsers(users map[string]*pager.User) error { - for _, u := range users { - if u == nil { - continue +func (r *TFRender) Write(ctx context.Context) error { + f, err := os.Create(r.dir + "/fh_imported.tf") + if err != nil { + return fmt.Errorf("creating file: %w", err) + } + defer f.Close() + + if err := r.DataFireHydrantUsers(ctx); err != nil { + return fmt.Errorf("rendering user block: %w", err) + } + + if err := r.ResourceFireHydrantTeams(ctx); err != nil { + return fmt.Errorf("rendering team block: %w", err) + } + + if _, err := f.Write(r.f.Bytes()); err != nil { + return fmt.Errorf("writing file: %w", err) + } + + return nil +} + +func (r *TFRender) ResourceFireHydrantTeams(ctx context.Context) error { + extTeams, err := store.Query.ListExtTeams(ctx) + if err != nil { + return fmt.Errorf("querying teams: %w", err) + } + + // Use hashmap to deduplicate import and membership. + // There is probably a smarter way to do it in SQL, this just so happen to be easy and convenient. + importedTeams := map[string]bool{} + importedMembership := map[string]bool{} + + fhTeamBlocks := map[string]*hclwrite.Body{} + for _, t := range extTeams { + // FireHydrant team name takes precedence as we need it to match existing whenever possible. + name := t.FhTeam().Name + tfSlug := t.FhTeam().TFSlug() + if name == "" { + name = t.ExtTeam().Name + tfSlug = t.ExtTeam().TFSlug() + } + if _, ok := fhTeamBlocks[name]; !ok { + r.root.AppendNewline() + fhTeamBlocks[name] = r.root.AppendNewBlock("resource", []string{"firehydrant_team", tfSlug}).Body() + fhTeamBlocks[name].SetAttributeValue("name", cty.StringVal(name)) + } + + members, err := store.Query.ListFhMembersByExtTeamID(ctx, t.ExtTeam().ID) + if err != nil { + return fmt.Errorf("querying team members: %w", err) } + for _, m := range members { + if importedMembership[tfSlug+m.TFSlug()] { + continue + } - block := r.root.AppendNewBlock("data", []string{"firehydrant_user", u.TFSlug()}).Body() - block.SetAttributeValue("email", cty.StringVal(u.Email)) + b := fhTeamBlocks[name] + b.AppendNewline() + b.AppendNewBlock("membership", []string{}).Body(). + SetAttributeTraversal("id", hcl.Traversal{ + hcl.TraverseRoot{Name: "data"}, + hcl.TraverseAttr{Name: "firehydrant_user"}, + hcl.TraverseAttr{Name: m.TFSlug()}, + hcl.TraverseAttr{Name: "id"}, + }) + importedMembership[tfSlug+m.TFSlug()] = true + } + + // If there is an existing FireHydrant team already, declare import to prevent duplication. + if t.FhTeamID.Valid && t.FhTeamID.String != "" && !importedTeams[t.FhTeamID.String] { + r.root.AppendNewline() + importBody := r.root.AppendNewBlock("import", []string{}).Body() + importBody.SetAttributeValue("id", cty.StringVal(t.FhTeamID.String)) + importBody.SetAttributeTraversal("to", hcl.Traversal{ + hcl.TraverseRoot{Name: "resource"}, + hcl.TraverseAttr{Name: "firehydrant_team"}, + hcl.TraverseAttr{Name: tfSlug}, + hcl.TraverseAttr{Name: "id"}, + }) + importedTeams[t.FhTeamID.String] = true + } + } + return nil +} + +func (r *TFRender) DataFireHydrantUsers(ctx context.Context) error { + users, err := store.Query.ListFhUsers(ctx) + if err != nil { + return fmt.Errorf("querying users: %w", err) + } + for _, u := range users { r.root.AppendNewline() + b := r.root.AppendNewBlock("data", []string{"firehydrant_user", u.TFSlug()}).Body() + b.SetAttributeValue("email", cty.StringVal(u.Email)) } return nil }