diff options
author | Michael Muré <batolettre@gmail.com> | 2018-12-23 17:11:37 +0100 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-12-23 17:11:37 +0100 |
commit | 1410a1af75b1ab9ea3f980a7e372728f9a123abf (patch) | |
tree | e24db8f84c48b20158b1f1fd6d281d700421279c /vendor/github.com/99designs/gqlgen | |
parent | 8fc15a032f021c855abf66ed303c003d57c340ea (diff) | |
download | git-bug-1410a1af75b1ab9ea3f980a7e372728f9a123abf.tar.gz |
upgrade gqlgen to v0.7.1
Diffstat (limited to 'vendor/github.com/99designs/gqlgen')
40 files changed, 1402 insertions, 483 deletions
diff --git a/vendor/github.com/99designs/gqlgen/cmd/gen.go b/vendor/github.com/99designs/gqlgen/cmd/gen.go new file mode 100644 index 00000000..3842f02b --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/cmd/gen.go @@ -0,0 +1,60 @@ +package cmd + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/99designs/gqlgen/codegen" + "github.com/pkg/errors" + "github.com/urfave/cli" +) + +var genCmd = cli.Command{ + Name: "generate", + Usage: "generate a graphql server based on schema", + Flags: []cli.Flag{ + cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, + cli.StringFlag{Name: "config, c", Usage: "the config filename"}, + }, + Action: func(ctx *cli.Context) { + var config *codegen.Config + var err error + if configFilename := ctx.String("config"); configFilename != "" { + config, err = codegen.LoadConfig(configFilename) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + } else { + config, err = codegen.LoadConfigFromDefaultLocations() + if os.IsNotExist(errors.Cause(err)) { + config = codegen.DefaultConfig() + } else if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + } + + for _, filename := range config.SchemaFilename { + var schemaRaw []byte + schemaRaw, err = ioutil.ReadFile(filename) + if err != nil { + fmt.Fprintln(os.Stderr, "unable to open schema: "+err.Error()) + os.Exit(1) + } + config.SchemaStr[filename] = string(schemaRaw) + } + + if err = config.Check(); err != nil { + fmt.Fprintln(os.Stderr, "invalid config format: "+err.Error()) + os.Exit(1) + } + + err = codegen.Generate(*config) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(2) + } + }, +} diff --git a/vendor/github.com/99designs/gqlgen/cmd/init.go b/vendor/github.com/99designs/gqlgen/cmd/init.go new file mode 100644 index 00000000..1e7c18b9 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/cmd/init.go @@ -0,0 +1,162 @@ +package cmd + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/99designs/gqlgen/codegen" + "github.com/pkg/errors" + "github.com/urfave/cli" + "gopkg.in/yaml.v2" +) + +var configComment = ` +# .gqlgen.yml example +# +# Refer to https://gqlgen.com/config/ +# for detailed .gqlgen.yml documentation. +` + +var schemaDefault = ` +# GraphQL schema example +# +# https://gqlgen.com/getting-started/ + +type Todo { + id: ID! + text: String! + done: Boolean! + user: User! +} + +type User { + id: ID! + name: String! +} + +type Query { + todos: [Todo!]! +} + +input NewTodo { + text: String! + userId: String! +} + +type Mutation { + createTodo(input: NewTodo!): Todo! +} +` + +var initCmd = cli.Command{ + Name: "init", + Usage: "create a new gqlgen project", + Flags: []cli.Flag{ + cli.BoolFlag{Name: "verbose, v", Usage: "show logs"}, + cli.StringFlag{Name: "config, c", Usage: "the config filename"}, + cli.StringFlag{Name: "server", Usage: "where to write the server stub to", Value: "server/server.go"}, + cli.StringFlag{Name: "schema", Usage: "where to write the schema stub to", Value: "schema.graphql"}, + }, + Action: func(ctx *cli.Context) { + initSchema(ctx.String("schema")) + config := initConfig(ctx) + + GenerateGraphServer(config, ctx.String("server")) + }, +} + +func GenerateGraphServer(config *codegen.Config, serverFilename string) { + for _, filename := range config.SchemaFilename { + schemaRaw, err := ioutil.ReadFile(filename) + if err != nil { + fmt.Fprintln(os.Stderr, "unable to open schema: "+err.Error()) + os.Exit(1) + } + config.SchemaStr[filename] = string(schemaRaw) + } + + if err := config.Check(); err != nil { + fmt.Fprintln(os.Stderr, "invalid config format: "+err.Error()) + os.Exit(1) + } + + if err := codegen.Generate(*config); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if err := codegen.GenerateServer(*config, serverFilename); err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + fmt.Fprintf(os.Stdout, "Exec \"go run ./%s\" to start GraphQL server\n", serverFilename) +} + +func initConfig(ctx *cli.Context) *codegen.Config { + var config *codegen.Config + var err error + configFilename := ctx.String("config") + if configFilename != "" { + config, err = codegen.LoadConfig(configFilename) + } else { + config, err = codegen.LoadConfigFromDefaultLocations() + } + + if config != nil { + fmt.Fprintf(os.Stderr, "init failed: a configuration file already exists at %s\n", config.FilePath) + os.Exit(1) + } + + if !os.IsNotExist(errors.Cause(err)) { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(1) + } + + if configFilename == "" { + configFilename = "gqlgen.yml" + } + config = codegen.DefaultConfig() + + config.Resolver = codegen.PackageConfig{ + Filename: "resolver.go", + Type: "Resolver", + } + + var buf bytes.Buffer + buf.WriteString(strings.TrimSpace(configComment)) + buf.WriteString("\n\n") + { + var b []byte + b, err = yaml.Marshal(config) + if err != nil { + fmt.Fprintln(os.Stderr, "unable to marshal yaml: "+err.Error()) + os.Exit(1) + } + buf.Write(b) + } + + err = ioutil.WriteFile(configFilename, buf.Bytes(), 0644) + if err != nil { + fmt.Fprintln(os.Stderr, "unable to write config file: "+err.Error()) + os.Exit(1) + } + + return config +} + +func initSchema(schemaFilename string) { + _, err := os.Stat(schemaFilename) + if !os.IsNotExist(err) { + return + } + + err = ioutil.WriteFile(schemaFilename, []byte(strings.TrimSpace(schemaDefault)), 0644) + if err != nil { + fmt.Fprintln(os.Stderr, "unable to write schema file: "+err.Error()) + os.Exit(1) + } +} diff --git a/vendor/github.com/99designs/gqlgen/cmd/root.go b/vendor/github.com/99designs/gqlgen/cmd/root.go new file mode 100644 index 00000000..519c2e1a --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/cmd/root.go @@ -0,0 +1,53 @@ +package cmd + +import ( + "fmt" + "io/ioutil" + "log" + "os" + + "github.com/99designs/gqlgen/graphql" + "github.com/99designs/gqlgen/internal/gopath" + "github.com/urfave/cli" + + // Required since otherwise dep will prune away these unused packages before codegen has a chance to run + _ "github.com/99designs/gqlgen/handler" +) + +func Execute() { + app := cli.NewApp() + app.Name = "gqlgen" + app.Usage = genCmd.Usage + app.Description = "This is a library for quickly creating strictly typed graphql servers in golang. See https://gqlgen.com/ for a getting started guide." + app.HideVersion = true + app.Flags = genCmd.Flags + app.Version = graphql.Version + app.Before = func(context *cli.Context) error { + pwd, err := os.Getwd() + if err != nil { + return fmt.Errorf("unable to determine current workding dir: %s\n", err.Error()) + } + + if !gopath.Contains(pwd) { + return fmt.Errorf("gqlgen must be run from inside your $GOPATH\n") + } + if context.Bool("verbose") { + log.SetFlags(0) + } else { + log.SetOutput(ioutil.Discard) + } + return nil + } + + app.Action = genCmd.Action + app.Commands = []cli.Command{ + genCmd, + initCmd, + versionCmd, + } + + if err := app.Run(os.Args); err != nil { + fmt.Fprintf(os.Stderr, err.Error()) + os.Exit(1) + } +} diff --git a/vendor/github.com/99designs/gqlgen/cmd/version.go b/vendor/github.com/99designs/gqlgen/cmd/version.go new file mode 100644 index 00000000..8b7442d4 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/cmd/version.go @@ -0,0 +1,16 @@ +package cmd + +import ( + "fmt" + + "github.com/99designs/gqlgen/graphql" + "github.com/urfave/cli" +) + +var versionCmd = cli.Command{ + Name: "version", + Usage: "print the version string", + Action: func(ctx *cli.Context) { + fmt.Println(graphql.Version) + }, +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/ambient.go b/vendor/github.com/99designs/gqlgen/codegen/ambient.go new file mode 100644 index 00000000..c9909fcc --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/ambient.go @@ -0,0 +1,10 @@ +package codegen + +import ( + // Import and ignore the ambient imports listed below so dependency managers + // don't prune unused code for us. Both lists should be kept in sync. + _ "github.com/99designs/gqlgen/graphql" + _ "github.com/99designs/gqlgen/graphql/introspection" + _ "github.com/vektah/gqlparser" + _ "github.com/vektah/gqlparser/ast" +) diff --git a/vendor/github.com/99designs/gqlgen/codegen/build.go b/vendor/github.com/99designs/gqlgen/codegen/build.go index 42dedbf8..582689a7 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/build.go @@ -15,25 +15,22 @@ type Build struct { Objects Objects Inputs Objects Interfaces []*Interface - Imports []*Import QueryRoot *Object MutationRoot *Object SubscriptionRoot *Object - SchemaRaw string - SchemaFilename string + SchemaRaw map[string]string + SchemaFilename SchemaFilenames Directives []*Directive } type ModelBuild struct { PackageName string - Imports []*Import Models []Model Enums []Enum } type ResolverBuild struct { PackageName string - Imports []*Import ResolverType string Objects Objects ResolverFound bool @@ -41,7 +38,6 @@ type ResolverBuild struct { type ServerBuild struct { PackageName string - Imports []*Import ExecPackageName string ResolverPackageName string } @@ -50,16 +46,16 @@ type ServerBuild struct { func (cfg *Config) models() (*ModelBuild, error) { namedTypes := cfg.buildNamedTypes() - progLoader := newLoader(namedTypes, true) + progLoader := cfg.newLoaderWithoutErrors() + prog, err := progLoader.Load() if err != nil { return nil, errors.Wrap(err, "loading failed") } - imports := buildImports(namedTypes, cfg.Model.Dir()) - cfg.bindTypes(imports, namedTypes, cfg.Model.Dir(), prog) + cfg.bindTypes(namedTypes, cfg.Model.Dir(), prog) - models, err := cfg.buildModels(namedTypes, prog, imports) + models, err := cfg.buildModels(namedTypes, prog) if err != nil { return nil, err } @@ -67,13 +63,12 @@ func (cfg *Config) models() (*ModelBuild, error) { PackageName: cfg.Model.Package, Models: models, Enums: cfg.buildEnums(namedTypes), - Imports: imports.finalize(), }, nil } // bind a schema together with some code to generate a Build func (cfg *Config) resolver() (*ResolverBuild, error) { - progLoader := newLoader(cfg.buildNamedTypes(), true) + progLoader := cfg.newLoaderWithoutErrors() progLoader.Import(cfg.Resolver.ImportPath()) prog, err := progLoader.Load() @@ -84,13 +79,10 @@ func (cfg *Config) resolver() (*ResolverBuild, error) { destDir := cfg.Resolver.Dir() namedTypes := cfg.buildNamedTypes() - imports := buildImports(namedTypes, destDir) - imports.add(cfg.Exec.ImportPath()) - imports.add("github.com/99designs/gqlgen/handler") // avoid import github.com/vektah/gqlgen/handler - cfg.bindTypes(imports, namedTypes, destDir, prog) + cfg.bindTypes(namedTypes, destDir, prog) - objects, err := cfg.buildObjects(namedTypes, prog, imports) + objects, err := cfg.buildObjects(namedTypes, prog) if err != nil { return nil, err } @@ -100,7 +92,6 @@ func (cfg *Config) resolver() (*ResolverBuild, error) { return &ResolverBuild{ PackageName: cfg.Resolver.Package, - Imports: imports.finalize(), Objects: objects, ResolverType: cfg.Resolver.Type, ResolverFound: resolverFound, @@ -108,15 +99,10 @@ func (cfg *Config) resolver() (*ResolverBuild, error) { } func (cfg *Config) server(destDir string) *ServerBuild { - imports := buildImports(NamedTypes{}, destDir) - imports.add(cfg.Exec.ImportPath()) - imports.add(cfg.Resolver.ImportPath()) - return &ServerBuild{ PackageName: cfg.Resolver.Package, - Imports: imports.finalize(), - ExecPackageName: cfg.Exec.Package, - ResolverPackageName: cfg.Resolver.Package, + ExecPackageName: cfg.Exec.ImportPath(), + ResolverPackageName: cfg.Resolver.ImportPath(), } } @@ -124,21 +110,20 @@ func (cfg *Config) server(destDir string) *ServerBuild { func (cfg *Config) bind() (*Build, error) { namedTypes := cfg.buildNamedTypes() - progLoader := newLoader(namedTypes, true) + progLoader := cfg.newLoaderWithoutErrors() prog, err := progLoader.Load() if err != nil { return nil, errors.Wrap(err, "loading failed") } - imports := buildImports(namedTypes, cfg.Exec.Dir()) - cfg.bindTypes(imports, namedTypes, cfg.Exec.Dir(), prog) + cfg.bindTypes(namedTypes, cfg.Exec.Dir(), prog) - objects, err := cfg.buildObjects(namedTypes, prog, imports) + objects, err := cfg.buildObjects(namedTypes, prog) if err != nil { return nil, err } - inputs, err := cfg.buildInputs(namedTypes, prog, imports) + inputs, err := cfg.buildInputs(namedTypes, prog) if err != nil { return nil, err } @@ -152,7 +137,6 @@ func (cfg *Config) bind() (*Build, error) { Objects: objects, Interfaces: cfg.buildInterfaces(namedTypes, prog), Inputs: inputs, - Imports: imports.finalize(), SchemaRaw: cfg.SchemaStr, SchemaFilename: cfg.SchemaFilename, Directives: directives, @@ -175,29 +159,25 @@ func (cfg *Config) bind() (*Build, error) { } func (cfg *Config) validate() error { - progLoader := newLoader(cfg.buildNamedTypes(), false) + progLoader := cfg.newLoaderWithErrors() _, err := progLoader.Load() return err } -func newLoader(namedTypes NamedTypes, allowErrors bool) loader.Config { +func (cfg *Config) newLoaderWithErrors() loader.Config { conf := loader.Config{} - if allowErrors { - conf = loader.Config{ - AllowErrors: true, - TypeChecker: types.Config{ - Error: func(e error) {}, - }, - } - } - for _, imp := range ambientImports { - conf.Import(imp) - } - - for _, imp := range namedTypes { - if imp.Package != "" { - conf.Import(imp.Package) - } + + for _, pkg := range cfg.Models.referencedPackages() { + conf.Import(pkg) + } + return conf +} + +func (cfg *Config) newLoaderWithoutErrors() loader.Config { + conf := cfg.newLoaderWithErrors() + conf.AllowErrors = true + conf.TypeChecker = types.Config{ + Error: func(e error) {}, } return conf } diff --git a/vendor/github.com/99designs/gqlgen/codegen/codegen.go b/vendor/github.com/99designs/gqlgen/codegen/codegen.go index 27873400..773e3db7 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/codegen.go +++ b/vendor/github.com/99designs/gqlgen/codegen/codegen.go @@ -151,8 +151,13 @@ func (cfg *Config) normalize() error { } } + var sources []*ast.Source + for _, filename := range cfg.SchemaFilename { + sources = append(sources, &ast.Source{Name: filename, Input: cfg.SchemaStr[filename]}) + } + var err *gqlerror.Error - cfg.schema, err = gqlparser.LoadSchema(&ast.Source{Name: cfg.SchemaFilename, Input: cfg.SchemaStr}) + cfg.schema, err = gqlparser.LoadSchema(sources...) if err != nil { return err } diff --git a/vendor/github.com/99designs/gqlgen/codegen/config.go b/vendor/github.com/99designs/gqlgen/codegen/config.go index db0e467b..f9df24fb 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/config.go +++ b/vendor/github.com/99designs/gqlgen/codegen/config.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strings" "github.com/99designs/gqlgen/internal/gopath" @@ -19,7 +20,8 @@ var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"} // DefaultConfig creates a copy of the default config func DefaultConfig() *Config { return &Config{ - SchemaFilename: "schema.graphql", + SchemaFilename: SchemaFilenames{"schema.graphql"}, + SchemaStr: map[string]string{}, Model: PackageConfig{Filename: "models_gen.go"}, Exec: PackageConfig{Filename: "generated.go"}, } @@ -53,19 +55,36 @@ func LoadConfig(filename string) (*Config, error) { return nil, errors.Wrap(err, "unable to parse config") } + preGlobbing := config.SchemaFilename + config.SchemaFilename = SchemaFilenames{} + for _, f := range preGlobbing { + matches, err := filepath.Glob(f) + if err != nil { + return nil, errors.Wrapf(err, "failed to glob schema filename %s", f) + } + + for _, m := range matches { + if config.SchemaFilename.Has(m) { + continue + } + config.SchemaFilename = append(config.SchemaFilename, m) + } + } + config.FilePath = filename + config.SchemaStr = map[string]string{} return config, nil } type Config struct { - SchemaFilename string `yaml:"schema,omitempty"` - SchemaStr string `yaml:"-"` - Exec PackageConfig `yaml:"exec"` - Model PackageConfig `yaml:"model"` - Resolver PackageConfig `yaml:"resolver,omitempty"` - Models TypeMap `yaml:"models,omitempty"` - StructTag string `yaml:"struct_tag,omitempty"` + SchemaFilename SchemaFilenames `yaml:"schema,omitempty"` + SchemaStr map[string]string `yaml:"-"` + Exec PackageConfig `yaml:"exec"` + Model PackageConfig `yaml:"model"` + Resolver PackageConfig `yaml:"resolver,omitempty"` + Models TypeMap `yaml:"models,omitempty"` + StructTag string `yaml:"struct_tag,omitempty"` FilePath string `yaml:"-"` @@ -84,8 +103,37 @@ type TypeMapEntry struct { } type TypeMapField struct { - Resolver bool `yaml:"resolver"` - FieldName string `yaml:"fieldName"` + Resolver bool `yaml:"resolver"` + FieldName string `yaml:"fieldName"` +} + +type SchemaFilenames []string + +func (a *SchemaFilenames) UnmarshalYAML(unmarshal func(interface{}) error) error { + var single string + err := unmarshal(&single) + if err == nil { + *a = []string{single} + return nil + } + + var multi []string + err = unmarshal(&multi) + if err != nil { + return err + } + + *a = multi + return nil +} + +func (a SchemaFilenames) Has(file string) bool { + for _, existing := range a { + if existing == file { + return true + } + } + return false } func (c *PackageConfig) normalize() error { @@ -162,6 +210,36 @@ func (tm TypeMap) Check() error { return nil } +func (tm TypeMap) referencedPackages() []string { + var pkgs []string + + for _, typ := range tm { + if typ.Model == "map[string]interface{}" { + continue + } + pkg, _ := pkgAndType(typ.Model) + if pkg == "" || inStrSlice(pkgs, pkg) { + continue + } + pkgs = append(pkgs, pkg) + } + + sort.Slice(pkgs, func(i, j int) bool { + return pkgs[i] > pkgs[j] + }) + return pkgs +} + +func inStrSlice(haystack []string, needle string) bool { + for _, v := range haystack { + if needle == v { + return true + } + } + + return false +} + // findCfg searches for the config file in this directory and all parents up the tree // looking for the closest match func findCfg() (string, error) { diff --git a/vendor/github.com/99designs/gqlgen/codegen/directive_build.go b/vendor/github.com/99designs/gqlgen/codegen/directive_build.go index 32828841..af77dc44 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/directive_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/directive_build.go @@ -32,7 +32,6 @@ func (cfg *Config) buildDirectives(types NamedTypes) ([]*Directive, error) { if err != nil { return nil, errors.Errorf("default value for directive argument %s(%s) is not valid: %s", dir.Name, arg.Name, err.Error()) } - newArg.StripPtr() } args = append(args, newArg) } diff --git a/vendor/github.com/99designs/gqlgen/codegen/import.go b/vendor/github.com/99designs/gqlgen/codegen/import.go deleted file mode 100644 index b511e8f6..00000000 --- a/vendor/github.com/99designs/gqlgen/codegen/import.go +++ /dev/null @@ -1,29 +0,0 @@ -package codegen - -import ( - "strconv" -) - -type Import struct { - Name string - Path string - - alias string -} - -type Imports struct { - imports []*Import - destDir string -} - -func (i *Import) Write() string { - return i.Alias() + " " + strconv.Quote(i.Path) -} - -func (i *Import) Alias() string { - if i.alias == "" { - panic("alias called before imports are finalized") - } - - return i.alias -} diff --git a/vendor/github.com/99designs/gqlgen/codegen/import_build.go b/vendor/github.com/99designs/gqlgen/codegen/import_build.go deleted file mode 100644 index d634834e..00000000 --- a/vendor/github.com/99designs/gqlgen/codegen/import_build.go +++ /dev/null @@ -1,120 +0,0 @@ -package codegen - -import ( - "fmt" - "go/build" - "sort" - "strconv" - - // Import and ignore the ambient imports listed below so dependency managers - // don't prune unused code for us. Both lists should be kept in sync. - _ "github.com/99designs/gqlgen/graphql" - _ "github.com/99designs/gqlgen/graphql/introspection" - "github.com/99designs/gqlgen/internal/gopath" - _ "github.com/vektah/gqlparser" - _ "github.com/vektah/gqlparser/ast" -) - -// These imports are referenced by the generated code, and are assumed to have the -// default alias. So lets make sure they get added first, and any later collisions get -// renamed. -var ambientImports = []string{ - "context", - "fmt", - "io", - "strconv", - "time", - "sync", - "errors", - - "github.com/vektah/gqlparser", - "github.com/vektah/gqlparser/ast", - "github.com/99designs/gqlgen/graphql", - "github.com/99designs/gqlgen/graphql/introspection", -} - -func buildImports(types NamedTypes, destDir string) *Imports { - imports := Imports{ - destDir: destDir, - } - - for _, ambient := range ambientImports { - imports.add(ambient) - } - - // Imports from top level user types - for _, t := range types { - t.Import = imports.add(t.Package) - } - - return &imports -} - -func (s *Imports) add(path string) *Import { - if path == "" { - return nil - } - - // if we are referencing our own package we dont need an import - if gopath.MustDir2Import(s.destDir) == path { - return nil - } - - if existing := s.findByPath(path); existing != nil { - return existing - } - - pkg, err := build.Default.Import(path, s.destDir, 0) - if err != nil { - panic(err) - } - - imp := &Import{ - Name: pkg.Name, - Path: path, - } - s.imports = append(s.imports, imp) - - return imp -} - -func (s Imports) finalize() []*Import { - // ensure stable ordering by sorting - sort.Slice(s.imports, func(i, j int) bool { - return s.imports[i].Path > s.imports[j].Path - }) - - for _, imp := range s.imports { - alias := imp.Name - - i := 1 - for s.findByAlias(alias) != nil { - alias = imp.Name + strconv.Itoa(i) - i++ - if i > 10 { - panic(fmt.Errorf("too many collisions, last attempt was %s", alias)) - } - } - imp.alias = alias - } - - return s.imports -} - -func (s Imports) findByPath(importPath string) *Import { - for _, imp := range s.imports { - if imp.Path == importPath { - return imp - } - } - return nil -} - -func (s Imports) findByAlias(alias string) *Import { - for _, imp := range s.imports { - if imp.alias == alias { - return imp - } - } - return nil -} diff --git a/vendor/github.com/99designs/gqlgen/codegen/input_build.go b/vendor/github.com/99designs/gqlgen/codegen/input_build.go index 06ff37a0..70fa564d 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/input_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/input_build.go @@ -9,7 +9,7 @@ import ( "golang.org/x/tools/go/loader" ) -func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program, imports *Imports) (Objects, error) { +func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program) (Objects, error) { var inputs Objects for _, typ := range cfg.schema.Types { @@ -26,7 +26,7 @@ func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program, impo } if def != nil { input.Marshaler = buildInputMarshaler(typ, def) - bindErrs := bindObject(def.Type(), input, imports, cfg.StructTag) + bindErrs := bindObject(def.Type(), input, cfg.StructTag) if len(bindErrs) > 0 { return nil, bindErrs } diff --git a/vendor/github.com/99designs/gqlgen/codegen/interface_build.go b/vendor/github.com/99designs/gqlgen/codegen/interface_build.go index 9f4a4ff4..92052ba6 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/interface_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/interface_build.go @@ -1,9 +1,7 @@ package codegen import ( - "fmt" "go/types" - "os" "sort" "github.com/vektah/gqlparser/ast" @@ -51,20 +49,5 @@ func (cfg *Config) isValueReceiver(intf *NamedType, implementor *NamedType, prog return true } - for i := 0; i < interfaceType.NumMethods(); i++ { - intfMethod := interfaceType.Method(i) - - implMethod := findMethod(implementorType, intfMethod.Name()) - if implMethod == nil { - fmt.Fprintf(os.Stderr, "missing method %s on %s\n", intfMethod.Name(), implementor.GoType) - return false - } - - sig := implMethod.Type().(*types.Signature) - if _, isPtr := sig.Recv().Type().(*types.Pointer); isPtr { - return false - } - } - - return true + return types.Implements(implementorType, interfaceType) } diff --git a/vendor/github.com/99designs/gqlgen/codegen/model.go b/vendor/github.com/99designs/gqlgen/codegen/model.go index 5ba50337..bcdc8703 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/model.go +++ b/vendor/github.com/99designs/gqlgen/codegen/model.go @@ -4,6 +4,7 @@ type Model struct { *NamedType Description string Fields []ModelField + Implements []*NamedType } type ModelField struct { diff --git a/vendor/github.com/99designs/gqlgen/codegen/models_build.go b/vendor/github.com/99designs/gqlgen/codegen/models_build.go index 9f98a07d..56d2ff1f 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/models_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/models_build.go @@ -7,14 +7,14 @@ import ( "golang.org/x/tools/go/loader" ) -func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program, imports *Imports) ([]Model, error) { +func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program) ([]Model, error) { var models []Model for _, typ := range cfg.schema.Types { var model Model switch typ.Kind { case ast.Object: - obj, err := cfg.buildObject(types, typ, imports) + obj, err := cfg.buildObject(types, typ) if err != nil { return nil, err } @@ -54,8 +54,9 @@ func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program, imports * func (cfg *Config) obj2Model(obj *Object) Model { model := Model{ - NamedType: obj.NamedType, - Fields: []ModelField{}, + NamedType: obj.NamedType, + Implements: obj.Implements, + Fields: []ModelField{}, } model.GoType = ucFirst(obj.GQLType) diff --git a/vendor/github.com/99designs/gqlgen/codegen/object.go b/vendor/github.com/99designs/gqlgen/codegen/object.go index d9f610f4..656af297 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/object.go +++ b/vendor/github.com/99designs/gqlgen/codegen/object.go @@ -24,6 +24,7 @@ type Object struct { Fields []Field Satisfies []string + Implements []*NamedType ResolverInterface *Ref Root bool DisableConcurrency bool @@ -32,16 +33,17 @@ type Object struct { type Field struct { *Type - Description string // Description of a field - GQLName string // The name of the field in graphql - GoFieldType GoFieldType // The field type in go, if any - GoReceiverName string // The name of method & var receiver in go, if any - GoFieldName string // The name of the method or var in go, if any - Args []FieldArgument // A list of arguments to be passed to this field - ForceResolver bool // Should be emit Resolver method - NoErr bool // If this is bound to a go method, does that method have an error as the second argument - Object *Object // A link back to the parent object - Default interface{} // The default value + Description string // Description of a field + GQLName string // The name of the field in graphql + GoFieldType GoFieldType // The field type in go, if any + GoReceiverName string // The name of method & var receiver in go, if any + GoFieldName string // The name of the method or var in go, if any + Args []FieldArgument // A list of arguments to be passed to this field + ForceResolver bool // Should be emit Resolver method + MethodHasContext bool // If this is bound to a go method, does the method also take a context + NoErr bool // If this is bound to a go method, does that method have an error as the second argument + Object *Object // A link back to the parent object + Default interface{} // The default value } type FieldArgument struct { @@ -102,7 +104,10 @@ func (f *Field) IsVariable() bool { } func (f *Field) IsConcurrent() bool { - return f.IsResolver() && !f.Object.DisableConcurrency + if f.Object.DisableConcurrency { + return false + } + return f.MethodHasContext || f.IsResolver() } func (f *Field) GoNameExported() string { @@ -203,11 +208,15 @@ func (f *Field) CallArgs() string { var args []string if f.IsResolver() { - args = append(args, "ctx") + args = append(args, "rctx") if !f.Object.Root { args = append(args, "obj") } + } else { + if f.MethodHasContext { + args = append(args, "ctx") + } } for _, arg := range f.Args { diff --git a/vendor/github.com/99designs/gqlgen/codegen/object_build.go b/vendor/github.com/99designs/gqlgen/codegen/object_build.go index ee2b2f1c..279d1eb6 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/object_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/object_build.go @@ -9,7 +9,7 @@ import ( "golang.org/x/tools/go/loader" ) -func (cfg *Config) buildObjects(types NamedTypes, prog *loader.Program, imports *Imports) (Objects, error) { +func (cfg *Config) buildObjects(types NamedTypes, prog *loader.Program) (Objects, error) { var objects Objects for _, typ := range cfg.schema.Types { @@ -17,7 +17,7 @@ func (cfg *Config) buildObjects(types NamedTypes, prog *loader.Program, imports continue } - obj, err := cfg.buildObject(types, typ, imports) + obj, err := cfg.buildObject(types, typ) if err != nil { return nil, err } @@ -27,7 +27,7 @@ func (cfg *Config) buildObjects(types NamedTypes, prog *loader.Program, imports return nil, err } if def != nil { - for _, bindErr := range bindObject(def.Type(), obj, imports, cfg.StructTag) { + for _, bindErr := range bindObject(def.Type(), obj, cfg.StructTag) { log.Println(bindErr.Error()) log.Println(" Adding resolver method") } @@ -81,12 +81,11 @@ func sanitizeArgName(name string) string { return name } -func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *Imports) (*Object, error) { +func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition) (*Object, error) { obj := &Object{NamedType: types[typ.Name]} typeEntry, entryExists := cfg.Models[typ.Name] - imp := imports.findByPath(cfg.Exec.ImportPath()) - obj.ResolverInterface = &Ref{GoType: obj.GQLType + "Resolver", Import: imp} + obj.ResolverInterface = &Ref{GoType: obj.GQLType + "Resolver"} if typ == cfg.schema.Query { obj.Root = true @@ -104,12 +103,15 @@ func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *I obj.Satisfies = append(obj.Satisfies, typ.Interfaces...) + for _, intf := range cfg.schema.GetImplements(typ) { + obj.Implements = append(obj.Implements, types[intf.Name]) + } + for _, field := range typ.Fields { if typ == cfg.schema.Query && field.Name == "__type" { obj.Fields = append(obj.Fields, Field{ Type: &Type{types["__Schema"], []string{modPtr}, ast.NamedType("__Schema", nil), nil}, GQLName: "__schema", - NoErr: true, GoFieldType: GoFieldMethod, GoReceiverName: "ec", GoFieldName: "introspectSchema", @@ -122,7 +124,6 @@ func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *I obj.Fields = append(obj.Fields, Field{ Type: &Type{types["__Type"], []string{modPtr}, ast.NamedType("__Schema", nil), nil}, GQLName: "__type", - NoErr: true, GoFieldType: GoFieldMethod, GoReceiverName: "ec", GoFieldName: "introspectType", @@ -162,7 +163,6 @@ func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *I if err != nil { return nil, errors.Errorf("default value for %s.%s is not valid: %s", typ.Name, field.Name, err.Error()) } - newArg.StripPtr() } args = append(args, newArg) } diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/data.go b/vendor/github.com/99designs/gqlgen/codegen/templates/data.go index d168fa31..d3098aaa 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/data.go +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/data.go @@ -2,12 +2,12 @@ package templates var data = map[string]string{ "args.gotpl": "\targs := map[string]interface{}{}\n\t{{- range $i, $arg := . }}\n\t\tvar arg{{$i}} {{$arg.Signature }}\n\t\tif tmp, ok := rawArgs[{{$arg.GQLName|quote}}]; ok {\n\t\t\tvar err error\n\t\t\t{{$arg.Unmarshal (print \"arg\" $i) \"tmp\" }}\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\targs[{{$arg.GQLName|quote}}] = arg{{$i}}\n\t{{- end }}\n\treturn args, nil\n", - "field.gotpl": "{{ $field := . }}\n{{ $object := $field.Object }}\n\n{{- if $object.Stream }}\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler {\n\t\t{{- if $field.Args }}\n\t\t\trawArgs := field.ArgumentMap(ec.Variables)\n\t\t\targs, err := {{ $field.ArgsFunc }}(rawArgs)\n\t\t\tif err != nil {\n\t\t\t\tec.Error(ctx, err)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t{{- end }}\n\t\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\t\tField: field,\n\t\t})\n\t\tresults, err := ec.resolvers.{{ $field.ShortInvocation }}\n\t\tif err != nil {\n\t\t\tec.Error(ctx, err)\n\t\t\treturn nil\n\t\t}\n\t\treturn func() graphql.Marshaler {\n\t\t\tres, ok := <-results\n\t\t\tif !ok {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tvar out graphql.OrderedMap\n\t\t\tout.Add(field.Alias, func() graphql.Marshaler { {{ $field.WriteJson }} }())\n\t\t\treturn &out\n\t\t}\n\t}\n{{ else }}\n\t// nolint: vetshadow\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField, {{if not $object.Root}}obj *{{$object.FullName}}{{end}}) graphql.Marshaler {\n\t\t{{- if $field.Args }}\n\t\t\trawArgs := field.ArgumentMap(ec.Variables)\n\t\t\targs, err := {{ $field.ArgsFunc }}(rawArgs)\n\t\t\tif err != nil {\n\t\t\t\tec.Error(ctx, err)\n\t\t\t\treturn graphql.Null\n\t\t\t}\n\t\t{{- end }}\n\t\trctx := &graphql.ResolverContext{\n\t\t\tObject: {{$object.GQLType|quote}},\n\t\t\tArgs: {{if $field.Args }}args{{else}}nil{{end}},\n\t\t\tField: field,\n\t\t}\n\t\tctx = graphql.WithResolverContext(ctx, rctx)\n\t\tresTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(ctx context.Context) (interface{}, error) {\n\t\t\t{{- if $field.IsResolver }}\n\t\t\t\treturn ec.resolvers.{{ $field.ShortInvocation }}\n\t\t\t{{- else if $field.IsMethod }}\n\t\t\t\t{{- if $field.NoErr }}\n\t\t\t\t\treturn {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }}), nil\n\t\t\t\t{{- else }}\n\t\t\t\t\treturn {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }})\n\t\t\t\t{{- end }}\n\t\t\t{{- else if $field.IsVariable }}\n\t\t\t\treturn {{$field.GoReceiverName}}.{{$field.GoFieldName}}, nil\n\t\t\t{{- end }}\n\t\t})\n\t\tif resTmp == nil {\n\t\t\t{{- if $field.ASTType.NonNull }}\n\t\t\t\tif !ec.HasError(rctx) {\n\t\t\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t\t\t}\n\t\t\t{{- end }}\n\t\t\treturn graphql.Null\n\t\t}\n\t\tres := resTmp.({{$field.Signature}})\n\t\trctx.Result = res\n\t\t{{ $field.WriteJson }}\n\t}\n{{ end }}\n", - "generated.gotpl": "// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\n// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.\nfunc NewExecutableSchema(cfg Config) graphql.ExecutableSchema {\n\treturn &executableSchema{\n\t\tresolvers: cfg.Resolvers,\n\t\tdirectives: cfg.Directives,\n\t\tcomplexity: cfg.Complexity,\n\t}\n}\n\ntype Config struct {\n\tResolvers ResolverRoot\n\tDirectives DirectiveRoot\n\tComplexity ComplexityRoot\n}\n\ntype ResolverRoot interface {\n{{- range $object := .Objects -}}\n\t{{ if $object.HasResolvers -}}\n\t\t{{$object.GQLType}}() {{$object.GQLType}}Resolver\n\t{{ end }}\n{{- end }}\n}\n\ntype DirectiveRoot struct {\n{{ range $directive := .Directives }}\n\t{{ $directive.Declaration }}\n{{ end }}\n}\n\ntype ComplexityRoot struct {\n{{ range $object := .Objects }}\n\t{{ if not $object.IsReserved -}}\n\t\t{{ $object.GQLType|toCamel }} struct {\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{ if not $field.IsReserved -}}\n\t\t\t\t{{ $field.GQLName|toCamel }} {{ $field.ComplexitySignature }}\n\t\t\t{{ end }}\n\t\t{{- end }}\n\t\t}\n\t{{- end }}\n{{ end }}\n}\n\n{{ range $object := .Objects -}}\n\t{{ if $object.HasResolvers }}\n\t\ttype {{$object.GQLType}}Resolver interface {\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{ $field.ShortResolverDeclaration }}\n\t\t{{ end }}\n\t\t}\n\t{{- end }}\n{{- end }}\n\n{{ range $object := .Objects -}}\n\t{{ range $field := $object.Fields -}}\n\t\t{{ if $field.Args }}\n\t\t\tfunc {{ $field.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) {\n\t\t\t{{ template \"args.gotpl\" $field.Args }}\n\t\t\t}\n\t\t{{ end }}\n\t{{ end }}\n{{- end }}\n\n{{ range $directive := .Directives }}\n\t{{ if $directive.Args }}\n\t\tfunc {{ $directive.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) {\n\t\t{{ template \"args.gotpl\" $directive.Args }}\n\t\t}\n\t{{ end }}\n{{ end }}\n\ntype executableSchema struct {\n\tresolvers ResolverRoot\n\tdirectives DirectiveRoot\n\tcomplexity ComplexityRoot\n}\n\nfunc (e *executableSchema) Schema() *ast.Schema {\n\treturn parsedSchema\n}\n\nfunc (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) {\n\tswitch typeName + \".\" + field {\n\t{{ range $object := .Objects }}\n\t\t{{ if not $object.IsReserved }}\n\t\t\t{{ range $field := $object.Fields }}\n\t\t\t\t{{ if not $field.IsReserved }}\n\t\t\t\t\tcase \"{{$object.GQLType}}.{{$field.GQLName}}\":\n\t\t\t\t\t\tif e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}} == nil {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t{{ if $field.Args }}\n\t\t\t\t\t\t\targs, err := {{ $field.ArgsFunc }}(rawArgs)\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\treturn 0, false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t{{ end }}\n\t\t\t\t\t\treturn e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}}(childComplexity{{if $field.Args}}, {{$field.ComplexityArgs}} {{end}}), true\n\t\t\t\t{{ end }}\n\t\t\t{{ end }}\n\t\t{{ end }}\n\t{{ end }}\n\t}\n\treturn 0, false\n}\n\nfunc (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinition) *graphql.Response {\n\t{{- if .QueryRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.QueryRoot.GQLType}}(ctx, op.SelectionSet)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\t\t\treturn buf.Bytes()\n\t\t})\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf,\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn graphql.ErrorResponse(ctx, \"queries are not supported\")\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefinition) *graphql.Response {\n\t{{- if .MutationRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.MutationRoot.GQLType}}(ctx, op.SelectionSet)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\t\t\treturn buf.Bytes()\n\t\t})\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf,\n\t\t\tErrors: ec.Errors,\n\t\t}\n\t{{- else }}\n\t\treturn graphql.ErrorResponse(ctx, \"mutations are not supported\")\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response {\n\t{{- if .SubscriptionRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e}\n\n\t\tnext := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.SelectionSet)\n\t\tif ec.Errors != nil {\n\t\t\treturn graphql.OneShot(&graphql.Response{Data: []byte(\"null\"), Errors: ec.Errors})\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\treturn func() *graphql.Response {\n\t\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\t\tbuf.Reset()\n\t\t\t\tdata := next()\n\n\t\t\t\tif data == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tdata.MarshalGQL(&buf)\n\t\t\t\treturn buf.Bytes()\n\t\t\t})\n\n\t\t\tif buf == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\treturn &graphql.Response{\n\t\t\t\tData: buf,\n\t\t\t\tErrors: ec.Errors,\n\t\t\t}\n\t\t}\n\t{{- else }}\n\t\treturn graphql.OneShot(graphql.ErrorResponse(ctx, \"subscriptions are not supported\"))\n\t{{- end }}\n}\n\ntype executionContext struct {\n\t*graphql.RequestContext\n\t*executableSchema\n}\n\n{{- range $object := .Objects }}\n\t{{ template \"object.gotpl\" $object }}\n\n\t{{- range $field := $object.Fields }}\n\t\t{{ template \"field.gotpl\" $field }}\n\t{{ end }}\n{{- end}}\n\n{{- range $interface := .Interfaces }}\n\t{{ template \"interface.gotpl\" $interface }}\n{{- end }}\n\n{{- range $input := .Inputs }}\n\t{{ template \"input.gotpl\" $input }}\n{{- end }}\n\nfunc (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) (ret interface{}) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = nil\n\t\t}\n\t}()\n\t{{- if .Directives }}\n\trctx := graphql.GetResolverContext(ctx)\n\tfor _, d := range rctx.Field.Definition.Directives {\n\t\tswitch d.Name {\n\t\t{{- range $directive := .Directives }}\n\t\tcase \"{{$directive.Name}}\":\n\t\t\tif ec.directives.{{$directive.Name|ucFirst}} != nil {\n\t\t\t\t{{- if $directive.Args }}\n\t\t\t\t\trawArgs := d.ArgumentMap(ec.Variables)\n\t\t\t\t\targs, err := {{ $directive.ArgsFunc }}(rawArgs)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tec.Error(ctx, err)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t{{- end }}\n\t\t\t\tn := next\n\t\t\t\tnext = func(ctx context.Context) (interface{}, error) {\n\t\t\t\t\treturn ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}})\n\t\t\t\t}\n\t\t\t}\n\t\t{{- end }}\n\t\t}\n\t}\n\t{{- end }}\n\tres, err := ec.ResolverMiddleware(ctx, next)\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn nil\n\t}\n\treturn res\n}\n\nfunc (ec *executionContext) introspectSchema() *introspection.Schema {\n\treturn introspection.WrapSchema(parsedSchema)\n}\n\nfunc (ec *executionContext) introspectType(name string) *introspection.Type {\n\treturn introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name])\n}\n\nvar parsedSchema = gqlparser.MustLoadSchema(\n\t&ast.Source{Name: {{.SchemaFilename|quote}}, Input: {{.SchemaRaw|rawQuote}}},\n)\n", + "field.gotpl": "{{ $field := . }}\n{{ $object := $field.Object }}\n\n{{- if $object.Stream }}\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler {\n\t\t{{- if $field.Args }}\n\t\t\trawArgs := field.ArgumentMap(ec.Variables)\n\t\t\targs, err := {{ $field.ArgsFunc }}(rawArgs)\n\t\t\tif err != nil {\n\t\t\t\tec.Error(ctx, err)\n\t\t\t\treturn nil\n\t\t\t}\n\t\t{{- end }}\n\t\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\t\tField: field,\n\t\t})\n\t\t// FIXME: subscriptions are missing request middleware stack https://github.com/99designs/gqlgen/issues/259\n\t\t// and Tracer stack\n\t\trctx := ctx\n\t\tresults, err := ec.resolvers.{{ $field.ShortInvocation }}\n\t\tif err != nil {\n\t\t\tec.Error(ctx, err)\n\t\t\treturn nil\n\t\t}\n\t\treturn func() graphql.Marshaler {\n\t\t\tres, ok := <-results\n\t\t\tif !ok {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tvar out graphql.OrderedMap\n\t\t\tout.Add(field.Alias, func() graphql.Marshaler { {{ $field.WriteJson }} }())\n\t\t\treturn &out\n\t\t}\n\t}\n{{ else }}\n\t// nolint: vetshadow\n\tfunc (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField, {{if not $object.Root}}obj *{{$object.FullName}}{{end}}) graphql.Marshaler {\n\t\tctx = ec.Tracer.StartFieldExecution(ctx, field)\n\t\tdefer func () { ec.Tracer.EndFieldExecution(ctx) }()\n\t\t{{- if $field.Args }}\n\t\t\trawArgs := field.ArgumentMap(ec.Variables)\n\t\t\targs, err := {{ $field.ArgsFunc }}(rawArgs)\n\t\t\tif err != nil {\n\t\t\t\tec.Error(ctx, err)\n\t\t\t\treturn graphql.Null\n\t\t\t}\n\t\t{{- end }}\n\t\trctx := &graphql.ResolverContext{\n\t\t\tObject: {{$object.GQLType|quote}},\n\t\t\tArgs: {{if $field.Args }}args{{else}}nil{{end}},\n\t\t\tField: field,\n\t\t}\n\t\tctx = graphql.WithResolverContext(ctx, rctx)\n\t\tctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx)\n\t\tresTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) {\n\t\t\tctx = rctx // use context from middleware stack in children\n\t\t\t{{- if $field.IsResolver }}\n\t\t\t\treturn ec.resolvers.{{ $field.ShortInvocation }}\n\t\t\t{{- else if $field.IsMethod }}\n\t\t\t\t{{- if $field.NoErr }}\n\t\t\t\t\treturn {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }}), nil\n\t\t\t\t{{- else }}\n\t\t\t\t\treturn {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }})\n\t\t\t\t{{- end }}\n\t\t\t{{- else if $field.IsVariable }}\n\t\t\t\treturn {{$field.GoReceiverName}}.{{$field.GoFieldName}}, nil\n\t\t\t{{- end }}\n\t\t})\n\t\tif resTmp == nil {\n\t\t\t{{- if $field.ASTType.NonNull }}\n\t\t\t\tif !ec.HasError(rctx) {\n\t\t\t\t\tec.Errorf(ctx, \"must not be null\")\n\t\t\t\t}\n\t\t\t{{- end }}\n\t\t\treturn graphql.Null\n\t\t}\n\t\tres := resTmp.({{$field.Signature}})\n\t\trctx.Result = res\n\t\tctx = ec.Tracer.StartFieldChildExecution(ctx)\n\t\t{{ $field.WriteJson }}\n\t}\n{{ end }}\n", + "generated.gotpl": "// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\npackage {{ .PackageName }}\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"fmt\" }}\n\t{{ reserveImport \"io\" }}\n\t{{ reserveImport \"strconv\" }}\n\t{{ reserveImport \"time\" }}\n\t{{ reserveImport \"sync\" }}\n\t{{ reserveImport \"errors\" }}\n\t{{ reserveImport \"bytes\" }}\n\n\t{{ reserveImport \"github.com/vektah/gqlparser\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser/ast\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql/introspection\" }}\n)\n\n// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.\nfunc NewExecutableSchema(cfg Config) graphql.ExecutableSchema {\n\treturn &executableSchema{\n\t\tresolvers: cfg.Resolvers,\n\t\tdirectives: cfg.Directives,\n\t\tcomplexity: cfg.Complexity,\n\t}\n}\n\ntype Config struct {\n\tResolvers ResolverRoot\n\tDirectives DirectiveRoot\n\tComplexity ComplexityRoot\n}\n\ntype ResolverRoot interface {\n{{- range $object := .Objects -}}\n\t{{ if $object.HasResolvers -}}\n\t\t{{$object.GQLType}}() {{$object.GQLType}}Resolver\n\t{{ end }}\n{{- end }}\n}\n\ntype DirectiveRoot struct {\n{{ range $directive := .Directives }}\n\t{{ $directive.Declaration }}\n{{ end }}\n}\n\ntype ComplexityRoot struct {\n{{ range $object := .Objects }}\n\t{{ if not $object.IsReserved -}}\n\t\t{{ $object.GQLType|toCamel }} struct {\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{ if not $field.IsReserved -}}\n\t\t\t\t{{ $field.GQLName|toCamel }} {{ $field.ComplexitySignature }}\n\t\t\t{{ end }}\n\t\t{{- end }}\n\t\t}\n\t{{- end }}\n{{ end }}\n}\n\n{{ range $object := .Objects -}}\n\t{{ if $object.HasResolvers }}\n\t\ttype {{$object.GQLType}}Resolver interface {\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{ $field.ShortResolverDeclaration }}\n\t\t{{ end }}\n\t\t}\n\t{{- end }}\n{{- end }}\n\n{{ range $object := .Objects -}}\n\t{{ range $field := $object.Fields -}}\n\t\t{{ if $field.Args }}\n\t\t\tfunc {{ $field.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) {\n\t\t\t{{ template \"args.gotpl\" $field.Args }}\n\t\t\t}\n\t\t{{ end }}\n\t{{ end }}\n{{- end }}\n\n{{ range $directive := .Directives }}\n\t{{ if $directive.Args }}\n\t\tfunc {{ $directive.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) {\n\t\t{{ template \"args.gotpl\" $directive.Args }}\n\t\t}\n\t{{ end }}\n{{ end }}\n\ntype executableSchema struct {\n\tresolvers ResolverRoot\n\tdirectives DirectiveRoot\n\tcomplexity ComplexityRoot\n}\n\nfunc (e *executableSchema) Schema() *ast.Schema {\n\treturn parsedSchema\n}\n\nfunc (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) {\n\tswitch typeName + \".\" + field {\n\t{{ range $object := .Objects }}\n\t\t{{ if not $object.IsReserved }}\n\t\t\t{{ range $field := $object.Fields }}\n\t\t\t\t{{ if not $field.IsReserved }}\n\t\t\t\t\tcase \"{{$object.GQLType}}.{{$field.GQLName}}\":\n\t\t\t\t\t\tif e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}} == nil {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\t{{ if $field.Args }}\n\t\t\t\t\t\t\targs, err := {{ $field.ArgsFunc }}(rawArgs)\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\treturn 0, false\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t{{ end }}\n\t\t\t\t\t\treturn e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}}(childComplexity{{if $field.Args}}, {{$field.ComplexityArgs}} {{end}}), true\n\t\t\t\t{{ end }}\n\t\t\t{{ end }}\n\t\t{{ end }}\n\t{{ end }}\n\t}\n\treturn 0, false\n}\n\nfunc (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinition) *graphql.Response {\n\t{{- if .QueryRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.QueryRoot.GQLType}}(ctx, op.SelectionSet)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\t\t\treturn buf.Bytes()\n\t\t})\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf,\n\t\t\tErrors: ec.Errors,\n\t\t\tExtensions: ec.Extensions,\t\t}\n\t{{- else }}\n\t\treturn graphql.ErrorResponse(ctx, \"queries are not supported\")\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefinition) *graphql.Response {\n\t{{- if .MutationRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.MutationRoot.GQLType}}(ctx, op.SelectionSet)\n\t\t\tvar buf bytes.Buffer\n\t\t\tdata.MarshalGQL(&buf)\n\t\t\treturn buf.Bytes()\n\t\t})\n\n\t\treturn &graphql.Response{\n\t\t\tData: buf,\n\t\t\tErrors: ec.Errors,\n\t\t\tExtensions: ec.Extensions,\n\t\t}\n\t{{- else }}\n\t\treturn graphql.ErrorResponse(ctx, \"mutations are not supported\")\n\t{{- end }}\n}\n\nfunc (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response {\n\t{{- if .SubscriptionRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e}\n\n\t\tnext := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.SelectionSet)\n\t\tif ec.Errors != nil {\n\t\t\treturn graphql.OneShot(&graphql.Response{Data: []byte(\"null\"), Errors: ec.Errors})\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\treturn func() *graphql.Response {\n\t\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\t\tbuf.Reset()\n\t\t\t\tdata := next()\n\n\t\t\t\tif data == nil {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\tdata.MarshalGQL(&buf)\n\t\t\t\treturn buf.Bytes()\n\t\t\t})\n\n\t\t\tif buf == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\treturn &graphql.Response{\n\t\t\t\tData: buf,\n\t\t\t\tErrors: ec.Errors,\n\t\t\t\tExtensions: ec.Extensions,\n\t\t\t}\n\t\t}\n\t{{- else }}\n\t\treturn graphql.OneShot(graphql.ErrorResponse(ctx, \"subscriptions are not supported\"))\n\t{{- end }}\n}\n\ntype executionContext struct {\n\t*graphql.RequestContext\n\t*executableSchema\n}\n\n{{- range $object := .Objects }}\n\t{{ template \"object.gotpl\" $object }}\n\n\t{{- range $field := $object.Fields }}\n\t\t{{ template \"field.gotpl\" $field }}\n\t{{ end }}\n{{- end}}\n\n{{- range $interface := .Interfaces }}\n\t{{ template \"interface.gotpl\" $interface }}\n{{- end }}\n\n{{- range $input := .Inputs }}\n\t{{ template \"input.gotpl\" $input }}\n{{- end }}\n\nfunc (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) (ret interface{}) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tec.Error(ctx, ec.Recover(ctx, r))\n\t\t\tret = nil\n\t\t}\n\t}()\n\t{{- if .Directives }}\n\trctx := graphql.GetResolverContext(ctx)\n\tfor _, d := range rctx.Field.Definition.Directives {\n\t\tswitch d.Name {\n\t\t{{- range $directive := .Directives }}\n\t\tcase \"{{$directive.Name}}\":\n\t\t\tif ec.directives.{{$directive.Name|ucFirst}} != nil {\n\t\t\t\t{{- if $directive.Args }}\n\t\t\t\t\trawArgs := d.ArgumentMap(ec.Variables)\n\t\t\t\t\targs, err := {{ $directive.ArgsFunc }}(rawArgs)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tec.Error(ctx, err)\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t{{- end }}\n\t\t\t\tn := next\n\t\t\t\tnext = func(ctx context.Context) (interface{}, error) {\n\t\t\t\t\treturn ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}})\n\t\t\t\t}\n\t\t\t}\n\t\t{{- end }}\n\t\t}\n\t}\n\t{{- end }}\n\tres, err := ec.ResolverMiddleware(ctx, next)\n\tif err != nil {\n\t\tec.Error(ctx, err)\n\t\treturn nil\n\t}\n\treturn res\n}\n\nfunc (ec *executionContext) introspectSchema() (*introspection.Schema, error) {\n\tif ec.DisableIntrospection {\n\t\treturn nil, errors.New(\"introspection disabled\")\n\t}\n\treturn introspection.WrapSchema(parsedSchema), nil\n}\n\nfunc (ec *executionContext) introspectType(name string) (*introspection.Type, error) {\n\tif ec.DisableIntrospection {\n\t\treturn nil, errors.New(\"introspection disabled\")\n\t}\n\treturn introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil\n}\n\nvar parsedSchema = gqlparser.MustLoadSchema(\n\t{{- range $filename, $schema := .SchemaRaw }}\n\t\t&ast.Source{Name: {{$filename|quote}}, Input: {{$schema|rawQuote}}},\n\t{{- end }}\n)\n", "input.gotpl": "\t{{- if .IsMarshaled }}\n\tfunc Unmarshal{{ .GQLType }}(v interface{}) ({{.FullName}}, error) {\n\t\tvar it {{.FullName}}\n\t\tvar asMap = v.(map[string]interface{})\n\t\t{{ range $field := .Fields}}\n\t\t\t{{- if $field.Default}}\n\t\t\t\tif _, present := asMap[{{$field.GQLName|quote}}] ; !present {\n\t\t\t\t\tasMap[{{$field.GQLName|quote}}] = {{ $field.Default | dump }}\n\t\t\t\t}\n\t\t\t{{- end}}\n\t\t{{- end }}\n\n\t\tfor k, v := range asMap {\n\t\t\tswitch k {\n\t\t\t{{- range $field := .Fields }}\n\t\t\tcase {{$field.GQLName|quote}}:\n\t\t\t\tvar err error\n\t\t\t\t{{ $field.Unmarshal (print \"it.\" $field.GoFieldName) \"v\" }}\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn it, err\n\t\t\t\t}\n\t\t\t{{- end }}\n\t\t\t}\n\t\t}\n\n\t\treturn it, nil\n\t}\n\t{{- end }}\n", "interface.gotpl": "{{- $interface := . }}\n\nfunc (ec *executionContext) _{{$interface.GQLType}}(ctx context.Context, sel ast.SelectionSet, obj *{{$interface.FullName}}) graphql.Marshaler {\n\tswitch obj := (*obj).(type) {\n\tcase nil:\n\t\treturn graphql.Null\n\t{{- range $implementor := $interface.Implementors }}\n\t\t{{- if $implementor.ValueReceiver }}\n\t\t\tcase {{$implementor.FullName}}:\n\t\t\t\treturn ec._{{$implementor.GQLType}}(ctx, sel, &obj)\n\t\t{{- end}}\n\t\tcase *{{$implementor.FullName}}:\n\t\t\treturn ec._{{$implementor.GQLType}}(ctx, sel, obj)\n\t{{- end }}\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unexpected type %T\", obj))\n\t}\n}\n", - "models.gotpl": "// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\n{{ range $model := .Models }}\n\t{{with .Description}} {{.|prefixLines \"// \"}} {{end}}\n\t{{- if .IsInterface }}\n\t\ttype {{.GoType}} interface {}\n\t{{- else }}\n\t\ttype {{.GoType}} struct {\n\t\t\t{{- range $field := .Fields }}\n\t\t\t\t{{- with .Description}}\n\t\t\t\t\t{{.|prefixLines \"// \"}}\n\t\t\t\t{{- end}}\n\t\t\t\t{{- if $field.GoFieldName }}\n\t\t\t\t\t{{ $field.GoFieldName }} {{$field.Signature}} `json:\"{{$field.GQLName}}\"`\n\t\t\t\t{{- else }}\n\t\t\t\t\t{{ $field.GoFKName }} {{$field.GoFKType}}\n\t\t\t\t{{- end }}\n\t\t\t{{- end }}\n\t\t}\n\t{{- end }}\n{{- end}}\n\n{{ range $enum := .Enums }}\n\t{{with .Description}}{{.|prefixLines \"// \"}} {{end}}\n\ttype {{.GoType}} string\n\tconst (\n\t{{- range $value := .Values}}\n\t\t{{- with .Description}}\n\t\t\t{{.|prefixLines \"// \"}}\n\t\t{{- end}}\n\t\t{{$enum.GoType}}{{ .Name|toCamel }} {{$enum.GoType}} = {{.Name|quote}}\n\t{{- end }}\n\t)\n\n\tfunc (e {{.GoType}}) IsValid() bool {\n\t\tswitch e {\n\t\tcase {{ range $index, $element := .Values}}{{if $index}},{{end}}{{ $enum.GoType }}{{ $element.Name|toCamel }}{{end}}:\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\tfunc (e {{.GoType}}) String() string {\n\t\treturn string(e)\n\t}\n\n\tfunc (e *{{.GoType}}) UnmarshalGQL(v interface{}) error {\n\t\tstr, ok := v.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"enums must be strings\")\n\t\t}\n\n\t\t*e = {{.GoType}}(str)\n\t\tif !e.IsValid() {\n\t\t\treturn fmt.Errorf(\"%s is not a valid {{.GQLType}}\", str)\n\t\t}\n\t\treturn nil\n\t}\n\n\tfunc (e {{.GoType}}) MarshalGQL(w io.Writer) {\n\t\tfmt.Fprint(w, strconv.Quote(e.String()))\n\t}\n\n{{- end }}\n", + "models.gotpl": "// Code generated by github.com/99designs/gqlgen, DO NOT EDIT.\n\npackage {{ .PackageName }}\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"fmt\" }}\n\t{{ reserveImport \"io\" }}\n\t{{ reserveImport \"strconv\" }}\n\t{{ reserveImport \"time\" }}\n\t{{ reserveImport \"sync\" }}\n\t{{ reserveImport \"errors\" }}\n\t{{ reserveImport \"bytes\" }}\n\n\t{{ reserveImport \"github.com/vektah/gqlparser\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser/ast\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql/introspection\" }}\n)\n\n{{ range $model := .Models }}\n\t{{with .Description}} {{.|prefixLines \"// \"}} {{end}}\n\t{{- if .IsInterface }}\n\t\ttype {{.GoType}} interface {\n\t\t\tIs{{.GoType}}()\n\t\t}\n\t{{- else }}\n\t\ttype {{.GoType}} struct {\n\t\t\t{{- range $field := .Fields }}\n\t\t\t\t{{- with .Description}}\n\t\t\t\t\t{{.|prefixLines \"// \"}}\n\t\t\t\t{{- end}}\n\t\t\t\t{{- if $field.GoFieldName }}\n\t\t\t\t\t{{ $field.GoFieldName }} {{$field.Signature}} `json:\"{{$field.GQLName}}\"`\n\t\t\t\t{{- else }}\n\t\t\t\t\t{{ $field.GoFKName }} {{$field.GoFKType}}\n\t\t\t\t{{- end }}\n\t\t\t{{- end }}\n\t\t}\n\n\t\t{{- range $iface := .Implements }}\n\t\t\tfunc ({{$model.GoType}}) Is{{$iface.GoType}}() {}\n\t\t{{- end }}\n\n\t{{- end }}\n{{- end}}\n\n{{ range $enum := .Enums }}\n\t{{with .Description}}{{.|prefixLines \"// \"}} {{end}}\n\ttype {{.GoType}} string\n\tconst (\n\t{{- range $value := .Values}}\n\t\t{{- with .Description}}\n\t\t\t{{.|prefixLines \"// \"}}\n\t\t{{- end}}\n\t\t{{$enum.GoType}}{{ .Name|toCamel }} {{$enum.GoType}} = {{.Name|quote}}\n\t{{- end }}\n\t)\n\n\tfunc (e {{.GoType}}) IsValid() bool {\n\t\tswitch e {\n\t\tcase {{ range $index, $element := .Values}}{{if $index}},{{end}}{{ $enum.GoType }}{{ $element.Name|toCamel }}{{end}}:\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\n\tfunc (e {{.GoType}}) String() string {\n\t\treturn string(e)\n\t}\n\n\tfunc (e *{{.GoType}}) UnmarshalGQL(v interface{}) error {\n\t\tstr, ok := v.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"enums must be strings\")\n\t\t}\n\n\t\t*e = {{.GoType}}(str)\n\t\tif !e.IsValid() {\n\t\t\treturn fmt.Errorf(\"%s is not a valid {{.GQLType}}\", str)\n\t\t}\n\t\treturn nil\n\t}\n\n\tfunc (e {{.GoType}}) MarshalGQL(w io.Writer) {\n\t\tfmt.Fprint(w, strconv.Quote(e.String()))\n\t}\n\n{{- end }}\n", "object.gotpl": "{{ $object := . }}\n\nvar {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}}\n\n// nolint: gocyclo, errcheck, gas, goconst\n{{- if .Stream }}\nfunc (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet) func() graphql.Marshaler {\n\tfields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors)\n\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\tObject: {{$object.GQLType|quote}},\n\t})\n\tif len(fields) != 1 {\n\t\tec.Errorf(ctx, \"must subscribe to exactly one stream\")\n\t\treturn nil\n\t}\n\n\tswitch fields[0].Name {\n\t{{- range $field := $object.Fields }}\n\tcase \"{{$field.GQLName}}\":\n\t\treturn ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, fields[0])\n\t{{- end }}\n\tdefault:\n\t\tpanic(\"unknown field \" + strconv.Quote(fields[0].Name))\n\t}\n}\n{{- else }}\nfunc (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet{{if not $object.Root}}, obj *{{$object.FullName}} {{end}}) graphql.Marshaler {\n\tfields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors)\n\t{{if $object.Root}}\n\t\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\t\tObject: {{$object.GQLType|quote}},\n\t\t})\n\t{{end}}\n\n\t{{if $object.IsConcurrent}} var wg sync.WaitGroup {{end}}\n\tout := graphql.NewOrderedMap(len(fields))\n\tinvalid := false\n\tfor i, field := range fields {\n\t\tout.Keys[i] = field.Alias\n\n\t\tswitch field.Name {\n\t\tcase \"__typename\":\n\t\t\tout.Values[i] = graphql.MarshalString({{$object.GQLType|quote}})\n\t\t{{- range $field := $object.Fields }}\n\t\tcase \"{{$field.GQLName}}\":\n\t\t\t{{- if $field.IsConcurrent }}\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func(i int, field graphql.CollectedField) {\n\t\t\t{{- end }}\n\t\t\t\tout.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, field{{if not $object.Root}}, obj{{end}})\n\t\t\t\t{{- if $field.ASTType.NonNull }}\n\t\t\t\t\tif out.Values[i] == graphql.Null {\n\t\t\t\t\t\tinvalid = true\n\t\t\t\t\t}\n\t\t\t\t{{- end }}\n\t\t\t{{- if $field.IsConcurrent }}\n\t\t\t\t\twg.Done()\n\t\t\t\t}(i, field)\n\t\t\t{{- end }}\n\t\t{{- end }}\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\t{{if $object.IsConcurrent}} wg.Wait() {{end}}\n\tif invalid { return graphql.Null }\n\treturn out\n}\n{{- end }}\n", - "resolver.gotpl": "//go:generate gorunpkg github.com/99designs/gqlgen\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\ntype {{.ResolverType}} struct {}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\tfunc (r *{{$.ResolverType}}) {{$object.GQLType}}() {{ $object.ResolverInterface.FullName }} {\n\t\t\treturn &{{lcFirst $object.GQLType}}Resolver{r}\n\t\t}\n\t{{ end -}}\n{{ end }}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\ttype {{lcFirst $object.GQLType}}Resolver struct { *Resolver }\n\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{- if $field.IsResolver -}}\n\t\t\tfunc (r *{{lcFirst $object.GQLType}}Resolver) {{ $field.ShortResolverDeclaration }} {\n\t\t\t\tpanic(\"not implemented\")\n\t\t\t}\n\t\t\t{{ end -}}\n\t\t{{ end -}}\n\t{{ end -}}\n{{ end }}\n", - "server.gotpl": "package main\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\nconst defaultPort = \"8080\"\n\nfunc main() {\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = defaultPort\n\t}\n\n\thttp.Handle(\"/\", handler.Playground(\"GraphQL playground\", \"/query\"))\n\thttp.Handle(\"/query\", handler.GraphQL({{.ExecPackageName}}.NewExecutableSchema({{.ExecPackageName}}.Config{Resolvers: &{{.ResolverPackageName}}.Resolver{}})))\n\n\tlog.Printf(\"connect to http://localhost:%s/ for GraphQL playground\", port)\n\tlog.Fatal(http.ListenAndServe(\":\" + port, nil))\n}\n", + "resolver.gotpl": "package {{ .PackageName }}\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"fmt\" }}\n\t{{ reserveImport \"io\" }}\n\t{{ reserveImport \"strconv\" }}\n\t{{ reserveImport \"time\" }}\n\t{{ reserveImport \"sync\" }}\n\t{{ reserveImport \"errors\" }}\n\t{{ reserveImport \"bytes\" }}\n\n\t{{ reserveImport \"github.com/99designs/gqlgen/handler\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser\" }}\n\t{{ reserveImport \"github.com/vektah/gqlparser/ast\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/graphql/introspection\" }}\n)\n\ntype {{.ResolverType}} struct {}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\tfunc (r *{{$.ResolverType}}) {{$object.GQLType}}() {{ $object.ResolverInterface.FullName }} {\n\t\t\treturn &{{lcFirst $object.GQLType}}Resolver{r}\n\t\t}\n\t{{ end -}}\n{{ end }}\n\n{{ range $object := .Objects -}}\n\t{{- if $object.HasResolvers -}}\n\t\ttype {{lcFirst $object.GQLType}}Resolver struct { *Resolver }\n\n\t\t{{ range $field := $object.Fields -}}\n\t\t\t{{- if $field.IsResolver -}}\n\t\t\tfunc (r *{{lcFirst $object.GQLType}}Resolver) {{ $field.ShortResolverDeclaration }} {\n\t\t\t\tpanic(\"not implemented\")\n\t\t\t}\n\t\t\t{{ end -}}\n\t\t{{ end -}}\n\t{{ end -}}\n{{ end }}\n", + "server.gotpl": "package main\n\nimport (\n\t%%%IMPORTS%%%\n\n\t{{ reserveImport \"context\" }}\n\t{{ reserveImport \"log\" }}\n\t{{ reserveImport \"net/http\" }}\n\t{{ reserveImport \"os\" }}\n\t{{ reserveImport \"github.com/99designs/gqlgen/handler\" }}\n)\n\nconst defaultPort = \"8080\"\n\nfunc main() {\n\tport := os.Getenv(\"PORT\")\n\tif port == \"\" {\n\t\tport = defaultPort\n\t}\n\n\thttp.Handle(\"/\", handler.Playground(\"GraphQL playground\", \"/query\"))\n\thttp.Handle(\"/query\", handler.GraphQL({{ lookupImport .ExecPackageName }}.NewExecutableSchema({{ lookupImport .ExecPackageName}}.Config{Resolvers: &{{ lookupImport .ResolverPackageName}}.Resolver{}})))\n\n\tlog.Printf(\"connect to http://localhost:%s/ for GraphQL playground\", port)\n\tlog.Fatal(http.ListenAndServe(\":\" + port, nil))\n}\n", } diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl index b33f2123..3df847fa 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl @@ -14,6 +14,9 @@ ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ Field: field, }) + // FIXME: subscriptions are missing request middleware stack https://github.com/99designs/gqlgen/issues/259 + // and Tracer stack + rctx := ctx results, err := ec.resolvers.{{ $field.ShortInvocation }} if err != nil { ec.Error(ctx, err) @@ -32,6 +35,8 @@ {{ else }} // nolint: vetshadow func (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField, {{if not $object.Root}}obj *{{$object.FullName}}{{end}}) graphql.Marshaler { + ctx = ec.Tracer.StartFieldExecution(ctx, field) + defer func () { ec.Tracer.EndFieldExecution(ctx) }() {{- if $field.Args }} rawArgs := field.ArgumentMap(ec.Variables) args, err := {{ $field.ArgsFunc }}(rawArgs) @@ -46,7 +51,9 @@ Field: field, } ctx = graphql.WithResolverContext(ctx, rctx) - resTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(ctx context.Context) (interface{}, error) { + ctx = ec.Tracer.StartFieldResolverExecution(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children {{- if $field.IsResolver }} return ec.resolvers.{{ $field.ShortInvocation }} {{- else if $field.IsMethod }} @@ -69,6 +76,7 @@ } res := resTmp.({{$field.Signature}}) rctx.Result = res + ctx = ec.Tracer.StartFieldChildExecution(ctx) {{ $field.WriteJson }} } {{ end }} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/generated.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/generated.gotpl index 8250bc7a..a37a1613 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/generated.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/generated.gotpl @@ -3,9 +3,21 @@ package {{ .PackageName }} import ( -{{- range $import := .Imports }} - {{- $import.Write }} -{{ end }} + %%%IMPORTS%%% + + {{ reserveImport "context" }} + {{ reserveImport "fmt" }} + {{ reserveImport "io" }} + {{ reserveImport "strconv" }} + {{ reserveImport "time" }} + {{ reserveImport "sync" }} + {{ reserveImport "errors" }} + {{ reserveImport "bytes" }} + + {{ reserveImport "github.com/vektah/gqlparser" }} + {{ reserveImport "github.com/vektah/gqlparser/ast" }} + {{ reserveImport "github.com/99designs/gqlgen/graphql" }} + {{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }} ) // NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. @@ -126,9 +138,9 @@ func (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinitio }) return &graphql.Response{ - Data: buf, - Errors: ec.Errors, - } + Data: buf, + Errors: ec.Errors, + Extensions: ec.Extensions, } {{- else }} return graphql.ErrorResponse(ctx, "queries are not supported") {{- end }} @@ -146,8 +158,9 @@ func (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefini }) return &graphql.Response{ - Data: buf, - Errors: ec.Errors, + Data: buf, + Errors: ec.Errors, + Extensions: ec.Extensions, } {{- else }} return graphql.ErrorResponse(ctx, "mutations are not supported") @@ -181,8 +194,9 @@ func (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDe } return &graphql.Response{ - Data: buf, - Errors: ec.Errors, + Data: buf, + Errors: ec.Errors, + Extensions: ec.Extensions, } } {{- else }} @@ -250,14 +264,22 @@ func (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{} return res } -func (ec *executionContext) introspectSchema() *introspection.Schema { - return introspection.WrapSchema(parsedSchema) +func (ec *executionContext) introspectSchema() (*introspection.Schema, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapSchema(parsedSchema), nil } -func (ec *executionContext) introspectType(name string) *introspection.Type { - return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]) +func (ec *executionContext) introspectType(name string) (*introspection.Type, error) { + if ec.DisableIntrospection { + return nil, errors.New("introspection disabled") + } + return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]), nil } var parsedSchema = gqlparser.MustLoadSchema( - &ast.Source{Name: {{.SchemaFilename|quote}}, Input: {{.SchemaRaw|rawQuote}}}, + {{- range $filename, $schema := .SchemaRaw }} + &ast.Source{Name: {{$filename|quote}}, Input: {{$schema|rawQuote}}}, + {{- end }} ) diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/import.go b/vendor/github.com/99designs/gqlgen/codegen/templates/import.go new file mode 100644 index 00000000..c9db2d96 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/import.go @@ -0,0 +1,135 @@ +package templates + +import ( + "fmt" + "go/build" + "strconv" + + "github.com/99designs/gqlgen/internal/gopath" +) + +type Import struct { + Name string + Path string + Alias string +} + +type Imports struct { + imports []*Import + destDir string +} + +func (i *Import) String() string { + if i.Alias == i.Name { + return strconv.Quote(i.Path) + } + + return i.Alias + " " + strconv.Quote(i.Path) +} + +func (s *Imports) String() string { + res := "" + for i, imp := range s.imports { + if i != 0 { + res += "\n" + } + res += imp.String() + } + return res +} + +func (s *Imports) Reserve(path string, aliases ...string) string { + if path == "" { + panic("empty ambient import") + } + + // if we are referencing our own package we dont need an import + if gopath.MustDir2Import(s.destDir) == path { + return "" + } + + pkg, err := build.Default.Import(path, s.destDir, 0) + if err != nil { + panic(err) + } + + var alias string + if len(aliases) != 1 { + alias = pkg.Name + } else { + alias = aliases[0] + } + + if existing := s.findByPath(path); existing != nil { + panic("ambient import already exists") + } + + if alias := s.findByAlias(alias); alias != nil { + panic("ambient import collides on an alias") + } + + s.imports = append(s.imports, &Import{ + Name: pkg.Name, + Path: path, + Alias: alias, + }) + + return "" +} + +func (s *Imports) Lookup(path string) string { + if path == "" { + return "" + } + + // if we are referencing our own package we dont need an import + if gopath.MustDir2Import(s.destDir) == path { + return "" + } + + if existing := s.findByPath(path); existing != nil { + return existing.Alias + } + + pkg, err := build.Default.Import(path, s.destDir, 0) + if err != nil { + panic(err) + } + + imp := &Import{ + Name: pkg.Name, + Path: path, + } + s.imports = append(s.imports, imp) + + alias := imp.Name + i := 1 + for s.findByAlias(alias) != nil { + alias = imp.Name + strconv.Itoa(i) + i++ + if i > 10 { + panic(fmt.Errorf("too many collisions, last attempt was %s", alias)) + } + } + imp.Alias = alias + + return imp.Alias +} + +func (s Imports) findByPath(importPath string) *Import { + for _, imp := range s.imports { + if imp.Path == importPath { + return imp + } + } + return nil +} + +func (s Imports) findByAlias(alias string) *Import { + for _, imp := range s.imports { + if imp.Alias == alias { + return imp + } + } + return nil +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl index 7427d71d..db63a996 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl @@ -3,15 +3,29 @@ package {{ .PackageName }} import ( -{{- range $import := .Imports }} - {{- $import.Write }} -{{ end }} + %%%IMPORTS%%% + + {{ reserveImport "context" }} + {{ reserveImport "fmt" }} + {{ reserveImport "io" }} + {{ reserveImport "strconv" }} + {{ reserveImport "time" }} + {{ reserveImport "sync" }} + {{ reserveImport "errors" }} + {{ reserveImport "bytes" }} + + {{ reserveImport "github.com/vektah/gqlparser" }} + {{ reserveImport "github.com/vektah/gqlparser/ast" }} + {{ reserveImport "github.com/99designs/gqlgen/graphql" }} + {{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }} ) {{ range $model := .Models }} {{with .Description}} {{.|prefixLines "// "}} {{end}} {{- if .IsInterface }} - type {{.GoType}} interface {} + type {{.GoType}} interface { + Is{{.GoType}}() + } {{- else }} type {{.GoType}} struct { {{- range $field := .Fields }} @@ -25,6 +39,11 @@ import ( {{- end }} {{- end }} } + + {{- range $iface := .Implements }} + func ({{$model.GoType}}) Is{{$iface.GoType}}() {} + {{- end }} + {{- end }} {{- end}} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl index dd8acf24..53ba8c43 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl @@ -1,11 +1,22 @@ -//go:generate gorunpkg github.com/99designs/gqlgen - package {{ .PackageName }} import ( -{{- range $import := .Imports }} - {{- $import.Write }} -{{ end }} + %%%IMPORTS%%% + + {{ reserveImport "context" }} + {{ reserveImport "fmt" }} + {{ reserveImport "io" }} + {{ reserveImport "strconv" }} + {{ reserveImport "time" }} + {{ reserveImport "sync" }} + {{ reserveImport "errors" }} + {{ reserveImport "bytes" }} + + {{ reserveImport "github.com/99designs/gqlgen/handler" }} + {{ reserveImport "github.com/vektah/gqlparser" }} + {{ reserveImport "github.com/vektah/gqlparser/ast" }} + {{ reserveImport "github.com/99designs/gqlgen/graphql" }} + {{ reserveImport "github.com/99designs/gqlgen/graphql/introspection" }} ) type {{.ResolverType}} struct {} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl index f23b30e1..38dc0d18 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl @@ -1,9 +1,13 @@ package main import ( -{{- range $import := .Imports }} - {{- $import.Write }} -{{ end }} + %%%IMPORTS%%% + + {{ reserveImport "context" }} + {{ reserveImport "log" }} + {{ reserveImport "net/http" }} + {{ reserveImport "os" }} + {{ reserveImport "github.com/99designs/gqlgen/handler" }} ) const defaultPort = "8080" @@ -15,7 +19,7 @@ func main() { } http.Handle("/", handler.Playground("GraphQL playground", "/query")) - http.Handle("/query", handler.GraphQL({{.ExecPackageName}}.NewExecutableSchema({{.ExecPackageName}}.Config{Resolvers: &{{.ResolverPackageName}}.Resolver{}}))) + http.Handle("/query", handler.GraphQL({{ lookupImport .ExecPackageName }}.NewExecutableSchema({{ lookupImport .ExecPackageName}}.Config{Resolvers: &{{ lookupImport .ResolverPackageName}}.Resolver{}}))) log.Printf("connect to http://localhost:%s/ for GraphQL playground", port) log.Fatal(http.ListenAndServe(":" + port, nil)) diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go b/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go index df909cb5..22e5d739 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go @@ -14,21 +14,25 @@ import ( "text/template" "unicode" - "log" + "github.com/99designs/gqlgen/internal/imports" "github.com/pkg/errors" - "golang.org/x/tools/imports" ) +// this is done with a global because subtemplates currently get called in functions. Lets aim to remove this eventually. +var CurrentImports *Imports + func Run(name string, tpldata interface{}) (*bytes.Buffer, error) { t := template.New("").Funcs(template.FuncMap{ - "ucFirst": ucFirst, - "lcFirst": lcFirst, - "quote": strconv.Quote, - "rawQuote": rawQuote, - "toCamel": ToCamel, - "dump": dump, - "prefixLines": prefixLines, + "ucFirst": ucFirst, + "lcFirst": lcFirst, + "quote": strconv.Quote, + "rawQuote": rawQuote, + "toCamel": ToCamel, + "dump": dump, + "prefixLines": prefixLines, + "reserveImport": CurrentImports.Reserve, + "lookupImport": CurrentImports.Lookup, }) for filename, data := range data { @@ -149,27 +153,21 @@ func prefixLines(prefix, s string) string { } func RenderToFile(tpl string, filename string, data interface{}) error { + if CurrentImports != nil { + panic(fmt.Errorf("recursive or concurrent call to RenderToFile detected")) + } + CurrentImports = &Imports{destDir: filepath.Dir(filename)} + var buf *bytes.Buffer buf, err := Run(tpl, data) if err != nil { return errors.Wrap(err, filename+" generation failed") } - if err := write(filename, buf.Bytes()); err != nil { - return err - } - - log.Println(filename) + b := bytes.Replace(buf.Bytes(), []byte("%%%IMPORTS%%%"), []byte(CurrentImports.String()), -1) + CurrentImports = nil - return nil -} - -func gofmt(filename string, b []byte) ([]byte, error) { - out, err := imports.Process(filename, b, nil) - if err != nil { - return b, errors.Wrap(err, "unable to gofmt") - } - return out, nil + return write(filename, b) } func write(filename string, b []byte) error { @@ -178,9 +176,9 @@ func write(filename string, b []byte) error { return errors.Wrap(err, "failed to create directory") } - formatted, err := gofmt(filename, b) + formatted, err := imports.Prune(filename, b) if err != nil { - fmt.Fprintf(os.Stderr, "gofmt failed: %s\n", err.Error()) + fmt.Fprintf(os.Stderr, "gofmt failed on %s: %s\n", filepath.Base(filename), err.Error()) formatted = b } diff --git a/vendor/github.com/99designs/gqlgen/codegen/type.go b/vendor/github.com/99designs/gqlgen/codegen/type.go index 8c53fe55..04d9bb2f 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/type.go +++ b/vendor/github.com/99designs/gqlgen/codegen/type.go @@ -4,6 +4,8 @@ import ( "strconv" "strings" + "github.com/99designs/gqlgen/codegen/templates" + "github.com/vektah/gqlparser/ast" ) @@ -19,10 +21,9 @@ type NamedType struct { } type Ref struct { - GoType string // Name of the go type - Package string // the package the go type lives in - Import *Import // the resolved import with alias - IsUserDefined bool // does the type exist in the typemap + GoType string // Name of the go type + Package string // the package the go type lives in + IsUserDefined bool // does the type exist in the typemap } type Type struct { @@ -43,10 +44,13 @@ func (t Ref) FullName() string { } func (t Ref) PkgDot() string { - if t.Import == nil || t.Import.Alias() == "" { + name := templates.CurrentImports.Lookup(t.Package) + if name == "" { return "" + } - return t.Import.Alias() + "." + + return name + "." } func (t Type) Signature() string { diff --git a/vendor/github.com/99designs/gqlgen/codegen/type_build.go b/vendor/github.com/99designs/gqlgen/codegen/type_build.go index f0ec6785..586b0db2 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/type_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/type_build.go @@ -27,7 +27,7 @@ func (cfg *Config) buildNamedTypes() NamedTypes { return types } -func (cfg *Config) bindTypes(imports *Imports, namedTypes NamedTypes, destDir string, prog *loader.Program) { +func (cfg *Config) bindTypes(namedTypes NamedTypes, destDir string, prog *loader.Program) { for _, t := range namedTypes { if t.Package == "" { continue @@ -41,7 +41,6 @@ func (cfg *Config) bindTypes(imports *Imports, namedTypes NamedTypes, destDir st t.Marshaler = &cpy t.Package, t.GoType = pkgAndType(sig.Params().At(0).Type().String()) - t.Import = imports.add(t.Package) } } } diff --git a/vendor/github.com/99designs/gqlgen/codegen/util.go b/vendor/github.com/99designs/gqlgen/codegen/util.go index 1849f100..cc6246fd 100644 --- a/vendor/github.com/99designs/gqlgen/codegen/util.go +++ b/vendor/github.com/99designs/gqlgen/codegen/util.go @@ -105,6 +105,12 @@ func findMethod(typ *types.Named, name string) *types.Func { return nil } +func equalFieldName(source, target string) bool { + source = strings.Replace(source, "_", "", -1) + target = strings.Replace(target, "_", "", -1) + return strings.EqualFold(source, target) +} + // findField attempts to match the name to a struct field with the following // priorites: // 1. If struct tag is passed then struct tag has highest priority @@ -120,7 +126,7 @@ func findField(typ *types.Struct, name, structTag string) (*types.Var, error) { if structTag != "" { tags := reflect.StructTag(typ.Tag(i)) if val, ok := tags.Lookup(structTag); ok { - if strings.EqualFold(val, name) { + if equalFieldName(val, name) { if foundField != nil && foundFieldWasTag { return nil, errors.Errorf("tag %s is ambigious; multiple fields have the same tag value of %s", structTag, val) } @@ -132,17 +138,16 @@ func findField(typ *types.Struct, name, structTag string) (*types.Var, error) { } if field.Anonymous() { - if named, ok := field.Type().(*types.Struct); ok { - f, err := findField(named, name, structTag) - if err != nil && !strings.HasPrefix(err.Error(), "no field named") { - return nil, err - } - if f != nil && foundField == nil { - foundField = f - } + + fieldType := field.Type() + + if ptr, ok := fieldType.(*types.Pointer); ok { + fieldType = ptr.Elem() } - if named, ok := field.Type().Underlying().(*types.Struct); ok { + // Type.Underlying() returns itself for all types except types.Named, where it returns a struct type. + // It should be safe to always call. + if named, ok := fieldType.Underlying().(*types.Struct); ok { f, err := findField(named, name, structTag) if err != nil && !strings.HasPrefix(err.Error(), "no field named") { return nil, err @@ -157,7 +162,7 @@ func findField(typ *types.Struct, name, structTag string) (*types.Var, error) { continue } - if strings.EqualFold(field.Name(), name) && foundField == nil { + if equalFieldName(field.Name(), name) && foundField == nil { // aqui! foundField = field } } @@ -198,7 +203,7 @@ func (b BindErrors) Error() string { return strings.Join(errs, "\n\n") } -func bindObject(t types.Type, object *Object, imports *Imports, structTag string) BindErrors { +func bindObject(t types.Type, object *Object, structTag string) BindErrors { var errs BindErrors for i := range object.Fields { field := &object.Fields[i] @@ -208,13 +213,13 @@ func bindObject(t types.Type, object *Object, imports *Imports, structTag string } // first try binding to a method - methodErr := bindMethod(imports, t, field) + methodErr := bindMethod(t, field) if methodErr == nil { continue } // otherwise try binding to a var - varErr := bindVar(imports, t, field, structTag) + varErr := bindVar(t, field, structTag) if varErr != nil { errs = append(errs, BindError{ @@ -229,7 +234,7 @@ func bindObject(t types.Type, object *Object, imports *Imports, structTag string return errs } -func bindMethod(imports *Imports, t types.Type, field *Field) error { +func bindMethod(t types.Type, field *Field) error { namedType, ok := t.(*types.Named) if !ok { return fmt.Errorf("not a named type") @@ -250,13 +255,25 @@ func bindMethod(imports *Imports, t types.Type, field *Field) error { } else if sig.Results().Len() != 2 { return fmt.Errorf("method has wrong number of args") } - newArgs, err := matchArgs(field, sig.Params()) + params := sig.Params() + // If the first argument is the context, remove it from the comparison and set + // the MethodHasContext flag so that the context will be passed to this model's method + if params.Len() > 0 && params.At(0).Type().String() == "context.Context" { + field.MethodHasContext = true + vars := make([]*types.Var, params.Len()-1) + for i := 1; i < params.Len(); i++ { + vars[i-1] = params.At(i) + } + params = types.NewTuple(vars...) + } + + newArgs, err := matchArgs(field, params) if err != nil { return err } result := sig.Results().At(0) - if err := validateTypeBinding(imports, field, result.Type()); err != nil { + if err := validateTypeBinding(field, result.Type()); err != nil { return errors.Wrap(err, "method has wrong return type") } @@ -268,7 +285,7 @@ func bindMethod(imports *Imports, t types.Type, field *Field) error { return nil } -func bindVar(imports *Imports, t types.Type, field *Field, structTag string) error { +func bindVar(t types.Type, field *Field, structTag string) error { underlying, ok := t.Underlying().(*types.Struct) if !ok { return fmt.Errorf("not a struct") @@ -283,7 +300,7 @@ func bindVar(imports *Imports, t types.Type, field *Field, structTag string) err return err } - if err := validateTypeBinding(imports, field, structField.Type()); err != nil { + if err := validateTypeBinding(field, structField.Type()); err != nil { return errors.Wrap(err, "field has wrong type") } @@ -316,22 +333,21 @@ nextArg: return newArgs, nil } -func validateTypeBinding(imports *Imports, field *Field, goType types.Type) error { +func validateTypeBinding(field *Field, goType types.Type) error { gqlType := normalizeVendor(field.Type.FullSignature()) goTypeStr := normalizeVendor(goType.String()) - if goTypeStr == gqlType || "*"+goTypeStr == gqlType || goTypeStr == "*"+gqlType { + if equalTypes(goTypeStr, gqlType) { field.Type.Modifiers = modifiersFromGoType(goType) return nil } // deal with type aliases underlyingStr := normalizeVendor(goType.Underlying().String()) - if underlyingStr == gqlType || "*"+underlyingStr == gqlType || underlyingStr == "*"+gqlType { + if equalTypes(underlyingStr, gqlType) { field.Type.Modifiers = modifiersFromGoType(goType) pkg, typ := pkgAndType(goType.String()) - imp := imports.findByPath(pkg) - field.AliasedType = &Ref{GoType: typ, Import: imp} + field.AliasedType = &Ref{GoType: typ, Package: pkg} return nil } @@ -365,3 +381,7 @@ func normalizeVendor(pkg string) string { parts := strings.Split(pkg, "/vendor/") return modifiers + parts[len(parts)-1] } + +func equalTypes(goType string, gqlType string) bool { + return goType == gqlType || "*"+goType == gqlType || goType == "*"+gqlType || strings.Replace(goType, "[]*", "[]", -1) == gqlType +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/context.go b/vendor/github.com/99designs/gqlgen/graphql/context.go index 6baee83c..f83fa36f 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/context.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context.go @@ -17,6 +17,11 @@ type RequestContext struct { RawQuery string Variables map[string]interface{} Doc *ast.QueryDocument + + ComplexityLimit int + OperationComplexity int + DisableIntrospection bool + // ErrorPresenter will be used to generate the error // message from errors given to Error(). ErrorPresenter ErrorPresenterFunc @@ -24,9 +29,12 @@ type RequestContext struct { ResolverMiddleware FieldMiddleware DirectiveMiddleware FieldMiddleware RequestMiddleware RequestMiddleware + Tracer Tracer - errorsMu sync.Mutex - Errors gqlerror.List + errorsMu sync.Mutex + Errors gqlerror.List + extensionsMu sync.Mutex + Extensions map[string]interface{} } func DefaultResolverMiddleware(ctx context.Context, next Resolver) (res interface{}, err error) { @@ -51,6 +59,7 @@ func NewRequestContext(doc *ast.QueryDocument, query string, variables map[strin RequestMiddleware: DefaultRequestMiddleware, Recover: DefaultRecover, ErrorPresenter: DefaultErrorPresenter, + Tracer: &NopTracer{}, } } @@ -153,6 +162,21 @@ func (c *RequestContext) HasError(rctx *ResolverContext) bool { return false } +// GetErrors returns a list of errors that occurred in the current field +func (c *RequestContext) GetErrors(rctx *ResolverContext) gqlerror.List { + c.errorsMu.Lock() + defer c.errorsMu.Unlock() + path := rctx.Path() + + var errs gqlerror.List + for _, err := range c.Errors { + if equalPath(err.Path, path) { + errs = append(errs, err) + } + } + return errs +} + func equalPath(a []interface{}, b []interface{}) bool { if len(a) != len(b) { return false @@ -176,3 +200,20 @@ func AddError(ctx context.Context, err error) { func AddErrorf(ctx context.Context, format string, args ...interface{}) { GetRequestContext(ctx).Errorf(ctx, format, args...) } + +// RegisterExtension registers an extension, returns error if extension has already been registered +func (c *RequestContext) RegisterExtension(key string, value interface{}) error { + c.extensionsMu.Lock() + defer c.extensionsMu.Unlock() + + if c.Extensions == nil { + c.Extensions = make(map[string]interface{}) + } + + if _, ok := c.Extensions[key]; ok { + return fmt.Errorf("extension already registered for key %s", key) + } + + c.Extensions[key] = value + return nil +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/float.go b/vendor/github.com/99designs/gqlgen/graphql/float.go index d204335c..fabbad04 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/float.go +++ b/vendor/github.com/99designs/gqlgen/graphql/float.go @@ -9,7 +9,7 @@ import ( func MarshalFloat(f float64) Marshaler { return WriterFunc(func(w io.Writer) { - io.WriteString(w, fmt.Sprintf("%f", f)) + io.WriteString(w, fmt.Sprintf("%g", f)) }) } diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go index baff882e..ca0b065f 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go @@ -12,19 +12,17 @@ type ( } EnumValue struct { - Name string - Description string - IsDeprecated bool - DeprecationReason string + Name string + Description string + deprecation *ast.Directive } Field struct { - Name string - Description string - Type *Type - Args []InputValue - IsDeprecated bool - DeprecationReason string + Name string + Description string + Type *Type + Args []InputValue + deprecation *ast.Directive } InputValue struct { @@ -39,20 +37,36 @@ func WrapSchema(schema *ast.Schema) *Schema { return &Schema{schema: schema} } -func isDeprecated(directives ast.DirectiveList) bool { - return directives.ForName("deprecated") != nil +func (f *EnumValue) IsDeprecated() bool { + return f.deprecation != nil +} + +func (f *EnumValue) DeprecationReason() *string { + if f.deprecation == nil { + return nil + } + + reason := f.deprecation.Arguments.ForName("reason") + if reason == nil { + return nil + } + + return &reason.Value.Raw +} + +func (f *Field) IsDeprecated() bool { + return f.deprecation != nil } -func deprecationReason(directives ast.DirectiveList) string { - deprecation := directives.ForName("deprecated") - if deprecation == nil { - return "" +func (f *Field) DeprecationReason() *string { + if f.deprecation == nil { + return nil } - reason := deprecation.Arguments.ForName("reason") + reason := f.deprecation.Arguments.ForName("reason") if reason == nil { - return "" + return nil } - return reason.Value.Raw + return &reason.Value.Raw } diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/type.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/type.go index dce144e0..b963aa0e 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/introspection/type.go +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/type.go @@ -81,12 +81,11 @@ func (t *Type) Fields(includeDeprecated bool) []Field { } fields = append(fields, Field{ - Name: f.Name, - Description: f.Description, - Args: args, - Type: WrapTypeFromType(t.schema, f.Type), - IsDeprecated: isDeprecated(f.Directives), - DeprecationReason: deprecationReason(f.Directives), + Name: f.Name, + Description: f.Description, + Args: args, + Type: WrapTypeFromType(t.schema, f.Type), + deprecation: f.Directives.ForName("deprecated"), }) } return fields @@ -150,10 +149,9 @@ func (t *Type) EnumValues(includeDeprecated bool) []EnumValue { var res []EnumValue for _, val := range t.def.EnumValues { res = append(res, EnumValue{ - Name: val.Name, - Description: val.Description, - IsDeprecated: isDeprecated(val.Directives), - DeprecationReason: deprecationReason(val.Directives), + Name: val.Name, + Description: val.Description, + deprecation: val.Directives.ForName("deprecated"), }) } return res diff --git a/vendor/github.com/99designs/gqlgen/graphql/response.go b/vendor/github.com/99designs/gqlgen/graphql/response.go index 18664dca..6fe55d56 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/response.go +++ b/vendor/github.com/99designs/gqlgen/graphql/response.go @@ -8,9 +8,13 @@ import ( "github.com/vektah/gqlparser/gqlerror" ) +// Errors are intentionally serialized first based on the advice in +// https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107 +// and https://github.com/facebook/graphql/pull/384 type Response struct { - Data json.RawMessage `json:"data"` - Errors gqlerror.List `json:"errors,omitempty"` + Errors gqlerror.List `json:"errors,omitempty"` + Data json.RawMessage `json:"data"` + Extensions map[string]interface{} `json:"extensions,omitempty"` } func ErrorResponse(ctx context.Context, messagef string, args ...interface{}) *Response { diff --git a/vendor/github.com/99designs/gqlgen/graphql/tracer.go b/vendor/github.com/99designs/gqlgen/graphql/tracer.go new file mode 100644 index 00000000..0597ce8c --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/tracer.go @@ -0,0 +1,58 @@ +package graphql + +import ( + "context" +) + +var _ Tracer = (*NopTracer)(nil) + +type Tracer interface { + StartOperationParsing(ctx context.Context) context.Context + EndOperationParsing(ctx context.Context) + StartOperationValidation(ctx context.Context) context.Context + EndOperationValidation(ctx context.Context) + StartOperationExecution(ctx context.Context) context.Context + StartFieldExecution(ctx context.Context, field CollectedField) context.Context + StartFieldResolverExecution(ctx context.Context, rc *ResolverContext) context.Context + StartFieldChildExecution(ctx context.Context) context.Context + EndFieldExecution(ctx context.Context) + EndOperationExecution(ctx context.Context) +} + +type NopTracer struct{} + +func (NopTracer) StartOperationParsing(ctx context.Context) context.Context { + return ctx +} + +func (NopTracer) EndOperationParsing(ctx context.Context) { +} + +func (NopTracer) StartOperationValidation(ctx context.Context) context.Context { + return ctx +} + +func (NopTracer) EndOperationValidation(ctx context.Context) { +} + +func (NopTracer) StartOperationExecution(ctx context.Context) context.Context { + return ctx +} + +func (NopTracer) StartFieldExecution(ctx context.Context, field CollectedField) context.Context { + return ctx +} + +func (NopTracer) StartFieldResolverExecution(ctx context.Context, rc *ResolverContext) context.Context { + return ctx +} + +func (NopTracer) StartFieldChildExecution(ctx context.Context) context.Context { + return ctx +} + +func (NopTracer) EndFieldExecution(ctx context.Context) { +} + +func (NopTracer) EndOperationExecution(ctx context.Context) { +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/version.go b/vendor/github.com/99designs/gqlgen/graphql/version.go index 38d3720b..8cf3c9ba 100644 --- a/vendor/github.com/99designs/gqlgen/graphql/version.go +++ b/vendor/github.com/99designs/gqlgen/graphql/version.go @@ -1,3 +1,3 @@ package graphql -const Version = "v0.5.1" +const Version = "dev" diff --git a/vendor/github.com/99designs/gqlgen/handler/context.go b/vendor/github.com/99designs/gqlgen/handler/context.go new file mode 100644 index 00000000..2992aa3d --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/handler/context.go @@ -0,0 +1,57 @@ +package handler + +import "context" + +type key string + +const ( + initpayload key = "ws_initpayload_context" +) + +// InitPayload is a structure that is parsed from the websocket init message payload. TO use +// request headers for non-websocket, instead wrap the graphql handler in a middleware. +type InitPayload map[string]interface{} + +// GetString safely gets a string value from the payload. It returns an empty string if the +// payload is nil or the value isn't set. +func (payload InitPayload) GetString(key string) string { + if payload == nil { + return "" + } + + if value, ok := payload[key]; ok { + res, _ := value.(string) + return res + } + + return "" +} + +// Authorization is a short hand for getting the Authorization header from the +// payload. +func (payload InitPayload) Authorization() string { + if value := payload.GetString("Authorization"); value != "" { + return value + } + + if value := payload.GetString("authorization"); value != "" { + return value + } + + return "" +} + +func withInitPayload(ctx context.Context, payload InitPayload) context.Context { + return context.WithValue(ctx, initpayload, payload) +} + +// GetInitPayload gets a map of the data sent with the connection_init message, which is used by +// graphql clients as a stand-in for HTTP headers. +func GetInitPayload(ctx context.Context) InitPayload { + payload, ok := ctx.Value(initpayload).(InitPayload) + if !ok { + return nil + } + + return payload +} diff --git a/vendor/github.com/99designs/gqlgen/handler/graphql.go b/vendor/github.com/99designs/gqlgen/handler/graphql.go index 9d222826..eb8880de 100644 --- a/vendor/github.com/99designs/gqlgen/handler/graphql.go +++ b/vendor/github.com/99designs/gqlgen/handler/graphql.go @@ -12,9 +12,9 @@ import ( "github.com/99designs/gqlgen/graphql" "github.com/gorilla/websocket" "github.com/hashicorp/golang-lru" - "github.com/vektah/gqlparser" "github.com/vektah/gqlparser/ast" "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/parser" "github.com/vektah/gqlparser/validator" ) @@ -25,17 +25,21 @@ type params struct { } type Config struct { - cacheSize int - upgrader websocket.Upgrader - recover graphql.RecoverFunc - errorPresenter graphql.ErrorPresenterFunc - resolverHook graphql.FieldMiddleware - requestHook graphql.RequestMiddleware - complexityLimit int + cacheSize int + upgrader websocket.Upgrader + recover graphql.RecoverFunc + errorPresenter graphql.ErrorPresenterFunc + resolverHook graphql.FieldMiddleware + requestHook graphql.RequestMiddleware + tracer graphql.Tracer + complexityLimit int + disableIntrospection bool } -func (c *Config) newRequestContext(doc *ast.QueryDocument, query string, variables map[string]interface{}) *graphql.RequestContext { +func (c *Config) newRequestContext(es graphql.ExecutableSchema, doc *ast.QueryDocument, op *ast.OperationDefinition, query string, variables map[string]interface{}) *graphql.RequestContext { reqCtx := graphql.NewRequestContext(doc, query, variables) + reqCtx.DisableIntrospection = c.disableIntrospection + if hook := c.recover; hook != nil { reqCtx.Recover = hook } @@ -52,6 +56,18 @@ func (c *Config) newRequestContext(doc *ast.QueryDocument, query string, variabl reqCtx.RequestMiddleware = hook } + if hook := c.tracer; hook != nil { + reqCtx.Tracer = hook + } else { + reqCtx.Tracer = &graphql.NopTracer{} + } + + if c.complexityLimit > 0 { + reqCtx.ComplexityLimit = c.complexityLimit + operationComplexity := complexity.Calculate(es, op, variables) + reqCtx.OperationComplexity = operationComplexity + } + return reqCtx } @@ -78,6 +94,14 @@ func ErrorPresenter(f graphql.ErrorPresenterFunc) Option { } } +// IntrospectionEnabled = false will forbid clients from calling introspection endpoints. Can be useful in prod when you dont +// want clients introspecting the full schema. +func IntrospectionEnabled(enabled bool) Option { + return func(cfg *Config) { + cfg.disableIntrospection = !enabled + } +} + // ComplexityLimit sets a maximum query complexity that is allowed to be executed. // If a query is submitted that exceeds the limit, a 422 status code will be returned. func ComplexityLimit(limit int) Option { @@ -87,7 +111,7 @@ func ComplexityLimit(limit int) Option { } // ResolverMiddleware allows you to define a function that will be called around every resolver, -// useful for tracing and logging. +// useful for logging. func ResolverMiddleware(middleware graphql.FieldMiddleware) Option { return func(cfg *Config) { if cfg.resolverHook == nil { @@ -105,7 +129,7 @@ func ResolverMiddleware(middleware graphql.FieldMiddleware) Option { } // RequestMiddleware allows you to define a function that will be called around the root request, -// after the query has been parsed. This is useful for logging and tracing +// after the query has been parsed. This is useful for logging func RequestMiddleware(middleware graphql.RequestMiddleware) Option { return func(cfg *Config) { if cfg.requestHook == nil { @@ -122,6 +146,93 @@ func RequestMiddleware(middleware graphql.RequestMiddleware) Option { } } +// Tracer allows you to add a request/resolver tracer that will be called around the root request, +// calling resolver. This is useful for tracing +func Tracer(tracer graphql.Tracer) Option { + return func(cfg *Config) { + if cfg.tracer == nil { + cfg.tracer = tracer + + } else { + lastResolve := cfg.tracer + cfg.tracer = &tracerWrapper{ + tracer1: lastResolve, + tracer2: tracer, + } + } + + opt := RequestMiddleware(func(ctx context.Context, next func(ctx context.Context) []byte) []byte { + ctx = tracer.StartOperationExecution(ctx) + resp := next(ctx) + tracer.EndOperationExecution(ctx) + + return resp + }) + opt(cfg) + } +} + +type tracerWrapper struct { + tracer1 graphql.Tracer + tracer2 graphql.Tracer +} + +func (tw *tracerWrapper) StartOperationParsing(ctx context.Context) context.Context { + ctx = tw.tracer1.StartOperationParsing(ctx) + ctx = tw.tracer2.StartOperationParsing(ctx) + return ctx +} + +func (tw *tracerWrapper) EndOperationParsing(ctx context.Context) { + tw.tracer2.EndOperationParsing(ctx) + tw.tracer1.EndOperationParsing(ctx) +} + +func (tw *tracerWrapper) StartOperationValidation(ctx context.Context) context.Context { + ctx = tw.tracer1.StartOperationValidation(ctx) + ctx = tw.tracer2.StartOperationValidation(ctx) + return ctx +} + +func (tw *tracerWrapper) EndOperationValidation(ctx context.Context) { + tw.tracer2.EndOperationValidation(ctx) + tw.tracer1.EndOperationValidation(ctx) +} + +func (tw *tracerWrapper) StartOperationExecution(ctx context.Context) context.Context { + ctx = tw.tracer1.StartOperationExecution(ctx) + ctx = tw.tracer2.StartOperationExecution(ctx) + return ctx +} + +func (tw *tracerWrapper) StartFieldExecution(ctx context.Context, field graphql.CollectedField) context.Context { + ctx = tw.tracer1.StartFieldExecution(ctx, field) + ctx = tw.tracer2.StartFieldExecution(ctx, field) + return ctx +} + +func (tw *tracerWrapper) StartFieldResolverExecution(ctx context.Context, rc *graphql.ResolverContext) context.Context { + ctx = tw.tracer1.StartFieldResolverExecution(ctx, rc) + ctx = tw.tracer2.StartFieldResolverExecution(ctx, rc) + return ctx +} + +func (tw *tracerWrapper) StartFieldChildExecution(ctx context.Context) context.Context { + ctx = tw.tracer1.StartFieldChildExecution(ctx) + ctx = tw.tracer2.StartFieldChildExecution(ctx) + return ctx +} + +func (tw *tracerWrapper) EndFieldExecution(ctx context.Context) { + tw.tracer2.EndFieldExecution(ctx) + tw.tracer1.EndFieldExecution(ctx) +} + +func (tw *tracerWrapper) EndOperationExecution(ctx context.Context) { + tw.tracer2.EndOperationExecution(ctx) + tw.tracer1.EndOperationExecution(ctx) +} + // CacheSize sets the maximum size of the query cache. // If size is less than or equal to 0, the cache is disabled. func CacheSize(size int) Option { @@ -133,7 +244,7 @@ func CacheSize(size int) Option { const DefaultCacheSize = 1000 func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc { - cfg := Config{ + cfg := &Config{ cacheSize: DefaultCacheSize, upgrader: websocket.Upgrader{ ReadBufferSize: 1024, @@ -142,7 +253,7 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc } for _, option := range options { - option(&cfg) + option(cfg) } var cache *lru.Cache @@ -155,112 +266,187 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc panic("unexpected error creating cache: " + err.Error()) } } + if cfg.tracer == nil { + cfg.tracer = &graphql.NopTracer{} + } - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodOptions { - w.Header().Set("Allow", "OPTIONS, GET, POST") - w.WriteHeader(http.StatusOK) - return - } + handler := &graphqlHandler{ + cfg: cfg, + cache: cache, + exec: exec, + } - if strings.Contains(r.Header.Get("Upgrade"), "websocket") { - connectWs(exec, w, r, &cfg) - return - } + return handler.ServeHTTP +} - var reqParams params - switch r.Method { - case http.MethodGet: - reqParams.Query = r.URL.Query().Get("query") - reqParams.OperationName = r.URL.Query().Get("operationName") - - if variables := r.URL.Query().Get("variables"); variables != "" { - if err := jsonDecode(strings.NewReader(variables), &reqParams.Variables); err != nil { - sendErrorf(w, http.StatusBadRequest, "variables could not be decoded") - return - } - } - case http.MethodPost: - if err := jsonDecode(r.Body, &reqParams); err != nil { - sendErrorf(w, http.StatusBadRequest, "json body could not be decoded: "+err.Error()) +var _ http.Handler = (*graphqlHandler)(nil) + +type graphqlHandler struct { + cfg *Config + cache *lru.Cache + exec graphql.ExecutableSchema +} + +func (gh *graphqlHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodOptions { + w.Header().Set("Allow", "OPTIONS, GET, POST") + w.WriteHeader(http.StatusOK) + return + } + + if strings.Contains(r.Header.Get("Upgrade"), "websocket") { + connectWs(gh.exec, w, r, gh.cfg) + return + } + + var reqParams params + switch r.Method { + case http.MethodGet: + reqParams.Query = r.URL.Query().Get("query") + reqParams.OperationName = r.URL.Query().Get("operationName") + + if variables := r.URL.Query().Get("variables"); variables != "" { + if err := jsonDecode(strings.NewReader(variables), &reqParams.Variables); err != nil { + sendErrorf(w, http.StatusBadRequest, "variables could not be decoded") return } - default: - w.WriteHeader(http.StatusMethodNotAllowed) + } + case http.MethodPost: + if err := jsonDecode(r.Body, &reqParams); err != nil { + sendErrorf(w, http.StatusBadRequest, "json body could not be decoded: "+err.Error()) return } - w.Header().Set("Content-Type", "application/json") + default: + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + w.Header().Set("Content-Type", "application/json") - var doc *ast.QueryDocument - if cache != nil { - val, ok := cache.Get(reqParams.Query) - if ok { - doc = val.(*ast.QueryDocument) - } - } - if doc == nil { - var qErr gqlerror.List - doc, qErr = gqlparser.LoadQuery(exec.Schema(), reqParams.Query) - if len(qErr) > 0 { - sendError(w, http.StatusUnprocessableEntity, qErr...) - return - } - if cache != nil { - cache.Add(reqParams.Query, doc) - } - } + ctx := r.Context() - op := doc.Operations.ForName(reqParams.OperationName) - if op == nil { - sendErrorf(w, http.StatusUnprocessableEntity, "operation %s not found", reqParams.OperationName) - return + var doc *ast.QueryDocument + var cacheHit bool + if gh.cache != nil { + val, ok := gh.cache.Get(reqParams.Query) + if ok { + doc = val.(*ast.QueryDocument) + cacheHit = true } + } - if op.Operation != ast.Query && r.Method == http.MethodGet { - sendErrorf(w, http.StatusUnprocessableEntity, "GET requests only allow query operations") - return + ctx, doc, gqlErr := gh.parseOperation(ctx, &parseOperationArgs{ + Query: reqParams.Query, + CachedDoc: doc, + }) + if gqlErr != nil { + sendError(w, http.StatusUnprocessableEntity, gqlErr) + return + } + + ctx, op, vars, listErr := gh.validateOperation(ctx, &validateOperationArgs{ + Doc: doc, + OperationName: reqParams.OperationName, + CacheHit: cacheHit, + R: r, + Variables: reqParams.Variables, + }) + if len(listErr) != 0 { + sendError(w, http.StatusUnprocessableEntity, listErr...) + return + } + + if gh.cache != nil && !cacheHit { + gh.cache.Add(reqParams.Query, doc) + } + + reqCtx := gh.cfg.newRequestContext(gh.exec, doc, op, reqParams.Query, vars) + ctx = graphql.WithRequestContext(ctx, reqCtx) + + defer func() { + if err := recover(); err != nil { + userErr := reqCtx.Recover(ctx, err) + sendErrorf(w, http.StatusUnprocessableEntity, userErr.Error()) } + }() - vars, err := validator.VariableValues(exec.Schema(), op, reqParams.Variables) + if reqCtx.ComplexityLimit > 0 && reqCtx.OperationComplexity > reqCtx.ComplexityLimit { + sendErrorf(w, http.StatusUnprocessableEntity, "operation has complexity %d, which exceeds the limit of %d", reqCtx.OperationComplexity, reqCtx.ComplexityLimit) + return + } + + switch op.Operation { + case ast.Query: + b, err := json.Marshal(gh.exec.Query(ctx, op)) if err != nil { - sendError(w, http.StatusUnprocessableEntity, err) - return + panic(err) } - reqCtx := cfg.newRequestContext(doc, reqParams.Query, vars) - ctx := graphql.WithRequestContext(r.Context(), reqCtx) + w.Write(b) + case ast.Mutation: + b, err := json.Marshal(gh.exec.Mutation(ctx, op)) + if err != nil { + panic(err) + } + w.Write(b) + default: + sendErrorf(w, http.StatusBadRequest, "unsupported operation type") + } +} - defer func() { - if err := recover(); err != nil { - userErr := reqCtx.Recover(ctx, err) - sendErrorf(w, http.StatusUnprocessableEntity, userErr.Error()) - } - }() +type parseOperationArgs struct { + Query string + CachedDoc *ast.QueryDocument +} - if cfg.complexityLimit > 0 { - queryComplexity := complexity.Calculate(exec, op, vars) - if queryComplexity > cfg.complexityLimit { - sendErrorf(w, http.StatusUnprocessableEntity, "query has complexity %d, which exceeds the limit of %d", queryComplexity, cfg.complexityLimit) - return - } - } +func (gh *graphqlHandler) parseOperation(ctx context.Context, args *parseOperationArgs) (context.Context, *ast.QueryDocument, *gqlerror.Error) { + ctx = gh.cfg.tracer.StartOperationParsing(ctx) + defer func() { gh.cfg.tracer.EndOperationParsing(ctx) }() - switch op.Operation { - case ast.Query: - b, err := json.Marshal(exec.Query(ctx, op)) - if err != nil { - panic(err) - } - w.Write(b) - case ast.Mutation: - b, err := json.Marshal(exec.Mutation(ctx, op)) - if err != nil { - panic(err) - } - w.Write(b) - default: - sendErrorf(w, http.StatusBadRequest, "unsupported operation type") + if args.CachedDoc != nil { + return ctx, args.CachedDoc, nil + } + + doc, gqlErr := parser.ParseQuery(&ast.Source{Input: args.Query}) + if gqlErr != nil { + return ctx, nil, gqlErr + } + + return ctx, doc, nil +} + +type validateOperationArgs struct { + Doc *ast.QueryDocument + OperationName string + CacheHit bool + R *http.Request + Variables map[string]interface{} +} + +func (gh *graphqlHandler) validateOperation(ctx context.Context, args *validateOperationArgs) (context.Context, *ast.OperationDefinition, map[string]interface{}, gqlerror.List) { + ctx = gh.cfg.tracer.StartOperationValidation(ctx) + defer func() { gh.cfg.tracer.EndOperationValidation(ctx) }() + + if !args.CacheHit { + listErr := validator.Validate(gh.exec.Schema(), args.Doc) + if len(listErr) != 0 { + return ctx, nil, nil, listErr } - }) + } + + op := args.Doc.Operations.ForName(args.OperationName) + if op == nil { + return ctx, nil, nil, gqlerror.List{gqlerror.Errorf("operation %s not found", args.OperationName)} + } + + if op.Operation != ast.Query && args.R.Method == http.MethodGet { + return ctx, nil, nil, gqlerror.List{gqlerror.Errorf("GET requests only allow query operations")} + } + + vars, err := validator.VariableValues(gh.exec.Schema(), op, args.Variables) + if err != nil { + return ctx, nil, nil, gqlerror.List{err} + } + + return ctx, op, vars, nil } func jsonDecode(r io.Reader, val interface{}) error { diff --git a/vendor/github.com/99designs/gqlgen/handler/playground.go b/vendor/github.com/99designs/gqlgen/handler/playground.go index d0ada8ca..f1687def 100644 --- a/vendor/github.com/99designs/gqlgen/handler/playground.go +++ b/vendor/github.com/99designs/gqlgen/handler/playground.go @@ -45,7 +45,7 @@ func Playground(title string, endpoint string) http.HandlerFunc { err := page.Execute(w, map[string]string{ "title": title, "endpoint": endpoint, - "version": "1.6.2", + "version": "1.7.8", }) if err != nil { panic(err) diff --git a/vendor/github.com/99designs/gqlgen/handler/websocket.go b/vendor/github.com/99designs/gqlgen/handler/websocket.go index 2be1e87f..dae262bd 100644 --- a/vendor/github.com/99designs/gqlgen/handler/websocket.go +++ b/vendor/github.com/99designs/gqlgen/handler/websocket.go @@ -43,6 +43,8 @@ type wsConnection struct { active map[string]context.CancelFunc mu sync.Mutex cfg *Config + + initPayload InitPayload } func connectWs(exec graphql.ExecutableSchema, w http.ResponseWriter, r *http.Request, cfg *Config) { @@ -79,6 +81,14 @@ func (c *wsConnection) init() bool { switch message.Type { case connectionInitMsg: + if len(message.Payload) > 0 { + c.initPayload = make(InitPayload) + err := json.Unmarshal(message.Payload, &c.initPayload) + if err != nil { + return false + } + } + c.write(&operationMessage{Type: connectionAckMsg}) case connectionTerminateMsg: c.close(websocket.CloseNormalClosure, "terminated") @@ -155,9 +165,13 @@ func (c *wsConnection) subscribe(message *operationMessage) bool { c.sendError(message.ID, err) return true } - reqCtx := c.cfg.newRequestContext(doc, reqParams.Query, vars) + reqCtx := c.cfg.newRequestContext(c.exec, doc, op, reqParams.Query, vars) ctx := graphql.WithRequestContext(c.ctx, reqCtx) + if c.initPayload != nil { + ctx = withInitPayload(ctx, c.initPayload) + } + if op.Operation != ast.Subscription { var result *graphql.Response if op.Operation == ast.Query { diff --git a/vendor/github.com/99designs/gqlgen/internal/imports/prune.go b/vendor/github.com/99designs/gqlgen/internal/imports/prune.go new file mode 100644 index 00000000..d2469e83 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/internal/imports/prune.go @@ -0,0 +1,119 @@ +// Wrapper around x/tools/imports that only removes imports, never adds new ones. + +package imports + +import ( + "bytes" + "go/ast" + "go/build" + "go/parser" + "go/printer" + "go/token" + "path/filepath" + "strings" + + "golang.org/x/tools/imports" + + "golang.org/x/tools/go/ast/astutil" +) + +type visitFn func(node ast.Node) + +func (fn visitFn) Visit(node ast.Node) ast.Visitor { + fn(node) + return fn +} + +// Prune removes any unused imports +func Prune(filename string, src []byte) ([]byte, error) { + fset := token.NewFileSet() + + file, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.AllErrors) + if err != nil { + return nil, err + } + + unused, err := getUnusedImports(file, filename) + if err != nil { + return nil, err + } + for ipath, name := range unused { + astutil.DeleteNamedImport(fset, file, name, ipath) + } + printConfig := &printer.Config{Mode: printer.TabIndent, Tabwidth: 8} + + var buf bytes.Buffer + if err := printConfig.Fprint(&buf, fset, file); err != nil { + return nil, err + } + + return imports.Process(filename, buf.Bytes(), &imports.Options{FormatOnly: true, Comments: true, TabIndent: true, TabWidth: 8}) +} + +func getUnusedImports(file ast.Node, filename string) (map[string]string, error) { + imported := map[string]*ast.ImportSpec{} + used := map[string]bool{} + + abs, err := filepath.Abs(filename) + if err != nil { + return nil, err + } + srcDir := filepath.Dir(abs) + + ast.Walk(visitFn(func(node ast.Node) { + if node == nil { + return + } + switch v := node.(type) { + case *ast.ImportSpec: + if v.Name != nil { + imported[v.Name.Name] = v + break + } + ipath := strings.Trim(v.Path.Value, `"`) + if ipath == "C" { + break + } + + local := importPathToName(ipath, srcDir) + + imported[local] = v + case *ast.SelectorExpr: + xident, ok := v.X.(*ast.Ident) + if !ok { + break + } + if xident.Obj != nil { + // if the parser can resolve it, it's not a package ref + break + } + used[xident.Name] = true + } + }), file) + + for pkg := range used { + delete(imported, pkg) + } + + unusedImport := map[string]string{} + for pkg, is := range imported { + if !used[pkg] && pkg != "_" && pkg != "." { + name := "" + if is.Name != nil { + name = is.Name.Name + } + unusedImport[strings.Trim(is.Path.Value, `"`)] = name + } + } + + return unusedImport, nil +} + +func importPathToName(importPath, srcDir string) (packageName string) { + pkg, err := build.Default.Import(importPath, srcDir, 0) + if err != nil { + return "" + } + + return pkg.Name +} |