From 27ee3c2dbd5fa8067e45b7be3e2c5b582189196f Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Tue, 30 Mar 2021 17:53:31 -0700 Subject: [PATCH] fixes while integrating into webapp --- README.md | 10 ++++++++++ generate/config.go | 19 ++++++++++--------- generate/generate.go | 11 +++++++++-- generate/main.go | 8 ++++++++ generate/parse.go | 8 ++++---- generate/types.go | 5 +++-- go.mod | 7 ++----- go.sum | 32 +++++++++++++++++--------------- graphql/client.go | 2 +- 9 files changed, 64 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 41a7120..1feed6f 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,16 @@ fmt.Println("you are", viewerResp.Viewer.MyName) For a complete working example, see `example/`. +## Documentation for generated code + +For each GraphQL operation (query or mutation), genqlient generates a Go function with the exact same name, which accepts: +- a `context.Context` (unless configured otherwise) +- a `genqlient/graphql.Client` (you might have a global one, or init it inline) +- arguments corresponding to the query arguments +It returns a pointer to a struct representing the query-result, and an `error`. The struct will always be initialized (never nil), even on error. The error may be a `github.com/vektah/gqlparser/v2/gqlerror.List`, if it was a GraphQL-level error (in this case the returned struct may still contain useful data, if the API returns data even on error), or may be another error if, for example, the whole HTTP request failed (in which case the struct is unlikely to contain useful data). If the GraphQL operation has a comment immediately above it, that comment text will be used as the GoDoc for the generated function. + +TODO: document generated types further, especially if they become customizable. + ## Tests `go test ./...` tests code generation. (This is run by GitHub Actions.) diff --git a/generate/config.go b/generate/config.go index b717729..1a9cfe2 100644 --- a/generate/config.go +++ b/generate/config.go @@ -27,6 +27,7 @@ type Config struct { // how to convert that to SDL). Schema string `yaml:"schema"` // The filename with the queries; defaults to queries.graphql + // TODO: allow multiple files? Queries string `yaml:"queries"` // The filename to which to write the generated code; defaults to // generated.go @@ -45,14 +46,20 @@ type Config struct { ClientGetter string `yaml:"client_getter"` } -func (c *Config) ValidateAndFillDefaults() error { +func (c *Config) ValidateAndFillDefaults(configFilename string) error { + // Make paths relative to config dir + configDir := filepath.Dir(configFilename) + c.Schema = filepath.Join(configDir, c.Schema) + c.Queries = filepath.Join(configDir, c.Queries) + c.Generated = filepath.Join(configDir, c.Generated) + if c.Package == "" { abs, err := filepath.Abs(c.Generated) if err != nil { return fmt.Errorf("unable to guess package-name: %v", err) } - base := filepath.Base(abs) + base := filepath.Base(filepath.Dir(abs)) if !token.IsIdentifier(base) { return fmt.Errorf("unable to guess package-name: %v is not a valid identifier", base) } @@ -86,16 +93,10 @@ func ReadAndValidateConfig(filename string) (*Config, error) { } } - err := config.ValidateAndFillDefaults() + err := config.ValidateAndFillDefaults(filename) if err != nil { return nil, fmt.Errorf("invalid config file %v: %v", filename, err) } - // Make paths relative to config dir - basename := filepath.Dir(filename) - config.Schema = filepath.Join(basename, config.Schema) - config.Queries = filepath.Join(basename, config.Queries) - config.Generated = filepath.Join(basename, config.Generated) - return &config, nil } diff --git a/generate/generate.go b/generate/generate.go index ecc98a9..031f2cd 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -7,8 +7,8 @@ import ( "sort" "strings" - "github.com/vektah/gqlparser/ast" - "github.com/vektah/gqlparser/formatter" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/formatter" ) var fileTemplate = mustTemplate("operation.go.tmpl") @@ -154,6 +154,13 @@ func Generate(config *Config) ([]byte, error) { return nil, err } + // TODO: we could also allow this, and generate an empty file with just the + // package-name, if it turns out to be more convenient that way. (As-is, + // we generate a broken file, with just (unused) imports.) + if len(document.Operations) == 0 { + return nil, fmt.Errorf("no queries found in %v", config.Queries) + } + g := newGenerator(config, schema) for _, op := range document.Operations { if err = g.addOperation(op); err != nil { diff --git a/generate/main.go b/generate/main.go index 4e77243..0dd347a 100644 --- a/generate/main.go +++ b/generate/main.go @@ -4,6 +4,7 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" ) func readConfigGenerateAndWrite(configFilename string) error { @@ -17,6 +18,13 @@ func readConfigGenerateAndWrite(configFilename string) error { return err } + err = os.MkdirAll(filepath.Dir(config.Generated), 0o755) + if err != nil { + return fmt.Errorf( + "could not create parent directory for generated file %v: %v", + config.Generated, err) + } + err = ioutil.WriteFile(config.Generated, code, 0o644) if err != nil { return fmt.Errorf("could not write generated file %v: %v", diff --git a/generate/parse.go b/generate/parse.go index 62ad944..0b0610a 100644 --- a/generate/parse.go +++ b/generate/parse.go @@ -4,10 +4,10 @@ import ( "fmt" "io/ioutil" - "github.com/vektah/gqlparser" - "github.com/vektah/gqlparser/ast" - "github.com/vektah/gqlparser/parser" - "github.com/vektah/gqlparser/validator" + "github.com/vektah/gqlparser/v2" + "github.com/vektah/gqlparser/v2/ast" + "github.com/vektah/gqlparser/v2/parser" + "github.com/vektah/gqlparser/v2/validator" ) func getSchema(filename string) (*ast.Schema, error) { diff --git a/generate/types.go b/generate/types.go index 6ff4dd0..c4fee4d 100644 --- a/generate/types.go +++ b/generate/types.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/v2/ast" ) type typeBuilder struct { @@ -120,7 +120,7 @@ type outputField struct{ field *ast.Field } func (s outputField) Alias() string { // gqlparser sets Alias even if the field is not aliased, see e.g. - // https://github.com/vektah/gqlparser/blob/c06d8e0d135f285e37e7f1ff397f10e049733eb3/parser/query.go#L150 + // https://github.com/vektah/gqlparser/v2/blob/c06d8e0d135f285e37e7f1ff397f10e049733eb3/parser/query.go#L150 return s.field.Alias } @@ -201,6 +201,7 @@ func (builder *typeBuilder) writeField(field field) error { // `query q { a: f { b }, c: f { d } }` we need separate types for a // and c, even though they are the same type in GraphQL, because they // have different fields. + // TODO: if this is an input type, we should skip the prefixing! builder.typeNamePrefix+upperFirst(field.Alias()), "", typ, fields) if err != nil { return err diff --git a/go.mod b/go.mod index ae5038e..0403baa 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,6 @@ module github.com/Khan/genqlient go 1.13 require ( - github.com/Khan/graphql v0.0.0-20191109005718-3b51154b2bc5 - github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f // indirect - github.com/vektah/gqlparser v1.2.0 - golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect - gopkg.in/yaml.v2 v2.2.2 + github.com/vektah/gqlparser/v2 v2.1.0 + gopkg.in/yaml.v2 v2.2.4 ) diff --git a/go.sum b/go.sum index 818b0ef..9378da9 100644 --- a/go.sum +++ b/go.sum @@ -1,28 +1,30 @@ -github.com/Khan/graphql v0.0.0-20191109005718-3b51154b2bc5 h1:C7qbo6snTa0/lSpVKYhXiw3dJ/9FYiwZSsU+4Bov9qg= -github.com/Khan/graphql v0.0.0-20191109005718-3b51154b2bc5/go.mod h1:DLkRTcWV7KR4zw1iTm1lpShI3XP3nFmIMs3fHxYAsF0= github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= 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/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/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/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.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f h1:tygelZueB1EtXkPI6mQ4o9DQ0+FKW41hTbunoXZCTqk= -github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/vektah/gqlparser v1.2.0 h1:ntkSCX7F5ZJKl+HIVnmLaO269MruasVpNiMOjX9kgo0= -github.com/vektah/gqlparser v1.2.0/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/vektah/gqlparser/v2 v2.1.0 h1:uiKJ+T5HMGGQM2kRKQ8Pxw8+Zq9qhhZhz/lieYvCMns= +github.com/vektah/gqlparser/v2 v2.1.0/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms= golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/graphql/client.go b/graphql/client.go index 85804fb..827367e 100644 --- a/graphql/client.go +++ b/graphql/client.go @@ -7,7 +7,7 @@ import ( "fmt" "net/http" - "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/v2/gqlerror" ) // Client is the interface that the generate code calls into to actually make