diff options
author | Michael Muré <batolettre@gmail.com> | 2018-09-14 12:40:31 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-09-14 12:41:59 +0200 |
commit | b478cd1bcb4756b20f7f4b15fcf81f23e1a60a02 (patch) | |
tree | 8ce232dcab3dd00708f8ba66c334472457e5980d /vendor/github.com/99designs | |
parent | a3fc9abb921f5ce7084d6ab7473442d0b72b1d78 (diff) | |
download | git-bug-b478cd1bcb4756b20f7f4b15fcf81f23e1a60a02.tar.gz |
graphql: update gqlgen to 0.5.1
fix #6
Diffstat (limited to 'vendor/github.com/99designs')
56 files changed, 5160 insertions, 0 deletions
diff --git a/vendor/github.com/99designs/gqlgen/LICENSE b/vendor/github.com/99designs/gqlgen/LICENSE new file mode 100644 index 00000000..18e1b249 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Adam Scarr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/99designs/gqlgen/codegen/build.go b/vendor/github.com/99designs/gqlgen/codegen/build.go new file mode 100644 index 00000000..42dedbf8 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/build.go @@ -0,0 +1,214 @@ +package codegen + +import ( + "fmt" + "go/build" + "go/types" + "os" + + "github.com/pkg/errors" + "golang.org/x/tools/go/loader" +) + +type Build struct { + PackageName string + Objects Objects + Inputs Objects + Interfaces []*Interface + Imports []*Import + QueryRoot *Object + MutationRoot *Object + SubscriptionRoot *Object + SchemaRaw string + SchemaFilename string + 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 +} + +type ServerBuild struct { + PackageName string + Imports []*Import + ExecPackageName string + ResolverPackageName string +} + +// Create a list of models that need to be generated +func (cfg *Config) models() (*ModelBuild, error) { + namedTypes := cfg.buildNamedTypes() + + progLoader := newLoader(namedTypes, true) + 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) + + models, err := cfg.buildModels(namedTypes, prog, imports) + if err != nil { + return nil, err + } + return &ModelBuild{ + 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.Import(cfg.Resolver.ImportPath()) + + prog, err := progLoader.Load() + if err != nil { + return nil, err + } + + 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) + + objects, err := cfg.buildObjects(namedTypes, prog, imports) + if err != nil { + return nil, err + } + + def, _ := findGoType(prog, cfg.Resolver.ImportPath(), cfg.Resolver.Type) + resolverFound := def != nil + + return &ResolverBuild{ + PackageName: cfg.Resolver.Package, + Imports: imports.finalize(), + Objects: objects, + ResolverType: cfg.Resolver.Type, + ResolverFound: resolverFound, + }, nil +} + +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, + } +} + +// bind a schema together with some code to generate a Build +func (cfg *Config) bind() (*Build, error) { + namedTypes := cfg.buildNamedTypes() + + progLoader := newLoader(namedTypes, true) + 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) + + objects, err := cfg.buildObjects(namedTypes, prog, imports) + if err != nil { + return nil, err + } + + inputs, err := cfg.buildInputs(namedTypes, prog, imports) + if err != nil { + return nil, err + } + directives, err := cfg.buildDirectives(namedTypes) + if err != nil { + return nil, err + } + + b := &Build{ + PackageName: cfg.Exec.Package, + Objects: objects, + Interfaces: cfg.buildInterfaces(namedTypes, prog), + Inputs: inputs, + Imports: imports.finalize(), + SchemaRaw: cfg.SchemaStr, + SchemaFilename: cfg.SchemaFilename, + Directives: directives, + } + + if cfg.schema.Query != nil { + b.QueryRoot = b.Objects.ByName(cfg.schema.Query.Name) + } else { + return b, fmt.Errorf("query entry point missing") + } + + if cfg.schema.Mutation != nil { + b.MutationRoot = b.Objects.ByName(cfg.schema.Mutation.Name) + } + + if cfg.schema.Subscription != nil { + b.SubscriptionRoot = b.Objects.ByName(cfg.schema.Subscription.Name) + } + return b, nil +} + +func (cfg *Config) validate() error { + progLoader := newLoader(cfg.buildNamedTypes(), false) + _, err := progLoader.Load() + return err +} + +func newLoader(namedTypes NamedTypes, allowErrors bool) 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) + } + } + return conf +} + +func resolvePkg(pkgName string) (string, error) { + cwd, _ := os.Getwd() + + pkg, err := build.Default.Import(pkgName, cwd, build.FindOnly) + if err != nil { + return "", err + } + + return pkg.ImportPath, nil +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/codegen.go b/vendor/github.com/99designs/gqlgen/codegen/codegen.go new file mode 100644 index 00000000..27873400 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/codegen.go @@ -0,0 +1,174 @@ +package codegen + +import ( + "log" + "os" + "path/filepath" + "regexp" + "syscall" + + "github.com/99designs/gqlgen/codegen/templates" + "github.com/pkg/errors" + "github.com/vektah/gqlparser" + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" +) + +func Generate(cfg Config) error { + if err := cfg.normalize(); err != nil { + return err + } + + _ = syscall.Unlink(cfg.Exec.Filename) + _ = syscall.Unlink(cfg.Model.Filename) + + modelsBuild, err := cfg.models() + if err != nil { + return errors.Wrap(err, "model plan failed") + } + if len(modelsBuild.Models) > 0 || len(modelsBuild.Enums) > 0 { + if err = templates.RenderToFile("models.gotpl", cfg.Model.Filename, modelsBuild); err != nil { + return err + } + + for _, model := range modelsBuild.Models { + modelCfg := cfg.Models[model.GQLType] + modelCfg.Model = cfg.Model.ImportPath() + "." + model.GoType + cfg.Models[model.GQLType] = modelCfg + } + + for _, enum := range modelsBuild.Enums { + modelCfg := cfg.Models[enum.GQLType] + modelCfg.Model = cfg.Model.ImportPath() + "." + enum.GoType + cfg.Models[enum.GQLType] = modelCfg + } + } + + build, err := cfg.bind() + if err != nil { + return errors.Wrap(err, "exec plan failed") + } + + if err := templates.RenderToFile("generated.gotpl", cfg.Exec.Filename, build); err != nil { + return err + } + + if cfg.Resolver.IsDefined() { + if err := generateResolver(cfg); err != nil { + return errors.Wrap(err, "generating resolver failed") + } + } + + if err := cfg.validate(); err != nil { + return errors.Wrap(err, "validation failed") + } + + return nil +} + +func GenerateServer(cfg Config, filename string) error { + if err := cfg.Exec.normalize(); err != nil { + return errors.Wrap(err, "exec") + } + if err := cfg.Resolver.normalize(); err != nil { + return errors.Wrap(err, "resolver") + } + + serverFilename := abs(filename) + serverBuild := cfg.server(filepath.Dir(serverFilename)) + + if _, err := os.Stat(serverFilename); os.IsNotExist(errors.Cause(err)) { + err = templates.RenderToFile("server.gotpl", serverFilename, serverBuild) + if err != nil { + return errors.Wrap(err, "generate server failed") + } + } else { + log.Printf("Skipped server: %s already exists\n", serverFilename) + } + return nil +} + +func generateResolver(cfg Config) error { + resolverBuild, err := cfg.resolver() + if err != nil { + return errors.Wrap(err, "resolver build failed") + } + filename := cfg.Resolver.Filename + + if resolverBuild.ResolverFound { + log.Printf("Skipped resolver: %s.%s already exists\n", cfg.Resolver.ImportPath(), cfg.Resolver.Type) + return nil + } + + if _, err := os.Stat(filename); os.IsNotExist(errors.Cause(err)) { + if err := templates.RenderToFile("resolver.gotpl", filename, resolverBuild); err != nil { + return err + } + } else { + log.Printf("Skipped resolver: %s already exists\n", filename) + } + + return nil +} + +func (cfg *Config) normalize() error { + if err := cfg.Model.normalize(); err != nil { + return errors.Wrap(err, "model") + } + + if err := cfg.Exec.normalize(); err != nil { + return errors.Wrap(err, "exec") + } + + if cfg.Resolver.IsDefined() { + if err := cfg.Resolver.normalize(); err != nil { + return errors.Wrap(err, "resolver") + } + } + + builtins := TypeMap{ + "__Directive": {Model: "github.com/99designs/gqlgen/graphql/introspection.Directive"}, + "__Type": {Model: "github.com/99designs/gqlgen/graphql/introspection.Type"}, + "__Field": {Model: "github.com/99designs/gqlgen/graphql/introspection.Field"}, + "__EnumValue": {Model: "github.com/99designs/gqlgen/graphql/introspection.EnumValue"}, + "__InputValue": {Model: "github.com/99designs/gqlgen/graphql/introspection.InputValue"}, + "__Schema": {Model: "github.com/99designs/gqlgen/graphql/introspection.Schema"}, + "Int": {Model: "github.com/99designs/gqlgen/graphql.Int"}, + "Float": {Model: "github.com/99designs/gqlgen/graphql.Float"}, + "String": {Model: "github.com/99designs/gqlgen/graphql.String"}, + "Boolean": {Model: "github.com/99designs/gqlgen/graphql.Boolean"}, + "ID": {Model: "github.com/99designs/gqlgen/graphql.ID"}, + "Time": {Model: "github.com/99designs/gqlgen/graphql.Time"}, + "Map": {Model: "github.com/99designs/gqlgen/graphql.Map"}, + } + + if cfg.Models == nil { + cfg.Models = TypeMap{} + } + for typeName, entry := range builtins { + if !cfg.Models.Exists(typeName) { + cfg.Models[typeName] = entry + } + } + + var err *gqlerror.Error + cfg.schema, err = gqlparser.LoadSchema(&ast.Source{Name: cfg.SchemaFilename, Input: cfg.SchemaStr}) + if err != nil { + return err + } + return nil +} + +var invalidPackageNameChar = regexp.MustCompile(`[^\w]`) + +func sanitizePackageName(pkg string) string { + return invalidPackageNameChar.ReplaceAllLiteralString(filepath.Base(pkg), "_") +} + +func abs(path string) string { + absPath, err := filepath.Abs(path) + if err != nil { + panic(err) + } + return filepath.ToSlash(absPath) +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/config.go b/vendor/github.com/99designs/gqlgen/codegen/config.go new file mode 100644 index 00000000..db0e467b --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/config.go @@ -0,0 +1,195 @@ +package codegen + +import ( + "fmt" + "go/build" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/99designs/gqlgen/internal/gopath" + "github.com/pkg/errors" + "github.com/vektah/gqlparser/ast" + "gopkg.in/yaml.v2" +) + +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", + Model: PackageConfig{Filename: "models_gen.go"}, + Exec: PackageConfig{Filename: "generated.go"}, + } +} + +// LoadConfigFromDefaultLocations looks for a config file in the current directory, and all parent directories +// walking up the tree. The closest config file will be returned. +func LoadConfigFromDefaultLocations() (*Config, error) { + cfgFile, err := findCfg() + if err != nil { + return nil, err + } + + err = os.Chdir(filepath.Dir(cfgFile)) + if err != nil { + return nil, errors.Wrap(err, "unable to enter config dir") + } + return LoadConfig(cfgFile) +} + +// LoadConfig reads the gqlgen.yml config file +func LoadConfig(filename string) (*Config, error) { + config := DefaultConfig() + + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, errors.Wrap(err, "unable to read config") + } + + if err := yaml.UnmarshalStrict(b, config); err != nil { + return nil, errors.Wrap(err, "unable to parse config") + } + + config.FilePath = filename + + 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"` + + FilePath string `yaml:"-"` + + schema *ast.Schema `yaml:"-"` +} + +type PackageConfig struct { + Filename string `yaml:"filename,omitempty"` + Package string `yaml:"package,omitempty"` + Type string `yaml:"type,omitempty"` +} + +type TypeMapEntry struct { + Model string `yaml:"model"` + Fields map[string]TypeMapField `yaml:"fields,omitempty"` +} + +type TypeMapField struct { + Resolver bool `yaml:"resolver"` + FieldName string `yaml:"fieldName"` +} + +func (c *PackageConfig) normalize() error { + if c.Filename == "" { + return errors.New("Filename is required") + } + c.Filename = abs(c.Filename) + // If Package is not set, first attempt to load the package at the output dir. If that fails + // fallback to just the base dir name of the output filename. + if c.Package == "" { + cwd, _ := os.Getwd() + pkg, _ := build.Default.Import(c.ImportPath(), cwd, 0) + if pkg.Name != "" { + c.Package = pkg.Name + } else { + c.Package = filepath.Base(c.Dir()) + } + } + c.Package = sanitizePackageName(c.Package) + return nil +} + +func (c *PackageConfig) ImportPath() string { + return gopath.MustDir2Import(c.Dir()) +} + +func (c *PackageConfig) Dir() string { + return filepath.Dir(c.Filename) +} + +func (c *PackageConfig) Check() error { + if strings.ContainsAny(c.Package, "./\\") { + return fmt.Errorf("package should be the output package name only, do not include the output filename") + } + if c.Filename != "" && !strings.HasSuffix(c.Filename, ".go") { + return fmt.Errorf("filename should be path to a go source file") + } + return nil +} + +func (c *PackageConfig) IsDefined() bool { + return c.Filename != "" +} + +func (cfg *Config) Check() error { + if err := cfg.Models.Check(); err != nil { + return errors.Wrap(err, "config.models") + } + if err := cfg.Exec.Check(); err != nil { + return errors.Wrap(err, "config.exec") + } + if err := cfg.Model.Check(); err != nil { + return errors.Wrap(err, "config.model") + } + if err := cfg.Resolver.Check(); err != nil { + return errors.Wrap(err, "config.resolver") + } + return nil +} + +type TypeMap map[string]TypeMapEntry + +func (tm TypeMap) Exists(typeName string) bool { + _, ok := tm[typeName] + return ok +} + +func (tm TypeMap) Check() error { + for typeName, entry := range tm { + if strings.LastIndex(entry.Model, ".") < strings.LastIndex(entry.Model, "/") { + return fmt.Errorf("model %s: invalid type specifier \"%s\" - you need to specify a struct to map to", typeName, entry.Model) + } + } + return nil +} + +// findCfg searches for the config file in this directory and all parents up the tree +// looking for the closest match +func findCfg() (string, error) { + dir, err := os.Getwd() + if err != nil { + return "", errors.Wrap(err, "unable to get working dir to findCfg") + } + + cfg := findCfgInDir(dir) + + for cfg == "" && dir != filepath.Dir(dir) { + dir = filepath.Dir(dir) + cfg = findCfgInDir(dir) + } + + if cfg == "" { + return "", os.ErrNotExist + } + + return cfg, nil +} + +func findCfgInDir(dir string) string { + for _, cfgName := range cfgFilenames { + path := filepath.Join(dir, cfgName) + if _, err := os.Stat(path); err == nil { + return path + } + } + return "" +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/directive.go b/vendor/github.com/99designs/gqlgen/codegen/directive.go new file mode 100644 index 00000000..8017da06 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/directive.go @@ -0,0 +1,41 @@ +package codegen + +import ( + "fmt" + "strconv" + "strings" +) + +type Directive struct { + Name string + Args []FieldArgument +} + +func (d *Directive) ArgsFunc() string { + if len(d.Args) == 0 { + return "" + } + + return "dir_" + d.Name + "_args" +} + +func (d *Directive) CallArgs() string { + args := []string{"ctx", "obj", "n"} + + for _, arg := range d.Args { + args = append(args, "args["+strconv.Quote(arg.GQLName)+"].("+arg.Signature()+")") + } + + return strings.Join(args, ", ") +} + +func (d *Directive) Declaration() string { + res := ucFirst(d.Name) + " func(ctx context.Context, obj interface{}, next graphql.Resolver" + + for _, arg := range d.Args { + res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature()) + } + + res += ") (res interface{}, err error)" + return res +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/directive_build.go b/vendor/github.com/99designs/gqlgen/codegen/directive_build.go new file mode 100644 index 00000000..32828841 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/directive_build.go @@ -0,0 +1,49 @@ +package codegen + +import ( + "sort" + + "github.com/pkg/errors" +) + +func (cfg *Config) buildDirectives(types NamedTypes) ([]*Directive, error) { + var directives []*Directive + + for name, dir := range cfg.schema.Directives { + if name == "skip" || name == "include" || name == "deprecated" { + continue + } + + var args []FieldArgument + for _, arg := range dir.Arguments { + newArg := FieldArgument{ + GQLName: arg.Name, + Type: types.getType(arg.Type), + GoVarName: sanitizeArgName(arg.Name), + } + + if !newArg.Type.IsInput && !newArg.Type.IsScalar { + return nil, errors.Errorf("%s cannot be used as argument of directive %s(%s) only input and scalar types are allowed", arg.Type, dir.Name, arg.Name) + } + + if arg.DefaultValue != nil { + var err error + newArg.Default, err = arg.DefaultValue.Value(nil) + 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) + } + + directives = append(directives, &Directive{ + Name: name, + Args: args, + }) + } + + sort.Slice(directives, func(i, j int) bool { return directives[i].Name < directives[j].Name }) + + return directives, nil +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/enum.go b/vendor/github.com/99designs/gqlgen/codegen/enum.go new file mode 100644 index 00000000..7804971c --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/enum.go @@ -0,0 +1,12 @@ +package codegen + +type Enum struct { + *NamedType + Description string + Values []EnumValue +} + +type EnumValue struct { + Name string + Description string +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/enum_build.go b/vendor/github.com/99designs/gqlgen/codegen/enum_build.go new file mode 100644 index 00000000..457d923f --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/enum_build.go @@ -0,0 +1,39 @@ +package codegen + +import ( + "sort" + "strings" + + "github.com/99designs/gqlgen/codegen/templates" + "github.com/vektah/gqlparser/ast" +) + +func (cfg *Config) buildEnums(types NamedTypes) []Enum { + var enums []Enum + + for _, typ := range cfg.schema.Types { + namedType := types[typ.Name] + if typ.Kind != ast.Enum || strings.HasPrefix(typ.Name, "__") || namedType.IsUserDefined { + continue + } + + var values []EnumValue + for _, v := range typ.EnumValues { + values = append(values, EnumValue{v.Name, v.Description}) + } + + enum := Enum{ + NamedType: namedType, + Values: values, + Description: typ.Description, + } + enum.GoType = templates.ToCamel(enum.GQLType) + enums = append(enums, enum) + } + + sort.Slice(enums, func(i, j int) bool { + return enums[i].GQLType < enums[j].GQLType + }) + + return enums +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/import.go b/vendor/github.com/99designs/gqlgen/codegen/import.go new file mode 100644 index 00000000..b511e8f6 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/import.go @@ -0,0 +1,29 @@ +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 new file mode 100644 index 00000000..d634834e --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/import_build.go @@ -0,0 +1,120 @@ +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 new file mode 100644 index 00000000..06ff37a0 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/input_build.go @@ -0,0 +1,96 @@ +package codegen + +import ( + "go/types" + "sort" + + "github.com/pkg/errors" + "github.com/vektah/gqlparser/ast" + "golang.org/x/tools/go/loader" +) + +func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program, imports *Imports) (Objects, error) { + var inputs Objects + + for _, typ := range cfg.schema.Types { + switch typ.Kind { + case ast.InputObject: + input, err := cfg.buildInput(namedTypes, typ) + if err != nil { + return nil, err + } + + def, err := findGoType(prog, input.Package, input.GoType) + if err != nil { + return nil, errors.Wrap(err, "cannot find type") + } + if def != nil { + input.Marshaler = buildInputMarshaler(typ, def) + bindErrs := bindObject(def.Type(), input, imports, cfg.StructTag) + if len(bindErrs) > 0 { + return nil, bindErrs + } + } + + inputs = append(inputs, input) + } + } + + sort.Slice(inputs, func(i, j int) bool { + return inputs[i].GQLType < inputs[j].GQLType + }) + + return inputs, nil +} + +func (cfg *Config) buildInput(types NamedTypes, typ *ast.Definition) (*Object, error) { + obj := &Object{NamedType: types[typ.Name]} + typeEntry, entryExists := cfg.Models[typ.Name] + + for _, field := range typ.Fields { + newField := Field{ + GQLName: field.Name, + Type: types.getType(field.Type), + Object: obj, + } + + if entryExists { + if typeField, ok := typeEntry.Fields[field.Name]; ok { + newField.GoFieldName = typeField.FieldName + } + } + + if field.DefaultValue != nil { + var err error + newField.Default, err = field.DefaultValue.Value(nil) + if err != nil { + return nil, errors.Errorf("default value for %s.%s is not valid: %s", typ.Name, field.Name, err.Error()) + } + } + + if !newField.Type.IsInput && !newField.Type.IsScalar { + return nil, errors.Errorf("%s cannot be used as a field of %s. only input and scalar types are allowed", newField.GQLType, obj.GQLType) + } + + obj.Fields = append(obj.Fields, newField) + + } + return obj, nil +} + +// if user has implemented an UnmarshalGQL method on the input type manually, use it +// otherwise we will generate one. +func buildInputMarshaler(typ *ast.Definition, def types.Object) *Ref { + switch def := def.(type) { + case *types.TypeName: + namedType := def.Type().(*types.Named) + for i := 0; i < namedType.NumMethods(); i++ { + method := namedType.Method(i) + if method.Name() == "UnmarshalGQL" { + return nil + } + } + } + + return &Ref{GoType: typ.Name} +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/interface.go b/vendor/github.com/99designs/gqlgen/codegen/interface.go new file mode 100644 index 00000000..2de0c88a --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/interface.go @@ -0,0 +1,13 @@ +package codegen + +type Interface struct { + *NamedType + + Implementors []InterfaceImplementor +} + +type InterfaceImplementor struct { + ValueReceiver bool + + *NamedType +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/interface_build.go b/vendor/github.com/99designs/gqlgen/codegen/interface_build.go new file mode 100644 index 00000000..9f4a4ff4 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/interface_build.go @@ -0,0 +1,70 @@ +package codegen + +import ( + "fmt" + "go/types" + "os" + "sort" + + "github.com/vektah/gqlparser/ast" + "golang.org/x/tools/go/loader" +) + +func (cfg *Config) buildInterfaces(types NamedTypes, prog *loader.Program) []*Interface { + var interfaces []*Interface + for _, typ := range cfg.schema.Types { + if typ.Kind == ast.Union || typ.Kind == ast.Interface { + interfaces = append(interfaces, cfg.buildInterface(types, typ, prog)) + } + } + + sort.Slice(interfaces, func(i, j int) bool { + return interfaces[i].GQLType < interfaces[j].GQLType + }) + + return interfaces +} + +func (cfg *Config) buildInterface(types NamedTypes, typ *ast.Definition, prog *loader.Program) *Interface { + i := &Interface{NamedType: types[typ.Name]} + + for _, implementor := range cfg.schema.GetPossibleTypes(typ) { + t := types[implementor.Name] + + i.Implementors = append(i.Implementors, InterfaceImplementor{ + NamedType: t, + ValueReceiver: cfg.isValueReceiver(types[typ.Name], t, prog), + }) + } + + return i +} + +func (cfg *Config) isValueReceiver(intf *NamedType, implementor *NamedType, prog *loader.Program) bool { + interfaceType, err := findGoInterface(prog, intf.Package, intf.GoType) + if interfaceType == nil || err != nil { + return true + } + + implementorType, err := findGoNamedType(prog, implementor.Package, implementor.GoType) + if implementorType == nil || err != nil { + 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 +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/model.go b/vendor/github.com/99designs/gqlgen/codegen/model.go new file mode 100644 index 00000000..5ba50337 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/model.go @@ -0,0 +1,16 @@ +package codegen + +type Model struct { + *NamedType + Description string + Fields []ModelField +} + +type ModelField struct { + *Type + GQLName string + GoFieldName string + GoFKName string + GoFKType string + Description string +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/models_build.go b/vendor/github.com/99designs/gqlgen/codegen/models_build.go new file mode 100644 index 00000000..9f98a07d --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/models_build.go @@ -0,0 +1,90 @@ +package codegen + +import ( + "sort" + + "github.com/vektah/gqlparser/ast" + "golang.org/x/tools/go/loader" +) + +func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program, imports *Imports) ([]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) + if err != nil { + return nil, err + } + if obj.Root || obj.IsUserDefined { + continue + } + model = cfg.obj2Model(obj) + case ast.InputObject: + obj, err := cfg.buildInput(types, typ) + if err != nil { + return nil, err + } + if obj.IsUserDefined { + continue + } + model = cfg.obj2Model(obj) + case ast.Interface, ast.Union: + intf := cfg.buildInterface(types, typ, prog) + if intf.IsUserDefined { + continue + } + model = int2Model(intf) + default: + continue + } + model.Description = typ.Description // It's this or change both obj2Model and buildObject + + models = append(models, model) + } + + sort.Slice(models, func(i, j int) bool { + return models[i].GQLType < models[j].GQLType + }) + + return models, nil +} + +func (cfg *Config) obj2Model(obj *Object) Model { + model := Model{ + NamedType: obj.NamedType, + Fields: []ModelField{}, + } + + model.GoType = ucFirst(obj.GQLType) + model.Marshaler = &Ref{GoType: obj.GoType} + + for i := range obj.Fields { + field := &obj.Fields[i] + mf := ModelField{Type: field.Type, GQLName: field.GQLName} + + if field.GoFieldName != "" { + mf.GoFieldName = field.GoFieldName + } else { + mf.GoFieldName = field.GoNameExported() + } + + model.Fields = append(model.Fields, mf) + } + + return model +} + +func int2Model(obj *Interface) Model { + model := Model{ + NamedType: obj.NamedType, + Fields: []ModelField{}, + } + + model.GoType = ucFirst(obj.GQLType) + model.Marshaler = &Ref{GoType: obj.GoType} + + return model +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/object.go b/vendor/github.com/99designs/gqlgen/codegen/object.go new file mode 100644 index 00000000..d9f610f4 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/object.go @@ -0,0 +1,464 @@ +package codegen + +import ( + "bytes" + "fmt" + "strconv" + "strings" + "text/template" + "unicode" + + "github.com/vektah/gqlparser/ast" +) + +type GoFieldType int + +const ( + GoFieldUndefined GoFieldType = iota + GoFieldMethod + GoFieldVariable +) + +type Object struct { + *NamedType + + Fields []Field + Satisfies []string + ResolverInterface *Ref + Root bool + DisableConcurrency bool + Stream bool +} + +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 +} + +type FieldArgument struct { + *Type + + GQLName string // The name of the argument in graphql + GoVarName string // The name of the var in go + Object *Object // A link back to the parent object + Default interface{} // The default value +} + +type Objects []*Object + +func (o *Object) Implementors() string { + satisfiedBy := strconv.Quote(o.GQLType) + for _, s := range o.Satisfies { + satisfiedBy += ", " + strconv.Quote(s) + } + return "[]string{" + satisfiedBy + "}" +} + +func (o *Object) HasResolvers() bool { + for _, f := range o.Fields { + if f.IsResolver() { + return true + } + } + return false +} + +func (o *Object) IsConcurrent() bool { + for _, f := range o.Fields { + if f.IsConcurrent() { + return true + } + } + return false +} + +func (o *Object) IsReserved() bool { + return strings.HasPrefix(o.GQLType, "__") +} + +func (f *Field) IsResolver() bool { + return f.GoFieldName == "" +} + +func (f *Field) IsReserved() bool { + return strings.HasPrefix(f.GQLName, "__") +} + +func (f *Field) IsMethod() bool { + return f.GoFieldType == GoFieldMethod +} + +func (f *Field) IsVariable() bool { + return f.GoFieldType == GoFieldVariable +} + +func (f *Field) IsConcurrent() bool { + return f.IsResolver() && !f.Object.DisableConcurrency +} + +func (f *Field) GoNameExported() string { + return lintName(ucFirst(f.GQLName)) +} + +func (f *Field) GoNameUnexported() string { + return lintName(f.GQLName) +} + +func (f *Field) ShortInvocation() string { + if !f.IsResolver() { + return "" + } + + return fmt.Sprintf("%s().%s(%s)", f.Object.GQLType, f.GoNameExported(), f.CallArgs()) +} + +func (f *Field) ArgsFunc() string { + if len(f.Args) == 0 { + return "" + } + + return "field_" + f.Object.GQLType + "_" + f.GQLName + "_args" +} + +func (f *Field) ResolverType() string { + if !f.IsResolver() { + return "" + } + + return fmt.Sprintf("%s().%s(%s)", f.Object.GQLType, f.GoNameExported(), f.CallArgs()) +} + +func (f *Field) ShortResolverDeclaration() string { + if !f.IsResolver() { + return "" + } + res := fmt.Sprintf("%s(ctx context.Context", f.GoNameExported()) + + if !f.Object.Root { + res += fmt.Sprintf(", obj *%s", f.Object.FullName()) + } + for _, arg := range f.Args { + res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature()) + } + + result := f.Signature() + if f.Object.Stream { + result = "<-chan " + result + } + + res += fmt.Sprintf(") (%s, error)", result) + return res +} + +func (f *Field) ResolverDeclaration() string { + if !f.IsResolver() { + return "" + } + res := fmt.Sprintf("%s_%s(ctx context.Context", f.Object.GQLType, f.GoNameUnexported()) + + if !f.Object.Root { + res += fmt.Sprintf(", obj *%s", f.Object.FullName()) + } + for _, arg := range f.Args { + res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature()) + } + + result := f.Signature() + if f.Object.Stream { + result = "<-chan " + result + } + + res += fmt.Sprintf(") (%s, error)", result) + return res +} + +func (f *Field) ComplexitySignature() string { + res := fmt.Sprintf("func(childComplexity int") + for _, arg := range f.Args { + res += fmt.Sprintf(", %s %s", arg.GoVarName, arg.Signature()) + } + res += ") int" + return res +} + +func (f *Field) ComplexityArgs() string { + var args []string + for _, arg := range f.Args { + args = append(args, "args["+strconv.Quote(arg.GQLName)+"].("+arg.Signature()+")") + } + + return strings.Join(args, ", ") +} + +func (f *Field) CallArgs() string { + var args []string + + if f.IsResolver() { + args = append(args, "ctx") + + if !f.Object.Root { + args = append(args, "obj") + } + } + + for _, arg := range f.Args { + args = append(args, "args["+strconv.Quote(arg.GQLName)+"].("+arg.Signature()+")") + } + + return strings.Join(args, ", ") +} + +// should be in the template, but its recursive and has a bunch of args +func (f *Field) WriteJson() string { + return f.doWriteJson("res", f.Type.Modifiers, f.ASTType, false, 1) +} + +func (f *Field) doWriteJson(val string, remainingMods []string, astType *ast.Type, isPtr bool, depth int) string { + switch { + case len(remainingMods) > 0 && remainingMods[0] == modPtr: + return tpl(` + if {{.val}} == nil { + {{- if .nonNull }} + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + {{- end }} + return graphql.Null + } + {{.next }}`, map[string]interface{}{ + "val": val, + "nonNull": astType.NonNull, + "next": f.doWriteJson(val, remainingMods[1:], astType, true, depth+1), + }) + + case len(remainingMods) > 0 && remainingMods[0] == modList: + if isPtr { + val = "*" + val + } + var arr = "arr" + strconv.Itoa(depth) + var index = "idx" + strconv.Itoa(depth) + var usePtr bool + if len(remainingMods) == 1 && !isPtr { + usePtr = true + } + + return tpl(` + {{.arr}} := make(graphql.Array, len({{.val}})) + {{ if and .top (not .isScalar) }} var wg sync.WaitGroup {{ end }} + {{ if not .isScalar }} + isLen1 := len({{.val}}) == 1 + if !isLen1 { + wg.Add(len({{.val}})) + } + {{ end }} + for {{.index}} := range {{.val}} { + {{- if not .isScalar }} + {{.index}} := {{.index}} + rctx := &graphql.ResolverContext{ + Index: &{{.index}}, + Result: {{ if .usePtr }}&{{end}}{{.val}}[{{.index}}], + } + ctx := graphql.WithResolverContext(ctx, rctx) + f := func({{.index}} int) { + if !isLen1 { + defer wg.Done() + } + {{.arr}}[{{.index}}] = func() graphql.Marshaler { + {{ .next }} + }() + } + if isLen1 { + f({{.index}}) + } else { + go f({{.index}}) + } + {{ else }} + {{.arr}}[{{.index}}] = func() graphql.Marshaler { + {{ .next }} + }() + {{- end}} + } + {{ if and .top (not .isScalar) }} wg.Wait() {{ end }} + return {{.arr}}`, map[string]interface{}{ + "val": val, + "arr": arr, + "index": index, + "top": depth == 1, + "arrayLen": len(val), + "isScalar": f.IsScalar, + "usePtr": usePtr, + "next": f.doWriteJson(val+"["+index+"]", remainingMods[1:], astType.Elem, false, depth+1), + }) + + case f.IsScalar: + if isPtr { + val = "*" + val + } + return f.Marshal(val) + + default: + if !isPtr { + val = "&" + val + } + return tpl(` + return ec._{{.type}}(ctx, field.Selections, {{.val}})`, map[string]interface{}{ + "type": f.GQLType, + "val": val, + }) + } +} + +func (f *FieldArgument) Stream() bool { + return f.Object != nil && f.Object.Stream +} + +func (os Objects) ByName(name string) *Object { + for i, o := range os { + if strings.EqualFold(o.GQLType, name) { + return os[i] + } + } + return nil +} + +func tpl(tpl string, vars map[string]interface{}) string { + b := &bytes.Buffer{} + err := template.Must(template.New("inline").Parse(tpl)).Execute(b, vars) + if err != nil { + panic(err) + } + return b.String() +} + +func ucFirst(s string) string { + if s == "" { + return "" + } + + r := []rune(s) + r[0] = unicode.ToUpper(r[0]) + return string(r) +} + +// copy from https://github.com/golang/lint/blob/06c8688daad7faa9da5a0c2f163a3d14aac986ca/lint.go#L679 + +// lintName returns a different name if it should be different. +func lintName(name string) (should string) { + // Fast path for simple cases: "_" and all lowercase. + if name == "_" { + return name + } + allLower := true + for _, r := range name { + if !unicode.IsLower(r) { + allLower = false + break + } + } + if allLower { + return name + } + + // Split camelCase at any lower->upper transition, and split on underscores. + // Check each word for common initialisms. + runes := []rune(name) + w, i := 0, 0 // index of start of word, scan + for i+1 <= len(runes) { + eow := false // whether we hit the end of a word + if i+1 == len(runes) { + eow = true + } else if runes[i+1] == '_' { + // underscore; shift the remainder forward over any run of underscores + eow = true + n := 1 + for i+n+1 < len(runes) && runes[i+n+1] == '_' { + n++ + } + + // Leave at most one underscore if the underscore is between two digits + if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) { + n-- + } + + copy(runes[i+1:], runes[i+n+1:]) + runes = runes[:len(runes)-n] + } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) { + // lower->non-lower + eow = true + } + i++ + if !eow { + continue + } + + // [w,i) is a word. + word := string(runes[w:i]) + if u := strings.ToUpper(word); commonInitialisms[u] { + // Keep consistent case, which is lowercase only at the start. + if w == 0 && unicode.IsLower(runes[w]) { + u = strings.ToLower(u) + } + // All the common initialisms are ASCII, + // so we can replace the bytes exactly. + copy(runes[w:], []rune(u)) + } else if w > 0 && strings.ToLower(word) == word { + // already all lowercase, and not the first word, so uppercase the first character. + runes[w] = unicode.ToUpper(runes[w]) + } + w = i + } + return string(runes) +} + +// commonInitialisms is a set of common initialisms. +// Only add entries that are highly unlikely to be non-initialisms. +// For instance, "ID" is fine (Freudian code is rare), but "AND" is not. +var commonInitialisms = map[string]bool{ + "ACL": true, + "API": true, + "ASCII": true, + "CPU": true, + "CSS": true, + "DNS": true, + "EOF": true, + "GUID": true, + "HTML": true, + "HTTP": true, + "HTTPS": true, + "ID": true, + "IP": true, + "JSON": true, + "LHS": true, + "QPS": true, + "RAM": true, + "RHS": true, + "RPC": true, + "SLA": true, + "SMTP": true, + "SQL": true, + "SSH": true, + "TCP": true, + "TLS": true, + "TTL": true, + "UDP": true, + "UI": true, + "UID": true, + "UUID": true, + "URI": true, + "URL": true, + "UTF8": true, + "VM": true, + "XML": true, + "XMPP": true, + "XSRF": true, + "XSS": true, +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/object_build.go b/vendor/github.com/99designs/gqlgen/codegen/object_build.go new file mode 100644 index 00000000..ee2b2f1c --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/object_build.go @@ -0,0 +1,181 @@ +package codegen + +import ( + "log" + "sort" + + "github.com/pkg/errors" + "github.com/vektah/gqlparser/ast" + "golang.org/x/tools/go/loader" +) + +func (cfg *Config) buildObjects(types NamedTypes, prog *loader.Program, imports *Imports) (Objects, error) { + var objects Objects + + for _, typ := range cfg.schema.Types { + if typ.Kind != ast.Object { + continue + } + + obj, err := cfg.buildObject(types, typ, imports) + if err != nil { + return nil, err + } + + def, err := findGoType(prog, obj.Package, obj.GoType) + if err != nil { + return nil, err + } + if def != nil { + for _, bindErr := range bindObject(def.Type(), obj, imports, cfg.StructTag) { + log.Println(bindErr.Error()) + log.Println(" Adding resolver method") + } + } + + objects = append(objects, obj) + } + + sort.Slice(objects, func(i, j int) bool { + return objects[i].GQLType < objects[j].GQLType + }) + + return objects, nil +} + +var keywords = []string{ + "break", + "default", + "func", + "interface", + "select", + "case", + "defer", + "go", + "map", + "struct", + "chan", + "else", + "goto", + "package", + "switch", + "const", + "fallthrough", + "if", + "range", + "type", + "continue", + "for", + "import", + "return", + "var", +} + +// sanitizeArgName prevents collisions with go keywords for arguments to resolver functions +func sanitizeArgName(name string) string { + for _, k := range keywords { + if name == k { + return name + "Arg" + } + } + return name +} + +func (cfg *Config) buildObject(types NamedTypes, typ *ast.Definition, imports *Imports) (*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} + + if typ == cfg.schema.Query { + obj.Root = true + } + + if typ == cfg.schema.Mutation { + obj.Root = true + obj.DisableConcurrency = true + } + + if typ == cfg.schema.Subscription { + obj.Root = true + obj.Stream = true + } + + obj.Satisfies = append(obj.Satisfies, typ.Interfaces...) + + 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", + Object: obj, + Description: field.Description, + }) + continue + } + if typ == cfg.schema.Query && field.Name == "__schema" { + 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", + Args: []FieldArgument{ + {GQLName: "name", Type: &Type{types["String"], []string{}, ast.NamedType("String", nil), nil}, Object: &Object{}}, + }, + Object: obj, + }) + continue + } + + var forceResolver bool + var goName string + if entryExists { + if typeField, ok := typeEntry.Fields[field.Name]; ok { + goName = typeField.FieldName + forceResolver = typeField.Resolver + } + } + + var args []FieldArgument + for _, arg := range field.Arguments { + newArg := FieldArgument{ + GQLName: arg.Name, + Type: types.getType(arg.Type), + Object: obj, + GoVarName: sanitizeArgName(arg.Name), + } + + if !newArg.Type.IsInput && !newArg.Type.IsScalar { + return nil, errors.Errorf("%s cannot be used as argument of %s.%s. only input and scalar types are allowed", arg.Type, obj.GQLType, field.Name) + } + + if arg.DefaultValue != nil { + var err error + newArg.Default, err = arg.DefaultValue.Value(nil) + 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) + } + + obj.Fields = append(obj.Fields, Field{ + GQLName: field.Name, + Type: types.getType(field.Type), + Args: args, + Object: obj, + GoFieldName: goName, + ForceResolver: forceResolver, + }) + } + + return obj, nil +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/args.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/args.gotpl new file mode 100644 index 00000000..870a99ed --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/args.gotpl @@ -0,0 +1,13 @@ + args := map[string]interface{}{} + {{- range $i, $arg := . }} + var arg{{$i}} {{$arg.Signature }} + if tmp, ok := rawArgs[{{$arg.GQLName|quote}}]; ok { + var err error + {{$arg.Unmarshal (print "arg" $i) "tmp" }} + if err != nil { + return nil, err + } + } + args[{{$arg.GQLName|quote}}] = arg{{$i}} + {{- end }} + return args, nil diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/data.go b/vendor/github.com/99designs/gqlgen/codegen/templates/data.go new file mode 100644 index 00000000..d168fa31 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/data.go @@ -0,0 +1,13 @@ +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", + "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", + "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", +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl new file mode 100644 index 00000000..b33f2123 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/field.gotpl @@ -0,0 +1,74 @@ +{{ $field := . }} +{{ $object := $field.Object }} + +{{- if $object.Stream }} + func (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler { + {{- if $field.Args }} + rawArgs := field.ArgumentMap(ec.Variables) + args, err := {{ $field.ArgsFunc }}(rawArgs) + if err != nil { + ec.Error(ctx, err) + return nil + } + {{- end }} + ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ + Field: field, + }) + results, err := ec.resolvers.{{ $field.ShortInvocation }} + if err != nil { + ec.Error(ctx, err) + return nil + } + return func() graphql.Marshaler { + res, ok := <-results + if !ok { + return nil + } + var out graphql.OrderedMap + out.Add(field.Alias, func() graphql.Marshaler { {{ $field.WriteJson }} }()) + return &out + } + } +{{ 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 { + {{- if $field.Args }} + rawArgs := field.ArgumentMap(ec.Variables) + args, err := {{ $field.ArgsFunc }}(rawArgs) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + {{- end }} + rctx := &graphql.ResolverContext{ + Object: {{$object.GQLType|quote}}, + Args: {{if $field.Args }}args{{else}}nil{{end}}, + Field: field, + } + ctx = graphql.WithResolverContext(ctx, rctx) + resTmp := ec.FieldMiddleware(ctx, {{if $object.Root}}nil{{else}}obj{{end}}, func(ctx context.Context) (interface{}, error) { + {{- if $field.IsResolver }} + return ec.resolvers.{{ $field.ShortInvocation }} + {{- else if $field.IsMethod }} + {{- if $field.NoErr }} + return {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }}), nil + {{- else }} + return {{$field.GoReceiverName}}.{{$field.GoFieldName}}({{ $field.CallArgs }}) + {{- end }} + {{- else if $field.IsVariable }} + return {{$field.GoReceiverName}}.{{$field.GoFieldName}}, nil + {{- end }} + }) + if resTmp == nil { + {{- if $field.ASTType.NonNull }} + if !ec.HasError(rctx) { + ec.Errorf(ctx, "must not be null") + } + {{- end }} + return graphql.Null + } + res := resTmp.({{$field.Signature}}) + rctx.Result = res + {{ $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 new file mode 100644 index 00000000..8250bc7a --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/generated.gotpl @@ -0,0 +1,263 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package {{ .PackageName }} + +import ( +{{- range $import := .Imports }} + {{- $import.Write }} +{{ end }} +) + +// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. +func NewExecutableSchema(cfg Config) graphql.ExecutableSchema { + return &executableSchema{ + resolvers: cfg.Resolvers, + directives: cfg.Directives, + complexity: cfg.Complexity, + } +} + +type Config struct { + Resolvers ResolverRoot + Directives DirectiveRoot + Complexity ComplexityRoot +} + +type ResolverRoot interface { +{{- range $object := .Objects -}} + {{ if $object.HasResolvers -}} + {{$object.GQLType}}() {{$object.GQLType}}Resolver + {{ end }} +{{- end }} +} + +type DirectiveRoot struct { +{{ range $directive := .Directives }} + {{ $directive.Declaration }} +{{ end }} +} + +type ComplexityRoot struct { +{{ range $object := .Objects }} + {{ if not $object.IsReserved -}} + {{ $object.GQLType|toCamel }} struct { + {{ range $field := $object.Fields -}} + {{ if not $field.IsReserved -}} + {{ $field.GQLName|toCamel }} {{ $field.ComplexitySignature }} + {{ end }} + {{- end }} + } + {{- end }} +{{ end }} +} + +{{ range $object := .Objects -}} + {{ if $object.HasResolvers }} + type {{$object.GQLType}}Resolver interface { + {{ range $field := $object.Fields -}} + {{ $field.ShortResolverDeclaration }} + {{ end }} + } + {{- end }} +{{- end }} + +{{ range $object := .Objects -}} + {{ range $field := $object.Fields -}} + {{ if $field.Args }} + func {{ $field.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) { + {{ template "args.gotpl" $field.Args }} + } + {{ end }} + {{ end }} +{{- end }} + +{{ range $directive := .Directives }} + {{ if $directive.Args }} + func {{ $directive.ArgsFunc }}(rawArgs map[string]interface{}) (map[string]interface{}, error) { + {{ template "args.gotpl" $directive.Args }} + } + {{ end }} +{{ end }} + +type executableSchema struct { + resolvers ResolverRoot + directives DirectiveRoot + complexity ComplexityRoot +} + +func (e *executableSchema) Schema() *ast.Schema { + return parsedSchema +} + +func (e *executableSchema) Complexity(typeName, field string, childComplexity int, rawArgs map[string]interface{}) (int, bool) { + switch typeName + "." + field { + {{ range $object := .Objects }} + {{ if not $object.IsReserved }} + {{ range $field := $object.Fields }} + {{ if not $field.IsReserved }} + case "{{$object.GQLType}}.{{$field.GQLName}}": + if e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}} == nil { + break + } + {{ if $field.Args }} + args, err := {{ $field.ArgsFunc }}(rawArgs) + if err != nil { + return 0, false + } + {{ end }} + return e.complexity.{{$object.GQLType|toCamel}}.{{$field.GQLName|toCamel}}(childComplexity{{if $field.Args}}, {{$field.ComplexityArgs}} {{end}}), true + {{ end }} + {{ end }} + {{ end }} + {{ end }} + } + return 0, false +} + +func (e *executableSchema) Query(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + {{- if .QueryRoot }} + ec := executionContext{graphql.GetRequestContext(ctx), e} + + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._{{.QueryRoot.GQLType}}(ctx, op.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) + + return &graphql.Response{ + Data: buf, + Errors: ec.Errors, + } + {{- else }} + return graphql.ErrorResponse(ctx, "queries are not supported") + {{- end }} +} + +func (e *executableSchema) Mutation(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + {{- if .MutationRoot }} + ec := executionContext{graphql.GetRequestContext(ctx), e} + + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + data := ec._{{.MutationRoot.GQLType}}(ctx, op.SelectionSet) + var buf bytes.Buffer + data.MarshalGQL(&buf) + return buf.Bytes() + }) + + return &graphql.Response{ + Data: buf, + Errors: ec.Errors, + } + {{- else }} + return graphql.ErrorResponse(ctx, "mutations are not supported") + {{- end }} +} + +func (e *executableSchema) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response { + {{- if .SubscriptionRoot }} + ec := executionContext{graphql.GetRequestContext(ctx), e} + + next := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.SelectionSet) + if ec.Errors != nil { + return graphql.OneShot(&graphql.Response{Data: []byte("null"), Errors: ec.Errors}) + } + + var buf bytes.Buffer + return func() *graphql.Response { + buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { + buf.Reset() + data := next() + + if data == nil { + return nil + } + data.MarshalGQL(&buf) + return buf.Bytes() + }) + + if buf == nil { + return nil + } + + return &graphql.Response{ + Data: buf, + Errors: ec.Errors, + } + } + {{- else }} + return graphql.OneShot(graphql.ErrorResponse(ctx, "subscriptions are not supported")) + {{- end }} +} + +type executionContext struct { + *graphql.RequestContext + *executableSchema +} + +{{- range $object := .Objects }} + {{ template "object.gotpl" $object }} + + {{- range $field := $object.Fields }} + {{ template "field.gotpl" $field }} + {{ end }} +{{- end}} + +{{- range $interface := .Interfaces }} + {{ template "interface.gotpl" $interface }} +{{- end }} + +{{- range $input := .Inputs }} + {{ template "input.gotpl" $input }} +{{- end }} + +func (ec *executionContext) FieldMiddleware(ctx context.Context, obj interface{}, next graphql.Resolver) (ret interface{}) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + {{- if .Directives }} + rctx := graphql.GetResolverContext(ctx) + for _, d := range rctx.Field.Definition.Directives { + switch d.Name { + {{- range $directive := .Directives }} + case "{{$directive.Name}}": + if ec.directives.{{$directive.Name|ucFirst}} != nil { + {{- if $directive.Args }} + rawArgs := d.ArgumentMap(ec.Variables) + args, err := {{ $directive.ArgsFunc }}(rawArgs) + if err != nil { + ec.Error(ctx, err) + return nil + } + {{- end }} + n := next + next = func(ctx context.Context) (interface{}, error) { + return ec.directives.{{$directive.Name|ucFirst}}({{$directive.CallArgs}}) + } + } + {{- end }} + } + } + {{- end }} + res, err := ec.ResolverMiddleware(ctx, next) + if err != nil { + ec.Error(ctx, err) + return nil + } + return res +} + +func (ec *executionContext) introspectSchema() *introspection.Schema { + return introspection.WrapSchema(parsedSchema) +} + +func (ec *executionContext) introspectType(name string) *introspection.Type { + return introspection.WrapTypeFromDef(parsedSchema, parsedSchema.Types[name]) +} + +var parsedSchema = gqlparser.MustLoadSchema( + &ast.Source{Name: {{.SchemaFilename|quote}}, Input: {{.SchemaRaw|rawQuote}}}, +) diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/input.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/input.gotpl new file mode 100644 index 00000000..f543608d --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/input.gotpl @@ -0,0 +1,28 @@ + {{- if .IsMarshaled }} + func Unmarshal{{ .GQLType }}(v interface{}) ({{.FullName}}, error) { + var it {{.FullName}} + var asMap = v.(map[string]interface{}) + {{ range $field := .Fields}} + {{- if $field.Default}} + if _, present := asMap[{{$field.GQLName|quote}}] ; !present { + asMap[{{$field.GQLName|quote}}] = {{ $field.Default | dump }} + } + {{- end}} + {{- end }} + + for k, v := range asMap { + switch k { + {{- range $field := .Fields }} + case {{$field.GQLName|quote}}: + var err error + {{ $field.Unmarshal (print "it." $field.GoFieldName) "v" }} + if err != nil { + return it, err + } + {{- end }} + } + } + + return it, nil + } + {{- end }} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/interface.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/interface.gotpl new file mode 100644 index 00000000..84cbe500 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/interface.gotpl @@ -0,0 +1,18 @@ +{{- $interface := . }} + +func (ec *executionContext) _{{$interface.GQLType}}(ctx context.Context, sel ast.SelectionSet, obj *{{$interface.FullName}}) graphql.Marshaler { + switch obj := (*obj).(type) { + case nil: + return graphql.Null + {{- range $implementor := $interface.Implementors }} + {{- if $implementor.ValueReceiver }} + case {{$implementor.FullName}}: + return ec._{{$implementor.GQLType}}(ctx, sel, &obj) + {{- end}} + case *{{$implementor.FullName}}: + return ec._{{$implementor.GQLType}}(ctx, sel, obj) + {{- end }} + default: + panic(fmt.Errorf("unexpected type %T", obj)) + } +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl new file mode 100644 index 00000000..7427d71d --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl @@ -0,0 +1,72 @@ +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. + +package {{ .PackageName }} + +import ( +{{- range $import := .Imports }} + {{- $import.Write }} +{{ end }} +) + +{{ range $model := .Models }} + {{with .Description}} {{.|prefixLines "// "}} {{end}} + {{- if .IsInterface }} + type {{.GoType}} interface {} + {{- else }} + type {{.GoType}} struct { + {{- range $field := .Fields }} + {{- with .Description}} + {{.|prefixLines "// "}} + {{- end}} + {{- if $field.GoFieldName }} + {{ $field.GoFieldName }} {{$field.Signature}} `json:"{{$field.GQLName}}"` + {{- else }} + {{ $field.GoFKName }} {{$field.GoFKType}} + {{- end }} + {{- end }} + } + {{- end }} +{{- end}} + +{{ range $enum := .Enums }} + {{with .Description}}{{.|prefixLines "// "}} {{end}} + type {{.GoType}} string + const ( + {{- range $value := .Values}} + {{- with .Description}} + {{.|prefixLines "// "}} + {{- end}} + {{$enum.GoType}}{{ .Name|toCamel }} {{$enum.GoType}} = {{.Name|quote}} + {{- end }} + ) + + func (e {{.GoType}}) IsValid() bool { + switch e { + case {{ range $index, $element := .Values}}{{if $index}},{{end}}{{ $enum.GoType }}{{ $element.Name|toCamel }}{{end}}: + return true + } + return false + } + + func (e {{.GoType}}) String() string { + return string(e) + } + + func (e *{{.GoType}}) UnmarshalGQL(v interface{}) error { + str, ok := v.(string) + if !ok { + return fmt.Errorf("enums must be strings") + } + + *e = {{.GoType}}(str) + if !e.IsValid() { + return fmt.Errorf("%s is not a valid {{.GQLType}}", str) + } + return nil + } + + func (e {{.GoType}}) MarshalGQL(w io.Writer) { + fmt.Fprint(w, strconv.Quote(e.String())) + } + +{{- end }} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/object.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/object.gotpl new file mode 100644 index 00000000..e98cbe1e --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/object.gotpl @@ -0,0 +1,69 @@ +{{ $object := . }} + +var {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}} + +// nolint: gocyclo, errcheck, gas, goconst +{{- if .Stream }} +func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet) func() graphql.Marshaler { + fields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors) + ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ + Object: {{$object.GQLType|quote}}, + }) + if len(fields) != 1 { + ec.Errorf(ctx, "must subscribe to exactly one stream") + return nil + } + + switch fields[0].Name { + {{- range $field := $object.Fields }} + case "{{$field.GQLName}}": + return ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, fields[0]) + {{- end }} + default: + panic("unknown field " + strconv.Quote(fields[0].Name)) + } +} +{{- else }} +func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel ast.SelectionSet{{if not $object.Root}}, obj *{{$object.FullName}} {{end}}) graphql.Marshaler { + fields := graphql.CollectFields(ctx, sel, {{$object.GQLType|lcFirst}}Implementors) + {{if $object.Root}} + ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ + Object: {{$object.GQLType|quote}}, + }) + {{end}} + + {{if $object.IsConcurrent}} var wg sync.WaitGroup {{end}} + out := graphql.NewOrderedMap(len(fields)) + invalid := false + for i, field := range fields { + out.Keys[i] = field.Alias + + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString({{$object.GQLType|quote}}) + {{- range $field := $object.Fields }} + case "{{$field.GQLName}}": + {{- if $field.IsConcurrent }} + wg.Add(1) + go func(i int, field graphql.CollectedField) { + {{- end }} + out.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, field{{if not $object.Root}}, obj{{end}}) + {{- if $field.ASTType.NonNull }} + if out.Values[i] == graphql.Null { + invalid = true + } + {{- end }} + {{- if $field.IsConcurrent }} + wg.Done() + }(i, field) + {{- end }} + {{- end }} + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + {{if $object.IsConcurrent}} wg.Wait() {{end}} + if invalid { return graphql.Null } + return out +} +{{- end }} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl new file mode 100644 index 00000000..dd8acf24 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/resolver.gotpl @@ -0,0 +1,33 @@ +//go:generate gorunpkg github.com/99designs/gqlgen + +package {{ .PackageName }} + +import ( +{{- range $import := .Imports }} + {{- $import.Write }} +{{ end }} +) + +type {{.ResolverType}} struct {} + +{{ range $object := .Objects -}} + {{- if $object.HasResolvers -}} + func (r *{{$.ResolverType}}) {{$object.GQLType}}() {{ $object.ResolverInterface.FullName }} { + return &{{lcFirst $object.GQLType}}Resolver{r} + } + {{ end -}} +{{ end }} + +{{ range $object := .Objects -}} + {{- if $object.HasResolvers -}} + type {{lcFirst $object.GQLType}}Resolver struct { *Resolver } + + {{ range $field := $object.Fields -}} + {{- if $field.IsResolver -}} + func (r *{{lcFirst $object.GQLType}}Resolver) {{ $field.ShortResolverDeclaration }} { + panic("not implemented") + } + {{ end -}} + {{ end -}} + {{ end -}} +{{ end }} diff --git a/vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl new file mode 100644 index 00000000..f23b30e1 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/server.gotpl @@ -0,0 +1,22 @@ +package main + +import ( +{{- range $import := .Imports }} + {{- $import.Write }} +{{ end }} +) + +const defaultPort = "8080" + +func main() { + port := os.Getenv("PORT") + if port == "" { + port = defaultPort + } + + http.Handle("/", handler.Playground("GraphQL playground", "/query")) + http.Handle("/query", handler.GraphQL({{.ExecPackageName}}.NewExecutableSchema({{.ExecPackageName}}.Config{Resolvers: &{{.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 new file mode 100644 index 00000000..df909cb5 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go @@ -0,0 +1,193 @@ +//go:generate go run ./inliner/inliner.go + +package templates + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strconv" + "strings" + "text/template" + "unicode" + + "log" + + "github.com/pkg/errors" + "golang.org/x/tools/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, + }) + + for filename, data := range data { + _, err := t.New(filename).Parse(data) + if err != nil { + panic(err) + } + } + + buf := &bytes.Buffer{} + err := t.Lookup(name).Execute(buf, tpldata) + if err != nil { + return nil, err + } + + return buf, nil +} + +func ucFirst(s string) string { + if s == "" { + return "" + } + r := []rune(s) + r[0] = unicode.ToUpper(r[0]) + return string(r) +} + +func lcFirst(s string) string { + if s == "" { + return "" + } + + r := []rune(s) + r[0] = unicode.ToLower(r[0]) + return string(r) +} + +func isDelimiter(c rune) bool { + return c == '-' || c == '_' || unicode.IsSpace(c) +} + +func ToCamel(s string) string { + buffer := make([]rune, 0, len(s)) + upper := true + lastWasUpper := false + + for _, c := range s { + if isDelimiter(c) { + upper = true + continue + } + if !lastWasUpper && unicode.IsUpper(c) { + upper = true + } + + if upper { + buffer = append(buffer, unicode.ToUpper(c)) + } else { + buffer = append(buffer, unicode.ToLower(c)) + } + upper = false + lastWasUpper = unicode.IsUpper(c) + } + + return string(buffer) +} + +func rawQuote(s string) string { + return "`" + strings.Replace(s, "`", "`+\"`\"+`", -1) + "`" +} + +func dump(val interface{}) string { + switch val := val.(type) { + case int: + return strconv.Itoa(val) + case int64: + return fmt.Sprintf("%d", val) + case float64: + return fmt.Sprintf("%f", val) + case string: + return strconv.Quote(val) + case bool: + return strconv.FormatBool(val) + case nil: + return "nil" + case []interface{}: + var parts []string + for _, part := range val { + parts = append(parts, dump(part)) + } + return "[]interface{}{" + strings.Join(parts, ",") + "}" + case map[string]interface{}: + buf := bytes.Buffer{} + buf.WriteString("map[string]interface{}{") + var keys []string + for key := range val { + keys = append(keys, key) + } + sort.Strings(keys) + + for _, key := range keys { + data := val[key] + + buf.WriteString(strconv.Quote(key)) + buf.WriteString(":") + buf.WriteString(dump(data)) + buf.WriteString(",") + } + buf.WriteString("}") + return buf.String() + default: + panic(fmt.Errorf("unsupported type %T", val)) + } +} + +func prefixLines(prefix, s string) string { + return prefix + strings.Replace(s, "\n", "\n"+prefix, -1) +} + +func RenderToFile(tpl string, filename string, data interface{}) error { + 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) + + 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 +} + +func write(filename string, b []byte) error { + err := os.MkdirAll(filepath.Dir(filename), 0755) + if err != nil { + return errors.Wrap(err, "failed to create directory") + } + + formatted, err := gofmt(filename, b) + if err != nil { + fmt.Fprintf(os.Stderr, "gofmt failed: %s\n", err.Error()) + formatted = b + } + + err = ioutil.WriteFile(filename, formatted, 0644) + if err != nil { + return errors.Wrapf(err, "failed to write %s", filename) + } + + return nil +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/type.go b/vendor/github.com/99designs/gqlgen/codegen/type.go new file mode 100644 index 00000000..8c53fe55 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/type.go @@ -0,0 +1,170 @@ +package codegen + +import ( + "strconv" + "strings" + + "github.com/vektah/gqlparser/ast" +) + +type NamedTypes map[string]*NamedType + +type NamedType struct { + Ref + IsScalar bool + IsInterface bool + IsInput bool + GQLType string // Name of the graphql type + Marshaler *Ref // If this type has an external marshaler this will be set +} + +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 +} + +type Type struct { + *NamedType + + Modifiers []string + ASTType *ast.Type + AliasedType *Ref +} + +const ( + modList = "[]" + modPtr = "*" +) + +func (t Ref) FullName() string { + return t.PkgDot() + t.GoType +} + +func (t Ref) PkgDot() string { + if t.Import == nil || t.Import.Alias() == "" { + return "" + } + return t.Import.Alias() + "." +} + +func (t Type) Signature() string { + if t.AliasedType != nil { + return strings.Join(t.Modifiers, "") + t.AliasedType.FullName() + } + return strings.Join(t.Modifiers, "") + t.FullName() +} + +func (t Type) FullSignature() string { + pkg := "" + if t.Package != "" { + pkg = t.Package + "." + } + + return strings.Join(t.Modifiers, "") + pkg + t.GoType +} + +func (t Type) IsPtr() bool { + return len(t.Modifiers) > 0 && t.Modifiers[0] == modPtr +} + +func (t *Type) StripPtr() { + if !t.IsPtr() { + return + } + t.Modifiers = t.Modifiers[0 : len(t.Modifiers)-1] +} + +func (t Type) IsSlice() bool { + return len(t.Modifiers) > 0 && t.Modifiers[0] == modList || + len(t.Modifiers) > 1 && t.Modifiers[0] == modPtr && t.Modifiers[1] == modList +} + +func (t NamedType) IsMarshaled() bool { + return t.Marshaler != nil +} + +func (t Type) Unmarshal(result, raw string) string { + return t.unmarshal(result, raw, t.Modifiers, 1) +} + +func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) string { + switch { + case len(remainingMods) > 0 && remainingMods[0] == modPtr: + ptr := "ptr" + strconv.Itoa(depth) + return tpl(`var {{.ptr}} {{.mods}}{{.t.FullName}} + if {{.raw}} != nil { + {{.next}} + {{.result}} = &{{.ptr -}} + } + `, map[string]interface{}{ + "ptr": ptr, + "t": t, + "raw": raw, + "result": result, + "mods": strings.Join(remainingMods[1:], ""), + "next": t.unmarshal(ptr, raw, remainingMods[1:], depth+1), + }) + + case len(remainingMods) > 0 && remainingMods[0] == modList: + var rawIf = "rawIf" + strconv.Itoa(depth) + var index = "idx" + strconv.Itoa(depth) + + return tpl(`var {{.rawSlice}} []interface{} + if {{.raw}} != nil { + if tmp1, ok := {{.raw}}.([]interface{}); ok { + {{.rawSlice}} = tmp1 + } else { + {{.rawSlice}} = []interface{}{ {{.raw}} } + } + } + {{.result}} = make({{.type}}, len({{.rawSlice}})) + for {{.index}} := range {{.rawSlice}} { + {{ .next -}} + }`, map[string]interface{}{ + "raw": raw, + "rawSlice": rawIf, + "index": index, + "result": result, + "type": strings.Join(remainingMods, "") + t.NamedType.FullName(), + "next": t.unmarshal(result+"["+index+"]", rawIf+"["+index+"]", remainingMods[1:], depth+1), + }) + } + + realResult := result + if t.AliasedType != nil { + result = "castTmp" + } + + return tpl(`{{- if .t.AliasedType }} + var castTmp {{.t.FullName}} + {{ end }} + {{- if eq .t.GoType "map[string]interface{}" }} + {{- .result }} = {{.raw}}.(map[string]interface{}) + {{- else if .t.Marshaler }} + {{- .result }}, err = {{ .t.Marshaler.PkgDot }}Unmarshal{{.t.Marshaler.GoType}}({{.raw}}) + {{- else -}} + err = (&{{.result}}).UnmarshalGQL({{.raw}}) + {{- end }} + {{- if .t.AliasedType }} + {{ .realResult }} = {{.t.AliasedType.FullName}}(castTmp) + {{- end }}`, map[string]interface{}{ + "realResult": realResult, + "result": result, + "raw": raw, + "t": t, + }) +} + +func (t Type) Marshal(val string) string { + if t.AliasedType != nil { + val = t.GoType + "(" + val + ")" + } + + if t.Marshaler != nil { + return "return " + t.Marshaler.PkgDot() + "Marshal" + t.Marshaler.GoType + "(" + val + ")" + } + + return "return " + val +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/type_build.go b/vendor/github.com/99designs/gqlgen/codegen/type_build.go new file mode 100644 index 00000000..f0ec6785 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/type_build.go @@ -0,0 +1,101 @@ +package codegen + +import ( + "go/types" + "strings" + + "github.com/vektah/gqlparser/ast" + "golang.org/x/tools/go/loader" +) + +// namedTypeFromSchema objects for every graphql type, including scalars. There should only be one instance of Type for each thing +func (cfg *Config) buildNamedTypes() NamedTypes { + types := map[string]*NamedType{} + for _, schemaType := range cfg.schema.Types { + t := namedTypeFromSchema(schemaType) + + if userEntry, ok := cfg.Models[t.GQLType]; ok && userEntry.Model != "" { + t.IsUserDefined = true + t.Package, t.GoType = pkgAndType(userEntry.Model) + } else if t.IsScalar { + t.Package = "github.com/99designs/gqlgen/graphql" + t.GoType = "String" + } + + types[t.GQLType] = t + } + return types +} + +func (cfg *Config) bindTypes(imports *Imports, namedTypes NamedTypes, destDir string, prog *loader.Program) { + for _, t := range namedTypes { + if t.Package == "" { + continue + } + + def, _ := findGoType(prog, t.Package, "Marshal"+t.GoType) + switch def := def.(type) { + case *types.Func: + sig := def.Type().(*types.Signature) + cpy := t.Ref + t.Marshaler = &cpy + + t.Package, t.GoType = pkgAndType(sig.Params().At(0).Type().String()) + t.Import = imports.add(t.Package) + } + } +} + +// namedTypeFromSchema objects for every graphql type, including primitives. +// don't recurse into object fields or interfaces yet, lets make sure we have collected everything first. +func namedTypeFromSchema(schemaType *ast.Definition) *NamedType { + switch schemaType.Kind { + case ast.Scalar, ast.Enum: + return &NamedType{GQLType: schemaType.Name, IsScalar: true} + case ast.Interface, ast.Union: + return &NamedType{GQLType: schemaType.Name, IsInterface: true} + case ast.InputObject: + return &NamedType{GQLType: schemaType.Name, IsInput: true} + default: + return &NamedType{GQLType: schemaType.Name} + } +} + +// take a string in the form github.com/package/blah.Type and split it into package and type +func pkgAndType(name string) (string, string) { + parts := strings.Split(name, ".") + if len(parts) == 1 { + return "", name + } + + return normalizeVendor(strings.Join(parts[:len(parts)-1], ".")), parts[len(parts)-1] +} + +func (n NamedTypes) getType(t *ast.Type) *Type { + orig := t + var modifiers []string + for { + if t.Elem != nil { + modifiers = append(modifiers, modList) + t = t.Elem + } else { + if !t.NonNull { + modifiers = append(modifiers, modPtr) + } + if n[t.NamedType] == nil { + panic("missing type " + t.NamedType) + } + res := &Type{ + NamedType: n[t.NamedType], + Modifiers: modifiers, + ASTType: orig, + } + + if res.IsInterface { + res.StripPtr() + } + + return res + } + } +} diff --git a/vendor/github.com/99designs/gqlgen/codegen/util.go b/vendor/github.com/99designs/gqlgen/codegen/util.go new file mode 100644 index 00000000..1849f100 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/codegen/util.go @@ -0,0 +1,367 @@ +package codegen + +import ( + "fmt" + "go/types" + "reflect" + "regexp" + "strings" + + "github.com/pkg/errors" + "golang.org/x/tools/go/loader" +) + +func findGoType(prog *loader.Program, pkgName string, typeName string) (types.Object, error) { + if pkgName == "" { + return nil, nil + } + fullName := typeName + if pkgName != "" { + fullName = pkgName + "." + typeName + } + + pkgName, err := resolvePkg(pkgName) + if err != nil { + return nil, errors.Errorf("unable to resolve package for %s: %s\n", fullName, err.Error()) + } + + pkg := prog.Imported[pkgName] + if pkg == nil { + return nil, errors.Errorf("required package was not loaded: %s", fullName) + } + + for astNode, def := range pkg.Defs { + if astNode.Name != typeName || def.Parent() == nil || def.Parent() != pkg.Pkg.Scope() { + continue + } + + return def, nil + } + + return nil, errors.Errorf("unable to find type %s\n", fullName) +} + +func findGoNamedType(prog *loader.Program, pkgName string, typeName string) (*types.Named, error) { + def, err := findGoType(prog, pkgName, typeName) + if err != nil { + return nil, err + } + if def == nil { + return nil, nil + } + + namedType, ok := def.Type().(*types.Named) + if !ok { + return nil, errors.Errorf("expected %s to be a named type, instead found %T\n", typeName, def.Type()) + } + + return namedType, nil +} + +func findGoInterface(prog *loader.Program, pkgName string, typeName string) (*types.Interface, error) { + namedType, err := findGoNamedType(prog, pkgName, typeName) + if err != nil { + return nil, err + } + if namedType == nil { + return nil, nil + } + + underlying, ok := namedType.Underlying().(*types.Interface) + if !ok { + return nil, errors.Errorf("expected %s to be a named interface, instead found %s", typeName, namedType.String()) + } + + return underlying, nil +} + +func findMethod(typ *types.Named, name string) *types.Func { + for i := 0; i < typ.NumMethods(); i++ { + method := typ.Method(i) + if !method.Exported() { + continue + } + + if strings.EqualFold(method.Name(), name) { + return method + } + } + + if s, ok := typ.Underlying().(*types.Struct); ok { + for i := 0; i < s.NumFields(); i++ { + field := s.Field(i) + if !field.Anonymous() { + continue + } + + if named, ok := field.Type().(*types.Named); ok { + if f := findMethod(named, name); f != nil { + return f + } + } + } + } + + return nil +} + +// 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 +// 2. Field in an embedded struct +// 3. Actual Field name +func findField(typ *types.Struct, name, structTag string) (*types.Var, error) { + var foundField *types.Var + foundFieldWasTag := false + + for i := 0; i < typ.NumFields(); i++ { + field := typ.Field(i) + + if structTag != "" { + tags := reflect.StructTag(typ.Tag(i)) + if val, ok := tags.Lookup(structTag); ok { + if strings.EqualFold(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) + } + + foundField = field + foundFieldWasTag = true + } + } + } + + 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 + } + } + + if named, ok := field.Type().Underlying().(*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 + } + } + } + + if !field.Exported() { + continue + } + + if strings.EqualFold(field.Name(), name) && foundField == nil { + foundField = field + } + } + + if foundField == nil { + return nil, fmt.Errorf("no field named %s", name) + } + + return foundField, nil +} + +type BindError struct { + object *Object + field *Field + typ types.Type + methodErr error + varErr error +} + +func (b BindError) Error() string { + return fmt.Sprintf( + "Unable to bind %s.%s to %s\n %s\n %s", + b.object.GQLType, + b.field.GQLName, + b.typ.String(), + b.methodErr.Error(), + b.varErr.Error(), + ) +} + +type BindErrors []BindError + +func (b BindErrors) Error() string { + var errs []string + for _, err := range b { + errs = append(errs, err.Error()) + } + return strings.Join(errs, "\n\n") +} + +func bindObject(t types.Type, object *Object, imports *Imports, structTag string) BindErrors { + var errs BindErrors + for i := range object.Fields { + field := &object.Fields[i] + + if field.ForceResolver { + continue + } + + // first try binding to a method + methodErr := bindMethod(imports, t, field) + if methodErr == nil { + continue + } + + // otherwise try binding to a var + varErr := bindVar(imports, t, field, structTag) + + if varErr != nil { + errs = append(errs, BindError{ + object: object, + typ: t, + field: field, + varErr: varErr, + methodErr: methodErr, + }) + } + } + return errs +} + +func bindMethod(imports *Imports, t types.Type, field *Field) error { + namedType, ok := t.(*types.Named) + if !ok { + return fmt.Errorf("not a named type") + } + + goName := field.GQLName + if field.GoFieldName != "" { + goName = field.GoFieldName + } + method := findMethod(namedType, goName) + if method == nil { + return fmt.Errorf("no method named %s", field.GQLName) + } + sig := method.Type().(*types.Signature) + + if sig.Results().Len() == 1 { + field.NoErr = true + } else if sig.Results().Len() != 2 { + return fmt.Errorf("method has wrong number of args") + } + newArgs, err := matchArgs(field, sig.Params()) + if err != nil { + return err + } + + result := sig.Results().At(0) + if err := validateTypeBinding(imports, field, result.Type()); err != nil { + return errors.Wrap(err, "method has wrong return type") + } + + // success, args and return type match. Bind to method + field.GoFieldType = GoFieldMethod + field.GoReceiverName = "obj" + field.GoFieldName = method.Name() + field.Args = newArgs + return nil +} + +func bindVar(imports *Imports, t types.Type, field *Field, structTag string) error { + underlying, ok := t.Underlying().(*types.Struct) + if !ok { + return fmt.Errorf("not a struct") + } + + goName := field.GQLName + if field.GoFieldName != "" { + goName = field.GoFieldName + } + structField, err := findField(underlying, goName, structTag) + if err != nil { + return err + } + + if err := validateTypeBinding(imports, field, structField.Type()); err != nil { + return errors.Wrap(err, "field has wrong type") + } + + // success, bind to var + field.GoFieldType = GoFieldVariable + field.GoReceiverName = "obj" + field.GoFieldName = structField.Name() + return nil +} + +func matchArgs(field *Field, params *types.Tuple) ([]FieldArgument, error) { + var newArgs []FieldArgument + +nextArg: + for j := 0; j < params.Len(); j++ { + param := params.At(j) + for _, oldArg := range field.Args { + if strings.EqualFold(oldArg.GQLName, param.Name()) { + if !field.ForceResolver { + oldArg.Type.Modifiers = modifiersFromGoType(param.Type()) + } + newArgs = append(newArgs, oldArg) + continue nextArg + } + } + + // no matching arg found, abort + return nil, fmt.Errorf("arg %s not found on method", param.Name()) + } + return newArgs, nil +} + +func validateTypeBinding(imports *Imports, field *Field, goType types.Type) error { + gqlType := normalizeVendor(field.Type.FullSignature()) + goTypeStr := normalizeVendor(goType.String()) + + if goTypeStr == gqlType || "*"+goTypeStr == gqlType || 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 { + field.Type.Modifiers = modifiersFromGoType(goType) + pkg, typ := pkgAndType(goType.String()) + imp := imports.findByPath(pkg) + field.AliasedType = &Ref{GoType: typ, Import: imp} + return nil + } + + return fmt.Errorf("%s is not compatible with %s", gqlType, goTypeStr) +} + +func modifiersFromGoType(t types.Type) []string { + var modifiers []string + for { + switch val := t.(type) { + case *types.Pointer: + modifiers = append(modifiers, modPtr) + t = val.Elem() + case *types.Array: + modifiers = append(modifiers, modList) + t = val.Elem() + case *types.Slice: + modifiers = append(modifiers, modList) + t = val.Elem() + default: + return modifiers + } + } +} + +var modsRegex = regexp.MustCompile(`^(\*|\[\])*`) + +func normalizeVendor(pkg string) string { + modifiers := modsRegex.FindAllString(pkg, 1)[0] + pkg = strings.TrimPrefix(pkg, modifiers) + parts := strings.Split(pkg, "/vendor/") + return modifiers + parts[len(parts)-1] +} diff --git a/vendor/github.com/99designs/gqlgen/complexity/complexity.go b/vendor/github.com/99designs/gqlgen/complexity/complexity.go new file mode 100644 index 00000000..d5b46bf4 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/complexity/complexity.go @@ -0,0 +1,104 @@ +package complexity + +import ( + "github.com/99designs/gqlgen/graphql" + "github.com/vektah/gqlparser/ast" +) + +func Calculate(es graphql.ExecutableSchema, op *ast.OperationDefinition, vars map[string]interface{}) int { + walker := complexityWalker{ + es: es, + schema: es.Schema(), + vars: vars, + } + return walker.selectionSetComplexity(op.SelectionSet) +} + +type complexityWalker struct { + es graphql.ExecutableSchema + schema *ast.Schema + vars map[string]interface{} +} + +func (cw complexityWalker) selectionSetComplexity(selectionSet ast.SelectionSet) int { + var complexity int + for _, selection := range selectionSet { + switch s := selection.(type) { + case *ast.Field: + fieldDefinition := cw.schema.Types[s.Definition.Type.Name()] + var childComplexity int + switch fieldDefinition.Kind { + case ast.Object, ast.Interface, ast.Union: + childComplexity = cw.selectionSetComplexity(s.SelectionSet) + } + + args := s.ArgumentMap(cw.vars) + var fieldComplexity int + if s.ObjectDefinition.Kind == ast.Interface { + fieldComplexity = cw.interfaceFieldComplexity(s.ObjectDefinition, s.Name, childComplexity, args) + } else { + fieldComplexity = cw.fieldComplexity(s.ObjectDefinition.Name, s.Name, childComplexity, args) + } + complexity = safeAdd(complexity, fieldComplexity) + + case *ast.FragmentSpread: + complexity = safeAdd(complexity, cw.selectionSetComplexity(s.Definition.SelectionSet)) + + case *ast.InlineFragment: + complexity = safeAdd(complexity, cw.selectionSetComplexity(s.SelectionSet)) + } + } + return complexity +} + +func (cw complexityWalker) interfaceFieldComplexity(def *ast.Definition, field string, childComplexity int, args map[string]interface{}) int { + // Interfaces don't have their own separate field costs, so they have to assume the worst case. + // We iterate over all implementors and choose the most expensive one. + maxComplexity := 0 + implementors := cw.schema.GetPossibleTypes(def) + for _, t := range implementors { + fieldComplexity := cw.fieldComplexity(t.Name, field, childComplexity, args) + if fieldComplexity > maxComplexity { + maxComplexity = fieldComplexity + } + } + return maxComplexity +} + +func (cw complexityWalker) fieldComplexity(object, field string, childComplexity int, args map[string]interface{}) int { + if customComplexity, ok := cw.es.Complexity(object, field, childComplexity, args); ok && customComplexity >= childComplexity { + return customComplexity + } + // default complexity calculation + return safeAdd(1, childComplexity) +} + +const maxInt = int(^uint(0) >> 1) + +// safeAdd is a saturating add of a and b that ignores negative operands. +// If a + b would overflow through normal Go addition, +// it returns the maximum integer value instead. +// +// Adding complexities with this function prevents attackers from intentionally +// overflowing the complexity calculation to allow overly-complex queries. +// +// It also helps mitigate the impact of custom complexities that accidentally +// return negative values. +func safeAdd(a, b int) int { + // Ignore negative operands. + if a < 0 { + if b < 0 { + return 1 + } + return b + } else if b < 0 { + return a + } + + c := a + b + if c < a { + // Set c to maximum integer instead of overflowing. + c = maxInt + } + return c +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/bool.go b/vendor/github.com/99designs/gqlgen/graphql/bool.go new file mode 100644 index 00000000..7053bbca --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/bool.go @@ -0,0 +1,30 @@ +package graphql + +import ( + "fmt" + "io" + "strings" +) + +func MarshalBoolean(b bool) Marshaler { + return WriterFunc(func(w io.Writer) { + if b { + w.Write(trueLit) + } else { + w.Write(falseLit) + } + }) +} + +func UnmarshalBoolean(v interface{}) (bool, error) { + switch v := v.(type) { + case string: + return "true" == strings.ToLower(v), nil + case int: + return v != 0, nil + case bool: + return v, nil + default: + return false, fmt.Errorf("%T is not a bool", v) + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/context.go b/vendor/github.com/99designs/gqlgen/graphql/context.go new file mode 100644 index 00000000..6baee83c --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/context.go @@ -0,0 +1,178 @@ +package graphql + +import ( + "context" + "fmt" + "sync" + + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" +) + +type Resolver func(ctx context.Context) (res interface{}, err error) +type FieldMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error) +type RequestMiddleware func(ctx context.Context, next func(ctx context.Context) []byte) []byte + +type RequestContext struct { + RawQuery string + Variables map[string]interface{} + Doc *ast.QueryDocument + // ErrorPresenter will be used to generate the error + // message from errors given to Error(). + ErrorPresenter ErrorPresenterFunc + Recover RecoverFunc + ResolverMiddleware FieldMiddleware + DirectiveMiddleware FieldMiddleware + RequestMiddleware RequestMiddleware + + errorsMu sync.Mutex + Errors gqlerror.List +} + +func DefaultResolverMiddleware(ctx context.Context, next Resolver) (res interface{}, err error) { + return next(ctx) +} + +func DefaultDirectiveMiddleware(ctx context.Context, next Resolver) (res interface{}, err error) { + return next(ctx) +} + +func DefaultRequestMiddleware(ctx context.Context, next func(ctx context.Context) []byte) []byte { + return next(ctx) +} + +func NewRequestContext(doc *ast.QueryDocument, query string, variables map[string]interface{}) *RequestContext { + return &RequestContext{ + Doc: doc, + RawQuery: query, + Variables: variables, + ResolverMiddleware: DefaultResolverMiddleware, + DirectiveMiddleware: DefaultDirectiveMiddleware, + RequestMiddleware: DefaultRequestMiddleware, + Recover: DefaultRecover, + ErrorPresenter: DefaultErrorPresenter, + } +} + +type key string + +const ( + request key = "request_context" + resolver key = "resolver_context" +) + +func GetRequestContext(ctx context.Context) *RequestContext { + val := ctx.Value(request) + if val == nil { + return nil + } + + return val.(*RequestContext) +} + +func WithRequestContext(ctx context.Context, rc *RequestContext) context.Context { + return context.WithValue(ctx, request, rc) +} + +type ResolverContext struct { + Parent *ResolverContext + // The name of the type this field belongs to + Object string + // These are the args after processing, they can be mutated in middleware to change what the resolver will get. + Args map[string]interface{} + // The raw field + Field CollectedField + // The index of array in path. + Index *int + // The result object of resolver + Result interface{} +} + +func (r *ResolverContext) Path() []interface{} { + var path []interface{} + for it := r; it != nil; it = it.Parent { + if it.Index != nil { + path = append(path, *it.Index) + } else if it.Field.Field != nil { + path = append(path, it.Field.Alias) + } + } + + // because we are walking up the chain, all the elements are backwards, do an inplace flip. + for i := len(path)/2 - 1; i >= 0; i-- { + opp := len(path) - 1 - i + path[i], path[opp] = path[opp], path[i] + } + + return path +} + +func GetResolverContext(ctx context.Context) *ResolverContext { + val, _ := ctx.Value(resolver).(*ResolverContext) + return val +} + +func WithResolverContext(ctx context.Context, rc *ResolverContext) context.Context { + rc.Parent = GetResolverContext(ctx) + return context.WithValue(ctx, resolver, rc) +} + +// This is just a convenient wrapper method for CollectFields +func CollectFieldsCtx(ctx context.Context, satisfies []string) []CollectedField { + resctx := GetResolverContext(ctx) + return CollectFields(ctx, resctx.Field.Selections, satisfies) +} + +// Errorf sends an error string to the client, passing it through the formatter. +func (c *RequestContext) Errorf(ctx context.Context, format string, args ...interface{}) { + c.errorsMu.Lock() + defer c.errorsMu.Unlock() + + c.Errors = append(c.Errors, c.ErrorPresenter(ctx, fmt.Errorf(format, args...))) +} + +// Error sends an error to the client, passing it through the formatter. +func (c *RequestContext) Error(ctx context.Context, err error) { + c.errorsMu.Lock() + defer c.errorsMu.Unlock() + + c.Errors = append(c.Errors, c.ErrorPresenter(ctx, err)) +} + +// HasError returns true if the current field has already errored +func (c *RequestContext) HasError(rctx *ResolverContext) bool { + c.errorsMu.Lock() + defer c.errorsMu.Unlock() + path := rctx.Path() + + for _, err := range c.Errors { + if equalPath(err.Path, path) { + return true + } + } + return false +} + +func equalPath(a []interface{}, b []interface{}) bool { + if len(a) != len(b) { + return false + } + + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + + return true +} + +// AddError is a convenience method for adding an error to the current response +func AddError(ctx context.Context, err error) { + GetRequestContext(ctx).Error(ctx, err) +} + +// AddErrorf is a convenience method for adding an error to the current response +func AddErrorf(ctx context.Context, format string, args ...interface{}) { + GetRequestContext(ctx).Errorf(ctx, format, args...) +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/error.go b/vendor/github.com/99designs/gqlgen/graphql/error.go new file mode 100644 index 00000000..7f161a43 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/error.go @@ -0,0 +1,31 @@ +package graphql + +import ( + "context" + + "github.com/vektah/gqlparser/gqlerror" +) + +type ErrorPresenterFunc func(context.Context, error) *gqlerror.Error + +type ExtendedError interface { + Extensions() map[string]interface{} +} + +func DefaultErrorPresenter(ctx context.Context, err error) *gqlerror.Error { + if gqlerr, ok := err.(*gqlerror.Error); ok { + gqlerr.Path = GetResolverContext(ctx).Path() + return gqlerr + } + + var extensions map[string]interface{} + if ee, ok := err.(ExtendedError); ok { + extensions = ee.Extensions() + } + + return &gqlerror.Error{ + Message: err.Error(), + Path: GetResolverContext(ctx).Path(), + Extensions: extensions, + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/exec.go b/vendor/github.com/99designs/gqlgen/graphql/exec.go new file mode 100644 index 00000000..9beb3149 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/exec.go @@ -0,0 +1,135 @@ +package graphql + +import ( + "context" + "fmt" + + "github.com/vektah/gqlparser/ast" +) + +type ExecutableSchema interface { + Schema() *ast.Schema + + Complexity(typeName, fieldName string, childComplexity int, args map[string]interface{}) (int, bool) + Query(ctx context.Context, op *ast.OperationDefinition) *Response + Mutation(ctx context.Context, op *ast.OperationDefinition) *Response + Subscription(ctx context.Context, op *ast.OperationDefinition) func() *Response +} + +func CollectFields(ctx context.Context, selSet ast.SelectionSet, satisfies []string) []CollectedField { + return collectFields(GetRequestContext(ctx), selSet, satisfies, map[string]bool{}) +} + +func collectFields(reqCtx *RequestContext, selSet ast.SelectionSet, satisfies []string, visited map[string]bool) []CollectedField { + var groupedFields []CollectedField + + for _, sel := range selSet { + switch sel := sel.(type) { + case *ast.Field: + if !shouldIncludeNode(sel.Directives, reqCtx.Variables) { + continue + } + f := getOrCreateField(&groupedFields, sel.Alias, func() CollectedField { + return CollectedField{Field: sel} + }) + + f.Selections = append(f.Selections, sel.SelectionSet...) + case *ast.InlineFragment: + if !shouldIncludeNode(sel.Directives, reqCtx.Variables) || !instanceOf(sel.TypeCondition, satisfies) { + continue + } + for _, childField := range collectFields(reqCtx, sel.SelectionSet, satisfies, visited) { + f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField }) + f.Selections = append(f.Selections, childField.Selections...) + } + + case *ast.FragmentSpread: + if !shouldIncludeNode(sel.Directives, reqCtx.Variables) { + continue + } + fragmentName := sel.Name + if _, seen := visited[fragmentName]; seen { + continue + } + visited[fragmentName] = true + + fragment := reqCtx.Doc.Fragments.ForName(fragmentName) + if fragment == nil { + // should never happen, validator has already run + panic(fmt.Errorf("missing fragment %s", fragmentName)) + } + + if !instanceOf(fragment.TypeCondition, satisfies) { + continue + } + + for _, childField := range collectFields(reqCtx, fragment.SelectionSet, satisfies, visited) { + f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField }) + f.Selections = append(f.Selections, childField.Selections...) + } + + default: + panic(fmt.Errorf("unsupported %T", sel)) + } + } + + return groupedFields +} + +type CollectedField struct { + *ast.Field + + Selections ast.SelectionSet +} + +func instanceOf(val string, satisfies []string) bool { + for _, s := range satisfies { + if val == s { + return true + } + } + return false +} + +func getOrCreateField(c *[]CollectedField, name string, creator func() CollectedField) *CollectedField { + for i, cf := range *c { + if cf.Alias == name { + return &(*c)[i] + } + } + + f := creator() + + *c = append(*c, f) + return &(*c)[len(*c)-1] +} + +func shouldIncludeNode(directives ast.DirectiveList, variables map[string]interface{}) bool { + skip, include := false, true + + if d := directives.ForName("skip"); d != nil { + skip = resolveIfArgument(d, variables) + } + + if d := directives.ForName("include"); d != nil { + include = resolveIfArgument(d, variables) + } + + return !skip && include +} + +func resolveIfArgument(d *ast.Directive, variables map[string]interface{}) bool { + arg := d.Arguments.ForName("if") + if arg == nil { + panic(fmt.Sprintf("%s: argument 'if' not defined", d.Name)) + } + value, err := arg.Value.Value(variables) + if err != nil { + panic(err) + } + ret, ok := value.(bool) + if !ok { + panic(fmt.Sprintf("%s: argument 'if' is not a boolean", d.Name)) + } + return ret +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/float.go b/vendor/github.com/99designs/gqlgen/graphql/float.go new file mode 100644 index 00000000..d204335c --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/float.go @@ -0,0 +1,31 @@ +package graphql + +import ( + "encoding/json" + "fmt" + "io" + "strconv" +) + +func MarshalFloat(f float64) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, fmt.Sprintf("%f", f)) + }) +} + +func UnmarshalFloat(v interface{}) (float64, error) { + switch v := v.(type) { + case string: + return strconv.ParseFloat(v, 64) + case int: + return float64(v), nil + case int64: + return float64(v), nil + case float64: + return v, nil + case json.Number: + return strconv.ParseFloat(string(v), 64) + default: + return 0, fmt.Errorf("%T is not an float", v) + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/id.go b/vendor/github.com/99designs/gqlgen/graphql/id.go new file mode 100644 index 00000000..a5a7960f --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/id.go @@ -0,0 +1,36 @@ +package graphql + +import ( + "encoding/json" + "fmt" + "io" + "strconv" +) + +func MarshalID(s string) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.Quote(s)) + }) +} +func UnmarshalID(v interface{}) (string, error) { + switch v := v.(type) { + case string: + return v, nil + case json.Number: + return string(v), nil + case int: + return strconv.Itoa(v), nil + case float64: + return fmt.Sprintf("%f", v), nil + case bool: + if v { + return "true", nil + } else { + return "false", nil + } + case nil: + return "null", nil + default: + return "", fmt.Errorf("%T is not a string", v) + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/int.go b/vendor/github.com/99designs/gqlgen/graphql/int.go new file mode 100644 index 00000000..ff87574c --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/int.go @@ -0,0 +1,29 @@ +package graphql + +import ( + "encoding/json" + "fmt" + "io" + "strconv" +) + +func MarshalInt(i int) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.Itoa(i)) + }) +} + +func UnmarshalInt(v interface{}) (int, error) { + switch v := v.(type) { + case string: + return strconv.Atoi(v) + case int: + return v, nil + case int64: + return int(v), nil + case json.Number: + return strconv.Atoi(string(v)) + default: + return 0, fmt.Errorf("%T is not an int", v) + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go new file mode 100644 index 00000000..baff882e --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go @@ -0,0 +1,58 @@ +// introspection implements the spec defined in https://github.com/facebook/graphql/blob/master/spec/Section%204%20--%20Introspection.md#schema-introspection +package introspection + +import "github.com/vektah/gqlparser/ast" + +type ( + Directive struct { + Name string + Description string + Locations []string + Args []InputValue + } + + EnumValue struct { + Name string + Description string + IsDeprecated bool + DeprecationReason string + } + + Field struct { + Name string + Description string + Type *Type + Args []InputValue + IsDeprecated bool + DeprecationReason string + } + + InputValue struct { + Name string + Description string + DefaultValue *string + Type *Type + } +) + +func WrapSchema(schema *ast.Schema) *Schema { + return &Schema{schema: schema} +} + +func isDeprecated(directives ast.DirectiveList) bool { + return directives.ForName("deprecated") != nil +} + +func deprecationReason(directives ast.DirectiveList) string { + deprecation := directives.ForName("deprecated") + if deprecation == nil { + return "" + } + + reason := deprecation.Arguments.ForName("reason") + if reason == nil { + return "" + } + + return reason.Value.Raw +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/query.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/query.go new file mode 100644 index 00000000..b1e4fbc6 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/query.go @@ -0,0 +1,104 @@ +package introspection + +// Query is the query generated by graphiql to determine type information +const Query = ` +query IntrospectionQuery { + __schema { + queryType { + name + } + mutationType { + name + } + subscriptionType { + name + } + types { + ...FullType + } + directives { + name + description + locations + args { + ...InputValue + } + } + } +} + +fragment FullType on __Type { + kind + name + description + fields(includeDeprecated: true) { + name + description + args { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } +} + +fragment InputValue on __InputValue { + name + description + type { + ...TypeRef + } + defaultValue +} + +fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } +} +` diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go new file mode 100644 index 00000000..b5d2c482 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go @@ -0,0 +1,68 @@ +package introspection + +import ( + "strings" + + "github.com/vektah/gqlparser/ast" +) + +type Schema struct { + schema *ast.Schema +} + +func (s *Schema) Types() []Type { + var types []Type + for _, typ := range s.schema.Types { + if strings.HasPrefix(typ.Name, "__") { + continue + } + types = append(types, *WrapTypeFromDef(s.schema, typ)) + } + return types +} + +func (s *Schema) QueryType() *Type { + return WrapTypeFromDef(s.schema, s.schema.Query) +} + +func (s *Schema) MutationType() *Type { + return WrapTypeFromDef(s.schema, s.schema.Mutation) +} + +func (s *Schema) SubscriptionType() *Type { + return WrapTypeFromDef(s.schema, s.schema.Subscription) +} + +func (s *Schema) Directives() []Directive { + var res []Directive + + for _, d := range s.schema.Directives { + res = append(res, s.directiveFromDef(d)) + } + + return res +} + +func (s *Schema) directiveFromDef(d *ast.DirectiveDefinition) Directive { + var locs []string + for _, loc := range d.Locations { + locs = append(locs, string(loc)) + } + + var args []InputValue + for _, arg := range d.Arguments { + args = append(args, InputValue{ + Name: arg.Name, + Description: arg.Description, + DefaultValue: defaultValue(arg.DefaultValue), + Type: WrapTypeFromType(s.schema, arg.Type), + }) + } + + return Directive{ + Name: d.Name, + Description: d.Description, + Locations: locs, + Args: args, + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/introspection/type.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/type.go new file mode 100644 index 00000000..dce144e0 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/type.go @@ -0,0 +1,174 @@ +package introspection + +import ( + "strings" + + "github.com/vektah/gqlparser/ast" +) + +type Type struct { + schema *ast.Schema + def *ast.Definition + typ *ast.Type +} + +func WrapTypeFromDef(s *ast.Schema, def *ast.Definition) *Type { + if def == nil { + return nil + } + return &Type{schema: s, def: def} +} + +func WrapTypeFromType(s *ast.Schema, typ *ast.Type) *Type { + if typ == nil { + return nil + } + + if !typ.NonNull && typ.NamedType != "" { + return &Type{schema: s, def: s.Types[typ.NamedType]} + } + return &Type{schema: s, typ: typ} +} + +func (t *Type) Kind() string { + if t.typ != nil { + if t.typ.NonNull { + return "NON_NULL" + } + + if t.typ.Elem != nil { + return "LIST" + } + } else { + return string(t.def.Kind) + } + + panic("UNKNOWN") +} + +func (t *Type) Name() *string { + if t.def == nil { + return nil + } + return &t.def.Name +} + +func (t *Type) Description() string { + if t.def == nil { + return "" + } + return t.def.Description +} + +func (t *Type) Fields(includeDeprecated bool) []Field { + if t.def == nil || (t.def.Kind != ast.Object && t.def.Kind != ast.Interface) { + return nil + } + var fields []Field + for _, f := range t.def.Fields { + if strings.HasPrefix(f.Name, "__") { + continue + } + + var args []InputValue + for _, arg := range f.Arguments { + args = append(args, InputValue{ + Type: WrapTypeFromType(t.schema, arg.Type), + Name: arg.Name, + Description: arg.Description, + DefaultValue: defaultValue(arg.DefaultValue), + }) + } + + 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), + }) + } + return fields +} + +func (t *Type) InputFields() []InputValue { + if t.def == nil || t.def.Kind != ast.InputObject { + return nil + } + + var res []InputValue + for _, f := range t.def.Fields { + res = append(res, InputValue{ + Name: f.Name, + Description: f.Description, + Type: WrapTypeFromType(t.schema, f.Type), + DefaultValue: defaultValue(f.DefaultValue), + }) + } + return res +} + +func defaultValue(value *ast.Value) *string { + if value == nil { + return nil + } + val := value.String() + return &val +} + +func (t *Type) Interfaces() []Type { + if t.def == nil || t.def.Kind != ast.Object { + return nil + } + + var res []Type + for _, intf := range t.def.Interfaces { + res = append(res, *WrapTypeFromDef(t.schema, t.schema.Types[intf])) + } + + return res +} + +func (t *Type) PossibleTypes() []Type { + if t.def == nil || (t.def.Kind != ast.Interface && t.def.Kind != ast.Union) { + return nil + } + + var res []Type + for _, pt := range t.schema.GetPossibleTypes(t.def) { + res = append(res, *WrapTypeFromDef(t.schema, pt)) + } + return res +} + +func (t *Type) EnumValues(includeDeprecated bool) []EnumValue { + if t.def == nil || t.def.Kind != ast.Enum { + return nil + } + + 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), + }) + } + return res +} + +func (t *Type) OfType() *Type { + if t.typ == nil { + return nil + } + if t.typ.NonNull { + // fake non null nodes + cpy := *t.typ + cpy.NonNull = false + + return WrapTypeFromType(t.schema, &cpy) + } + return WrapTypeFromType(t.schema, t.typ.Elem) +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/jsonw.go b/vendor/github.com/99designs/gqlgen/graphql/jsonw.go new file mode 100644 index 00000000..c112444a --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/jsonw.go @@ -0,0 +1,83 @@ +package graphql + +import ( + "io" + "strconv" +) + +var nullLit = []byte(`null`) +var trueLit = []byte(`true`) +var falseLit = []byte(`false`) +var openBrace = []byte(`{`) +var closeBrace = []byte(`}`) +var openBracket = []byte(`[`) +var closeBracket = []byte(`]`) +var colon = []byte(`:`) +var comma = []byte(`,`) + +var Null = &lit{nullLit} +var True = &lit{trueLit} +var False = &lit{falseLit} + +type Marshaler interface { + MarshalGQL(w io.Writer) +} + +type Unmarshaler interface { + UnmarshalGQL(v interface{}) error +} + +type OrderedMap struct { + Keys []string + Values []Marshaler +} + +type WriterFunc func(writer io.Writer) + +func (f WriterFunc) MarshalGQL(w io.Writer) { + f(w) +} + +func NewOrderedMap(len int) *OrderedMap { + return &OrderedMap{ + Keys: make([]string, len), + Values: make([]Marshaler, len), + } +} + +func (m *OrderedMap) Add(key string, value Marshaler) { + m.Keys = append(m.Keys, key) + m.Values = append(m.Values, value) +} + +func (m *OrderedMap) MarshalGQL(writer io.Writer) { + writer.Write(openBrace) + for i, key := range m.Keys { + if i != 0 { + writer.Write(comma) + } + io.WriteString(writer, strconv.Quote(key)) + writer.Write(colon) + m.Values[i].MarshalGQL(writer) + } + writer.Write(closeBrace) +} + +type Array []Marshaler + +func (a Array) MarshalGQL(writer io.Writer) { + writer.Write(openBracket) + for i, val := range a { + if i != 0 { + writer.Write(comma) + } + val.MarshalGQL(writer) + } + writer.Write(closeBracket) +} + +type lit struct{ b []byte } + +func (l lit) MarshalGQL(w io.Writer) { + w.Write(l.b) +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/map.go b/vendor/github.com/99designs/gqlgen/graphql/map.go new file mode 100644 index 00000000..1e91d1d9 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/map.go @@ -0,0 +1,24 @@ +package graphql + +import ( + "encoding/json" + "fmt" + "io" +) + +func MarshalMap(val map[string]interface{}) Marshaler { + return WriterFunc(func(w io.Writer) { + err := json.NewEncoder(w).Encode(val) + if err != nil { + panic(err) + } + }) +} + +func UnmarshalMap(v interface{}) (map[string]interface{}, error) { + if m, ok := v.(map[string]interface{}); ok { + return m, nil + } + + return nil, fmt.Errorf("%T is not a map", v) +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/oneshot.go b/vendor/github.com/99designs/gqlgen/graphql/oneshot.go new file mode 100644 index 00000000..dd31f5ba --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/oneshot.go @@ -0,0 +1,14 @@ +package graphql + +func OneShot(resp *Response) func() *Response { + var oneshot bool + + return func() *Response { + if oneshot { + return nil + } + oneshot = true + + return resp + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/recovery.go b/vendor/github.com/99designs/gqlgen/graphql/recovery.go new file mode 100644 index 00000000..3aa032dc --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/recovery.go @@ -0,0 +1,19 @@ +package graphql + +import ( + "context" + "errors" + "fmt" + "os" + "runtime/debug" +) + +type RecoverFunc func(ctx context.Context, err interface{}) (userMessage error) + +func DefaultRecover(ctx context.Context, err interface{}) error { + fmt.Fprintln(os.Stderr, err) + fmt.Fprintln(os.Stderr) + debug.PrintStack() + + return errors.New("internal system error") +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/response.go b/vendor/github.com/99designs/gqlgen/graphql/response.go new file mode 100644 index 00000000..18664dca --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/response.go @@ -0,0 +1,20 @@ +package graphql + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/vektah/gqlparser/gqlerror" +) + +type Response struct { + Data json.RawMessage `json:"data"` + Errors gqlerror.List `json:"errors,omitempty"` +} + +func ErrorResponse(ctx context.Context, messagef string, args ...interface{}) *Response { + return &Response{ + Errors: gqlerror.List{{Message: fmt.Sprintf(messagef, args...)}}, + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/string.go b/vendor/github.com/99designs/gqlgen/graphql/string.go new file mode 100644 index 00000000..d5fb3294 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/string.go @@ -0,0 +1,63 @@ +package graphql + +import ( + "fmt" + "io" + "strconv" +) + +const encodeHex = "0123456789ABCDEF" + +func MarshalString(s string) Marshaler { + return WriterFunc(func(w io.Writer) { + start := 0 + io.WriteString(w, `"`) + + for i, c := range s { + if c < 0x20 || c == '\\' || c == '"' { + io.WriteString(w, s[start:i]) + + switch c { + case '\t': + io.WriteString(w, `\t`) + case '\r': + io.WriteString(w, `\r`) + case '\n': + io.WriteString(w, `\n`) + case '\\': + io.WriteString(w, `\\`) + case '"': + io.WriteString(w, `\"`) + default: + io.WriteString(w, `\u00`) + w.Write([]byte{encodeHex[c>>4], encodeHex[c&0xf]}) + } + + start = i + 1 + } + } + + io.WriteString(w, s[start:]) + io.WriteString(w, `"`) + }) +} +func UnmarshalString(v interface{}) (string, error) { + switch v := v.(type) { + case string: + return v, nil + case int: + return strconv.Itoa(v), nil + case float64: + return fmt.Sprintf("%f", v), nil + case bool: + if v { + return "true", nil + } else { + return "false", nil + } + case nil: + return "null", nil + default: + return "", fmt.Errorf("%T is not a string", v) + } +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/time.go b/vendor/github.com/99designs/gqlgen/graphql/time.go new file mode 100644 index 00000000..4f448560 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/time.go @@ -0,0 +1,21 @@ +package graphql + +import ( + "errors" + "io" + "strconv" + "time" +) + +func MarshalTime(t time.Time) Marshaler { + return WriterFunc(func(w io.Writer) { + io.WriteString(w, strconv.Quote(t.Format(time.RFC3339))) + }) +} + +func UnmarshalTime(v interface{}) (time.Time, error) { + if tmpStr, ok := v.(string); ok { + return time.Parse(time.RFC3339, tmpStr) + } + return time.Time{}, errors.New("time should be RFC3339 formatted string") +} diff --git a/vendor/github.com/99designs/gqlgen/graphql/version.go b/vendor/github.com/99designs/gqlgen/graphql/version.go new file mode 100644 index 00000000..38d3720b --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/graphql/version.go @@ -0,0 +1,3 @@ +package graphql + +const Version = "v0.5.1" diff --git a/vendor/github.com/99designs/gqlgen/handler/graphql.go b/vendor/github.com/99designs/gqlgen/handler/graphql.go new file mode 100644 index 00000000..9d222826 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/handler/graphql.go @@ -0,0 +1,283 @@ +package handler + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "strings" + + "github.com/99designs/gqlgen/complexity" + "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/validator" +) + +type params struct { + Query string `json:"query"` + OperationName string `json:"operationName"` + Variables map[string]interface{} `json:"variables"` +} + +type Config struct { + cacheSize int + upgrader websocket.Upgrader + recover graphql.RecoverFunc + errorPresenter graphql.ErrorPresenterFunc + resolverHook graphql.FieldMiddleware + requestHook graphql.RequestMiddleware + complexityLimit int +} + +func (c *Config) newRequestContext(doc *ast.QueryDocument, query string, variables map[string]interface{}) *graphql.RequestContext { + reqCtx := graphql.NewRequestContext(doc, query, variables) + if hook := c.recover; hook != nil { + reqCtx.Recover = hook + } + + if hook := c.errorPresenter; hook != nil { + reqCtx.ErrorPresenter = hook + } + + if hook := c.resolverHook; hook != nil { + reqCtx.ResolverMiddleware = hook + } + + if hook := c.requestHook; hook != nil { + reqCtx.RequestMiddleware = hook + } + + return reqCtx +} + +type Option func(cfg *Config) + +func WebsocketUpgrader(upgrader websocket.Upgrader) Option { + return func(cfg *Config) { + cfg.upgrader = upgrader + } +} + +func RecoverFunc(recover graphql.RecoverFunc) Option { + return func(cfg *Config) { + cfg.recover = recover + } +} + +// ErrorPresenter transforms errors found while resolving into errors that will be returned to the user. It provides +// a good place to add any extra fields, like error.type, that might be desired by your frontend. Check the default +// implementation in graphql.DefaultErrorPresenter for an example. +func ErrorPresenter(f graphql.ErrorPresenterFunc) Option { + return func(cfg *Config) { + cfg.errorPresenter = f + } +} + +// 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 { + return func(cfg *Config) { + cfg.complexityLimit = limit + } +} + +// ResolverMiddleware allows you to define a function that will be called around every resolver, +// useful for tracing and logging. +func ResolverMiddleware(middleware graphql.FieldMiddleware) Option { + return func(cfg *Config) { + if cfg.resolverHook == nil { + cfg.resolverHook = middleware + return + } + + lastResolve := cfg.resolverHook + cfg.resolverHook = func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { + return lastResolve(ctx, func(ctx context.Context) (res interface{}, err error) { + return middleware(ctx, next) + }) + } + } +} + +// 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 +func RequestMiddleware(middleware graphql.RequestMiddleware) Option { + return func(cfg *Config) { + if cfg.requestHook == nil { + cfg.requestHook = middleware + return + } + + lastResolve := cfg.requestHook + cfg.requestHook = func(ctx context.Context, next func(ctx context.Context) []byte) []byte { + return lastResolve(ctx, func(ctx context.Context) []byte { + return middleware(ctx, next) + }) + } + } +} + +// 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 { + return func(cfg *Config) { + cfg.cacheSize = size + } +} + +const DefaultCacheSize = 1000 + +func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc { + cfg := Config{ + cacheSize: DefaultCacheSize, + upgrader: websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + }, + } + + for _, option := range options { + option(&cfg) + } + + var cache *lru.Cache + if cfg.cacheSize > 0 { + var err error + cache, err = lru.New(DefaultCacheSize) + if err != nil { + // An error is only returned for non-positive cache size + // and we already checked for that. + panic("unexpected error creating cache: " + err.Error()) + } + } + + 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 + } + + if strings.Contains(r.Header.Get("Upgrade"), "websocket") { + connectWs(exec, w, r, &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 + } + } + case http.MethodPost: + if err := jsonDecode(r.Body, &reqParams); err != nil { + sendErrorf(w, http.StatusBadRequest, "json body could not be decoded: "+err.Error()) + return + } + 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) + } + } + + op := doc.Operations.ForName(reqParams.OperationName) + if op == nil { + sendErrorf(w, http.StatusUnprocessableEntity, "operation %s not found", reqParams.OperationName) + return + } + + if op.Operation != ast.Query && r.Method == http.MethodGet { + sendErrorf(w, http.StatusUnprocessableEntity, "GET requests only allow query operations") + return + } + + vars, err := validator.VariableValues(exec.Schema(), op, reqParams.Variables) + if err != nil { + sendError(w, http.StatusUnprocessableEntity, err) + return + } + reqCtx := cfg.newRequestContext(doc, reqParams.Query, vars) + ctx := graphql.WithRequestContext(r.Context(), reqCtx) + + defer func() { + if err := recover(); err != nil { + userErr := reqCtx.Recover(ctx, err) + sendErrorf(w, http.StatusUnprocessableEntity, userErr.Error()) + } + }() + + 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 + } + } + + 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") + } + }) +} + +func jsonDecode(r io.Reader, val interface{}) error { + dec := json.NewDecoder(r) + dec.UseNumber() + return dec.Decode(val) +} + +func sendError(w http.ResponseWriter, code int, errors ...*gqlerror.Error) { + w.WriteHeader(code) + b, err := json.Marshal(&graphql.Response{Errors: errors}) + if err != nil { + panic(err) + } + w.Write(b) +} + +func sendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { + sendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) +} diff --git a/vendor/github.com/99designs/gqlgen/handler/playground.go b/vendor/github.com/99designs/gqlgen/handler/playground.go new file mode 100644 index 00000000..d0ada8ca --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/handler/playground.go @@ -0,0 +1,54 @@ +package handler + +import ( + "html/template" + "net/http" +) + +var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html> +<html> +<head> + <meta charset=utf-8/> + <meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui"> + <link rel="shortcut icon" href="https://graphcool-playground.netlify.com/favicon.png"> + <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/css/index.css"/> + <link rel="shortcut icon" href="//cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/favicon.png"/> + <script src="//cdn.jsdelivr.net/npm/graphql-playground-react@{{ .version }}/build/static/js/middleware.js"></script> + <title>{{.title}}</title> +</head> +<body> +<style type="text/css"> + html { font-family: "Open Sans", sans-serif; overflow: hidden; } + body { margin: 0; background: #172a3a; } +</style> +<div id="root"/> +<script type="text/javascript"> + window.addEventListener('load', function (event) { + const root = document.getElementById('root'); + root.classList.add('playgroundIn'); + const wsProto = location.protocol == 'https:' ? 'wss:' : 'ws:' + GraphQLPlayground.init(root, { + endpoint: location.protocol + '//' + location.host + '{{.endpoint}}', + subscriptionsEndpoint: wsProto + '//' + location.host + '{{.endpoint }}', + settings: { + 'request.credentials': 'same-origin' + } + }) + }) +</script> +</body> +</html> +`)) + +func Playground(title string, endpoint string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + err := page.Execute(w, map[string]string{ + "title": title, + "endpoint": endpoint, + "version": "1.6.2", + }) + if err != nil { + panic(err) + } + } +} diff --git a/vendor/github.com/99designs/gqlgen/handler/stub.go b/vendor/github.com/99designs/gqlgen/handler/stub.go new file mode 100644 index 00000000..d237e188 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/handler/stub.go @@ -0,0 +1,51 @@ +package handler + +import ( + "context" + + "github.com/99designs/gqlgen/graphql" + "github.com/vektah/gqlparser" + "github.com/vektah/gqlparser/ast" +) + +type executableSchemaStub struct { + NextResp chan struct{} +} + +var _ graphql.ExecutableSchema = &executableSchemaStub{} + +func (e *executableSchemaStub) Schema() *ast.Schema { + return gqlparser.MustLoadSchema(&ast.Source{Input: ` + schema { query: Query } + type Query { + me: User! + user(id: Int): User! + } + type User { name: String! } + `}) +} + +func (e *executableSchemaStub) Complexity(typeName, field string, childComplexity int, args map[string]interface{}) (int, bool) { + return 0, false +} + +func (e *executableSchemaStub) Query(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + return &graphql.Response{Data: []byte(`{"name":"test"}`)} +} + +func (e *executableSchemaStub) Mutation(ctx context.Context, op *ast.OperationDefinition) *graphql.Response { + return graphql.ErrorResponse(ctx, "mutations are not supported") +} + +func (e *executableSchemaStub) Subscription(ctx context.Context, op *ast.OperationDefinition) func() *graphql.Response { + return func() *graphql.Response { + select { + case <-ctx.Done(): + return nil + case <-e.NextResp: + return &graphql.Response{ + Data: []byte(`{"name":"test"}`), + } + } + } +} diff --git a/vendor/github.com/99designs/gqlgen/handler/websocket.go b/vendor/github.com/99designs/gqlgen/handler/websocket.go new file mode 100644 index 00000000..2be1e87f --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/handler/websocket.go @@ -0,0 +1,252 @@ +package handler + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "log" + "net/http" + "sync" + + "github.com/99designs/gqlgen/graphql" + "github.com/gorilla/websocket" + "github.com/vektah/gqlparser" + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/validator" +) + +const ( + connectionInitMsg = "connection_init" // Client -> Server + connectionTerminateMsg = "connection_terminate" // Client -> Server + startMsg = "start" // Client -> Server + stopMsg = "stop" // Client -> Server + connectionAckMsg = "connection_ack" // Server -> Client + connectionErrorMsg = "connection_error" // Server -> Client + dataMsg = "data" // Server -> Client + errorMsg = "error" // Server -> Client + completeMsg = "complete" // Server -> Client + //connectionKeepAliveMsg = "ka" // Server -> Client TODO: keepalives +) + +type operationMessage struct { + Payload json.RawMessage `json:"payload,omitempty"` + ID string `json:"id,omitempty"` + Type string `json:"type"` +} + +type wsConnection struct { + ctx context.Context + conn *websocket.Conn + exec graphql.ExecutableSchema + active map[string]context.CancelFunc + mu sync.Mutex + cfg *Config +} + +func connectWs(exec graphql.ExecutableSchema, w http.ResponseWriter, r *http.Request, cfg *Config) { + ws, err := cfg.upgrader.Upgrade(w, r, http.Header{ + "Sec-Websocket-Protocol": []string{"graphql-ws"}, + }) + if err != nil { + log.Printf("unable to upgrade %T to websocket %s: ", w, err.Error()) + sendErrorf(w, http.StatusBadRequest, "unable to upgrade") + return + } + + conn := wsConnection{ + active: map[string]context.CancelFunc{}, + exec: exec, + conn: ws, + ctx: r.Context(), + cfg: cfg, + } + + if !conn.init() { + return + } + + conn.run() +} + +func (c *wsConnection) init() bool { + message := c.readOp() + if message == nil { + c.close(websocket.CloseProtocolError, "decoding error") + return false + } + + switch message.Type { + case connectionInitMsg: + c.write(&operationMessage{Type: connectionAckMsg}) + case connectionTerminateMsg: + c.close(websocket.CloseNormalClosure, "terminated") + return false + default: + c.sendConnectionError("unexpected message %s", message.Type) + c.close(websocket.CloseProtocolError, "unexpected message") + return false + } + + return true +} + +func (c *wsConnection) write(msg *operationMessage) { + c.mu.Lock() + c.conn.WriteJSON(msg) + c.mu.Unlock() +} + +func (c *wsConnection) run() { + for { + message := c.readOp() + if message == nil { + return + } + + switch message.Type { + case startMsg: + if !c.subscribe(message) { + return + } + case stopMsg: + c.mu.Lock() + closer := c.active[message.ID] + c.mu.Unlock() + if closer == nil { + c.sendError(message.ID, gqlerror.Errorf("%s is not running, cannot stop", message.ID)) + continue + } + + closer() + case connectionTerminateMsg: + c.close(websocket.CloseNormalClosure, "terminated") + return + default: + c.sendConnectionError("unexpected message %s", message.Type) + c.close(websocket.CloseProtocolError, "unexpected message") + return + } + } +} + +func (c *wsConnection) subscribe(message *operationMessage) bool { + var reqParams params + if err := jsonDecode(bytes.NewReader(message.Payload), &reqParams); err != nil { + c.sendConnectionError("invalid json") + return false + } + + doc, qErr := gqlparser.LoadQuery(c.exec.Schema(), reqParams.Query) + if qErr != nil { + c.sendError(message.ID, qErr...) + return true + } + + op := doc.Operations.ForName(reqParams.OperationName) + if op == nil { + c.sendError(message.ID, gqlerror.Errorf("operation %s not found", reqParams.OperationName)) + return true + } + + vars, err := validator.VariableValues(c.exec.Schema(), op, reqParams.Variables) + if err != nil { + c.sendError(message.ID, err) + return true + } + reqCtx := c.cfg.newRequestContext(doc, reqParams.Query, vars) + ctx := graphql.WithRequestContext(c.ctx, reqCtx) + + if op.Operation != ast.Subscription { + var result *graphql.Response + if op.Operation == ast.Query { + result = c.exec.Query(ctx, op) + } else { + result = c.exec.Mutation(ctx, op) + } + + c.sendData(message.ID, result) + c.write(&operationMessage{ID: message.ID, Type: completeMsg}) + return true + } + + ctx, cancel := context.WithCancel(ctx) + c.mu.Lock() + c.active[message.ID] = cancel + c.mu.Unlock() + go func() { + defer func() { + if r := recover(); r != nil { + userErr := reqCtx.Recover(ctx, r) + c.sendError(message.ID, &gqlerror.Error{Message: userErr.Error()}) + } + }() + next := c.exec.Subscription(ctx, op) + for result := next(); result != nil; result = next() { + c.sendData(message.ID, result) + } + + c.write(&operationMessage{ID: message.ID, Type: completeMsg}) + + c.mu.Lock() + delete(c.active, message.ID) + c.mu.Unlock() + cancel() + }() + + return true +} + +func (c *wsConnection) sendData(id string, response *graphql.Response) { + b, err := json.Marshal(response) + if err != nil { + c.sendError(id, gqlerror.Errorf("unable to encode json response: %s", err.Error())) + return + } + + c.write(&operationMessage{Type: dataMsg, ID: id, Payload: b}) +} + +func (c *wsConnection) sendError(id string, errors ...*gqlerror.Error) { + var errs []error + for _, err := range errors { + errs = append(errs, err) + } + b, err := json.Marshal(errs) + if err != nil { + panic(err) + } + c.write(&operationMessage{Type: errorMsg, ID: id, Payload: b}) +} + +func (c *wsConnection) sendConnectionError(format string, args ...interface{}) { + b, err := json.Marshal(&gqlerror.Error{Message: fmt.Sprintf(format, args...)}) + if err != nil { + panic(err) + } + + c.write(&operationMessage{Type: connectionErrorMsg, Payload: b}) +} + +func (c *wsConnection) readOp() *operationMessage { + _, r, err := c.conn.NextReader() + if err != nil { + c.sendConnectionError("invalid json") + return nil + } + message := operationMessage{} + if err := jsonDecode(r, &message); err != nil { + c.sendConnectionError("invalid json") + return nil + } + + return &message +} + +func (c *wsConnection) close(closeCode int, message string) { + c.mu.Lock() + _ = c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(closeCode, message)) + c.mu.Unlock() + _ = c.conn.Close() +} diff --git a/vendor/github.com/99designs/gqlgen/internal/gopath/gopath.go b/vendor/github.com/99designs/gqlgen/internal/gopath/gopath.go new file mode 100644 index 00000000..c9b66167 --- /dev/null +++ b/vendor/github.com/99designs/gqlgen/internal/gopath/gopath.go @@ -0,0 +1,37 @@ +package gopath + +import ( + "fmt" + "go/build" + "path/filepath" + "strings" +) + +var NotFound = fmt.Errorf("not on GOPATH") + +// Contains returns true if the given directory is in the GOPATH +func Contains(dir string) bool { + _, err := Dir2Import(dir) + return err == nil +} + +// Dir2Import takes an *absolute* path and returns a golang import path for the package, and returns an error if it isn't on the gopath +func Dir2Import(dir string) (string, error) { + dir = filepath.ToSlash(dir) + for _, gopath := range filepath.SplitList(build.Default.GOPATH) { + gopath = filepath.ToSlash(filepath.Join(gopath, "src")) + if len(gopath) < len(dir) && strings.EqualFold(gopath, dir[0:len(gopath)]) { + return dir[len(gopath)+1:], nil + } + } + return "", NotFound +} + +// MustDir2Import takes an *absolute* path and returns a golang import path for the package, and panics if it isn't on the gopath +func MustDir2Import(dir string) string { + pkg, err := Dir2Import(dir) + if err != nil { + panic(err) + } + return pkg +} |