From 9e6c7d23a345197deb6654067bf6110ecaf37857 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Wed, 14 Feb 2024 22:31:04 -0500 Subject: [PATCH 01/17] Pin lint workflow's Go version (#311) We weren't pinning the Go version for golangci-lint (only the golangci-lint version); seemingly it breaks on 1.21+. Now we're using a new version making it easy to pin back to 1.20. Later I'll look at why 1.21+ are failing (#313). While I was at it I added tests on 1.21 (1.22+ is failing, again will look later, #312), and upgraded the setup-go action itself. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable (n/a) - [x] Included a link to the issue fixed, if applicable (n/a) - [x] Included documentation, for new features (n/a) - [x] Added an entry to the changelog (n/a) --- .github/workflows/go.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 8faa3cc9..5fccda16 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,11 +12,11 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.18', '1.19', '1.20' ] + go: [ '1.18', '1.19', '1.20', '1.21' ] steps: - name: Set up Go - uses: actions/setup-go@v1 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go }} id: go @@ -36,10 +36,16 @@ jobs: runs-on: ubuntu-latest steps: + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.20' + id: go + - name: Git checkout uses: actions/checkout@v2 - name: Run lint - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 with: version: v1.52.2 # should match internal/lint/go.mod From 20a8c5ec38b5d3b9a219781521e8387a7be1b1e8 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Thu, 15 Feb 2024 13:11:48 -0500 Subject: [PATCH 02/17] Upgrade golangci-lint and which go we run it with (#314) Apparently our version of golangci-lint didn't work with Go 1.21+ on our codebase. A newer version does, which required redoing the `depguard` config (I think it's equivalent to what we had) and one tweak for the benefit of `gocritic`. Fixes #313. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable (n/a) - [x] Included a link to the issue fixed, if applicable - [x] Included documentation, for new features (n/a) - [x] Added an entry to the changelog (n/a) --- .github/workflows/go.yml | 4 +- .golangci.yml | 18 +- generate/generate.go | 2 +- internal/lint/go.mod | 145 ++++++++------- internal/lint/go.sum | 379 +++++++++++++++++++-------------------- 5 files changed, 277 insertions(+), 271 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 5fccda16..a0d704e2 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -39,7 +39,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.22' id: go - name: Git checkout @@ -48,4 +48,4 @@ jobs: - name: Run lint uses: golangci/golangci-lint-action@v3 with: - version: v1.52.2 # should match internal/lint/go.mod + version: v1.56.1 # should match internal/lint/go.mod diff --git a/.golangci.yml b/.golangci.yml index 2076d4b1..095c6226 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -42,13 +42,17 @@ linters-settings: # We have a ton of test-only packages; but make sure we keep prod deps small. depguard: - list-type: whitelist - packages: - - github.com/Khan/genqlient - - github.com/vektah/gqlparser/v2 - - golang.org/x/tools - - gopkg.in/yaml.v2 - - github.com/alexflint/go-arg + rules: + main: + list-mode: strict + files: $all + allow: + - $gostd + - github.com/Khan/genqlient + - github.com/vektah/gqlparser/v2 + - golang.org/x/tools + - gopkg.in/yaml.v2 + - github.com/alexflint/go-arg forbidigo: forbid: diff --git a/generate/generate.go b/generate/generate.go index 22ee618d..cc39c8e4 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -279,7 +279,7 @@ func (g *generator) addOperation(op *ast.OperationDefinition) error { } var docComment string - if len(commentLines) > 0 { + if commentLines != "" { docComment = "// " + strings.ReplaceAll(commentLines, "\n", "\n// ") } diff --git a/internal/lint/go.mod b/internal/lint/go.mod index bb44cdc8..766f4b4e 100644 --- a/internal/lint/go.mod +++ b/internal/lint/go.mod @@ -1,47 +1,57 @@ module github.com/Khan/genqlient/internal/lint -go 1.18 +go 1.21 + +toolchain go1.22.0 // Should match golangci-lint version in .github/workflows/go.yml -require github.com/golangci/golangci-lint v1.52.2 +require github.com/golangci/golangci-lint v1.56.1 require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect - github.com/Abirdcfly/dupword v0.0.11 // indirect - github.com/Antonboom/errname v0.1.9 // indirect - github.com/Antonboom/nilnil v0.1.3 // indirect - github.com/BurntSushi/toml v1.2.1 // indirect + github.com/4meepo/tagalign v1.3.3 // indirect + github.com/Abirdcfly/dupword v0.0.13 // indirect + github.com/Antonboom/errname v0.1.12 // indirect + github.com/Antonboom/nilnil v0.1.7 // indirect + github.com/Antonboom/testifylint v1.1.1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/OpenPeeDeeP/depguard v1.1.1 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect + github.com/alecthomas/go-check-sumtype v0.1.4 // indirect + github.com/alexkohler/nakedret/v2 v2.0.2 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect - github.com/ashanbrown/forbidigo v1.5.1 // 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 - github.com/bkielbasa/cyclop v1.2.0 // indirect + github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v3 v3.4.0 // indirect - github.com/breml/bidichk v0.2.4 // indirect - github.com/breml/errchkjson v0.3.1 // indirect - github.com/butuzov/ireturn v0.1.1 // indirect + github.com/bombsimon/wsl/v4 v4.2.0 // indirect + github.com/breml/bidichk v0.2.7 // indirect + github.com/breml/errchkjson v0.3.6 // indirect + github.com/butuzov/ireturn v0.3.0 // indirect + github.com/butuzov/mirror v1.1.0 // indirect + github.com/catenacyber/perfsprint v0.6.0 // indirect + github.com/ccojocar/zxcvbn-go v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/charithe/durationcheck v0.0.10 // indirect - github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect + github.com/chavacava/garif v0.1.0 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect - github.com/daixiang0/gci v0.10.1 // indirect + github.com/daixiang0/gci v0.12.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denis-tingaikin/go-header v0.4.3 // indirect github.com/esimonov/ifshort v1.0.4 // indirect - github.com/ettle/strcase v0.1.1 // indirect - github.com/fatih/color v1.15.0 // 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 github.com/firefart/nonamedreturns v1.0.4 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/go-critic/go-critic v0.7.0 // indirect + github.com/ghostiam/protogetter v0.3.4 // indirect + github.com/go-critic/go-critic v0.11.0 // 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.1.0 // indirect @@ -49,131 +59,136 @@ require ( github.com/go-toolsmith/astp v1.1.0 // indirect github.com/go-toolsmith/strparse v1.1.0 // indirect github.com/go-toolsmith/typep v1.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect 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.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect - github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect + github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e // indirect github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect - github.com/golangci/misspell v0.4.0 // indirect - github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect + github.com/golangci/misspell v0.4.1 // indirect + github.com/golangci/revgrep v0.5.2 // indirect github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // indirect + github.com/google/go-cmp v0.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/errwrap v1.0.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect - github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/jgautheron/goconst v1.5.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jgautheron/goconst v1.7.0 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect + github.com/jjti/go-spancheck v0.5.2 // indirect github.com/julz/importas v0.1.0 // indirect - github.com/junk1tm/musttag v0.5.0 // indirect - github.com/kisielk/errcheck v1.6.3 // indirect + github.com/kisielk/errcheck v1.7.0 // indirect github.com/kisielk/gotool v1.0.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.6 // indirect + github.com/kunwardeep/paralleltest v1.0.9 // indirect github.com/kyoh86/exportloopref v0.1.11 // indirect github.com/ldez/gomoddirectives v0.2.3 // indirect - github.com/ldez/tagliatelle v0.4.0 // indirect + github.com/ldez/tagliatelle v0.5.0 // indirect github.com/leonklingele/grouper v1.1.1 // indirect github.com/lufeee/execinquery v1.2.1 // indirect + github.com/macabu/inamedparam v0.1.3 // indirect github.com/magiconair/properties v1.8.6 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect - github.com/mgechev/revive v1.3.1 // indirect + github.com/mgechev/revive v1.3.7 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect 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/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect - github.com/nishanths/exhaustive v0.9.5 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.9.0 // indirect + github.com/nunnatsa/ginkgolinter v0.15.2 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.0.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/polyfloyd/go-errorlint v1.4.0 // indirect + github.com/polyfloyd/go-errorlint v1.4.8 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/quasilyte/go-ruleguard v0.3.19 // indirect + github.com/quasilyte/go-ruleguard v0.4.0 // indirect 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/ryancurrah/gomodguard v1.3.0 // indirect - github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect + github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect - github.com/securego/gosec/v2 v2.15.0 // indirect + github.com/sashamelentyev/usestdlibvars v1.24.0 // indirect + github.com/securego/gosec/v2 v2.18.2 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect - github.com/sirupsen/logrus v1.9.0 // indirect - github.com/sivchari/containedctx v1.0.2 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/sivchari/containedctx v1.0.3 // indirect github.com/sivchari/nosnakecase v1.7.0 // indirect github.com/sivchari/tenv v1.7.1 // indirect github.com/sonatard/noctx v0.0.2 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect - github.com/spf13/afero v1.8.2 // indirect + github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.6.1 // indirect + github.com/spf13/cobra v1.7.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.12.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect github.com/stretchr/objx v0.5.0 // indirect - github.com/stretchr/testify v1.8.2 // indirect + github.com/stretchr/testify v1.8.4 // indirect github.com/subosito/gotenv v1.4.1 // 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.11 // indirect - github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e // indirect + github.com/tetafro/godot v1.4.16 // 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.1 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect - github.com/ultraware/funlen v0.0.3 // indirect - github.com/ultraware/whitespace v0.0.5 // indirect - github.com/uudashr/gocognit v1.0.6 // indirect + 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/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect - gitlab.com/bosi/decorder v0.2.3 // indirect + github.com/ykadowak/zerologlint v0.1.5 // indirect + gitlab.com/bosi/decorder v0.4.1 // indirect + go-simpler.org/musttag v0.8.0 // indirect + go-simpler.org/sloglint v0.4.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 // indirect - golang.org/x/mod v0.9.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.7.0 // indirect - golang.org/x/tools v0.7.0 // indirect - google.golang.org/protobuf v1.28.0 // indirect + golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc // indirect + golang.org/x/exp/typeparams v0.0.0-20231219180239-dc181d75b848 // indirect + golang.org/x/mod v0.15.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.17.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - honnef.co/go/tools v0.4.3 // indirect - mvdan.cc/gofumpt v0.4.0 // indirect + honnef.co/go/tools v0.4.6 // indirect + mvdan.cc/gofumpt v0.6.0 // indirect mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect - mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d // indirect + mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 // indirect ) diff --git a/internal/lint/go.sum b/internal/lint/go.sum index 723b435a..7ad45de9 100644 --- a/internal/lint/go.sum +++ b/internal/lint/go.sum @@ -7,7 +7,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= @@ -18,9 +17,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -38,76 +34,92 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Abirdcfly/dupword v0.0.11 h1:z6v8rMETchZXUIuHxYNmlUAuKuB21PeaSymTed16wgU= -github.com/Abirdcfly/dupword v0.0.11/go.mod h1:wH8mVGuf3CP5fsBTkfWwwwKTjDnVVCxtU8d8rgeVYXA= -github.com/Antonboom/errname v0.1.9 h1:BZDX4r3l4TBZxZ2o2LNrlGxSHran4d1u4veZdoORTT4= -github.com/Antonboom/errname v0.1.9/go.mod h1:nLTcJzevREuAsgTbG85UsuiWpMpAqbKD1HNZ29OzE58= -github.com/Antonboom/nilnil v0.1.3 h1:6RTbx3d2mcEu3Zwq9TowQpQMVpP75zugwOtqY1RTtcE= -github.com/Antonboom/nilnil v0.1.3/go.mod h1:iOov/7gRcXkeEU+EMGpBu2ORih3iyVEiWjeste1SJm8= +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.13 h1:SMS17YXypwP000fA7Lr+kfyBQyW14tTT+nRv9ASwUUo= +github.com/Abirdcfly/dupword v0.0.13/go.mod h1:Ut6Ue2KgF/kCOawpW4LnExT+xZLQviJPE4klBPMK/5Y= +github.com/Antonboom/errname v0.1.12 h1:oh9ak2zUtsLp5oaEd/erjB4GPu9w19NyoIskZClDcQY= +github.com/Antonboom/errname v0.1.12/go.mod h1:bK7todrzvlaZoQagP1orKzWXv59X/x0W0Io2XT1Ssro= +github.com/Antonboom/nilnil v0.1.7 h1:ofgL+BA7vlA1K2wNQOsHzLJ2Pw5B5DpWRLdDAVvvTow= +github.com/Antonboom/nilnil v0.1.7/go.mod h1:TP+ScQWVEq0eSIxqU8CbdT5DFWoHp0MbP+KMUO1BKYQ= +github.com/Antonboom/testifylint v1.1.1 h1:xCxYDNOBLImTKjBKPGtx1cHkTSywDAn76mYHTwH5lG8= +github.com/Antonboom/testifylint v1.1.1/go.mod h1:9PFi+vWa8zzl4/B/kqmFJcw85ZUv8ReyBzuQCd30+WI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 h1:+r1rSv4gvYn0wmRjC8X7IAzX8QezqtFV9m0MUHFJgts= -github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0/go.mod h1:b3g59n2Y+T5xmcxJL+UEG2f8cQploZm1mR/v6BW0mU0= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 h1:sATXp1x6/axKxz2Gjxv8MALP0bXaNRfQinEwyfMcx8c= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0/go.mod h1:Nl76DrGNJTA1KJ0LePKBw/vznBX1EHbAZX8mwjR82nI= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/OpenPeeDeeP/depguard v1.1.1 h1:TSUznLjvp/4IUP+OQ0t/4jF4QUyxIcVX8YnghZdunyA= -github.com/OpenPeeDeeP/depguard v1.1.1/go.mod h1:JtAMzWkmFEzDPyAd+W0NHl1lvpQKTvT9jnRVsohBKpc= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= +github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk= +github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= +github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alexkohler/nakedret/v2 v2.0.2 h1:qnXuZNvv3/AxkAb22q/sEsEpcA99YxLFACDtEw9TPxE= +github.com/alexkohler/nakedret/v2 v2.0.2/go.mod h1:2b8Gkk0GsOrqQv/gPWjNLDSKwG8I5moSXG1K4VIBcTQ= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= 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/ashanbrown/forbidigo v1.5.1 h1:WXhzLjOlnuDYPYQo/eFlcFMi8X/kLfvWLYu6CSoebis= -github.com/ashanbrown/forbidigo v1.5.1/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= +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 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +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= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bkielbasa/cyclop v1.2.0 h1:7Jmnh0yL2DjKfw28p86YTd/B4lRGcNuu12sKE35sM7A= -github.com/bkielbasa/cyclop v1.2.0/go.mod h1:qOI0yy6A7dYC4Zgsa72Ppm9kONl0RoIlPbzot9mhmeI= +github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY= +github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v3 v3.4.0 h1:RkSxjT3tmlptwfgEgTgU+KYKLI35p/tviNXNXiL2aNU= -github.com/bombsimon/wsl/v3 v3.4.0/go.mod h1:KkIB+TXkqy6MvK9BDZVbZxKNYsE1/oLRJbIFtf14qqo= -github.com/breml/bidichk v0.2.4 h1:i3yedFWWQ7YzjdZJHnPo9d/xURinSq3OM+gyM43K4/8= -github.com/breml/bidichk v0.2.4/go.mod h1:7Zk0kRFt1LIZxtQdl9W9JwGAcLTTkOs+tN7wuEYGJ3s= -github.com/breml/errchkjson v0.3.1 h1:hlIeXuspTyt8Y/UmP5qy1JocGNR00KQHgfaNtRAjoxQ= -github.com/breml/errchkjson v0.3.1/go.mod h1:XroxrzKjdiutFyW3nWhw34VGg7kiMsDQox73yWCGI2U= -github.com/butuzov/ireturn v0.1.1 h1:QvrO2QF2+/Cx1WA/vETCIYBKtRjc30vesdoPUNo1EbY= -github.com/butuzov/ireturn v0.1.1/go.mod h1:Wh6Zl3IMtTpaIKbmwzqi6olnM9ptYQxxVacMsOEFPoc= +github.com/bombsimon/wsl/v4 v4.2.0 h1:dKK3o/Hk2aIt6t72CWg02ham2P5lnH9MBSW6cTU9xxU= +github.com/bombsimon/wsl/v4 v4.2.0/go.mod h1:1zaTbf/7ywOQtMdoUdTF2X1fbbBLiBUkajyuFAanT28= +github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= +github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= +github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= +github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U= +github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0= +github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA= +github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= +github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= +github.com/catenacyber/perfsprint v0.6.0 h1:VSv95RRkk5+BxrU/YTPcnxuMEWar1iMK5Vyh3fWcBfs= +github.com/catenacyber/perfsprint v0.6.0/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= +github.com/ccojocar/zxcvbn-go v1.0.1 h1:+sxrANSCj6CdadkcMnvde/GWU1vZiiXRbqYSCalV4/4= +github.com/ccojocar/zxcvbn-go v1.0.1/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= -github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 h1:W9o46d2kbNL06lq7UNDPV0zYLzkrde/bjIqO02eoll0= -github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8/go.mod h1:gakxgyXaaPkxvLw1XQxNGK4I37ys9iBRzNUx/B7pUCo= +github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= +github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/daixiang0/gci v0.10.1 h1:eheNA3ljF6SxnPD/vE4lCBusVHmV3Rs3dkKvFrJ7MR0= -github.com/daixiang0/gci v0.10.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= +github.com/daixiang0/gci v0.12.1 h1:ugsG+KRYny1VK4oqrX4Vtj70bo4akYKa0tgT1DXMYiY= +github.com/daixiang0/gci v0.12.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -116,26 +128,27 @@ github.com/denis-tingaikin/go-header v0.4.3/go.mod h1:0wOCWuN71D5qIgE2nz9KrKmuYB github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= -github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= -github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= -github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= -github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= +github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= github.com/firefart/nonamedreturns v1.0.4 h1:abzI1p7mAEPYuR4A+VLKn4eNDOycjYo2phmY9sfv40Y= github.com/firefart/nonamedreturns v1.0.4/go.mod h1:TDhe/tjI1BXo48CmYbUduTV7BdIga8MAO/xbKdcVsGI= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/go-critic/go-critic v0.7.0 h1:tqbKzB8pqi0NsRZ+1pyU4aweAF7A7QN0Pi4Q02+rYnQ= -github.com/go-critic/go-critic v0.7.0/go.mod h1:moYzd7GdVXE2C2hYTwd7h0CPcqlUeclsyBRwMa38v64= +github.com/ghostiam/protogetter v0.3.4 h1:5SZ+lZSNmNkSbGVSF9hUHhv/b7ELF9Rwchoq7btYo6c= +github.com/ghostiam/protogetter v0.3.4/go.mod h1:A0JgIhs0fgVnotGinjQiKaFVG3waItLJNwPmcMzDnvk= +github.com/go-critic/go-critic v0.11.0 h1:mARtIFX7jPtJ3SzxO9Isa5T2jd2dZxFmQHK3yNf0wrE= +github.com/go-critic/go-critic v0.11.0/go.mod h1:Cz6lr1PlkIu/0Y0U9KqJgcIJJECAF8mEwmzVjKnhbfI= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -145,8 +158,11 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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= github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8= github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU= github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= @@ -159,11 +175,14 @@ github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlN github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA= github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk= +github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus= github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw= github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -197,26 +216,27 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 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/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= 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/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6J5HIP8ZtyMdiDscjMLfRBSPuzVVeo= github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 h1:amWTbTGqOZ71ruzrdA+Nx5WA3tV1N0goTspwmKCQvBY= -github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2/go.mod h1:9wOXstvyDRshQ9LggQuzBCGysxs3b6Uo/1MvYCR2NMs= -github.com/golangci/golangci-lint v1.52.2 h1:FrPElUUI5rrHXg1mQ7KxI1MXPAw5lBVskiz7U7a8a1A= -github.com/golangci/golangci-lint v1.52.2/go.mod h1:S5fhC5sHM5kE22/HcATKd1XLWQxX+y7mHj8B5H91Q/0= +github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g= +github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM= +github.com/golangci/golangci-lint v1.56.1 h1:vR6rJpjE1w6pRp2EkVeCAbISyUIl6c7OO/hrEtGK1yo= +github.com/golangci/golangci-lint v1.56.1/go.mod h1:sOHqnOxdEZ0u9JYrDuofOaIyO0jRgT8Y6nWfzuoSv0Y= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= -github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0= -github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 h1:DIPQnGy2Gv2FSA4B/hh8Q7xx3B7AIDk3DAMeHclH1vQ= -github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6/go.mod h1:0AKcRCkMoKvUvlf89F6O7H2LYdhr1zBh736mBItOdRs= +github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj/g= +github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI= +github.com/golangci/revgrep v0.5.2 h1:EndcWoRhcnfj2NHQ+28hyuXpLMF+dQmCN+YaeeIl4FU= +github.com/golangci/revgrep v0.5.2/go.mod h1:bjAMA+Sh/QUfTDcHzxfyHxr4xKvllVr/0sCv2e7jJHA= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -233,12 +253,11 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -246,16 +265,13 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 h1:9alfqbrhuD+9fLZ4iaAVwhlp5PEhmnBt7yvK2Oy5C1U= -github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= +github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= @@ -267,10 +283,7 @@ github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3 github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A= github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -281,15 +294,16 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= -github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jgautheron/goconst v1.5.1 h1:HxVbL1MhydKs8R8n/HE5NPvzfaYmQJA3o879lE4+WcM= -github.com/jgautheron/goconst v1.5.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jgautheron/goconst v1.7.0 h1:cEqH+YBKLsECnRSd4F4TK5ri8t/aXtt/qoL0Ft252B0= +github.com/jgautheron/goconst v1.7.0/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/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.2 h1:WXTZG3efY/ji1Vi8mkH+23O3bLeKR6hp3tI3YB7XwKk= +github.com/jjti/go-spancheck v0.5.2/go.mod h1:ARPNI1JRG1V2Rjnd6/2f2NEfghjSVDZGVmruNKlnXU0= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -301,37 +315,38 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY= github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0= -github.com/junk1tm/musttag v0.5.0 h1:bV1DTdi38Hi4pG4OVWa7Kap0hi0o7EczuK6wQt9zPOM= -github.com/junk1tm/musttag v0.5.0/go.mod h1:PcR7BA+oREQYvHwgjIDmw3exJeds5JzRcvEJTfjrA0M= -github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8= -github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= +github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= +github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkHAIKE/contextcheck v1.1.4 h1:B6zAaLhOEEcjvUgIYEqystmnFk1Oemn8bvJhbt0GMb8= github.com/kkHAIKE/contextcheck v1.1.4/go.mod h1:1+i/gWqokIa+dm31mqGLZhZJ7Uh44DJGZVmr6QRBNJg= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.6 h1:FCKYMF1OF2+RveWlABsdnmsvJrei5aoyZoaGS+Ugg8g= -github.com/kunwardeep/paralleltest v1.0.6/go.mod h1:Y0Y0XISdZM5IKm3TREQMZ6iteqn1YuwCsJO/0kL9Zes= +github.com/kunwardeep/paralleltest v1.0.9 h1:3Sr2IfFNcsMmlqPk1cjTUbJ4zofKPGyHxenwPebgTug= +github.com/kunwardeep/paralleltest v1.0.9/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA= github.com/ldez/gomoddirectives v0.2.3 h1:y7MBaisZVDYmKvt9/l1mjNCiSA1BVn34U0ObUcJwlhA= github.com/ldez/gomoddirectives v0.2.3/go.mod h1:cpgBogWITnCfRq2qGoDkKMEVSaarhdBr6g8G04uz6d0= -github.com/ldez/tagliatelle v0.4.0 h1:sylp7d9kh6AdXN2DpVGHBRb5guTVAgOxqNGhbqc4b1c= -github.com/ldez/tagliatelle v0.4.0/go.mod h1:mNtTfrHy2haaBAw+VT7IBV6VXBThS7TCreYWbBcJ87I= +github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo= +github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= github.com/leonklingele/grouper v1.1.1 h1:suWXRU57D4/Enn6pXR0QVqqWWrnJ9Osrz+5rjt8ivzU= github.com/leonklingele/grouper v1.1.1/go.mod h1:uk3I3uDfi9B6PeUjsCKi6ndcf63Uy7snXgR4yDYQVDY= github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= +github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= +github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= @@ -345,16 +360,16 @@ github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwM github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/revive v1.3.1 h1:OlQkcH40IB2cGuprTPcjB0iIUddgVZgGmDX3IAMR8D4= -github.com/mgechev/revive v1.3.1/go.mod h1:YlD6TTWl2B8A103R9KWJSPVI9DrEf+oqr15q21Ld+5I= +github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE= +github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -370,21 +385,23 @@ 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/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= -github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/nishanths/exhaustive v0.9.5 h1:TzssWan6orBiLYVqewCG8faud9qlFntJE30ACpzmGME= -github.com/nishanths/exhaustive v0.9.5/go.mod h1:IbwrGdVMizvDcIxPYGVdQn5BqWJaOwpCvg4RGb8r/TA= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +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= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.9.0 h1:Sm0zX5QfjJzkeCjEp+t6d3Ha0jwvoDjleP9XCsrEzOA= -github.com/nunnatsa/ginkgolinter v0.9.0/go.mod h1:FHaMLURXP7qImeH6bvxWJUpyH+2tuqe5j4rW1gxJRmI= +github.com/nunnatsa/ginkgolinter v0.15.2 h1:N2ORxUxPU56R9gsfLIlVVvCv/V/VVou5qVI1oBKBNHg= +github.com/nunnatsa/ginkgolinter v0.15.2/go.mod h1:oYxE7dt1vZI8cK2rZOs3RgTaBN2vggkqnENmoJ8kVvc= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= -github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= -github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= @@ -397,11 +414,10 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.4.0 h1:b+sQ5HibPIAjEZwtuwU8Wz/u0dMZ7YL+bk+9yWyHVJk= -github.com/polyfloyd/go-errorlint v1.4.0/go.mod h1:qJCkPeBn+0EXkdKTrUCcuFStM2xrDKfxI3MGLXPexUs= +github.com/polyfloyd/go-errorlint v1.4.8 h1:jiEjKDH33ouFktyez7sckv6pHWif9B7SuS8cutDXFHw= +github.com/polyfloyd/go-errorlint v1.4.8/go.mod h1:NNCxFcFjZcw3xNjVdCchERkEM6Oz7wta2XJVxRftwO4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -424,8 +440,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/quasilyte/go-ruleguard v0.3.19 h1:tfMnabXle/HzOb5Xe9CUZYWXKfkS1KwRmZyPmD9nVcc= -github.com/quasilyte/go-ruleguard v0.3.19/go.mod h1:lHSn69Scl48I7Gt9cX3VrbsZYvYiBYszZOZW4A+oTEw= +github.com/quasilyte/go-ruleguard v0.4.0 h1:DyM6r+TKL+xbKB4Nm7Afd1IQh9kEUKQs2pboWGKtvQo= +github.com/quasilyte/go-ruleguard v0.4.0/go.mod h1:Eu76Z/R8IXtViWUIHkE3p8gdH3/PKk1eh3YGfaEof10= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng= github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU= @@ -433,20 +449,21 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:r 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/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryancurrah/gomodguard v1.3.0 h1:q15RT/pd6UggBXVBuLps8BXRvl5GPBcwVA7BJHMLuTw= github.com/ryancurrah/gomodguard v1.3.0/go.mod h1:ggBxb3luypPEzqVtq33ee7YSN35V28XeGnid8dnni50= -github.com/ryanrolds/sqlclosecheck v0.4.0 h1:i8SX60Rppc1wRuyQjMciLqIzV3xnoHB7/tXbr6RGYNI= -github.com/ryanrolds/sqlclosecheck v0.4.0/go.mod h1:TBRRjzL31JONc9i4XMinicuo+s+E8yKZ5FN8X3G6CKQ= +github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= +github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc= github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.23.0 h1:01h+/2Kd+NblNItNeux0veSL5cBF1jbEOPrEhDzGYq0= -github.com/sashamelentyev/usestdlibvars v1.23.0/go.mod h1:YPwr/Y1LATzHI93CqoPUN/2BzGQ/6N/cl/KwgR0B/aU= -github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw= -github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8= +github.com/sashamelentyev/usestdlibvars v1.24.0 h1:MKNzmXtGh5N0y74Z/CIaJh4GlB364l0K1RUT08WSWAc= +github.com/sashamelentyev/usestdlibvars v1.24.0/go.mod h1:9cYkq+gYJ+a5W2RPdhfaSCnTVUC1OQP/bSiiBhq3OZE= +github.com/securego/gosec/v2 v2.18.2 h1:DkDt3wCiOtAHf1XkiXZBhQ6m6mK/b9T/wD257R3/c+I= +github.com/securego/gosec/v2 v2.18.2/go.mod h1:xUuqSF6i0So56Y2wwohWAmB07EdBkUN6crbLlHwbyJs= 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= @@ -454,10 +471,10 @@ github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOms github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sivchari/containedctx v1.0.2 h1:0hLQKpgC53OVF1VT7CeoFHk9YKstur1XOgfYIc1yrHI= -github.com/sivchari/containedctx v1.0.2/go.mod h1:PwZOeqm4/DLoJOqMSIJs3aKqXRX4YO+uXww087KZ7Bw= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= +github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt95do8= github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= @@ -466,12 +483,12 @@ github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00= github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -487,7 +504,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -495,8 +511,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8= @@ -507,26 +523,30 @@ github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.11 h1:BVoBIqAf/2QdbFmSwAWnaIqDivZdOV0ZRwEm6jivLKw= -github.com/tetafro/godot v1.4.11/go.mod h1:LR3CJpxDVGlYOWn3ZZg1PgNZdTUvzsZWu8xaEohUpn8= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e h1:MV6KaVu/hzByHP0UvJ4HcMGE/8a6A4Rggc/0wx2AvJo= -github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= +github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= +github.com/tetafro/godot v1.4.16/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +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= github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg= github.com/tomarrell/wrapcheck/v2 v2.8.1 h1:HxSqDSN0sAt0yJYsrcYVoEeyM4aI9yAm3KQpIXDJRhQ= github.com/tomarrell/wrapcheck/v2 v2.8.1/go.mod h1:/n2Q3NZ4XFT50ho6Hbxg+RV1uyo2Uow/Vdm9NQcl5SE= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= -github.com/ultraware/funlen v0.0.3 h1:5ylVWm8wsNwH5aWo9438pwvsK0QiqVuUrt9bn7S/iLA= -github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= -github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= -github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= -github.com/uudashr/gocognit v1.0.6 h1:2Cgi6MweCsdB6kpcVQp7EW4U23iBFQWfTXiWlyp842Y= -github.com/uudashr/gocognit v1.0.6/go.mod h1:nAIUuVBnYU7pcninia3BHOvQkpQCeO76Uscky5BOwcY= +github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= +github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= +github.com/ultraware/whitespace v0.1.0 h1:O1HKYoh0kIeqE8sFqZf1o0qbORXUCOQFrlaQyZsczZw= +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/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= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= +github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= +github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -534,17 +554,23 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -gitlab.com/bosi/decorder v0.2.3 h1:gX4/RgK16ijY8V+BRQHAySfQAb354T7/xQpDB2n10P0= -gitlab.com/bosi/decorder v0.2.3/go.mod h1:9K1RB5+VPNQYtXtTDAzd2OEftsZb1oV0IrJrzChSdGE= +gitlab.com/bosi/decorder v0.4.1 h1:VdsdfxhstabyhZovHafFw+9eJ6eU0d2CkFNJcZz/NU4= +gitlab.com/bosi/decorder v0.4.1/go.mod h1:jecSqWUew6Yle1pCr2eLWTensJMmsxHsBwt+PVbkAqA= +go-simpler.org/assert v0.7.0 h1:OzWWZqfNxt8cLS+MlUp6Tgk1HjPkmgdKBq9qvy8lZsA= +go-simpler.org/assert v0.7.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= +go-simpler.org/musttag v0.8.0 h1:DR4UTgetNNhPRNo02rkK1hwDTRzAPotN+ZqYpdtEwWc= +go-simpler.org/musttag v0.8.0/go.mod h1:fiNdCkXt2S6je9Eblma3okjnlva9NT1Eg/WUt19rWu8= +go-simpler.org/sloglint v0.4.0 h1:UVJuUJo63iNQNFEOtZ6o1xAgagVg/giVLLvG9nNLobI= +go-simpler.org/sloglint v0.4.0/go.mod h1:v6zJ++j/thFPhefs2wEXoCKwT10yo5nkBDYRCXyqgNQ= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= @@ -555,9 +581,7 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 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= @@ -569,12 +593,12 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc h1:ao2WRsKSzW6KuUY9IWPwWahcHCgR0s52IfwutMfEbdM= +golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2 h1:J74nGeMgeFnYQJN59eFwh06jX/V8g0lB7LWpjSLxtgU= -golang.org/x/exp/typeparams v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20231219180239-dc181d75b848 h1:UhRVJ0i7bF9n/Hd8YjW3eKjlPVBHzbQdxrBgjbSKl64= +golang.org/x/exp/typeparams v0.0.0-20231219180239-dc181d75b848/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -587,7 +611,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -596,7 +619,6 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= @@ -605,8 +627,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -636,9 +658,6 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -646,19 +665,15 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +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.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -672,8 +687,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -705,17 +721,12 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -730,16 +741,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= 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/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -747,18 +757,16 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +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/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -806,14 +814,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU= @@ -825,11 +827,10 @@ golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +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= @@ -850,16 +851,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -889,13 +886,6 @@ 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 v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -908,10 +898,6 @@ 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.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 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= @@ -924,13 +910,14 @@ 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.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -951,16 +938,16 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.4.3 h1:o/n5/K5gXqk8Gozvs2cnL0F2S1/g1vcGCAx2vETjITw= -honnef.co/go/tools v0.4.3/go.mod h1:36ZgoUOrqOk1GxwHhyryEkq8FQWkUO2xGuSMhUCcdvA= -mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM= -mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ= +honnef.co/go/tools v0.4.6 h1:oFEHCKeID7to/3autwsWfnuv69j3NsfcXbvJKuIcep8= +honnef.co/go/tools v0.4.6/go.mod h1:+rnGS1THNh8zMwnd2oVOTL9QF6vmfyG6ZXBULae2uc0= +mvdan.cc/gofumpt v0.6.0 h1:G3QvahNDmpD+Aek/bNOLrFR2XC6ZAdo62dZu65gmwGo= +mvdan.cc/gofumpt v0.6.0/go.mod h1:4L0wf+kgIPZtcCWXynNS2e6bhmj73umwnuXSZarixzA= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d h1:3rvTIIM22r9pvXk+q3swxUQAQOxksVMGK7sml4nG57w= -mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is= +mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 h1:zCr3iRRgdk5eIikZNDphGcM6KGVTx3Yu+/Uu9Es254w= +mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14/go.mod h1:ZzZjEpJDOmx8TdVU6umamY3Xy0UAQUI2DHbf05USVbI= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From c14574a04d81eaa46e06be26a1b1f1a2922aa2ea Mon Sep 17 00:00:00 2001 From: Zihao Zhang Date: Fri, 16 Feb 2024 14:39:02 -0800 Subject: [PATCH 03/17] fix implementation type generation for fragment on a union (#310) before the fix ``` === RUN TestGenerate/ComplexNamedFragments.graphql/Build # command-line-arguments /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2008:8: cannot use new(TopicNewestContentNewestContentArticle) (value of type *TopicNewestContentNewestContentArticle) as type TopicNewestContentNewestContentLeafContent in assignment: *TopicNewestContentNewestContentArticle does not implement TopicNewestContentNewestContentLeafContent (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2011:8: cannot use new(TopicNewestContentNewestContentVideo) (value of type *TopicNewestContentNewestContentVideo) as type TopicNewestContentNewestContentLeafContent in assignment: *TopicNewestContentNewestContentVideo does not implement TopicNewestContentNewestContentLeafContent (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2026:7: impossible type switch case: *TopicNewestContentNewestContentArticle (*v) (variable of type TopicNewestContentNewestContentLeafContent) cannot have dynamic type *TopicNewestContentNewestContentArticle (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2034:7: impossible type switch case: *TopicNewestContentNewestContentVideo (*v) (variable of type TopicNewestContentNewestContentLeafContent) cannot have dynamic type *TopicNewestContentNewestContentVideo (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2177:8: cannot use new(UserLastContentLastContentArticle) (value of type *UserLastContentLastContentArticle) as type UserLastContentLastContentLeafContent in assignment: *UserLastContentLastContentArticle does not implement UserLastContentLastContentLeafContent (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2180:8: cannot use new(UserLastContentLastContentVideo) (value of type *UserLastContentLastContentVideo) as type UserLastContentLastContentLeafContent in assignment: *UserLastContentLastContentVideo does not implement UserLastContentLastContentLeafContent (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2195:7: impossible type switch case: *UserLastContentLastContentArticle (*v) (variable of type UserLastContentLastContentLeafContent) cannot have dynamic type *UserLastContentLastContentArticle (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/testdata/tmp/ComplexNamedFragments.graphql_727791265.go:2203:7: impossible type switch case: *UserLastContentLastContentVideo (*v) (variable of type UserLastContentLastContentLeafContent) cannot have dynamic type *UserLastContentLastContentVideo (missing implementsGraphQLInterfaceSimpleLeafContent method) /genqlient/generate/generate_test.go:120: generated code does not compile: exit status 2 ``` after fix https://github.com/Khan/genqlient/pull/310/files#diff-3eaa9f1b68120a67e7558f37dd5984e995a6df5d454656180befca84c2dbf53aR2164 I also took a look at https://github.com/Khan/genqlient/issues/64 which is the real underlaying issue i wanted to fix but that seems to be extremely complicated and probably needs multiple weeks of work. the nested interface issues came up on a query with union spreading. this PR fixes the problem of spreading when the union is the same fragment, however if the spread contains different fragment we run into #64 . --- docs/CHANGELOG.md | 1 + generate/convert.go | 11 + .../queries/ComplexNamedFragments.graphql | 31 + generate/testdata/queries/schema.graphql | 4 + ...s.graphql-ComplexNamedFragments.graphql.go | 1004 +++++++++++++++-- ...graphql-ComplexNamedFragments.graphql.json | 5 + 6 files changed, 980 insertions(+), 76 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bec20ef4..978ad90f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -30,6 +30,7 @@ When releasing a new version: ### Bug fixes: - The presence of negative pointer directives, i.e., `# @genqlient(pointer: false)` are now respected even in the when `optional: pointer` is set in the configuration file. - Made name collisions between query/mutation arguments and local function variables less likely. +- Fix generation issue related to golang type implementation of complex graphql union fragments ## v0.6.0 diff --git a/generate/convert.go b/generate/convert.go index 3b1f750a..d3a9f8d3 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -701,6 +701,17 @@ func fragmentMatches(containingTypedef, fragmentTypedef *ast.Definition) bool { return true } } + + // Handle the special case where the fragment is on a union, then the + // fragment can match any of the types in the union. + if fragmentTypedef.Kind == ast.Union { + for _, typeName := range fragmentTypedef.Types { + if typeName == containingTypedef.Name { + return true + } + } + } + return false } diff --git a/generate/testdata/queries/ComplexNamedFragments.graphql b/generate/testdata/queries/ComplexNamedFragments.graphql index 57feb656..12036c80 100644 --- a/generate/testdata/queries/ComplexNamedFragments.graphql +++ b/generate/testdata/queries/ComplexNamedFragments.graphql @@ -47,3 +47,34 @@ fragment ContentFields on Content { query ComplexNamedFragments { ... on Query { ...QueryFragment } } + +## two fragments of different types with fields containing the same inline named fragment of a union +fragment SimpleLeafContent on LeafContent { + ... on Article { + id + } + ... on Video { + id + } +} + +fragment UserLastContent on User { + lastContent { + ... SimpleLeafContent + } +} + +fragment TopicNewestContent on Topic { + newestContent { + ... SimpleLeafContent + } +} + +query ComplexNamedFragmentsWithInlineUnion { + user { + ...UserLastContent + } + root { + ...TopicNewestContent + } +} diff --git a/generate/testdata/queries/schema.graphql b/generate/testdata/queries/schema.graphql index 17db360a..eb71bdd4 100644 --- a/generate/testdata/queries/schema.graphql +++ b/generate/testdata/queries/schema.graphql @@ -69,6 +69,8 @@ type User { pokemon: [Pokemon!] greeting: Clip birthdate: Date + + lastContent: LeafContent } """An audio clip, such as of a user saying hello.""" @@ -141,6 +143,8 @@ type Topic implements Content { schoolGrade: String next: Topic related: [Topic!] + + newestContent: LeafContent } input RecursiveInput { diff --git a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go index 5e7350c9..7888b2e8 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go @@ -113,6 +113,159 @@ func (v *ComplexNamedFragmentsResponse) __premarshalJSON() (*__premarshalComplex return &retval, nil } +// ComplexNamedFragmentsWithInlineUnionResponse is returned by ComplexNamedFragmentsWithInlineUnion on success. +type ComplexNamedFragmentsWithInlineUnionResponse struct { + // user looks up a user by some stuff. + // + // See UserQueryInput for what stuff is supported. + // If query is null, returns the current user. + User ComplexNamedFragmentsWithInlineUnionUser `json:"user"` + Root ComplexNamedFragmentsWithInlineUnionRootTopic `json:"root"` +} + +// GetUser returns ComplexNamedFragmentsWithInlineUnionResponse.User, and is useful for accessing the field via an interface. +func (v *ComplexNamedFragmentsWithInlineUnionResponse) GetUser() ComplexNamedFragmentsWithInlineUnionUser { + return v.User +} + +// GetRoot returns ComplexNamedFragmentsWithInlineUnionResponse.Root, and is useful for accessing the field via an interface. +func (v *ComplexNamedFragmentsWithInlineUnionResponse) GetRoot() ComplexNamedFragmentsWithInlineUnionRootTopic { + return v.Root +} + +// ComplexNamedFragmentsWithInlineUnionRootTopic includes the requested fields of the GraphQL type Topic. +type ComplexNamedFragmentsWithInlineUnionRootTopic struct { + TopicNewestContent `json:"-"` +} + +// GetNewestContent returns ComplexNamedFragmentsWithInlineUnionRootTopic.NewestContent, and is useful for accessing the field via an interface. +func (v *ComplexNamedFragmentsWithInlineUnionRootTopic) GetNewestContent() TopicNewestContentNewestContentLeafContent { + return v.TopicNewestContent.NewestContent +} + +func (v *ComplexNamedFragmentsWithInlineUnionRootTopic) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *ComplexNamedFragmentsWithInlineUnionRootTopic + graphql.NoUnmarshalJSON + } + firstPass.ComplexNamedFragmentsWithInlineUnionRootTopic = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.TopicNewestContent) + if err != nil { + return err + } + return nil +} + +type __premarshalComplexNamedFragmentsWithInlineUnionRootTopic struct { + NewestContent json.RawMessage `json:"newestContent"` +} + +func (v *ComplexNamedFragmentsWithInlineUnionRootTopic) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *ComplexNamedFragmentsWithInlineUnionRootTopic) __premarshalJSON() (*__premarshalComplexNamedFragmentsWithInlineUnionRootTopic, error) { + var retval __premarshalComplexNamedFragmentsWithInlineUnionRootTopic + + { + + dst := &retval.NewestContent + src := v.TopicNewestContent.NewestContent + var err error + *dst, err = __marshalTopicNewestContentNewestContentLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal ComplexNamedFragmentsWithInlineUnionRootTopic.TopicNewestContent.NewestContent: %w", err) + } + } + return &retval, nil +} + +// ComplexNamedFragmentsWithInlineUnionUser includes the requested fields of the GraphQL type User. +// The GraphQL type's documentation follows. +// +// A User is a user! +type ComplexNamedFragmentsWithInlineUnionUser struct { + UserLastContent `json:"-"` +} + +// GetLastContent returns ComplexNamedFragmentsWithInlineUnionUser.LastContent, and is useful for accessing the field via an interface. +func (v *ComplexNamedFragmentsWithInlineUnionUser) GetLastContent() UserLastContentLastContentLeafContent { + return v.UserLastContent.LastContent +} + +func (v *ComplexNamedFragmentsWithInlineUnionUser) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *ComplexNamedFragmentsWithInlineUnionUser + graphql.NoUnmarshalJSON + } + firstPass.ComplexNamedFragmentsWithInlineUnionUser = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.UserLastContent) + if err != nil { + return err + } + return nil +} + +type __premarshalComplexNamedFragmentsWithInlineUnionUser struct { + LastContent json.RawMessage `json:"lastContent"` +} + +func (v *ComplexNamedFragmentsWithInlineUnionUser) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *ComplexNamedFragmentsWithInlineUnionUser) __premarshalJSON() (*__premarshalComplexNamedFragmentsWithInlineUnionUser, error) { + var retval __premarshalComplexNamedFragmentsWithInlineUnionUser + + { + + dst := &retval.LastContent + src := v.UserLastContent.LastContent + var err error + *dst, err = __marshalUserLastContentLastContentLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal ComplexNamedFragmentsWithInlineUnionUser.UserLastContent.LastContent: %w", err) + } + } + return &retval, nil +} + // ContentFields includes the GraphQL fields of Content requested by the fragment ContentFields. // The GraphQL type's documentation follows. // @@ -1649,70 +1802,141 @@ func (v *QueryFragment) __premarshalJSON() (*__premarshalQueryFragment, error) { return &retval, nil } -// VideoFields includes the GraphQL fields of Video requested by the fragment VideoFields. -type VideoFields struct { +// # two fragments of different types with fields containing the same inline named fragment of a union +// +// SimpleLeafContent is implemented by the following types: +// SimpleLeafContentArticle +// SimpleLeafContentVideo +type SimpleLeafContent interface { + implementsGraphQLInterfaceSimpleLeafContent() +} + +func (v *SimpleLeafContentArticle) implementsGraphQLInterfaceSimpleLeafContent() {} +func (v *SimpleLeafContentVideo) implementsGraphQLInterfaceSimpleLeafContent() {} + +func __unmarshalSimpleLeafContent(b []byte, v *SimpleLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(SimpleLeafContentArticle) + return json.Unmarshal(b, *v) + case "Video": + *v = new(SimpleLeafContentVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for SimpleLeafContent: "%v"`, tn.TypeName) + } +} + +func __marshalSimpleLeafContent(v *SimpleLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *SimpleLeafContentArticle: + typename = "Article" + + result := struct { + TypeName string `json:"__typename"` + *SimpleLeafContentArticle + }{typename, v} + return json.Marshal(result) + case *SimpleLeafContentVideo: + typename = "Video" + + result := struct { + TypeName string `json:"__typename"` + *SimpleLeafContentVideo + }{typename, v} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for SimpleLeafContent: "%T"`, v) + } +} + +// # two fragments of different types with fields containing the same inline named fragment of a union +type SimpleLeafContentArticle struct { // ID is documented in the Content interface. - Id testutil.ID `json:"id"` - Name string `json:"name"` - Url string `json:"url"` - Duration int `json:"duration"` - Thumbnail VideoFieldsThumbnail `json:"thumbnail"` - ContentFieldsVideo `json:"-"` + Id testutil.ID `json:"id"` } -// GetId returns VideoFields.Id, and is useful for accessing the field via an interface. -func (v *VideoFields) GetId() testutil.ID { return v.Id } +// GetId returns SimpleLeafContentArticle.Id, and is useful for accessing the field via an interface. +func (v *SimpleLeafContentArticle) GetId() testutil.ID { return v.Id } -// GetName returns VideoFields.Name, and is useful for accessing the field via an interface. -func (v *VideoFields) GetName() string { return v.Name } +// # two fragments of different types with fields containing the same inline named fragment of a union +type SimpleLeafContentVideo struct { + // ID is documented in the Content interface. + Id testutil.ID `json:"id"` +} -// GetUrl returns VideoFields.Url, and is useful for accessing the field via an interface. -func (v *VideoFields) GetUrl() string { return v.Url } +// GetId returns SimpleLeafContentVideo.Id, and is useful for accessing the field via an interface. +func (v *SimpleLeafContentVideo) GetId() testutil.ID { return v.Id } -// GetDuration returns VideoFields.Duration, and is useful for accessing the field via an interface. -func (v *VideoFields) GetDuration() int { return v.Duration } +// TopicNewestContent includes the GraphQL fields of Topic requested by the fragment TopicNewestContent. +type TopicNewestContent struct { + NewestContent TopicNewestContentNewestContentLeafContent `json:"-"` +} -// GetThumbnail returns VideoFields.Thumbnail, and is useful for accessing the field via an interface. -func (v *VideoFields) GetThumbnail() VideoFieldsThumbnail { return v.Thumbnail } +// GetNewestContent returns TopicNewestContent.NewestContent, and is useful for accessing the field via an interface. +func (v *TopicNewestContent) GetNewestContent() TopicNewestContentNewestContentLeafContent { + return v.NewestContent +} -func (v *VideoFields) UnmarshalJSON(b []byte) error { +func (v *TopicNewestContent) UnmarshalJSON(b []byte) error { if string(b) == "null" { return nil } var firstPass struct { - *VideoFields + *TopicNewestContent + NewestContent json.RawMessage `json:"newestContent"` graphql.NoUnmarshalJSON } - firstPass.VideoFields = v + firstPass.TopicNewestContent = v err := json.Unmarshal(b, &firstPass) if err != nil { return err } - err = json.Unmarshal( - b, &v.ContentFieldsVideo) - if err != nil { - return err + { + dst := &v.NewestContent + src := firstPass.NewestContent + if len(src) != 0 && string(src) != "null" { + err = __unmarshalTopicNewestContentNewestContentLeafContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal TopicNewestContent.NewestContent: %w", err) + } + } } return nil } -type __premarshalVideoFields struct { - Id testutil.ID `json:"id"` - - Name string `json:"name"` - - Url string `json:"url"` - - Duration int `json:"duration"` - - Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +type __premarshalTopicNewestContent struct { + NewestContent json.RawMessage `json:"newestContent"` } -func (v *VideoFields) MarshalJSON() ([]byte, error) { +func (v *TopicNewestContent) MarshalJSON() ([]byte, error) { premarshaled, err := v.__premarshalJSON() if err != nil { return nil, err @@ -1720,56 +1944,631 @@ func (v *VideoFields) MarshalJSON() ([]byte, error) { return json.Marshal(premarshaled) } -func (v *VideoFields) __premarshalJSON() (*__premarshalVideoFields, error) { - var retval __premarshalVideoFields +func (v *TopicNewestContent) __premarshalJSON() (*__premarshalTopicNewestContent, error) { + var retval __premarshalTopicNewestContent - retval.Id = v.Id - retval.Name = v.Name - retval.Url = v.Url - retval.Duration = v.Duration - retval.Thumbnail = v.Thumbnail + { + + dst := &retval.NewestContent + src := v.NewestContent + var err error + *dst, err = __marshalTopicNewestContentNewestContentLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal TopicNewestContent.NewestContent: %w", err) + } + } return &retval, nil } -// VideoFieldsThumbnail includes the requested fields of the GraphQL type Thumbnail. -type VideoFieldsThumbnail struct { - Id testutil.ID `json:"id"` +// TopicNewestContentNewestContentArticle includes the requested fields of the GraphQL type Article. +type TopicNewestContentNewestContentArticle struct { + Typename string `json:"__typename"` + SimpleLeafContentArticle `json:"-"` } -// GetId returns VideoFieldsThumbnail.Id, and is useful for accessing the field via an interface. -func (v *VideoFieldsThumbnail) GetId() testutil.ID { return v.Id } +// GetTypename returns TopicNewestContentNewestContentArticle.Typename, and is useful for accessing the field via an interface. +func (v *TopicNewestContentNewestContentArticle) GetTypename() string { return v.Typename } -// The query or mutation executed by ComplexNamedFragments. -const ComplexNamedFragments_Operation = ` -query ComplexNamedFragments { - ... on Query { - ... QueryFragment - } -} -fragment QueryFragment on Query { - ... InnerQueryFragment +// GetId returns TopicNewestContentNewestContentArticle.Id, and is useful for accessing the field via an interface. +func (v *TopicNewestContentNewestContentArticle) GetId() testutil.ID { + return v.SimpleLeafContentArticle.Id } -fragment InnerQueryFragment on Query { - randomItem { - __typename - id - name - ... VideoFields - ... ContentFields + +func (v *TopicNewestContentNewestContentArticle) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil } - randomLeaf { - __typename - ... VideoFields - ... MoreVideoFields - ... ContentFields + + var firstPass struct { + *TopicNewestContentNewestContentArticle + graphql.NoUnmarshalJSON } - otherLeaf: randomLeaf { - __typename - ... on Video { - ... MoreVideoFields - ... ContentFields - } - ... ContentFields + firstPass.TopicNewestContentNewestContentArticle = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.SimpleLeafContentArticle) + if err != nil { + return err + } + return nil +} + +type __premarshalTopicNewestContentNewestContentArticle struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` +} + +func (v *TopicNewestContentNewestContentArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *TopicNewestContentNewestContentArticle) __premarshalJSON() (*__premarshalTopicNewestContentNewestContentArticle, error) { + var retval __premarshalTopicNewestContentNewestContentArticle + + retval.Typename = v.Typename + retval.Id = v.SimpleLeafContentArticle.Id + return &retval, nil +} + +// TopicNewestContentNewestContentLeafContent includes the requested fields of the GraphQL interface LeafContent. +// +// TopicNewestContentNewestContentLeafContent is implemented by the following types: +// TopicNewestContentNewestContentArticle +// TopicNewestContentNewestContentVideo +// The GraphQL type's documentation follows. +// +// LeafContent represents content items that can't have child-nodes. +type TopicNewestContentNewestContentLeafContent interface { + implementsGraphQLInterfaceTopicNewestContentNewestContentLeafContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string + SimpleLeafContent +} + +func (v *TopicNewestContentNewestContentArticle) implementsGraphQLInterfaceTopicNewestContentNewestContentLeafContent() { +} +func (v *TopicNewestContentNewestContentVideo) implementsGraphQLInterfaceTopicNewestContentNewestContentLeafContent() { +} + +func __unmarshalTopicNewestContentNewestContentLeafContent(b []byte, v *TopicNewestContentNewestContentLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(TopicNewestContentNewestContentArticle) + return json.Unmarshal(b, *v) + case "Video": + *v = new(TopicNewestContentNewestContentVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for TopicNewestContentNewestContentLeafContent: "%v"`, tn.TypeName) + } +} + +func __marshalTopicNewestContentNewestContentLeafContent(v *TopicNewestContentNewestContentLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *TopicNewestContentNewestContentArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalTopicNewestContentNewestContentArticle + }{typename, premarshaled} + return json.Marshal(result) + case *TopicNewestContentNewestContentVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalTopicNewestContentNewestContentVideo + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for TopicNewestContentNewestContentLeafContent: "%T"`, v) + } +} + +// TopicNewestContentNewestContentVideo includes the requested fields of the GraphQL type Video. +type TopicNewestContentNewestContentVideo struct { + Typename string `json:"__typename"` + SimpleLeafContentVideo `json:"-"` +} + +// GetTypename returns TopicNewestContentNewestContentVideo.Typename, and is useful for accessing the field via an interface. +func (v *TopicNewestContentNewestContentVideo) GetTypename() string { return v.Typename } + +// GetId returns TopicNewestContentNewestContentVideo.Id, and is useful for accessing the field via an interface. +func (v *TopicNewestContentNewestContentVideo) GetId() testutil.ID { + return v.SimpleLeafContentVideo.Id +} + +func (v *TopicNewestContentNewestContentVideo) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *TopicNewestContentNewestContentVideo + graphql.NoUnmarshalJSON + } + firstPass.TopicNewestContentNewestContentVideo = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.SimpleLeafContentVideo) + if err != nil { + return err + } + return nil +} + +type __premarshalTopicNewestContentNewestContentVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` +} + +func (v *TopicNewestContentNewestContentVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *TopicNewestContentNewestContentVideo) __premarshalJSON() (*__premarshalTopicNewestContentNewestContentVideo, error) { + var retval __premarshalTopicNewestContentNewestContentVideo + + retval.Typename = v.Typename + retval.Id = v.SimpleLeafContentVideo.Id + return &retval, nil +} + +// UserLastContent includes the GraphQL fields of User requested by the fragment UserLastContent. +// The GraphQL type's documentation follows. +// +// A User is a user! +type UserLastContent struct { + LastContent UserLastContentLastContentLeafContent `json:"-"` +} + +// GetLastContent returns UserLastContent.LastContent, and is useful for accessing the field via an interface. +func (v *UserLastContent) GetLastContent() UserLastContentLastContentLeafContent { + return v.LastContent +} + +func (v *UserLastContent) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *UserLastContent + LastContent json.RawMessage `json:"lastContent"` + graphql.NoUnmarshalJSON + } + firstPass.UserLastContent = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + { + dst := &v.LastContent + src := firstPass.LastContent + if len(src) != 0 && string(src) != "null" { + err = __unmarshalUserLastContentLastContentLeafContent( + src, dst) + if err != nil { + return fmt.Errorf( + "unable to unmarshal UserLastContent.LastContent: %w", err) + } + } + } + return nil +} + +type __premarshalUserLastContent struct { + LastContent json.RawMessage `json:"lastContent"` +} + +func (v *UserLastContent) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *UserLastContent) __premarshalJSON() (*__premarshalUserLastContent, error) { + var retval __premarshalUserLastContent + + { + + dst := &retval.LastContent + src := v.LastContent + var err error + *dst, err = __marshalUserLastContentLastContentLeafContent( + &src) + if err != nil { + return nil, fmt.Errorf( + "unable to marshal UserLastContent.LastContent: %w", err) + } + } + return &retval, nil +} + +// UserLastContentLastContentArticle includes the requested fields of the GraphQL type Article. +type UserLastContentLastContentArticle struct { + Typename string `json:"__typename"` + SimpleLeafContentArticle `json:"-"` +} + +// GetTypename returns UserLastContentLastContentArticle.Typename, and is useful for accessing the field via an interface. +func (v *UserLastContentLastContentArticle) GetTypename() string { return v.Typename } + +// GetId returns UserLastContentLastContentArticle.Id, and is useful for accessing the field via an interface. +func (v *UserLastContentLastContentArticle) GetId() testutil.ID { return v.SimpleLeafContentArticle.Id } + +func (v *UserLastContentLastContentArticle) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *UserLastContentLastContentArticle + graphql.NoUnmarshalJSON + } + firstPass.UserLastContentLastContentArticle = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.SimpleLeafContentArticle) + if err != nil { + return err + } + return nil +} + +type __premarshalUserLastContentLastContentArticle struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` +} + +func (v *UserLastContentLastContentArticle) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *UserLastContentLastContentArticle) __premarshalJSON() (*__premarshalUserLastContentLastContentArticle, error) { + var retval __premarshalUserLastContentLastContentArticle + + retval.Typename = v.Typename + retval.Id = v.SimpleLeafContentArticle.Id + return &retval, nil +} + +// UserLastContentLastContentLeafContent includes the requested fields of the GraphQL interface LeafContent. +// +// UserLastContentLastContentLeafContent is implemented by the following types: +// UserLastContentLastContentArticle +// UserLastContentLastContentVideo +// The GraphQL type's documentation follows. +// +// LeafContent represents content items that can't have child-nodes. +type UserLastContentLastContentLeafContent interface { + implementsGraphQLInterfaceUserLastContentLastContentLeafContent() + // GetTypename returns the receiver's concrete GraphQL type-name (see interface doc for possible values). + GetTypename() string + SimpleLeafContent +} + +func (v *UserLastContentLastContentArticle) implementsGraphQLInterfaceUserLastContentLastContentLeafContent() { +} +func (v *UserLastContentLastContentVideo) implementsGraphQLInterfaceUserLastContentLastContentLeafContent() { +} + +func __unmarshalUserLastContentLastContentLeafContent(b []byte, v *UserLastContentLastContentLeafContent) error { + if string(b) == "null" { + return nil + } + + var tn struct { + TypeName string `json:"__typename"` + } + err := json.Unmarshal(b, &tn) + if err != nil { + return err + } + + switch tn.TypeName { + case "Article": + *v = new(UserLastContentLastContentArticle) + return json.Unmarshal(b, *v) + case "Video": + *v = new(UserLastContentLastContentVideo) + return json.Unmarshal(b, *v) + case "": + return fmt.Errorf( + "response was missing LeafContent.__typename") + default: + return fmt.Errorf( + `unexpected concrete type for UserLastContentLastContentLeafContent: "%v"`, tn.TypeName) + } +} + +func __marshalUserLastContentLastContentLeafContent(v *UserLastContentLastContentLeafContent) ([]byte, error) { + + var typename string + switch v := (*v).(type) { + case *UserLastContentLastContentArticle: + typename = "Article" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalUserLastContentLastContentArticle + }{typename, premarshaled} + return json.Marshal(result) + case *UserLastContentLastContentVideo: + typename = "Video" + + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + result := struct { + TypeName string `json:"__typename"` + *__premarshalUserLastContentLastContentVideo + }{typename, premarshaled} + return json.Marshal(result) + case nil: + return []byte("null"), nil + default: + return nil, fmt.Errorf( + `unexpected concrete type for UserLastContentLastContentLeafContent: "%T"`, v) + } +} + +// UserLastContentLastContentVideo includes the requested fields of the GraphQL type Video. +type UserLastContentLastContentVideo struct { + Typename string `json:"__typename"` + SimpleLeafContentVideo `json:"-"` +} + +// GetTypename returns UserLastContentLastContentVideo.Typename, and is useful for accessing the field via an interface. +func (v *UserLastContentLastContentVideo) GetTypename() string { return v.Typename } + +// GetId returns UserLastContentLastContentVideo.Id, and is useful for accessing the field via an interface. +func (v *UserLastContentLastContentVideo) GetId() testutil.ID { return v.SimpleLeafContentVideo.Id } + +func (v *UserLastContentLastContentVideo) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *UserLastContentLastContentVideo + graphql.NoUnmarshalJSON + } + firstPass.UserLastContentLastContentVideo = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.SimpleLeafContentVideo) + if err != nil { + return err + } + return nil +} + +type __premarshalUserLastContentLastContentVideo struct { + Typename string `json:"__typename"` + + Id testutil.ID `json:"id"` +} + +func (v *UserLastContentLastContentVideo) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *UserLastContentLastContentVideo) __premarshalJSON() (*__premarshalUserLastContentLastContentVideo, error) { + var retval __premarshalUserLastContentLastContentVideo + + retval.Typename = v.Typename + retval.Id = v.SimpleLeafContentVideo.Id + return &retval, nil +} + +// VideoFields includes the GraphQL fields of Video requested by the fragment VideoFields. +type VideoFields struct { + // ID is documented in the Content interface. + Id testutil.ID `json:"id"` + Name string `json:"name"` + Url string `json:"url"` + Duration int `json:"duration"` + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` + ContentFieldsVideo `json:"-"` +} + +// GetId returns VideoFields.Id, and is useful for accessing the field via an interface. +func (v *VideoFields) GetId() testutil.ID { return v.Id } + +// GetName returns VideoFields.Name, and is useful for accessing the field via an interface. +func (v *VideoFields) GetName() string { return v.Name } + +// GetUrl returns VideoFields.Url, and is useful for accessing the field via an interface. +func (v *VideoFields) GetUrl() string { return v.Url } + +// GetDuration returns VideoFields.Duration, and is useful for accessing the field via an interface. +func (v *VideoFields) GetDuration() int { return v.Duration } + +// GetThumbnail returns VideoFields.Thumbnail, and is useful for accessing the field via an interface. +func (v *VideoFields) GetThumbnail() VideoFieldsThumbnail { return v.Thumbnail } + +func (v *VideoFields) UnmarshalJSON(b []byte) error { + + if string(b) == "null" { + return nil + } + + var firstPass struct { + *VideoFields + graphql.NoUnmarshalJSON + } + firstPass.VideoFields = v + + err := json.Unmarshal(b, &firstPass) + if err != nil { + return err + } + + err = json.Unmarshal( + b, &v.ContentFieldsVideo) + if err != nil { + return err + } + return nil +} + +type __premarshalVideoFields struct { + Id testutil.ID `json:"id"` + + Name string `json:"name"` + + Url string `json:"url"` + + Duration int `json:"duration"` + + Thumbnail VideoFieldsThumbnail `json:"thumbnail"` +} + +func (v *VideoFields) MarshalJSON() ([]byte, error) { + premarshaled, err := v.__premarshalJSON() + if err != nil { + return nil, err + } + return json.Marshal(premarshaled) +} + +func (v *VideoFields) __premarshalJSON() (*__premarshalVideoFields, error) { + var retval __premarshalVideoFields + + retval.Id = v.Id + retval.Name = v.Name + retval.Url = v.Url + retval.Duration = v.Duration + retval.Thumbnail = v.Thumbnail + return &retval, nil +} + +// VideoFieldsThumbnail includes the requested fields of the GraphQL type Thumbnail. +type VideoFieldsThumbnail struct { + Id testutil.ID `json:"id"` +} + +// GetId returns VideoFieldsThumbnail.Id, and is useful for accessing the field via an interface. +func (v *VideoFieldsThumbnail) GetId() testutil.ID { return v.Id } + +// The query or mutation executed by ComplexNamedFragments. +const ComplexNamedFragments_Operation = ` +query ComplexNamedFragments { + ... on Query { + ... QueryFragment + } +} +fragment QueryFragment on Query { + ... InnerQueryFragment +} +fragment InnerQueryFragment on Query { + randomItem { + __typename + id + name + ... VideoFields + ... ContentFields + } + randomLeaf { + __typename + ... VideoFields + ... MoreVideoFields + ... ContentFields + } + otherLeaf: randomLeaf { + __typename + ... on Video { + ... MoreVideoFields + ... ContentFields + } + ... ContentFields } } fragment VideoFields on Video { @@ -1821,3 +2620,56 @@ func ComplexNamedFragments( return &data_, err_ } +// The query or mutation executed by ComplexNamedFragmentsWithInlineUnion. +const ComplexNamedFragmentsWithInlineUnion_Operation = ` +query ComplexNamedFragmentsWithInlineUnion { + user { + ... UserLastContent + } + root { + ... TopicNewestContent + } +} +fragment UserLastContent on User { + lastContent { + __typename + ... SimpleLeafContent + } +} +fragment TopicNewestContent on Topic { + newestContent { + __typename + ... SimpleLeafContent + } +} +fragment SimpleLeafContent on LeafContent { + ... on Article { + id + } + ... on Video { + id + } +} +` + +func ComplexNamedFragmentsWithInlineUnion( + client_ graphql.Client, +) (*ComplexNamedFragmentsWithInlineUnionResponse, error) { + req_ := &graphql.Request{ + OpName: "ComplexNamedFragmentsWithInlineUnion", + Query: ComplexNamedFragmentsWithInlineUnion_Operation, + } + var err_ error + + var data_ ComplexNamedFragmentsWithInlineUnionResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.json b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.json index 114023cd..f78ee92c 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.json +++ b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.json @@ -4,6 +4,11 @@ "operationName": "ComplexNamedFragments", "query": "\nquery ComplexNamedFragments {\n\t... on Query {\n\t\t... QueryFragment\n\t}\n}\nfragment QueryFragment on Query {\n\t... InnerQueryFragment\n}\nfragment InnerQueryFragment on Query {\n\trandomItem {\n\t\t__typename\n\t\tid\n\t\tname\n\t\t... VideoFields\n\t\t... ContentFields\n\t}\n\trandomLeaf {\n\t\t__typename\n\t\t... VideoFields\n\t\t... MoreVideoFields\n\t\t... ContentFields\n\t}\n\totherLeaf: randomLeaf {\n\t\t__typename\n\t\t... on Video {\n\t\t\t... MoreVideoFields\n\t\t\t... ContentFields\n\t\t}\n\t\t... ContentFields\n\t}\n}\nfragment VideoFields on Video {\n\tid\n\tname\n\turl\n\tduration\n\tthumbnail {\n\t\tid\n\t}\n\t... ContentFields\n}\nfragment ContentFields on Content {\n\tname\n\turl\n}\nfragment MoreVideoFields on Video {\n\tid\n\tparent {\n\t\tname\n\t\turl\n\t\t... ContentFields\n\t\tchildren {\n\t\t\t__typename\n\t\t\t... VideoFields\n\t\t}\n\t}\n}\n", "sourceLocation": "testdata/queries/ComplexNamedFragments.graphql" + }, + { + "operationName": "ComplexNamedFragmentsWithInlineUnion", + "query": "\nquery ComplexNamedFragmentsWithInlineUnion {\n\tuser {\n\t\t... UserLastContent\n\t}\n\troot {\n\t\t... TopicNewestContent\n\t}\n}\nfragment UserLastContent on User {\n\tlastContent {\n\t\t__typename\n\t\t... SimpleLeafContent\n\t}\n}\nfragment TopicNewestContent on Topic {\n\tnewestContent {\n\t\t__typename\n\t\t... SimpleLeafContent\n\t}\n}\nfragment SimpleLeafContent on LeafContent {\n\t... on Article {\n\t\tid\n\t}\n\t... on Video {\n\t\tid\n\t}\n}\n", + "sourceLocation": "testdata/queries/ComplexNamedFragments.graphql" } ] } From 7740a6a0cf041ecd3b844736401e4525265b5777 Mon Sep 17 00:00:00 2001 From: Zihao Zhang Date: Fri, 16 Feb 2024 14:40:14 -0800 Subject: [PATCH 04/17] Support more valid graphql file extensions (#309) There are two other commonly used graphql file extension we should support .gql - used by apollo and other client library to differentiate query from schema .graphqls - used by gqlgen and other server library to differentiate schema from query https://github.com/apollographql/apollo-tooling/blob/a778ca162bdd20be9e60a914887302390c274644/packages/apollo/src/commands/client/download-schema.ts#L19 --- docs/CHANGELOG.md | 1 + generate/parse.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 978ad90f..be0c36f9 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -26,6 +26,7 @@ When releasing a new version: - The new `optional: generic` allows using a generic type to represent optionality. See the [documentation](genqlient.yaml) for details. - For schemas with enum values that differ only in casing, it's now possible to disable smart-casing in genqlient.yaml; see the [documentation](genqlient.yaml) for `casing` for details. +- Support .graphqls and .gql file extensions ### Bug fixes: - The presence of negative pointer directives, i.e., `# @genqlient(pointer: false)` are now respected even in the when `optional: pointer` is set in the configuration file. diff --git a/generate/parse.go b/generate/parse.go index 4569f25c..6ef7656b 100644 --- a/generate/parse.go +++ b/generate/parse.go @@ -130,7 +130,7 @@ func getQueries(basedir string, globs StringList) (*ast.QueryDocument, error) { } switch filepath.Ext(filename) { - case ".graphql": + case ".graphql", ".graphqls", ".gql": queryDoc, err := getQueriesFromString(string(text), basedir, filename) if err != nil { return nil, err From 9ddd26f81383af30e1b3bcbe2f8710bdb84b2ca8 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Sat, 17 Feb 2024 20:58:13 -0500 Subject: [PATCH 05/17] Add tests on Go 1.22, and upgrade x/tools to make them work (#315) I guess our x/tools was too old? I'm unclear on how x/tools versioning works with language versioning. Anyway, now it all works. Fixes #312. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable (n/a) - [x] Included a link to the issue fixed, if applicable - [x] Included documentation, for new features (n/a) - [x] Added an entry to the changelog --- .github/workflows/go.yml | 2 +- docs/CHANGELOG.md | 2 ++ go.mod | 5 ++--- go.sum | 12 +++++------- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index a0d704e2..0ad6d392 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.18', '1.19', '1.20', '1.21' ] + go: [ '1.18', '1.19', '1.20', '1.21', '1.22' ] steps: - name: Set up Go diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index be0c36f9..1405bef1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -20,6 +20,8 @@ When releasing a new version: +Note that genqlient is now tested through Go 1.22. + ### Breaking changes: ### New features: diff --git a/go.mod b/go.mod index bac27203..d775094b 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/bradleyjkemp/cupaloy/v2 v2.6.0 github.com/stretchr/testify v1.8.2 github.com/vektah/gqlparser/v2 v2.5.8 - golang.org/x/tools v0.9.3 + golang.org/x/tools v0.18.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -20,7 +20,6 @@ require ( github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/mod v0.10.0 // indirect - golang.org/x/sys v0.8.0 // indirect + golang.org/x/mod v0.15.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4321f309..e99c44a5 100644 --- a/go.sum +++ b/go.sum @@ -45,13 +45,11 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 4afbe4b0494a49a3fedf68fd12347ee0430cae64 Mon Sep 17 00:00:00 2001 From: Steve Coffman Date: Sun, 18 Feb 2024 11:39:14 -0500 Subject: [PATCH 06/17] Update gqlgen to latest (#317) Signed-off-by: Steve Coffman Signed-off-by: Steve Coffman --- go.mod | 10 ++++++---- go.sum | 36 +++++++++++++----------------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index d775094b..c011393e 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/Khan/genqlient go 1.18 require ( - github.com/99designs/gqlgen v0.17.35 + github.com/99designs/gqlgen v0.17.44 github.com/alexflint/go-arg v1.4.2 github.com/bradleyjkemp/cupaloy/v2 v2.6.0 - github.com/stretchr/testify v1.8.2 - github.com/vektah/gqlparser/v2 v2.5.8 + github.com/stretchr/testify v1.8.4 + github.com/vektah/gqlparser/v2 v2.5.11 golang.org/x/tools v0.18.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -16,10 +16,12 @@ require ( github.com/agnivade/levenshtein v1.1.1 // indirect github.com/alexflint/go-scalar v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/golang-lru/v2 v2.0.3 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/sosodev/duration v1.2.0 // indirect golang.org/x/mod v0.15.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index e99c44a5..668fa62a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/99designs/gqlgen v0.17.35 h1:r0KF1xL3cPMyUArNWeC3e2Ckuc4iiLm7bj5xzYZQYbQ= -github.com/99designs/gqlgen v0.17.35/go.mod h1:Vlf7TeY3ZdVI9SagB5IZE8CYhpq8kJPCVPJ7MrlVoX0= +github.com/99designs/gqlgen v0.17.44 h1:OS2wLk/67Y+vXM75XHbwRnNYJcbuJd4OBL76RX3NQQA= +github.com/99designs/gqlgen v0.17.44/go.mod h1:UTCu3xpK2mLI5qcMNw+HKDiEL77it/1XtAjisC4sLwM= github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= github.com/alexflint/go-arg v1.4.2 h1:lDWZAXxpAnZUq4qwb86p/3rIJJ2Li81EoMbTMujhVa0= @@ -7,7 +7,6 @@ github.com/alexflint/go-arg v1.4.2/go.mod h1:9iRbDxne7LcR/GSvEr7ma++GLpdIU1zrghf github.com/alexflint/go-scalar v1.0.0 h1:NGupf1XV/Xb04wXskDFzS0KWOLH632W/EO4fAFi+A70= github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxjy3MxYW0gjYw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs= @@ -17,43 +16,34 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru/v2 v2.0.3 h1:kmRrRLlInXvng0SmLxmQpQkpbYAvcXm7NPDrgxJa9mE= -github.com/hashicorp/golang-lru/v2 v2.0.3/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +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/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= -github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sosodev/duration v1.2.0 h1:pqK/FLSjsAADWY74SyWDCjOcd5l7H8GSnnOGEB9A1Us= +github.com/sosodev/duration v1.2.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg= 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= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/vektah/gqlparser/v2 v2.5.8 h1:pm6WOnGdzFOCfcQo9L3+xzW51mKrlwTEg4Wr7AH1JW4= -github.com/vektah/gqlparser/v2 v2.5.8/go.mod h1:z8xXUff237NntSuH8mLFijZ+1tjV1swDbpDqjJmk6ME= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= +github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/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= From 662ca8f1ee3491cb32d3420f0f43aef0b3542869 Mon Sep 17 00:00:00 2001 From: Steve Coffman Date: Sun, 18 Feb 2024 15:48:04 -0500 Subject: [PATCH 07/17] move genqlient Go module to 1.20 (#318) gqlgen's module supports Go 1.20+, and I suggest that genqlient should also do so. Go 1.20 was released more than 1 year ago (01 Feb 2023), and support ended on 06 Feb 2024. Each major Go release is supported until there are two newer major releases. In the past, we have only prolonged gqlgen/genqlient support for older Go when AppEngine Go was mired in the past. Currently, [Appengine no longer supports Go 1.18](https://cloud.google.com/appengine/docs/standard/go/release-notes), and supports up to Go 1.21. I will wait for possible objections for a few days before merging this PR. I also expect that we would want to cut a new release of genqlient either before or after merging this PR. Signed-off-by: Steve Coffman --------- Signed-off-by: Steve Coffman --- .github/workflows/go.yml | 2 +- docs/CHANGELOG.md | 4 +++- go.mod | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 0ad6d392..f8c928a5 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - go: [ '1.18', '1.19', '1.20', '1.21', '1.22' ] + go: [ '1.20', '1.21', '1.22' ] steps: - name: Set up Go diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1405bef1..4ad9c609 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -20,10 +20,12 @@ When releasing a new version: -Note that genqlient is now tested through Go 1.22. +Note that genqlient is now tested from Go 1.20 through Go 1.22. ### Breaking changes: +- genqlient now requires Go 1.20 or higher. It may still work with earlier versions, but we aren't explicitly keeping dependency modules to Go 1.18 and lower. + ### New features: - The new `optional: generic` allows using a generic type to represent optionality. See the [documentation](genqlient.yaml) for details. diff --git a/go.mod b/go.mod index c011393e..5741c420 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Khan/genqlient -go 1.18 +go 1.20 require ( github.com/99designs/gqlgen v0.17.44 From 5bdd7fd3ea9a766b7d1f1dfdbbcfdd443f3b5939 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Mon, 19 Feb 2024 11:41:13 -0500 Subject: [PATCH 08/17] Improve package-sniffing and bind correctly to types in the same package (#316) The original motivation here is to fix #283, which is that if you try to use `bindings` to bind to a type in the same package as the generated code, we generate a self-import, which Go doesn't allow. Fixing that is easy -- the three lines in `imports.go` -- once you know the package-path of the generated code. (The test that that all fits together is in integration-tests because that was the easiest place to set up the right situation.) Determining the package-path is not too much harder: you ask `go/packages`, which we already use for `package_bindings`. But once we're doing that, and handling errors, it's kinda silly that we ask you to specify the package your generated code will use, because we have that too, usually. So I rewrote that handling too, making `package` now rarely necessary (see for example the `example` config), and warning if it looks wrong. This is the changes in `config.go`, and is the more substantial non-test change. (I also renamed some of the testdata dirs to be valid package-names, to exercise more of that code, or in the case of `find-config`, just for consistency.) I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable (and checked that the test for the bugfix fails without the rest of the changes) - [x] Included a link to the issue fixed, if applicable - [x] Included documentation, for new features - [x] Added ~an entry~ two entries to the changelog --- docs/CHANGELOG.md | 2 + docs/genqlient.yaml | 14 ++- example/genqlient.yaml | 2 - generate/config.go | 98 +++++++++++++++---- generate/config_test.go | 6 +- generate/imports.go | 3 + generate/main.go | 5 + .../current/genqlient.yaml | 0 .../filenames/dotyaml/.genqlient.yaml | 0 .../filenames/dotyml/.genqlient.yml | 0 .../filenames/none/.gitkeep | 0 .../filenames/yaml/genqlient.yaml | 0 .../filenames/yml/genqlient.yml | 0 .../{find-config => findConfig}/none/.gitkeep | 0 .../none/child/.gitkeep | 0 .../parent/child/.gitkeep | 0 .../parent/genqlient.yaml | 0 .../InvalidCasing.yaml | 0 .../InvalidOptional.yaml | 0 .../InvalidPackage.yaml | 0 .../TestInvalidConfigs-CantGuessPackage.yaml | 3 - .../TestInvalidConfigs-InvalidCasing.yaml | 2 +- .../TestInvalidConfigs-InvalidOptional.yaml | 2 +- .../TestInvalidConfigs-InvalidPackage.yaml | 2 +- ...Simple.yaml => TestValidConfigs-Empty.yml} | 5 +- .../snapshots/TestValidConfigs-Lists.yaml | 13 +-- .../snapshots/TestValidConfigs-Strings.yaml | 9 +- generate/testdata/valid-config/Simple.yaml | 1 - .../Empty.yml} | 0 .../{valid-config => validConfig}/Lists.yaml | 2 - .../Strings.yaml | 1 - internal/integration/generated.go | 11 ++- internal/integration/genqlient.yaml | 2 + internal/integration/integration_test.go | 2 +- internal/integration/schema.graphql | 2 + internal/integration/server/gqlgen_exec.go | 83 ++++++++++++++++ internal/integration/server/gqlgen_models.go | 1 + internal/integration/server/server.go | 9 +- internal/integration/util.go | 6 ++ 39 files changed, 232 insertions(+), 54 deletions(-) rename generate/testdata/{find-config => findConfig}/current/genqlient.yaml (100%) rename generate/testdata/{find-config => findConfig}/filenames/dotyaml/.genqlient.yaml (100%) rename generate/testdata/{find-config => findConfig}/filenames/dotyml/.genqlient.yml (100%) rename generate/testdata/{find-config => findConfig}/filenames/none/.gitkeep (100%) rename generate/testdata/{find-config => findConfig}/filenames/yaml/genqlient.yaml (100%) rename generate/testdata/{find-config => findConfig}/filenames/yml/genqlient.yml (100%) rename generate/testdata/{find-config => findConfig}/none/.gitkeep (100%) rename generate/testdata/{find-config => findConfig}/none/child/.gitkeep (100%) rename generate/testdata/{find-config => findConfig}/parent/child/.gitkeep (100%) rename generate/testdata/{find-config => findConfig}/parent/genqlient.yaml (100%) rename generate/testdata/{invalid-config => invalidConfig}/InvalidCasing.yaml (100%) rename generate/testdata/{invalid-config => invalidConfig}/InvalidOptional.yaml (100%) rename generate/testdata/{invalid-config => invalidConfig}/InvalidPackage.yaml (100%) delete mode 100644 generate/testdata/snapshots/TestInvalidConfigs-CantGuessPackage.yaml rename generate/testdata/snapshots/{TestValidConfigs-Simple.yaml => TestValidConfigs-Empty.yml} (76%) delete mode 100644 generate/testdata/valid-config/Simple.yaml rename generate/testdata/{invalid-config/CantGuessPackage.yaml => validConfig/Empty.yml} (100%) rename generate/testdata/{valid-config => validConfig}/Lists.yaml (84%) rename generate/testdata/{valid-config => validConfig}/Strings.yaml (72%) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4ad9c609..78de6230 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -31,11 +31,13 @@ Note that genqlient is now tested from Go 1.20 through Go 1.22. - The new `optional: generic` allows using a generic type to represent optionality. See the [documentation](genqlient.yaml) for details. - For schemas with enum values that differ only in casing, it's now possible to disable smart-casing in genqlient.yaml; see the [documentation](genqlient.yaml) for `casing` for details. - Support .graphqls and .gql file extensions +- More accurately guess the package name for generated code (and warn if the config option -- now almost never needed -- looks wrong). ### Bug fixes: - The presence of negative pointer directives, i.e., `# @genqlient(pointer: false)` are now respected even in the when `optional: pointer` is set in the configuration file. - Made name collisions between query/mutation arguments and local function variables less likely. - Fix generation issue related to golang type implementation of complex graphql union fragments +- Bind correctly to types in the same package as the generated code. ## v0.6.0 diff --git a/docs/genqlient.yaml b/docs/genqlient.yaml index ddfb760b..55178a29 100644 --- a/docs/genqlient.yaml +++ b/docs/genqlient.yaml @@ -29,8 +29,12 @@ operations: # genqlient.yaml. Default: generated.go. generated: generated/genqlient.go -# The package name for the output code; defaults to the directory name of -# the generated-code file. +# The package name for the output code; defaults to the package-name +# corresponding to the setting of `generated`, above. +# +# This is rarely needed: only if you want the package-name to differ from the +# suffix of the package-path, and there are no other Go files in the package +# already. package: mygenerated # If set, a file at this path (relative to genqlient.yaml) will be generated @@ -139,6 +143,9 @@ optional_generic_type: github.com/organisation/repository/example.Type # guarantees that the fields requested in the query match those present in # the Go type. # +# Note: if binding to types in the same package as the generated code, make +# sure you don't bind to generated types! Otherwise, things get very circular. +# # To get equivalent behavior in just one query, use @genqlient(bind: ...); # see genqlient_directive.graphql for more details. bindings: @@ -224,6 +231,9 @@ bindings: # to the bindings map, above, for each exported type in the package. Multiple # packages may be specified, and later ones take precedence over earlier ones. # Explicit entries in bindings take precedence over all package bindings. +# +# Note: make sure this isn't the package with your generated code, or things +# will get circular very fast. package_bindings: - package: github.com/you/yourpkg/models diff --git a/example/genqlient.yaml b/example/genqlient.yaml index 11cd0348..1fe772a5 100644 --- a/example/genqlient.yaml +++ b/example/genqlient.yaml @@ -2,8 +2,6 @@ schema: schema.graphql operations: - genqlient.graphql generated: generated.go -# needed since it doesn't match the directory name: -package: main # We bind github's DateTime scalar type to Go's time.Time (which conveniently # already defines MarshalJSON and UnmarshalJSON). This means genqlient will diff --git a/generate/config.go b/generate/config.go index 94ee12c4..bca64945 100644 --- a/generate/config.go +++ b/generate/config.go @@ -47,6 +47,8 @@ type Config struct { // The directory of the config-file (relative to which all the other paths // are resolved). Set by ValidateAndFillDefaults. baseDir string + // The package-path into which we are generating. + pkgPath string } // A TypeBinding represents a Go type to which genqlient will bind a particular @@ -132,6 +134,52 @@ func pathJoin(a, b string) string { return filepath.Join(a, b) } +// Try to figure out the package-name and package-path of the given .go file. +// +// Returns a best-guess pkgName if possible, even on error. +func getPackageNameAndPath(filename string) (pkgName, pkgPath string, err error) { + abs, err := filepath.Abs(filename) + if err != nil { // path is totally bogus + return "", "", err + } + + dir := filepath.Dir(abs) + // If we don't get a clean answer from go/packages, we'll use the + // directory-name as a backup guess, as long as it's a valid identifier. + pkgNameGuess := filepath.Base(dir) + if !token.IsIdentifier(pkgNameGuess) { + pkgNameGuess = "" + } + + pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName}, dir) + if err != nil { // e.g. not in a Go module + return pkgNameGuess, "", err + } else if len(pkgs) != 1 { // probably never happens? + return pkgNameGuess, "", fmt.Errorf("found %v packages in %v, expected 1", len(pkgs), dir) + } + + pkg := pkgs[0] + // TODO(benkraft): Can PkgPath ever be empty while in a module? If so, we + // could warn. + if pkg.Name != "" { // found a good package! + return pkg.Name, pkg.PkgPath, nil + } + + // Package path is valid, but name is empty: probably an empty package + // (within a valid module). If the package-path-suffix is a valid + // identifier, that's a better guess than the directory-suffix, so use it. + pathSuffix := filepath.Base(pkg.PkgPath) + if token.IsIdentifier(pathSuffix) { + pkgNameGuess = pathSuffix + } + + if pkgNameGuess != "" { + return pkgNameGuess, pkg.PkgPath, nil + } else { + return "", "", fmt.Errorf("no package found in %v", dir) + } +} + // ValidateAndFillDefaults ensures that the configuration is valid, and fills // in any options that were unspecified. // @@ -167,29 +215,40 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error { "\nExample: \"github.com/Org/Repo/optional.Value\"") } - if c.Package != "" { - if !token.IsIdentifier(c.Package) { - // No need for link here -- if you're already setting the package - // you know where to set the package. - return errorf(nil, "invalid package in genqlient.yaml: '%v' is not a valid identifier", c.Package) - } - } else { - abs, err := filepath.Abs(c.Generated) - if err != nil { + if c.Package != "" && !token.IsIdentifier(c.Package) { + // No need for link here -- if you're already setting the package + // you know where to set the package. + return errorf(nil, "invalid package in genqlient.yaml: '%v' is not a valid identifier", c.Package) + } + + pkgName, pkgPath, err := getPackageNameAndPath(c.Generated) + if err != nil { + // Try to guess a name anyway (or use one you specified) -- pkgPath + // isn't always needed. (But you'll run into trouble binding against + // the generated package, so at least warn.) + if c.Package != "" { + warn(errorf(nil, "warning: unable to identify current package-path "+ + "(using 'package' config '%v'): %v\n", c.Package, err)) + } else if pkgName != "" { + warn(errorf(nil, "warning: unable to identify current package-path "+ + "(using directory name '%v': %v\n", pkgName, err)) + c.Package = pkgName + } else { return errorf(nil, "unable to guess package-name: %v"+ "\nSet package name in genqlient.yaml"+ "\nExample: https://github.com/Khan/genqlient/blob/main/example/genqlient.yaml#L6", err) } - - base := filepath.Base(filepath.Dir(abs)) - if !token.IsIdentifier(base) { - return errorf(nil, "unable to guess package-name: '%v' is not a valid identifier"+ - "\nSet package name in genqlient.yaml"+ - "\nExample: https://github.com/Khan/genqlient/blob/main/example/genqlient.yaml#L6", base) + } else { // err == nil + if c.Package == pkgName || c.Package == "" { + c.Package = pkgName + } else { + warn(errorf(nil, "warning: package setting in genqlient.yaml '%v' looks wrong "+ + "('%v' is in package '%v') but proceeding with '%v' anyway\n", + c.Package, c.Generated, pkgName, c.Package)) } - - c.Package = base } + // This is a no-op in some of the error cases, but it still doesn't hurt. + c.pkgPath = pkgPath if len(c.PackageBindings) > 0 { for _, binding := range c.PackageBindings { @@ -201,6 +260,11 @@ func (c *Config) ValidateAndFillDefaults(baseDir string) error { binding.Package) } + if binding.Package == c.pkgPath { + warn(errorf(nil, "warning: package_bindings set to the same package as your generated "+ + "code ('%v'); this may cause nondeterministic output due to circularity", c.pkgPath)) + } + mode := packages.NeedDeps | packages.NeedTypes pkgs, err := packages.Load(&packages.Config{ Mode: mode, diff --git a/generate/config_test.go b/generate/config_test.go index 004c8f49..d3ea4dbe 100644 --- a/generate/config_test.go +++ b/generate/config_test.go @@ -11,9 +11,9 @@ import ( ) const ( - findConfigDir = "testdata/find-config" - validConfigDir = "testdata/valid-config" - invalidConfigDir = "testdata/invalid-config" + findConfigDir = "testdata/findConfig" + validConfigDir = "testdata/validConfig" + invalidConfigDir = "testdata/invalidConfig" ) func TestFindCfg(t *testing.T) { diff --git a/generate/imports.go b/generate/imports.go index 5d6f0a29..5daee2cd 100644 --- a/generate/imports.go +++ b/generate/imports.go @@ -99,6 +99,9 @@ func (g *generator) ref(fullyQualifiedName string) (qualifiedName string, err er pkgPath := nameToImport[:i] localName := nameToImport[i+1:] + if pkgPath == g.Config.pkgPath { + return prefix + localName, nil + } alias, ok := g.imports[pkgPath] if !ok { if g.importsLocked { diff --git a/generate/main.go b/generate/main.go index 4c2d43ec..739e923b 100644 --- a/generate/main.go +++ b/generate/main.go @@ -14,6 +14,11 @@ import ( "github.com/alexflint/go-arg" ) +// TODO(benkraft): Make this mockable for tests? +func warn(err error) { + fmt.Println(err) +} + func readConfigGenerateAndWrite(configFilename string) error { var config *Config var err error diff --git a/generate/testdata/find-config/current/genqlient.yaml b/generate/testdata/findConfig/current/genqlient.yaml similarity index 100% rename from generate/testdata/find-config/current/genqlient.yaml rename to generate/testdata/findConfig/current/genqlient.yaml diff --git a/generate/testdata/find-config/filenames/dotyaml/.genqlient.yaml b/generate/testdata/findConfig/filenames/dotyaml/.genqlient.yaml similarity index 100% rename from generate/testdata/find-config/filenames/dotyaml/.genqlient.yaml rename to generate/testdata/findConfig/filenames/dotyaml/.genqlient.yaml diff --git a/generate/testdata/find-config/filenames/dotyml/.genqlient.yml b/generate/testdata/findConfig/filenames/dotyml/.genqlient.yml similarity index 100% rename from generate/testdata/find-config/filenames/dotyml/.genqlient.yml rename to generate/testdata/findConfig/filenames/dotyml/.genqlient.yml diff --git a/generate/testdata/find-config/filenames/none/.gitkeep b/generate/testdata/findConfig/filenames/none/.gitkeep similarity index 100% rename from generate/testdata/find-config/filenames/none/.gitkeep rename to generate/testdata/findConfig/filenames/none/.gitkeep diff --git a/generate/testdata/find-config/filenames/yaml/genqlient.yaml b/generate/testdata/findConfig/filenames/yaml/genqlient.yaml similarity index 100% rename from generate/testdata/find-config/filenames/yaml/genqlient.yaml rename to generate/testdata/findConfig/filenames/yaml/genqlient.yaml diff --git a/generate/testdata/find-config/filenames/yml/genqlient.yml b/generate/testdata/findConfig/filenames/yml/genqlient.yml similarity index 100% rename from generate/testdata/find-config/filenames/yml/genqlient.yml rename to generate/testdata/findConfig/filenames/yml/genqlient.yml diff --git a/generate/testdata/find-config/none/.gitkeep b/generate/testdata/findConfig/none/.gitkeep similarity index 100% rename from generate/testdata/find-config/none/.gitkeep rename to generate/testdata/findConfig/none/.gitkeep diff --git a/generate/testdata/find-config/none/child/.gitkeep b/generate/testdata/findConfig/none/child/.gitkeep similarity index 100% rename from generate/testdata/find-config/none/child/.gitkeep rename to generate/testdata/findConfig/none/child/.gitkeep diff --git a/generate/testdata/find-config/parent/child/.gitkeep b/generate/testdata/findConfig/parent/child/.gitkeep similarity index 100% rename from generate/testdata/find-config/parent/child/.gitkeep rename to generate/testdata/findConfig/parent/child/.gitkeep diff --git a/generate/testdata/find-config/parent/genqlient.yaml b/generate/testdata/findConfig/parent/genqlient.yaml similarity index 100% rename from generate/testdata/find-config/parent/genqlient.yaml rename to generate/testdata/findConfig/parent/genqlient.yaml diff --git a/generate/testdata/invalid-config/InvalidCasing.yaml b/generate/testdata/invalidConfig/InvalidCasing.yaml similarity index 100% rename from generate/testdata/invalid-config/InvalidCasing.yaml rename to generate/testdata/invalidConfig/InvalidCasing.yaml diff --git a/generate/testdata/invalid-config/InvalidOptional.yaml b/generate/testdata/invalidConfig/InvalidOptional.yaml similarity index 100% rename from generate/testdata/invalid-config/InvalidOptional.yaml rename to generate/testdata/invalidConfig/InvalidOptional.yaml diff --git a/generate/testdata/invalid-config/InvalidPackage.yaml b/generate/testdata/invalidConfig/InvalidPackage.yaml similarity index 100% rename from generate/testdata/invalid-config/InvalidPackage.yaml rename to generate/testdata/invalidConfig/InvalidPackage.yaml diff --git a/generate/testdata/snapshots/TestInvalidConfigs-CantGuessPackage.yaml b/generate/testdata/snapshots/TestInvalidConfigs-CantGuessPackage.yaml deleted file mode 100644 index 2571f712..00000000 --- a/generate/testdata/snapshots/TestInvalidConfigs-CantGuessPackage.yaml +++ /dev/null @@ -1,3 +0,0 @@ -invalid config file testdata/invalid-config/CantGuessPackage.yaml: unable to guess package-name: 'invalid-config' is not a valid identifier -Set package name in genqlient.yaml -Example: https://github.com/Khan/genqlient/blob/main/example/genqlient.yaml#L6 diff --git a/generate/testdata/snapshots/TestInvalidConfigs-InvalidCasing.yaml b/generate/testdata/snapshots/TestInvalidConfigs-InvalidCasing.yaml index 2bcb5e06..6b2cf919 100644 --- a/generate/testdata/snapshots/TestInvalidConfigs-InvalidCasing.yaml +++ b/generate/testdata/snapshots/TestInvalidConfigs-InvalidCasing.yaml @@ -1 +1 @@ -invalid config file testdata/invalid-config/InvalidCasing.yaml: unknown casing algorithm: bogus +invalid config file testdata/invalidConfig/InvalidCasing.yaml: unknown casing algorithm: bogus diff --git a/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml b/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml index fc5394bf..b6addeb0 100644 --- a/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml +++ b/generate/testdata/snapshots/TestInvalidConfigs-InvalidOptional.yaml @@ -1 +1 @@ -invalid config file testdata/invalid-config/InvalidOptional.yaml: optional must be one of: 'value' (default), 'pointer', or 'generic' +invalid config file testdata/invalidConfig/InvalidOptional.yaml: optional must be one of: 'value' (default), 'pointer', or 'generic' diff --git a/generate/testdata/snapshots/TestInvalidConfigs-InvalidPackage.yaml b/generate/testdata/snapshots/TestInvalidConfigs-InvalidPackage.yaml index a3d047a2..28af6380 100644 --- a/generate/testdata/snapshots/TestInvalidConfigs-InvalidPackage.yaml +++ b/generate/testdata/snapshots/TestInvalidConfigs-InvalidPackage.yaml @@ -1 +1 @@ -invalid config file testdata/invalid-config/InvalidPackage.yaml: invalid package in genqlient.yaml: 'bogus-package-name' is not a valid identifier +invalid config file testdata/invalidConfig/InvalidPackage.yaml: invalid package in genqlient.yaml: 'bogus-package-name' is not a valid identifier diff --git a/generate/testdata/snapshots/TestValidConfigs-Simple.yaml b/generate/testdata/snapshots/TestValidConfigs-Empty.yml similarity index 76% rename from generate/testdata/snapshots/TestValidConfigs-Simple.yaml rename to generate/testdata/snapshots/TestValidConfigs-Empty.yml index 77cc1b32..709dea13 100644 --- a/generate/testdata/snapshots/TestValidConfigs-Simple.yaml +++ b/generate/testdata/snapshots/TestValidConfigs-Empty.yml @@ -1,7 +1,7 @@ (*generate.Config)({ Schema: (generate.StringList) , Operations: (generate.StringList) , - Generated: (string) (len=34) "testdata/valid-config/generated.go", + Generated: (string) (len=33) "testdata/validConfig/generated.go", Package: (string) (len=11) "validConfig", ExportOperations: (string) "", ContextType: (string) (len=15) "context.Context", @@ -17,5 +17,6 @@ StructReferences: (bool) false, Extensions: (bool) false, AllowBrokenFeatures: (bool) false, - baseDir: (string) (len=21) "testdata/valid-config" + baseDir: (string) (len=20) "testdata/validConfig", + pkgPath: (string) (len=55) "github.com/Khan/genqlient/generate/testdata/validConfig" }) diff --git a/generate/testdata/snapshots/TestValidConfigs-Lists.yaml b/generate/testdata/snapshots/TestValidConfigs-Lists.yaml index 7ff70462..1c10e82a 100644 --- a/generate/testdata/snapshots/TestValidConfigs-Lists.yaml +++ b/generate/testdata/snapshots/TestValidConfigs-Lists.yaml @@ -1,13 +1,13 @@ (*generate.Config)({ Schema: (generate.StringList) (len=2) { - (string) (len=42) "testdata/valid-config/first_schema.graphql", - (string) (len=43) "testdata/valid-config/second_schema.graphql" + (string) (len=41) "testdata/validConfig/first_schema.graphql", + (string) (len=42) "testdata/validConfig/second_schema.graphql" }, Operations: (generate.StringList) (len=2) { - (string) (len=46) "testdata/valid-config/first_operations.graphql", - (string) (len=47) "testdata/valid-config/second_operations.graphql" + (string) (len=45) "testdata/validConfig/first_operations.graphql", + (string) (len=46) "testdata/validConfig/second_operations.graphql" }, - Generated: (string) (len=34) "testdata/valid-config/generated.go", + Generated: (string) (len=33) "testdata/validConfig/generated.go", Package: (string) (len=11) "validConfig", ExportOperations: (string) "", ContextType: (string) (len=15) "context.Context", @@ -23,5 +23,6 @@ StructReferences: (bool) false, Extensions: (bool) false, AllowBrokenFeatures: (bool) false, - baseDir: (string) (len=21) "testdata/valid-config" + baseDir: (string) (len=20) "testdata/validConfig", + pkgPath: (string) (len=55) "github.com/Khan/genqlient/generate/testdata/validConfig" }) diff --git a/generate/testdata/snapshots/TestValidConfigs-Strings.yaml b/generate/testdata/snapshots/TestValidConfigs-Strings.yaml index 1638e8da..c47d4ad2 100644 --- a/generate/testdata/snapshots/TestValidConfigs-Strings.yaml +++ b/generate/testdata/snapshots/TestValidConfigs-Strings.yaml @@ -1,11 +1,11 @@ (*generate.Config)({ Schema: (generate.StringList) (len=1) { - (string) (len=36) "testdata/valid-config/schema.graphql" + (string) (len=35) "testdata/validConfig/schema.graphql" }, Operations: (generate.StringList) (len=1) { - (string) (len=40) "testdata/valid-config/operations.graphql" + (string) (len=39) "testdata/validConfig/operations.graphql" }, - Generated: (string) (len=34) "testdata/valid-config/generated.go", + Generated: (string) (len=33) "testdata/validConfig/generated.go", Package: (string) (len=11) "validConfig", ExportOperations: (string) "", ContextType: (string) (len=15) "context.Context", @@ -21,5 +21,6 @@ StructReferences: (bool) false, Extensions: (bool) false, AllowBrokenFeatures: (bool) false, - baseDir: (string) (len=21) "testdata/valid-config" + baseDir: (string) (len=20) "testdata/validConfig", + pkgPath: (string) (len=55) "github.com/Khan/genqlient/generate/testdata/validConfig" }) diff --git a/generate/testdata/valid-config/Simple.yaml b/generate/testdata/valid-config/Simple.yaml deleted file mode 100644 index 3084ef55..00000000 --- a/generate/testdata/valid-config/Simple.yaml +++ /dev/null @@ -1 +0,0 @@ -package: validConfig diff --git a/generate/testdata/invalid-config/CantGuessPackage.yaml b/generate/testdata/validConfig/Empty.yml similarity index 100% rename from generate/testdata/invalid-config/CantGuessPackage.yaml rename to generate/testdata/validConfig/Empty.yml diff --git a/generate/testdata/valid-config/Lists.yaml b/generate/testdata/validConfig/Lists.yaml similarity index 84% rename from generate/testdata/valid-config/Lists.yaml rename to generate/testdata/validConfig/Lists.yaml index 8348498b..e3a8af11 100644 --- a/generate/testdata/valid-config/Lists.yaml +++ b/generate/testdata/validConfig/Lists.yaml @@ -5,5 +5,3 @@ schema: operations: - first_operations.graphql - second_operations.graphql - -package: validConfig diff --git a/generate/testdata/valid-config/Strings.yaml b/generate/testdata/validConfig/Strings.yaml similarity index 72% rename from generate/testdata/valid-config/Strings.yaml rename to generate/testdata/validConfig/Strings.yaml index 7830e0f9..d4a4957f 100644 --- a/generate/testdata/valid-config/Strings.yaml +++ b/generate/testdata/validConfig/Strings.yaml @@ -1,3 +1,2 @@ schema: schema.graphql operations: operations.graphql -package: validConfig diff --git a/internal/integration/generated.go b/internal/integration/generated.go index 1f0e079d..2183ce82 100644 --- a/internal/integration/generated.go +++ b/internal/integration/generated.go @@ -3056,9 +3056,10 @@ func (v *simpleQueryExtResponse) GetMe() simpleQueryExtMeUser { return v.Me } // simpleQueryMeUser includes the requested fields of the GraphQL type User. type simpleQueryMeUser struct { - Id string `json:"id"` - Name string `json:"name"` - LuckyNumber int `json:"luckyNumber"` + Id string `json:"id"` + Name string `json:"name"` + LuckyNumber int `json:"luckyNumber"` + GreatScalar MyGreatScalar `json:"greatScalar"` } // GetId returns simpleQueryMeUser.Id, and is useful for accessing the field via an interface. @@ -3070,6 +3071,9 @@ func (v *simpleQueryMeUser) GetName() string { return v.Name } // GetLuckyNumber returns simpleQueryMeUser.LuckyNumber, and is useful for accessing the field via an interface. func (v *simpleQueryMeUser) GetLuckyNumber() int { return v.LuckyNumber } +// GetGreatScalar returns simpleQueryMeUser.GreatScalar, and is useful for accessing the field via an interface. +func (v *simpleQueryMeUser) GetGreatScalar() MyGreatScalar { return v.GreatScalar } + // simpleQueryResponse is returned by simpleQuery on success. type simpleQueryResponse struct { Me simpleQueryMeUser `json:"me"` @@ -3656,6 +3660,7 @@ query simpleQuery { id name luckyNumber + greatScalar } } ` diff --git a/internal/integration/genqlient.yaml b/internal/integration/genqlient.yaml index db6f988f..8f57602a 100644 --- a/internal/integration/genqlient.yaml +++ b/internal/integration/genqlient.yaml @@ -8,3 +8,5 @@ bindings: type: time.Time marshaler: "github.com/Khan/genqlient/internal/testutil.MarshalDate" unmarshaler: "github.com/Khan/genqlient/internal/testutil.UnmarshalDate" + MyGreatScalar: + type: github.com/Khan/genqlient/internal/integration.MyGreatScalar diff --git a/internal/integration/integration_test.go b/internal/integration/integration_test.go index 0e056fab..37b02425 100644 --- a/internal/integration/integration_test.go +++ b/internal/integration/integration_test.go @@ -20,7 +20,7 @@ import ( func TestSimpleQuery(t *testing.T) { _ = `# @genqlient - query simpleQuery { me { id name luckyNumber } }` + query simpleQuery { me { id name luckyNumber greatScalar } }` ctx := context.Background() server := server.RunServer() diff --git a/internal/integration/schema.graphql b/internal/integration/schema.graphql index 7857a4a7..17e20e2d 100644 --- a/internal/integration/schema.graphql +++ b/internal/integration/schema.graphql @@ -1,4 +1,5 @@ scalar Date +scalar MyGreatScalar type Query { me: User @@ -23,6 +24,7 @@ type User implements Being & Lucky { hair: Hair birthdate: Date friends: [User!]! + greatScalar: MyGreatScalar } input NewUser { diff --git a/internal/integration/server/gqlgen_exec.go b/internal/integration/server/gqlgen_exec.go index 10b07b03..e1dce252 100644 --- a/internal/integration/server/gqlgen_exec.go +++ b/internal/integration/server/gqlgen_exec.go @@ -78,6 +78,7 @@ type ComplexityRoot struct { User struct { Birthdate func(childComplexity int) int Friends func(childComplexity int) int + GreatScalar func(childComplexity int) int Hair func(childComplexity int) int ID func(childComplexity int) int LuckyNumber func(childComplexity int) int @@ -288,6 +289,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.User.Friends(childComplexity), true + case "User.greatScalar": + if e.complexity.User.GreatScalar == nil { + break + } + + return e.complexity.User.GreatScalar(childComplexity), true + case "User.hair": if e.complexity.User.Hair == nil { break @@ -423,6 +431,7 @@ func (ec *executionContext) introspectType(name string) (*introspection.Type, er var sources = []*ast.Source{ {Name: "../schema.graphql", Input: `scalar Date +scalar MyGreatScalar type Query { me: User @@ -447,6 +456,7 @@ type User implements Being & Lucky { hair: Hair birthdate: Date friends: [User!]! + greatScalar: MyGreatScalar } input NewUser { @@ -1022,6 +1032,8 @@ func (ec *executionContext) fieldContext_Mutation_createUser(ctx context.Context return ec.fieldContext_User_birthdate(ctx, field) case "friends": return ec.fieldContext_User_friends(ctx, field) + case "greatScalar": + return ec.fieldContext_User_greatScalar(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1088,6 +1100,8 @@ func (ec *executionContext) fieldContext_Query_me(ctx context.Context, field gra return ec.fieldContext_User_birthdate(ctx, field) case "friends": return ec.fieldContext_User_friends(ctx, field) + case "greatScalar": + return ec.fieldContext_User_greatScalar(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1143,6 +1157,8 @@ func (ec *executionContext) fieldContext_Query_user(ctx context.Context, field g return ec.fieldContext_User_birthdate(ctx, field) case "friends": return ec.fieldContext_User_friends(ctx, field) + case "greatScalar": + return ec.fieldContext_User_greatScalar(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1371,6 +1387,8 @@ func (ec *executionContext) fieldContext_Query_usersBornOn(ctx context.Context, return ec.fieldContext_User_birthdate(ctx, field) case "friends": return ec.fieldContext_User_friends(ctx, field) + case "greatScalar": + return ec.fieldContext_User_greatScalar(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1440,6 +1458,8 @@ func (ec *executionContext) fieldContext_Query_usersBornOnDates(ctx context.Cont return ec.fieldContext_User_birthdate(ctx, field) case "friends": return ec.fieldContext_User_friends(ctx, field) + case "greatScalar": + return ec.fieldContext_User_greatScalar(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1506,6 +1526,8 @@ func (ec *executionContext) fieldContext_Query_userSearch(ctx context.Context, f return ec.fieldContext_User_birthdate(ctx, field) case "friends": return ec.fieldContext_User_friends(ctx, field) + case "greatScalar": + return ec.fieldContext_User_greatScalar(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1960,6 +1982,8 @@ func (ec *executionContext) fieldContext_User_friends(ctx context.Context, field return ec.fieldContext_User_birthdate(ctx, field) case "friends": return ec.fieldContext_User_friends(ctx, field) + case "greatScalar": + return ec.fieldContext_User_greatScalar(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type User", field.Name) }, @@ -1967,6 +1991,47 @@ func (ec *executionContext) fieldContext_User_friends(ctx context.Context, field return fc, nil } +func (ec *executionContext) _User_greatScalar(ctx context.Context, field graphql.CollectedField, obj *User) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_User_greatScalar(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.GreatScalar, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.(*string) + fc.Result = res + return ec.marshalOMyGreatScalar2ᚖstring(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_User_greatScalar(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "User", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type MyGreatScalar does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) ___Directive_name(ctx context.Context, field graphql.CollectedField, obj *introspection.Directive) (ret graphql.Marshaler) { fc, err := ec.fieldContext___Directive_name(ctx, field) if err != nil { @@ -4255,6 +4320,8 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { out.Invalids++ } + case "greatScalar": + out.Values[i] = ec._User_greatScalar(ctx, field, obj) default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -5209,6 +5276,22 @@ func (ec *executionContext) marshalOLucky2githubᚗcomᚋKhanᚋgenqlientᚋinte return ec._Lucky(ctx, sel, v) } +func (ec *executionContext) unmarshalOMyGreatScalar2ᚖstring(ctx context.Context, v interface{}) (*string, error) { + if v == nil { + return nil, nil + } + res, err := graphql.UnmarshalString(v) + return &res, graphql.ErrorOnPath(ctx, err) +} + +func (ec *executionContext) marshalOMyGreatScalar2ᚖstring(ctx context.Context, sel ast.SelectionSet, v *string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + res := graphql.MarshalString(*v) + return res +} + func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { if v == nil { return nil, nil diff --git a/internal/integration/server/gqlgen_models.go b/internal/integration/server/gqlgen_models.go index 1ac2e113..0c28bc55 100644 --- a/internal/integration/server/gqlgen_models.go +++ b/internal/integration/server/gqlgen_models.go @@ -50,6 +50,7 @@ type User struct { Hair *Hair `json:"hair,omitempty"` Birthdate *string `json:"birthdate,omitempty"` Friends []*User `json:"friends"` + GreatScalar *string `json:"greatScalar,omitempty"` } func (User) IsBeing() {} diff --git a/internal/integration/server/server.go b/internal/integration/server/server.go index 00dbb308..a8a45a72 100644 --- a/internal/integration/server/server.go +++ b/internal/integration/server/server.go @@ -17,10 +17,11 @@ func intptr(v int) *int { return &v } var users = []*User{ { ID: "1", Name: "Yours Truly", LuckyNumber: intptr(17), - Birthdate: strptr("2025-01-01"), - Hair: &Hair{Color: strptr("Black")}, + Birthdate: strptr("2025-01-01"), + Hair: &Hair{Color: strptr("Black")}, + GreatScalar: strptr("cool value"), }, - {ID: "2", Name: "Raven", LuckyNumber: intptr(-1), Hair: nil}, + {ID: "2", Name: "Raven", LuckyNumber: intptr(-1), Hair: nil, GreatScalar: strptr("cool value")}, } func init() { @@ -177,4 +178,4 @@ func (r *resolver) Mutation() MutationResolver { func (r *resolver) Query() QueryResolver { return &queryResolver{} } -//go:generate go run github.com/99designs/gqlgen +//go:generate go run github.com/99designs/gqlgen@v0.17.35 diff --git a/internal/integration/util.go b/internal/integration/util.go index 0e0a5127..b55e9b60 100644 --- a/internal/integration/util.go +++ b/internal/integration/util.go @@ -62,3 +62,9 @@ func RunGenerateTest(t *testing.T, relConfigFilename string) { } } } + +// Used for a binding in genqlient.yaml. +// +// This is here rather than in testutil to test the case where the generated +// code and the bound type are in the same package. +type MyGreatScalar string From ae275ac62108444eb42e232c256594b452bdd16b Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Mon, 19 Feb 2024 21:02:11 -0800 Subject: [PATCH 09/17] Reorganize and improve documentation (#322) We've been in need of a documentation revamp for a while; a giant FAQ was never the greatest structure and got worse as it grew. In this commit I reorganize the documentation. Most of it is just moving around existing text, but I added some new documentation here and there. The changes: - Many of the FAQ questions have moved to several new docs, on the client/runtime, handling your schema, and various operation types; the FAQ has answers to a few of the actually most frequent questions, as well as a few things that didn't fit elsewhere. - We now have a `docs/README.md` which acts as an index, so we can just link to `/docs`. - I lowercased the files that don't need match a GitHub convention, so it's a bit less yell-y. - I added documentation on: - how we version genqlient (fixes #63) - newer options for optional types - a bit more on custom scalars and integer types (fixes #128) Reviewers, you can see what it all looks like together [here](https://github.com/Khan/genqlient/tree/benkraft.docs-reorg/docs#readme). Fixes #245. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable (n/a) - [x] Included a link to the issue fixed, if applicable - [x] Included documentation, for new features - [x] Added an entry to the changelog (n/a) --- README.md | 4 +- docs/CHANGELOG.md | 6 +- docs/CONTRIBUTING.md | 8 +- docs/FAQ.md | 564 ------------------ docs/README.md | 34 ++ docs/client_config.md | 157 +++++ docs/{DESIGN.md => design.md} | 0 docs/faq.md | 121 ++++ docs/genqlient_directive.graphql | 2 +- docs/{INTRODUCTION.md => introduction.md} | 6 +- docs/operations.md | 408 +++++++++++++ docs/schema.md | 130 ++++ docs/versioning.md | 30 + generate/convert.go | 4 +- generate/names.go | 4 +- .../queries/ComplexInlineFragments.graphql | 2 +- ....graphql-ComplexInlineFragments.graphql.go | 2 +- 17 files changed, 898 insertions(+), 584 deletions(-) delete mode 100644 docs/FAQ.md create mode 100644 docs/README.md create mode 100644 docs/client_config.md rename docs/{DESIGN.md => design.md} (100%) create mode 100644 docs/faq.md rename docs/{INTRODUCTION.md => introduction.md} (85%) create mode 100644 docs/operations.md create mode 100644 docs/schema.md create mode 100644 docs/versioning.md diff --git a/README.md b/README.md index e1e9fa34..d26d0c50 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ genqlient provides: ## How do I use genqlient? -You can download and run genqlient the usual way: `go run github.com/Khan/genqlient`. To set your project up to use genqlient, see the [getting started guide](docs/INTRODUCTION.md), or the [example](example). For more complete documentation, see the [docs](docs). +You can download and run genqlient the usual way: `go run github.com/Khan/genqlient`. To set your project up to use genqlient, see the [getting started guide](docs/introduction.md), or the [example](example). For more complete documentation, see the [docs](docs). ## How can I help? @@ -47,6 +47,6 @@ This code works, but it has a few problems: - The GraphQL variables aren't type-safe at all; you could have passed `{"id": true}` and again you won't know until runtime! - You have to write everything twice, or hide the query in complicated struct tags, or give up what type safety you do have and resort to `interface{}`. -These problems aren't a big deal in a small application, but for serious production-grade tools they're not ideal. And they should be entirely avoidable: GraphQL and Go are both typed languages; and GraphQL servers expose their schema in a standard, machine-readable format. We should be able to simply write a query and have that automatically validated against the schema and turned into a Go struct which we can use in our code. In fact, there's already good prior art to do this sort of thing: [99designs/gqlgen](https://github.com/99designs/gqlgen) is a popular server library that generates types, and Apollo has a [codegen tool](https://www.apollographql.com/docs/devtools/cli/#supported-commands) to generate similar client-types for several other languages. (See [docs/DESIGN.md](docs/DESIGN.md) for more prior art.) +These problems aren't a big deal in a small application, but for serious production-grade tools they're not ideal. And they should be entirely avoidable: GraphQL and Go are both typed languages; and GraphQL servers expose their schema in a standard, machine-readable format. We should be able to simply write a query and have that automatically validated against the schema and turned into a Go struct which we can use in our code. In fact, there's already good prior art to do this sort of thing: [99designs/gqlgen](https://github.com/99designs/gqlgen) is a popular server library that generates types, and Apollo has a [codegen tool](https://www.apollographql.com/docs/devtools/cli/#supported-commands) to generate similar client-types for several other languages. (See the [design note](docs/design.md) for more prior art.) genqlient fills that gap: you just specify the query, and it generates type-safe helpers, validated against the schema, that make the query. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 78de6230..ab9b287c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -113,9 +113,9 @@ Version 0.3.0 adds several new configuration options, allowing simplification of ### New features: -- genqlient's types are now safe to JSON-marshal, which can be useful for putting them in a cache, for example. See the [docs](FAQ.md#-let-me-json-marshal-my-response-objects) for details. -- The new `flatten` option in the `# @genqlient` directive allows for a simpler form of type-sharing using fragment spreads. See the [docs](FAQ.md#-shared-types-between-different-parts-of-the-query) for details. -- The new `for` option in the `# @genqlient` directive allows applying options to a particular field anywhere it appears in the query. This is especially useful for fields of input types, for which there is otherwise no way to specify options; see the [documentation on handling nullable fields](FAQ.md#-nullable-fields) for an example, and the [`# @genqlient` directive reference](genqlient_directive.graphql) for the full details. +- genqlient's types are now safe to JSON-marshal, which can be useful for putting them in a cache, for example. See the [docs](client_config.md#marshaling) for details. +- The new `flatten` option in the `# @genqlient` directive allows for a simpler form of type-sharing using fragment spreads. See the [docs](operations.md#sharing-types) for details. +- The new `for` option in the `# @genqlient` directive allows applying options to a particular field anywhere it appears in the query. This is especially useful for fields of input types, for which there is otherwise no way to specify options; see the [documentation on handling nullable fields](operations.md#nullable-fields) for an example, and the [`# @genqlient` directive reference](genqlient_directive.graphql) for the full details. ### Bug fixes: diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index c4a845d6..0094a415 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -22,9 +22,9 @@ Pull requests should have: - documentation, for new features - changelog entries -Pull requests will be squash-merged, so subsequent commit messages may be brief (e.g. "review comments"). +The PR description template will remind you of these. Pull requests will be squash-merged, so subsequent commit messages may be brief (e.g. "review comments"). -Large changes should typically be discussed on the issue tracker first, and should ideally be broken up into separate PRs, or failing that, several commits, for ease of reviewing. +Large changes should typically be discussed on the issue tracker first, and should ideally be broken up into separate PRs, or failing that, several commits, for ease of reviewing. This is especially true of breaking changes; see the [versioning policy](versioning.md) for what we consider breaking. ## Style @@ -44,11 +44,11 @@ If you update any code-generation logic or templates, even if no new tests are n ## Finding your way around -If you're new to genqlient, start out by reading the source of `generate.Generate`, whose comments describe most of the high-level operation of genqlient. In general, the code is documented inline, often with an introductory comment at the top of the file. See [DESIGN.md](DESIGN.md) for documentation of major design decisions, which is a good way to get a sense of why genqlient is structured the way it is. +If you're new to genqlient, start out by reading the source of `generate.Generate`, whose comments describe most of the high-level operation of genqlient. In general, the code is documented inline, often with an introductory comment at the top of the file. See the [design note](design.md) for documentation of major design decisions, which is a good way to get a sense of why genqlient is structured the way it is. ## Making a release -We try to cut releases periodically. To make a release: +See the [versioning strategy](versioning.md) for when to make a release. To make a release: - Scan PRs since the last release to check we didn't miss anything in the changelog. - Check if there are any regressions or major problems with new features we want to fix before cutting the release. diff --git a/docs/FAQ.md b/docs/FAQ.md deleted file mode 100644 index 35b084c1..00000000 --- a/docs/FAQ.md +++ /dev/null @@ -1,564 +0,0 @@ -# Frequently Asked Questions - -This document describes common questions about genqlient, and provides an index to how to represent common query structures. For a full list of configuration options, see [genqlient.yaml](genqlient.yaml) and [genqlient_directive.graphql](genqlient_directive.graphql). - -## How do I set up genqlient to … - -### … get started? - -There's a [doc for that](INTRODUCTION.md)! - -### … use GET requests instead of POST requests? - -You can use `graphql.NewClientUsingGet` to create a client that will use query parameters to create the request. For example: -```go -ctx := context.Background() -client := graphql.NewClientUsingGet("https://api.github.com/graphql", http.DefaultClient) -resp, err := getUser(ctx, client, "benjaminjkraft") -fmt.Println(resp.User.Name, err) -``` - -This request will be sent via an HTTP GET request, with the query, operation name and variables encoded in the URL. - -For example, if the query is defined as: - -```graphql -query getUser($login: String!) { - user(login: $login) { - name - } -} -``` - -The URL requested will be: - -`https://api.github.com/graphql?operationName%3DgetUser%26query%3D%0Aquery%20getUser(%24login%3A%20String!)%20%7B%0A%20%20user(login%3A%20%24login)%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%7D%0A%26variables%3D%7B%22login%22%3A%22benjaminjkraft%22%7D` - -The client does not support mutations, and will return an error if passed a request that attempts one. - -### … use an API that requires authentication? - -When you call `graphql.NewClient`, pass in an HTTP client that adds whatever authentication headers you need (typically by wrapping the client's `Transport`). For example: - -```go -type authedTransport struct { - wrapped http.RoundTripper -} - -func (t *authedTransport) RoundTrip(req *http.Request) (*http.Response, error) { - key := ... - req.Header.Set("Authorization", "bearer "+key) - return t.wrapped.RoundTrip(req) -} - -func MakeQuery(...) { - client := graphql.NewClient("https://api.github.com/graphql", - &http.Client{Transport: &authedTransport{wrapped: http.DefaultTransport}}) - - resp, err := MyQuery(ctx, client, ...) -} -``` - -For more on wrapping HTTP clients, see [this post](https://dev.to/stevenacoffman/tripperwares-http-client-middleware-chaining-roundtrippers-3o00). - -### … make requests against a mock server, for tests? - -Testing code that uses genqlient typically involves passing in a special HTTP client that does what you want, similar to authentication. For example, you might write a client whose `RoundTrip` returns a fixed response, constructed with [`httptest`](https://pkg.go.dev/net/http/httptest). Or, you can use `httptest` to start up a temporary server, and point genqlient at that. Many third-party packages provide support for this sort of thing; genqlient should work with any HTTP-level mocking that can expose a regular `http.Client`. - -### … test my GraphQL APIs? - -If you want, you can use genqlient to test your GraphQL APIs; as with mocking you can point genqlient at anything that exposes an ordinary HTTP endpoint or a custom `http.Client`. However, at Khan Academy we've found that genqlient usually isn't the best client for testing; we prefer to use a lightweight (and weakly-typed) client for that, and may separately open-source ours in the future. - -### … handle GraphQL errors? - -Each genqlient-generated helper function returns two results, a pointer to a response-struct, and an error. The response-struct will always be initialized (never nil), even on error. If the request returns a valid GraphQL response containing errors, the returned error will be [`As`-able](https://pkg.go.dev/errors#As) as [`gqlerror.List`](https://pkg.go.dev/github.com/vektah/gqlparser/v2/gqlerror#List), and the struct may be partly-populated (if one field failed but another was computed successfully). If the request fails entirely, the error will be another error (e.g. a [`*url.Error`](https://pkg.go.dev/net/url#Error)), and the response will be blank (but still non-nil). - -For example, you might do one of the following: -```go -// return both error and field: -resp, err := GetUser(...) -return resp.User.Name, err - -// handle different errors differently: -resp, err := GetUser(...) -var errList *gqlerror.List -if errors.As(err, &errList) { - for _, err := range errList { - fmt.Printf("%v at %v\n", err.Message, err.Path) - } - fmt.Printf("partial response: %v\n", resp) -} else if err != nil { - fmt.Printf("http/network error: %v\n", err) -} else { - fmt.Printf("successful response: %v\n", resp) -} -``` - -### … use custom scalars? - -Just tell genqlient via the `bindings` option in `genqlient.yaml`: - -```yaml -bindings: - DateTime: - type: time.Time -``` - -Make sure the given type has whatever logic is needed to convert to/from JSON (e.g. `MarshalJSON`/`UnmarshalJSON` or JSON tags). See the [`genqlient.yaml` documentation](genqlient.yaml) for the full syntax. - -### … require 32-bit integers? - -The GraphQL spec officially defines the `Int` type to be a [signed 32-bit integer](https://spec.graphql.org/draft/#sec-Int). GraphQL clients and servers vary wildly in their enforcement of this; for example: -- [Apollo Server](https://github.com/apollographql/apollo-server/) explicitly checks that integers are at most 32 bits -- [gqlgen](https://github.com/99designs/gqlgen) by default allows any integer that fits in `int` (i.e. 64 bits on most platforms) -- [Apollo Client](https://github.com/apollographql/apollo-client) doesn't check (but implicitly is limited to 53 bits by JavaScript) -- [shurcooL/graphql](https://github.com/shurcooL/graphql) requires integers be passed as a `graphql.Int`, defined to be an `int32` - -By default, genqlient maps GraphQL `Int`s to Go's `int`, meaning that on 64 bit systems there's no client-side restriction. If you prefer to limit integers to `int32`, you can set a binding in your `genqlient.yaml`: - -```yaml -bindings: - Int: - type: int32 -``` - -Or, you can bind it to any other type, perhaps one with size-checked constructors; see the [`genqlient.yaml` documentation](genqlient.yaml) for more details. - -### … let me json-marshal my response objects? - -This is supported by default! All genqlient-generated types support both JSON-marshaling and unmarshaling, which can be useful for putting them in a cache, inspecting them by hand, using them in mocks (although this is [not recommended](#-test-my-graphql-apis)), or anything else you can do with JSON. It's not guaranteed that marshaling a genqlient type will produce the exact GraphQL input -- we try to get as close as we can but there are some limitations around Go zero values -- but unmarshaling again should produce the value genqlient returned. That is: - -```go -resp, err := MyQuery(...) -// not guaranteed to match what the server sent (but close): -b, err := json.Marshal(resp) -// guaranteed to match resp: -var respAgain MyQueryResponse -err := json.Unmarshal(b, &resp) -``` - -### … let me use introspection to fetch my client schema? - -This is currently not supported by default. You can however use a tool such as [gqlfetch](https://github.com/suessflorian/gqlfetch) to build your client schema using introspection and then let `genqlient` continue from there. Moreover, you can define yourself what happens when `go:generate` is run via managing your own _go runnable_ progam. - -For example - suppose the file `generate/main.go`; - -```go -package main - -import ( - "context" - "fmt" - "os" - - "github.com/Khan/genqlient/generate" - "github.com/suessflorian/gqlfetch" -) - -func main() { - schema, err := gqlfetch.BuildClientSchema(context.Background(), "http://localhost:8080/query") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - if err = os.WriteFile("schema.graphql", []byte(schema), 0644); err != nil { - fmt.Println(err) - os.Exit(1) - } - - generate.Main() -} -``` - -This can now be invoked upon `go generate` via `//go:generate yourpkg/generate`. - -## How do I make a query with … - -### … a specific name for a field? - -genqlient supports GraphQL field-aliases, and uses them to determine the Go struct field name. For example, if you do -```graphql -query MyQuery { - myGreatName: myString -} -``` -and genqlient will generate a Go field `MyGreatName string`. Note that the alias will always be uppercased, to ensure the field is visible to the Go JSON library. - -### … nullable fields? - -There are two ways to handle nullable fields in genqlient. One way is to use the Go idiom, where null gets mapped to the zero value; this is the default in genqlient. So if you have a GraphQL field of type `String`, and you do: - -```graphql -query MyQuery(arg: String) { - myString -} -``` - -then genqlient will generate a Go field `MyString string`, and set it to the empty string if the server returns null. This works even for structs: if an object type in GraphQL is null, genqlient will set the corresponding struct to its zero value. It can be helpful to request `id` in such cases, since that’s a field that should always be set, or `__typename` which is guaranteed to be set, so you can use its presence to decide whether to look at the other fields. - -For input fields, you often want to tell genqlient to send null to the server if the argument is set to the zero value, similar to the JSON `omitempty` tag. In this case, you can do: - -```graphql -query MyQuery( - # @genqlient(omitempty: true) - arg: String, -) { - myString -} -``` - -You can also put the `# @genqlient(omitempty: true)` on the first line, which will apply it to all arguments in the query, or `# @genqlient(for: "MyInput.myField", omitempty: true)` on the first line to apply it to a particular field of a particular input type used by the query (for which there would otherwise be no place to put the directive, as the field never appears explicitly in the query, but only in the schema). - -If you need to distinguish null from the empty string (or generally from the Go zero value of your type), you can tell genqlient to use a pointer for the field or argument like this: -```graphql -query MyQuery( - # @genqlient(pointer: true) - arg: String, -) { - # @genqlient(pointer: true) - myString -} -``` - -This will generate a Go field `MyString *string`, and set it to `nil` if the server returns null (and in reverse for arguments). Such fields can be harder to work with in Go, but allow a clear distinction between null and the Go zero value. Again, you can put the directive on the first line to apply it to everything in the query, although this usually gets cumbersome, or use `for` to apply it to a specific input-type field. - -As an example of using all these options together: -```graphql -# @genqlient(omitempty: true) -# @genqlient(for: "MyInputType.id", omitempty: false, pointer: true) -# @genqlient(for: "MyInputType.name", omitempty: false, pointer: true) -query MyQuery( - arg1: MyInputType!, - # @genqlient(pointer: true) - arg2: String!, - # @genqlient(omitempty: false) - arg3: String!, -) { - myString(arg1: $arg1, arg2: $arg2, arg3: $arg3) -} -``` -This will generate: -```go -func MyQuery( - ctx context.Context, - client graphql.Client, - arg1 MyInputType, - arg2 *string, // omitempty - arg3 string, -) (*MyQueryResponse, error) - -type MyInputType struct { - Id *string `json:"id"` - Name *string `json:"name"` - Title string `json:"title,omitempty"` - Age int `json:"age,omitempty"` -} -``` - -See [genqlient_directive.graphql](genqlient_directive.graphql) for complete documentation on these options. - -### … GraphQL interfaces? - -If you request an interface field, genqlient generates an interface type corresponding to the GraphQL interface, and several struct types corresponding to its implementations. For example, given a query: - -```graphql -query GetBooks { - favorite { - title - ... on Novel { - protagonist - } - ... on Dictionary { - language - } - } -} -``` - -genqlient will generate the following types (see [below](#-genqlient-generate-such-complicated-type-names) for more on the names): - -```go -type GetBooksFavoriteBook interface { - GetTitle() string -} -type GetBooksFavoriteNovel struct { - Title string - Protagonist string -} -type GetBooksFavoriteDictionary struct { - Title string - Language string -} -// (similarly for any other types that implement Book) -``` - -These can be used in the ordinary Go ways: to access shared fields, use the interface methods; to access type-specific fields, use a type switch: - -```go -resp, err := GetBooks(...) -fmt.Println("Favorite book:", resp.Favorite.GetTitle()) -if novel, ok := resp.Favorite.(*GetBooksFavoriteNovel); ok { - fmt.Println("Protagonist:", novel.Protagonist) -} -``` - -The interface-type's GoDoc will include a list of its implementations, for your convenience. - -If you only want to request shared fields of the interface (i.e. no fragments), this may seem like a lot of ceremony. If you prefer, you can instead add `# @genqlient(struct: true)` to the field, and genqlient will just generate a struct, like it does for GraphQL object types. For example, given: - -```graphql -query GetBooks { - # @genqlient(struct: true) - favorite { - title - } -} -``` - -genqlient will generate just: - -```go -type GetBooksFavoriteBook struct { - Title string -} -``` - -Keep in mind that if you later want to add fragments to your selection, you won't be able to use `struct` anymore; when you remove it you may need to update your code to replace `.Title` with `.GetTitle()` and so on. - - -### … shared types between different parts of the query? - -Suppose you have a query which requests several different fields each of the same GraphQL type, e.g. `User` (or `[User]`): - -```graphql -query GetMonopolyPlayers { - game { - winner { id name } - banker { id name } - spectators { id name } - } -} -``` - -This will produce a Go type like: -```go -type GetMonopolyPlayersGame struct { - Winner GetMonopolyPlayersGameWinnerUser - Banker GetMonopolyPlayersGameBankerUser - Spectators []GetMonopolyPlayersGameSpectatorsUser -} - -type GetMonopolyPlayersGameWinnerUser struct { - Id string - Name string -} - -// (others similarly) -``` - -But maybe you wanted to be able to pass all those users to a shared function (defined in your code), say `FormatUser(user ???) string`. That's no good; you need to put three different types as the `???`. genqlient has several ways to deal with this. - -**Fragments:** One option -- the GraphQL Way, perhaps -- is to use fragments. You'd write your query like: - -```graphql -fragment MonopolyUser on User { - id - name -} - -query GetMonopolyPlayers { - game { - winner { ...MonopolyUser } - banker { ...MonopolyUser } - spectators { ...MonopolyUser } - } -} -``` - -genqlient will notice this, and generate a type corresponding to the fragment; `GetMonopolyPlayersGame` will look as before, but each of the field types will have a shared embed: - -```go -type MonopolyUser struct { - Id string - Name string -} - -type GetMonopolyPlayersGameWinnerUser struct { - MonopolyUser -} - -// (others similarly) -``` - -Thus you can have `FormatUser` accept a `MonopolyUser`, and pass it `game.Winner.MonopolyUser`, `game.Spectators[i].MonopolyUser`, etc. This is convenient if you may later want to add other fields to some of the queries, because you can still do - -```graphql -fragment MonopolyUser on User { - id - name -} - -query GetMonopolyPlayers { - game { - winner { - winCount - ...MonopolyUser - } - banker { - bankerRating - ...MonopolyUser - } - spectators { ...MonopolyUser } - } -} -``` - -and you can even spread the fragment into interface types. It also avoids having to list the fields several times. - -**Fragments, flattened:** The Go field for `winner`, in the first query above, has type `GetMonopolyPlayersGameWinnerUser` which just wraps `MonopolyUser`. If we don't want to add any other fields, that's unnecessary! Instead, we could do -``` -query GetMonopolyPlayers { - game { - # @genqlient(flatten: true) - winner { - ...MonopolyUser - } - # (etc.) - } -} -``` -and genqlient will skip the indirection and give the field `Winner` type `MonopolyUser` directly. This is often much more convenient if you put all the fields in the fragment, like the first query did. See the [options documentation](genqlient_directive.graphql) for more details. - -**Interfaces:** For each struct field it generates, genqlient also generates an interface method. If you want to share code between two types which to GraphQL are unrelated, you can define an interface containing that getter method, and genqlient's struct types will implement it. (Depending on your exact query, you may need to do a type-assertion from a genqlient-generated interface to yours.) For example, in the above query you could simply do: -```go -type MonopolyUser interface { - GetId() string - GetName() string -} - -func FormatUser(user MonopolyUser) { ... } - -FormatUser(resp.Game.Winner) -``` - -In general in such cases it's better to change the GraphQL schema to show how the types are related, and use one of the other mechanisms, but this option is useful for schemas where you can't do that, or in the meantime. - -**Type names:** Finally, if you always want exactly the same fields on exactly the same types, and don't want to deal with interfaces at all, you can use the simpler but more restrictive genqlient option `typename`: - -```graphql -query GetMonopolyPlayers { - game { - # @genqlient(typename: "User") - winner { id name } - # @genqlient(typename: "User") - banker { id name } - # @genqlient(typename: "User") - spectators { id name } - } -} -``` - -This will tell genqlient to use the same types for each field: - -```go -type GetMonopolyPlayersGame struct { - Winner User - Banker User - Spectators []User -} - -type User struct { - Id string - Name string -} -``` - -In this case, genqlient will validate that each type given the name `User` has the exact same fields; see the [full documentation](genqlient_directive.graphql) for details. - -**Bindings:** It's also possible to use the `bindings` option (see [`genqlient.yaml` documentation](genqlient.yaml)) for a similar purpose, but this is not recommended as it typically requires more work for less gain. - -### … documentation on the output types? - -For any GraphQL types or fields with documentation in the GraphQL schema, genqlient automatically includes that documentation in the generated code's GoDoc. To add additional information to genqlient entrypoints, you can put comments in the GraphQL source: - -```graphql -# This query gets the current user. -# -# If you also need to specify options on the query, you can put -# the @genqlient directive after the docuentation, like this: -# -# @genqlient(omitempty: true) -query GetUser { ... } -``` - -## Why does… - -### … genqlient generate such complicated type-names? - -The short answer is that GraphQL forces our hand. For example, consider a query -```graphql -query GetFamilyNames { - user { - name - children { - name - } - } -} -``` -which returns the following JSON: -```graphql -{ - "user": { - "name": "Ellis Marsalis Jr.", - "children": [ - {"name": "Branford Marsalis"}, - {"name": "Delfeayo Marsalis"}, - {"name": "Jason Marsalis"}, - {"name": "Wynton Marsalis"} - ] - } -} -``` -We need two different `User` types to represent this: one with a `Children` field, and one without. (And there may be more in other queries!) Of course, we could name them `User1` and `User2`, but that's both less descriptive and less stable as the query changes (perhaps to add `parent`), so we call them `GetFamilyNamesUser` and `GetFamilyNamesUserChildrenUser`. - -For the long answer, see [DESIGN.md](DESIGN.md#named-vs-unnamed-types). - -If you find yourself needing to reference long generated names, you can always add type aliases for them, e.g.: -```go -type User = GetFamilyNamesUser -type ChildUser = GetFamilyNamesUserChildrenUser -``` - -Alternately, you can use the `typename` option: if you query -```graphql -query GetFamilyNames { - # @genqlient(typename: "User") - user { - name - # @genqlient(typename: "ChildUser") - children { - name - } - } -} -``` -genqlient will instead generate types with the given names. (You'll need to avoid conflicts; see the [full documentation](genqlient_directive.graphql) for details.) - -### … my editor/IDE plugin not know about the code genqlient just generated? - -If your tools are backed by [gopls](https://github.com/golang/tools/blob/master/gopls/README.md) (which is most of them), they simply don't know it was updated. In most cases, keeping the generated file (typically `generated.go`) open in the background, and reloading it after each run of `genqlient`, will do the trick. - -### … genqlient fail after `go mod tidy`? - -If genqlient fails with an error `missing go.sum entry for module providing package`, this is typically because `go mod tidy` removed its dependencies because they weren't imported by your Go module. You can read more about this in golang/go#45552; see in particular [this comment](https://github.com/golang/go/issues/45552#issuecomment-819545037). In short, if you want to be able to `go run` on newer Go you'll need to have a (blank) import of genqlient's entrypoint in a special `tools.go` file somewhere in your module so `go mod tidy` doesn't prune it: - -```go -//go:build tools -// +build tools - -package client - -import _ "github.com/Khan/genqlient" -``` diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..9d4a7334 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,34 @@ +# genqlient documentation + +generated graphql client ⇒ genqlient + +Welcome to the genqlient documentation! This documentation is made possible by viewers like you; if you see something unclear, file an [issue] or make a [pull request] to improve it! + +[issue]: https://github.com/Khan/genqlient/issues/new/choose +[pull request]: https://github.com/Khan/genqlient/compare + +## Usage/recipes + +- [Getting started guide](introduction.md) +- [Runnable usage example](../example) +- [Handling your GraphQL schema](schema.md) +- [Client configuration and usage](client_config.md) +- [Writing your GraphQL operations](operations.md) + +# Reference + +- [Go package reference](https://pkg.go.dev/github.com/Khan/genqlient) +- [`genqlient.yaml` configuration reference](genqlient.yaml) +- [`@genqlient` directive reference](genqlient_directive.graphql) +- [changelog](CHANGELOG.md) + +## Background + +- [Why genqlient](../README.md#why-another-graphql-client) +- [Notes on the design of genqlient](design.md) +- Blog posts on the [usage][blog1] and [design][blog2] of genqlient +- [Contributing to genqlient](CONTRIBUTING.md) +- [Security policy](SECURITY.md) + +[blog1]: https://blog.khanacademy.org/genqlient-a-truly-type-safe-go-graphql-client/ +[blog2]: https://blog.khanacademy.org/where-go-and-graphql-collide-behind-the-curtain-with-genqlient/ diff --git a/docs/client_config.md b/docs/client_config.md new file mode 100644 index 00000000..b980e53d --- /dev/null +++ b/docs/client_config.md @@ -0,0 +1,157 @@ +# Configuring and using the genqlient client + +This document describes common patterns for using the genqlient client at runtime. For full client reference documentation, see the [godoc]. + +[godoc]: https://pkg.go.dev/github.com/Khan/genqlient/graphql + +## Creating a client + +For most users, just call [`graphql.NewClient`][godoc#NewClient] to get a `graphql.Client`, which you can then pass to genqlient's generated functions. For example, `graphql.NewClient("https://your.api.example/path", http.DefaultClient)` will call an API at the given URL in a fashion compatible with most GraphQL servers. + +For example (see the [getting started docs](INTRODUCTION.md) for the full setup): + +```go +ctx := context.Background() +client := graphql.NewClient("https://api.github.com/graphql", http.DefaultClient) +resp, err := getUser(ctx, client, "benjaminjkraft") +fmt.Println(resp.User.Name, err) +``` + +You can pass the client around however you like to inject dependencies, such as via a global variable, context value, or [fancy typed context][kacontext]. + +[godoc#NewClient]: https://pkg.go.dev/github.com/Khan/genqlient/graphql#NewClient +[kacontext]: https://blog.khanacademy.org/statically-typed-context-in-go/ + +### Authentication and other headers + +To use an API requiring authentication, you can customize the HTTP client passed to [`graphql.NewClient`][godoc#NewClient] to add whatever headers you need. The usual way to do this is to wrap the client's `Transport`: + +```go +type authedTransport struct { + wrapped http.RoundTripper +} + +func (t *authedTransport) RoundTrip(req *http.Request) (*http.Response, error) { + key := ... + req.Header.Set("Authorization", "bearer "+key) + return t.wrapped.RoundTrip(req) +} + +func MakeQuery(...) { + client := graphql.NewClient("https://api.github.com/graphql", + &http.Client{Transport: &authedTransport{wrapped: http.DefaultTransport}}) + + resp, err := MyQuery(ctx, client, ...) +} +``` + +The same method works for passing other HTTP headers, like [`traceparent`](https://www.w3.org/TR/trace-context/). To set a request-dependent header, the `RoundTrip` method has access to the full request, including the context from `req.Context()`. For more on wrapping HTTP clients, see [this post](https://dev.to/stevenacoffman/tripperwares-http-client-middleware-chaining-roundtrippers-3o00). + +### GET requests + +To use GET instead of POST requests, use [`graphql.NewClientUsingGet`][godoc#NewClientUsingGet) to create a client that puts the request in GET query parameters, compatible with many GraphQL servers. For example: +```go +ctx := context.Background() +client := graphql.NewClientUsingGet("https://api.github.com/graphql", http.DefaultClient) +resp, err := getUser(ctx, client, "benjaminjkraft") +fmt.Println(resp.User.Name, err) +``` + +The request will be sent via an HTTP GET request, with the query, operation name and variables encoded in the URL, like so: +``` +https://api.github.com/graphql?operationName%3DgetUser%26query%3D%0Aquery%20getUser(%24login%3A%20String!)%20%7B%0A%20%20user(login%3A%20%24login)%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%7D%0A%26variables%3D%7B%22login%22%3A%22benjaminjkraft%22%7D +``` + +This is useful for caching requests in a CDN or browser cache. It's not recommended for requests containing sensitive data. This client does not support mutations, and will return an error if used for a mutation. + +[godoc#NewClientUsingGet]: https://pkg.go.dev/github.com/Khan/genqlient/graphql#NewClientUsingGet + +### Custom clients + +The genqlient client is an interface; you may define your own implementation. This could wrap the ordinary client to handle GraphQL extensions or set query-specific headers; or start from scratch to use a custom transport. For details, see the [documentation][godoc#Client]. + +[godoc#Client]: https://pkg.go.dev/github.com/Khan/genqlient/graphql#Client + +## Testing + +### Testing code that uses genqlient + +Testing code that uses genqlient typically involves passing in a special HTTP client that does what you want, similar to authentication. For example, you might write a client whose `RoundTrip` returns a fixed response, constructed with [`httptest`]. Or, you can use `httptest` to start up a temporary server, and point genqlient at that. Many third-party packages provide support for this sort of thing; genqlient should work with any HTTP-level mocking that can expose a regular `http.Client`. + +For an example, genqlient's own integration tests use both approaches: +- we [set up a simple GraphQL server](../internal/integration/server/server.go) using [`gqlgen`][gqlgen] and [`httptest`][httptest], and run requests against that +- we also [wrap the HTTP client](../internal/integration/roundtrip.go) to do extra assertions about each request and response (to check the marshaling and unmarshaling logic). + +[gqlgen]: https://gqlgen.com/ +[httptest]: https://pkg.go.dev/net/http/httptest + +### Testing servers + +If you want, you can use genqlient to test your GraphQL APIs; as with mocking you can point genqlient at anything that exposes an ordinary HTTP endpoint or a custom `http.Client`. However, at Khan Academy we've found that genqlient usually isn't the best client for testing: for example, manually constructing values of genqlient's response types gets cumbersome when interfaces or fragments are involved. Instead, we prefer to use a lightweight (and weakly-typed) client for that, and may separately open-source ours in the future. + +## Response objects + +Each genqlient-generated helper function returns a struct whose type corresponds to the query result. For example, given a simple query: + +```graphql +query getUser($login: String!) { + user(login: $login) { + name + } +} +``` + +genqlient will generate something like the following: + +```go +func getUser(...) (*getUserResponse, error) { ... } + +type getUserResponse struct { + User getUserUser +} + +type getUserUser struct { + Name string +} +``` + +For more on accessing response objects for interfaces and fragments, see the [operations documentation](operations.md#interfaces). + +### Handling errors + +In addition to the response-struct, each genqlient-generated helper function returns an error. The response-struct will always be initialized (never nil), even on error. If the request returns a valid GraphQL response containing errors, the returned error will be [`As`-able](https://pkg.go.dev/errors#As) as [`gqlerror.List`](https://pkg.go.dev/github.com/vektah/gqlparser/v2/gqlerror#List), and the struct may be partly-populated (if one field failed but another was computed successfully). If the request fails entirely, the error will be another error (e.g. a [`*url.Error`](https://pkg.go.dev/net/url#Error)), and the response will be blank (but still non-nil). + +For example, you might do one of the following: +```go +// return both error and field: +resp, err := getUser(...) +return resp.User.Name, err + +// handle different errors differently: +resp, err := getUser(...) +var errList *gqlerror.List +if errors.As(err, &errList) { + for _, err := range errList { + fmt.Printf("%v at %v\n", err.Message, err.Path) + } + fmt.Printf("partial response: %v\n", resp) +} else if err != nil { + fmt.Printf("http/network error: %v\n", err) +} else { + fmt.Printf("successful response: %v\n", resp) +} +``` + +### Marshaling + +All genqlient-generated types support both JSON-marshaling and unmarshaling, which can be useful for putting them in a cache, inspecting them by hand, using them in mocks (although this is [not recommended](#testing-servers)), or anything else you can do with JSON. It's not guaranteed that marshaling a genqlient type will produce the exact GraphQL input -- we try to get as close as we can but there are some limitations around Go zero values -- but unmarshaling again should produce the value genqlient returned. That is: + +```go +resp, err := MyQuery(...) +// not guaranteed to match what the server sent (but close): +b, err := json.Marshal(resp) +// guaranteed to match resp: +var respAgain MyQueryResponse +err := json.Unmarshal(b, &resp) +``` + diff --git a/docs/DESIGN.md b/docs/design.md similarity index 100% rename from docs/DESIGN.md rename to docs/design.md diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 00000000..85178c2f --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,121 @@ +# Frequently Asked Questions + +This document describes common questions about genqlient, and provides an index to how to represent common query structures. For a full list of configuration options, see [genqlient.yaml](genqlient.yaml) and [genqlient_directive.graphql](genqlient_directive.graphql). + +## Configuring genqlient + +### How do I set up genqlient to use an API that requires authentication? + +Customize the `http.Client` of your [`graphql.Client`](client_config.md#authentication-and-other-headers). + +### How do I make requests against a mock server, for tests? + +Inject a test HTTP response or server [into the `graphql.Client`](client_config.md#testing). + +### Does genqlient support custom scalars? + +Tell genqlient how to handle your custom scalars with the [`bindings` option](schema.md#custom-scalars). + +### Can I use introspection to fetch my client schema? + +Yes, but you'll need to use a separate tool ([example](schema.md#fetching-your-schema)). + +## Why? + +### Why use genqlient? + +See the [README.md](../README.md#why-another-graphql-client). + +### Why does genqlient generate such complicated type-names? + +The short answer is that GraphQL forces our hand. For example, consider a query +```graphql +query GetFamilyNames { + user { + name + children { + name + } + } +} +``` +which returns the following JSON: +```graphql +{ + "user": { + "name": "Ellis Marsalis Jr.", + "children": [ + {"name": "Branford Marsalis"}, + {"name": "Delfeayo Marsalis"}, + {"name": "Jason Marsalis"}, + {"name": "Wynton Marsalis"} + ] + } +} +``` +We need two different `User` types to represent this: one with a `Children` field, and one without. (And there may be more in other queries!) Of course, we could name them `User1` and `User2`, but that's both less descriptive and less stable as the query changes (perhaps to add `parent`), so we call them `GetFamilyNamesUser` and `GetFamilyNamesUserChildrenUser`. + +This applies even in cases where the types are exactly the same, so that the type names will be stable as the query changes. For example, in the following query there are three different "User" types: + +```graphql +query GetMonopolyPlayers { + game { + winner { id name } + banker { id name } + spectators { id name } + } +} +``` + +This will produce a Go type like: +```go +type GetMonopolyPlayersGame struct { + Winner GetMonopolyPlayersGameWinnerUser + Banker GetMonopolyPlayersGameBankerUser + Spectators []GetMonopolyPlayersGameSpectatorsUser +} + +type GetMonopolyPlayersGameWinnerUser struct { + Id string + Name string +} + +// (other structs identical) +``` + +This may seem unnecessary, but imagine if we generated a single type `GetMonopolyPlayersGameUser`, and then later you changed the query to do `spectators { id name favoritePlayer }`; we'd now have to change all three type names, potentially forcing you to update all the code that uses the query result. + +For more on customizing the type names -- including combining several types into one -- see the [operations documentation](operations.md#type-names). + +For the long answer, see the [design note](design.md#named-vs-unnamed-types). + +## Known issues + +### My editor/IDE plugin doesn't know about the code genqlient just generated + +If your tools are backed by [gopls](https://github.com/golang/tools/blob/master/gopls/README.md) (which is most of them), they simply don't know it was updated. In most cases, keeping the generated file (typically `generated.go`) open in the background, and reloading it after each run of `genqlient`, will do the trick. + +### genqlient fails after `go mod tidy` + +If genqlient fails with an error `missing go.sum entry for module providing package`, this is typically because `go mod tidy` removed its dependencies because they weren't imported by your Go module. You can read more about this in golang/go#45552; see in particular [this comment](https://github.com/golang/go/issues/45552#issuecomment-819545037). In short, if you want to be able to `go run` on newer Go you'll need to have a (blank) import of genqlient's entrypoint in a special `tools.go` file somewhere in your module so `go mod tidy` doesn't prune it: + +```go +//go:build tools +// +build tools + +package client + +import _ "github.com/Khan/genqlient" +``` + +### I'm getting confusing errors from `@genqlient` directives + +Currently, `@genqlient` directives apply to all relevant nodes on the following line, see [#151](https://github.com/Khan/genqlient/issues/151) or the [`@genqlient` documentation](genqlient_directive.graphql). If in doubt, spread things out onto more lines and they'll probably work! + +Common examples of this error: +- `for is only applicable to operations and arguments` +- `omitempty may only be used on optional arguments` + +### My issue is fixed in `main` but not in the latest release + +genqlient does not publish a release for every bugfix; read more about our [versioning strategy](versioning.md) or use `go get -u github.com/Khan/genqlient@main` to install from latest `main`. diff --git a/docs/genqlient_directive.graphql b/docs/genqlient_directive.graphql index 7ef93797..2ba0eb6c 100644 --- a/docs/genqlient_directive.graphql +++ b/docs/genqlient_directive.graphql @@ -222,7 +222,7 @@ directive genqlient( # request the exact same fields). They must even have the fields in the # same order. They should also have matching @genqlient directives, although # this is not currently validated (see issue #123). Fragments are often - # easier to use (see the discussion of code-sharing in FAQ.md, and the + # easier to use (see the discussion of code-sharing in operations.md, and the # "flatten" option above). # # Note that unlike most directives, if applied to the entire operation, diff --git a/docs/INTRODUCTION.md b/docs/introduction.md similarity index 85% rename from docs/INTRODUCTION.md rename to docs/introduction.md index e1fe33df..f5915d50 100644 --- a/docs/INTRODUCTION.md +++ b/docs/introduction.md @@ -1,6 +1,6 @@ # Getting started with genqlient -This document describes how to set up genqlient and use it for simple queries. See also the full worked [example](../example), the [FAQ](FAQ.md), and the reference for [project-wide](genqlient.yaml) and [query-specific](genqlient_directive.graphql) configuration options. +This document describes how to set up genqlient and use it for simple queries. See also the full worked [example](../example), the [FAQ](faq.md), and the rest of the [documentation](docs). ## Step 1: Download your schema @@ -61,6 +61,4 @@ resp, err := getUser(...) ``` (You don't need to do anything with the constant, just keep it somewhere in the source as documentation and for the next time you run genqlient.) In this case you'll need to update `genqlient.yaml` to tell it to look at your Go code. -All the filenames above, and many other aspects of genqlient, are configurable; see [genqlient.yaml](genqlient.yaml) for the full range of options. You can also configure how genqlient converts specific parts of your query with the [`@genqlient` directive](genqlient_directive.graphql). See the [FAQ](FAQ.md) for common options. - -If you want to know even more, and help contribute to genqlient, see [DESIGN.md](DESIGN.md) and [CONTRIBUTING.md](CONTRIBUTING.md). Happy querying! +All the filenames above, and many other aspects of genqlient, are configurable; see the [full documentation](.) for usage guides, reference information, and documentation on how to contribute to genqlient. diff --git a/docs/operations.md b/docs/operations.md new file mode 100644 index 00000000..0610a310 --- /dev/null +++ b/docs/operations.md @@ -0,0 +1,408 @@ +# Writing your GraphQL operations for genqlient + +While simple query structures map naturally from GraphQL to Go, more complex queries require different handling. This document describes how genqlient maps various GraphQL structures to Go, and the best ways to structure your queries and configure genqlient to handle them. For a complete list of options, see the [`genqlient.yaml`](genqlient.yaml) and [`@genqlient`](genqlient_directive.graphql) references. + +## Nullable fields + +There are several ways to handle nullable fields in genqlient: using [zero values](#zero-values), [pointers](#pointers), or [generics](#generics). In some cases you'll also need [`omitempty`](#omitempty). + +### Zero values + +One way is to use the Go idiom, where null gets mapped to the zero value; this is the default in genqlient. So if you have a GraphQL field of type `String`, and you do: + +```graphql +query MyQuery(arg: String) { + myString +} +``` + +then genqlient will generate a Go field `MyString string`, and set it to the empty string if the server returns null. This works even for structs: if an object type in GraphQL is null, genqlient will set the corresponding struct to its zero value. It can be helpful to request `id` in such cases, since that’s a field that should always be set, or `__typename` which is guaranteed to be set, so you can use its presence to decide whether to look at the other fields. + +### omitempty + +For input fields, you often want to tell genqlient to send null to the server if the argument is set to the zero value, similar to the JSON `omitempty` tag. In this case, you can do: + +```graphql +query MyQuery( + # @genqlient(omitempty: true) + arg: String, +) { + myString +} +``` + +You can also put the `# @genqlient(omitempty: true)` on the first line, which will apply it to all arguments in the query, or `# @genqlient(for: "MyInput.myField", omitempty: true)` on the first line to apply it to a particular field of a particular input type used by the query (for which there would otherwise be no place to put the directive, as the field never appears explicitly in the query, but only in the schema). + +Note that omitempty doesn't apply to structs, just like `encoding/json`. For structs that may be entirely unset, you can use `# @genqlient(omitempty: true, pointer: true)`, since `nil` pointers are omitted. + +### Generics + +If you need to distinguish null from the empty string (or generally from the Go zero value of your type), you can tell genqlient to use a generic type for optional fields/arguments, similar to Rust's `Option`. + +You can configure this by defining the type to use in your code (or using one from a library), for example: +```go +type Option[T any] struct { + Value T + HasValue bool +} + +// MarshalJSON, UnmarshalJSON, constructors, etc. +``` + +Then tell genqlient to use it like so: +```yaml +optional: generic +optional_generic_type: github.com/path/to/your/package.Option +``` + +This will generate a Go field `MyString Option[string]`, which you can handle as desired. + +### Pointers + +Similar to generics, you can tell genqlient to use a pointer for a field or argument: +```graphql +query MyQuery( + # @genqlient(pointer: true) + arg: String, +) { + # @genqlient(pointer: true) + myString +} +``` + +This will generate a Go field `MyString *string`, and set it to `nil` if the server returns null (and in reverse for arguments). Such fields can be harder to work with in Go, but allow a clear distinction between null and the Go zero value. You can put `optional: pointer` to apply this to all optional fields, or put the directive on the first line to apply it to everything in the query, although both often get cumbersome. To apply it to a specific input-type field, use `for`: + +As an example of using all these options together: +```graphql +# @genqlient(omitempty: true) +# @genqlient(for: "MyInputType.id", omitempty: false, pointer: true) +# @genqlient(for: "MyInputType.name", omitempty: false, pointer: true) +query MyQuery( + arg1: MyInputType!, + # @genqlient(pointer: true) + arg2: String!, + # @genqlient(omitempty: false) + arg3: String!, +) { + myString(arg1: $arg1, arg2: $arg2, arg3: $arg3) +} +``` +This will generate: +```go +func MyQuery( + ctx context.Context, + client graphql.Client, + arg1 MyInputType, + arg2 *string, // omitempty + arg3 string, +) (*MyQueryResponse, error) + +type MyInputType struct { + Id *string `json:"id"` + Name *string `json:"name"` + Title string `json:"title,omitempty"` + Age int `json:"age,omitempty"` +} +``` + +## GraphQL Interfaces + +If you request an interface field, genqlient generates an interface type corresponding to the GraphQL interface, and several struct types corresponding to its implementations. For example, given a query: + +```graphql +query GetBooks { + favorite { + title + ... on Novel { + protagonist + } + ... on Dictionary { + language + } + } +} +``` + +genqlient will generate the following types (see [below](#-genqlient-generate-such-complicated-type-names) for more on the names): + +```go +type GetBooksFavoriteBook interface { + GetTitle() string +} +type GetBooksFavoriteNovel struct { + Title string + Protagonist string +} +type GetBooksFavoriteDictionary struct { + Title string + Language string +} +// (similarly for any other types that implement Book) +``` + +These can be used in the ordinary Go ways: to access shared fields, use the interface methods; to access type-specific fields, use a type switch: + +```go +resp, err := GetBooks(...) +fmt.Println("Favorite book:", resp.Favorite.GetTitle()) +if novel, ok := resp.Favorite.(*GetBooksFavoriteNovel); ok { + fmt.Println("Protagonist:", novel.Protagonist) +} +``` + +The interface-type's GoDoc will include a list of its implementations, for your convenience. + +If you only want to request shared fields of the interface (i.e. no fragments), this may seem like a lot of ceremony. If you prefer, you can instead add `# @genqlient(struct: true)` to the field, and genqlient will just generate a struct, like it does for GraphQL object types. For example, given: + +```graphql +query GetBooks { + # @genqlient(struct: true) + favorite { + title + } +} +``` + +genqlient will generate just: + +```go +type GetBooksFavoriteBook struct { + Title string +} +``` + +Keep in mind that if you later want to add fragments to your selection, you won't be able to use `struct` anymore; when you remove it you may need to update your code to replace `.Title` with `.GetTitle()` and so on. + +## Sharing types + +By default, genqlient generates a different type for each part of each query, [even those which are structurally the same](faq.md#why-does-genqlient-generate-such-complicated-type-names-). Sometimes, however, you want to have some common code that can accept data from several queries or parts of queries. + +For example, suppose you have a query which requests several different fields each of the same GraphQL type, e.g. `User` (or `[User]`): + +```graphql +query GetMonopolyPlayers { + game { + winner { id name } + banker { id name } + spectators { id name } + } +} +``` + +This will produce a Go type like: +```go +type GetMonopolyPlayersGame struct { + Winner GetMonopolyPlayersGameWinnerUser + Banker GetMonopolyPlayersGameBankerUser + Spectators []GetMonopolyPlayersGameSpectatorsUser +} + +type GetMonopolyPlayersGameWinnerUser struct { + Id string + Name string +} + +// (other structs identical) +``` + +But maybe you wanted to be able to pass all those users to a shared function (defined in your code), say `FormatUser(user ???) string`. That's no good; you need to put three different types as the `???`. + +genqlient has several ways to deal with this. The two best methods for most uses are [fragments](#fragments), useful for reuse that exactly matches the query; and [Go interfaces](#go-interfaces), useful for more flexible access to types with common fields. For some use cases, the [`typename`](#shared-type-names) and [`bindings`](#bindings) options can be useful. + +### Fragments + +One option -- the GraphQL Way, perhaps -- is to use fragments. You'd write your query like: + +```graphql +fragment MonopolyUser on User { + id + name +} + +query GetMonopolyPlayers { + game { + winner { ...MonopolyUser } + banker { ...MonopolyUser } + spectators { ...MonopolyUser } + } +} +``` + +genqlient will notice this, and generate a type corresponding to the fragment; `GetMonopolyPlayersGame` will look as before, but each of the field types will have a shared embed: + +```go +type MonopolyUser struct { + Id string + Name string +} + +type GetMonopolyPlayersGameWinnerUser struct { + MonopolyUser +} + +// (others similarly) +``` + +Thus you can have `FormatUser` accept a `MonopolyUser`, and pass it `game.Winner.MonopolyUser`, `game.Spectators[i].MonopolyUser`, etc. This is convenient if you may later want to add other fields to some of the queries, because you can still do + +```graphql +fragment MonopolyUser on User { + id + name +} + +query GetMonopolyPlayers { + game { + winner { + winCount + ...MonopolyUser + } + banker { + bankerRating + ...MonopolyUser + } + spectators { ...MonopolyUser } + } +} +``` + +and you can even spread the fragment into interface types. It also avoids having to list the fields several times. + +### Flattening fragments +The Go field for `winner`, in the first query above, has type `GetMonopolyPlayersGameWinnerUser` which just wraps `MonopolyUser`. If we don't want to add any other fields, that's unnecessary! Instead, we could do +``` +query GetMonopolyPlayers { + game { + # @genqlient(flatten: true) + winner { + ...MonopolyUser + } + # (etc.) + } +} +``` +and genqlient will skip the indirection and give the field `Winner` type `MonopolyUser` directly. This is often much more convenient if you put all the fields in the fragment, like the first query did. + +### Go interfaces + +For each struct field it generates, genqlient also generates an interface method. If you want to share code between two types which to GraphQL are unrelated, you can define an interface containing that getter method, and genqlient's struct types will implement it. (Depending on your exact query, you may need to do a type-assertion from a genqlient-generated interface to yours.) For example, in the above query you could simply do: +```go +type MonopolyUser interface { + GetId() string + GetName() string +} + +func FormatUser(user MonopolyUser) { ... } + +FormatUser(resp.Game.Winner) +``` + +In general in such cases it's better to change the GraphQL schema to show how the types are related, and use one of the other mechanisms, but this option is useful for schemas where you can't do that, or in the meantime. + +### Shared type names + +Finally, if you always want exactly the same fields on exactly the same types, and don't want to deal with interfaces at all, you can assign the same type name to multiple fields + +```graphql +query GetMonopolyPlayers { + game { + # @genqlient(typename: "User") + winner { id name } + # @genqlient(typename: "User") + banker { id name } + # @genqlient(typename: "User") + spectators { id name } + } +} +``` + +This will tell genqlient to use the same types for each field: + +```go +type GetMonopolyPlayersGame struct { + Winner User + Banker User + Spectators []User +} + +type User struct { + Id string + Name string +} +``` + +See [below](#type-names) for more on this option. + +### Bindings + +It's also possible to use the `bindings` option (see [`genqlient.yaml` documentation](genqlient.yaml)) for a similar purpose, but this is not recommended as it typically requires more work for less gain. + +## Names + +### Operation names + +genqlient will use the exact name of your query as the generated function name. For example, if your query looks like `query myQuery { ... }`, then genqlient will generate `func myQuery(...) (*myQueryResponse, error)`. This means your queries should follow the usual Go conventions, especially starting with an uppercase letter if the query should be exported. + +### Field names + +By default, genqlient chooses field names based on the schema's field names. To customize the name, genqlient supports GraphQL field-aliases. For example, if you do +```graphql +query MyQuery { + myGreatName: myString +} +``` +and genqlient will generate a Go field `MyGreatName string`. Note that the alias will always be uppercased, to ensure the field is visible to the Go JSON library. + +### Type names + +genqlient generates quite verbose type names in many cases. (In short, this is because the same `User` GraphQL type must map to different Go types depending which fields are requested; see the FAQ for [more](faq.md#why-does-genqlient-generate-such-complicated-type-names-). + +For example, in the following query there are two different user structs. +```graphql +query GetFamilyNames { + user { # GetFamilyNamesUser + name + children { # GetFamilyNamesUserChildrenUser + name + } + } +} +``` + +In many cases, you won't need to refer to these directly (only the field names, which are short). But when you do, you can add type aliases for them in your code: +```go +type User = GetFamilyNamesUser +type ChildUser = GetFamilyNamesUserChildrenUser +``` + +Alternately, you can use the `typename` option: if you query +```graphql +query GetFamilyNames { + # @genqlient(typename: "User") + user { + name + # @genqlient(typename: "ChildUser") + children { + name + } + } +} +``` +genqlient will instead generate types with the given names. You can even [map multiple identical types to one](#shared-type-names). + +This approach can be quite convenient, but you'll need to take care to avoid conflicts: each name must be unique, unless the fields are exactly the same (see the [full documentation](genqlient_directive.graphql) for details). + +## Documentation + +For any GraphQL types or fields with documentation in the GraphQL schema, genqlient automatically includes that documentation in the generated code's GoDoc. To add additional information to genqlient entrypoints, you can put comments in the GraphQL source: + +```graphql +# This query gets the current user. +# +# If you also need to specify options on the query, you can put +# the @genqlient directive after the docuentation, like this: +# +# @genqlient(omitempty: true) +query GetUser { ... } +``` + diff --git a/docs/schema.md b/docs/schema.md new file mode 100644 index 00000000..e28be829 --- /dev/null +++ b/docs/schema.md @@ -0,0 +1,130 @@ +# Configuring genqlient to use your GraphQL schema + +This document describes common configuration options to get genqlient to work well with your GraphQL schema. For a complete list of options, see the [`genqlient.yaml` reference](genqlient.yaml). + +## Fetching your schema + +At present, genqlient expects your schema to exist on-disk. To fetch the schema from the server using introspection, you can use a tool such as [gqlfetch] and then let `genqlient` continue from there. Similarly, for [federated] servers you might fetch the supergraph (federated) schema from a registry, or construct it locally from the subgraph schemas. + +[gqlfetch]: https://github.com/suessflorian/gqlfetch +[federated]: https://www.apollographql.com/docs/federation/ + +If desired, you can wrap this process up in a tool that you call via `go generate`, for example: + +```go +package main + +import ( + "context" + "fmt" + "os" + + "github.com/Khan/genqlient/generate" + "github.com/suessflorian/gqlfetch" +) + +func main() { + schema, err := gqlfetch.BuildClientSchema(context.Background(), "http://localhost:8080/query") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + if err = os.WriteFile("schema.graphql", []byte(schema), 0644); err != nil { + fmt.Println(err) + os.Exit(1) + } + + generate.Main() +} +``` + +This can now be invoked upon `go generate` via `//go:generate yourpkg/generate`. + +## Scalars + +GraphQL [defines][spec#scalar] five standard scalar types, which genqlient automatically maps to the following Go types: + +| GraphQL type | Go type | +---------------------------- +| `Int` | `int` | +| `Float` | `float64` | +| `String` | `string` | +| `Boolean` | `bool` | +| `ID` | `string` | + +For custom scalars, or to map to different types, use the `bindings` option in [`genqlient.yaml`](genqlient.yaml). + +[spec#scalar]: https://spec.graphql.org/draft/#sec-Scalars + +### Custom scalars + +Some schemas define custom scalars. You'll need to tell genqlient what types to use for those via the `bindings` option in `genqlient.yaml`, for example: + +```yaml +bindings: + DateTime: + type: time.Time +``` + +The schema should define how custom scalars are encoded in JSON; you'll need to make sure the given type has the appropriate `MarshalJSON`/`UnmarshalJSON` or `json` tags. When using a third-party type, like `time.Time`, you can alternately define separate functions: + +```yaml +bindings: + DateTime: + type: time.Time + marshaler: "github.com/your/package.MarshalDateTime" + unmarshaler: "github.com/your/package.UnmarshalDateTime" +``` + +See genqlient's integration tests for a full example: [types](../internal/testutil/types.go), [config](../internal/integration/genqlient.yaml). + +To leave a custom scalar as raw JSON, map it to `encoding/json.RawMessage`: + +```yaml +bindings: + JSON: + type: encoding/json.RawMessage +``` + +### Integer sizing + + +The GraphQL spec officially defines the `Int` type to be a [signed 32-bit integer](https://spec.graphql.org/draft/#sec-Int). GraphQL clients and servers vary wildly in their enforcement of this; for example: +- [Apollo Server](https://github.com/apollographql/apollo-server/) explicitly checks that integers are at most 32 bits +- [gqlgen](https://github.com/99designs/gqlgen) by default allows any integer that fits in `int` (i.e. 64 bits on most platforms) +- [Apollo Client](https://github.com/apollographql/apollo-client) doesn't check (but implicitly is limited to 53 bits by JavaScript) +- [shurcooL/graphql](https://github.com/shurcooL/graphql) requires integers be passed as a `graphql.Int`, defined to be an `int32` + +By default, genqlient maps GraphQL `Int`s to Go's `int`, meaning that on 64 bit systems there's no client-side restriction. This is convenient for most use cases, but means the client won't prevent you from passing a 64-bit integer to a server that will reject or truncate it. + +If you prefer to limit integers to `int32`, you can set a binding in your `genqlient.yaml`: + +```yaml +bindings: + Int: + type: int32 +``` + +Or, you can bind it to any other type, perhaps one with size-checked constructors, similar to a custom scalar. + +If your schema has a big-integer type, you can bind that similarly to other custom scalars: +```yaml +bindings: + BigInt: + type: math/big # or int64, or string, etc. + # if you need custom marshaling + marshaler: "github.com/path/to/package.MarshalBigInt" + unmarshaler: "github.com/path/to/package.UnmarshalBigInt" +``` + +## Extensions + +Some schemas/servers make use of GraphQL extensions, for example to add rate-limit information to responses. There are two ways to handle these in genqlient: + +1. If you want to handle extensions in a general way for all queries (for example, to automatically retry after the rate-limit resets, you can do this in your [`graphql.Client` implementation](client_config.md#custom-clients). +2. To return response extensions directly in the generated helper functions (so that callers can decide what to do), set `use_extensions: true` in your [`genqlient.yaml`](genqlient.yaml). + +## Hasura, Dgraph, and other generated schemas + +Some GraphQL tools, like Hasura and Dgraph, generate large schemas automatically from non-GraphQL data (like database schemas). These schemas tend to be quite large and complex, and often run into trouble with GraphQL. See [#272](https://github.com/Khan/genqlient/issues/272) for discussion of how to use these tools with genqlient. diff --git a/docs/versioning.md b/docs/versioning.md new file mode 100644 index 00000000..ea4410da --- /dev/null +++ b/docs/versioning.md @@ -0,0 +1,30 @@ +# Versioning policy + +This document describes how we manage genqlient versions. See all published versions on [pkg.go.dev](https://pkg.go.dev/github.com/Khan/genqlient?tab=versions) or [GitHub](https://github.com/Khan/genqlient/releases). + +## When do we make a release? + +In general, we do not cut a release for every bugfix; instead we try to cut a release after major changes have had some time to bake, and in some cases after large codebases using genqlient have tried them. This ensures releases are somewhat more likely to work. + +If that stability is desirable to you, use tagged releases of genqlient only (e.g. `go get github.com/Khan/genqlient@latest`), and be aware that new features may take somewhat longer to make it to a release. (Feel free to make an issue to request a release if it's been a while.) + +If you always want the latest and greatest changes quickly, Go has excellent support for installing packages at any commit. We do have extensive tests and try to keep the main branch safe for production use, but we're never perfect, so take care appropriate to your use case. You can install the main branch with `go get github.com/Khan/genqlient@main`, or replace `main` with any commit SHA. Please report any bugs you see so they can be fixed before the next release! + +For the details of actually making a release, see the [contributor docs](CONTRIBUTING.md#making-a-release). + +## What is a breaking change? + +We consider the following changes to be breaking: +- breaking changes to the runtime `graphql` package (obviously) +- changes which cause genqlient to, given the same valid query, make a breaking change to the API or behavior of the generating code (i.e. it should be safe to re-run a newer version of genqlient on existing queries) +- changes to the `graphql` runtime package which require corresponding changes to the code-generator (i.e. there's no obligation to run genqlient every time you upgrade) + +We don't consider the following changes to be breaking: +- syntactic changes to the generated output for existing queries; if you check that your generated code is up to date in CI you should expect to need to update it when you update genqlient +- changes, including breaking API changes, to any double-underscore-prefixed names in the generated code (i.e. don't refer to these in your code); the same applies to any names from the `graphql` runtime package documented as "intended for the use of genqlient's generated code only" +- changes to the code-generator which require corresponding changes to the `graphql` runtime package (these are safe because your runtime should be the same or newer). +- dropping support for Go versions which are no longer supported by the Go project (all but the [two newest](https://go.dev/doc/devel/release)) + +Additionally, your version of the `graphql` runtime package must be the same or newer (and the same major version) as your version of the code-generator. (It's recommended to use the same version of both, but it's not required to regenerate all your queries after upgrading.) + +Note that while genqlient is on version 0.x we may make breaking changes at any time, although we still aim to do so only in minor version bumps (0.6.0, not 0.5.1), and we aim to minimize breaking changes, especially to core functionality. diff --git a/generate/convert.go b/generate/convert.go index d3a9f8d3..b7e6a6d2 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -678,7 +678,7 @@ func (g *generator) convertSelectionSet( // the fragment's type. This is distinct from the rules for when a fragment // spread is legal, which is true when the fragment would be active for *any* // of the concrete types the spread-context could have (see the [GraphQL spec] -// or docs/DESIGN.md). +// or docs/design.md). // // containingTypedef is as described in convertInlineFragment, below. // fragmentTypedef is the definition of the fragment's type-condition, i.e. the @@ -726,7 +726,7 @@ func fragmentMatches(containingTypedef, fragmentTypedef *ast.Definition) bool { // // In general, we treat such fragments' fields as if they were fields of the // parent selection-set (except of course they are only included in types the -// fragment matches); see docs/DESIGN.md for more. +// fragment matches); see docs/design.md for more. func (g *generator) convertInlineFragment( namePrefix *prefixList, fragment *ast.InlineFragment, diff --git a/generate/names.go b/generate/names.go index 29074e0e..0aecacc2 100644 --- a/generate/names.go +++ b/generate/names.go @@ -2,7 +2,7 @@ package generate // This file generates the names for genqlient's generated types. This is // somewhat tricky because the names need to be unique, stable, and, to the -// extent possible, human-readable and -writable. See docs/DESIGN.md for an +// extent possible, human-readable and -writable. See docs/design.md for an // overview of the considerations; in short, we need long names. // // Specifically, the names we generate are of the form: @@ -33,7 +33,7 @@ package generate // One subtlety in the above description is: is the "MyType" the interface or // the implementation? When it's a suffix, the answer is both: we generate // both MyFieldMyInterface and MyFieldMyImplementation, and the latter, in Go, -// implements the former. (See docs/DESIGN.md for more.) But as an infix, we +// implements the former. (See docs/design.md for more.) But as an infix, we // use the type on which the field is requested. Concretely, the following // schema and query: // type Query { f: I } diff --git a/generate/testdata/queries/ComplexInlineFragments.graphql b/generate/testdata/queries/ComplexInlineFragments.graphql index 0e4dbe4c..456973d5 100644 --- a/generate/testdata/queries/ComplexInlineFragments.graphql +++ b/generate/testdata/queries/ComplexInlineFragments.graphql @@ -1,4 +1,4 @@ -# We test all the spread cases from docs/DESIGN.md, see there for more context +# We test all the spread cases from docs/design.md, see there for more context # on each, as well as various other nonsense. But for abstract-in-abstract # spreads, we can't test cases (4b) and (4c), where I implements J or vice # versa, because gqlparser doesn't support interfaces that implement other diff --git a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go index 5cd21cd9..d5591c0c 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexInlineFragments.graphql-ComplexInlineFragments.graphql.go @@ -1429,7 +1429,7 @@ query ComplexInlineFragments { } ` -// We test all the spread cases from docs/DESIGN.md, see there for more context +// We test all the spread cases from docs/design.md, see there for more context // on each, as well as various other nonsense. But for abstract-in-abstract // spreads, we can't test cases (4b) and (4c), where I implements J or vice // versa, because gqlparser doesn't support interfaces that implement other From 9f8e2b262fc908a9e4846dedb96fa82e07c1bb72 Mon Sep 17 00:00:00 2001 From: Craig Silverstein Date: Mon, 26 Feb 2024 11:32:48 -0800 Subject: [PATCH 10/17] Fix non-deterministic generated code involving interfaces and fragments. (#323) In #209, @vikstrous2 added a bugfix to sort the list of interfaces when generating code, so the generated output was stable. This worked great, but there was one more case where we need to do that, involving interfaces in fragments. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable - [ ] Included a link to the issue fixed, if applicable - [ ] Included documentation, for new features - [x] Added an entry to the changelog --- docs/CHANGELOG.md | 3 ++- generate/convert.go | 2 ++ ...s.graphql-ComplexNamedFragments.graphql.go | 22 +++++++++---------- ...enerate-Flatten.graphql-Flatten.graphql.go | 22 +++++++++---------- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ab9b287c..62016434 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -36,8 +36,9 @@ Note that genqlient is now tested from Go 1.20 through Go 1.22. ### Bug fixes: - The presence of negative pointer directives, i.e., `# @genqlient(pointer: false)` are now respected even in the when `optional: pointer` is set in the configuration file. - Made name collisions between query/mutation arguments and local function variables less likely. -- Fix generation issue related to golang type implementation of complex graphql union fragments +- Fix generation issue related to golang type implementation of complex graphql union fragments. - Bind correctly to types in the same package as the generated code. +- Fix non-deterministic generated code when querying graphql interfaces via named fragments. ## v0.6.0 diff --git a/generate/convert.go b/generate/convert.go index b7e6a6d2..3e3dc03f 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -841,6 +841,8 @@ func (g *generator) convertNamedFragment(fragment *ast.FragmentDefinition) (goTy return goType, nil case ast.Interface, ast.Union: implementationTypes := g.schema.GetPossibleTypes(typ) + // Make sure we generate stable output by sorting the types by name when we get them + sort.Slice(implementationTypes, func(i, j int) bool { return implementationTypes[i].Name < implementationTypes[j].Name }) goType := &goInterfaceType{ GoName: fragment.Name, SharedFields: fields, diff --git a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go index 7888b2e8..b7fa016d 100644 --- a/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-ComplexNamedFragments.graphql-ComplexNamedFragments.graphql.go @@ -273,8 +273,8 @@ func (v *ComplexNamedFragmentsWithInlineUnionUser) __premarshalJSON() (*__premar // // ContentFields is implemented by the following types: // ContentFieldsArticle -// ContentFieldsVideo // ContentFieldsTopic +// ContentFieldsVideo type ContentFields interface { implementsGraphQLInterfaceContentFields() // GetName returns the interface-field "name" from its implementation. @@ -284,8 +284,8 @@ type ContentFields interface { } func (v *ContentFieldsArticle) implementsGraphQLInterfaceContentFields() {} -func (v *ContentFieldsVideo) implementsGraphQLInterfaceContentFields() {} func (v *ContentFieldsTopic) implementsGraphQLInterfaceContentFields() {} +func (v *ContentFieldsVideo) implementsGraphQLInterfaceContentFields() {} func __unmarshalContentFields(b []byte, v *ContentFields) error { if string(b) == "null" { @@ -304,12 +304,12 @@ func __unmarshalContentFields(b []byte, v *ContentFields) error { case "Article": *v = new(ContentFieldsArticle) return json.Unmarshal(b, *v) - case "Video": - *v = new(ContentFieldsVideo) - return json.Unmarshal(b, *v) case "Topic": *v = new(ContentFieldsTopic) return json.Unmarshal(b, *v) + case "Video": + *v = new(ContentFieldsVideo) + return json.Unmarshal(b, *v) case "": return fmt.Errorf( "response was missing Content.__typename") @@ -331,20 +331,20 @@ func __marshalContentFields(v *ContentFields) ([]byte, error) { *ContentFieldsArticle }{typename, v} return json.Marshal(result) - case *ContentFieldsVideo: - typename = "Video" + case *ContentFieldsTopic: + typename = "Topic" result := struct { TypeName string `json:"__typename"` - *ContentFieldsVideo + *ContentFieldsTopic }{typename, v} return json.Marshal(result) - case *ContentFieldsTopic: - typename = "Topic" + case *ContentFieldsVideo: + typename = "Video" result := struct { TypeName string `json:"__typename"` - *ContentFieldsTopic + *ContentFieldsVideo }{typename, v} return json.Marshal(result) case nil: diff --git a/generate/testdata/snapshots/TestGenerate-Flatten.graphql-Flatten.graphql.go b/generate/testdata/snapshots/TestGenerate-Flatten.graphql-Flatten.graphql.go index c05e913d..76499bc7 100644 --- a/generate/testdata/snapshots/TestGenerate-Flatten.graphql-Flatten.graphql.go +++ b/generate/testdata/snapshots/TestGenerate-Flatten.graphql-Flatten.graphql.go @@ -30,8 +30,8 @@ func (v *ChildVideoFields) GetName() string { return v.Name } // // ContentFields is implemented by the following types: // ContentFieldsArticle -// ContentFieldsVideo // ContentFieldsTopic +// ContentFieldsVideo type ContentFields interface { implementsGraphQLInterfaceContentFields() // GetName returns the interface-field "name" from its implementation. @@ -41,8 +41,8 @@ type ContentFields interface { } func (v *ContentFieldsArticle) implementsGraphQLInterfaceContentFields() {} -func (v *ContentFieldsVideo) implementsGraphQLInterfaceContentFields() {} func (v *ContentFieldsTopic) implementsGraphQLInterfaceContentFields() {} +func (v *ContentFieldsVideo) implementsGraphQLInterfaceContentFields() {} func __unmarshalContentFields(b []byte, v *ContentFields) error { if string(b) == "null" { @@ -61,12 +61,12 @@ func __unmarshalContentFields(b []byte, v *ContentFields) error { case "Article": *v = new(ContentFieldsArticle) return json.Unmarshal(b, *v) - case "Video": - *v = new(ContentFieldsVideo) - return json.Unmarshal(b, *v) case "Topic": *v = new(ContentFieldsTopic) return json.Unmarshal(b, *v) + case "Video": + *v = new(ContentFieldsVideo) + return json.Unmarshal(b, *v) case "": return fmt.Errorf( "response was missing Content.__typename") @@ -88,20 +88,20 @@ func __marshalContentFields(v *ContentFields) ([]byte, error) { *ContentFieldsArticle }{typename, v} return json.Marshal(result) - case *ContentFieldsVideo: - typename = "Video" + case *ContentFieldsTopic: + typename = "Topic" result := struct { TypeName string `json:"__typename"` - *ContentFieldsVideo + *ContentFieldsTopic }{typename, v} return json.Marshal(result) - case *ContentFieldsTopic: - typename = "Topic" + case *ContentFieldsVideo: + typename = "Video" result := struct { TypeName string `json:"__typename"` - *ContentFieldsTopic + *ContentFieldsVideo }{typename, v} return json.Marshal(result) case nil: From 83d2f2502157c343d84603bed2ef7baf158adb06 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Mon, 4 Mar 2024 22:59:09 -0800 Subject: [PATCH 11/17] Release v0.7.0 (#324) It's getting close to a year, we should do a release! As usual I did a bit of rewording in the changelog, and also changed this and past changelogs to not treat dropping older Go versions as breaking per our handy new versioning policy. Fixes #321. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable (n/a) - [x] Included a link to the issue fixed, if applicable - [x] Included documentation, for new features (n/a) - [x] Added an entry to the changelog --- docs/CHANGELOG.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 62016434..ee43b5fb 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -20,21 +20,25 @@ When releasing a new version: -Note that genqlient is now tested from Go 1.20 through Go 1.22. - ### Breaking changes: -- genqlient now requires Go 1.20 or higher. It may still work with earlier versions, but we aren't explicitly keeping dependency modules to Go 1.18 and lower. +### New features: + +### Bug fixes: + +## v0.7.0 + +In addition to several new features and bugfixes, along with this release comes reorganized [documentation](.) for genqlient. Note that genqlient now requires Go 1.20 or higher, and is tested through Go 1.22. ### New features: - The new `optional: generic` allows using a generic type to represent optionality. See the [documentation](genqlient.yaml) for details. - For schemas with enum values that differ only in casing, it's now possible to disable smart-casing in genqlient.yaml; see the [documentation](genqlient.yaml) for `casing` for details. -- Support .graphqls and .gql file extensions +- genqlient now supports .graphqls and .gql file extensions for schemas and queries. - More accurately guess the package name for generated code (and warn if the config option -- now almost never needed -- looks wrong). ### Bug fixes: -- The presence of negative pointer directives, i.e., `# @genqlient(pointer: false)` are now respected even in the when `optional: pointer` is set in the configuration file. +- Negative pointer directives, i.e., `# @genqlient(pointer: false)` are now respected even in the when `optional: pointer` is set in the configuration file. - Made name collisions between query/mutation arguments and local function variables less likely. - Fix generation issue related to golang type implementation of complex graphql union fragments. - Bind correctly to types in the same package as the generated code. @@ -44,10 +48,6 @@ Note that genqlient is now tested from Go 1.20 through Go 1.22. Version 0.6.0 includes some small features and bugfixes. Note that genqlient now requires Go 1.18 or higher, and is tested through Go 1.20. -### Breaking changes: - -- genqlient now requires Go 1.18 or higher. - ### New features: - You can now bind all types from a package in `genqlient.yaml` using the new `package_bindings` option. @@ -66,7 +66,6 @@ Version 0.5.0 adds several new configuration options and convenience features. N ### Breaking changes: -- genqlient now requires Go 1.16 or higher. - The [`graphql.Client`](https://pkg.go.dev/github.com/Khan/genqlient/graphql#Client) interface now accepts two structs for the request and response, to allow future expansion, rather than several individual arguments. Clients implementing the interface themselves will need to change the signature; clients who simply call `graphql.NewClient` are unaffected. ### New features: From 8dc71037926c32fe163ffd297dc27bba1eb1a64a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:56:48 -0700 Subject: [PATCH 12/17] Bump google.golang.org/protobuf from 1.31.0 to 1.33.0 in /internal/lint (#325) Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0. [![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=google.golang.org/protobuf&package-manager=go_modules&previous-version=1.31.0&new-version=1.33.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/Khan/genqlient/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- internal/lint/go.mod | 2 +- internal/lint/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/lint/go.mod b/internal/lint/go.mod index 766f4b4e..7c01dcb8 100644 --- a/internal/lint/go.mod +++ b/internal/lint/go.mod @@ -181,7 +181,7 @@ require ( golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.17.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/internal/lint/go.sum b/internal/lint/go.sum index 7ad45de9..9f4789bf 100644 --- a/internal/lint/go.sum +++ b/internal/lint/go.sum @@ -910,8 +910,8 @@ 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 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -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= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 3da9a0f905b39f3b74156cb47d892543e5ab40aa Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Fri, 5 Apr 2024 09:39:08 -0700 Subject: [PATCH 13/17] Add support for double-star globs (#330) It's really quite reasonable to want this to work -- you want to say to look for queries in `pkg/**/*.graphql`, or `**/queries/*.graphql`, or whatever. Now it does. I picked `doublestar` because it seemed a reasonable combination of popular and well-documented. Fixes #167. Hopefully obsoletes #327. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable - [x] Included a link to the issue fixed, if applicable - [x] Included documentation, for new features - [x] Added an entry to the changelog --- .golangci.yml | 1 + docs/CHANGELOG.md | 2 ++ docs/genqlient.yaml | 10 ++++-- generate/parse.go | 9 +++-- generate/parse_test.go | 44 +++++++++++++++++++++-- generate/testdata/expandFilenames/a/b/c | 0 generate/testdata/expandFilenames/a/b/c.d | 0 go.mod | 1 + go.sum | 2 ++ 9 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 generate/testdata/expandFilenames/a/b/c create mode 100644 generate/testdata/expandFilenames/a/b/c.d diff --git a/.golangci.yml b/.golangci.yml index 095c6226..0446dda9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -53,6 +53,7 @@ linters-settings: - golang.org/x/tools - gopkg.in/yaml.v2 - github.com/alexflint/go-arg + - github.com/bmatcuk/doublestar/v4 forbidigo: forbid: diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ee43b5fb..239de388 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -24,6 +24,8 @@ When releasing a new version: ### New features: +- genqlient now supports double-star globs for schema and query files; see [`genqlient.yaml` docs](genqlient.yaml) for more. + ### Bug fixes: ## v0.7.0 diff --git a/docs/genqlient.yaml b/docs/genqlient.yaml index 55178a29..33a49c06 100644 --- a/docs/genqlient.yaml +++ b/docs/genqlient.yaml @@ -6,11 +6,15 @@ # The filename with the GraphQL schema (in SDL format), relative to # genqlient.yaml. -# This can also be a list of filenames, such as: +# This can also be a glob-pattern, or a list of filenames or globs, such as: # schema: # - user.graphql # - ./schema/*.graphql -# - ./another_directory/*/*.graphql +# - ./*/*.graphql # matches ./a/b.graphql, but not ./a/b/c.graphql +# - ./**/*.graphql # matches ./a.graphql, ./a/b/c.graphql, etc. +# The glob-pattern "**" is interpreted by github.com/bmatcuk/doublestar/v4, and +# matches zero or more path components (so you want **/*.graphql, not +# **.graphql). Each pattern must match at least one file, to avoid mistakes. schema: schema.graphql # Filename(s) or globs with the operations for which to generate code, relative @@ -20,7 +24,7 @@ schema: schema.graphql # Go files, in which case any string-literal starting with (optional # whitespace and) the string "# @genqlient" will be extracted as a query. # -# Like schema, this may be a single file or a list. +# Like schema, this may be a single filename or glob, or a list of those. operations: - genqlient.graphql - "pkg/*.go" diff --git a/generate/parse.go b/generate/parse.go index 6ef7656b..196f91aa 100644 --- a/generate/parse.go +++ b/generate/parse.go @@ -6,10 +6,12 @@ import ( goParser "go/parser" goToken "go/token" "os" + "path" "path/filepath" "strconv" "strings" + "github.com/bmatcuk/doublestar/v4" "github.com/vektah/gqlparser/v2/ast" "github.com/vektah/gqlparser/v2/parser" "github.com/vektah/gqlparser/v2/validator" @@ -87,7 +89,10 @@ func getAndValidateQueries(basedir string, filenames StringList, schema *ast.Sch func expandFilenames(globs []string) ([]string, error) { uniqFilenames := make(map[string]bool, len(globs)) for _, glob := range globs { - matches, err := filepath.Glob(glob) + // SplitPattern in case the path is absolute or something; a valid path + // isn't necessarily a valid glob-pattern. + base, pattern := doublestar.SplitPattern(glob) + matches, err := doublestar.Glob(os.DirFS(base), pattern, doublestar.WithFilesOnly()) if err != nil { return nil, errorf(nil, "can't expand file-glob %v: %v", glob, err) } @@ -95,7 +100,7 @@ func expandFilenames(globs []string) ([]string, error) { return nil, errorf(nil, "%v did not match any files", glob) } for _, match := range matches { - uniqFilenames[match] = true + uniqFilenames[path.Join(base, match)] = true } } filenames := make([]string, 0, len(uniqFilenames)) diff --git a/generate/parse_test.go b/generate/parse_test.go index 500c7e84..e08d8a4c 100644 --- a/generate/parse_test.go +++ b/generate/parse_test.go @@ -6,12 +6,14 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/vektah/gqlparser/v2/ast" ) var ( - parseDataDir = "testdata/parsing" - parseErrorsDir = "testdata/parsing-errors" + parseDataDir = "testdata/parsing" + parseErrorsDir = "testdata/parsing-errors" + expandFilenamesDir = "testdata/expandFilenames" ) func sortQueries(queryDoc *ast.QueryDocument) { @@ -80,6 +82,44 @@ func removeComments(gotWithComments string) string { return got } +func filepathJoinAll(a string, bs []string) []string { + ret := make([]string, len(bs)) + for i, b := range bs { + ret[i] = filepath.Join(a, b) + } + return ret +} + +func TestExpandFilenames(t *testing.T) { + tests := []struct { + name string + globs []string + files []string + err bool + }{ + {"SingleFile", []string{"a/b/c"}, []string{"a/b/c"}, false}, + {"OneStar", []string{"a/*/c"}, []string{"a/b/c"}, false}, + {"StarExt", []string{"a/b/*"}, []string{"a/b/c", "a/b/c.d"}, false}, + {"TwoStar", []string{"**/c"}, []string{"a/b/c"}, false}, + {"TwoStarSuffix", []string{"**/*"}, []string{"a/b/c", "a/b/c.d"}, false}, + {"Repeated", []string{"a/b/c", "a/b/*"}, []string{"a/b/c", "a/b/c.d"}, false}, + {"Empty", []string{"bogus/*"}, nil, true}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + files, err := expandFilenames(filepathJoinAll(expandFilenamesDir, test.globs)) + if test.err && err == nil { + t.Errorf("got %v, wanted error", files) + } else if !test.err && err != nil { + t.Errorf("got error %v, wanted %v", err, test.files) + } else { + assert.ElementsMatch(t, filepathJoinAll(expandFilenamesDir, test.files), files) + } + }) + } +} + // TestParseErrors tests that query-extraction from different language source files // produces appropriate errors if your query is invalid. func TestParseErrors(t *testing.T) { diff --git a/generate/testdata/expandFilenames/a/b/c b/generate/testdata/expandFilenames/a/b/c new file mode 100644 index 00000000..e69de29b diff --git a/generate/testdata/expandFilenames/a/b/c.d b/generate/testdata/expandFilenames/a/b/c.d new file mode 100644 index 00000000..e69de29b diff --git a/go.mod b/go.mod index 5741c420..c6fb5870 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( require ( github.com/agnivade/levenshtein v1.1.1 // indirect github.com/alexflint/go-scalar v1.0.0 // indirect + github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect diff --git a/go.sum b/go.sum index 668fa62a..a3a0616f 100644 --- a/go.sum +++ b/go.sum @@ -9,6 +9,8 @@ github.com/alexflint/go-scalar v1.0.0/go.mod h1:GpHzbCOZXEKMEcygYQ5n/aa4Aq84zbxj github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= +github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs= github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 1a44ec78e370b9aaacc9c174550afc606bb67b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zahradn=C3=AD=C4=8Dek?= <18536740+Fazt01@users.noreply.github.com> Date: Fri, 7 Jun 2024 18:24:36 +0200 Subject: [PATCH 14/17] omitempty validation on input fields with/without defaults (#338) Fixes https://github.com/Khan/genqlient/issues/290 and https://github.com/Khan/genqlient/issues/228#issuecomment-2023396706 (for latter, only the comment of mine, not the whole issue) This is only changing where the `omitempty` is allowed and forbidden - it does not change where is the `omitempty` actually generated or not in generated code. Separately each line of changelog: - allow `omitempty` on non-nullable input field, if the field has a default (pretty much https://github.com/Khan/genqlient/issues/228#issuecomment-2023396706) - added a `&& field.DefaultValue == nil` to the `"omitempty may only be used on optional arguments"` error - allow `omitempty: false` on an input field, even when it is non-nullable (https://github.com/Khan/genqlient/issues/290) - `fieldDir.Omitempty != nil` changed to `fieldOptions.GetOmitempty()` - forbid `omitempty: false` (including implicit behaviour) when using pointer on non-null, no-default input field - as setting a correct combination of directives (and potentially some options, which changes implicit pointer and/or omitempty), and this library promises "Compile-time validation of GraphQL queries: never ship an invalid GraphQL query again!", I found it fitting to guard against the most simple case, that can be enforced in Go type system. - this, however, is a breaking change, so not sure if I should include it here. No previously present test failed after such change, but for example `generate/testdata/errors/DefaultInputsNoOmitPointerForDirective.graphql` would previously generate following (below - which has a possibility to send invalid graphql input), but now the generation fails. ``` type InputWithDefaults struct { Field *string `json:"field"` NullableField string `json:"nullableField"` } ``` - - alternative would be to force omitempty tag in such cases (even if there is no omitempty directive/option) - so that generation would not fail. But I'm not sure if I can afford to do that. That would probably still be breaking change (different generated code for same query), but a bit better. Maybe just setting omitempty flag instead of returning error would be sufficient. In general, I have also moved the omitempty check from directives to the time of creating Go types/tags of field. This seems more consistent, as not all possibilities were caught before (i.e. general `@genqlient(omitemtpy: true)` vs `@genqlient(for: ..., omitempty: true)`). When creating Go type/tags, all the options/directives are already merged, so the final result is being checked. There is minor difference in error message (instead of reference to directive, the error refers to the whole operation, but also includes type.field name) I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable - [x] Included a link to the issue fixed, if applicable - [ ] Included documentation, for new features - [x] Added an entry to the changelog --- docs/CHANGELOG.md | 7 ++ generate/convert.go | 13 ++++ generate/genqlient_directive.go | 4 -- .../errors/DefaultInputsNoOmitPointer.graphql | 7 ++ ...ultInputsNoOmitPointerForDirective.graphql | 8 +++ .../errors/OmitemptyDirective.graphql | 7 ++ .../errors/OmitemptyForDirective.graphql | 7 ++ generate/testdata/errors/schema.graphql | 14 ++++ .../testdata/queries/DefaultInputs.graphql | 7 ++ .../queries/DefaultInputsPointer.graphql | 9 +++ .../DefaultInputsWithDirective.graphql | 7 ++ .../DefaultInputsWithForDirective.graphql | 7 ++ .../testdata/queries/OmitemptyFalse.graphql | 7 ++ generate/testdata/queries/schema.graphql | 14 ++++ ...ultInputs.graphql-DefaultInputs.graphql.go | 69 ++++++++++++++++++ ...tInputs.graphql-DefaultInputs.graphql.json | 9 +++ ...er.graphql-DefaultInputsPointer.graphql.go | 70 +++++++++++++++++++ ....graphql-DefaultInputsPointer.graphql.json | 9 +++ ...phql-DefaultInputsWithDirective.graphql.go | 68 ++++++++++++++++++ ...ql-DefaultInputsWithDirective.graphql.json | 9 +++ ...l-DefaultInputsWithForDirective.graphql.go | 67 ++++++++++++++++++ ...DefaultInputsWithForDirective.graphql.json | 9 +++ ...ptyFalse.graphql-OmitemptyFalse.graphql.go | 67 ++++++++++++++++++ ...yFalse.graphql-OmitemptyFalse.graphql.json | 9 +++ ...eErrors-DefaultInputsNoOmitPointer-graphql | 1 + ...ultInputsNoOmitPointerForDirective-graphql | 1 + ...tGenerateErrors-OmitemptyDirective-graphql | 1 + ...nerateErrors-OmitemptyForDirective-graphql | 1 + 28 files changed, 504 insertions(+), 4 deletions(-) create mode 100644 generate/testdata/errors/DefaultInputsNoOmitPointer.graphql create mode 100644 generate/testdata/errors/DefaultInputsNoOmitPointerForDirective.graphql create mode 100644 generate/testdata/errors/OmitemptyDirective.graphql create mode 100644 generate/testdata/errors/OmitemptyForDirective.graphql create mode 100644 generate/testdata/queries/DefaultInputs.graphql create mode 100644 generate/testdata/queries/DefaultInputsPointer.graphql create mode 100644 generate/testdata/queries/DefaultInputsWithDirective.graphql create mode 100644 generate/testdata/queries/DefaultInputsWithForDirective.graphql create mode 100644 generate/testdata/queries/OmitemptyFalse.graphql create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointer-graphql create mode 100644 generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointerForDirective-graphql create mode 100644 generate/testdata/snapshots/TestGenerateErrors-OmitemptyDirective-graphql create mode 100644 generate/testdata/snapshots/TestGenerateErrors-OmitemptyForDirective-graphql diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 239de388..bb73a750 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -22,12 +22,19 @@ When releasing a new version: ### Breaking changes: +- omitempty validation: + - forbid `omitempty: false` (including implicit behaviour) when using pointer on non-null, no-default input field + ### New features: - genqlient now supports double-star globs for schema and query files; see [`genqlient.yaml` docs](genqlient.yaml) for more. ### Bug fixes: +- omitempty validation: + - allow `omitempty` on non-nullable input field, if the field has a default + - allow `omitempty: false` on an input field, even when it is non-nullable + ## v0.7.0 In addition to several new features and bugfixes, along with this release comes reorganized [documentation](.) for genqlient. Note that genqlient now requires Go 1.20 or higher, and is tested through Go 1.22. diff --git a/generate/convert.go b/generate/convert.go index 3e3dc03f..b61f227b 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -450,6 +450,19 @@ func (g *generator) convertDefinition( return nil, err } + // Try to protect against generating field type that has possibility to send `null` to non-nullable graphQL + // type. This does not protect against lists/slices, as Go zero-slices are already serialized as `null` + // (which can therefore currently send invalid graphQL value - e.g. `null` for [String!]!) + // And does not protect against custom MarshalJSON. + _, isPointer := fieldGoType.(*goPointerType) + if field.Type.NonNull && isPointer && !fieldOptions.GetOmitempty() { + return nil, errorf(pos, "pointer on non-null input field can only be used together with omitempty: %s.%s", name, field.Name) + } + + if fieldOptions.GetOmitempty() && field.Type.NonNull && field.DefaultValue == nil { + return nil, errorf(pos, "omitempty may only be used on optional arguments: %s.%s", name, field.Name) + } + goType.Fields[i] = &goStructField{ GoName: goName, GoType: fieldGoType, diff --git a/generate/genqlient_directive.go b/generate/genqlient_directive.go index ba5119e8..f6adfdab 100644 --- a/generate/genqlient_directive.go +++ b/generate/genqlient_directive.go @@ -213,10 +213,6 @@ func (dir *genqlientDirective) validate(node interface{}, schema *ast.Schema) er return errorf(fieldDir.pos, "struct and flatten can't be used via for") } - if fieldDir.Omitempty != nil && field.Type.NonNull { - return errorf(fieldDir.pos, "omitempty may only be used on optional arguments") - } - if fieldDir.TypeName != "" && fieldDir.Bind != "" && fieldDir.Bind != "-" { return errorf(fieldDir.pos, "typename and bind may not be used together") } diff --git a/generate/testdata/errors/DefaultInputsNoOmitPointer.graphql b/generate/testdata/errors/DefaultInputsNoOmitPointer.graphql new file mode 100644 index 00000000..7260705f --- /dev/null +++ b/generate/testdata/errors/DefaultInputsNoOmitPointer.graphql @@ -0,0 +1,7 @@ +# very similar to DefaultInputsNoOmitPointerForDirective.graphql - same expected behaviour, but takes a different code path(?) +# @genqlient(pointer: true) +query DefaultInputs( + $input: InputWithDefaults! +) { + default(input: $input) +} diff --git a/generate/testdata/errors/DefaultInputsNoOmitPointerForDirective.graphql b/generate/testdata/errors/DefaultInputsNoOmitPointerForDirective.graphql new file mode 100644 index 00000000..58ba83a1 --- /dev/null +++ b/generate/testdata/errors/DefaultInputsNoOmitPointerForDirective.graphql @@ -0,0 +1,8 @@ +# Non-nullable input field with default cannot be pointer without omitempty - as that +# would send `null`, which is invalid value. +# @genqlient(for: "InputWithDefaults.field", pointer: true) +query DefaultInputs( + $input: InputWithDefaults! +) { + default(input: $input) +} diff --git a/generate/testdata/errors/OmitemptyDirective.graphql b/generate/testdata/errors/OmitemptyDirective.graphql new file mode 100644 index 00000000..ce9cac94 --- /dev/null +++ b/generate/testdata/errors/OmitemptyDirective.graphql @@ -0,0 +1,7 @@ +# One of the input filed is non-nullable -> cannot omit it +# @genqlient(omitempty: true) +query OmitemptyDirective ( + $input: OmitemptyInput +) { + omitempty(input: $input) +} diff --git a/generate/testdata/errors/OmitemptyForDirective.graphql b/generate/testdata/errors/OmitemptyForDirective.graphql new file mode 100644 index 00000000..3d247f4c --- /dev/null +++ b/generate/testdata/errors/OmitemptyForDirective.graphql @@ -0,0 +1,7 @@ +# The specific input field is non-nullable -> cannot omit it +# @genqlient(for: "OmitemptyInput.field", omitempty: true) +query OmitemptyDirective ( + $input: OmitemptyInput +) { + omitempty(input: $input) +} diff --git a/generate/testdata/errors/schema.graphql b/generate/testdata/errors/schema.graphql index 95219767..dacfda44 100644 --- a/generate/testdata/errors/schema.graphql +++ b/generate/testdata/errors/schema.graphql @@ -1,6 +1,10 @@ type Query { f: String user: User + # The top-level default is currently not used - there is no query that would not specify the input argument. + # But it is here for completeness - maybe it will be used in future or cause some other unexpected issues. + default(input: InputWithDefaults! = {field: "input omitted"}): Boolean + omitempty(input: OmitemptyInput): Boolean } type User { @@ -9,3 +13,13 @@ type User { } scalar ValidScalar + +input InputWithDefaults { + field: String! = "input field omitted" + nullableField: String = "nullable input field omitted" +} + +input OmitemptyInput { + field: String! + nullableField: String +} diff --git a/generate/testdata/queries/DefaultInputs.graphql b/generate/testdata/queries/DefaultInputs.graphql new file mode 100644 index 00000000..3a22b51e --- /dev/null +++ b/generate/testdata/queries/DefaultInputs.graphql @@ -0,0 +1,7 @@ +# Without any extra directives or configuration, the defaults are never considered, +# as the client sends at least zero-value (struct with empty string). +query DefaultInputs( + $input: InputWithDefaults! +) { + default(input: $input) +} diff --git a/generate/testdata/queries/DefaultInputsPointer.graphql b/generate/testdata/queries/DefaultInputsPointer.graphql new file mode 100644 index 00000000..a6604c20 --- /dev/null +++ b/generate/testdata/queries/DefaultInputsPointer.graphql @@ -0,0 +1,9 @@ +# The `InputWithDefaults.field` cannot be `pointer: true`, together with implicit `omitempty: false`, as `null` is +# not a valid value there. However, nullableField should still be ok +# (this will send null, overwriting the server's default) +# @genqlient(for: "InputWithDefaults.nullableField", pointer: true) +query DefaultInputs( + $input: InputWithDefaults! +) { + default(input: $input) +} diff --git a/generate/testdata/queries/DefaultInputsWithDirective.graphql b/generate/testdata/queries/DefaultInputsWithDirective.graphql new file mode 100644 index 00000000..6739976d --- /dev/null +++ b/generate/testdata/queries/DefaultInputsWithDirective.graphql @@ -0,0 +1,7 @@ +# very similar to DefaultInputsWithForDirective.graphql - same expected behaviour, but takes a different code path(?) +# @genqlient(omitempty: true) +query DefaultInputs( + $input: InputWithDefaults! +) { + default(input: $input) +} diff --git a/generate/testdata/queries/DefaultInputsWithForDirective.graphql b/generate/testdata/queries/DefaultInputsWithForDirective.graphql new file mode 100644 index 00000000..ad493b98 --- /dev/null +++ b/generate/testdata/queries/DefaultInputsWithForDirective.graphql @@ -0,0 +1,7 @@ +# @genqlient(for: "InputWithDefaults.field", omitempty: true) +# @genqlient(for: "InputWithDefaults.nullableField", omitempty: true) +query DefaultInputs( + $input: InputWithDefaults! +) { + default(input: $input) +} diff --git a/generate/testdata/queries/OmitemptyFalse.graphql b/generate/testdata/queries/OmitemptyFalse.graphql new file mode 100644 index 00000000..506d2129 --- /dev/null +++ b/generate/testdata/queries/OmitemptyFalse.graphql @@ -0,0 +1,7 @@ +# @genqlient(omitempty: true) +# @genqlient(for: "OmitemptyInput.field",omitempty: false) +query OmitemptyFalse( + $input: OmitemptyInput +) { + omitempty(input: $input) +} diff --git a/generate/testdata/queries/schema.graphql b/generate/testdata/queries/schema.graphql index eb71bdd4..52559823 100644 --- a/generate/testdata/queries/schema.graphql +++ b/generate/testdata/queries/schema.graphql @@ -185,6 +185,10 @@ type Query { recur(input: RecursiveInput!): Recursive acceptsListOfListOfListsOfDates(datesss: [[[Date!]!]!]!): Boolean getPokemon(where: getPokemonBoolExp): [Pokemon!]! + # The top-level default is currently not used - there is no query that would not specify the input argument. + # But it is here for completeness - maybe it will be used in future or cause some other unexpected issues. + default(input: InputWithDefaults! = {field: "input omitted"}): Boolean + omitempty(input: OmitemptyInput): Boolean } type Mutation { @@ -212,3 +216,13 @@ input IntComparisonExp { _neq: Int _nin: [Int!] } + +input InputWithDefaults { + field: String! = "input field omitted" + nullableField: String = "nullable input field omitted" +} + +input OmitemptyInput { + field: String! + nullableField: String +} diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.go b/generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.go new file mode 100644 index 00000000..b3971938 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.go @@ -0,0 +1,69 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "github.com/Khan/genqlient/graphql" +) + +// DefaultInputsResponse is returned by DefaultInputs on success. +type DefaultInputsResponse struct { + Default bool `json:"default"` +} + +// GetDefault returns DefaultInputsResponse.Default, and is useful for accessing the field via an interface. +func (v *DefaultInputsResponse) GetDefault() bool { return v.Default } + +type InputWithDefaults struct { + Field string `json:"field"` + NullableField string `json:"nullableField"` +} + +// GetField returns InputWithDefaults.Field, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetField() string { return v.Field } + +// GetNullableField returns InputWithDefaults.NullableField, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetNullableField() string { return v.NullableField } + +// __DefaultInputsInput is used internally by genqlient +type __DefaultInputsInput struct { + Input InputWithDefaults `json:"input"` +} + +// GetInput returns __DefaultInputsInput.Input, and is useful for accessing the field via an interface. +func (v *__DefaultInputsInput) GetInput() InputWithDefaults { return v.Input } + +// The query or mutation executed by DefaultInputs. +const DefaultInputs_Operation = ` +query DefaultInputs ($input: InputWithDefaults!) { + default(input: $input) +} +` + +// Without any extra directives or configuration, the defaults are never considered, +// as the client sends at least zero-value (struct with empty string). +func DefaultInputs( + client_ graphql.Client, + input InputWithDefaults, +) (*DefaultInputsResponse, error) { + req_ := &graphql.Request{ + OpName: "DefaultInputs", + Query: DefaultInputs_Operation, + Variables: &__DefaultInputsInput{ + Input: input, + }, + } + var err_ error + + var data_ DefaultInputsResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.json b/generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.json new file mode 100644 index 00000000..14090db7 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputs.graphql-DefaultInputs.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "DefaultInputs", + "query": "\nquery DefaultInputs ($input: InputWithDefaults!) {\n\tdefault(input: $input)\n}\n", + "sourceLocation": "testdata/queries/DefaultInputs.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.go b/generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.go new file mode 100644 index 00000000..a2793024 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.go @@ -0,0 +1,70 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "github.com/Khan/genqlient/graphql" +) + +// DefaultInputsResponse is returned by DefaultInputs on success. +type DefaultInputsResponse struct { + Default bool `json:"default"` +} + +// GetDefault returns DefaultInputsResponse.Default, and is useful for accessing the field via an interface. +func (v *DefaultInputsResponse) GetDefault() bool { return v.Default } + +type InputWithDefaults struct { + Field string `json:"field"` + NullableField *string `json:"nullableField"` +} + +// GetField returns InputWithDefaults.Field, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetField() string { return v.Field } + +// GetNullableField returns InputWithDefaults.NullableField, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetNullableField() *string { return v.NullableField } + +// __DefaultInputsInput is used internally by genqlient +type __DefaultInputsInput struct { + Input InputWithDefaults `json:"input"` +} + +// GetInput returns __DefaultInputsInput.Input, and is useful for accessing the field via an interface. +func (v *__DefaultInputsInput) GetInput() InputWithDefaults { return v.Input } + +// The query or mutation executed by DefaultInputs. +const DefaultInputs_Operation = ` +query DefaultInputs ($input: InputWithDefaults!) { + default(input: $input) +} +` + +// The `InputWithDefaults.field` cannot be `pointer: true`, together with implicit `omitempty: false`, as `null` is +// not a valid value there. However, nullableField should still be ok +// (this will send null, overwriting the server's default) +func DefaultInputs( + client_ graphql.Client, + input InputWithDefaults, +) (*DefaultInputsResponse, error) { + req_ := &graphql.Request{ + OpName: "DefaultInputs", + Query: DefaultInputs_Operation, + Variables: &__DefaultInputsInput{ + Input: input, + }, + } + var err_ error + + var data_ DefaultInputsResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.json b/generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.json new file mode 100644 index 00000000..dd893fb0 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputsPointer.graphql-DefaultInputsPointer.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "DefaultInputs", + "query": "\nquery DefaultInputs ($input: InputWithDefaults!) {\n\tdefault(input: $input)\n}\n", + "sourceLocation": "testdata/queries/DefaultInputsPointer.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.go b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.go new file mode 100644 index 00000000..51aee5c7 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.go @@ -0,0 +1,68 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "github.com/Khan/genqlient/graphql" +) + +// DefaultInputsResponse is returned by DefaultInputs on success. +type DefaultInputsResponse struct { + Default bool `json:"default"` +} + +// GetDefault returns DefaultInputsResponse.Default, and is useful for accessing the field via an interface. +func (v *DefaultInputsResponse) GetDefault() bool { return v.Default } + +type InputWithDefaults struct { + Field string `json:"field,omitempty"` + NullableField string `json:"nullableField,omitempty"` +} + +// GetField returns InputWithDefaults.Field, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetField() string { return v.Field } + +// GetNullableField returns InputWithDefaults.NullableField, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetNullableField() string { return v.NullableField } + +// __DefaultInputsInput is used internally by genqlient +type __DefaultInputsInput struct { + Input InputWithDefaults `json:"input,omitempty"` +} + +// GetInput returns __DefaultInputsInput.Input, and is useful for accessing the field via an interface. +func (v *__DefaultInputsInput) GetInput() InputWithDefaults { return v.Input } + +// The query or mutation executed by DefaultInputs. +const DefaultInputs_Operation = ` +query DefaultInputs ($input: InputWithDefaults!) { + default(input: $input) +} +` + +// very similar to DefaultInputsWithForDirective.graphql - same expected behaviour, but takes a different code path(?) +func DefaultInputs( + client_ graphql.Client, + input InputWithDefaults, +) (*DefaultInputsResponse, error) { + req_ := &graphql.Request{ + OpName: "DefaultInputs", + Query: DefaultInputs_Operation, + Variables: &__DefaultInputsInput{ + Input: input, + }, + } + var err_ error + + var data_ DefaultInputsResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.json b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.json new file mode 100644 index 00000000..8e1334f3 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithDirective.graphql-DefaultInputsWithDirective.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "DefaultInputs", + "query": "\nquery DefaultInputs ($input: InputWithDefaults!) {\n\tdefault(input: $input)\n}\n", + "sourceLocation": "testdata/queries/DefaultInputsWithDirective.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.go b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.go new file mode 100644 index 00000000..5c76a562 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.go @@ -0,0 +1,67 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "github.com/Khan/genqlient/graphql" +) + +// DefaultInputsResponse is returned by DefaultInputs on success. +type DefaultInputsResponse struct { + Default bool `json:"default"` +} + +// GetDefault returns DefaultInputsResponse.Default, and is useful for accessing the field via an interface. +func (v *DefaultInputsResponse) GetDefault() bool { return v.Default } + +type InputWithDefaults struct { + Field string `json:"field,omitempty"` + NullableField string `json:"nullableField,omitempty"` +} + +// GetField returns InputWithDefaults.Field, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetField() string { return v.Field } + +// GetNullableField returns InputWithDefaults.NullableField, and is useful for accessing the field via an interface. +func (v *InputWithDefaults) GetNullableField() string { return v.NullableField } + +// __DefaultInputsInput is used internally by genqlient +type __DefaultInputsInput struct { + Input InputWithDefaults `json:"input"` +} + +// GetInput returns __DefaultInputsInput.Input, and is useful for accessing the field via an interface. +func (v *__DefaultInputsInput) GetInput() InputWithDefaults { return v.Input } + +// The query or mutation executed by DefaultInputs. +const DefaultInputs_Operation = ` +query DefaultInputs ($input: InputWithDefaults!) { + default(input: $input) +} +` + +func DefaultInputs( + client_ graphql.Client, + input InputWithDefaults, +) (*DefaultInputsResponse, error) { + req_ := &graphql.Request{ + OpName: "DefaultInputs", + Query: DefaultInputs_Operation, + Variables: &__DefaultInputsInput{ + Input: input, + }, + } + var err_ error + + var data_ DefaultInputsResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.json b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.json new file mode 100644 index 00000000..b618fc03 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-DefaultInputsWithForDirective.graphql-DefaultInputsWithForDirective.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "DefaultInputs", + "query": "\nquery DefaultInputs ($input: InputWithDefaults!) {\n\tdefault(input: $input)\n}\n", + "sourceLocation": "testdata/queries/DefaultInputsWithForDirective.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.go b/generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.go new file mode 100644 index 00000000..74b81eb5 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.go @@ -0,0 +1,67 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "github.com/Khan/genqlient/graphql" +) + +// OmitemptyFalseResponse is returned by OmitemptyFalse on success. +type OmitemptyFalseResponse struct { + Omitempty bool `json:"omitempty"` +} + +// GetOmitempty returns OmitemptyFalseResponse.Omitempty, and is useful for accessing the field via an interface. +func (v *OmitemptyFalseResponse) GetOmitempty() bool { return v.Omitempty } + +type OmitemptyInput struct { + Field string `json:"field"` + NullableField string `json:"nullableField,omitempty"` +} + +// GetField returns OmitemptyInput.Field, and is useful for accessing the field via an interface. +func (v *OmitemptyInput) GetField() string { return v.Field } + +// GetNullableField returns OmitemptyInput.NullableField, and is useful for accessing the field via an interface. +func (v *OmitemptyInput) GetNullableField() string { return v.NullableField } + +// __OmitemptyFalseInput is used internally by genqlient +type __OmitemptyFalseInput struct { + Input OmitemptyInput `json:"input,omitempty"` +} + +// GetInput returns __OmitemptyFalseInput.Input, and is useful for accessing the field via an interface. +func (v *__OmitemptyFalseInput) GetInput() OmitemptyInput { return v.Input } + +// The query or mutation executed by OmitemptyFalse. +const OmitemptyFalse_Operation = ` +query OmitemptyFalse ($input: OmitemptyInput) { + omitempty(input: $input) +} +` + +func OmitemptyFalse( + client_ graphql.Client, + input OmitemptyInput, +) (*OmitemptyFalseResponse, error) { + req_ := &graphql.Request{ + OpName: "OmitemptyFalse", + Query: OmitemptyFalse_Operation, + Variables: &__OmitemptyFalseInput{ + Input: input, + }, + } + var err_ error + + var data_ OmitemptyFalseResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.json b/generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.json new file mode 100644 index 00000000..8cd58706 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-OmitemptyFalse.graphql-OmitemptyFalse.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "OmitemptyFalse", + "query": "\nquery OmitemptyFalse ($input: OmitemptyInput) {\n\tomitempty(input: $input)\n}\n", + "sourceLocation": "testdata/queries/OmitemptyFalse.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointer-graphql b/generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointer-graphql new file mode 100644 index 00000000..25377c2e --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointer-graphql @@ -0,0 +1 @@ +testdata/errors/DefaultInputsNoOmitPointer.graphql:4: pointer on non-null input field can only be used together with omitempty: InputWithDefaults.field diff --git a/generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointerForDirective-graphql b/generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointerForDirective-graphql new file mode 100644 index 00000000..b1f1efff --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateErrors-DefaultInputsNoOmitPointerForDirective-graphql @@ -0,0 +1 @@ +testdata/errors/DefaultInputsNoOmitPointerForDirective.graphql:5: pointer on non-null input field can only be used together with omitempty: InputWithDefaults.field diff --git a/generate/testdata/snapshots/TestGenerateErrors-OmitemptyDirective-graphql b/generate/testdata/snapshots/TestGenerateErrors-OmitemptyDirective-graphql new file mode 100644 index 00000000..3d7a89a9 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateErrors-OmitemptyDirective-graphql @@ -0,0 +1 @@ +testdata/errors/OmitemptyDirective.graphql:4: omitempty may only be used on optional arguments: OmitemptyInput.field diff --git a/generate/testdata/snapshots/TestGenerateErrors-OmitemptyForDirective-graphql b/generate/testdata/snapshots/TestGenerateErrors-OmitemptyForDirective-graphql new file mode 100644 index 00000000..0091ca34 --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateErrors-OmitemptyForDirective-graphql @@ -0,0 +1 @@ +testdata/errors/OmitemptyForDirective.graphql:4: omitempty may only be used on optional arguments: OmitemptyInput.field From 35fbf5b7e2627c977faea48a233609164b6ed66a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 21:06:24 -0400 Subject: [PATCH 15/17] Bump github.com/vektah/gqlparser/v2 from 2.5.11 to 2.5.14 (#341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/vektah/gqlparser/v2](https://github.com/vektah/gqlparser) from 2.5.11 to 2.5.14.
Release notes

Sourced from github.com/vektah/gqlparser/v2's releases.

v2.5.14

What's Changed

Full Changelog: https://github.com/vektah/gqlparser/compare/v2.5.13...v2.5.14

v2.5.13

What's Changed

New Contributors

Full Changelog: https://github.com/vektah/gqlparser/compare/v2.5.12...v2.5.13

v2.5.12

What's Changed

New Contributors

Full Changelog: https://github.com/vektah/gqlparser/compare/v2.5.11...v2.5.12

Commits
  • 36a3658 Add ParseQueryWithLimit (#304)
  • d457fc0 Token limit fix CVE-2023-49559 (#291)
  • 6db1bd3 Bump braces from 3.0.2 to 3.0.3 in /validator/imported (#302)
  • 3900414 Bump the actions-deps group in /validator/imported with 7 updates (#301)
  • 7c770f6 Bump prettier in /validator/imported in the actions-deps group (#299)
  • 0ed4973 Bump the actions-deps group in /validator/imported with 6 updates (#298)
  • 00fd36f Bump the actions-deps group in /validator/imported with 8 updates (#297)
  • 9638a21 Bump github.com/stretchr/testify in the actions-deps group (#296)
  • 55ebe37 Add Dependabot.yml
  • 6d16c05 Redo github actions (#295)
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/vektah/gqlparser/v2&package-manager=go_modules&previous-version=2.5.11&new-version=2.5.14)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/Khan/genqlient/network/alerts).
--------- Signed-off-by: dependabot[bot] Signed-off-by: Steve Coffman Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Steve Coffman --- go.mod | 6 +++--- go.sum | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index c6fb5870..2a1f83c6 100644 --- a/go.mod +++ b/go.mod @@ -5,9 +5,10 @@ go 1.20 require ( github.com/99designs/gqlgen v0.17.44 github.com/alexflint/go-arg v1.4.2 + github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/bradleyjkemp/cupaloy/v2 v2.6.0 - github.com/stretchr/testify v1.8.4 - github.com/vektah/gqlparser/v2 v2.5.11 + github.com/stretchr/testify v1.9.0 + github.com/vektah/gqlparser/v2 v2.5.16 golang.org/x/tools v0.18.0 gopkg.in/yaml.v2 v2.4.0 ) @@ -15,7 +16,6 @@ require ( require ( github.com/agnivade/levenshtein v1.1.1 // indirect github.com/alexflint/go-scalar v1.0.0 // indirect - github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect diff --git a/go.sum b/go.sum index a3a0616f..8013cb2d 100644 --- a/go.sum +++ b/go.sum @@ -35,10 +35,10 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/vektah/gqlparser/v2 v2.5.11 h1:JJxLtXIoN7+3x6MBdtIP59TP1RANnY7pXOaDnADQSf8= -github.com/vektah/gqlparser/v2 v2.5.11/go.mod h1:1rCcfwB2ekJofmluGWXMSEnPMZgbxzwj6FaZ/4OT8Cc= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vektah/gqlparser/v2 v2.5.16 h1:1gcmLTvs3JLKXckwCwlUagVn/IlV2bwqle0vJ0vy5p8= +github.com/vektah/gqlparser/v2 v2.5.16/go.mod h1:1lz1OeCqgQbQepsGxPVywrjdBHW2T08PUS3pJqepRww= golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= From 2f265d2ed7d3c22f9748ca958461e2d5aac1dbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zahradn=C3=AD=C4=8Dek?= <18536740+Fazt01@users.noreply.github.com> Date: Wed, 3 Jul 2024 19:08:43 +0200 Subject: [PATCH 16/17] disable input validation when `use_struct_references` is enabled (#344) Fix for https://github.com/Khan/genqlient/issues/342. As `use_struct_references` is incompatible with `omitempty` and `pointer` validation, the most simple change is to disable the validation when the `use_struct_references` is enabled. Note this implementation disables the validation even for types that are not influenced by `use_struct_references`, but still better than disabling the validation globally. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable - [x] Included a link to the issue fixed, if applicable - [ ] Included documentation, for new features - [x] Added an entry to the changelog --- docs/CHANGELOG.md | 3 +- generate/convert.go | 33 ++++--- generate/generate_test.go | 5 + .../queries/UseStructReference.graphql | 6 ++ generate/testdata/queries/schema.graphql | 13 +++ ...ence.graphql-UseStructReference.graphql.go | 89 ++++++++++++++++++ ...ce.graphql-UseStructReference.graphql.json | 9 ++ ...uctReference-testdata-queries-generated.go | 92 +++++++++++++++++++ 8 files changed, 237 insertions(+), 13 deletions(-) create mode 100644 generate/testdata/queries/UseStructReference.graphql create mode 100644 generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.go create mode 100644 generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.json create mode 100644 generate/testdata/snapshots/TestGenerateWithConfig-UseStructReference-testdata-queries-generated.go diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bb73a750..b15e61f1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -23,7 +23,7 @@ When releasing a new version: ### Breaking changes: - omitempty validation: - - forbid `omitempty: false` (including implicit behaviour) when using pointer on non-null, no-default input field + - forbid `omitempty: false` (including implicit behaviour) when using pointer on non-null input field ### New features: @@ -34,6 +34,7 @@ When releasing a new version: - omitempty validation: - allow `omitempty` on non-nullable input field, if the field has a default - allow `omitempty: false` on an input field, even when it is non-nullable +- don't do `omitempty` and `pointer` input types validation when `use_struct_reference` is used, as the generated type is often not compatible with validation logic. ## v0.7.0 diff --git a/generate/convert.go b/generate/convert.go index b61f227b..44db36dd 100644 --- a/generate/convert.go +++ b/generate/convert.go @@ -248,6 +248,9 @@ func (g *generator) convertType( def := g.schema.Types[typ.Name()] goTyp, err := g.convertDefinition( namePrefix, def, typ.Position, selectionSet, options, queryOptions) + if err != nil { + return nil, err + } if g.getStructReference(def) { if options.Pointer == nil || *options.Pointer { @@ -274,7 +277,8 @@ func (g *generator) convertType( Elem: goTyp, } } - return goTyp, err + + return goTyp, nil } // getStructReference decides if a field should be of pointer type and have the omitempty flag set. @@ -450,17 +454,22 @@ func (g *generator) convertDefinition( return nil, err } - // Try to protect against generating field type that has possibility to send `null` to non-nullable graphQL - // type. This does not protect against lists/slices, as Go zero-slices are already serialized as `null` - // (which can therefore currently send invalid graphQL value - e.g. `null` for [String!]!) - // And does not protect against custom MarshalJSON. - _, isPointer := fieldGoType.(*goPointerType) - if field.Type.NonNull && isPointer && !fieldOptions.GetOmitempty() { - return nil, errorf(pos, "pointer on non-null input field can only be used together with omitempty: %s.%s", name, field.Name) - } - - if fieldOptions.GetOmitempty() && field.Type.NonNull && field.DefaultValue == nil { - return nil, errorf(pos, "omitempty may only be used on optional arguments: %s.%s", name, field.Name) + if !g.Config.StructReferences { + // Only do these validation when StructReferences are not used, as that can generate types that would not + // pass these validations. See https://github.com/Khan/genqlient/issues/342 + + // Try to protect against generating field type that has possibility to send `null` to non-nullable graphQL + // type. This does not protect against lists/slices, as Go zero-slices are already serialized as `null` + // (which can therefore currently send invalid graphQL value - e.g. `null` for [String!]!). + // And does not protect against custom MarshalJSON. + _, isPointer := fieldGoType.(*goPointerType) + if field.Type.NonNull && isPointer && !fieldOptions.GetOmitempty() { + return nil, errorf(pos, "pointer on non-null input field can only be used together with omitempty: %s.%s", name, field.Name) + } + + if fieldOptions.GetOmitempty() && field.Type.NonNull && field.DefaultValue == nil { + return nil, errorf(pos, "omitempty may only be used on optional arguments: %s.%s", name, field.Name) + } } goType.Fields[i] = &goStructField{ diff --git a/generate/generate_test.go b/generate/generate_test.go index 21dcaae2..01c46459 100644 --- a/generate/generate_test.go +++ b/generate/generate_test.go @@ -240,6 +240,11 @@ func TestGenerateWithConfig(t *testing.T) { Enums: map[string]CasingAlgorithm{"Role": CasingRaw}, }, }}, + { + "UseStructReference", "", []string{"UseStructReference.graphql"}, &Config{ + StructReferences: true, + }, + }, } sourceFilename := "SimpleQuery.graphql" diff --git a/generate/testdata/queries/UseStructReference.graphql b/generate/testdata/queries/UseStructReference.graphql new file mode 100644 index 00000000..dc682a62 --- /dev/null +++ b/generate/testdata/queries/UseStructReference.graphql @@ -0,0 +1,6 @@ +#https://github.com/Khan/genqlient/issues/342 +query UseStructReference( + $input: UseStructReferencesInput! +) { + useStructReferencesInput(input: $input) +} diff --git a/generate/testdata/queries/schema.graphql b/generate/testdata/queries/schema.graphql index 52559823..528b21ef 100644 --- a/generate/testdata/queries/schema.graphql +++ b/generate/testdata/queries/schema.graphql @@ -189,6 +189,7 @@ type Query { # But it is here for completeness - maybe it will be used in future or cause some other unexpected issues. default(input: InputWithDefaults! = {field: "input omitted"}): Boolean omitempty(input: OmitemptyInput): Boolean + useStructReferencesInput(input: UseStructReferencesInput!): Boolean } type Mutation { @@ -226,3 +227,15 @@ input OmitemptyInput { field: String! nullableField: String } + +input StructInput { + field: String +} + +input UseStructReferencesInput { + struct: StructInput! + nullableStruct: StructInput + list: [StructInput!]! + listOfNullable: [StructInput]! + nullableList: [StructInput!] +} diff --git a/generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.go b/generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.go new file mode 100644 index 00000000..09c5c6ce --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.go @@ -0,0 +1,89 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package test + +import ( + "github.com/Khan/genqlient/graphql" +) + +type StructInput struct { + Field string `json:"field"` +} + +// GetField returns StructInput.Field, and is useful for accessing the field via an interface. +func (v *StructInput) GetField() string { return v.Field } + +// UseStructReferenceResponse is returned by UseStructReference on success. +type UseStructReferenceResponse struct { + UseStructReferencesInput bool `json:"useStructReferencesInput"` +} + +// GetUseStructReferencesInput returns UseStructReferenceResponse.UseStructReferencesInput, and is useful for accessing the field via an interface. +func (v *UseStructReferenceResponse) GetUseStructReferencesInput() bool { + return v.UseStructReferencesInput +} + +type UseStructReferencesInput struct { + Struct StructInput `json:"struct"` + NullableStruct StructInput `json:"nullableStruct"` + List []StructInput `json:"list"` + ListOfNullable []StructInput `json:"listOfNullable"` + NullableList []StructInput `json:"nullableList"` +} + +// GetStruct returns UseStructReferencesInput.Struct, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetStruct() StructInput { return v.Struct } + +// GetNullableStruct returns UseStructReferencesInput.NullableStruct, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetNullableStruct() StructInput { return v.NullableStruct } + +// GetList returns UseStructReferencesInput.List, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetList() []StructInput { return v.List } + +// GetListOfNullable returns UseStructReferencesInput.ListOfNullable, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetListOfNullable() []StructInput { return v.ListOfNullable } + +// GetNullableList returns UseStructReferencesInput.NullableList, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetNullableList() []StructInput { return v.NullableList } + +// __UseStructReferenceInput is used internally by genqlient +type __UseStructReferenceInput struct { + Input UseStructReferencesInput `json:"input"` +} + +// GetInput returns __UseStructReferenceInput.Input, and is useful for accessing the field via an interface. +func (v *__UseStructReferenceInput) GetInput() UseStructReferencesInput { return v.Input } + +// The query or mutation executed by UseStructReference. +const UseStructReference_Operation = ` +query UseStructReference ($input: UseStructReferencesInput!) { + useStructReferencesInput(input: $input) +} +` + +// https://github.com/Khan/genqlient/issues/342 +func UseStructReference( + client_ graphql.Client, + input UseStructReferencesInput, +) (*UseStructReferenceResponse, error) { + req_ := &graphql.Request{ + OpName: "UseStructReference", + Query: UseStructReference_Operation, + Variables: &__UseStructReferenceInput{ + Input: input, + }, + } + var err_ error + + var data_ UseStructReferenceResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + nil, + req_, + resp_, + ) + + return &data_, err_ +} + diff --git a/generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.json b/generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.json new file mode 100644 index 00000000..2f3bf39a --- /dev/null +++ b/generate/testdata/snapshots/TestGenerate-UseStructReference.graphql-UseStructReference.graphql.json @@ -0,0 +1,9 @@ +{ + "operations": [ + { + "operationName": "UseStructReference", + "query": "\nquery UseStructReference ($input: UseStructReferencesInput!) {\n\tuseStructReferencesInput(input: $input)\n}\n", + "sourceLocation": "testdata/queries/UseStructReference.graphql" + } + ] +} diff --git a/generate/testdata/snapshots/TestGenerateWithConfig-UseStructReference-testdata-queries-generated.go b/generate/testdata/snapshots/TestGenerateWithConfig-UseStructReference-testdata-queries-generated.go new file mode 100644 index 00000000..c2c3723a --- /dev/null +++ b/generate/testdata/snapshots/TestGenerateWithConfig-UseStructReference-testdata-queries-generated.go @@ -0,0 +1,92 @@ +// Code generated by github.com/Khan/genqlient, DO NOT EDIT. + +package queries + +import ( + "context" + + "github.com/Khan/genqlient/graphql" +) + +type StructInput struct { + Field string `json:"field"` +} + +// GetField returns StructInput.Field, and is useful for accessing the field via an interface. +func (v *StructInput) GetField() string { return v.Field } + +// UseStructReferenceResponse is returned by UseStructReference on success. +type UseStructReferenceResponse struct { + UseStructReferencesInput bool `json:"useStructReferencesInput"` +} + +// GetUseStructReferencesInput returns UseStructReferenceResponse.UseStructReferencesInput, and is useful for accessing the field via an interface. +func (v *UseStructReferenceResponse) GetUseStructReferencesInput() bool { + return v.UseStructReferencesInput +} + +type UseStructReferencesInput struct { + Struct *StructInput `json:"struct,omitempty"` + NullableStruct *StructInput `json:"nullableStruct,omitempty"` + List []*StructInput `json:"list,omitempty"` + ListOfNullable []*StructInput `json:"listOfNullable,omitempty"` + NullableList []*StructInput `json:"nullableList,omitempty"` +} + +// GetStruct returns UseStructReferencesInput.Struct, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetStruct() *StructInput { return v.Struct } + +// GetNullableStruct returns UseStructReferencesInput.NullableStruct, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetNullableStruct() *StructInput { return v.NullableStruct } + +// GetList returns UseStructReferencesInput.List, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetList() []*StructInput { return v.List } + +// GetListOfNullable returns UseStructReferencesInput.ListOfNullable, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetListOfNullable() []*StructInput { return v.ListOfNullable } + +// GetNullableList returns UseStructReferencesInput.NullableList, and is useful for accessing the field via an interface. +func (v *UseStructReferencesInput) GetNullableList() []*StructInput { return v.NullableList } + +// __UseStructReferenceInput is used internally by genqlient +type __UseStructReferenceInput struct { + Input *UseStructReferencesInput `json:"input,omitempty"` +} + +// GetInput returns __UseStructReferenceInput.Input, and is useful for accessing the field via an interface. +func (v *__UseStructReferenceInput) GetInput() *UseStructReferencesInput { return v.Input } + +// The query or mutation executed by UseStructReference. +const UseStructReference_Operation = ` +query UseStructReference ($input: UseStructReferencesInput!) { + useStructReferencesInput(input: $input) +} +` + +// https://github.com/Khan/genqlient/issues/342 +func UseStructReference( + ctx_ context.Context, + client_ graphql.Client, + input *UseStructReferencesInput, +) (*UseStructReferenceResponse, error) { + req_ := &graphql.Request{ + OpName: "UseStructReference", + Query: UseStructReference_Operation, + Variables: &__UseStructReferenceInput{ + Input: input, + }, + } + var err_ error + + var data_ UseStructReferenceResponse + resp_ := &graphql.Response{Data: &data_} + + err_ = client_.MakeRequest( + ctx_, + req_, + resp_, + ) + + return &data_, err_ +} + From 07f367726050f90e94a42941a4dcc2ea694cd2af Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Mon, 12 Aug 2024 19:52:29 -0700 Subject: [PATCH 17/17] Fix markdown in schema docs (#346) See: https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/organizing-information-with-tables#creating-a-table. Fixes #339. I have: - [x] Written a clear PR title and description (above) - [x] Signed the [Khan Academy CLA](https://www.khanacademy.org/r/cla) - [x] Added tests covering my changes, if applicable - [x] Included a link to the issue fixed, if applicable - [x] Included documentation, for new features - [x] Added an entry to the changelog --- docs/schema.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/schema.md b/docs/schema.md index e28be829..91a9c4a5 100644 --- a/docs/schema.md +++ b/docs/schema.md @@ -46,7 +46,7 @@ This can now be invoked upon `go generate` via `//go:generate yourpkg/generate`. GraphQL [defines][spec#scalar] five standard scalar types, which genqlient automatically maps to the following Go types: | GraphQL type | Go type | ----------------------------- +|--------------|-----------| | `Int` | `int` | | `Float` | `float64` | | `String` | `string` |