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 | |
parent | a3fc9abb921f5ce7084d6ab7473442d0b72b1d78 (diff) | |
download | git-bug-b478cd1bcb4756b20f7f4b15fcf81f23e1a60a02.tar.gz |
graphql: update gqlgen to 0.5.1
fix #6
Diffstat (limited to 'vendor')
163 files changed, 11379 insertions, 4338 deletions
diff --git a/vendor/github.com/vektah/gqlgen/LICENSE b/vendor/github.com/99designs/gqlgen/LICENSE index 18e1b249..18e1b249 100644 --- a/vendor/github.com/vektah/gqlgen/LICENSE +++ b/vendor/github.com/99designs/gqlgen/LICENSE 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/vektah/gqlgen/codegen/config.go b/vendor/github.com/99designs/gqlgen/codegen/config.go index cd42ae6b..db0e467b 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/config.go +++ b/vendor/github.com/99designs/gqlgen/codegen/config.go @@ -8,26 +8,29 @@ import ( "path/filepath" "strings" + "github.com/99designs/gqlgen/internal/gopath" "github.com/pkg/errors" - "github.com/vektah/gqlgen/neelance/schema" + "github.com/vektah/gqlparser/ast" "gopkg.in/yaml.v2" ) -var defaults = Config{ - SchemaFilename: "schema.graphql", - Model: PackageConfig{Filename: "models_gen.go"}, - Exec: PackageConfig{Filename: "generated.go"}, -} - var cfgFilenames = []string{".gqlgen.yml", "gqlgen.yml", "gqlgen.yaml"} -// LoadDefaultConfig looks for a config file in the current directory, and all parent directories +// 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 LoadDefaultConfig() (*Config, error) { +func LoadConfigFromDefaultLocations() (*Config, error) { cfgFile, err := findCfg() - if err != nil || cfgFile == "" { - cpy := defaults - return &cpy, err + if err != nil { + return nil, err } err = os.Chdir(filepath.Dir(cfgFile)) @@ -39,18 +42,20 @@ func LoadDefaultConfig() (*Config, error) { // LoadConfig reads the gqlgen.yml config file func LoadConfig(filename string) (*Config, error) { - config := defaults + 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 { + if err := yaml.UnmarshalStrict(b, config); err != nil { return nil, errors.Wrap(err, "unable to parse config") } - return &config, nil + config.FilePath = filename + + return config, nil } type Config struct { @@ -58,14 +63,19 @@ type Config struct { 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 *schema.Schema `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 { @@ -74,7 +84,8 @@ type TypeMapEntry struct { } type TypeMapField struct { - Resolver bool `yaml:"resolver"` + Resolver bool `yaml:"resolver"` + FieldName string `yaml:"fieldName"` } func (c *PackageConfig) normalize() error { @@ -98,22 +109,11 @@ func (c *PackageConfig) normalize() error { } func (c *PackageConfig) ImportPath() string { - dir := filepath.ToSlash(c.Dir()) - for _, gopath := range filepath.SplitList(build.Default.GOPATH) { - gopath = filepath.ToSlash(gopath) + "/src/" - if len(gopath) > len(dir) { - continue - } - if strings.EqualFold(gopath, dir[0:len(gopath)]) { - dir = dir[len(gopath):] - break - } - } - return dir + return gopath.MustDir2Import(c.Dir()) } func (c *PackageConfig) Dir() string { - return filepath.ToSlash(filepath.Dir(c.Filename)) + return filepath.Dir(c.Filename) } func (c *PackageConfig) Check() error { @@ -126,6 +126,10 @@ func (c *PackageConfig) Check() error { 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") @@ -136,6 +140,9 @@ func (cfg *Config) Check() error { 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 } @@ -170,6 +177,10 @@ func findCfg() (string, error) { cfg = findCfgInDir(dir) } + if cfg == "" { + return "", os.ErrNotExist + } + return cfg, nil } 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/vektah/gqlgen/codegen/enum.go b/vendor/github.com/99designs/gqlgen/codegen/enum.go index e62fd2b1..7804971c 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/enum.go +++ b/vendor/github.com/99designs/gqlgen/codegen/enum.go @@ -2,8 +2,8 @@ package codegen type Enum struct { *NamedType - - Values []EnumValue + Description string + Values []EnumValue } type EnumValue struct { 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/vektah/gqlgen/codegen/import.go b/vendor/github.com/99designs/gqlgen/codegen/import.go index b511e8f6..b511e8f6 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/import.go +++ b/vendor/github.com/99designs/gqlgen/codegen/import.go diff --git a/vendor/github.com/vektah/gqlgen/codegen/import_build.go b/vendor/github.com/99designs/gqlgen/codegen/import_build.go index f0877ed3..d634834e 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/import_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/import_build.go @@ -5,7 +5,14 @@ import ( "go/build" "sort" "strconv" - "strings" + + // 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 @@ -18,12 +25,12 @@ var ambientImports = []string{ "strconv", "time", "sync", - "github.com/vektah/gqlgen/neelance/introspection", - "github.com/vektah/gqlgen/neelance/errors", - "github.com/vektah/gqlgen/neelance/query", - "github.com/vektah/gqlgen/neelance/schema", - "github.com/vektah/gqlgen/neelance/validation", - "github.com/vektah/gqlgen/graphql", + "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 { @@ -48,7 +55,8 @@ func (s *Imports) add(path string) *Import { return nil } - if stringHasSuffixFold(s.destDir, path) { + // if we are referencing our own package we dont need an import + if gopath.MustDir2Import(s.destDir) == path { return nil } @@ -70,10 +78,6 @@ func (s *Imports) add(path string) *Import { return imp } -func stringHasSuffixFold(s, suffix string) bool { - return len(s) >= len(suffix) && strings.EqualFold(s[len(s)-len(suffix):], suffix) -} - func (s Imports) finalize() []*Import { // ensure stable ordering by sorting sort.Slice(s.imports, func(i, j int) bool { diff --git a/vendor/github.com/vektah/gqlgen/codegen/input_build.go b/vendor/github.com/99designs/gqlgen/codegen/input_build.go index 98b25b8b..06ff37a0 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/input_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/input_build.go @@ -3,10 +3,9 @@ package codegen import ( "go/types" "sort" - "strings" "github.com/pkg/errors" - "github.com/vektah/gqlgen/neelance/schema" + "github.com/vektah/gqlparser/ast" "golang.org/x/tools/go/loader" ) @@ -14,9 +13,9 @@ func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program, impo var inputs Objects for _, typ := range cfg.schema.Types { - switch typ := typ.(type) { - case *schema.InputObject: - input, err := buildInput(namedTypes, typ) + switch typ.Kind { + case ast.InputObject: + input, err := cfg.buildInput(namedTypes, typ) if err != nil { return nil, err } @@ -27,7 +26,7 @@ func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program, impo } if def != nil { input.Marshaler = buildInputMarshaler(typ, def) - bindErrs := bindObject(def.Type(), input, imports) + bindErrs := bindObject(def.Type(), input, imports, cfg.StructTag) if len(bindErrs) > 0 { return nil, bindErrs } @@ -38,24 +37,35 @@ func (cfg *Config) buildInputs(namedTypes NamedTypes, prog *loader.Program, impo } sort.Slice(inputs, func(i, j int) bool { - return strings.Compare(inputs[i].GQLType, inputs[j].GQLType) == -1 + return inputs[i].GQLType < inputs[j].GQLType }) return inputs, nil } -func buildInput(types NamedTypes, typ *schema.InputObject) (*Object, error) { - obj := &Object{NamedType: types[typ.TypeName()]} +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.Values { + for _, field := range typ.Fields { newField := Field{ - GQLName: field.Name.Name, + GQLName: field.Name, Type: types.getType(field.Type), Object: obj, } - if field.Default != nil { - newField.Default = field.Default.Value(nil) + 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 { @@ -70,7 +80,7 @@ func buildInput(types NamedTypes, typ *schema.InputObject) (*Object, error) { // if user has implemented an UnmarshalGQL method on the input type manually, use it // otherwise we will generate one. -func buildInputMarshaler(typ *schema.InputObject, def types.Object) *Ref { +func buildInputMarshaler(typ *ast.Definition, def types.Object) *Ref { switch def := def.(type) { case *types.TypeName: namedType := def.Type().(*types.Named) diff --git a/vendor/github.com/vektah/gqlgen/codegen/interface.go b/vendor/github.com/99designs/gqlgen/codegen/interface.go index 2de0c88a..2de0c88a 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/interface.go +++ b/vendor/github.com/99designs/gqlgen/codegen/interface.go diff --git a/vendor/github.com/vektah/gqlgen/codegen/interface_build.go b/vendor/github.com/99designs/gqlgen/codegen/interface_build.go index cdf0f597..9f4a4ff4 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/interface_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/interface_build.go @@ -5,63 +5,39 @@ import ( "go/types" "os" "sort" - "strings" - "github.com/vektah/gqlgen/neelance/schema" + "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 { - switch typ := typ.(type) { - case *schema.Union, *schema.Interface: + if typ.Kind == ast.Union || typ.Kind == ast.Interface { interfaces = append(interfaces, cfg.buildInterface(types, typ, prog)) - default: - continue } } sort.Slice(interfaces, func(i, j int) bool { - return strings.Compare(interfaces[i].GQLType, interfaces[j].GQLType) == -1 + return interfaces[i].GQLType < interfaces[j].GQLType }) return interfaces } -func (cfg *Config) buildInterface(types NamedTypes, typ schema.NamedType, prog *loader.Program) *Interface { - switch typ := typ.(type) { +func (cfg *Config) buildInterface(types NamedTypes, typ *ast.Definition, prog *loader.Program) *Interface { + i := &Interface{NamedType: types[typ.Name]} - case *schema.Union: - i := &Interface{NamedType: types[typ.TypeName()]} + for _, implementor := range cfg.schema.GetPossibleTypes(typ) { + t := types[implementor.Name] - for _, implementor := range typ.PossibleTypes { - t := types[implementor.TypeName()] - - i.Implementors = append(i.Implementors, InterfaceImplementor{ - NamedType: t, - ValueReceiver: cfg.isValueReceiver(types[typ.Name], t, prog), - }) - } - - return i - - case *schema.Interface: - i := &Interface{NamedType: types[typ.TypeName()]} - - for _, implementor := range typ.PossibleTypes { - t := types[implementor.TypeName()] - - i.Implementors = append(i.Implementors, InterfaceImplementor{ - NamedType: t, - ValueReceiver: cfg.isValueReceiver(types[typ.Name], t, prog), - }) - } - - return i - default: - panic(fmt.Errorf("unknown interface %#v", typ)) + 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 { 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/vektah/gqlgen/codegen/models_build.go b/vendor/github.com/99designs/gqlgen/codegen/models_build.go index 211d4bd4..9f98a07d 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/models_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/models_build.go @@ -2,20 +2,19 @@ package codegen import ( "sort" - "strings" - "github.com/vektah/gqlgen/neelance/schema" + "github.com/vektah/gqlparser/ast" "golang.org/x/tools/go/loader" ) -func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program) ([]Model, error) { +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 := typ.(type) { - case *schema.Object: - obj, err := cfg.buildObject(types, typ) + switch typ.Kind { + case ast.Object: + obj, err := cfg.buildObject(types, typ, imports) if err != nil { return nil, err } @@ -23,8 +22,8 @@ func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program) ([]Model, continue } model = cfg.obj2Model(obj) - case *schema.InputObject: - obj, err := buildInput(types, typ) + case ast.InputObject: + obj, err := cfg.buildInput(types, typ) if err != nil { return nil, err } @@ -32,7 +31,7 @@ func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program) ([]Model, continue } model = cfg.obj2Model(obj) - case *schema.Interface, *schema.Union: + case ast.Interface, ast.Union: intf := cfg.buildInterface(types, typ, prog) if intf.IsUserDefined { continue @@ -41,12 +40,13 @@ func (cfg *Config) buildModels(types NamedTypes, prog *loader.Program) ([]Model, 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 strings.Compare(models[i].GQLType, models[j].GQLType) == -1 + return models[i].GQLType < models[j].GQLType }) return models, nil @@ -65,11 +65,10 @@ func (cfg *Config) obj2Model(obj *Object) Model { field := &obj.Fields[i] mf := ModelField{Type: field.Type, GQLName: field.GQLName} - mf.GoVarName = ucFirst(field.GQLName) - if mf.IsScalar { - if mf.GoVarName == "Id" { - mf.GoVarName = "ID" - } + if field.GoFieldName != "" { + mf.GoFieldName = field.GoFieldName + } else { + mf.GoFieldName = field.GoNameExported() } model.Fields = append(model.Fields, mf) 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/vektah/gqlgen/codegen/templates/input.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/input.gotpl index 6073daf4..f543608d 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/input.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/input.gotpl @@ -15,7 +15,7 @@ {{- range $field := .Fields }} case {{$field.GQLName|quote}}: var err error - {{ $field.Unmarshal (print "it." $field.GoVarName) "v" }} + {{ $field.Unmarshal (print "it." $field.GoFieldName) "v" }} if err != nil { return it, err } diff --git a/vendor/github.com/vektah/gqlgen/codegen/templates/interface.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/interface.gotpl index 817d0abe..84cbe500 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/interface.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/interface.gotpl @@ -1,6 +1,6 @@ {{- $interface := . }} -func (ec *executionContext) _{{$interface.GQLType}}(ctx context.Context, sel []query.Selection, obj *{{$interface.FullName}}) graphql.Marshaler { +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 diff --git a/vendor/github.com/vektah/gqlgen/codegen/templates/models.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl index e66266a5..7427d71d 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/models.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/models.gotpl @@ -1,4 +1,4 @@ -// Code generated by github.com/vektah/gqlgen, DO NOT EDIT. +// Code generated by github.com/99designs/gqlgen, DO NOT EDIT. package {{ .PackageName }} @@ -9,13 +9,17 @@ import ( ) {{ range $model := .Models }} + {{with .Description}} {{.|prefixLines "// "}} {{end}} {{- if .IsInterface }} type {{.GoType}} interface {} {{- else }} type {{.GoType}} struct { {{- range $field := .Fields }} - {{- if $field.GoVarName }} - {{ $field.GoVarName }} {{$field.Signature}} `json:"{{$field.GQLName}}"` + {{- with .Description}} + {{.|prefixLines "// "}} + {{- end}} + {{- if $field.GoFieldName }} + {{ $field.GoFieldName }} {{$field.Signature}} `json:"{{$field.GQLName}}"` {{- else }} {{ $field.GoFKName }} {{$field.GoFKType}} {{- end }} @@ -25,10 +29,13 @@ import ( {{- end}} {{ range $enum := .Enums }} + {{with .Description}}{{.|prefixLines "// "}} {{end}} type {{.GoType}} string const ( - {{ range $value := .Values -}} - {{with .Description}} {{.|prefixLines "// "}} {{end}} + {{- range $value := .Values}} + {{- with .Description}} + {{.|prefixLines "// "}} + {{- end}} {{$enum.GoType}}{{ .Name|toCamel }} {{$enum.GoType}} = {{.Name|quote}} {{- end }} ) diff --git a/vendor/github.com/vektah/gqlgen/codegen/templates/object.gotpl b/vendor/github.com/99designs/gqlgen/codegen/templates/object.gotpl index b531d5fe..e98cbe1e 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/object.gotpl +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/object.gotpl @@ -4,8 +4,8 @@ var {{ $object.GQLType|lcFirst}}Implementors = {{$object.Implementors}} // nolint: gocyclo, errcheck, gas, goconst {{- if .Stream }} -func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel []query.Selection) func() graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.Variables) +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}}, }) @@ -24,14 +24,17 @@ func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel []quer } } {{- else }} -func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel []query.Selection{{if not $object.Root}}, obj *{{$object.FullName}} {{end}}) graphql.Marshaler { - fields := graphql.CollectFields(ec.Doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.Variables) +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 @@ -40,13 +43,27 @@ func (ec *executionContext) _{{$object.GQLType}}(ctx context.Context, sel []quer out.Values[i] = graphql.MarshalString({{$object.GQLType|quote}}) {{- range $field := $object.Fields }} case "{{$field.GQLName}}": - out.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, field{{if not $object.Root}}, obj{{end}}) + {{- 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/vektah/gqlgen/codegen/templates/templates.go b/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go index 3d29b403..df909cb5 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/templates.go +++ b/vendor/github.com/99designs/gqlgen/codegen/templates/templates.go @@ -5,11 +5,19 @@ 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) { @@ -96,6 +104,8 @@ 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: @@ -137,3 +147,47 @@ func dump(val interface{}) string { 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/vektah/gqlgen/codegen/type.go b/vendor/github.com/99designs/gqlgen/codegen/type.go index 7af24b3c..8c53fe55 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/type.go +++ b/vendor/github.com/99designs/gqlgen/codegen/type.go @@ -3,6 +3,8 @@ package codegen import ( "strconv" "strings" + + "github.com/vektah/gqlparser/ast" ) type NamedTypes map[string]*NamedType @@ -26,8 +28,9 @@ type Ref struct { type Type struct { *NamedType - Modifiers []string - CastType *Ref // the type to cast to when unmarshalling + Modifiers []string + ASTType *ast.Type + AliasedType *Ref } const ( @@ -47,6 +50,9 @@ func (t Ref) PkgDot() string { } func (t Type) Signature() string { + if t.AliasedType != nil { + return strings.Join(t.Modifiers, "") + t.AliasedType.FullName() + } return strings.Join(t.Modifiers, "") + t.FullName() } @@ -109,6 +115,8 @@ func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) s if {{.raw}} != nil { if tmp1, ok := {{.raw}}.([]interface{}); ok { {{.rawSlice}} = tmp1 + } else { + {{.rawSlice}} = []interface{}{ {{.raw}} } } } {{.result}} = make({{.type}}, len({{.rawSlice}})) @@ -125,11 +133,11 @@ func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) s } realResult := result - if t.CastType != nil { + if t.AliasedType != nil { result = "castTmp" } - return tpl(`{{- if .t.CastType }} + return tpl(`{{- if .t.AliasedType }} var castTmp {{.t.FullName}} {{ end }} {{- if eq .t.GoType "map[string]interface{}" }} @@ -139,8 +147,8 @@ func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) s {{- else -}} err = (&{{.result}}).UnmarshalGQL({{.raw}}) {{- end }} - {{- if .t.CastType }} - {{ .realResult }} = {{.t.CastType.FullName}}(castTmp) + {{- if .t.AliasedType }} + {{ .realResult }} = {{.t.AliasedType.FullName}}(castTmp) {{- end }}`, map[string]interface{}{ "realResult": realResult, "result": result, @@ -150,7 +158,7 @@ func (t Type) unmarshal(result, raw string, remainingMods []string, depth int) s } func (t Type) Marshal(val string) string { - if t.CastType != nil { + if t.AliasedType != nil { val = t.GoType + "(" + val + ")" } diff --git a/vendor/github.com/vektah/gqlgen/codegen/type_build.go b/vendor/github.com/99designs/gqlgen/codegen/type_build.go index ba2874b0..f0ec6785 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/type_build.go +++ b/vendor/github.com/99designs/gqlgen/codegen/type_build.go @@ -1,12 +1,10 @@ package codegen import ( - "fmt" "go/types" "strings" - "github.com/vektah/gqlgen/neelance/common" - "github.com/vektah/gqlgen/neelance/schema" + "github.com/vektah/gqlparser/ast" "golang.org/x/tools/go/loader" ) @@ -20,7 +18,7 @@ func (cfg *Config) buildNamedTypes() NamedTypes { t.IsUserDefined = true t.Package, t.GoType = pkgAndType(userEntry.Model) } else if t.IsScalar { - t.Package = "github.com/vektah/gqlgen/graphql" + t.Package = "github.com/99designs/gqlgen/graphql" t.GoType = "String" } @@ -50,16 +48,16 @@ func (cfg *Config) bindTypes(imports *Imports, namedTypes NamedTypes, destDir st // 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 schema.NamedType) *NamedType { - switch val := schemaType.(type) { - case *schema.Scalar, *schema.Enum: - return &NamedType{GQLType: val.TypeName(), IsScalar: true} - case *schema.Interface, *schema.Union: - return &NamedType{GQLType: val.TypeName(), IsInterface: true} - case *schema.InputObject: - return &NamedType{GQLType: val.TypeName(), IsInput: true} +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: val.TypeName()} + return &NamedType{GQLType: schemaType.Name} } } @@ -73,40 +71,31 @@ func pkgAndType(name string) (string, string) { return normalizeVendor(strings.Join(parts[:len(parts)-1], ".")), parts[len(parts)-1] } -func (n NamedTypes) getType(t common.Type) *Type { +func (n NamedTypes) getType(t *ast.Type) *Type { + orig := t var modifiers []string - usePtr := true for { - if _, nonNull := t.(*common.NonNull); nonNull { - usePtr = false - } else if _, nonNull := t.(*common.List); nonNull { - usePtr = true + if t.Elem != nil { + modifiers = append(modifiers, modList) + t = t.Elem } else { - if usePtr { + if !t.NonNull { modifiers = append(modifiers, modPtr) } - usePtr = true - } - - switch val := t.(type) { - case *common.NonNull: - t = val.OfType - case *common.List: - modifiers = append(modifiers, modList) - t = val.OfType - case schema.NamedType: - t := &Type{ - NamedType: n[val.TypeName()], + if n[t.NamedType] == nil { + panic("missing type " + t.NamedType) + } + res := &Type{ + NamedType: n[t.NamedType], Modifiers: modifiers, + ASTType: orig, } - if t.IsInterface { - t.StripPtr() + if res.IsInterface { + res.StripPtr() } - return t - default: - panic(fmt.Errorf("unknown type %T", t)) + return res } } } diff --git a/vendor/github.com/vektah/gqlgen/codegen/util.go b/vendor/github.com/99designs/gqlgen/codegen/util.go index 5ff41074..1849f100 100644 --- a/vendor/github.com/vektah/gqlgen/codegen/util.go +++ b/vendor/github.com/99designs/gqlgen/codegen/util.go @@ -3,6 +3,7 @@ package codegen import ( "fmt" "go/types" + "reflect" "regexp" "strings" @@ -104,19 +105,50 @@ func findMethod(typ *types.Named, name string) *types.Func { return nil } -func findField(typ *types.Struct, name string) *types.Var { +// 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 { - if f := findField(named, name); f != nil { - return f + 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 { - if f := findField(named, name); f != nil { - return f + 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 } } } @@ -125,11 +157,16 @@ func findField(typ *types.Struct, name string) *types.Var { continue } - if strings.EqualFold(field.Name(), name) { - return field + if strings.EqualFold(field.Name(), name) && foundField == nil { + foundField = field } } - return nil + + if foundField == nil { + return nil, fmt.Errorf("no field named %s", name) + } + + return foundField, nil } type BindError struct { @@ -161,11 +198,15 @@ func (b BindErrors) Error() string { return strings.Join(errs, "\n\n") } -func bindObject(t types.Type, object *Object, imports *Imports) BindErrors { +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 { @@ -173,7 +214,7 @@ func bindObject(t types.Type, object *Object, imports *Imports) BindErrors { } // otherwise try binding to a var - varErr := bindVar(imports, t, field) + varErr := bindVar(imports, t, field, structTag) if varErr != nil { errs = append(errs, BindError{ @@ -194,7 +235,11 @@ func bindMethod(imports *Imports, t types.Type, field *Field) error { return fmt.Errorf("not a named type") } - method := findMethod(namedType, field.GQLName) + 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) } @@ -216,20 +261,26 @@ func bindMethod(imports *Imports, t types.Type, field *Field) error { } // success, args and return type match. Bind to method - field.GoMethodName = "obj." + method.Name() + field.GoFieldType = GoFieldMethod + field.GoReceiverName = "obj" + field.GoFieldName = method.Name() field.Args = newArgs return nil } -func bindVar(imports *Imports, t types.Type, field *Field) error { +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") } - structField := findField(underlying, field.GQLName) - if structField == nil { - return fmt.Errorf("no field named %s", field.GQLName) + 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 { @@ -237,7 +288,9 @@ func bindVar(imports *Imports, t types.Type, field *Field) error { } // success, bind to var - field.GoVarName = structField.Name() + field.GoFieldType = GoFieldVariable + field.GoReceiverName = "obj" + field.GoFieldName = structField.Name() return nil } @@ -249,7 +302,9 @@ nextArg: param := params.At(j) for _, oldArg := range field.Args { if strings.EqualFold(oldArg.GQLName, param.Name()) { - oldArg.Type.Modifiers = modifiersFromGoType(param.Type()) + if !field.ForceResolver { + oldArg.Type.Modifiers = modifiersFromGoType(param.Type()) + } newArgs = append(newArgs, oldArg) continue nextArg } @@ -276,7 +331,7 @@ func validateTypeBinding(imports *Imports, field *Field, goType types.Type) erro field.Type.Modifiers = modifiersFromGoType(goType) pkg, typ := pkgAndType(goType.String()) imp := imports.findByPath(pkg) - field.CastType = &Ref{GoType: typ, Import: imp} + field.AliasedType = &Ref{GoType: typ, Import: imp} return nil } 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/vektah/gqlgen/graphql/bool.go b/vendor/github.com/99designs/gqlgen/graphql/bool.go index 7053bbca..7053bbca 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/bool.go +++ b/vendor/github.com/99designs/gqlgen/graphql/bool.go diff --git a/vendor/github.com/vektah/gqlgen/graphql/context.go b/vendor/github.com/99designs/gqlgen/graphql/context.go index 8f544100..6baee83c 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/context.go +++ b/vendor/github.com/99designs/gqlgen/graphql/context.go @@ -5,45 +5,52 @@ import ( "fmt" "sync" - "github.com/vektah/gqlgen/neelance/query" + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" ) type Resolver func(ctx context.Context) (res interface{}, err error) -type ResolverMiddleware func(ctx context.Context, next Resolver) (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 *query.Document + Doc *ast.QueryDocument // ErrorPresenter will be used to generate the error // message from errors given to Error(). - ErrorPresenter ErrorPresenterFunc - Recover RecoverFunc - ResolverMiddleware ResolverMiddleware - RequestMiddleware RequestMiddleware + ErrorPresenter ErrorPresenterFunc + Recover RecoverFunc + ResolverMiddleware FieldMiddleware + DirectiveMiddleware FieldMiddleware + RequestMiddleware RequestMiddleware errorsMu sync.Mutex - Errors []*Error + 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 *query.Document, query string, variables map[string]interface{}) *RequestContext { +func NewRequestContext(doc *ast.QueryDocument, query string, variables map[string]interface{}) *RequestContext { return &RequestContext{ - Doc: doc, - RawQuery: query, - Variables: variables, - ResolverMiddleware: DefaultResolverMiddleware, - RequestMiddleware: DefaultRequestMiddleware, - Recover: DefaultRecover, - ErrorPresenter: DefaultErrorPresenter, + Doc: doc, + RawQuery: query, + Variables: variables, + ResolverMiddleware: DefaultResolverMiddleware, + DirectiveMiddleware: DefaultDirectiveMiddleware, + RequestMiddleware: DefaultRequestMiddleware, + Recover: DefaultRecover, + ErrorPresenter: DefaultErrorPresenter, } } @@ -68,54 +75,52 @@ func WithRequestContext(ctx context.Context, rc *RequestContext) context.Context } 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 path of fields to get to this resolver - Path []interface{} -} - -func (r *ResolverContext) PushField(alias string) { - r.Path = append(r.Path, alias) -} + // 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) + } + } -func (r *ResolverContext) PushIndex(index int) { - r.Path = append(r.Path, index) -} + // 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] + } -func (r *ResolverContext) Pop() { - r.Path = r.Path[0 : len(r.Path)-1] + return path } func GetResolverContext(ctx context.Context) *ResolverContext { - val := ctx.Value(resolver) - if val == nil { - return nil - } - - return val.(*ResolverContext) + val, _ := ctx.Value(resolver).(*ResolverContext) + return val } func WithResolverContext(ctx context.Context, rc *ResolverContext) context.Context { - parent := GetResolverContext(ctx) - rc.Path = nil - if parent != nil { - rc.Path = append(rc.Path, parent.Path...) - } - if rc.Field.Alias != "" { - rc.PushField(rc.Field.Alias) - } + 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 { - reqctx := GetRequestContext(ctx) resctx := GetResolverContext(ctx) - return CollectFields(reqctx.Doc, resctx.Field.Selections, satisfies, reqctx.Variables) + return CollectFields(ctx, resctx.Field.Selections, satisfies) } // Errorf sends an error string to the client, passing it through the formatter. @@ -134,6 +139,34 @@ func (c *RequestContext) Error(ctx context.Context, err error) { 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) 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/vektah/gqlgen/graphql/float.go b/vendor/github.com/99designs/gqlgen/graphql/float.go index c08b490a..d204335c 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/float.go +++ b/vendor/github.com/99designs/gqlgen/graphql/float.go @@ -1,6 +1,7 @@ package graphql import ( + "encoding/json" "fmt" "io" "strconv" @@ -18,8 +19,12 @@ func UnmarshalFloat(v interface{}) (float64, error) { 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/vektah/gqlgen/graphql/id.go b/vendor/github.com/99designs/gqlgen/graphql/id.go index 7958670c..a5a7960f 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/id.go +++ b/vendor/github.com/99designs/gqlgen/graphql/id.go @@ -1,6 +1,7 @@ package graphql import ( + "encoding/json" "fmt" "io" "strconv" @@ -15,6 +16,8 @@ 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: diff --git a/vendor/github.com/vektah/gqlgen/graphql/int.go b/vendor/github.com/99designs/gqlgen/graphql/int.go index b63b4c2a..ff87574c 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/int.go +++ b/vendor/github.com/99designs/gqlgen/graphql/int.go @@ -1,6 +1,7 @@ package graphql import ( + "encoding/json" "fmt" "io" "strconv" @@ -18,8 +19,10 @@ func UnmarshalInt(v interface{}) (int, error) { return strconv.Atoi(v) case int: return v, nil - case float64: + 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/vektah/gqlgen/neelance/introspection/query.go b/vendor/github.com/99designs/gqlgen/graphql/introspection/query.go index b1e4fbc6..b1e4fbc6 100644 --- a/vendor/github.com/vektah/gqlgen/neelance/introspection/query.go +++ b/vendor/github.com/99designs/gqlgen/graphql/introspection/query.go 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/vektah/gqlgen/graphql/jsonw.go b/vendor/github.com/99designs/gqlgen/graphql/jsonw.go index ef9e69c7..c112444a 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/jsonw.go +++ b/vendor/github.com/99designs/gqlgen/graphql/jsonw.go @@ -15,9 +15,9 @@ var closeBracket = []byte(`]`) var colon = []byte(`:`) var comma = []byte(`,`) -var Null = lit(nullLit) -var True = lit(trueLit) -var False = lit(falseLit) +var Null = &lit{nullLit} +var True = &lit{trueLit} +var False = &lit{falseLit} type Marshaler interface { MarshalGQL(w io.Writer) @@ -76,8 +76,8 @@ func (a Array) MarshalGQL(writer io.Writer) { writer.Write(closeBracket) } -func lit(b []byte) Marshaler { - return WriterFunc(func(w io.Writer) { - w.Write(b) - }) +type lit struct{ b []byte } + +func (l lit) MarshalGQL(w io.Writer) { + w.Write(l.b) } diff --git a/vendor/github.com/vektah/gqlgen/graphql/map.go b/vendor/github.com/99designs/gqlgen/graphql/map.go index 1e91d1d9..1e91d1d9 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/map.go +++ b/vendor/github.com/99designs/gqlgen/graphql/map.go diff --git a/vendor/github.com/vektah/gqlgen/graphql/oneshot.go b/vendor/github.com/99designs/gqlgen/graphql/oneshot.go index dd31f5ba..dd31f5ba 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/oneshot.go +++ b/vendor/github.com/99designs/gqlgen/graphql/oneshot.go diff --git a/vendor/github.com/vektah/gqlgen/graphql/recovery.go b/vendor/github.com/99designs/gqlgen/graphql/recovery.go index 3aa032dc..3aa032dc 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/recovery.go +++ b/vendor/github.com/99designs/gqlgen/graphql/recovery.go diff --git a/vendor/github.com/vektah/gqlgen/graphql/response.go b/vendor/github.com/99designs/gqlgen/graphql/response.go index c0dc1c23..18664dca 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/response.go +++ b/vendor/github.com/99designs/gqlgen/graphql/response.go @@ -4,15 +4,17 @@ import ( "context" "encoding/json" "fmt" + + "github.com/vektah/gqlparser/gqlerror" ) type Response struct { Data json.RawMessage `json:"data"` - Errors []*Error `json:"errors,omitempty"` + Errors gqlerror.List `json:"errors,omitempty"` } func ErrorResponse(ctx context.Context, messagef string, args ...interface{}) *Response { return &Response{ - Errors: []*Error{{Message: fmt.Sprintf(messagef, args...)}}, + Errors: gqlerror.List{{Message: fmt.Sprintf(messagef, args...)}}, } } diff --git a/vendor/github.com/vektah/gqlgen/graphql/string.go b/vendor/github.com/99designs/gqlgen/graphql/string.go index d5fb3294..d5fb3294 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/string.go +++ b/vendor/github.com/99designs/gqlgen/graphql/string.go diff --git a/vendor/github.com/vektah/gqlgen/graphql/time.go b/vendor/github.com/99designs/gqlgen/graphql/time.go index 4f448560..4f448560 100644 --- a/vendor/github.com/vektah/gqlgen/graphql/time.go +++ b/vendor/github.com/99designs/gqlgen/graphql/time.go 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/vektah/gqlgen/handler/graphql.go b/vendor/github.com/99designs/gqlgen/handler/graphql.go index 4a5c61f5..9d222826 100644 --- a/vendor/github.com/vektah/gqlgen/handler/graphql.go +++ b/vendor/github.com/99designs/gqlgen/handler/graphql.go @@ -4,14 +4,18 @@ 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/vektah/gqlgen/graphql" - "github.com/vektah/gqlgen/neelance/errors" - "github.com/vektah/gqlgen/neelance/query" - "github.com/vektah/gqlgen/neelance/validation" + "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 { @@ -21,14 +25,16 @@ type params struct { } type Config struct { - upgrader websocket.Upgrader - recover graphql.RecoverFunc - errorPresenter graphql.ErrorPresenterFunc - resolverHook graphql.ResolverMiddleware - requestHook graphql.RequestMiddleware + cacheSize int + upgrader websocket.Upgrader + recover graphql.RecoverFunc + errorPresenter graphql.ErrorPresenterFunc + resolverHook graphql.FieldMiddleware + requestHook graphql.RequestMiddleware + complexityLimit int } -func (c *Config) newRequestContext(doc *query.Document, query string, variables map[string]interface{}) *graphql.RequestContext { +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 @@ -72,11 +78,17 @@ func ErrorPresenter(f graphql.ErrorPresenterFunc) Option { } } +// 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. -// It will only be called for user defined resolvers, any direct binding to models is assumed -// to cost nothing. -func ResolverMiddleware(middleware graphql.ResolverMiddleware) Option { +func ResolverMiddleware(middleware graphql.FieldMiddleware) Option { return func(cfg *Config) { if cfg.resolverHook == nil { cfg.resolverHook = middleware @@ -110,8 +122,19 @@ func RequestMiddleware(middleware graphql.RequestMiddleware) Option { } } +// 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, @@ -122,6 +145,17 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc 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") @@ -141,13 +175,13 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc reqParams.OperationName = r.URL.Query().Get("operationName") if variables := r.URL.Query().Get("variables"); variables != "" { - if err := json.Unmarshal([]byte(variables), &reqParams.Variables); err != nil { + 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 := json.NewDecoder(r.Body).Decode(&reqParams); err != nil { + if err := jsonDecode(r.Body, &reqParams); err != nil { sendErrorf(w, http.StatusBadRequest, "json body could not be decoded: "+err.Error()) return } @@ -157,25 +191,42 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc } w.Header().Set("Content-Type", "application/json") - doc, qErr := query.Parse(reqParams.Query) - if qErr != nil { - sendError(w, http.StatusUnprocessableEntity, qErr) + 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 } - errs := validation.Validate(exec.Schema(), doc) - if len(errs) != 0 { - sendError(w, http.StatusUnprocessableEntity, errs...) + if op.Operation != ast.Query && r.Method == http.MethodGet { + sendErrorf(w, http.StatusUnprocessableEntity, "GET requests only allow query operations") return } - op, err := doc.GetOperation(reqParams.OperationName) + vars, err := validator.VariableValues(exec.Schema(), op, reqParams.Variables) if err != nil { - sendErrorf(w, http.StatusUnprocessableEntity, err.Error()) + sendError(w, http.StatusUnprocessableEntity, err) return } - - reqCtx := cfg.newRequestContext(doc, reqParams.Query, reqParams.Variables) + reqCtx := cfg.newRequestContext(doc, reqParams.Query, vars) ctx := graphql.WithRequestContext(r.Context(), reqCtx) defer func() { @@ -185,14 +236,22 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc } }() - switch op.Type { - case query.Query: + 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 query.Mutation: + case ast.Mutation: b, err := json.Marshal(exec.Mutation(ctx, op)) if err != nil { panic(err) @@ -204,26 +263,15 @@ func GraphQL(exec graphql.ExecutableSchema, options ...Option) http.HandlerFunc }) } -func sendError(w http.ResponseWriter, code int, errors ...*errors.QueryError) { - w.WriteHeader(code) - var errs []*graphql.Error - for _, err := range errors { - var locations []graphql.ErrorLocation - for _, l := range err.Locations { - fmt.Println(graphql.ErrorLocation(l)) - locations = append(locations, graphql.ErrorLocation{ - Line: l.Line, - Column: l.Column, - }) - } +func jsonDecode(r io.Reader, val interface{}) error { + dec := json.NewDecoder(r) + dec.UseNumber() + return dec.Decode(val) +} - errs = append(errs, &graphql.Error{ - Message: err.Message, - Path: err.Path, - Locations: locations, - }) - } - b, err := json.Marshal(&graphql.Response{Errors: errs}) +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) } @@ -231,5 +279,5 @@ func sendError(w http.ResponseWriter, code int, errors ...*errors.QueryError) { } func sendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { - sendError(w, code, &errors.QueryError{Message: fmt.Sprintf(format, args...)}) + sendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) } diff --git a/vendor/github.com/vektah/gqlgen/handler/playground.go b/vendor/github.com/99designs/gqlgen/handler/playground.go index 44533590..d0ada8ca 100644 --- a/vendor/github.com/vektah/gqlgen/handler/playground.go +++ b/vendor/github.com/99designs/gqlgen/handler/playground.go @@ -30,6 +30,9 @@ var page = template.Must(template.New("graphiql").Parse(`<!DOCTYPE html> GraphQLPlayground.init(root, { endpoint: location.protocol + '//' + location.host + '{{.endpoint}}', subscriptionsEndpoint: wsProto + '//' + location.host + '{{.endpoint }}', + settings: { + 'request.credentials': 'same-origin' + } }) }) </script> @@ -42,7 +45,7 @@ func Playground(title string, endpoint string) http.HandlerFunc { err := page.Execute(w, map[string]string{ "title": title, "endpoint": endpoint, - "version": "1.4.3", + "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/vektah/gqlgen/handler/websocket.go b/vendor/github.com/99designs/gqlgen/handler/websocket.go index e80748ca..2be1e87f 100644 --- a/vendor/github.com/vektah/gqlgen/handler/websocket.go +++ b/vendor/github.com/99designs/gqlgen/handler/websocket.go @@ -1,6 +1,7 @@ package handler import ( + "bytes" "context" "encoding/json" "fmt" @@ -8,11 +9,12 @@ import ( "net/http" "sync" + "github.com/99designs/gqlgen/graphql" "github.com/gorilla/websocket" - "github.com/vektah/gqlgen/graphql" - "github.com/vektah/gqlgen/neelance/errors" - "github.com/vektah/gqlgen/neelance/query" - "github.com/vektah/gqlgen/neelance/validation" + "github.com/vektah/gqlparser" + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/validator" ) const ( @@ -113,7 +115,7 @@ func (c *wsConnection) run() { closer := c.active[message.ID] c.mu.Unlock() if closer == nil { - c.sendError(message.ID, errors.Errorf("%s is not running, cannot stop", message.ID)) + c.sendError(message.ID, gqlerror.Errorf("%s is not running, cannot stop", message.ID)) continue } @@ -131,35 +133,34 @@ func (c *wsConnection) run() { func (c *wsConnection) subscribe(message *operationMessage) bool { var reqParams params - if err := json.Unmarshal(message.Payload, &reqParams); err != nil { + if err := jsonDecode(bytes.NewReader(message.Payload), &reqParams); err != nil { c.sendConnectionError("invalid json") return false } - doc, qErr := query.Parse(reqParams.Query) + doc, qErr := gqlparser.LoadQuery(c.exec.Schema(), reqParams.Query) if qErr != nil { - c.sendError(message.ID, qErr) + c.sendError(message.ID, qErr...) return true } - errs := validation.Validate(c.exec.Schema(), doc) - if len(errs) != 0 { - c.sendError(message.ID, errs...) + op := doc.Operations.ForName(reqParams.OperationName) + if op == nil { + c.sendError(message.ID, gqlerror.Errorf("operation %s not found", reqParams.OperationName)) return true } - op, err := doc.GetOperation(reqParams.OperationName) + vars, err := validator.VariableValues(c.exec.Schema(), op, reqParams.Variables) if err != nil { - c.sendError(message.ID, errors.Errorf("%s", err.Error())) + c.sendError(message.ID, err) return true } - - reqCtx := c.cfg.newRequestContext(doc, reqParams.Query, reqParams.Variables) + reqCtx := c.cfg.newRequestContext(doc, reqParams.Query, vars) ctx := graphql.WithRequestContext(c.ctx, reqCtx) - if op.Type != query.Subscription { + if op.Operation != ast.Subscription { var result *graphql.Response - if op.Type == query.Query { + if op.Operation == ast.Query { result = c.exec.Query(ctx, op) } else { result = c.exec.Mutation(ctx, op) @@ -178,7 +179,7 @@ func (c *wsConnection) subscribe(message *operationMessage) bool { defer func() { if r := recover(); r != nil { userErr := reqCtx.Recover(ctx, r) - c.sendError(message.ID, &errors.QueryError{Message: userErr.Error()}) + c.sendError(message.ID, &gqlerror.Error{Message: userErr.Error()}) } }() next := c.exec.Subscription(ctx, op) @@ -200,14 +201,14 @@ func (c *wsConnection) subscribe(message *operationMessage) bool { func (c *wsConnection) sendData(id string, response *graphql.Response) { b, err := json.Marshal(response) if err != nil { - c.sendError(id, errors.Errorf("unable to encode json response: %s", err.Error())) + 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 ...*errors.QueryError) { +func (c *wsConnection) sendError(id string, errors ...*gqlerror.Error) { var errs []error for _, err := range errors { errs = append(errs, err) @@ -220,7 +221,7 @@ func (c *wsConnection) sendError(id string, errors ...*errors.QueryError) { } func (c *wsConnection) sendConnectionError(format string, args ...interface{}) { - b, err := json.Marshal(&graphql.Error{Message: fmt.Sprintf(format, args...)}) + b, err := json.Marshal(&gqlerror.Error{Message: fmt.Sprintf(format, args...)}) if err != nil { panic(err) } @@ -229,11 +230,17 @@ func (c *wsConnection) sendConnectionError(format string, args ...interface{}) { } func (c *wsConnection) readOp() *operationMessage { + _, r, err := c.conn.NextReader() + if err != nil { + c.sendConnectionError("invalid json") + return nil + } message := operationMessage{} - if err := c.conn.ReadJSON(&message); err != nil { + if err := jsonDecode(r, &message); err != nil { c.sendConnectionError("invalid json") return nil } + return &message } 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 +} diff --git a/vendor/github.com/agnivade/levenshtein/.gitignore b/vendor/github.com/agnivade/levenshtein/.gitignore new file mode 100644 index 00000000..345780a4 --- /dev/null +++ b/vendor/github.com/agnivade/levenshtein/.gitignore @@ -0,0 +1,5 @@ +coverage.txt +fuzz/fuzz-fuzz.zip +fuzz/corpus/corpus/* +fuzz/corpus/suppressions/* +fuzz/corpus/crashes/* diff --git a/vendor/github.com/agnivade/levenshtein/.travis.yml b/vendor/github.com/agnivade/levenshtein/.travis.yml new file mode 100644 index 00000000..06b3ba55 --- /dev/null +++ b/vendor/github.com/agnivade/levenshtein/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: +- 1.8.x +- 1.9.x +- 1.10.x +- tip diff --git a/vendor/github.com/agnivade/levenshtein/License.txt b/vendor/github.com/agnivade/levenshtein/License.txt new file mode 100644 index 00000000..54b51f49 --- /dev/null +++ b/vendor/github.com/agnivade/levenshtein/License.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Agniva De Sarker + +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/agnivade/levenshtein/Makefile b/vendor/github.com/agnivade/levenshtein/Makefile new file mode 100644 index 00000000..4bef27dd --- /dev/null +++ b/vendor/github.com/agnivade/levenshtein/Makefile @@ -0,0 +1,13 @@ +all: test install + +install: + go install + +lint: + gofmt -l -s -w . && go tool vet -all . && golint + +test: + go test -race -v -coverprofile=coverage.txt -covermode=atomic + +bench: + go test -run=XXX -bench=. -benchmem diff --git a/vendor/github.com/agnivade/levenshtein/README.md b/vendor/github.com/agnivade/levenshtein/README.md new file mode 100644 index 00000000..b0fd81df --- /dev/null +++ b/vendor/github.com/agnivade/levenshtein/README.md @@ -0,0 +1,57 @@ +levenshtein [![Build Status](https://travis-ci.org/agnivade/levenshtein.svg?branch=master)](https://travis-ci.org/agnivade/levenshtein) [![Go Report Card](https://goreportcard.com/badge/github.com/agnivade/levenshtein)](https://goreportcard.com/report/github.com/agnivade/levenshtein) [![GoDoc](https://godoc.org/github.com/agnivade/levenshtein?status.svg)](https://godoc.org/github.com/agnivade/levenshtein) +=========== + +[Go](http://golang.org) package to calculate the [Levenshtein Distance](http://en.wikipedia.org/wiki/Levenshtein_distance) + +The library is fully capable of working with non-ascii strings. But the strings are not normalized. That is left as a user-dependant use case. Please normalize the strings before passing it to the library if you have such a requirement. +- https://blog.golang.org/normalization + +Install +------- + + go get github.com/agnivade/levenshtein + +Example +------- + +```go +package main + +import ( + "fmt" + "github.com/agnivade/levenshtein" +) + +func main() { + s1 := "kitten" + s2 := "sitting" + distance := levenshtein.ComputeDistance(s1, s2) + fmt.Printf("The distance between %s and %s is %d.\n", s1, s2, distance) + // Output: + // The distance between kitten and sitting is 3. +} + +``` + +Benchmarks +---------- + +``` +name time/op +Simple/ASCII-4 537ns ± 2% +Simple/French-4 956ns ± 0% +Simple/Nordic-4 1.95µs ± 1% +Simple/Tibetan-4 1.53µs ± 2% + +name alloc/op +Simple/ASCII-4 96.0B ± 0% +Simple/French-4 128B ± 0% +Simple/Nordic-4 192B ± 0% +Simple/Tibetan-4 144B ± 0% + +name allocs/op +Simple/ASCII-4 1.00 ± 0% +Simple/French-4 1.00 ± 0% +Simple/Nordic-4 1.00 ± 0% +Simple/Tibetan-4 1.00 ± 0% +``` diff --git a/vendor/github.com/agnivade/levenshtein/go.mod b/vendor/github.com/agnivade/levenshtein/go.mod new file mode 100644 index 00000000..b2921fb3 --- /dev/null +++ b/vendor/github.com/agnivade/levenshtein/go.mod @@ -0,0 +1 @@ +module github.com/agnivade/levenshtein diff --git a/vendor/github.com/agnivade/levenshtein/levenshtein.go b/vendor/github.com/agnivade/levenshtein/levenshtein.go new file mode 100644 index 00000000..e215813f --- /dev/null +++ b/vendor/github.com/agnivade/levenshtein/levenshtein.go @@ -0,0 +1,71 @@ +// Package levenshtein is a Go implementation to calculate Levenshtein Distance. +// +// Implementation taken from +// https://gist.github.com/andrei-m/982927#gistcomment-1931258 +package levenshtein + +// ComputeDistance computes the levenshtein distance between the two +// strings passed as an argument. The return value is the levenshtein distance +// +// Works on runes (Unicode code points) but does not normalize +// the input strings. See https://blog.golang.org/normalization +// and the golang.org/x/text/unicode/norm pacage. +func ComputeDistance(a, b string) int { + if a == b { + return 0 + } + + // We need to convert to []rune if the strings are non-ascii. + // This could be avoided by using utf8.RuneCountInString + // and then doing some juggling with rune indices. + // The primary challenge is keeping track of the previous rune. + // With a range loop, its not that easy. And with a for-loop + // we need to keep track of the inter-rune width using utf8.DecodeRuneInString + s1 := []rune(a) + s2 := []rune(b) + + // swap to save some memory O(min(a,b)) instead of O(a) + if len(s1) > len(s2) { + s1, s2 = s2, s1 + } + lenS1 := len(s1) + lenS2 := len(s2) + + // init the row + x := make([]int, lenS1+1) + for i := 0; i <= lenS1; i++ { + x[i] = i + } + + // fill in the rest + for i := 1; i <= lenS2; i++ { + prev := i + var current int + + for j := 1; j <= lenS1; j++ { + + if s2[i-1] == s1[j-1] { + current = x[j-1] // match + } else { + current = min(x[j-1]+1, prev+1, x[j]+1) + } + x[j-1] = prev + prev = current + } + x[lenS1] = prev + } + return x[lenS1] +} + +func min(a, b, c int) int { + if a < b { + if a < c { + return a + } + } else { + if b < c { + return b + } + } + return c +} diff --git a/vendor/github.com/hashicorp/golang-lru/.gitignore b/vendor/github.com/hashicorp/golang-lru/.gitignore new file mode 100644 index 00000000..83656241 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test diff --git a/vendor/github.com/hashicorp/golang-lru/2q.go b/vendor/github.com/hashicorp/golang-lru/2q.go new file mode 100644 index 00000000..e474cd07 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/2q.go @@ -0,0 +1,223 @@ +package lru + +import ( + "fmt" + "sync" + + "github.com/hashicorp/golang-lru/simplelru" +) + +const ( + // Default2QRecentRatio is the ratio of the 2Q cache dedicated + // to recently added entries that have only been accessed once. + Default2QRecentRatio = 0.25 + + // Default2QGhostEntries is the default ratio of ghost + // entries kept to track entries recently evicted + Default2QGhostEntries = 0.50 +) + +// TwoQueueCache is a thread-safe fixed size 2Q cache. +// 2Q is an enhancement over the standard LRU cache +// in that it tracks both frequently and recently used +// entries separately. This avoids a burst in access to new +// entries from evicting frequently used entries. It adds some +// additional tracking overhead to the standard LRU cache, and is +// computationally about 2x the cost, and adds some metadata over +// head. The ARCCache is similar, but does not require setting any +// parameters. +type TwoQueueCache struct { + size int + recentSize int + + recent simplelru.LRUCache + frequent simplelru.LRUCache + recentEvict simplelru.LRUCache + lock sync.RWMutex +} + +// New2Q creates a new TwoQueueCache using the default +// values for the parameters. +func New2Q(size int) (*TwoQueueCache, error) { + return New2QParams(size, Default2QRecentRatio, Default2QGhostEntries) +} + +// New2QParams creates a new TwoQueueCache using the provided +// parameter values. +func New2QParams(size int, recentRatio float64, ghostRatio float64) (*TwoQueueCache, error) { + if size <= 0 { + return nil, fmt.Errorf("invalid size") + } + if recentRatio < 0.0 || recentRatio > 1.0 { + return nil, fmt.Errorf("invalid recent ratio") + } + if ghostRatio < 0.0 || ghostRatio > 1.0 { + return nil, fmt.Errorf("invalid ghost ratio") + } + + // Determine the sub-sizes + recentSize := int(float64(size) * recentRatio) + evictSize := int(float64(size) * ghostRatio) + + // Allocate the LRUs + recent, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + frequent, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + recentEvict, err := simplelru.NewLRU(evictSize, nil) + if err != nil { + return nil, err + } + + // Initialize the cache + c := &TwoQueueCache{ + size: size, + recentSize: recentSize, + recent: recent, + frequent: frequent, + recentEvict: recentEvict, + } + return c, nil +} + +// Get looks up a key's value from the cache. +func (c *TwoQueueCache) Get(key interface{}) (value interface{}, ok bool) { + c.lock.Lock() + defer c.lock.Unlock() + + // Check if this is a frequent value + if val, ok := c.frequent.Get(key); ok { + return val, ok + } + + // If the value is contained in recent, then we + // promote it to frequent + if val, ok := c.recent.Peek(key); ok { + c.recent.Remove(key) + c.frequent.Add(key, val) + return val, ok + } + + // No hit + return nil, false +} + +// Add adds a value to the cache. +func (c *TwoQueueCache) Add(key, value interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + + // Check if the value is frequently used already, + // and just update the value + if c.frequent.Contains(key) { + c.frequent.Add(key, value) + return + } + + // Check if the value is recently used, and promote + // the value into the frequent list + if c.recent.Contains(key) { + c.recent.Remove(key) + c.frequent.Add(key, value) + return + } + + // If the value was recently evicted, add it to the + // frequently used list + if c.recentEvict.Contains(key) { + c.ensureSpace(true) + c.recentEvict.Remove(key) + c.frequent.Add(key, value) + return + } + + // Add to the recently seen list + c.ensureSpace(false) + c.recent.Add(key, value) + return +} + +// ensureSpace is used to ensure we have space in the cache +func (c *TwoQueueCache) ensureSpace(recentEvict bool) { + // If we have space, nothing to do + recentLen := c.recent.Len() + freqLen := c.frequent.Len() + if recentLen+freqLen < c.size { + return + } + + // If the recent buffer is larger than + // the target, evict from there + if recentLen > 0 && (recentLen > c.recentSize || (recentLen == c.recentSize && !recentEvict)) { + k, _, _ := c.recent.RemoveOldest() + c.recentEvict.Add(k, nil) + return + } + + // Remove from the frequent list otherwise + c.frequent.RemoveOldest() +} + +// Len returns the number of items in the cache. +func (c *TwoQueueCache) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + return c.recent.Len() + c.frequent.Len() +} + +// Keys returns a slice of the keys in the cache. +// The frequently used keys are first in the returned slice. +func (c *TwoQueueCache) Keys() []interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + k1 := c.frequent.Keys() + k2 := c.recent.Keys() + return append(k1, k2...) +} + +// Remove removes the provided key from the cache. +func (c *TwoQueueCache) Remove(key interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + if c.frequent.Remove(key) { + return + } + if c.recent.Remove(key) { + return + } + if c.recentEvict.Remove(key) { + return + } +} + +// Purge is used to completely clear the cache. +func (c *TwoQueueCache) Purge() { + c.lock.Lock() + defer c.lock.Unlock() + c.recent.Purge() + c.frequent.Purge() + c.recentEvict.Purge() +} + +// Contains is used to check if the cache contains a key +// without updating recency or frequency. +func (c *TwoQueueCache) Contains(key interface{}) bool { + c.lock.RLock() + defer c.lock.RUnlock() + return c.frequent.Contains(key) || c.recent.Contains(key) +} + +// Peek is used to inspect the cache value of a key +// without updating recency or frequency. +func (c *TwoQueueCache) Peek(key interface{}) (value interface{}, ok bool) { + c.lock.RLock() + defer c.lock.RUnlock() + if val, ok := c.frequent.Peek(key); ok { + return val, ok + } + return c.recent.Peek(key) +} diff --git a/vendor/github.com/hashicorp/golang-lru/LICENSE b/vendor/github.com/hashicorp/golang-lru/LICENSE new file mode 100644 index 00000000..be2cc4df --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/LICENSE @@ -0,0 +1,362 @@ +Mozilla Public License, version 2.0 + +1. Definitions + +1.1. "Contributor" + + means each individual or legal entity that creates, contributes to the + creation of, or owns Covered Software. + +1.2. "Contributor Version" + + means the combination of the Contributions of others (if any) used by a + Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + + means Source Code Form to which the initial Contributor has attached the + notice in Exhibit A, the Executable Form of such Source Code Form, and + Modifications of such Source Code Form, in each case including portions + thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + a. that the initial Contributor has attached the notice described in + Exhibit B to the Covered Software; or + + b. that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the terms of + a Secondary License. + +1.6. "Executable Form" + + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + + means a work that combines Covered Software with other material, in a + separate file or files, that is not Covered Software. + +1.8. "License" + + means this document. + +1.9. "Licensable" + + means having the right to grant, to the maximum extent possible, whether + at the time of the initial grant or subsequently, any and all of the + rights conveyed by this License. + +1.10. "Modifications" + + means any of the following: + + a. any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered Software; or + + b. any new file in Source Code Form that contains any Covered Software. + +1.11. "Patent Claims" of a Contributor + + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the License, + by the making, using, selling, offering for sale, having made, import, + or transfer of either its Contributions or its Contributor Version. + +1.12. "Secondary License" + + means either the GNU General Public License, Version 2.0, the GNU Lesser + General Public License, Version 2.1, the GNU Affero General Public + License, Version 3.0, or any later versions of those licenses. + +1.13. "Source Code Form" + + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that controls, is + controlled by, or is under common control with You. For purposes of this + definition, "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by contract or + otherwise, or (b) ownership of more than fifty percent (50%) of the + outstanding shares or beneficial ownership of such entity. + + +2. License Grants and Conditions + +2.1. Grants + + Each Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + a. under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + + b. under Patent Claims of such Contributor to make, use, sell, offer for + sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + + The licenses granted in Section 2.1 with respect to any Contribution + become effective for each Contribution on the date the Contributor first + distributes such Contribution. + +2.3. Limitations on Grant Scope + + The licenses granted in this Section 2 are the only rights granted under + this License. No additional rights or licenses will be implied from the + distribution or licensing of Covered Software under this License. + Notwithstanding Section 2.1(b) above, no patent license is granted by a + Contributor: + + a. for any code that a Contributor has removed from Covered Software; or + + b. for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + + c. under Patent Claims infringed by Covered Software in the absence of + its Contributions. + + This License does not grant any rights in the trademarks, service marks, + or logos of any Contributor (except as may be necessary to comply with + the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + + No Contributor makes additional grants as a result of Your choice to + distribute the Covered Software under a subsequent version of this + License (see Section 10.2) or under the terms of a Secondary License (if + permitted under the terms of Section 3.3). + +2.5. Representation + + Each Contributor represents that the Contributor believes its + Contributions are its original creation(s) or it has sufficient rights to + grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + + This License is not intended to limit any rights You have under + applicable copyright doctrines of fair use, fair dealing, or other + equivalents. + +2.7. Conditions + + Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in + Section 2.1. + + +3. Responsibilities + +3.1. Distribution of Source Form + + All distribution of Covered Software in Source Code Form, including any + Modifications that You create or to which You contribute, must be under + the terms of this License. You must inform recipients that the Source + Code Form of the Covered Software is governed by the terms of this + License, and how they can obtain a copy of this License. You may not + attempt to alter or restrict the recipients' rights in the Source Code + Form. + +3.2. Distribution of Executable Form + + If You distribute Covered Software in Executable Form then: + + a. such Covered Software must also be made available in Source Code Form, + as described in Section 3.1, and You must inform recipients of the + Executable Form how they can obtain a copy of such Source Code Form by + reasonable means in a timely manner, at a charge no more than the cost + of distribution to the recipient; and + + b. You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter the + recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + + You may create and distribute a Larger Work under terms of Your choice, + provided that You also comply with the requirements of this License for + the Covered Software. If the Larger Work is a combination of Covered + Software with a work governed by one or more Secondary Licenses, and the + Covered Software is not Incompatible With Secondary Licenses, this + License permits You to additionally distribute such Covered Software + under the terms of such Secondary License(s), so that the recipient of + the Larger Work may, at their option, further distribute the Covered + Software under the terms of either this License or such Secondary + License(s). + +3.4. Notices + + You may not remove or alter the substance of any license notices + (including copyright notices, patent notices, disclaimers of warranty, or + limitations of liability) contained within the Source Code Form of the + Covered Software, except that You may alter any license notices to the + extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + + You may choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of Covered + Software. However, You may do so only on Your own behalf, and not on + behalf of any Contributor. You must make it absolutely clear that any + such warranty, support, indemnity, or liability obligation is offered by + You alone, and You hereby agree to indemnify every Contributor for any + liability incurred by such Contributor as a result of warranty, support, + indemnity or liability terms You offer. You may include additional + disclaimers of warranty and limitations of liability specific to any + jurisdiction. + +4. Inability to Comply Due to Statute or Regulation + + If it is impossible for You to comply with any of the terms of this License + with respect to some or all of the Covered Software due to statute, + judicial order, or regulation then You must: (a) comply with the terms of + this License to the maximum extent possible; and (b) describe the + limitations and the code they affect. Such description must be placed in a + text file included with all distributions of the Covered Software under + this License. Except to the extent prohibited by statute or regulation, + such description must be sufficiently detailed for a recipient of ordinary + skill to be able to understand it. + +5. Termination + +5.1. The rights granted under this License will terminate automatically if You + fail to comply with any of its terms. However, if You become compliant, + then the rights granted under this License from a particular Contributor + are reinstated (a) provisionally, unless and until such Contributor + explicitly and finally terminates Your grants, and (b) on an ongoing + basis, if such Contributor fails to notify You of the non-compliance by + some reasonable means prior to 60 days after You have come back into + compliance. Moreover, Your grants from a particular Contributor are + reinstated on an ongoing basis if such Contributor notifies You of the + non-compliance by some reasonable means, this is the first time You have + received notice of non-compliance with this License from such + Contributor, and You become compliant prior to 30 days after Your receipt + of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent + infringement claim (excluding declaratory judgment actions, + counter-claims, and cross-claims) alleging that a Contributor Version + directly or indirectly infringes any patent, then the rights granted to + You by any and all Contributors for the Covered Software under Section + 2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user + license agreements (excluding distributors and resellers) which have been + validly granted by You or Your distributors under this License prior to + termination shall survive termination. + +6. Disclaimer of Warranty + + Covered Software is provided under this License on an "as is" basis, + without warranty of any kind, either expressed, implied, or statutory, + including, without limitation, warranties that the Covered Software is free + of defects, merchantable, fit for a particular purpose or non-infringing. + The entire risk as to the quality and performance of the Covered Software + is with You. Should any Covered Software prove defective in any respect, + You (not any Contributor) assume the cost of any necessary servicing, + repair, or correction. This disclaimer of warranty constitutes an essential + part of this License. No use of any Covered Software is authorized under + this License except under this disclaimer. + +7. Limitation of Liability + + Under no circumstances and under no legal theory, whether tort (including + negligence), contract, or otherwise, shall any Contributor, or anyone who + distributes Covered Software as permitted above, be liable to You for any + direct, indirect, special, incidental, or consequential damages of any + character including, without limitation, damages for lost profits, loss of + goodwill, work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses, even if such party shall have been + informed of the possibility of such damages. This limitation of liability + shall not apply to liability for death or personal injury resulting from + such party's negligence to the extent applicable law prohibits such + limitation. Some jurisdictions do not allow the exclusion or limitation of + incidental or consequential damages, so this exclusion and limitation may + not apply to You. + +8. Litigation + + Any litigation relating to this License may be brought only in the courts + of a jurisdiction where the defendant maintains its principal place of + business and such litigation shall be governed by laws of that + jurisdiction, without reference to its conflict-of-law provisions. Nothing + in this Section shall prevent a party's ability to bring cross-claims or + counter-claims. + +9. Miscellaneous + + This License represents the complete agreement concerning the subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. Any law or regulation which provides that + the language of a contract shall be construed against the drafter shall not + be used to construe this License against a Contributor. + + +10. Versions of the License + +10.1. New Versions + + Mozilla Foundation is the license steward. Except as provided in Section + 10.3, no one other than the license steward has the right to modify or + publish new versions of this License. Each version will be given a + distinguishing version number. + +10.2. Effect of New Versions + + You may distribute the Covered Software under the terms of the version + of the License under which You originally received the Covered Software, + or under the terms of any subsequent version published by the license + steward. + +10.3. Modified Versions + + If you create software not governed by this License, and you want to + create a new license for such software, you may create and use a + modified version of this License if you rename the license and remove + any references to the name of the license steward (except to note that + such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary + Licenses If You choose to distribute Source Code Form that is + Incompatible With Secondary Licenses under the terms of this version of + the License, the notice described in Exhibit B of this License must be + attached. + +Exhibit A - Source Code Form License Notice + + This Source Code Form is subject to the + terms of the Mozilla Public License, v. + 2.0. If a copy of the MPL was not + distributed with this file, You can + obtain one at + http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular file, +then You may include the notice in a location (such as a LICENSE file in a +relevant directory) where a recipient would be likely to look for such a +notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice + + This Source Code Form is "Incompatible + With Secondary Licenses", as defined by + the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/golang-lru/README.md b/vendor/github.com/hashicorp/golang-lru/README.md new file mode 100644 index 00000000..33e58cfa --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/README.md @@ -0,0 +1,25 @@ +golang-lru +========== + +This provides the `lru` package which implements a fixed-size +thread safe LRU cache. It is based on the cache in Groupcache. + +Documentation +============= + +Full docs are available on [Godoc](http://godoc.org/github.com/hashicorp/golang-lru) + +Example +======= + +Using the LRU is very simple: + +```go +l, _ := New(128) +for i := 0; i < 256; i++ { + l.Add(i, nil) +} +if l.Len() != 128 { + panic(fmt.Sprintf("bad len: %v", l.Len())) +} +``` diff --git a/vendor/github.com/hashicorp/golang-lru/arc.go b/vendor/github.com/hashicorp/golang-lru/arc.go new file mode 100644 index 00000000..555225a2 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/arc.go @@ -0,0 +1,257 @@ +package lru + +import ( + "sync" + + "github.com/hashicorp/golang-lru/simplelru" +) + +// ARCCache is a thread-safe fixed size Adaptive Replacement Cache (ARC). +// ARC is an enhancement over the standard LRU cache in that tracks both +// frequency and recency of use. This avoids a burst in access to new +// entries from evicting the frequently used older entries. It adds some +// additional tracking overhead to a standard LRU cache, computationally +// it is roughly 2x the cost, and the extra memory overhead is linear +// with the size of the cache. ARC has been patented by IBM, but is +// similar to the TwoQueueCache (2Q) which requires setting parameters. +type ARCCache struct { + size int // Size is the total capacity of the cache + p int // P is the dynamic preference towards T1 or T2 + + t1 simplelru.LRUCache // T1 is the LRU for recently accessed items + b1 simplelru.LRUCache // B1 is the LRU for evictions from t1 + + t2 simplelru.LRUCache // T2 is the LRU for frequently accessed items + b2 simplelru.LRUCache // B2 is the LRU for evictions from t2 + + lock sync.RWMutex +} + +// NewARC creates an ARC of the given size +func NewARC(size int) (*ARCCache, error) { + // Create the sub LRUs + b1, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + b2, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + t1, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + t2, err := simplelru.NewLRU(size, nil) + if err != nil { + return nil, err + } + + // Initialize the ARC + c := &ARCCache{ + size: size, + p: 0, + t1: t1, + b1: b1, + t2: t2, + b2: b2, + } + return c, nil +} + +// Get looks up a key's value from the cache. +func (c *ARCCache) Get(key interface{}) (value interface{}, ok bool) { + c.lock.Lock() + defer c.lock.Unlock() + + // If the value is contained in T1 (recent), then + // promote it to T2 (frequent) + if val, ok := c.t1.Peek(key); ok { + c.t1.Remove(key) + c.t2.Add(key, val) + return val, ok + } + + // Check if the value is contained in T2 (frequent) + if val, ok := c.t2.Get(key); ok { + return val, ok + } + + // No hit + return nil, false +} + +// Add adds a value to the cache. +func (c *ARCCache) Add(key, value interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + + // Check if the value is contained in T1 (recent), and potentially + // promote it to frequent T2 + if c.t1.Contains(key) { + c.t1.Remove(key) + c.t2.Add(key, value) + return + } + + // Check if the value is already in T2 (frequent) and update it + if c.t2.Contains(key) { + c.t2.Add(key, value) + return + } + + // Check if this value was recently evicted as part of the + // recently used list + if c.b1.Contains(key) { + // T1 set is too small, increase P appropriately + delta := 1 + b1Len := c.b1.Len() + b2Len := c.b2.Len() + if b2Len > b1Len { + delta = b2Len / b1Len + } + if c.p+delta >= c.size { + c.p = c.size + } else { + c.p += delta + } + + // Potentially need to make room in the cache + if c.t1.Len()+c.t2.Len() >= c.size { + c.replace(false) + } + + // Remove from B1 + c.b1.Remove(key) + + // Add the key to the frequently used list + c.t2.Add(key, value) + return + } + + // Check if this value was recently evicted as part of the + // frequently used list + if c.b2.Contains(key) { + // T2 set is too small, decrease P appropriately + delta := 1 + b1Len := c.b1.Len() + b2Len := c.b2.Len() + if b1Len > b2Len { + delta = b1Len / b2Len + } + if delta >= c.p { + c.p = 0 + } else { + c.p -= delta + } + + // Potentially need to make room in the cache + if c.t1.Len()+c.t2.Len() >= c.size { + c.replace(true) + } + + // Remove from B2 + c.b2.Remove(key) + + // Add the key to the frequently used list + c.t2.Add(key, value) + return + } + + // Potentially need to make room in the cache + if c.t1.Len()+c.t2.Len() >= c.size { + c.replace(false) + } + + // Keep the size of the ghost buffers trim + if c.b1.Len() > c.size-c.p { + c.b1.RemoveOldest() + } + if c.b2.Len() > c.p { + c.b2.RemoveOldest() + } + + // Add to the recently seen list + c.t1.Add(key, value) + return +} + +// replace is used to adaptively evict from either T1 or T2 +// based on the current learned value of P +func (c *ARCCache) replace(b2ContainsKey bool) { + t1Len := c.t1.Len() + if t1Len > 0 && (t1Len > c.p || (t1Len == c.p && b2ContainsKey)) { + k, _, ok := c.t1.RemoveOldest() + if ok { + c.b1.Add(k, nil) + } + } else { + k, _, ok := c.t2.RemoveOldest() + if ok { + c.b2.Add(k, nil) + } + } +} + +// Len returns the number of cached entries +func (c *ARCCache) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + return c.t1.Len() + c.t2.Len() +} + +// Keys returns all the cached keys +func (c *ARCCache) Keys() []interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + k1 := c.t1.Keys() + k2 := c.t2.Keys() + return append(k1, k2...) +} + +// Remove is used to purge a key from the cache +func (c *ARCCache) Remove(key interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + if c.t1.Remove(key) { + return + } + if c.t2.Remove(key) { + return + } + if c.b1.Remove(key) { + return + } + if c.b2.Remove(key) { + return + } +} + +// Purge is used to clear the cache +func (c *ARCCache) Purge() { + c.lock.Lock() + defer c.lock.Unlock() + c.t1.Purge() + c.t2.Purge() + c.b1.Purge() + c.b2.Purge() +} + +// Contains is used to check if the cache contains a key +// without updating recency or frequency. +func (c *ARCCache) Contains(key interface{}) bool { + c.lock.RLock() + defer c.lock.RUnlock() + return c.t1.Contains(key) || c.t2.Contains(key) +} + +// Peek is used to inspect the cache value of a key +// without updating recency or frequency. +func (c *ARCCache) Peek(key interface{}) (value interface{}, ok bool) { + c.lock.RLock() + defer c.lock.RUnlock() + if val, ok := c.t1.Peek(key); ok { + return val, ok + } + return c.t2.Peek(key) +} diff --git a/vendor/github.com/hashicorp/golang-lru/doc.go b/vendor/github.com/hashicorp/golang-lru/doc.go new file mode 100644 index 00000000..2547df97 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/doc.go @@ -0,0 +1,21 @@ +// Package lru provides three different LRU caches of varying sophistication. +// +// Cache is a simple LRU cache. It is based on the +// LRU implementation in groupcache: +// https://github.com/golang/groupcache/tree/master/lru +// +// TwoQueueCache tracks frequently used and recently used entries separately. +// This avoids a burst of accesses from taking out frequently used entries, +// at the cost of about 2x computational overhead and some extra bookkeeping. +// +// ARCCache is an adaptive replacement cache. It tracks recent evictions as +// well as recent usage in both the frequent and recent caches. Its +// computational overhead is comparable to TwoQueueCache, but the memory +// overhead is linear with the size of the cache. +// +// ARC has been patented by IBM, so do not use it if that is problematic for +// your program. +// +// All caches in this package take locks while operating, and are therefore +// thread-safe for consumers. +package lru diff --git a/vendor/github.com/hashicorp/golang-lru/go.mod b/vendor/github.com/hashicorp/golang-lru/go.mod new file mode 100644 index 00000000..824cb97e --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/go.mod @@ -0,0 +1 @@ +module github.com/hashicorp/golang-lru diff --git a/vendor/github.com/hashicorp/golang-lru/lru.go b/vendor/github.com/hashicorp/golang-lru/lru.go new file mode 100644 index 00000000..c8d9b0a2 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/lru.go @@ -0,0 +1,110 @@ +package lru + +import ( + "sync" + + "github.com/hashicorp/golang-lru/simplelru" +) + +// Cache is a thread-safe fixed size LRU cache. +type Cache struct { + lru simplelru.LRUCache + lock sync.RWMutex +} + +// New creates an LRU of the given size. +func New(size int) (*Cache, error) { + return NewWithEvict(size, nil) +} + +// NewWithEvict constructs a fixed size cache with the given eviction +// callback. +func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) { + lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted)) + if err != nil { + return nil, err + } + c := &Cache{ + lru: lru, + } + return c, nil +} + +// Purge is used to completely clear the cache. +func (c *Cache) Purge() { + c.lock.Lock() + c.lru.Purge() + c.lock.Unlock() +} + +// Add adds a value to the cache. Returns true if an eviction occurred. +func (c *Cache) Add(key, value interface{}) (evicted bool) { + c.lock.Lock() + defer c.lock.Unlock() + return c.lru.Add(key, value) +} + +// Get looks up a key's value from the cache. +func (c *Cache) Get(key interface{}) (value interface{}, ok bool) { + c.lock.Lock() + defer c.lock.Unlock() + return c.lru.Get(key) +} + +// Contains checks if a key is in the cache, without updating the +// recent-ness or deleting it for being stale. +func (c *Cache) Contains(key interface{}) bool { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Contains(key) +} + +// Peek returns the key value (or undefined if not found) without updating +// the "recently used"-ness of the key. +func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Peek(key) +} + +// ContainsOrAdd checks if a key is in the cache without updating the +// recent-ness or deleting it for being stale, and if not, adds the value. +// Returns whether found and whether an eviction occurred. +func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) { + c.lock.Lock() + defer c.lock.Unlock() + + if c.lru.Contains(key) { + return true, false + } + evicted = c.lru.Add(key, value) + return false, evicted +} + +// Remove removes the provided key from the cache. +func (c *Cache) Remove(key interface{}) { + c.lock.Lock() + c.lru.Remove(key) + c.lock.Unlock() +} + +// RemoveOldest removes the oldest item from the cache. +func (c *Cache) RemoveOldest() { + c.lock.Lock() + c.lru.RemoveOldest() + c.lock.Unlock() +} + +// Keys returns a slice of the keys in the cache, from oldest to newest. +func (c *Cache) Keys() []interface{} { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Keys() +} + +// Len returns the number of items in the cache. +func (c *Cache) Len() int { + c.lock.RLock() + defer c.lock.RUnlock() + return c.lru.Len() +} diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go new file mode 100644 index 00000000..5673773b --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru.go @@ -0,0 +1,161 @@ +package simplelru + +import ( + "container/list" + "errors" +) + +// EvictCallback is used to get a callback when a cache entry is evicted +type EvictCallback func(key interface{}, value interface{}) + +// LRU implements a non-thread safe fixed size LRU cache +type LRU struct { + size int + evictList *list.List + items map[interface{}]*list.Element + onEvict EvictCallback +} + +// entry is used to hold a value in the evictList +type entry struct { + key interface{} + value interface{} +} + +// NewLRU constructs an LRU of the given size +func NewLRU(size int, onEvict EvictCallback) (*LRU, error) { + if size <= 0 { + return nil, errors.New("Must provide a positive size") + } + c := &LRU{ + size: size, + evictList: list.New(), + items: make(map[interface{}]*list.Element), + onEvict: onEvict, + } + return c, nil +} + +// Purge is used to completely clear the cache. +func (c *LRU) Purge() { + for k, v := range c.items { + if c.onEvict != nil { + c.onEvict(k, v.Value.(*entry).value) + } + delete(c.items, k) + } + c.evictList.Init() +} + +// Add adds a value to the cache. Returns true if an eviction occurred. +func (c *LRU) Add(key, value interface{}) (evicted bool) { + // Check for existing item + if ent, ok := c.items[key]; ok { + c.evictList.MoveToFront(ent) + ent.Value.(*entry).value = value + return false + } + + // Add new item + ent := &entry{key, value} + entry := c.evictList.PushFront(ent) + c.items[key] = entry + + evict := c.evictList.Len() > c.size + // Verify size not exceeded + if evict { + c.removeOldest() + } + return evict +} + +// Get looks up a key's value from the cache. +func (c *LRU) Get(key interface{}) (value interface{}, ok bool) { + if ent, ok := c.items[key]; ok { + c.evictList.MoveToFront(ent) + return ent.Value.(*entry).value, true + } + return +} + +// Contains checks if a key is in the cache, without updating the recent-ness +// or deleting it for being stale. +func (c *LRU) Contains(key interface{}) (ok bool) { + _, ok = c.items[key] + return ok +} + +// Peek returns the key value (or undefined if not found) without updating +// the "recently used"-ness of the key. +func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) { + var ent *list.Element + if ent, ok = c.items[key]; ok { + return ent.Value.(*entry).value, true + } + return nil, ok +} + +// Remove removes the provided key from the cache, returning if the +// key was contained. +func (c *LRU) Remove(key interface{}) (present bool) { + if ent, ok := c.items[key]; ok { + c.removeElement(ent) + return true + } + return false +} + +// RemoveOldest removes the oldest item from the cache. +func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) { + ent := c.evictList.Back() + if ent != nil { + c.removeElement(ent) + kv := ent.Value.(*entry) + return kv.key, kv.value, true + } + return nil, nil, false +} + +// GetOldest returns the oldest entry +func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) { + ent := c.evictList.Back() + if ent != nil { + kv := ent.Value.(*entry) + return kv.key, kv.value, true + } + return nil, nil, false +} + +// Keys returns a slice of the keys in the cache, from oldest to newest. +func (c *LRU) Keys() []interface{} { + keys := make([]interface{}, len(c.items)) + i := 0 + for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() { + keys[i] = ent.Value.(*entry).key + i++ + } + return keys +} + +// Len returns the number of items in the cache. +func (c *LRU) Len() int { + return c.evictList.Len() +} + +// removeOldest removes the oldest item from the cache. +func (c *LRU) removeOldest() { + ent := c.evictList.Back() + if ent != nil { + c.removeElement(ent) + } +} + +// removeElement is used to remove a given list element from the cache +func (c *LRU) removeElement(e *list.Element) { + c.evictList.Remove(e) + kv := e.Value.(*entry) + delete(c.items, kv.key) + if c.onEvict != nil { + c.onEvict(kv.key, kv.value) + } +} diff --git a/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go new file mode 100644 index 00000000..74c70774 --- /dev/null +++ b/vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go @@ -0,0 +1,36 @@ +package simplelru + +// LRUCache is the interface for simple LRU cache. +type LRUCache interface { + // Adds a value to the cache, returns true if an eviction occurred and + // updates the "recently used"-ness of the key. + Add(key, value interface{}) bool + + // Returns key's value from the cache and + // updates the "recently used"-ness of the key. #value, isFound + Get(key interface{}) (value interface{}, ok bool) + + // Check if a key exsists in cache without updating the recent-ness. + Contains(key interface{}) (ok bool) + + // Returns key's value without updating the "recently used"-ness of the key. + Peek(key interface{}) (value interface{}, ok bool) + + // Removes a key from the cache. + Remove(key interface{}) bool + + // Removes the oldest entry from cache. + RemoveOldest() (interface{}, interface{}, bool) + + // Returns the oldest entry from the cache. #key, value, isFound + GetOldest() (interface{}, interface{}, bool) + + // Returns a slice of the keys in the cache, from oldest to newest. + Keys() []interface{} + + // Returns the number of items in the cache. + Len() int + + // Clear all cache entries + Purge() +} diff --git a/vendor/github.com/vektah/gqlgen/codegen/build.go b/vendor/github.com/vektah/gqlgen/codegen/build.go deleted file mode 100644 index d56fc06f..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/build.go +++ /dev/null @@ -1,165 +0,0 @@ -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 -} - -type ModelBuild struct { - PackageName string - Imports []*Import - Models []Model - Enums []Enum -} - -// Create a list of models that need to be generated -func (cfg *Config) models() (*ModelBuild, error) { - namedTypes := cfg.buildNamedTypes() - - prog, err := cfg.loadProgram(namedTypes, true) - 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) - 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) bind() (*Build, error) { - namedTypes := cfg.buildNamedTypes() - - prog, err := cfg.loadProgram(namedTypes, true) - 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 - } - - b := &Build{ - PackageName: cfg.Exec.Package, - Objects: objects, - Interfaces: cfg.buildInterfaces(namedTypes, prog), - Inputs: inputs, - Imports: imports.finalize(), - SchemaRaw: cfg.SchemaStr, - } - - if qr, ok := cfg.schema.EntryPoints["query"]; ok { - b.QueryRoot = b.Objects.ByName(qr.TypeName()) - } - - if mr, ok := cfg.schema.EntryPoints["mutation"]; ok { - b.MutationRoot = b.Objects.ByName(mr.TypeName()) - } - - if sr, ok := cfg.schema.EntryPoints["subscription"]; ok { - b.SubscriptionRoot = b.Objects.ByName(sr.TypeName()) - } - - if b.QueryRoot == nil { - return b, fmt.Errorf("query entry point missing") - } - - // Poke a few magic methods into query - q := b.Objects.ByName(b.QueryRoot.GQLType) - q.Fields = append(q.Fields, Field{ - Type: &Type{namedTypes["__Schema"], []string{modPtr}, nil}, - GQLName: "__schema", - NoErr: true, - GoMethodName: "ec.introspectSchema", - Object: q, - }) - q.Fields = append(q.Fields, Field{ - Type: &Type{namedTypes["__Type"], []string{modPtr}, nil}, - GQLName: "__type", - NoErr: true, - GoMethodName: "ec.introspectType", - Args: []FieldArgument{ - {GQLName: "name", Type: &Type{namedTypes["String"], []string{}, nil}, Object: &Object{}}, - }, - Object: q, - }) - - return b, nil -} - -func (cfg *Config) validate() error { - namedTypes := cfg.buildNamedTypes() - - _, err := cfg.loadProgram(namedTypes, false) - return err -} - -func (cfg *Config) loadProgram(namedTypes NamedTypes, allowErrors bool) (*loader.Program, error) { - 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.Load() -} - -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/vektah/gqlgen/codegen/codegen.go b/vendor/github.com/vektah/gqlgen/codegen/codegen.go deleted file mode 100644 index 789ef2ec..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/codegen.go +++ /dev/null @@ -1,153 +0,0 @@ -package codegen - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "syscall" - - "github.com/pkg/errors" - "github.com/vektah/gqlgen/codegen/templates" - "github.com/vektah/gqlgen/neelance/schema" - "golang.org/x/tools/imports" -) - -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 { - var buf *bytes.Buffer - buf, err = templates.Run("models.gotpl", modelsBuild) - if err != nil { - return errors.Wrap(err, "model generation failed") - } - - if err = write(cfg.Model.Filename, buf.Bytes()); 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") - } - - var buf *bytes.Buffer - buf, err = templates.Run("generated.gotpl", build) - if err != nil { - return errors.Wrap(err, "exec codegen failed") - } - - if err = write(cfg.Exec.Filename, buf.Bytes()); err != nil { - return err - } - - if err = cfg.validate(); err != nil { - return errors.Wrap(err, "validation failed") - } - - 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") - } - - builtins := TypeMap{ - "__Directive": {Model: "github.com/vektah/gqlgen/neelance/introspection.Directive"}, - "__Type": {Model: "github.com/vektah/gqlgen/neelance/introspection.Type"}, - "__Field": {Model: "github.com/vektah/gqlgen/neelance/introspection.Field"}, - "__EnumValue": {Model: "github.com/vektah/gqlgen/neelance/introspection.EnumValue"}, - "__InputValue": {Model: "github.com/vektah/gqlgen/neelance/introspection.InputValue"}, - "__Schema": {Model: "github.com/vektah/gqlgen/neelance/introspection.Schema"}, - "Int": {Model: "github.com/vektah/gqlgen/graphql.Int"}, - "Float": {Model: "github.com/vektah/gqlgen/graphql.Float"}, - "String": {Model: "github.com/vektah/gqlgen/graphql.String"}, - "Boolean": {Model: "github.com/vektah/gqlgen/graphql.Boolean"}, - "ID": {Model: "github.com/vektah/gqlgen/graphql.ID"}, - "Time": {Model: "github.com/vektah/gqlgen/graphql.Time"}, - "Map": {Model: "github.com/vektah/gqlgen/graphql.Map"}, - } - - if cfg.Models == nil { - cfg.Models = TypeMap{} - } - for typeName, entry := range builtins { - if !cfg.Models.Exists(typeName) { - cfg.Models[typeName] = entry - } - } - - cfg.schema = schema.New() - return cfg.schema.Parse(cfg.SchemaStr) -} - -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) -} - -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/vektah/gqlgen/codegen/enum_build.go b/vendor/github.com/vektah/gqlgen/codegen/enum_build.go deleted file mode 100644 index f2e6f63c..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/enum_build.go +++ /dev/null @@ -1,39 +0,0 @@ -package codegen - -import ( - "sort" - "strings" - - "github.com/vektah/gqlgen/codegen/templates" - "github.com/vektah/gqlgen/neelance/schema" -) - -func (cfg *Config) buildEnums(types NamedTypes) []Enum { - var enums []Enum - - for _, typ := range cfg.schema.Types { - namedType := types[typ.TypeName()] - e, isEnum := typ.(*schema.Enum) - if !isEnum || strings.HasPrefix(typ.TypeName(), "__") || namedType.IsUserDefined { - continue - } - - var values []EnumValue - for _, v := range e.Values { - values = append(values, EnumValue{v.Name, v.Desc}) - } - - enum := Enum{ - NamedType: namedType, - Values: values, - } - enum.GoType = templates.ToCamel(enum.GQLType) - enums = append(enums, enum) - } - - sort.Slice(enums, func(i, j int) bool { - return strings.Compare(enums[i].GQLType, enums[j].GQLType) == -1 - }) - - return enums -} diff --git a/vendor/github.com/vektah/gqlgen/codegen/model.go b/vendor/github.com/vektah/gqlgen/codegen/model.go deleted file mode 100644 index 164a04d5..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/model.go +++ /dev/null @@ -1,15 +0,0 @@ -package codegen - -type Model struct { - *NamedType - - Fields []ModelField -} - -type ModelField struct { - *Type - GQLName string - GoVarName string - GoFKName string - GoFKType string -} diff --git a/vendor/github.com/vektah/gqlgen/codegen/object.go b/vendor/github.com/vektah/gqlgen/codegen/object.go deleted file mode 100644 index 1c03c0ba..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/object.go +++ /dev/null @@ -1,206 +0,0 @@ -package codegen - -import ( - "bytes" - "fmt" - "strconv" - "strings" - "text/template" - "unicode" -) - -type Object struct { - *NamedType - - Fields []Field - Satisfies []string - Root bool - DisableConcurrency bool - Stream bool -} - -type Field struct { - *Type - - GQLName string // The name of the field in graphql - GoMethodName string // The name of the method in go, if any - GoVarName string // The name of the 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 (f *Field) IsResolver() bool { - return f.ForceResolver || f.GoMethodName == "" && f.GoVarName == "" -} - -func (f *Field) IsConcurrent() bool { - return f.IsResolver() && !f.Object.DisableConcurrency -} -func (f *Field) ShortInvocation() string { - if !f.IsResolver() { - return "" - } - shortName := strings.ToUpper(f.GQLName[:1]) + f.GQLName[1:] - res := fmt.Sprintf("%s().%s(ctx", f.Object.GQLType, shortName) - if !f.Object.Root { - res += fmt.Sprintf(", obj") - } - for _, arg := range f.Args { - res += fmt.Sprintf(", %s", arg.GoVarName) - } - res += ")" - return res -} -func (f *Field) ShortResolverDeclaration() string { - if !f.IsResolver() { - return "" - } - decl := strings.TrimPrefix(f.ResolverDeclaration(), f.Object.GQLType+"_") - return strings.ToUpper(decl[:1]) + decl[1:] -} - -func (f *Field) ResolverDeclaration() string { - if !f.IsResolver() { - return "" - } - res := fmt.Sprintf("%s_%s(ctx context.Context", f.Object.GQLType, f.GQLName) - - 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) CallArgs() string { - var args []string - - if f.GoMethodName == "" { - 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, false, 1) -} - -func (f *Field) doWriteJson(val string, remainingMods []string, isPtr bool, depth int) string { - switch { - case len(remainingMods) > 0 && remainingMods[0] == modPtr: - return fmt.Sprintf("if %s == nil { return graphql.Null }\n%s", val, f.doWriteJson(val, remainingMods[1:], 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) - - return tpl(`{{.arr}} := graphql.Array{} - for {{.index}} := range {{.val}} { - {{.arr}} = append({{.arr}}, func() graphql.Marshaler { - rctx := graphql.GetResolverContext(ctx) - rctx.PushIndex({{.index}}) - defer rctx.Pop() - {{ .next }} - }()) - } - return {{.arr}}`, map[string]interface{}{ - "val": val, - "arr": arr, - "index": index, - "next": f.doWriteJson(val+"["+index+"]", remainingMods[1:], false, depth+1), - }) - - case f.IsScalar: - if isPtr { - val = "*" + val - } - return f.Marshal(val) - - default: - if !isPtr { - val = "&" + val - } - return fmt.Sprintf("return ec._%s(ctx, field.Selections, %s)", f.GQLType, val) - } -} - -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) -} diff --git a/vendor/github.com/vektah/gqlgen/codegen/object_build.go b/vendor/github.com/vektah/gqlgen/codegen/object_build.go deleted file mode 100644 index 0ef40fef..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/object_build.go +++ /dev/null @@ -1,144 +0,0 @@ -package codegen - -import ( - "log" - "sort" - "strings" - - "github.com/pkg/errors" - "github.com/vektah/gqlgen/neelance/schema" - "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 { - switch typ := typ.(type) { - case *schema.Object: - obj, err := cfg.buildObject(types, typ) - 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) { - log.Println(bindErr.Error()) - log.Println(" Adding resolver method") - } - } - - objects = append(objects, obj) - } - } - - sort.Slice(objects, func(i, j int) bool { - return strings.Compare(objects[i].GQLType, objects[j].GQLType) == -1 - }) - - 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", -} - -func sanitizeGoName(name string) string { - for _, k := range keywords { - if name == k { - return name + "_" - } - } - return name -} - -func (cfg *Config) buildObject(types NamedTypes, typ *schema.Object) (*Object, error) { - obj := &Object{NamedType: types[typ.TypeName()]} - typeEntry, entryExists := cfg.Models[typ.TypeName()] - - for _, i := range typ.Interfaces { - obj.Satisfies = append(obj.Satisfies, i.Name) - } - - for _, field := range typ.Fields { - - var forceResolver bool - if entryExists { - if typeField, ok := typeEntry.Fields[field.Name]; ok { - forceResolver = typeField.Resolver - } - } - - var args []FieldArgument - for _, arg := range field.Args { - newArg := FieldArgument{ - GQLName: arg.Name.Name, - Type: types.getType(arg.Type), - Object: obj, - GoVarName: sanitizeGoName(arg.Name.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.Default != nil { - newArg.Default = arg.Default.Value(nil) - newArg.StripPtr() - } - args = append(args, newArg) - } - - obj.Fields = append(obj.Fields, Field{ - GQLName: field.Name, - Type: types.getType(field.Type), - Args: args, - Object: obj, - ForceResolver: forceResolver, - }) - } - - for name, typ := range cfg.schema.EntryPoints { - schemaObj := typ.(*schema.Object) - if schemaObj.TypeName() != obj.GQLType { - continue - } - - obj.Root = true - if name == "mutation" { - obj.DisableConcurrency = true - } - if name == "subscription" { - obj.Stream = true - } - } - return obj, nil -} diff --git a/vendor/github.com/vektah/gqlgen/codegen/templates/args.gotpl b/vendor/github.com/vektah/gqlgen/codegen/templates/args.gotpl deleted file mode 100644 index f53aceec..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/args.gotpl +++ /dev/null @@ -1,30 +0,0 @@ - {{- if . }}args := map[string]interface{}{} {{end}} - {{- range $i, $arg := . }} - var arg{{$i}} {{$arg.Signature }} - if tmp, ok := field.Args[{{$arg.GQLName|quote}}]; ok { - var err error - {{$arg.Unmarshal (print "arg" $i) "tmp" }} - if err != nil { - ec.Error(ctx, err) - {{- if $arg.Object.Stream }} - return nil - {{- else }} - return graphql.Null - {{- end }} - } - } {{ if $arg.Default }} else { - var tmp interface{} = {{ $arg.Default | dump }} - var err error - {{$arg.Unmarshal (print "arg" $i) "tmp" }} - if err != nil { - ec.Error(ctx, err) - {{- if $arg.Object.Stream }} - return nil - {{- else }} - return graphql.Null - {{- end }} - } - } - {{end }} - args[{{$arg.GQLName|quote}}] = arg{{$i}} - {{- end -}} diff --git a/vendor/github.com/vektah/gqlgen/codegen/templates/data.go b/vendor/github.com/vektah/gqlgen/codegen/templates/data.go deleted file mode 100644 index d6da4807..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/data.go +++ /dev/null @@ -1,11 +0,0 @@ -package templates - -var data = map[string]string{ - "args.gotpl": "\t{{- if . }}args := map[string]interface{}{} {{end}}\n\t{{- range $i, $arg := . }}\n\t\tvar arg{{$i}} {{$arg.Signature }}\n\t\tif tmp, ok := field.Args[{{$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\tec.Error(ctx, err)\n\t\t\t\t{{- if $arg.Object.Stream }}\n\t\t\t\t\treturn nil\n\t\t\t\t{{- else }}\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t{{- end }}\n\t\t\t}\n\t\t} {{ if $arg.Default }} else {\n\t\t\tvar tmp interface{} = {{ $arg.Default | dump }}\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\tec.Error(ctx, err)\n\t\t\t\t{{- if $arg.Object.Stream }}\n\t\t\t\t\treturn nil\n\t\t\t\t{{- else }}\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t{{- end }}\n\t\t\t}\n\t\t}\n\t\t{{end }}\n\t\targs[{{$arg.GQLName|quote}}] = arg{{$i}}\n\t{{- end -}}\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{{- template \"args.gotpl\" $field.Args }}\n\t\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{Field: field})\n\t\tresults, err := ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }})\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\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{{- template \"args.gotpl\" $field.Args }}\n\n\t\t{{- if $field.IsConcurrent }}\n\t\t\tctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{\n\t\t\t\tObject: {{$object.GQLType|quote}},\n\t\t\t\tArgs: {{if $field.Args }}args{{else}}nil{{end}},\n\t\t\t\tField: field,\n\t\t\t})\n\t\t\treturn graphql.Defer(func() (ret graphql.Marshaler) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tuserErr := ec.Recover(ctx, r)\n\t\t\t\t\t\tec.Error(ctx, userErr)\n\t\t\t\t\t\tret = graphql.Null\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t{{ else }}\n\t\t\trctx := graphql.GetResolverContext(ctx)\n\t\t\trctx.Object = {{$object.GQLType|quote}}\n\t\t\trctx.Args = {{if $field.Args }}args{{else}}nil{{end}}\n\t\t\trctx.Field = field\n\t\t\trctx.PushField(field.Alias)\n\t\t\tdefer rctx.Pop()\n\t\t{{- end }}\n\n\t\t\t{{- if $field.IsResolver }}\n\t\t\t\tresTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) {\n\t\t\t\t\treturn ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }})\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\tec.Error(ctx, err)\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t}\n\t\t\t\tif resTmp == nil {\n\t\t\t\t\treturn graphql.Null\n\t\t\t\t}\n\t\t\t\tres := resTmp.({{$field.Signature}})\n\t\t\t{{- else if $field.GoVarName }}\n\t\t\t\tres := obj.{{$field.GoVarName}}\n\t\t\t{{- else if $field.GoMethodName }}\n\t\t\t\t{{- if $field.NoErr }}\n\t\t\t\t\tres := {{$field.GoMethodName}}({{ $field.CallArgs }})\n\t\t\t\t{{- else }}\n\t\t\t\t\tres, err := {{$field.GoMethodName}}({{ $field.CallArgs }})\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 graphql.Null\n\t\t\t\t\t}\n\t\t\t\t{{- end }}\n\t\t\t{{- end }}\n\t\t\t{{ $field.WriteJson }}\n\t\t{{- if $field.IsConcurrent }}\n\t\t\t})\n\t\t{{- end }}\n\t}\n{{ end }}\n", - "generated.gotpl": "// Code generated by github.com/vektah/gqlgen, DO NOT EDIT.\n\npackage {{ .PackageName }}\n\nimport (\n{{- range $import := .Imports }}\n\t{{- $import.Write }}\n{{ end }}\n)\n\n// MakeExecutableSchema creates an ExecutableSchema from the Resolvers interface.\nfunc MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema {\n\treturn &executableSchema{resolvers: resolvers}\n}\n\n// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface.\nfunc NewExecutableSchema(resolvers ResolverRoot) graphql.ExecutableSchema {\n\treturn MakeExecutableSchema(shortMapper{r: resolvers})\n}\n\ntype Resolvers interface {\n{{- range $object := .Objects -}}\n\t{{ range $field := $object.Fields -}}\n\t\t{{ $field.ResolverDeclaration }}\n\t{{ end }}\n{{- end }}\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\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\ntype shortMapper struct {\n\tr ResolverRoot\n}\n\n{{- range $object := .Objects -}}\n\t{{ range $field := $object.Fields -}}\n\t\t{{- if $field.IsResolver }}\n\t\t\tfunc (s shortMapper) {{ $field.ResolverDeclaration }} {\n\t\t\t\treturn s.r.{{$field.ShortInvocation}}\n\t\t\t}\n\t\t{{- end }}\n\t{{ end }}\n{{- end }}\n\ntype executableSchema struct {\n\tresolvers Resolvers\n}\n\nfunc (e *executableSchema) Schema() *schema.Schema {\n\treturn parsedSchema\n}\n\nfunc (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response {\n\t{{- if .QueryRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.QueryRoot.GQLType}}(ctx, op.Selections)\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 *query.Operation) *graphql.Response {\n\t{{- if .MutationRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tbuf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte {\n\t\t\tdata := ec._{{.MutationRoot.GQLType}}(ctx, op.Selections)\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 *query.Operation) func() *graphql.Response {\n\t{{- if .SubscriptionRoot }}\n\t\tec := executionContext{graphql.GetRequestContext(ctx), e.resolvers}\n\n\t\tnext := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.Selections)\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\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\n\tresolvers Resolvers\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) introspectSchema() *introspection.Schema {\n\treturn introspection.WrapSchema(parsedSchema)\n}\n\nfunc (ec *executionContext) introspectType(name string) *introspection.Type {\n\tt := parsedSchema.Resolve(name)\n\tif t == nil {\n\t\treturn nil\n\t}\n\treturn introspection.WrapType(t)\n}\n\nvar parsedSchema = schema.MustParse({{.SchemaRaw|rawQuote}})\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.GoVarName) \"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 []query.Selection, 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/vektah/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{{- 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{{- if $field.GoVarName }}\n\t\t\t\t\t{{ $field.GoVarName }} {{$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\ttype {{.GoType}} string\n\tconst (\n\t{{ range $value := .Values -}}\n\t\t{{with .Description}} {{.|prefixLines \"// \"}} {{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 []query.Selection) func() graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.Doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.Variables)\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 []query.Selection{{if not $object.Root}}, obj *{{$object.FullName}} {{end}}) graphql.Marshaler {\n\tfields := graphql.CollectFields(ec.Doc, sel, {{$object.GQLType|lcFirst}}Implementors, ec.Variables)\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\tout := graphql.NewOrderedMap(len(fields))\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\tout.Values[i] = ec._{{$object.GQLType}}_{{$field.GQLName}}(ctx, field{{if not $object.Root}}, obj{{end}})\n\t\t{{- end }}\n\t\tdefault:\n\t\t\tpanic(\"unknown field \" + strconv.Quote(field.Name))\n\t\t}\n\t}\n\n\treturn out\n}\n{{- end }}\n", -} diff --git a/vendor/github.com/vektah/gqlgen/codegen/templates/field.gotpl b/vendor/github.com/vektah/gqlgen/codegen/templates/field.gotpl deleted file mode 100644 index 4279ad8e..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/field.gotpl +++ /dev/null @@ -1,80 +0,0 @@ -{{ $field := . }} -{{ $object := $field.Object }} - -{{- if $object.Stream }} - func (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField) func() graphql.Marshaler { - {{- template "args.gotpl" $field.Args }} - ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{Field: field}) - results, err := ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }}) - 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 }} - func (ec *executionContext) _{{$object.GQLType}}_{{$field.GQLName}}(ctx context.Context, field graphql.CollectedField, {{if not $object.Root}}obj *{{$object.FullName}}{{end}}) graphql.Marshaler { - {{- template "args.gotpl" $field.Args }} - - {{- if $field.IsConcurrent }} - ctx = graphql.WithResolverContext(ctx, &graphql.ResolverContext{ - Object: {{$object.GQLType|quote}}, - Args: {{if $field.Args }}args{{else}}nil{{end}}, - Field: field, - }) - return graphql.Defer(func() (ret graphql.Marshaler) { - defer func() { - if r := recover(); r != nil { - userErr := ec.Recover(ctx, r) - ec.Error(ctx, userErr) - ret = graphql.Null - } - }() - {{ else }} - rctx := graphql.GetResolverContext(ctx) - rctx.Object = {{$object.GQLType|quote}} - rctx.Args = {{if $field.Args }}args{{else}}nil{{end}} - rctx.Field = field - rctx.PushField(field.Alias) - defer rctx.Pop() - {{- end }} - - {{- if $field.IsResolver }} - resTmp, err := ec.ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { - return ec.resolvers.{{ $object.GQLType }}_{{ $field.GQLName }}({{ $field.CallArgs }}) - }) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - if resTmp == nil { - return graphql.Null - } - res := resTmp.({{$field.Signature}}) - {{- else if $field.GoVarName }} - res := obj.{{$field.GoVarName}} - {{- else if $field.GoMethodName }} - {{- if $field.NoErr }} - res := {{$field.GoMethodName}}({{ $field.CallArgs }}) - {{- else }} - res, err := {{$field.GoMethodName}}({{ $field.CallArgs }}) - if err != nil { - ec.Error(ctx, err) - return graphql.Null - } - {{- end }} - {{- end }} - {{ $field.WriteJson }} - {{- if $field.IsConcurrent }} - }) - {{- end }} - } -{{ end }} diff --git a/vendor/github.com/vektah/gqlgen/codegen/templates/generated.gotpl b/vendor/github.com/vektah/gqlgen/codegen/templates/generated.gotpl deleted file mode 100644 index cc1dc459..00000000 --- a/vendor/github.com/vektah/gqlgen/codegen/templates/generated.gotpl +++ /dev/null @@ -1,175 +0,0 @@ -// Code generated by github.com/vektah/gqlgen, DO NOT EDIT. - -package {{ .PackageName }} - -import ( -{{- range $import := .Imports }} - {{- $import.Write }} -{{ end }} -) - -// MakeExecutableSchema creates an ExecutableSchema from the Resolvers interface. -func MakeExecutableSchema(resolvers Resolvers) graphql.ExecutableSchema { - return &executableSchema{resolvers: resolvers} -} - -// NewExecutableSchema creates an ExecutableSchema from the ResolverRoot interface. -func NewExecutableSchema(resolvers ResolverRoot) graphql.ExecutableSchema { - return MakeExecutableSchema(shortMapper{r: resolvers}) -} - -type Resolvers interface { -{{- range $object := .Objects -}} - {{ range $field := $object.Fields -}} - {{ $field.ResolverDeclaration }} - {{ end }} -{{- end }} -} - -type ResolverRoot interface { -{{- range $object := .Objects -}} - {{ if $object.HasResolvers -}} - {{$object.GQLType}}() {{$object.GQLType}}Resolver - {{ end }} -{{- end }} -} - -{{- range $object := .Objects -}} - {{ if $object.HasResolvers }} - type {{$object.GQLType}}Resolver interface { - {{ range $field := $object.Fields -}} - {{ $field.ShortResolverDeclaration }} - {{ end }} - } - {{- end }} -{{- end }} - -type shortMapper struct { - r ResolverRoot -} - -{{- range $object := .Objects -}} - {{ range $field := $object.Fields -}} - {{- if $field.IsResolver }} - func (s shortMapper) {{ $field.ResolverDeclaration }} { - return s.r.{{$field.ShortInvocation}} - } - {{- end }} - {{ end }} -{{- end }} - -type executableSchema struct { - resolvers Resolvers -} - -func (e *executableSchema) Schema() *schema.Schema { - return parsedSchema -} - -func (e *executableSchema) Query(ctx context.Context, op *query.Operation) *graphql.Response { - {{- if .QueryRoot }} - ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - - buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { - data := ec._{{.QueryRoot.GQLType}}(ctx, op.Selections) - 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 *query.Operation) *graphql.Response { - {{- if .MutationRoot }} - ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - - buf := ec.RequestMiddleware(ctx, func(ctx context.Context) []byte { - data := ec._{{.MutationRoot.GQLType}}(ctx, op.Selections) - 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 *query.Operation) func() *graphql.Response { - {{- if .SubscriptionRoot }} - ec := executionContext{graphql.GetRequestContext(ctx), e.resolvers} - - next := ec._{{.SubscriptionRoot.GQLType}}(ctx, op.Selections) - 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() - }) - - 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 - - resolvers Resolvers -} - -{{- 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) introspectSchema() *introspection.Schema { - return introspection.WrapSchema(parsedSchema) -} - -func (ec *executionContext) introspectType(name string) *introspection.Type { - t := parsedSchema.Resolve(name) - if t == nil { - return nil - } - return introspection.WrapType(t) -} - -var parsedSchema = schema.MustParse({{.SchemaRaw|rawQuote}}) diff --git a/vendor/github.com/vektah/gqlgen/graphql/defer.go b/vendor/github.com/vektah/gqlgen/graphql/defer.go deleted file mode 100644 index 79346a84..00000000 --- a/vendor/github.com/vektah/gqlgen/graphql/defer.go +++ /dev/null @@ -1,30 +0,0 @@ -package graphql - -import ( - "io" - "sync" -) - -// Defer will begin executing the given function and immediately return a result that will block until the function completes -func Defer(f func() Marshaler) Marshaler { - var deferred deferred - deferred.mu.Lock() - - go func() { - deferred.result = f() - deferred.mu.Unlock() - }() - - return &deferred -} - -type deferred struct { - result Marshaler - mu sync.Mutex -} - -func (d *deferred) MarshalGQL(w io.Writer) { - d.mu.Lock() - d.result.MarshalGQL(w) - d.mu.Unlock() -} diff --git a/vendor/github.com/vektah/gqlgen/graphql/error.go b/vendor/github.com/vektah/gqlgen/graphql/error.go deleted file mode 100644 index 15e65fab..00000000 --- a/vendor/github.com/vektah/gqlgen/graphql/error.go +++ /dev/null @@ -1,46 +0,0 @@ -package graphql - -import ( - "context" -) - -// Error is the standard graphql error type described in https://facebook.github.io/graphql/draft/#sec-Errors -type Error struct { - Message string `json:"message"` - Path []interface{} `json:"path,omitempty"` - Locations []ErrorLocation `json:"locations,omitempty"` - Extensions map[string]interface{} `json:"extensions,omitempty"` -} - -func (e *Error) Error() string { - return e.Message -} - -type ErrorLocation struct { - Line int `json:"line,omitempty"` - Column int `json:"column,omitempty"` -} - -type ErrorPresenterFunc func(context.Context, error) *Error - -type ExtendedError interface { - Extensions() map[string]interface{} -} - -func DefaultErrorPresenter(ctx context.Context, err error) *Error { - if gqlerr, ok := err.(*Error); ok { - gqlerr.Path = GetResolverContext(ctx).Path - return gqlerr - } - - var extensions map[string]interface{} - if ee, ok := err.(ExtendedError); ok { - extensions = ee.Extensions() - } - - return &Error{ - Message: err.Error(), - Path: GetResolverContext(ctx).Path, - Extensions: extensions, - } -} diff --git a/vendor/github.com/vektah/gqlgen/graphql/exec.go b/vendor/github.com/vektah/gqlgen/graphql/exec.go deleted file mode 100644 index 2c034888..00000000 --- a/vendor/github.com/vektah/gqlgen/graphql/exec.go +++ /dev/null @@ -1,118 +0,0 @@ -package graphql - -import ( - "context" - "fmt" - - "github.com/vektah/gqlgen/neelance/common" - "github.com/vektah/gqlgen/neelance/query" - "github.com/vektah/gqlgen/neelance/schema" -) - -type ExecutableSchema interface { - Schema() *schema.Schema - - Query(ctx context.Context, op *query.Operation) *Response - Mutation(ctx context.Context, op *query.Operation) *Response - Subscription(ctx context.Context, op *query.Operation) func() *Response -} - -func CollectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}) []CollectedField { - return collectFields(doc, selSet, satisfies, variables, map[string]bool{}) -} - -func collectFields(doc *query.Document, selSet []query.Selection, satisfies []string, variables map[string]interface{}, visited map[string]bool) []CollectedField { - var groupedFields []CollectedField - - for _, sel := range selSet { - switch sel := sel.(type) { - case *query.Field: - f := getOrCreateField(&groupedFields, sel.Alias.Name, func() CollectedField { - f := CollectedField{ - Alias: sel.Alias.Name, - Name: sel.Name.Name, - } - if len(sel.Arguments) > 0 { - f.Args = map[string]interface{}{} - for _, arg := range sel.Arguments { - if variable, ok := arg.Value.(*common.Variable); ok { - if val, ok := variables[variable.Name]; ok { - f.Args[arg.Name.Name] = val - } - } else { - f.Args[arg.Name.Name] = arg.Value.Value(variables) - } - } - } - return f - }) - - f.Selections = append(f.Selections, sel.Selections...) - case *query.InlineFragment: - if !instanceOf(sel.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range collectFields(doc, sel.Selections, satisfies, variables, visited) { - f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField }) - f.Selections = append(f.Selections, childField.Selections...) - } - - case *query.FragmentSpread: - fragmentName := sel.Name.Name - if _, seen := visited[fragmentName]; seen { - continue - } - visited[fragmentName] = true - - fragment := doc.Fragments.Get(fragmentName) - if fragment == nil { - // should never happen, validator has already run - panic(fmt.Errorf("missing fragment %s", fragmentName)) - } - - if !instanceOf(fragment.On.Ident.Name, satisfies) { - continue - } - - for _, childField := range collectFields(doc, fragment.Selections, satisfies, variables, 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 { - Alias string - Name string - Args map[string]interface{} - Selections []query.Selection -} - -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] -} diff --git a/vendor/github.com/vektah/gqlgen/handler/stub.go b/vendor/github.com/vektah/gqlgen/handler/stub.go deleted file mode 100644 index 46b27e46..00000000 --- a/vendor/github.com/vektah/gqlgen/handler/stub.go +++ /dev/null @@ -1,45 +0,0 @@ -package handler - -import ( - "context" - "time" - - "github.com/vektah/gqlgen/graphql" - "github.com/vektah/gqlgen/neelance/query" - "github.com/vektah/gqlgen/neelance/schema" -) - -type executableSchemaStub struct { -} - -var _ graphql.ExecutableSchema = &executableSchemaStub{} - -func (e *executableSchemaStub) Schema() *schema.Schema { - return schema.MustParse(` - schema { query: Query } - type Query { me: User! } - type User { name: String! } - `) -} - -func (e *executableSchemaStub) Query(ctx context.Context, op *query.Operation) *graphql.Response { - return &graphql.Response{Data: []byte(`{"name":"test"}`)} -} - -func (e *executableSchemaStub) Mutation(ctx context.Context, op *query.Operation) *graphql.Response { - return graphql.ErrorResponse(ctx, "mutations are not supported") -} - -func (e *executableSchemaStub) Subscription(ctx context.Context, op *query.Operation) func() *graphql.Response { - return func() *graphql.Response { - time.Sleep(50 * time.Millisecond) - select { - case <-ctx.Done(): - return nil - default: - return &graphql.Response{ - Data: []byte(`{"name":"test"}`), - } - } - } -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/LICENSE b/vendor/github.com/vektah/gqlgen/neelance/LICENSE deleted file mode 100644 index 3907ceca..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/LICENSE +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2016 Richard Musiol. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/vektah/gqlgen/neelance/common/directive.go b/vendor/github.com/vektah/gqlgen/neelance/common/directive.go deleted file mode 100644 index 62dca47f..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/common/directive.go +++ /dev/null @@ -1,32 +0,0 @@ -package common - -type Directive struct { - Name Ident - Args ArgumentList -} - -func ParseDirectives(l *Lexer) DirectiveList { - var directives DirectiveList - for l.Peek() == '@' { - l.ConsumeToken('@') - d := &Directive{} - d.Name = l.ConsumeIdentWithLoc() - d.Name.Loc.Column-- - if l.Peek() == '(' { - d.Args = ParseArguments(l) - } - directives = append(directives, d) - } - return directives -} - -type DirectiveList []*Directive - -func (l DirectiveList) Get(name string) *Directive { - for _, d := range l { - if d.Name.Name == name { - return d - } - } - return nil -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/common/lexer.go b/vendor/github.com/vektah/gqlgen/neelance/common/lexer.go deleted file mode 100644 index fdc1e622..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/common/lexer.go +++ /dev/null @@ -1,122 +0,0 @@ -package common - -import ( - "fmt" - "text/scanner" - - "github.com/vektah/gqlgen/neelance/errors" -) - -type syntaxError string - -type Lexer struct { - sc *scanner.Scanner - next rune - descComment string -} - -type Ident struct { - Name string - Loc errors.Location -} - -func New(sc *scanner.Scanner) *Lexer { - l := &Lexer{sc: sc} - l.Consume() - return l -} - -func (l *Lexer) CatchSyntaxError(f func()) (errRes *errors.QueryError) { - defer func() { - if err := recover(); err != nil { - if err, ok := err.(syntaxError); ok { - errRes = errors.Errorf("syntax error: %s", err) - errRes.Locations = []errors.Location{l.Location()} - return - } - panic(err) - } - }() - - f() - return -} - -func (l *Lexer) Peek() rune { - return l.next -} - -func (l *Lexer) Consume() { - l.descComment = "" - for { - l.next = l.sc.Scan() - if l.next == ',' { - continue - } - if l.next == '#' { - if l.sc.Peek() == ' ' { - l.sc.Next() - } - if l.descComment != "" { - l.descComment += "\n" - } - for { - next := l.sc.Next() - if next == '\n' || next == scanner.EOF { - break - } - l.descComment += string(next) - } - continue - } - break - } -} - -func (l *Lexer) ConsumeIdent() string { - name := l.sc.TokenText() - l.ConsumeToken(scanner.Ident) - return name -} - -func (l *Lexer) ConsumeIdentWithLoc() Ident { - loc := l.Location() - name := l.sc.TokenText() - l.ConsumeToken(scanner.Ident) - return Ident{name, loc} -} - -func (l *Lexer) ConsumeKeyword(keyword string) { - if l.next != scanner.Ident || l.sc.TokenText() != keyword { - l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %q", l.sc.TokenText(), keyword)) - } - l.Consume() -} - -func (l *Lexer) ConsumeLiteral() *BasicLit { - lit := &BasicLit{Type: l.next, Text: l.sc.TokenText()} - l.Consume() - return lit -} - -func (l *Lexer) ConsumeToken(expected rune) { - if l.next != expected { - l.SyntaxError(fmt.Sprintf("unexpected %q, expecting %s", l.sc.TokenText(), scanner.TokenString(expected))) - } - l.Consume() -} - -func (l *Lexer) DescComment() string { - return l.descComment -} - -func (l *Lexer) SyntaxError(message string) { - panic(syntaxError(message)) -} - -func (l *Lexer) Location() errors.Location { - return errors.Location{ - Line: l.sc.Line, - Column: l.sc.Column, - } -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/common/literals.go b/vendor/github.com/vektah/gqlgen/neelance/common/literals.go deleted file mode 100644 index 55619ba0..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/common/literals.go +++ /dev/null @@ -1,206 +0,0 @@ -package common - -import ( - "strconv" - "strings" - "text/scanner" - - "github.com/vektah/gqlgen/neelance/errors" -) - -type Literal interface { - Value(vars map[string]interface{}) interface{} - String() string - Location() errors.Location -} - -type BasicLit struct { - Type rune - Text string - Loc errors.Location -} - -func (lit *BasicLit) Value(vars map[string]interface{}) interface{} { - switch lit.Type { - case scanner.Int: - value, err := strconv.ParseInt(lit.Text, 10, 64) - if err != nil { - panic(err) - } - return int(value) - - case scanner.Float: - value, err := strconv.ParseFloat(lit.Text, 64) - if err != nil { - panic(err) - } - return value - - case scanner.String: - value, err := strconv.Unquote(lit.Text) - if err != nil { - panic(err) - } - return value - - case scanner.Ident: - switch lit.Text { - case "true": - return true - case "false": - return false - default: - return lit.Text - } - - default: - panic("invalid literal") - } -} - -func (lit *BasicLit) String() string { - return lit.Text -} - -func (lit *BasicLit) Location() errors.Location { - return lit.Loc -} - -type ListLit struct { - Entries []Literal - Loc errors.Location -} - -func (lit *ListLit) Value(vars map[string]interface{}) interface{} { - entries := make([]interface{}, len(lit.Entries)) - for i, entry := range lit.Entries { - entries[i] = entry.Value(vars) - } - return entries -} - -func (lit *ListLit) String() string { - entries := make([]string, len(lit.Entries)) - for i, entry := range lit.Entries { - entries[i] = entry.String() - } - return "[" + strings.Join(entries, ", ") + "]" -} - -func (lit *ListLit) Location() errors.Location { - return lit.Loc -} - -type ObjectLit struct { - Fields []*ObjectLitField - Loc errors.Location -} - -type ObjectLitField struct { - Name Ident - Value Literal -} - -func (lit *ObjectLit) Value(vars map[string]interface{}) interface{} { - fields := make(map[string]interface{}, len(lit.Fields)) - for _, f := range lit.Fields { - fields[f.Name.Name] = f.Value.Value(vars) - } - return fields -} - -func (lit *ObjectLit) String() string { - entries := make([]string, 0, len(lit.Fields)) - for _, f := range lit.Fields { - entries = append(entries, f.Name.Name+": "+f.Value.String()) - } - return "{" + strings.Join(entries, ", ") + "}" -} - -func (lit *ObjectLit) Location() errors.Location { - return lit.Loc -} - -type NullLit struct { - Loc errors.Location -} - -func (lit *NullLit) Value(vars map[string]interface{}) interface{} { - return nil -} - -func (lit *NullLit) String() string { - return "null" -} - -func (lit *NullLit) Location() errors.Location { - return lit.Loc -} - -type Variable struct { - Name string - Loc errors.Location -} - -func (v Variable) Value(vars map[string]interface{}) interface{} { - return vars[v.Name] -} - -func (v Variable) String() string { - return "$" + v.Name -} - -func (v *Variable) Location() errors.Location { - return v.Loc -} - -func ParseLiteral(l *Lexer, constOnly bool) Literal { - loc := l.Location() - switch l.Peek() { - case '$': - if constOnly { - l.SyntaxError("variable not allowed") - panic("unreachable") - } - l.ConsumeToken('$') - return &Variable{l.ConsumeIdent(), loc} - - case scanner.Int, scanner.Float, scanner.String, scanner.Ident: - lit := l.ConsumeLiteral() - if lit.Type == scanner.Ident && lit.Text == "null" { - return &NullLit{loc} - } - lit.Loc = loc - return lit - case '-': - l.ConsumeToken('-') - lit := l.ConsumeLiteral() - lit.Text = "-" + lit.Text - lit.Loc = loc - return lit - case '[': - l.ConsumeToken('[') - var list []Literal - for l.Peek() != ']' { - list = append(list, ParseLiteral(l, constOnly)) - } - l.ConsumeToken(']') - return &ListLit{list, loc} - - case '{': - l.ConsumeToken('{') - var fields []*ObjectLitField - for l.Peek() != '}' { - name := l.ConsumeIdentWithLoc() - l.ConsumeToken(':') - value := ParseLiteral(l, constOnly) - fields = append(fields, &ObjectLitField{name, value}) - } - l.ConsumeToken('}') - return &ObjectLit{fields, loc} - - default: - l.SyntaxError("invalid value") - panic("unreachable") - } -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/common/types.go b/vendor/github.com/vektah/gqlgen/neelance/common/types.go deleted file mode 100644 index 0bbf24ef..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/common/types.go +++ /dev/null @@ -1,80 +0,0 @@ -package common - -import ( - "github.com/vektah/gqlgen/neelance/errors" -) - -type Type interface { - Kind() string - String() string -} - -type List struct { - OfType Type -} - -type NonNull struct { - OfType Type -} - -type TypeName struct { - Ident -} - -func (*List) Kind() string { return "LIST" } -func (*NonNull) Kind() string { return "NON_NULL" } -func (*TypeName) Kind() string { panic("TypeName needs to be resolved to actual type") } - -func (t *List) String() string { return "[" + t.OfType.String() + "]" } -func (t *NonNull) String() string { return t.OfType.String() + "!" } -func (*TypeName) String() string { panic("TypeName needs to be resolved to actual type") } - -func ParseType(l *Lexer) Type { - t := parseNullType(l) - if l.Peek() == '!' { - l.ConsumeToken('!') - return &NonNull{OfType: t} - } - return t -} - -func parseNullType(l *Lexer) Type { - if l.Peek() == '[' { - l.ConsumeToken('[') - ofType := ParseType(l) - l.ConsumeToken(']') - return &List{OfType: ofType} - } - - return &TypeName{Ident: l.ConsumeIdentWithLoc()} -} - -type Resolver func(name string) Type - -func ResolveType(t Type, resolver Resolver) (Type, *errors.QueryError) { - switch t := t.(type) { - case *List: - ofType, err := ResolveType(t.OfType, resolver) - if err != nil { - return nil, err - } - return &List{OfType: ofType}, nil - case *NonNull: - ofType, err := ResolveType(t.OfType, resolver) - if err != nil { - return nil, err - } - return &NonNull{OfType: ofType}, nil - case *TypeName: - refT := resolver(t.Name) - if refT == nil { - err := errors.Errorf("Unknown type %q.", t.Name) - err.Rule = "KnownTypeNames" - err.Locations = []errors.Location{t.Loc} - return nil, err - } - return refT, nil - default: - return t, nil - } -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/common/values.go b/vendor/github.com/vektah/gqlgen/neelance/common/values.go deleted file mode 100644 index 09338da8..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/common/values.go +++ /dev/null @@ -1,77 +0,0 @@ -package common - -import ( - "github.com/vektah/gqlgen/neelance/errors" -) - -type InputValue struct { - Name Ident - Type Type - Default Literal - Desc string - Loc errors.Location - TypeLoc errors.Location -} - -type InputValueList []*InputValue - -func (l InputValueList) Get(name string) *InputValue { - for _, v := range l { - if v.Name.Name == name { - return v - } - } - return nil -} - -func ParseInputValue(l *Lexer) *InputValue { - p := &InputValue{} - p.Loc = l.Location() - p.Desc = l.DescComment() - p.Name = l.ConsumeIdentWithLoc() - l.ConsumeToken(':') - p.TypeLoc = l.Location() - p.Type = ParseType(l) - if l.Peek() == '=' { - l.ConsumeToken('=') - p.Default = ParseLiteral(l, true) - } - return p -} - -type Argument struct { - Name Ident - Value Literal -} - -type ArgumentList []Argument - -func (l ArgumentList) Get(name string) (Literal, bool) { - for _, arg := range l { - if arg.Name.Name == name { - return arg.Value, true - } - } - return nil, false -} - -func (l ArgumentList) MustGet(name string) Literal { - value, ok := l.Get(name) - if !ok { - panic("argument not found") - } - return value -} - -func ParseArguments(l *Lexer) ArgumentList { - var args ArgumentList - l.ConsumeToken('(') - for l.Peek() != ')' { - name := l.ConsumeIdentWithLoc() - l.ConsumeToken(':') - value := ParseLiteral(l, false) - args = append(args, Argument{Name: name, Value: value}) - } - l.ConsumeToken(')') - return args -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/errors/errors.go b/vendor/github.com/vektah/gqlgen/neelance/errors/errors.go deleted file mode 100644 index fdfa6202..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/errors/errors.go +++ /dev/null @@ -1,41 +0,0 @@ -package errors - -import ( - "fmt" -) - -type QueryError struct { - Message string `json:"message"` - Locations []Location `json:"locations,omitempty"` - Path []interface{} `json:"path,omitempty"` - Rule string `json:"-"` - ResolverError error `json:"-"` -} - -type Location struct { - Line int `json:"line"` - Column int `json:"column"` -} - -func (a Location) Before(b Location) bool { - return a.Line < b.Line || (a.Line == b.Line && a.Column < b.Column) -} - -func Errorf(format string, a ...interface{}) *QueryError { - return &QueryError{ - Message: fmt.Sprintf(format, a...), - } -} - -func (err *QueryError) Error() string { - if err == nil { - return "<nil>" - } - str := fmt.Sprintf("graphql: %s", err.Message) - for _, loc := range err.Locations { - str += fmt.Sprintf(" (line %d, column %d)", loc.Line, loc.Column) - } - return str -} - -var _ error = &QueryError{} diff --git a/vendor/github.com/vektah/gqlgen/neelance/introspection/introspection.go b/vendor/github.com/vektah/gqlgen/neelance/introspection/introspection.go deleted file mode 100644 index 5e354c9a..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/introspection/introspection.go +++ /dev/null @@ -1,313 +0,0 @@ -package introspection - -import ( - "sort" - - "github.com/vektah/gqlgen/neelance/common" - "github.com/vektah/gqlgen/neelance/schema" -) - -type Schema struct { - schema *schema.Schema -} - -// WrapSchema is only used internally. -func WrapSchema(schema *schema.Schema) *Schema { - return &Schema{schema} -} - -func (r *Schema) Types() []Type { - var names []string - for name := range r.schema.Types { - names = append(names, name) - } - sort.Strings(names) - - l := make([]Type, len(names)) - for i, name := range names { - l[i] = Type{r.schema.Types[name]} - } - return l -} - -func (r *Schema) Directives() []Directive { - var names []string - for name := range r.schema.Directives { - names = append(names, name) - } - sort.Strings(names) - - l := make([]Directive, len(names)) - for i, name := range names { - l[i] = Directive{r.schema.Directives[name]} - } - return l -} - -func (r *Schema) QueryType() Type { - t, ok := r.schema.EntryPoints["query"] - if !ok { - return Type{} - } - return Type{t} -} - -func (r *Schema) MutationType() *Type { - t, ok := r.schema.EntryPoints["mutation"] - if !ok { - return nil - } - return &Type{t} -} - -func (r *Schema) SubscriptionType() *Type { - t, ok := r.schema.EntryPoints["subscription"] - if !ok { - return nil - } - return &Type{t} -} - -type Type struct { - typ common.Type -} - -// WrapType is only used internally. -func WrapType(typ common.Type) *Type { - return &Type{typ} -} - -func (r *Type) Kind() string { - return r.typ.Kind() -} - -func (r *Type) Name() *string { - if named, ok := r.typ.(schema.NamedType); ok { - name := named.TypeName() - return &name - } - return nil -} - -func (r *Type) Description() *string { - if named, ok := r.typ.(schema.NamedType); ok { - desc := named.Description() - if desc == "" { - return nil - } - return &desc - } - return nil -} - -func (r *Type) Fields(includeDeprecated bool) []Field { - var fields schema.FieldList - switch t := r.typ.(type) { - case *schema.Object: - fields = t.Fields - case *schema.Interface: - fields = t.Fields - default: - return nil - } - - var l []Field - for _, f := range fields { - if d := f.Directives.Get("deprecated"); d == nil || includeDeprecated { - l = append(l, Field{f}) - } - } - return l -} - -func (r *Type) Interfaces() []Type { - t, ok := r.typ.(*schema.Object) - if !ok { - return nil - } - - l := make([]Type, len(t.Interfaces)) - for i, intf := range t.Interfaces { - l[i] = Type{intf} - } - return l -} - -func (r *Type) PossibleTypes() []Type { - var possibleTypes []*schema.Object - switch t := r.typ.(type) { - case *schema.Interface: - possibleTypes = t.PossibleTypes - case *schema.Union: - possibleTypes = t.PossibleTypes - default: - return nil - } - - l := make([]Type, len(possibleTypes)) - for i, intf := range possibleTypes { - l[i] = Type{intf} - } - return l -} - -func (r *Type) EnumValues(includeDeprecated bool) []EnumValue { - t, ok := r.typ.(*schema.Enum) - if !ok { - return nil - } - - var l []EnumValue - for _, v := range t.Values { - if d := v.Directives.Get("deprecated"); d == nil || includeDeprecated { - l = append(l, EnumValue{v}) - } - } - return l -} - -func (r *Type) InputFields() []InputValue { - t, ok := r.typ.(*schema.InputObject) - if !ok { - return nil - } - - l := make([]InputValue, len(t.Values)) - for i, v := range t.Values { - l[i] = InputValue{v} - } - return l -} - -func (r *Type) OfType() *Type { - switch t := r.typ.(type) { - case *common.List: - return &Type{t.OfType} - case *common.NonNull: - return &Type{t.OfType} - default: - return nil - } -} - -type Field struct { - field *schema.Field -} - -func (r *Field) Name() string { - return r.field.Name -} - -func (r *Field) Description() *string { - if r.field.Desc == "" { - return nil - } - return &r.field.Desc -} - -func (r *Field) Args() []InputValue { - l := make([]InputValue, len(r.field.Args)) - for i, v := range r.field.Args { - l[i] = InputValue{v} - } - return l -} - -func (r *Field) Type() Type { - return Type{r.field.Type} -} - -func (r *Field) IsDeprecated() bool { - return r.field.Directives.Get("deprecated") != nil -} - -func (r *Field) DeprecationReason() *string { - d := r.field.Directives.Get("deprecated") - if d == nil { - return nil - } - reason := d.Args.MustGet("reason").Value(nil).(string) - return &reason -} - -type InputValue struct { - value *common.InputValue -} - -func (r *InputValue) Name() string { - return r.value.Name.Name -} - -func (r *InputValue) Description() *string { - if r.value.Desc == "" { - return nil - } - return &r.value.Desc -} - -func (r *InputValue) Type() Type { - return Type{r.value.Type} -} - -func (r *InputValue) DefaultValue() *string { - if r.value.Default == nil { - return nil - } - s := r.value.Default.String() - return &s -} - -type EnumValue struct { - value *schema.EnumValue -} - -func (r *EnumValue) Name() string { - return r.value.Name -} - -func (r *EnumValue) Description() *string { - if r.value.Desc == "" { - return nil - } - return &r.value.Desc -} - -func (r *EnumValue) IsDeprecated() bool { - return r.value.Directives.Get("deprecated") != nil -} - -func (r *EnumValue) DeprecationReason() *string { - d := r.value.Directives.Get("deprecated") - if d == nil { - return nil - } - reason := d.Args.MustGet("reason").Value(nil).(string) - return &reason -} - -type Directive struct { - directive *schema.DirectiveDecl -} - -func (r *Directive) Name() string { - return r.directive.Name -} - -func (r *Directive) Description() *string { - if r.directive.Desc == "" { - return nil - } - return &r.directive.Desc -} - -func (r *Directive) Locations() []string { - return r.directive.Locs -} - -func (r *Directive) Args() []InputValue { - l := make([]InputValue, len(r.directive.Args)) - for i, v := range r.directive.Args { - l[i] = InputValue{v} - } - return l -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/query/query.go b/vendor/github.com/vektah/gqlgen/neelance/query/query.go deleted file mode 100644 index b6f35354..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/query/query.go +++ /dev/null @@ -1,261 +0,0 @@ -package query - -import ( - "fmt" - "strings" - "text/scanner" - - "github.com/vektah/gqlgen/neelance/common" - "github.com/vektah/gqlgen/neelance/errors" -) - -type Document struct { - Operations OperationList - Fragments FragmentList -} - -type OperationList []*Operation - -func (l OperationList) Get(name string) *Operation { - for _, f := range l { - if f.Name.Name == name { - return f - } - } - return nil -} - -type FragmentList []*FragmentDecl - -func (l FragmentList) Get(name string) *FragmentDecl { - for _, f := range l { - if f.Name.Name == name { - return f - } - } - return nil -} - -type Operation struct { - Type OperationType - Name common.Ident - Vars common.InputValueList - Selections []Selection - Directives common.DirectiveList - Loc errors.Location -} - -type OperationType string - -const ( - Query OperationType = "QUERY" - Mutation = "MUTATION" - Subscription = "SUBSCRIPTION" -) - -type Fragment struct { - On common.TypeName - Selections []Selection -} - -type FragmentDecl struct { - Fragment - Name common.Ident - Directives common.DirectiveList - Loc errors.Location -} - -type Selection interface { - isSelection() -} - -type Field struct { - Alias common.Ident - Name common.Ident - Arguments common.ArgumentList - Directives common.DirectiveList - Selections []Selection - SelectionSetLoc errors.Location -} - -type InlineFragment struct { - Fragment - Directives common.DirectiveList - Loc errors.Location -} - -type FragmentSpread struct { - Name common.Ident - Directives common.DirectiveList - Loc errors.Location -} - -func (Field) isSelection() {} -func (InlineFragment) isSelection() {} -func (FragmentSpread) isSelection() {} - -func Parse(queryString string) (*Document, *errors.QueryError) { - sc := &scanner.Scanner{ - Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings, - } - sc.Init(strings.NewReader(queryString)) - - l := common.New(sc) - var doc *Document - err := l.CatchSyntaxError(func() { - doc = parseDocument(l) - }) - if err != nil { - return nil, err - } - - return doc, nil -} - -func parseDocument(l *common.Lexer) *Document { - d := &Document{} - for l.Peek() != scanner.EOF { - if l.Peek() == '{' { - op := &Operation{Type: Query, Loc: l.Location()} - op.Selections = parseSelectionSet(l) - d.Operations = append(d.Operations, op) - continue - } - - loc := l.Location() - switch x := l.ConsumeIdent(); x { - case "query": - op := parseOperation(l, Query) - op.Loc = loc - d.Operations = append(d.Operations, op) - - case "mutation": - d.Operations = append(d.Operations, parseOperation(l, Mutation)) - - case "subscription": - d.Operations = append(d.Operations, parseOperation(l, Subscription)) - - case "fragment": - frag := parseFragment(l) - frag.Loc = loc - d.Fragments = append(d.Fragments, frag) - - default: - l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "fragment"`, x)) - } - } - return d -} - -func parseOperation(l *common.Lexer, opType OperationType) *Operation { - op := &Operation{Type: opType} - op.Name.Loc = l.Location() - if l.Peek() == scanner.Ident { - op.Name = l.ConsumeIdentWithLoc() - } - op.Directives = common.ParseDirectives(l) - if l.Peek() == '(' { - l.ConsumeToken('(') - for l.Peek() != ')' { - loc := l.Location() - l.ConsumeToken('$') - iv := common.ParseInputValue(l) - iv.Loc = loc - op.Vars = append(op.Vars, iv) - } - l.ConsumeToken(')') - } - op.Selections = parseSelectionSet(l) - return op -} - -func parseFragment(l *common.Lexer) *FragmentDecl { - f := &FragmentDecl{} - f.Name = l.ConsumeIdentWithLoc() - l.ConsumeKeyword("on") - f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()} - f.Directives = common.ParseDirectives(l) - f.Selections = parseSelectionSet(l) - return f -} - -func parseSelectionSet(l *common.Lexer) []Selection { - var sels []Selection - l.ConsumeToken('{') - for l.Peek() != '}' { - sels = append(sels, parseSelection(l)) - } - l.ConsumeToken('}') - return sels -} - -func parseSelection(l *common.Lexer) Selection { - if l.Peek() == '.' { - return parseSpread(l) - } - return parseField(l) -} - -func parseField(l *common.Lexer) *Field { - f := &Field{} - f.Alias = l.ConsumeIdentWithLoc() - f.Name = f.Alias - if l.Peek() == ':' { - l.ConsumeToken(':') - f.Name = l.ConsumeIdentWithLoc() - } - if l.Peek() == '(' { - f.Arguments = common.ParseArguments(l) - } - f.Directives = common.ParseDirectives(l) - if l.Peek() == '{' { - f.SelectionSetLoc = l.Location() - f.Selections = parseSelectionSet(l) - } - return f -} - -func parseSpread(l *common.Lexer) Selection { - loc := l.Location() - l.ConsumeToken('.') - l.ConsumeToken('.') - l.ConsumeToken('.') - - f := &InlineFragment{Loc: loc} - if l.Peek() == scanner.Ident { - ident := l.ConsumeIdentWithLoc() - if ident.Name != "on" { - fs := &FragmentSpread{ - Name: ident, - Loc: loc, - } - fs.Directives = common.ParseDirectives(l) - return fs - } - f.On = common.TypeName{Ident: l.ConsumeIdentWithLoc()} - } - f.Directives = common.ParseDirectives(l) - f.Selections = parseSelectionSet(l) - return f -} - -func (d *Document) GetOperation(operationName string) (*Operation, error) { - if len(d.Operations) == 0 { - return nil, fmt.Errorf("no operations in query document") - } - - if operationName == "" { - if len(d.Operations) > 1 { - return nil, fmt.Errorf("more than one operation in query document and no operation name given") - } - for _, op := range d.Operations { - return op, nil // return the one and only operation - } - } - - op := d.Operations.Get(operationName) - if op == nil { - return nil, fmt.Errorf("no operation with name %q", operationName) - } - return op, nil -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/schema/meta.go b/vendor/github.com/vektah/gqlgen/neelance/schema/meta.go deleted file mode 100644 index efdcaa2c..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/schema/meta.go +++ /dev/null @@ -1,193 +0,0 @@ -package schema - -var Meta *Schema - -func init() { - Meta = &Schema{} // bootstrap - Meta = New() - if err := Meta.Parse(metaSrc); err != nil { - panic(err) - } -} - -var metaSrc = ` - # The ` + "`" + `Int` + "`" + ` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. - scalar Int - - # The ` + "`" + `Float` + "`" + ` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). - scalar Float - - # The ` + "`" + `String` + "`" + ` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. - scalar String - - # The ` + "`" + `Boolean` + "`" + ` scalar type represents ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `. - scalar Boolean - - # The ` + "`" + `ID` + "`" + ` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as ` + "`" + `"4"` + "`" + `) or integer (such as ` + "`" + `4` + "`" + `) input value will be accepted as an ID. - scalar ID - - # The ` + "`" + `Map` + "`" + ` scalar type is a simple json object - scalar Map - - # Directs the executor to include this field or fragment only when the ` + "`" + `if` + "`" + ` argument is true. - directive @include( - # Included when true. - if: Boolean! - ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - # Directs the executor to skip this field or fragment when the ` + "`" + `if` + "`" + ` argument is true. - directive @skip( - # Skipped when true. - if: Boolean! - ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT - - # Marks an element of a GraphQL schema as no longer supported. - directive @deprecated( - # Explains why this element was deprecated, usually also including a suggestion - # for how to access supported similar data. Formatted in - # [Markdown](https://daringfireball.net/projects/markdown/). - reason: String = "No longer supported" - ) on FIELD_DEFINITION | ENUM_VALUE - - # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document. - # - # In some cases, you need to provide options to alter GraphQL's execution behavior - # in ways field arguments will not suffice, such as conditionally including or - # skipping a field. Directives provide this by describing additional information - # to the executor. - type __Directive { - name: String! - description: String - locations: [__DirectiveLocation!]! - args: [__InputValue!]! - } - - # A Directive can be adjacent to many parts of the GraphQL language, a - # __DirectiveLocation describes one such possible adjacencies. - enum __DirectiveLocation { - # Location adjacent to a query operation. - QUERY - # Location adjacent to a mutation operation. - MUTATION - # Location adjacent to a subscription operation. - SUBSCRIPTION - # Location adjacent to a field. - FIELD - # Location adjacent to a fragment definition. - FRAGMENT_DEFINITION - # Location adjacent to a fragment spread. - FRAGMENT_SPREAD - # Location adjacent to an inline fragment. - INLINE_FRAGMENT - # Location adjacent to a schema definition. - SCHEMA - # Location adjacent to a scalar definition. - SCALAR - # Location adjacent to an object type definition. - OBJECT - # Location adjacent to a field definition. - FIELD_DEFINITION - # Location adjacent to an argument definition. - ARGUMENT_DEFINITION - # Location adjacent to an interface definition. - INTERFACE - # Location adjacent to a union definition. - UNION - # Location adjacent to an enum definition. - ENUM - # Location adjacent to an enum value definition. - ENUM_VALUE - # Location adjacent to an input object type definition. - INPUT_OBJECT - # Location adjacent to an input object field definition. - INPUT_FIELD_DEFINITION - } - - # One possible value for a given Enum. Enum values are unique values, not a - # placeholder for a string or numeric value. However an Enum value is returned in - # a JSON response as a string. - type __EnumValue { - name: String! - description: String - isDeprecated: Boolean! - deprecationReason: String - } - - # Object and Interface types are described by a list of Fields, each of which has - # a name, potentially a list of arguments, and a return type. - type __Field { - name: String! - description: String - args: [__InputValue!]! - type: __Type! - isDeprecated: Boolean! - deprecationReason: String - } - - # Arguments provided to Fields or Directives and the input fields of an - # InputObject are represented as Input Values which describe their type and - # optionally a default value. - type __InputValue { - name: String! - description: String - type: __Type! - # A GraphQL-formatted string representing the default value for this input value. - defaultValue: String - } - - # A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all - # available types and directives on the server, as well as the entry points for - # query, mutation, and subscription operations. - type __Schema { - # A list of all types supported by this server. - types: [__Type!]! - # The type that query operations will be rooted at. - queryType: __Type! - # If this server supports mutation, the type that mutation operations will be rooted at. - mutationType: __Type - # If this server support subscription, the type that subscription operations will be rooted at. - subscriptionType: __Type - # A list of all directives supported by this server. - directives: [__Directive!]! - } - - # The fundamental unit of any GraphQL Schema is the type. There are many kinds of - # types in GraphQL as represented by the ` + "`" + `__TypeKind` + "`" + ` enum. - # - # Depending on the kind of a type, certain fields describe information about that - # type. Scalar types provide no information beyond a name and description, while - # Enum types provide their values. Object and Interface types provide the fields - # they describe. Abstract types, Union and Interface, provide the Object types - # possible at runtime. List and NonNull types compose other types. - type __Type { - kind: __TypeKind! - name: String - description: String - fields(includeDeprecated: Boolean = false): [__Field!] - interfaces: [__Type!] - possibleTypes: [__Type!] - enumValues(includeDeprecated: Boolean = false): [__EnumValue!] - inputFields: [__InputValue!] - ofType: __Type - } - - # An enum describing what kind of type a given ` + "`" + `__Type` + "`" + ` is. - enum __TypeKind { - # Indicates this type is a scalar. - SCALAR - # Indicates this type is an object. ` + "`" + `fields` + "`" + ` and ` + "`" + `interfaces` + "`" + ` are valid fields. - OBJECT - # Indicates this type is an interface. ` + "`" + `fields` + "`" + ` and ` + "`" + `possibleTypes` + "`" + ` are valid fields. - INTERFACE - # Indicates this type is a union. ` + "`" + `possibleTypes` + "`" + ` is a valid field. - UNION - # Indicates this type is an enum. ` + "`" + `enumValues` + "`" + ` is a valid field. - ENUM - # Indicates this type is an input object. ` + "`" + `inputFields` + "`" + ` is a valid field. - INPUT_OBJECT - # Indicates this type is a list. ` + "`" + `ofType` + "`" + ` is a valid field. - LIST - # Indicates this type is a non-null. ` + "`" + `ofType` + "`" + ` is a valid field. - NON_NULL - } -` diff --git a/vendor/github.com/vektah/gqlgen/neelance/schema/schema.go b/vendor/github.com/vektah/gqlgen/neelance/schema/schema.go deleted file mode 100644 index 0b1317a5..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/schema/schema.go +++ /dev/null @@ -1,489 +0,0 @@ -package schema - -import ( - "fmt" - "strings" - "text/scanner" - - "github.com/vektah/gqlgen/neelance/common" - "github.com/vektah/gqlgen/neelance/errors" -) - -type Schema struct { - EntryPoints map[string]NamedType - Types map[string]NamedType - Directives map[string]*DirectiveDecl - - entryPointNames map[string]string - objects []*Object - unions []*Union - enums []*Enum -} - -var defaultEntrypoints = map[string]string{ - "query": "Query", - "mutation": "Mutation", - "subscription": "Subscription", -} - -func (s *Schema) Resolve(name string) common.Type { - return s.Types[name] -} - -type NamedType interface { - common.Type - TypeName() string - Description() string -} - -type Scalar struct { - Name string - Desc string -} - -type Object struct { - Name string - Interfaces []*Interface - Fields FieldList - Desc string - - interfaceNames []string -} - -type Interface struct { - Name string - PossibleTypes []*Object - Fields FieldList - Desc string -} - -type Union struct { - Name string - PossibleTypes []*Object - Desc string - - typeNames []string -} - -type Enum struct { - Name string - Values []*EnumValue - Desc string -} - -type EnumValue struct { - Name string - Directives common.DirectiveList - Desc string -} - -type InputObject struct { - Name string - Desc string - Values common.InputValueList -} - -type FieldList []*Field - -func (l FieldList) Get(name string) *Field { - for _, f := range l { - if f.Name == name { - return f - } - } - return nil -} - -func (l FieldList) Names() []string { - names := make([]string, len(l)) - for i, f := range l { - names[i] = f.Name - } - return names -} - -type DirectiveDecl struct { - Name string - Desc string - Locs []string - Args common.InputValueList -} - -func (*Scalar) Kind() string { return "SCALAR" } -func (*Object) Kind() string { return "OBJECT" } -func (*Interface) Kind() string { return "INTERFACE" } -func (*Union) Kind() string { return "UNION" } -func (*Enum) Kind() string { return "ENUM" } -func (*InputObject) Kind() string { return "INPUT_OBJECT" } - -func (t *Scalar) String() string { return t.Name } -func (t *Object) String() string { return t.Name } -func (t *Interface) String() string { return t.Name } -func (t *Union) String() string { return t.Name } -func (t *Enum) String() string { return t.Name } -func (t *InputObject) String() string { return t.Name } - -func (t *Scalar) TypeName() string { return t.Name } -func (t *Object) TypeName() string { return t.Name } -func (t *Interface) TypeName() string { return t.Name } -func (t *Union) TypeName() string { return t.Name } -func (t *Enum) TypeName() string { return t.Name } -func (t *InputObject) TypeName() string { return t.Name } - -func (t *Scalar) Description() string { return t.Desc } -func (t *Object) Description() string { return t.Desc } -func (t *Interface) Description() string { return t.Desc } -func (t *Union) Description() string { return t.Desc } -func (t *Enum) Description() string { return t.Desc } -func (t *InputObject) Description() string { return t.Desc } - -type Field struct { - Name string - Args common.InputValueList - Type common.Type - Directives common.DirectiveList - Desc string -} - -func MustParse(str string) *Schema { - s := New() - err := s.Parse(str) - if err != nil { - panic(err) - } - return s -} - -func New() *Schema { - s := &Schema{ - entryPointNames: make(map[string]string), - Types: make(map[string]NamedType), - Directives: make(map[string]*DirectiveDecl), - } - for n, t := range Meta.Types { - s.Types[n] = t - } - for n, d := range Meta.Directives { - s.Directives[n] = d - } - return s -} - -func (s *Schema) Parse(schemaString string) error { - sc := &scanner.Scanner{ - Mode: scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings, - } - sc.Init(strings.NewReader(schemaString)) - - l := common.New(sc) - err := l.CatchSyntaxError(func() { - parseSchema(s, l) - }) - if err != nil { - return err - } - - for _, t := range s.Types { - if err := resolveNamedType(s, t); err != nil { - return err - } - } - for _, d := range s.Directives { - for _, arg := range d.Args { - t, err := common.ResolveType(arg.Type, s.Resolve) - if err != nil { - return err - } - arg.Type = t - } - } - - s.EntryPoints = make(map[string]NamedType) - for key, name := range s.entryPointNames { - t, ok := s.Types[name] - if !ok { - if !ok { - return errors.Errorf("type %q not found", name) - } - } - s.EntryPoints[key] = t - } - - for entrypointName, typeName := range defaultEntrypoints { - if _, ok := s.EntryPoints[entrypointName]; ok { - continue - } - - if _, ok := s.Types[typeName]; !ok { - continue - } - - s.EntryPoints[entrypointName] = s.Types[typeName] - } - - for _, obj := range s.objects { - obj.Interfaces = make([]*Interface, len(obj.interfaceNames)) - for i, intfName := range obj.interfaceNames { - t, ok := s.Types[intfName] - if !ok { - return errors.Errorf("interface %q not found", intfName) - } - intf, ok := t.(*Interface) - if !ok { - return errors.Errorf("type %q is not an interface", intfName) - } - obj.Interfaces[i] = intf - intf.PossibleTypes = append(intf.PossibleTypes, obj) - } - } - - for _, union := range s.unions { - union.PossibleTypes = make([]*Object, len(union.typeNames)) - for i, name := range union.typeNames { - t, ok := s.Types[name] - if !ok { - return errors.Errorf("object type %q not found", name) - } - obj, ok := t.(*Object) - if !ok { - return errors.Errorf("type %q is not an object", name) - } - union.PossibleTypes[i] = obj - } - } - - for _, enum := range s.enums { - for _, value := range enum.Values { - if err := resolveDirectives(s, value.Directives); err != nil { - return err - } - } - } - - return nil -} - -func resolveNamedType(s *Schema, t NamedType) error { - switch t := t.(type) { - case *Object: - for _, f := range t.Fields { - if err := resolveField(s, f); err != nil { - return err - } - } - case *Interface: - for _, f := range t.Fields { - if err := resolveField(s, f); err != nil { - return err - } - } - case *InputObject: - if err := resolveInputObject(s, t.Values); err != nil { - return err - } - } - return nil -} - -func resolveField(s *Schema, f *Field) error { - t, err := common.ResolveType(f.Type, s.Resolve) - if err != nil { - return err - } - f.Type = t - if err := resolveDirectives(s, f.Directives); err != nil { - return err - } - return resolveInputObject(s, f.Args) -} - -func resolveDirectives(s *Schema, directives common.DirectiveList) error { - for _, d := range directives { - dirName := d.Name.Name - dd, ok := s.Directives[dirName] - if !ok { - return errors.Errorf("directive %q not found", dirName) - } - for _, arg := range d.Args { - if dd.Args.Get(arg.Name.Name) == nil { - return errors.Errorf("invalid argument %q for directive %q", arg.Name.Name, dirName) - } - } - for _, arg := range dd.Args { - if _, ok := d.Args.Get(arg.Name.Name); !ok { - d.Args = append(d.Args, common.Argument{Name: arg.Name, Value: arg.Default}) - } - } - } - return nil -} - -func resolveInputObject(s *Schema, values common.InputValueList) error { - for _, v := range values { - t, err := common.ResolveType(v.Type, s.Resolve) - if err != nil { - return err - } - v.Type = t - } - return nil -} - -func parseSchema(s *Schema, l *common.Lexer) { - for l.Peek() != scanner.EOF { - desc := l.DescComment() - switch x := l.ConsumeIdent(); x { - case "schema": - l.ConsumeToken('{') - for l.Peek() != '}' { - name := l.ConsumeIdent() - l.ConsumeToken(':') - typ := l.ConsumeIdent() - s.entryPointNames[name] = typ - } - l.ConsumeToken('}') - case "type": - obj := parseObjectDecl(l) - obj.Desc = desc - s.Types[obj.Name] = obj - s.objects = append(s.objects, obj) - case "interface": - intf := parseInterfaceDecl(l) - intf.Desc = desc - s.Types[intf.Name] = intf - case "union": - union := parseUnionDecl(l) - union.Desc = desc - s.Types[union.Name] = union - s.unions = append(s.unions, union) - case "enum": - enum := parseEnumDecl(l) - enum.Desc = desc - s.Types[enum.Name] = enum - s.enums = append(s.enums, enum) - case "input": - input := parseInputDecl(l) - input.Desc = desc - s.Types[input.Name] = input - case "scalar": - name := l.ConsumeIdent() - s.Types[name] = &Scalar{Name: name, Desc: desc} - case "directive": - directive := parseDirectiveDecl(l) - directive.Desc = desc - s.Directives[directive.Name] = directive - default: - l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union", "input", "scalar" or "directive"`, x)) - } - } -} - -func parseObjectDecl(l *common.Lexer) *Object { - o := &Object{} - o.Name = l.ConsumeIdent() - if l.Peek() == scanner.Ident { - l.ConsumeKeyword("implements") - for { - o.interfaceNames = append(o.interfaceNames, l.ConsumeIdent()) - if l.Peek() == '{' { - break - } - } - } - l.ConsumeToken('{') - o.Fields = parseFields(l) - l.ConsumeToken('}') - return o -} - -func parseInterfaceDecl(l *common.Lexer) *Interface { - i := &Interface{} - i.Name = l.ConsumeIdent() - l.ConsumeToken('{') - i.Fields = parseFields(l) - l.ConsumeToken('}') - return i -} - -func parseUnionDecl(l *common.Lexer) *Union { - union := &Union{} - union.Name = l.ConsumeIdent() - l.ConsumeToken('=') - union.typeNames = []string{l.ConsumeIdent()} - for l.Peek() == '|' { - l.ConsumeToken('|') - union.typeNames = append(union.typeNames, l.ConsumeIdent()) - } - return union -} - -func parseInputDecl(l *common.Lexer) *InputObject { - i := &InputObject{} - i.Name = l.ConsumeIdent() - l.ConsumeToken('{') - for l.Peek() != '}' { - i.Values = append(i.Values, common.ParseInputValue(l)) - } - l.ConsumeToken('}') - return i -} - -func parseEnumDecl(l *common.Lexer) *Enum { - enum := &Enum{} - enum.Name = l.ConsumeIdent() - l.ConsumeToken('{') - for l.Peek() != '}' { - v := &EnumValue{} - v.Desc = l.DescComment() - v.Name = l.ConsumeIdent() - v.Directives = common.ParseDirectives(l) - enum.Values = append(enum.Values, v) - } - l.ConsumeToken('}') - return enum -} - -func parseDirectiveDecl(l *common.Lexer) *DirectiveDecl { - d := &DirectiveDecl{} - l.ConsumeToken('@') - d.Name = l.ConsumeIdent() - if l.Peek() == '(' { - l.ConsumeToken('(') - for l.Peek() != ')' { - v := common.ParseInputValue(l) - d.Args = append(d.Args, v) - } - l.ConsumeToken(')') - } - l.ConsumeKeyword("on") - for { - loc := l.ConsumeIdent() - d.Locs = append(d.Locs, loc) - if l.Peek() != '|' { - break - } - l.ConsumeToken('|') - } - return d -} - -func parseFields(l *common.Lexer) FieldList { - var fields FieldList - for l.Peek() != '}' { - f := &Field{} - f.Desc = l.DescComment() - f.Name = l.ConsumeIdent() - if l.Peek() == '(' { - l.ConsumeToken('(') - for l.Peek() != ')' { - f.Args = append(f.Args, common.ParseInputValue(l)) - } - l.ConsumeToken(')') - } - l.ConsumeToken(':') - f.Type = common.ParseType(l) - f.Directives = common.ParseDirectives(l) - fields = append(fields, f) - } - return fields -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/validation/suggestion.go b/vendor/github.com/vektah/gqlgen/neelance/validation/suggestion.go deleted file mode 100644 index 9702b5f5..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/validation/suggestion.go +++ /dev/null @@ -1,71 +0,0 @@ -package validation - -import ( - "fmt" - "sort" - "strconv" - "strings" -) - -func makeSuggestion(prefix string, options []string, input string) string { - var selected []string - distances := make(map[string]int) - for _, opt := range options { - distance := levenshteinDistance(input, opt) - threshold := max(len(input)/2, max(len(opt)/2, 1)) - if distance < threshold { - selected = append(selected, opt) - distances[opt] = distance - } - } - - if len(selected) == 0 { - return "" - } - sort.Slice(selected, func(i, j int) bool { - return distances[selected[i]] < distances[selected[j]] - }) - - parts := make([]string, len(selected)) - for i, opt := range selected { - parts[i] = strconv.Quote(opt) - } - if len(parts) > 1 { - parts[len(parts)-1] = "or " + parts[len(parts)-1] - } - return fmt.Sprintf(" %s %s?", prefix, strings.Join(parts, ", ")) -} - -func levenshteinDistance(s1, s2 string) int { - column := make([]int, len(s1)+1) - for y := range s1 { - column[y+1] = y + 1 - } - for x, rx := range s2 { - column[0] = x + 1 - lastdiag := x - for y, ry := range s1 { - olddiag := column[y+1] - if rx != ry { - lastdiag++ - } - column[y+1] = min(column[y+1]+1, min(column[y]+1, lastdiag)) - lastdiag = olddiag - } - } - return column[len(s1)] -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} diff --git a/vendor/github.com/vektah/gqlgen/neelance/validation/validation.go b/vendor/github.com/vektah/gqlgen/neelance/validation/validation.go deleted file mode 100644 index 28124310..00000000 --- a/vendor/github.com/vektah/gqlgen/neelance/validation/validation.go +++ /dev/null @@ -1,861 +0,0 @@ -package validation - -import ( - "fmt" - "math" - "reflect" - "strconv" - "strings" - "text/scanner" - - "github.com/vektah/gqlgen/neelance/common" - "github.com/vektah/gqlgen/neelance/errors" - "github.com/vektah/gqlgen/neelance/query" - "github.com/vektah/gqlgen/neelance/schema" -) - -type varSet map[*common.InputValue]struct{} - -type selectionPair struct{ a, b query.Selection } - -type fieldInfo struct { - sf *schema.Field - parent schema.NamedType -} - -type context struct { - schema *schema.Schema - doc *query.Document - errs []*errors.QueryError - opErrs map[*query.Operation][]*errors.QueryError - usedVars map[*query.Operation]varSet - fieldMap map[*query.Field]fieldInfo - overlapValidated map[selectionPair]struct{} -} - -func (c *context) addErr(loc errors.Location, rule string, format string, a ...interface{}) { - c.addErrMultiLoc([]errors.Location{loc}, rule, format, a...) -} - -func (c *context) addErrMultiLoc(locs []errors.Location, rule string, format string, a ...interface{}) { - c.errs = append(c.errs, &errors.QueryError{ - Message: fmt.Sprintf(format, a...), - Locations: locs, - Rule: rule, - }) -} - -type opContext struct { - *context - ops []*query.Operation -} - -func Validate(s *schema.Schema, doc *query.Document) []*errors.QueryError { - c := &context{ - schema: s, - doc: doc, - opErrs: make(map[*query.Operation][]*errors.QueryError), - usedVars: make(map[*query.Operation]varSet), - fieldMap: make(map[*query.Field]fieldInfo), - overlapValidated: make(map[selectionPair]struct{}), - } - - opNames := make(nameSet) - fragUsedBy := make(map[*query.FragmentDecl][]*query.Operation) - for _, op := range doc.Operations { - c.usedVars[op] = make(varSet) - opc := &opContext{c, []*query.Operation{op}} - - if op.Name.Name == "" && len(doc.Operations) != 1 { - c.addErr(op.Loc, "LoneAnonymousOperation", "This anonymous operation must be the only defined operation.") - } - if op.Name.Name != "" { - validateName(c, opNames, op.Name, "UniqueOperationNames", "operation") - } - - validateDirectives(opc, string(op.Type), op.Directives) - - varNames := make(nameSet) - for _, v := range op.Vars { - validateName(c, varNames, v.Name, "UniqueVariableNames", "variable") - - t := resolveType(c, v.Type) - if !canBeInput(t) { - c.addErr(v.TypeLoc, "VariablesAreInputTypes", "Variable %q cannot be non-input type %q.", "$"+v.Name.Name, t) - } - - if v.Default != nil { - validateLiteral(opc, v.Default) - - if t != nil { - if nn, ok := t.(*common.NonNull); ok { - c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q is required and will not use the default value. Perhaps you meant to use type %q.", "$"+v.Name.Name, t, nn.OfType) - } - - if ok, reason := validateValueType(opc, v.Default, t); !ok { - c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q has invalid default value %s.\n%s", "$"+v.Name.Name, t, v.Default, reason) - } - } - } - } - - var entryPoint schema.NamedType - switch op.Type { - case query.Query: - entryPoint = s.EntryPoints["query"] - case query.Mutation: - entryPoint = s.EntryPoints["mutation"] - case query.Subscription: - entryPoint = s.EntryPoints["subscription"] - default: - panic("unreachable") - } - - validateSelectionSet(opc, op.Selections, entryPoint) - - fragUsed := make(map[*query.FragmentDecl]struct{}) - markUsedFragments(c, op.Selections, fragUsed) - for frag := range fragUsed { - fragUsedBy[frag] = append(fragUsedBy[frag], op) - } - } - - fragNames := make(nameSet) - fragVisited := make(map[*query.FragmentDecl]struct{}) - for _, frag := range doc.Fragments { - opc := &opContext{c, fragUsedBy[frag]} - - validateName(c, fragNames, frag.Name, "UniqueFragmentNames", "fragment") - validateDirectives(opc, "FRAGMENT_DEFINITION", frag.Directives) - - t := unwrapType(resolveType(c, &frag.On)) - // continue even if t is nil - if t != nil && !canBeFragment(t) { - c.addErr(frag.On.Loc, "FragmentsOnCompositeTypes", "Fragment %q cannot condition on non composite type %q.", frag.Name.Name, t) - continue - } - - validateSelectionSet(opc, frag.Selections, t) - - if _, ok := fragVisited[frag]; !ok { - detectFragmentCycle(c, frag.Selections, fragVisited, nil, map[string]int{frag.Name.Name: 0}) - } - } - - for _, frag := range doc.Fragments { - if len(fragUsedBy[frag]) == 0 { - c.addErr(frag.Loc, "NoUnusedFragments", "Fragment %q is never used.", frag.Name.Name) - } - } - - for _, op := range doc.Operations { - c.errs = append(c.errs, c.opErrs[op]...) - - opUsedVars := c.usedVars[op] - for _, v := range op.Vars { - if _, ok := opUsedVars[v]; !ok { - opSuffix := "" - if op.Name.Name != "" { - opSuffix = fmt.Sprintf(" in operation %q", op.Name.Name) - } - c.addErr(v.Loc, "NoUnusedVariables", "Variable %q is never used%s.", "$"+v.Name.Name, opSuffix) - } - } - } - - return c.errs -} - -func validateSelectionSet(c *opContext, sels []query.Selection, t schema.NamedType) { - for _, sel := range sels { - validateSelection(c, sel, t) - } - - for i, a := range sels { - for _, b := range sels[i+1:] { - c.validateOverlap(a, b, nil, nil) - } - } -} - -func validateSelection(c *opContext, sel query.Selection, t schema.NamedType) { - switch sel := sel.(type) { - case *query.Field: - validateDirectives(c, "FIELD", sel.Directives) - - fieldName := sel.Name.Name - var f *schema.Field - switch fieldName { - case "__typename": - f = &schema.Field{ - Name: "__typename", - Type: c.schema.Types["String"], - } - case "__schema": - f = &schema.Field{ - Name: "__schema", - Type: c.schema.Types["__Schema"], - } - case "__type": - f = &schema.Field{ - Name: "__type", - Args: common.InputValueList{ - &common.InputValue{ - Name: common.Ident{Name: "name"}, - Type: &common.NonNull{OfType: c.schema.Types["String"]}, - }, - }, - Type: c.schema.Types["__Type"], - } - default: - f = fields(t).Get(fieldName) - if f == nil && t != nil { - suggestion := makeSuggestion("Did you mean", fields(t).Names(), fieldName) - c.addErr(sel.Alias.Loc, "FieldsOnCorrectType", "Cannot query field %q on type %q.%s", fieldName, t, suggestion) - } - } - c.fieldMap[sel] = fieldInfo{sf: f, parent: t} - - validateArgumentLiterals(c, sel.Arguments) - if f != nil { - validateArgumentTypes(c, sel.Arguments, f.Args, sel.Alias.Loc, - func() string { return fmt.Sprintf("field %q of type %q", fieldName, t) }, - func() string { return fmt.Sprintf("Field %q", fieldName) }, - ) - } - - var ft common.Type - if f != nil { - ft = f.Type - sf := hasSubfields(ft) - if sf && sel.Selections == nil { - c.addErr(sel.Alias.Loc, "ScalarLeafs", "Field %q of type %q must have a selection of subfields. Did you mean \"%s { ... }\"?", fieldName, ft, fieldName) - } - if !sf && sel.Selections != nil { - c.addErr(sel.SelectionSetLoc, "ScalarLeafs", "Field %q must not have a selection since type %q has no subfields.", fieldName, ft) - } - } - if sel.Selections != nil { - validateSelectionSet(c, sel.Selections, unwrapType(ft)) - } - - case *query.InlineFragment: - validateDirectives(c, "INLINE_FRAGMENT", sel.Directives) - if sel.On.Name != "" { - fragTyp := unwrapType(resolveType(c.context, &sel.On)) - if fragTyp != nil && !compatible(t, fragTyp) { - c.addErr(sel.Loc, "PossibleFragmentSpreads", "Fragment cannot be spread here as objects of type %q can never be of type %q.", t, fragTyp) - } - t = fragTyp - // continue even if t is nil - } - if t != nil && !canBeFragment(t) { - c.addErr(sel.On.Loc, "FragmentsOnCompositeTypes", "Fragment cannot condition on non composite type %q.", t) - return - } - validateSelectionSet(c, sel.Selections, unwrapType(t)) - - case *query.FragmentSpread: - validateDirectives(c, "FRAGMENT_SPREAD", sel.Directives) - frag := c.doc.Fragments.Get(sel.Name.Name) - if frag == nil { - c.addErr(sel.Name.Loc, "KnownFragmentNames", "Unknown fragment %q.", sel.Name.Name) - return - } - fragTyp := c.schema.Types[frag.On.Name] - if !compatible(t, fragTyp) { - c.addErr(sel.Loc, "PossibleFragmentSpreads", "Fragment %q cannot be spread here as objects of type %q can never be of type %q.", frag.Name.Name, t, fragTyp) - } - - default: - panic("unreachable") - } -} - -func compatible(a, b common.Type) bool { - for _, pta := range possibleTypes(a) { - for _, ptb := range possibleTypes(b) { - if pta == ptb { - return true - } - } - } - return false -} - -func possibleTypes(t common.Type) []*schema.Object { - switch t := t.(type) { - case *schema.Object: - return []*schema.Object{t} - case *schema.Interface: - return t.PossibleTypes - case *schema.Union: - return t.PossibleTypes - default: - return nil - } -} - -func markUsedFragments(c *context, sels []query.Selection, fragUsed map[*query.FragmentDecl]struct{}) { - for _, sel := range sels { - switch sel := sel.(type) { - case *query.Field: - if sel.Selections != nil { - markUsedFragments(c, sel.Selections, fragUsed) - } - - case *query.InlineFragment: - markUsedFragments(c, sel.Selections, fragUsed) - - case *query.FragmentSpread: - frag := c.doc.Fragments.Get(sel.Name.Name) - if frag == nil { - return - } - - if _, ok := fragUsed[frag]; ok { - return - } - fragUsed[frag] = struct{}{} - markUsedFragments(c, frag.Selections, fragUsed) - - default: - panic("unreachable") - } - } -} - -func detectFragmentCycle(c *context, sels []query.Selection, fragVisited map[*query.FragmentDecl]struct{}, spreadPath []*query.FragmentSpread, spreadPathIndex map[string]int) { - for _, sel := range sels { - detectFragmentCycleSel(c, sel, fragVisited, spreadPath, spreadPathIndex) - } -} - -func detectFragmentCycleSel(c *context, sel query.Selection, fragVisited map[*query.FragmentDecl]struct{}, spreadPath []*query.FragmentSpread, spreadPathIndex map[string]int) { - switch sel := sel.(type) { - case *query.Field: - if sel.Selections != nil { - detectFragmentCycle(c, sel.Selections, fragVisited, spreadPath, spreadPathIndex) - } - - case *query.InlineFragment: - detectFragmentCycle(c, sel.Selections, fragVisited, spreadPath, spreadPathIndex) - - case *query.FragmentSpread: - frag := c.doc.Fragments.Get(sel.Name.Name) - if frag == nil { - return - } - - spreadPath = append(spreadPath, sel) - if i, ok := spreadPathIndex[frag.Name.Name]; ok { - cyclePath := spreadPath[i:] - via := "" - if len(cyclePath) > 1 { - names := make([]string, len(cyclePath)-1) - for i, frag := range cyclePath[:len(cyclePath)-1] { - names[i] = frag.Name.Name - } - via = " via " + strings.Join(names, ", ") - } - - locs := make([]errors.Location, len(cyclePath)) - for i, frag := range cyclePath { - locs[i] = frag.Loc - } - c.addErrMultiLoc(locs, "NoFragmentCycles", "Cannot spread fragment %q within itself%s.", frag.Name.Name, via) - return - } - - if _, ok := fragVisited[frag]; ok { - return - } - fragVisited[frag] = struct{}{} - - spreadPathIndex[frag.Name.Name] = len(spreadPath) - detectFragmentCycle(c, frag.Selections, fragVisited, spreadPath, spreadPathIndex) - delete(spreadPathIndex, frag.Name.Name) - - default: - panic("unreachable") - } -} - -func (c *context) validateOverlap(a, b query.Selection, reasons *[]string, locs *[]errors.Location) { - if a == b { - return - } - - if _, ok := c.overlapValidated[selectionPair{a, b}]; ok { - return - } - c.overlapValidated[selectionPair{a, b}] = struct{}{} - c.overlapValidated[selectionPair{b, a}] = struct{}{} - - switch a := a.(type) { - case *query.Field: - switch b := b.(type) { - case *query.Field: - if b.Alias.Loc.Before(a.Alias.Loc) { - a, b = b, a - } - if reasons2, locs2 := c.validateFieldOverlap(a, b); len(reasons2) != 0 { - locs2 = append(locs2, a.Alias.Loc, b.Alias.Loc) - if reasons == nil { - c.addErrMultiLoc(locs2, "OverlappingFieldsCanBeMerged", "Fields %q conflict because %s. Use different aliases on the fields to fetch both if this was intentional.", a.Alias.Name, strings.Join(reasons2, " and ")) - return - } - for _, r := range reasons2 { - *reasons = append(*reasons, fmt.Sprintf("subfields %q conflict because %s", a.Alias.Name, r)) - } - *locs = append(*locs, locs2...) - } - - case *query.InlineFragment: - for _, sel := range b.Selections { - c.validateOverlap(a, sel, reasons, locs) - } - - case *query.FragmentSpread: - if frag := c.doc.Fragments.Get(b.Name.Name); frag != nil { - for _, sel := range frag.Selections { - c.validateOverlap(a, sel, reasons, locs) - } - } - - default: - panic("unreachable") - } - - case *query.InlineFragment: - for _, sel := range a.Selections { - c.validateOverlap(sel, b, reasons, locs) - } - - case *query.FragmentSpread: - if frag := c.doc.Fragments.Get(a.Name.Name); frag != nil { - for _, sel := range frag.Selections { - c.validateOverlap(sel, b, reasons, locs) - } - } - - default: - panic("unreachable") - } -} - -func (c *context) validateFieldOverlap(a, b *query.Field) ([]string, []errors.Location) { - if a.Alias.Name != b.Alias.Name { - return nil, nil - } - - if asf := c.fieldMap[a].sf; asf != nil { - if bsf := c.fieldMap[b].sf; bsf != nil { - if !typesCompatible(asf.Type, bsf.Type) { - return []string{fmt.Sprintf("they return conflicting types %s and %s", asf.Type, bsf.Type)}, nil - } - } - } - - at := c.fieldMap[a].parent - bt := c.fieldMap[b].parent - if at == nil || bt == nil || at == bt { - if a.Name.Name != b.Name.Name { - return []string{fmt.Sprintf("%s and %s are different fields", a.Name.Name, b.Name.Name)}, nil - } - - if argumentsConflict(a.Arguments, b.Arguments) { - return []string{"they have differing arguments"}, nil - } - } - - var reasons []string - var locs []errors.Location - for _, a2 := range a.Selections { - for _, b2 := range b.Selections { - c.validateOverlap(a2, b2, &reasons, &locs) - } - } - return reasons, locs -} - -func argumentsConflict(a, b common.ArgumentList) bool { - if len(a) != len(b) { - return true - } - for _, argA := range a { - valB, ok := b.Get(argA.Name.Name) - if !ok || !reflect.DeepEqual(argA.Value.Value(nil), valB.Value(nil)) { - return true - } - } - return false -} - -func fields(t common.Type) schema.FieldList { - switch t := t.(type) { - case *schema.Object: - return t.Fields - case *schema.Interface: - return t.Fields - default: - return nil - } -} - -func unwrapType(t common.Type) schema.NamedType { - if t == nil { - return nil - } - for { - switch t2 := t.(type) { - case schema.NamedType: - return t2 - case *common.List: - t = t2.OfType - case *common.NonNull: - t = t2.OfType - default: - panic("unreachable") - } - } -} - -func resolveType(c *context, t common.Type) common.Type { - t2, err := common.ResolveType(t, c.schema.Resolve) - if err != nil { - c.errs = append(c.errs, err) - } - return t2 -} - -func validateDirectives(c *opContext, loc string, directives common.DirectiveList) { - directiveNames := make(nameSet) - for _, d := range directives { - dirName := d.Name.Name - validateNameCustomMsg(c.context, directiveNames, d.Name, "UniqueDirectivesPerLocation", func() string { - return fmt.Sprintf("The directive %q can only be used once at this location.", dirName) - }) - - validateArgumentLiterals(c, d.Args) - - dd, ok := c.schema.Directives[dirName] - if !ok { - c.addErr(d.Name.Loc, "KnownDirectives", "Unknown directive %q.", dirName) - continue - } - - locOK := false - for _, allowedLoc := range dd.Locs { - if loc == allowedLoc { - locOK = true - break - } - } - if !locOK { - c.addErr(d.Name.Loc, "KnownDirectives", "Directive %q may not be used on %s.", dirName, loc) - } - - validateArgumentTypes(c, d.Args, dd.Args, d.Name.Loc, - func() string { return fmt.Sprintf("directive %q", "@"+dirName) }, - func() string { return fmt.Sprintf("Directive %q", "@"+dirName) }, - ) - } -} - -type nameSet map[string]errors.Location - -func validateName(c *context, set nameSet, name common.Ident, rule string, kind string) { - validateNameCustomMsg(c, set, name, rule, func() string { - return fmt.Sprintf("There can be only one %s named %q.", kind, name.Name) - }) -} - -func validateNameCustomMsg(c *context, set nameSet, name common.Ident, rule string, msg func() string) { - if loc, ok := set[name.Name]; ok { - c.addErrMultiLoc([]errors.Location{loc, name.Loc}, rule, msg()) - return - } - set[name.Name] = name.Loc -} - -func validateArgumentTypes(c *opContext, args common.ArgumentList, argDecls common.InputValueList, loc errors.Location, owner1, owner2 func() string) { - for _, selArg := range args { - arg := argDecls.Get(selArg.Name.Name) - if arg == nil { - c.addErr(selArg.Name.Loc, "KnownArgumentNames", "Unknown argument %q on %s.", selArg.Name.Name, owner1()) - continue - } - value := selArg.Value - if ok, reason := validateValueType(c, value, arg.Type); !ok { - c.addErr(value.Location(), "ArgumentsOfCorrectType", "Argument %q has invalid value %s.\n%s", arg.Name.Name, value, reason) - } - } - for _, decl := range argDecls { - if _, ok := decl.Type.(*common.NonNull); ok { - if _, ok := args.Get(decl.Name.Name); !ok { - c.addErr(loc, "ProvidedNonNullArguments", "%s argument %q of type %q is required but not provided.", owner2(), decl.Name.Name, decl.Type) - } - } - } -} - -func validateArgumentLiterals(c *opContext, args common.ArgumentList) { - argNames := make(nameSet) - for _, arg := range args { - validateName(c.context, argNames, arg.Name, "UniqueArgumentNames", "argument") - validateLiteral(c, arg.Value) - } -} - -func validateLiteral(c *opContext, l common.Literal) { - switch l := l.(type) { - case *common.ObjectLit: - fieldNames := make(nameSet) - for _, f := range l.Fields { - validateName(c.context, fieldNames, f.Name, "UniqueInputFieldNames", "input field") - validateLiteral(c, f.Value) - } - case *common.ListLit: - for _, entry := range l.Entries { - validateLiteral(c, entry) - } - case *common.Variable: - for _, op := range c.ops { - v := op.Vars.Get(l.Name) - if v == nil { - byOp := "" - if op.Name.Name != "" { - byOp = fmt.Sprintf(" by operation %q", op.Name.Name) - } - c.opErrs[op] = append(c.opErrs[op], &errors.QueryError{ - Message: fmt.Sprintf("Variable %q is not defined%s.", "$"+l.Name, byOp), - Locations: []errors.Location{l.Loc, op.Loc}, - Rule: "NoUndefinedVariables", - }) - continue - } - c.usedVars[op][v] = struct{}{} - } - } -} - -func validateValueType(c *opContext, v common.Literal, t common.Type) (bool, string) { - if v, ok := v.(*common.Variable); ok { - for _, op := range c.ops { - if v2 := op.Vars.Get(v.Name); v2 != nil { - t2, err := common.ResolveType(v2.Type, c.schema.Resolve) - if _, ok := t2.(*common.NonNull); !ok && v2.Default != nil { - t2 = &common.NonNull{OfType: t2} - } - if err == nil && !typeCanBeUsedAs(t2, t) { - c.addErrMultiLoc([]errors.Location{v2.Loc, v.Loc}, "VariablesInAllowedPosition", "Variable %q of type %q used in position expecting type %q.", "$"+v.Name, t2, t) - } - } - } - return true, "" - } - - if nn, ok := t.(*common.NonNull); ok { - if isNull(v) { - return false, fmt.Sprintf("Expected %q, found null.", t) - } - t = nn.OfType - } - if isNull(v) { - return true, "" - } - - switch t := t.(type) { - case *schema.Scalar, *schema.Enum: - if lit, ok := v.(*common.BasicLit); ok { - if validateBasicLit(lit, t) { - return true, "" - } - } else { - // custom complex scalars will be validated when unmarshaling - return true, "" - } - - case *common.List: - list, ok := v.(*common.ListLit) - if !ok { - return validateValueType(c, v, t.OfType) // single value instead of list - } - for i, entry := range list.Entries { - if ok, reason := validateValueType(c, entry, t.OfType); !ok { - return false, fmt.Sprintf("In element #%d: %s", i, reason) - } - } - return true, "" - - case *schema.InputObject: - v, ok := v.(*common.ObjectLit) - if !ok { - return false, fmt.Sprintf("Expected %q, found not an object.", t) - } - for _, f := range v.Fields { - name := f.Name.Name - iv := t.Values.Get(name) - if iv == nil { - return false, fmt.Sprintf("In field %q: Unknown field.", name) - } - if ok, reason := validateValueType(c, f.Value, iv.Type); !ok { - return false, fmt.Sprintf("In field %q: %s", name, reason) - } - } - for _, iv := range t.Values { - found := false - for _, f := range v.Fields { - if f.Name.Name == iv.Name.Name { - found = true - break - } - } - if !found { - if _, ok := iv.Type.(*common.NonNull); ok && iv.Default == nil { - return false, fmt.Sprintf("In field %q: Expected %q, found null.", iv.Name.Name, iv.Type) - } - } - } - return true, "" - } - - return false, fmt.Sprintf("Expected type %q, found %s.", t, v) -} - -func validateBasicLit(v *common.BasicLit, t common.Type) bool { - switch t := t.(type) { - case *schema.Scalar: - switch t.Name { - case "Int": - if v.Type != scanner.Int { - return false - } - f, err := strconv.ParseFloat(v.Text, 64) - if err != nil { - panic(err) - } - return f >= math.MinInt32 && f <= math.MaxInt32 - case "Float": - return v.Type == scanner.Int || v.Type == scanner.Float - case "String": - return v.Type == scanner.String - case "Boolean": - return v.Type == scanner.Ident && (v.Text == "true" || v.Text == "false") - case "ID": - return v.Type == scanner.Int || v.Type == scanner.String - default: - //TODO: Type-check against expected type by Unmarshaling - return true - } - - case *schema.Enum: - if v.Type != scanner.Ident { - return false - } - for _, option := range t.Values { - if option.Name == v.Text { - return true - } - } - return false - } - - return false -} - -func canBeFragment(t common.Type) bool { - switch t.(type) { - case *schema.Object, *schema.Interface, *schema.Union: - return true - default: - return false - } -} - -func canBeInput(t common.Type) bool { - switch t := t.(type) { - case *schema.InputObject, *schema.Scalar, *schema.Enum: - return true - case *common.List: - return canBeInput(t.OfType) - case *common.NonNull: - return canBeInput(t.OfType) - default: - return false - } -} - -func hasSubfields(t common.Type) bool { - switch t := t.(type) { - case *schema.Object, *schema.Interface, *schema.Union: - return true - case *common.List: - return hasSubfields(t.OfType) - case *common.NonNull: - return hasSubfields(t.OfType) - default: - return false - } -} - -func isLeaf(t common.Type) bool { - switch t.(type) { - case *schema.Scalar, *schema.Enum: - return true - default: - return false - } -} - -func isNull(lit interface{}) bool { - _, ok := lit.(*common.NullLit) - return ok -} - -func typesCompatible(a, b common.Type) bool { - al, aIsList := a.(*common.List) - bl, bIsList := b.(*common.List) - if aIsList || bIsList { - return aIsList && bIsList && typesCompatible(al.OfType, bl.OfType) - } - - ann, aIsNN := a.(*common.NonNull) - bnn, bIsNN := b.(*common.NonNull) - if aIsNN || bIsNN { - return aIsNN && bIsNN && typesCompatible(ann.OfType, bnn.OfType) - } - - if isLeaf(a) || isLeaf(b) { - return a == b - } - - return true -} - -func typeCanBeUsedAs(t, as common.Type) bool { - nnT, okT := t.(*common.NonNull) - if okT { - t = nnT.OfType - } - - nnAs, okAs := as.(*common.NonNull) - if okAs { - as = nnAs.OfType - if !okT { - return false // nullable can not be used as non-null - } - } - - if t == as { - return true - } - - if lT, ok := t.(*common.List); ok { - if lAs, ok := as.(*common.List); ok { - return typeCanBeUsedAs(lT.OfType, lAs.OfType) - } - } - return false -} diff --git a/vendor/github.com/vektah/gqlparser/.gitignore b/vendor/github.com/vektah/gqlparser/.gitignore new file mode 100644 index 00000000..877392a7 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/.gitignore @@ -0,0 +1,5 @@ +/vendor +/validator/imported/node_modules +/validator/imported/graphql-js + +.idea/ diff --git a/vendor/github.com/vektah/gqlparser/.gometalinter.json b/vendor/github.com/vektah/gqlparser/.gometalinter.json new file mode 100644 index 00000000..e4e00223 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/.gometalinter.json @@ -0,0 +1,13 @@ +{ + "sort": ["path"], + "Deadline": "5m", + "Linters": { + "errcheck": { + "Command": "errcheck -abspath -ignore '[rR]ead|[wW]rite|Close'", + "Pattern": "PATH:LINE:COL:MESSAGE", + "InstallFrom": "github.com/kisielk/errcheck", + "PartitionStrategy": "packages" + } + }, + "Disable": ["golint","gocyclo", "goconst", "gas", "interfacer", "vet","gosec"] +} diff --git a/vendor/github.com/vektah/gqlparser/LICENSE b/vendor/github.com/vektah/gqlparser/LICENSE new file mode 100644 index 00000000..1221b9d3 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/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.
\ No newline at end of file diff --git a/vendor/github.com/vektah/gqlparser/ast/argmap.go b/vendor/github.com/vektah/gqlparser/ast/argmap.go new file mode 100644 index 00000000..43f6a3d6 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/argmap.go @@ -0,0 +1,37 @@ +package ast + +func arg2map(defs ArgumentDefinitionList, args ArgumentList, vars map[string]interface{}) map[string]interface{} { + result := map[string]interface{}{} + var err error + + for _, argDef := range defs { + var val interface{} + var hasValue bool + + if argValue := args.ForName(argDef.Name); argValue != nil { + if argValue.Value.Kind == Variable { + val, hasValue = vars[argValue.Value.Raw] + } else { + val, err = argValue.Value.Value(vars) + if err != nil { + panic(err) + } + hasValue = true + } + } + + if !hasValue && argDef.DefaultValue != nil { + val, err = argDef.DefaultValue.Value(vars) + if err != nil { + panic(err) + } + hasValue = true + } + + if hasValue { + result[argDef.Name] = val + } + } + + return result +} diff --git a/vendor/github.com/vektah/gqlparser/ast/collections.go b/vendor/github.com/vektah/gqlparser/ast/collections.go new file mode 100644 index 00000000..6bf67297 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/collections.go @@ -0,0 +1,138 @@ +package ast + +type FieldList []*FieldDefinition + +func (l FieldList) ForName(name string) *FieldDefinition { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type EnumValueList []*EnumValueDefinition + +func (l EnumValueList) ForName(name string) *EnumValueDefinition { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type DirectiveList []*Directive + +func (l DirectiveList) ForName(name string) *Directive { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type OperationList []*OperationDefinition + +func (l OperationList) ForName(name string) *OperationDefinition { + if name == "" && len(l) == 1 { + return l[0] + } + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type FragmentDefinitionList []*FragmentDefinition + +func (l FragmentDefinitionList) ForName(name string) *FragmentDefinition { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type VariableDefinitionList []*VariableDefinition + +func (l VariableDefinitionList) ForName(name string) *VariableDefinition { + for _, it := range l { + if it.Variable == name { + return it + } + } + return nil +} + +type ArgumentList []*Argument + +func (l ArgumentList) ForName(name string) *Argument { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type ArgumentDefinitionList []*ArgumentDefinition + +func (l ArgumentDefinitionList) ForName(name string) *ArgumentDefinition { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type SchemaDefinitionList []*SchemaDefinition + +type DirectiveDefinitionList []*DirectiveDefinition + +func (l DirectiveDefinitionList) ForName(name string) *DirectiveDefinition { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type DefinitionList []*Definition + +func (l DefinitionList) ForName(name string) *Definition { + for _, it := range l { + if it.Name == name { + return it + } + } + return nil +} + +type OperationTypeDefinitionList []*OperationTypeDefinition + +func (l OperationTypeDefinitionList) ForType(name string) *OperationTypeDefinition { + for _, it := range l { + if it.Type == name { + return it + } + } + return nil +} + +type ChildValueList []*ChildValue + +func (v ChildValueList) ForName(name string) *Value { + for _, f := range v { + if f.Name == name { + return f.Value + } + } + return nil +} diff --git a/vendor/github.com/vektah/gqlparser/ast/definition.go b/vendor/github.com/vektah/gqlparser/ast/definition.go new file mode 100644 index 00000000..74f4ece5 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/definition.go @@ -0,0 +1,92 @@ +package ast + +type DefinitionKind string + +const ( + Scalar DefinitionKind = "SCALAR" + Object DefinitionKind = "OBJECT" + Interface DefinitionKind = "INTERFACE" + Union DefinitionKind = "UNION" + Enum DefinitionKind = "ENUM" + InputObject DefinitionKind = "INPUT_OBJECT" +) + +// ObjectDefinition is the core type definition object, it includes all of the definable types +// but does *not* cover schema or directives. +// +// @vektah: Javascript implementation has different types for all of these, but they are +// more similar than different and don't define any behaviour. I think this style of +// "some hot" struct works better, at least for go. +// +// Type extensions are also represented by this same struct. +type Definition struct { + Kind DefinitionKind + Description string + Name string + Directives DirectiveList + Interfaces []string // object and input object + Fields FieldList // object and input object + Types []string // union + EnumValues EnumValueList // enum + + Position *Position `dump:"-"` +} + +func (d *Definition) IsLeafType() bool { + return d.Kind == Enum || d.Kind == Scalar +} + +func (d *Definition) IsAbstractType() bool { + return d.Kind == Interface || d.Kind == Union +} + +func (d *Definition) IsCompositeType() bool { + return d.Kind == Object || d.Kind == Interface || d.Kind == Union +} + +func (d *Definition) IsInputType() bool { + return d.Kind == Scalar || d.Kind == Enum || d.Kind == InputObject +} + +func (d *Definition) OneOf(types ...string) bool { + for _, t := range types { + if d.Name == t { + return true + } + } + return false +} + +type FieldDefinition struct { + Description string + Name string + Arguments ArgumentDefinitionList // only for objects + DefaultValue *Value // only for input objects + Type *Type + Directives DirectiveList + Position *Position `dump:"-"` +} + +type ArgumentDefinition struct { + Description string + Name string + DefaultValue *Value + Type *Type + Directives DirectiveList + Position *Position `dump:"-"` +} + +type EnumValueDefinition struct { + Description string + Name string + Directives DirectiveList + Position *Position `dump:"-"` +} + +type DirectiveDefinition struct { + Description string + Name string + Arguments ArgumentDefinitionList + Locations []DirectiveLocation + Position *Position `dump:"-"` +} diff --git a/vendor/github.com/vektah/gqlparser/ast/directive.go b/vendor/github.com/vektah/gqlparser/ast/directive.go new file mode 100644 index 00000000..9b07c92a --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/directive.go @@ -0,0 +1,42 @@ +package ast + +type DirectiveLocation string + +const ( + // Executable + LocationQuery DirectiveLocation = `QUERY` + LocationMutation DirectiveLocation = `MUTATION` + LocationSubscription DirectiveLocation = `SUBSCRIPTION` + LocationField DirectiveLocation = `FIELD` + LocationFragmentDefinition DirectiveLocation = `FRAGMENT_DEFINITION` + LocationFragmentSpread DirectiveLocation = `FRAGMENT_SPREAD` + LocationInlineFragment DirectiveLocation = `INLINE_FRAGMENT` + + // Type System + LocationSchema DirectiveLocation = `SCHEMA` + LocationScalar DirectiveLocation = `SCALAR` + LocationObject DirectiveLocation = `OBJECT` + LocationFieldDefinition DirectiveLocation = `FIELD_DEFINITION` + LocationArgumentDefinition DirectiveLocation = `ARGUMENT_DEFINITION` + LocationInterface DirectiveLocation = `INTERFACE` + LocationUnion DirectiveLocation = `UNION` + LocationEnum DirectiveLocation = `ENUM` + LocationEnumValue DirectiveLocation = `ENUM_VALUE` + LocationInputObject DirectiveLocation = `INPUT_OBJECT` + LocationInputFieldDefinition DirectiveLocation = `INPUT_FIELD_DEFINITION` +) + +type Directive struct { + Name string + Arguments ArgumentList + Position *Position `dump:"-"` + + // Requires validation + ParentDefinition *Definition + Definition *DirectiveDefinition + Location DirectiveLocation +} + +func (d *Directive) ArgumentMap(vars map[string]interface{}) map[string]interface{} { + return arg2map(d.Definition.Arguments, d.Arguments, vars) +} diff --git a/vendor/github.com/vektah/gqlparser/ast/document.go b/vendor/github.com/vektah/gqlparser/ast/document.go new file mode 100644 index 00000000..b7657d62 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/document.go @@ -0,0 +1,65 @@ +package ast + +type QueryDocument struct { + Operations OperationList + Fragments FragmentDefinitionList + Position *Position `dump:"-"` +} + +type SchemaDocument struct { + Schema SchemaDefinitionList + SchemaExtension SchemaDefinitionList + Directives DirectiveDefinitionList + Definitions DefinitionList + Extensions DefinitionList + Position *Position `dump:"-"` +} + +func (d *SchemaDocument) Merge(other *SchemaDocument) { + d.Schema = append(d.Schema, other.Schema...) + d.SchemaExtension = append(d.SchemaExtension, other.SchemaExtension...) + d.Directives = append(d.Directives, other.Directives...) + d.Definitions = append(d.Definitions, other.Definitions...) + d.Extensions = append(d.Extensions, other.Extensions...) +} + +type Schema struct { + Query *Definition + Mutation *Definition + Subscription *Definition + + Types map[string]*Definition + Directives map[string]*DirectiveDefinition + + PossibleTypes map[string][]*Definition +} + +func (s *Schema) AddPossibleType(name string, def *Definition) { + s.PossibleTypes[name] = append(s.PossibleTypes[name], def) +} + +// GetPossibleTypes will enumerate all the definitions for a given interface or union +func (s *Schema) GetPossibleTypes(def *Definition) []*Definition { + if def.Kind == Union { + var defs []*Definition + for _, t := range def.Types { + defs = append(defs, s.Types[t]) + } + return defs + } + + return s.PossibleTypes[def.Name] +} + +type SchemaDefinition struct { + Description string + Directives DirectiveList + OperationTypes OperationTypeDefinitionList + Position *Position `dump:"-"` +} + +type OperationTypeDefinition struct { + Operation Operation + Type string + Position *Position `dump:"-"` +} diff --git a/vendor/github.com/vektah/gqlparser/ast/dumper.go b/vendor/github.com/vektah/gqlparser/ast/dumper.go new file mode 100644 index 00000000..dbb7a7ef --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/dumper.go @@ -0,0 +1,159 @@ +package ast + +import ( + "bytes" + "fmt" + "reflect" + "strconv" + "strings" +) + +// Dump turns ast into a stable string format for assertions in tests +func Dump(i interface{}) string { + v := reflect.ValueOf(i) + + d := dumper{Buffer: &bytes.Buffer{}} + d.dump(v) + + return d.String() +} + +type dumper struct { + *bytes.Buffer + indent int +} + +type Dumpable interface { + Dump() string +} + +func (d *dumper) dump(v reflect.Value) { + if dumpable, isDumpable := v.Interface().(Dumpable); isDumpable { + d.WriteString(dumpable.Dump()) + return + } + switch v.Kind() { + case reflect.Bool: + if v.Bool() { + d.WriteString("true") + } else { + d.WriteString("false") + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + d.WriteString(fmt.Sprintf("%d", v.Int())) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + d.WriteString(fmt.Sprintf("%d", v.Uint())) + + case reflect.Float32, reflect.Float64: + d.WriteString(fmt.Sprintf("%.2f", v.Float())) + + case reflect.String: + if v.Type().Name() != "string" { + d.WriteString(v.Type().Name() + "(" + strconv.Quote(v.String()) + ")") + } else { + d.WriteString(strconv.Quote(v.String())) + } + + case reflect.Array, reflect.Slice: + d.dumpArray(v) + + case reflect.Interface, reflect.Ptr: + d.dumpPtr(v) + + case reflect.Struct: + d.dumpStruct(v) + + default: + panic(fmt.Errorf("unsupported kind: %s\n buf: %s", v.Kind().String(), d.String())) + } +} + +func (d *dumper) writeIndent() { + d.Buffer.WriteString(strings.Repeat(" ", d.indent)) +} + +func (d *dumper) nl() { + d.Buffer.WriteByte('\n') + d.writeIndent() +} + +func typeName(t reflect.Type) string { + if t.Kind() == reflect.Ptr { + return typeName(t.Elem()) + } + return t.Name() +} + +func (d *dumper) dumpArray(v reflect.Value) { + d.WriteString("[" + typeName(v.Type().Elem()) + "]") + + for i := 0; i < v.Len(); i++ { + d.nl() + d.WriteString("- ") + d.indent++ + d.dump(v.Index(i)) + d.indent-- + } +} + +func (d *dumper) dumpStruct(v reflect.Value) { + d.WriteString("<" + v.Type().Name() + ">") + d.indent++ + + typ := v.Type() + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + if typ.Field(i).Tag.Get("dump") == "-" { + continue + } + + if isZero(f) { + continue + } + d.nl() + d.WriteString(typ.Field(i).Name) + d.WriteString(": ") + d.dump(v.Field(i)) + } + + d.indent-- +} + +func isZero(v reflect.Value) bool { + switch v.Kind() { + case reflect.Ptr, reflect.Interface: + return v.IsNil() + case reflect.Func, reflect.Map: + return v.IsNil() + + case reflect.Array, reflect.Slice: + if v.IsNil() { + return true + } + z := true + for i := 0; i < v.Len(); i++ { + z = z && isZero(v.Index(i)) + } + return z + case reflect.Struct: + z := true + for i := 0; i < v.NumField(); i++ { + z = z && isZero(v.Field(i)) + } + return z + case reflect.String: + return v.String() == "" + } + + // Compare other types directly: + return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type())) +} + +func (d *dumper) dumpPtr(v reflect.Value) { + if v.IsNil() { + d.WriteString("nil") + return + } + d.dump(v.Elem()) +} diff --git a/vendor/github.com/vektah/gqlparser/ast/fragment.go b/vendor/github.com/vektah/gqlparser/ast/fragment.go new file mode 100644 index 00000000..57ab56c7 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/fragment.go @@ -0,0 +1,38 @@ +package ast + +type FragmentSpread struct { + Name string + Directives DirectiveList + + // Require validation + ObjectDefinition *Definition + Definition *FragmentDefinition + + Position *Position `dump:"-"` +} + +type InlineFragment struct { + TypeCondition string + Directives DirectiveList + SelectionSet SelectionSet + + // Require validation + ObjectDefinition *Definition + + Position *Position `dump:"-"` +} + +type FragmentDefinition struct { + Name string + // Note: fragment variable definitions are experimental and may be changed + // or removed in the future. + VariableDefinition VariableDefinitionList + TypeCondition string + Directives DirectiveList + SelectionSet SelectionSet + + // Require validation + Definition *Definition + + Position *Position `dump:"-"` +} diff --git a/vendor/github.com/vektah/gqlparser/ast/operation.go b/vendor/github.com/vektah/gqlparser/ast/operation.go new file mode 100644 index 00000000..03e916a0 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/operation.go @@ -0,0 +1,29 @@ +package ast + +type Operation string + +const ( + Query Operation = "query" + Mutation Operation = "mutation" + Subscription Operation = "subscription" +) + +type OperationDefinition struct { + Operation Operation + Name string + VariableDefinitions VariableDefinitionList + Directives DirectiveList + SelectionSet SelectionSet + Position *Position `dump:"-"` +} + +type VariableDefinition struct { + Variable string + Type *Type + DefaultValue *Value + Position *Position `dump:"-"` + + // Requires validation + Definition *Definition + Used bool `dump:"-"` +} diff --git a/vendor/github.com/vektah/gqlparser/ast/selection.go b/vendor/github.com/vektah/gqlparser/ast/selection.go new file mode 100644 index 00000000..159db844 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/selection.go @@ -0,0 +1,39 @@ +package ast + +type SelectionSet []Selection + +type Selection interface { + isSelection() + GetPosition() *Position +} + +func (*Field) isSelection() {} +func (*FragmentSpread) isSelection() {} +func (*InlineFragment) isSelection() {} + +func (s *Field) GetPosition() *Position { return s.Position } +func (s *FragmentSpread) GetPosition() *Position { return s.Position } +func (s *InlineFragment) GetPosition() *Position { return s.Position } + +type Field struct { + Alias string + Name string + Arguments ArgumentList + Directives DirectiveList + SelectionSet SelectionSet + Position *Position `dump:"-"` + + // Require validation + Definition *FieldDefinition + ObjectDefinition *Definition +} + +type Argument struct { + Name string + Value *Value + Position *Position `dump:"-"` +} + +func (f *Field) ArgumentMap(vars map[string]interface{}) map[string]interface{} { + return arg2map(f.Definition.Arguments, f.Arguments, vars) +} diff --git a/vendor/github.com/vektah/gqlparser/ast/source.go b/vendor/github.com/vektah/gqlparser/ast/source.go new file mode 100644 index 00000000..9d44dd9c --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/source.go @@ -0,0 +1,14 @@ +package ast + +type Source struct { + Name string + Input string +} + +type Position struct { + Start int // The starting position, in runes, of this token in the input. + End int // The end position, in runes, of this token in the input. + Line int // The line number at the start of this item. + Column int // The line number at the start of this item. + Src *Source // The source document this token belongs to +} diff --git a/vendor/github.com/vektah/gqlparser/ast/type.go b/vendor/github.com/vektah/gqlparser/ast/type.go new file mode 100644 index 00000000..9577fdb4 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/type.go @@ -0,0 +1,68 @@ +package ast + +func NonNullNamedType(named string, pos *Position) *Type { + return &Type{NamedType: named, NonNull: true, Position: pos} +} + +func NamedType(named string, pos *Position) *Type { + return &Type{NamedType: named, NonNull: false, Position: pos} +} + +func NonNullListType(elem *Type, pos *Position) *Type { + return &Type{Elem: elem, NonNull: true, Position: pos} +} + +func ListType(elem *Type, pos *Position) *Type { + return &Type{Elem: elem, NonNull: false, Position: pos} +} + +type Type struct { + NamedType string + Elem *Type + NonNull bool + Position *Position `dump:"-"` +} + +func (t *Type) Name() string { + if t.NamedType != "" { + return t.NamedType + } + + return t.Elem.Name() +} + +func (t *Type) String() string { + nn := "" + if t.NonNull { + nn = "!" + } + if t.NamedType != "" { + return t.NamedType + nn + } + + return "[" + t.Elem.String() + "]" + nn +} + +func (t *Type) IsCompatible(other *Type) bool { + if t.NamedType != other.NamedType { + return false + } + + if t.Elem != nil && other.Elem == nil { + return false + } + + if t.Elem != nil && !t.Elem.IsCompatible(other.Elem) { + return false + } + + if other.NonNull { + return t.NonNull + } + + return true +} + +func (v *Type) Dump() string { + return v.String() +} diff --git a/vendor/github.com/vektah/gqlparser/ast/value.go b/vendor/github.com/vektah/gqlparser/ast/value.go new file mode 100644 index 00000000..3168b266 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/ast/value.go @@ -0,0 +1,120 @@ +package ast + +import ( + "fmt" + "strconv" + "strings" +) + +type ValueKind int + +const ( + Variable ValueKind = iota + IntValue + FloatValue + StringValue + BlockValue + BooleanValue + NullValue + EnumValue + ListValue + ObjectValue +) + +type Value struct { + Raw string + Children ChildValueList + Kind ValueKind + Position *Position `dump:"-"` + + // Require validation + Definition *Definition + VariableDefinition *VariableDefinition + ExpectedType *Type +} + +type ChildValue struct { + Name string + Value *Value + Position *Position `dump:"-"` +} + +func (v *Value) Value(vars map[string]interface{}) (interface{}, error) { + if v == nil { + return nil, nil + } + switch v.Kind { + case Variable: + if value, ok := vars[v.Raw]; ok { + return value, nil + } + if v.VariableDefinition != nil && v.VariableDefinition.DefaultValue != nil { + return v.VariableDefinition.DefaultValue.Value(vars) + } + return nil, nil + case IntValue: + return strconv.ParseInt(v.Raw, 10, 64) + case FloatValue: + return strconv.ParseFloat(v.Raw, 64) + case StringValue, BlockValue, EnumValue: + return v.Raw, nil + case BooleanValue: + return strconv.ParseBool(v.Raw) + case NullValue: + return nil, nil + case ListValue: + var val []interface{} + for _, elem := range v.Children { + elemVal, err := elem.Value.Value(vars) + if err != nil { + return val, err + } + val = append(val, elemVal) + } + return val, nil + case ObjectValue: + val := map[string]interface{}{} + for _, elem := range v.Children { + elemVal, err := elem.Value.Value(vars) + if err != nil { + return val, err + } + val[elem.Name] = elemVal + } + return val, nil + default: + panic(fmt.Errorf("unknown value kind %d", v.Kind)) + } +} + +func (v *Value) String() string { + if v == nil { + return "<nil>" + } + switch v.Kind { + case Variable: + return "$" + v.Raw + case IntValue, FloatValue, EnumValue, BooleanValue, NullValue: + return v.Raw + case StringValue, BlockValue: + return strconv.Quote(v.Raw) + case ListValue: + var val []string + for _, elem := range v.Children { + val = append(val, elem.Value.String()) + } + return "[" + strings.Join(val, ",") + "]" + case ObjectValue: + var val []string + for _, elem := range v.Children { + val = append(val, strconv.Quote(elem.Name)+":"+elem.Value.String()) + } + return "{" + strings.Join(val, ",") + "}" + default: + panic(fmt.Errorf("unknown value kind %d", v.Kind)) + } +} + +func (v *Value) Dump() string { + return v.String() +} diff --git a/vendor/github.com/vektah/gqlparser/gqlerror/error.go b/vendor/github.com/vektah/gqlparser/gqlerror/error.go new file mode 100644 index 00000000..c4c0847a --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/gqlerror/error.go @@ -0,0 +1,133 @@ +package gqlerror + +import ( + "bytes" + "fmt" + "strconv" + + "github.com/vektah/gqlparser/ast" +) + +// Error is the standard graphql error type described in https://facebook.github.io/graphql/draft/#sec-Errors +type Error struct { + Message string `json:"message"` + Path []interface{} `json:"path,omitempty"` + Locations []Location `json:"locations,omitempty"` + Extensions map[string]interface{} `json:"extensions,omitempty"` + Rule string `json:"-"` +} + +func (err *Error) SetFile(file string) { + if file == "" { + return + } + if err.Extensions == nil { + err.Extensions = map[string]interface{}{} + } + + err.Extensions["file"] = file +} + +type Location struct { + Line int `json:"line,omitempty"` + Column int `json:"column,omitempty"` +} + +type List []*Error + +func (err *Error) Error() string { + var res bytes.Buffer + if err == nil { + return "" + } + filename, _ := err.Extensions["file"].(string) + if filename == "" { + filename = "input" + } + res.WriteString(filename) + + if len(err.Locations) > 0 { + res.WriteByte(':') + res.WriteString(strconv.Itoa(err.Locations[0].Line)) + } + + res.WriteString(": ") + if ps := err.pathString(); ps != "" { + res.WriteString(ps) + res.WriteByte(' ') + } + + res.WriteString(err.Message) + + return res.String() +} + +func (err Error) pathString() string { + var str bytes.Buffer + for i, v := range err.Path { + + switch v := v.(type) { + case int, int64: + str.WriteString(fmt.Sprintf("[%d]", v)) + default: + if i != 0 { + str.WriteByte('.') + } + str.WriteString(fmt.Sprint(v)) + } + } + return str.String() +} + +func (errs List) Error() string { + var buf bytes.Buffer + for _, err := range errs { + buf.WriteString(err.Error()) + buf.WriteByte('\n') + } + return buf.String() +} + +func WrapPath(path []interface{}, err error) *Error { + return &Error{ + Message: err.Error(), + Path: path, + } +} + +func Errorf(message string, args ...interface{}) *Error { + return &Error{ + Message: fmt.Sprintf(message, args...), + } +} + +func ErrorPathf(path []interface{}, message string, args ...interface{}) *Error { + return &Error{ + Message: fmt.Sprintf(message, args...), + Path: path, + } +} + +func ErrorPosf(pos *ast.Position, message string, args ...interface{}) *Error { + return ErrorLocf( + pos.Src.Name, + pos.Line, + pos.Column, + message, + args..., + ) +} + +func ErrorLocf(file string, line int, col int, message string, args ...interface{}) *Error { + var extensions map[string]interface{} + if file != "" { + extensions = map[string]interface{}{"file": file} + } + return &Error{ + Message: fmt.Sprintf(message, args...), + Extensions: extensions, + Locations: []Location{ + {Line: line, Column: col}, + }, + } +} diff --git a/vendor/github.com/vektah/gqlparser/gqlparser.go b/vendor/github.com/vektah/gqlparser/gqlparser.go new file mode 100644 index 00000000..71e46407 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/gqlparser.go @@ -0,0 +1,42 @@ +package gqlparser + +import ( + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/parser" + "github.com/vektah/gqlparser/validator" + _ "github.com/vektah/gqlparser/validator/rules" +) + +func LoadSchema(str ...*ast.Source) (*ast.Schema, *gqlerror.Error) { + return validator.LoadSchema(append([]*ast.Source{validator.Prelude}, str...)...) +} + +func MustLoadSchema(str ...*ast.Source) *ast.Schema { + s, err := validator.LoadSchema(append([]*ast.Source{validator.Prelude}, str...)...) + if err != nil { + panic(err) + } + return s +} + +func LoadQuery(schema *ast.Schema, str string) (*ast.QueryDocument, gqlerror.List) { + query, err := parser.ParseQuery(&ast.Source{Input: str}) + if err != nil { + return nil, gqlerror.List{err} + } + errs := validator.Validate(schema, query) + if errs != nil { + return nil, errs + } + + return query, nil +} + +func MustLoadQuery(schema *ast.Schema, str string) *ast.QueryDocument { + q, err := LoadQuery(schema, str) + if err != nil { + panic(err) + } + return q +} diff --git a/vendor/github.com/vektah/gqlparser/lexer/blockstring.go b/vendor/github.com/vektah/gqlparser/lexer/blockstring.go new file mode 100644 index 00000000..4065a610 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/lexer/blockstring.go @@ -0,0 +1,58 @@ +package lexer + +import ( + "math" + "strings" +) + +// blockStringValue produces the value of a block string from its parsed raw value, similar to +// Coffeescript's block string, Python's docstring trim or Ruby's strip_heredoc. +// +// This implements the GraphQL spec's BlockStringValue() static algorithm. +func blockStringValue(raw string) string { + lines := strings.Split(raw, "\n") + + commonIndent := math.MaxInt32 + for _, line := range lines { + indent := leadingWhitespace(line) + if indent < len(line) && indent < commonIndent { + commonIndent = indent + if commonIndent == 0 { + break + } + } + } + + if commonIndent != math.MaxInt32 && len(lines) > 0 { + for i := 1; i < len(lines); i++ { + if len(lines[i]) < commonIndent { + lines[i] = "" + } else { + lines[i] = lines[i][commonIndent:] + } + } + } + + start := 0 + end := len(lines) + + for start < end && leadingWhitespace(lines[start]) == math.MaxInt32 { + start++ + } + + for start < end && leadingWhitespace(lines[end-1]) == math.MaxInt32 { + end-- + } + + return strings.Join(lines[start:end], "\n") +} + +func leadingWhitespace(str string) int { + for i, r := range str { + if r != ' ' && r != '\t' { + return i + } + } + // this line is made up entirely of whitespace, its leading whitespace doesnt count. + return math.MaxInt32 +} diff --git a/vendor/github.com/vektah/gqlparser/lexer/lexer.go b/vendor/github.com/vektah/gqlparser/lexer/lexer.go new file mode 100644 index 00000000..3aaa7102 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/lexer/lexer.go @@ -0,0 +1,510 @@ +package lexer + +import ( + "bytes" + "unicode/utf8" + + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" +) + +// Lexer turns graphql request and schema strings into tokens +type Lexer struct { + *ast.Source + // An offset into the string in bytes + start int + // An offset into the string in runes + startRunes int + // An offset into the string in bytes + end int + // An offset into the string in runes + endRunes int + // the current line number + line int + // An offset into the string in rune + lineStartRunes int +} + +func New(src *ast.Source) Lexer { + return Lexer{ + Source: src, + line: 1, + } +} + +// take one rune from input and advance end +func (s *Lexer) peek() (rune, int) { + return utf8.DecodeRuneInString(s.Input[s.end:]) +} + +func (s *Lexer) makeToken(kind Type) (Token, *gqlerror.Error) { + return s.makeValueToken(kind, s.Input[s.start:s.end]) +} + +func (s *Lexer) makeValueToken(kind Type, value string) (Token, *gqlerror.Error) { + return Token{ + Kind: kind, + Value: value, + Pos: ast.Position{ + Start: s.startRunes, + End: s.endRunes, + Line: s.line, + Column: s.startRunes - s.lineStartRunes + 1, + Src: s.Source, + }, + }, nil +} + +func (s *Lexer) makeError(format string, args ...interface{}) (Token, *gqlerror.Error) { + column := s.endRunes - s.lineStartRunes + 1 + return Token{ + Kind: Invalid, + Pos: ast.Position{ + Start: s.startRunes, + End: s.endRunes, + Line: s.line, + Column: column, + Src: s.Source, + }, + }, gqlerror.ErrorLocf(s.Source.Name, s.line, column, format, args...) +} + +// ReadToken gets the next token from the source starting at the given position. +// +// This skips over whitespace and comments until it finds the next lexable +// token, then lexes punctuators immediately or calls the appropriate helper +// function for more complicated tokens. +func (s *Lexer) ReadToken() (token Token, err *gqlerror.Error) { + + s.ws() + s.start = s.end + s.startRunes = s.endRunes + + if s.end >= len(s.Input) { + return s.makeToken(EOF) + } + r := s.Input[s.start] + s.end++ + s.endRunes++ + switch r { + case '!': + return s.makeValueToken(Bang, "") + + case '$': + return s.makeValueToken(Dollar, "") + case '&': + return s.makeValueToken(Amp, "") + case '(': + return s.makeValueToken(ParenL, "") + case ')': + return s.makeValueToken(ParenR, "") + case '.': + if len(s.Input) > s.start+2 && s.Input[s.start:s.start+3] == "..." { + s.end += 2 + s.endRunes += 2 + return s.makeValueToken(Spread, "") + } + case ':': + return s.makeValueToken(Colon, "") + case '=': + return s.makeValueToken(Equals, "") + case '@': + return s.makeValueToken(At, "") + case '[': + return s.makeValueToken(BracketL, "") + case ']': + return s.makeValueToken(BracketR, "") + case '{': + return s.makeValueToken(BraceL, "") + case '}': + return s.makeValueToken(BraceR, "") + case '|': + return s.makeValueToken(Pipe, "") + case '#': + s.readComment() + return s.ReadToken() + + case '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z': + return s.readName() + + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + return s.readNumber() + + case '"': + if len(s.Input) > s.start+2 && s.Input[s.start:s.start+3] == `"""` { + return s.readBlockString() + } + + return s.readString() + } + + s.end-- + s.endRunes-- + + if r < 0x0020 && r != 0x0009 && r != 0x000a && r != 0x000d { + return s.makeError(`Cannot contain the invalid character "\u%04d"`, r) + } + + if r == '\'' { + return s.makeError(`Unexpected single quote character ('), did you mean to use a double quote (")?`) + } + + return s.makeError(`Cannot parse the unexpected character "%s".`, string(r)) +} + +// ws reads from body starting at startPosition until it finds a non-whitespace +// or commented character, and updates the token end to include all whitespace +func (s *Lexer) ws() { + for s.end < len(s.Input) { + switch s.Input[s.end] { + case '\t', ' ', ',': + s.end++ + s.endRunes++ + case '\n': + s.end++ + s.endRunes++ + s.line++ + s.lineStartRunes = s.endRunes + case '\r': + s.end++ + s.endRunes++ + s.line++ + s.lineStartRunes = s.endRunes + // skip the following newline if its there + if s.end < len(s.Input) && s.Input[s.end] == '\n' { + s.end++ + s.endRunes++ + } + // byte order mark, given ws is hot path we aren't relying on the unicode package here. + case 0xef: + if s.end+2 < len(s.Input) && s.Input[s.end+1] == 0xBB && s.Input[s.end+2] == 0xBF { + s.end += 3 + s.endRunes++ + } else { + return + } + default: + return + } + } +} + +// readComment from the input +// +// #[\u0009\u0020-\uFFFF]* +func (s *Lexer) readComment() (Token, *gqlerror.Error) { + for s.end < len(s.Input) { + r, w := s.peek() + + // SourceCharacter but not LineTerminator + if r > 0x001f || r == '\t' { + s.end += w + s.endRunes++ + } else { + break + } + } + + return s.makeToken(Comment) +} + +// readNumber from the input, either a float +// or an int depending on whether a decimal point appears. +// +// Int: -?(0|[1-9][0-9]*) +// Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)? +func (s *Lexer) readNumber() (Token, *gqlerror.Error) { + float := false + + // backup to the first digit + s.end-- + s.endRunes-- + + s.acceptByte('-') + + if s.acceptByte('0') { + if consumed := s.acceptDigits(); consumed != 0 { + s.end -= consumed + s.endRunes -= consumed + return s.makeError("Invalid number, unexpected digit after 0: %s.", s.describeNext()) + } + } else { + if consumed := s.acceptDigits(); consumed == 0 { + return s.makeError("Invalid number, expected digit but got: %s.", s.describeNext()) + } + } + + if s.acceptByte('.') { + float = true + + if consumed := s.acceptDigits(); consumed == 0 { + return s.makeError("Invalid number, expected digit but got: %s.", s.describeNext()) + } + } + + if s.acceptByte('e', 'E') { + float = true + + s.acceptByte('-', '+') + + if consumed := s.acceptDigits(); consumed == 0 { + return s.makeError("Invalid number, expected digit but got: %s.", s.describeNext()) + } + } + + if float { + return s.makeToken(Float) + } else { + return s.makeToken(Int) + } +} + +// acceptByte if it matches any of given bytes, returning true if it found anything +func (s *Lexer) acceptByte(bytes ...uint8) bool { + if s.end >= len(s.Input) { + return false + } + + for _, accepted := range bytes { + if s.Input[s.end] == accepted { + s.end++ + s.endRunes++ + return true + } + } + return false +} + +// acceptDigits from the input, returning the number of digits it found +func (s *Lexer) acceptDigits() int { + consumed := 0 + for s.end < len(s.Input) && s.Input[s.end] >= '0' && s.Input[s.end] <= '9' { + s.end++ + s.endRunes++ + consumed++ + } + + return consumed +} + +// describeNext peeks at the input and returns a human readable string. This should will alloc +// and should only be used in errors +func (s *Lexer) describeNext() string { + if s.end < len(s.Input) { + return `"` + string(s.Input[s.end]) + `"` + } + return "<EOF>" +} + +// readString from the input +// +// "([^"\\\u000A\u000D]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*" +func (s *Lexer) readString() (Token, *gqlerror.Error) { + inputLen := len(s.Input) + + // this buffer is lazily created only if there are escape characters. + var buf *bytes.Buffer + + // skip the opening quote + s.start++ + s.startRunes++ + + for s.end < inputLen { + r := s.Input[s.end] + if r == '\n' || r == '\r' { + break + } + if r < 0x0020 && r != '\t' { + return s.makeError(`Invalid character within String: "\u%04d".`, r) + } + switch r { + default: + var char = rune(r) + var w = 1 + + // skip unicode overhead if we are in the ascii range + if r >= 127 { + char, w = utf8.DecodeRuneInString(s.Input[s.end:]) + } + s.end += w + s.endRunes++ + + if buf != nil { + buf.WriteRune(char) + } + + case '"': + t, err := s.makeToken(String) + // the token should not include the quotes in its value, but should cover them in its position + t.Pos.Start-- + t.Pos.End++ + + if buf != nil { + t.Value = buf.String() + } + + // skip the close quote + s.end++ + s.endRunes++ + + return t, err + + case '\\': + if s.end+1 >= inputLen { + s.end++ + s.endRunes++ + return s.makeError(`Invalid character escape sequence.`) + } + + if buf == nil { + buf = bytes.NewBufferString(s.Input[s.start:s.end]) + } + + escape := s.Input[s.end+1] + + if escape == 'u' { + if s.end+6 >= inputLen { + s.end++ + s.endRunes++ + return s.makeError("Invalid character escape sequence: \\%s.", s.Input[s.end:]) + } + + r, ok := unhex(s.Input[s.end+2 : s.end+6]) + if !ok { + s.end++ + s.endRunes++ + return s.makeError("Invalid character escape sequence: \\%s.", s.Input[s.end:s.end+5]) + } + buf.WriteRune(r) + s.end += 6 + s.endRunes += 6 + } else { + switch escape { + case '"', '/', '\\': + buf.WriteByte(escape) + case 'b': + buf.WriteByte('\b') + case 'f': + buf.WriteByte('\f') + case 'n': + buf.WriteByte('\n') + case 'r': + buf.WriteByte('\r') + case 't': + buf.WriteByte('\t') + default: + s.end += 1 + s.endRunes += 1 + return s.makeError("Invalid character escape sequence: \\%s.", string(escape)) + } + s.end += 2 + s.endRunes += 2 + } + } + } + + return s.makeError("Unterminated string.") +} + +// readBlockString from the input +// +// """("?"?(\\"""|\\(?!=""")|[^"\\]))*""" +func (s *Lexer) readBlockString() (Token, *gqlerror.Error) { + inputLen := len(s.Input) + + var buf bytes.Buffer + + // skip the opening quote + s.start += 3 + s.startRunes += 3 + s.end += 2 + s.endRunes += 2 + + for s.end < inputLen { + r := s.Input[s.end] + + // Closing triple quote (""") + if r == '"' && s.end+3 <= inputLen && s.Input[s.end:s.end+3] == `"""` { + t, err := s.makeValueToken(BlockString, blockStringValue(buf.String())) + + // the token should not include the quotes in its value, but should cover them in its position + t.Pos.Start -= 3 + t.Pos.End += 3 + + // skip the close quote + s.end += 3 + s.endRunes += 3 + + return t, err + } + + // SourceCharacter + if r < 0x0020 && r != '\t' && r != '\n' && r != '\r' { + return s.makeError(`Invalid character within String: "\u%04d".`, r) + } + + if r == '\\' && s.end+4 <= inputLen && s.Input[s.end:s.end+4] == `\"""` { + buf.WriteString(`"""`) + s.end += 4 + s.endRunes += 4 + } else if r == '\r' { + if s.end+1 <= inputLen && s.Input[s.end+1] == '\n' { + s.end++ + s.endRunes++ + } + + buf.WriteByte('\n') + s.end++ + s.endRunes++ + } else { + var char = rune(r) + var w = 1 + + // skip unicode overhead if we are in the ascii range + if r >= 127 { + char, w = utf8.DecodeRuneInString(s.Input[s.end:]) + } + s.end += w + s.endRunes++ + buf.WriteRune(char) + } + } + + return s.makeError("Unterminated string.") +} + +func unhex(b string) (v rune, ok bool) { + for _, c := range b { + v <<= 4 + switch { + case '0' <= c && c <= '9': + v |= c - '0' + case 'a' <= c && c <= 'f': + v |= c - 'a' + 10 + case 'A' <= c && c <= 'F': + v |= c - 'A' + 10 + default: + return 0, false + } + } + + return v, true +} + +// readName from the input +// +// [_A-Za-z][_0-9A-Za-z]* +func (s *Lexer) readName() (Token, *gqlerror.Error) { + for s.end < len(s.Input) { + r, w := s.peek() + + if (r >= '0' && r <= '9') || (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') || r == '_' { + s.end += w + s.endRunes++ + } else { + break + } + } + + return s.makeToken(Name) +} diff --git a/vendor/github.com/vektah/gqlparser/lexer/lexer_test.yml b/vendor/github.com/vektah/gqlparser/lexer/lexer_test.yml new file mode 100644 index 00000000..e2c26696 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/lexer/lexer_test.yml @@ -0,0 +1,672 @@ +encoding: + - name: disallows uncommon control characters + input: "\u0007" + error: + message: 'Cannot contain the invalid character "\u0007"' + locations: [{line: 1, column: 1}] + + - name: accepts BOM header + input: "\uFEFF foo" + tokens: + - + kind: NAME + start: 2 + end: 5 + value: 'foo' + +simple tokens: + - name: records line and column + input: "\n \r\n \r foo\n" + tokens: + - + kind: NAME + start: 8 + end: 11 + line: 4 + column: 3 + value: 'foo' + + - name: skips whitespace + input: "\n\n foo\n\n\n" + tokens: + - + kind: NAME + start: 6 + end: 9 + value: 'foo' + + - name: skips comments + input: "\n #comment\n foo#comment\n" + tokens: + - + kind: NAME + start: 18 + end: 21 + value: 'foo' + + - name: skips commas + input: ",,,foo,,," + tokens: + - + kind: NAME + start: 3 + end: 6 + value: 'foo' + + - name: errors respect whitespace + input: "\n\n ?\n\n\n" + error: + message: 'Cannot parse the unexpected character "?".' + locations: [{line: 3, column: 5}] + string: | + Syntax Error: Cannot parse the unexpected character "?". + GraphQL request (3:5) + 2: + 3: ? + ^ + 4: + + - name: lex reports useful information for dashes in names + input: "a-b" + error: + message: 'Invalid number, expected digit but got: "b".' + locations: [{ line: 1, column: 3 }] + tokens: + - + kind: Name + start: 0 + end: 1 + value: a + +lexes strings: + - name: basic + input: '"simple"' + tokens: + - + kind: STRING + start: 0 + end: 8 + value: 'simple' + + - name: whitespace + input: '" white space "' + tokens: + - + kind: STRING + start: 0 + end: 15 + value: ' white space ' + + - name: quote + input: '"quote \""' + tokens: + - + kind: STRING + start: 0 + end: 10 + value: 'quote "' + + - name: escaped + input: '"escaped \n\r\b\t\f"' + tokens: + - + kind: STRING + start: 0 + end: 20 + value: "escaped \n\r\b\t\f" + + - name: slashes + input: '"slashes \\ \/"' + tokens: + - + kind: STRING + start: 0 + end: 15 + value: 'slashes \ /' + + - name: unicode + input: '"unicode \u1234\u5678\u90AB\uCDEF"' + tokens: + - + kind: STRING + start: 0 + end: 34 + value: "unicode \u1234\u5678\u90AB\uCDEF" + +lex reports useful string errors: + - name: unterminated + input: '"' + error: + message: "Unterminated string." + locations: [{ line: 1, column: 2 }] + + - name: no end quote + input: '"no end quote' + error: + message: 'Unterminated string.' + locations: [{ line: 1, column: 14 }] + + - name: single quotes + input: "'single quotes'" + error: + message: "Unexpected single quote character ('), did you mean to use a double quote (\")?" + locations: [{ line: 1, column: 1 }] + + - name: control characters + input: "\"contains unescaped \u0007 control char\"" + error: + message: 'Invalid character within String: "\u0007".' + locations: [{ line: 1, column: 21 }] + + - name: null byte + input: "\"null-byte is not \u0000 end of file\"" + error: + message: 'Invalid character within String: "\u0000".' + locations: [{ line: 1, column: 19 }] + + - name: unterminated newline + input: "\"multi\nline\"" + error: + message: 'Unterminated string.' + locations: [{line: 1, column: 7 }] + + - name: unterminated carriage return + input: "\"multi\rline\"" + error: + message: 'Unterminated string.' + locations: [{ line: 1, column: 7 }] + + - name: bad escape character + input: '"bad \z esc"' + error: + message: 'Invalid character escape sequence: \z.' + locations: [{ line: 1, column: 7 }] + + - name: hex escape sequence + input: '"bad \x esc"' + error: + message: 'Invalid character escape sequence: \x.' + locations: [{ line: 1, column: 7 }] + + - name: short escape sequence + input: '"bad \u1 esc"' + error: + message: 'Invalid character escape sequence: \u1 es.' + locations: [{ line: 1, column: 7 }] + + - name: invalid escape sequence 1 + input: '"bad \u0XX1 esc"' + error: + message: 'Invalid character escape sequence: \u0XX1.' + locations: [{ line: 1, column: 7 }] + + - name: invalid escape sequence 2 + input: '"bad \uXXXX esc"' + error: + message: 'Invalid character escape sequence: \uXXXX.' + locations: [{ line: 1, column: 7 }] + + - name: invalid escape sequence 3 + input: '"bad \uFXXX esc"' + error: + message: 'Invalid character escape sequence: \uFXXX.' + locations: [{ line: 1, column: 7 }] + + - name: invalid character escape sequence + input: '"bad \uXXXF esc"' + error: + message: 'Invalid character escape sequence: \uXXXF.' + locations: [{ line: 1, column: 7 }] + +lexes block strings: + - name: simple + input: '"""simple"""' + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 12 + value: 'simple' + + - name: white space + input: '""" white space """' + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 19 + value: ' white space ' + + - name: contains quote + input: '"""contains " quote"""' + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 22 + value: 'contains " quote' + + - name: contains triplequote + input: "\"\"\"contains \\\"\"\" triplequote\"\"\"" + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 31 + value: 'contains """ triplequote' + + - name: multi line + input: "\"\"\"multi\nline\"\"\"" + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 16 + value: "multi\nline" + + - name: multi line normalized + input: "\"\"\"multi\rline\r\nnormalized\"\"\"" + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 28 + value: "multi\nline\nnormalized" + + - name: unescaped + input: '"""unescaped \n\r\b\t\f\u1234"""' + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 32 + value: 'unescaped \n\r\b\t\f\u1234' + + - name: slashes + input: '"""slashes \\ \/"""' + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 19 + value: 'slashes \\ \/' + + - name: multiple lines + input: | + """ + + spans + multiple + lines + + """ + tokens: + - + kind: BLOCK_STRING + start: 0 + end: 36 + value: "spans\n multiple\n lines" + +lex reports useful block string errors: + - name: unterminated string + input: '"""' + error: + message: "Unterminated string." + locations: [{ line: 1, column: 4 }] + + - name: unescaped control characters + input: "\"\"\"contains unescaped \u0007 control char\"\"\"" + error: + message: 'Invalid character within String: "\u0007".' + locations: [{ line: 1, column: 23 }] + + - name: null byte + input: "\"\"\"null-byte is not \u0000 end of file\"\"\"" + error: + message: 'Invalid character within String: "\u0000".' + locations: [{ line: 1, column: 21 }] + +lexes numbers: + - name: integer + input: "4" + tokens: + - + kind: INT + start: 0 + end: 1 + value: '4' + + - name: float + input: "4.123" + tokens: + - + kind: FLOAT + start: 0 + end: 5 + value: '4.123' + + - name: negative + input: "-4" + tokens: + - + kind: INT + start: 0 + end: 2 + value: '-4' + + - name: nine + input: "9" + tokens: + - + kind: INT + start: 0 + end: 1 + value: '9' + + - name: zero + input: "0" + tokens: + - + kind: INT + start: 0 + end: 1 + value: '0' + + - name: negative float + input: "-4.123" + tokens: + - + kind: FLOAT + start: 0 + end: 6 + value: '-4.123' + + - name: float leading zero + input: "0.123" + tokens: + - + kind: FLOAT + start: 0 + end: 5 + value: '0.123' + + - name: exponent whole + input: "123e4" + tokens: + - + kind: FLOAT + start: 0 + end: 5 + value: '123e4' + + - name: exponent uppercase + input: "123E4" + tokens: + - + kind: FLOAT + start: 0 + end: 5 + value: '123E4' + + - name: exponent negative power + input: "123e-4" + tokens: + - + kind: FLOAT + start: 0 + end: 6 + value: '123e-4' + + - name: exponent positive power + input: "123e+4" + tokens: + - + kind: FLOAT + start: 0 + end: 6 + value: '123e+4' + + - name: exponent negative base + input: "-1.123e4" + tokens: + - + kind: FLOAT + start: 0 + end: 8 + value: '-1.123e4' + + - name: exponent negative base upper + input: "-1.123E4" + tokens: + - + kind: FLOAT + start: 0 + end: 8 + value: '-1.123E4' + + - name: exponent negative base negative power + input: "-1.123e-4" + tokens: + - + kind: FLOAT + start: 0 + end: 9 + value: '-1.123e-4' + + - name: exponent negative base positive power + input: "-1.123e+4" + tokens: + - + kind: FLOAT + start: 0 + end: 9 + value: '-1.123e+4' + + - name: exponent negative base large power + input: "-1.123e4567" + tokens: + - + kind: FLOAT + start: 0 + end: 11 + value: '-1.123e4567' + +lex reports useful number errors: + - name: zero + input: "00" + error: + message: 'Invalid number, unexpected digit after 0: "0".' + locations: [{ line: 1, column: 2 }] + + - name: positive + input: "+1" + error: + message: 'Cannot parse the unexpected character "+".' + locations: [{ line: 1, column: 1 }] + + - name: trailing dot + input: "1." + error: + message: 'Invalid number, expected digit but got: <EOF>.' + locations: [{ line: 1, column: 3 }] + + - name: traililng dot exponent + input: "1.e1" + error: + message: 'Invalid number, expected digit but got: "e".' + locations: [{ line: 1, column: 3 }] + + - name: missing leading zero + input: ".123" + error: + message: 'Cannot parse the unexpected character ".".' + locations: [{ line: 1, column: 1 }] + + - name: characters + input: "1.A" + error: + message: 'Invalid number, expected digit but got: "A".' + locations: [{ line: 1, column: 3 }] + + - name: negative characters + input: "-A" + error: + message: 'Invalid number, expected digit but got: "A".' + locations: [{ line: 1, column: 2 }] + + - name: missing exponent + input: '1.0e' + error: + message: 'Invalid number, expected digit but got: <EOF>.' + locations: [{ line: 1, column: 5 }] + + - name: character exponent + input: "1.0eA" + error: + message: 'Invalid number, expected digit but got: "A".' + locations: [{ line: 1, column: 5 }] + +lexes punctuation: + - name: bang + input: "!" + tokens: + - + kind: BANG + start: 0 + end: 1 + value: undefined + + - name: dollar + input: "$" + tokens: + - + kind: DOLLAR + start: 0 + end: 1 + value: undefined + + - name: open paren + input: "(" + tokens: + - + kind: PAREN_L + start: 0 + end: 1 + value: undefined + + - name: close paren + input: ")" + tokens: + - + kind: PAREN_R + start: 0 + end: 1 + value: undefined + + - name: spread + input: "..." + tokens: + - + kind: SPREAD + start: 0 + end: 3 + value: undefined + + - name: colon + input: ":" + tokens: + - + kind: COLON + start: 0 + end: 1 + value: undefined + + - name: equals + input: "=" + tokens: + - + kind: EQUALS + start: 0 + end: 1 + value: undefined + + - name: at + input: "@" + tokens: + - + kind: AT + start: 0 + end: 1 + value: undefined + + - name: open bracket + input: "[" + tokens: + - + kind: BRACKET_L + start: 0 + end: 1 + value: undefined + + - name: close bracket + input: "]" + tokens: + - + kind: BRACKET_R + start: 0 + end: 1 + value: undefined + + - name: open brace + input: "{" + tokens: + - + kind: BRACE_L + start: 0 + end: 1 + value: undefined + + - name: close brace + input: "}" + tokens: + - + kind: BRACE_R + start: 0 + end: 1 + value: undefined + + - name: pipe + input: "|" + tokens: + - + kind: PIPE + start: 0 + end: 1 + value: undefined + +lex reports useful unknown character error: + - name: not a spread + input: ".." + error: + message: 'Cannot parse the unexpected character ".".' + locations: [{ line: 1, column: 1 }] + + - name: question mark + input: "?" + error: + message: 'Cannot parse the unexpected character "?".' + message: 'Cannot parse the unexpected character "?".' + locations: [{ line: 1, column: 1 }] + + - name: unicode 203 + input: "\u203B" + error: + message: 'Cannot parse the unexpected character "â".' + locations: [{ line: 1, column: 1 }] + + - name: unicode 200 + input: "\u200b" + error: + message: 'Cannot parse the unexpected character "â".' + locations: [{ line: 1, column: 1 }] + diff --git a/vendor/github.com/vektah/gqlparser/lexer/token.go b/vendor/github.com/vektah/gqlparser/lexer/token.go new file mode 100644 index 00000000..aef8b729 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/lexer/token.go @@ -0,0 +1,148 @@ +package lexer + +import ( + "strconv" + + "github.com/vektah/gqlparser/ast" +) + +const ( + Invalid Type = iota + EOF + Bang + Dollar + Amp + ParenL + ParenR + Spread + Colon + Equals + At + BracketL + BracketR + BraceL + BraceR + Pipe + Name + Int + Float + String + BlockString + Comment +) + +func (t Type) Name() string { + switch t { + case Invalid: + return "Invalid" + case EOF: + return "EOF" + case Bang: + return "Bang" + case Dollar: + return "Dollar" + case Amp: + return "Amp" + case ParenL: + return "ParenL" + case ParenR: + return "ParenR" + case Spread: + return "Spread" + case Colon: + return "Colon" + case Equals: + return "Equals" + case At: + return "At" + case BracketL: + return "BracketL" + case BracketR: + return "BracketR" + case BraceL: + return "BraceL" + case BraceR: + return "BraceR" + case Pipe: + return "Pipe" + case Name: + return "Name" + case Int: + return "Int" + case Float: + return "Float" + case String: + return "String" + case BlockString: + return "BlockString" + case Comment: + return "Comment" + } + return "Unknown " + strconv.Itoa(int(t)) +} + +func (t Type) String() string { + switch t { + case Invalid: + return "<Invalid>" + case EOF: + return "<EOF>" + case Bang: + return "!" + case Dollar: + return "$" + case Amp: + return "&" + case ParenL: + return "(" + case ParenR: + return ")" + case Spread: + return "..." + case Colon: + return ":" + case Equals: + return "=" + case At: + return "@" + case BracketL: + return "[" + case BracketR: + return "]" + case BraceL: + return "{" + case BraceR: + return "}" + case Pipe: + return "|" + case Name: + return "Name" + case Int: + return "Int" + case Float: + return "Float" + case String: + return "String" + case BlockString: + return "BlockString" + case Comment: + return "Comment" + } + return "Unknown " + strconv.Itoa(int(t)) +} + +// Kind represents a type of token. The types are predefined as constants. +type Type int + +type Token struct { + Kind Type // The token type. + Value string // The literal value consumed. + Pos ast.Position // The file and line this token was read from +} + +func (t Token) String() string { + if t.Value != "" { + return t.Kind.String() + " " + strconv.Quote(t.Value) + } + return t.Kind.String() +} diff --git a/vendor/github.com/vektah/gqlparser/parser/parser.go b/vendor/github.com/vektah/gqlparser/parser/parser.go new file mode 100644 index 00000000..f3648cb3 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/parser/parser.go @@ -0,0 +1,112 @@ +package parser + +import ( + "strconv" + + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/lexer" +) + +type parser struct { + lexer lexer.Lexer + err *gqlerror.Error + + peeked bool + peekToken lexer.Token + peekError *gqlerror.Error + + prev lexer.Token +} + +func (p *parser) peekPos() *ast.Position { + if p.err != nil { + return nil + } + + peek := p.peek() + return &peek.Pos +} + +func (p *parser) peek() lexer.Token { + if p.err != nil { + return p.prev + } + + if !p.peeked { + p.peekToken, p.peekError = p.lexer.ReadToken() + p.peeked = true + } + + return p.peekToken +} + +func (p *parser) error(tok lexer.Token, format string, args ...interface{}) { + if p.err != nil { + return + } + p.err = gqlerror.ErrorLocf(tok.Pos.Src.Name, tok.Pos.Line, tok.Pos.Column, format, args...) +} + +func (p *parser) next() lexer.Token { + if p.err != nil { + return p.prev + } + if p.peeked { + p.peeked = false + p.prev, p.err = p.peekToken, p.peekError + } else { + p.prev, p.err = p.lexer.ReadToken() + } + return p.prev +} + +func (p *parser) expectKeyword(value string) lexer.Token { + tok := p.peek() + if tok.Kind == lexer.Name && tok.Value == value { + return p.next() + } + + p.error(tok, "Expected %s, found %s", strconv.Quote(value), tok.String()) + return tok +} + +func (p *parser) expect(kind lexer.Type) lexer.Token { + tok := p.peek() + if tok.Kind == kind { + return p.next() + } + + p.error(tok, "Expected %s, found %s", kind, tok.Kind.String()) + return tok +} + +func (p *parser) skip(kind lexer.Type) bool { + tok := p.peek() + + if tok.Kind != kind { + return false + } + p.next() + return true +} + +func (p *parser) unexpectedError() { + p.unexpectedToken(p.peek()) +} + +func (p *parser) unexpectedToken(tok lexer.Token) { + p.error(tok, "Unexpected %s", tok.String()) +} + +func (p *parser) many(start lexer.Type, end lexer.Type, cb func()) { + hasDef := p.skip(start) + if !hasDef { + return + } + + for p.peek().Kind != end && p.err == nil { + cb() + } + p.next() +} diff --git a/vendor/github.com/vektah/gqlparser/parser/query.go b/vendor/github.com/vektah/gqlparser/parser/query.go new file mode 100644 index 00000000..7fecb57f --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/parser/query.go @@ -0,0 +1,334 @@ +package parser + +import ( + "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/lexer" + + . "github.com/vektah/gqlparser/ast" +) + +func ParseQuery(source *Source) (*QueryDocument, *gqlerror.Error) { + p := parser{ + lexer: lexer.New(source), + } + return p.parseQueryDocument(), p.err +} + +func (p *parser) parseQueryDocument() *QueryDocument { + var doc QueryDocument + for p.peek().Kind != lexer.EOF { + if p.err != nil { + return &doc + } + doc.Position = p.peekPos() + switch p.peek().Kind { + case lexer.Name: + switch p.peek().Value { + case "query", "mutation", "subscription": + doc.Operations = append(doc.Operations, p.parseOperationDefinition()) + case "fragment": + doc.Fragments = append(doc.Fragments, p.parseFragmentDefinition()) + default: + p.unexpectedError() + } + case lexer.BraceL: + doc.Operations = append(doc.Operations, p.parseOperationDefinition()) + default: + p.unexpectedError() + } + } + + return &doc +} + +func (p *parser) parseOperationDefinition() *OperationDefinition { + if p.peek().Kind == lexer.BraceL { + return &OperationDefinition{ + Position: p.peekPos(), + Operation: Query, + SelectionSet: p.parseSelectionSet(), + } + } + + var od OperationDefinition + od.Position = p.peekPos() + od.Operation = p.parseOperationType() + + if p.peek().Kind == lexer.Name { + od.Name = p.next().Value + } + + od.VariableDefinitions = p.parseVariableDefinitions() + od.Directives = p.parseDirectives(false) + od.SelectionSet = p.parseSelectionSet() + + return &od +} + +func (p *parser) parseOperationType() Operation { + tok := p.next() + switch tok.Value { + case "query": + return Query + case "mutation": + return Mutation + case "subscription": + return Subscription + } + p.unexpectedToken(tok) + return "" +} + +func (p *parser) parseVariableDefinitions() VariableDefinitionList { + var defs []*VariableDefinition + p.many(lexer.ParenL, lexer.ParenR, func() { + defs = append(defs, p.parseVariableDefinition()) + }) + + return defs +} + +func (p *parser) parseVariableDefinition() *VariableDefinition { + var def VariableDefinition + def.Position = p.peekPos() + def.Variable = p.parseVariable() + + p.expect(lexer.Colon) + + def.Type = p.parseTypeReference() + + if p.skip(lexer.Equals) { + def.DefaultValue = p.parseValueLiteral(true) + } + + return &def +} + +func (p *parser) parseVariable() string { + p.expect(lexer.Dollar) + return p.parseName() +} + +func (p *parser) parseSelectionSet() SelectionSet { + var selections []Selection + p.many(lexer.BraceL, lexer.BraceR, func() { + selections = append(selections, p.parseSelection()) + }) + + return SelectionSet(selections) +} + +func (p *parser) parseSelection() Selection { + if p.peek().Kind == lexer.Spread { + return p.parseFragment() + } + return p.parseField() +} + +func (p *parser) parseField() *Field { + var field Field + field.Position = p.peekPos() + field.Alias = p.parseName() + + if p.skip(lexer.Colon) { + field.Name = p.parseName() + } else { + field.Name = field.Alias + } + + field.Arguments = p.parseArguments(false) + field.Directives = p.parseDirectives(false) + if p.peek().Kind == lexer.BraceL { + field.SelectionSet = p.parseSelectionSet() + } + + return &field +} + +func (p *parser) parseArguments(isConst bool) ArgumentList { + var arguments ArgumentList + p.many(lexer.ParenL, lexer.ParenR, func() { + arguments = append(arguments, p.parseArgument(isConst)) + }) + + return arguments +} + +func (p *parser) parseArgument(isConst bool) *Argument { + arg := Argument{} + arg.Position = p.peekPos() + arg.Name = p.parseName() + p.expect(lexer.Colon) + + arg.Value = p.parseValueLiteral(isConst) + return &arg +} + +func (p *parser) parseFragment() Selection { + p.expect(lexer.Spread) + + if peek := p.peek(); peek.Kind == lexer.Name && peek.Value != "on" { + return &FragmentSpread{ + Position: p.peekPos(), + Name: p.parseFragmentName(), + Directives: p.parseDirectives(false), + } + } + + var def InlineFragment + def.Position = p.peekPos() + if p.peek().Value == "on" { + p.next() // "on" + + def.TypeCondition = p.parseName() + } + + def.Directives = p.parseDirectives(false) + def.SelectionSet = p.parseSelectionSet() + return &def +} + +func (p *parser) parseFragmentDefinition() *FragmentDefinition { + var def FragmentDefinition + def.Position = p.peekPos() + p.expectKeyword("fragment") + + def.Name = p.parseFragmentName() + def.VariableDefinition = p.parseVariableDefinitions() + + p.expectKeyword("on") + + def.TypeCondition = p.parseName() + def.Directives = p.parseDirectives(false) + def.SelectionSet = p.parseSelectionSet() + return &def +} + +func (p *parser) parseFragmentName() string { + if p.peek().Value == "on" { + p.unexpectedError() + return "" + } + + return p.parseName() +} + +func (p *parser) parseValueLiteral(isConst bool) *Value { + token := p.peek() + + var kind ValueKind + switch token.Kind { + case lexer.BracketL: + return p.parseList(isConst) + case lexer.BraceL: + return p.parseObject(isConst) + case lexer.Dollar: + if isConst { + p.unexpectedError() + return nil + } + return &Value{Position: &token.Pos, Raw: p.parseVariable(), Kind: Variable} + case lexer.Int: + kind = IntValue + case lexer.Float: + kind = FloatValue + case lexer.String: + kind = StringValue + case lexer.BlockString: + kind = BlockValue + case lexer.Name: + switch token.Value { + case "true", "false": + kind = BooleanValue + case "null": + kind = NullValue + default: + kind = EnumValue + } + default: + p.unexpectedError() + return nil + } + + p.next() + + return &Value{Position: &token.Pos, Raw: token.Value, Kind: kind} +} + +func (p *parser) parseList(isConst bool) *Value { + var values ChildValueList + pos := p.peekPos() + p.many(lexer.BracketL, lexer.BracketR, func() { + values = append(values, &ChildValue{Value: p.parseValueLiteral(isConst)}) + }) + + return &Value{Children: values, Kind: ListValue, Position: pos} +} + +func (p *parser) parseObject(isConst bool) *Value { + var fields ChildValueList + pos := p.peekPos() + p.many(lexer.BraceL, lexer.BraceR, func() { + fields = append(fields, p.parseObjectField(isConst)) + }) + + return &Value{Children: fields, Kind: ObjectValue, Position: pos} +} + +func (p *parser) parseObjectField(isConst bool) *ChildValue { + field := ChildValue{} + field.Position = p.peekPos() + field.Name = p.parseName() + + p.expect(lexer.Colon) + + field.Value = p.parseValueLiteral(isConst) + return &field +} + +func (p *parser) parseDirectives(isConst bool) []*Directive { + var directives []*Directive + + for p.peek().Kind == lexer.At { + if p.err != nil { + break + } + directives = append(directives, p.parseDirective(isConst)) + } + return directives +} + +func (p *parser) parseDirective(isConst bool) *Directive { + p.expect(lexer.At) + + return &Directive{ + Position: p.peekPos(), + Name: p.parseName(), + Arguments: p.parseArguments(isConst), + } +} + +func (p *parser) parseTypeReference() *Type { + var typ Type + + if p.skip(lexer.BracketL) { + typ.Position = p.peekPos() + typ.Elem = p.parseTypeReference() + p.expect(lexer.BracketR) + } else { + typ.Position = p.peekPos() + typ.NamedType = p.parseName() + } + + if p.skip(lexer.Bang) { + typ.Position = p.peekPos() + typ.NonNull = true + } + return &typ +} + +func (p *parser) parseName() string { + token := p.expect(lexer.Name) + + return token.Value +} diff --git a/vendor/github.com/vektah/gqlparser/parser/query_test.yml b/vendor/github.com/vektah/gqlparser/parser/query_test.yml new file mode 100644 index 00000000..f392eb8e --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/parser/query_test.yml @@ -0,0 +1,507 @@ +parser provides useful errors: + - name: unclosed paren + input: '{' + error: + message: "Expected Name, found <EOF>" + locations: [{line: 1, column: 2}] + + - name: missing on in fragment + input: | + { ...MissingOn } + fragment MissingOn Type + error: + message: 'Expected "on", found Name "Type"' + locations: [{ line: 2, column: 20 }] + + - name: missing name after alias + input: '{ field: {} }' + error: + message: "Expected Name, found {" + locations: [{ line: 1, column: 10 }] + + - name: not an operation + input: 'notanoperation Foo { field }' + error: + message: 'Unexpected Name "notanoperation"' + locations: [{ line: 1, column: 1 }] + + - name: a wild splat appears + input: '...' + error: + message: 'Unexpected ...' + locations: [{ line: 1, column: 1}] + +variables: + - name: are allowed in args + input: '{ field(complex: { a: { b: [ $var ] } }) }' + + - name: are not allowed in default args + input: 'query Foo($x: Complex = { a: { b: [ $var ] } }) { field }' + error: + message: 'Unexpected $' + locations: [{ line: 1, column: 37 }] + +fragments: + - name: can not be named 'on' + input: 'fragment on on on { on }' + error: + message: 'Unexpected Name "on"' + locations: [{ line: 1, column: 10 }] + + - name: can not spread fragments called 'on' + input: '{ ...on }' + error: + message: 'Expected Name, found }' + locations: [{ line: 1, column: 9 }] + +encoding: + - name: multibyte characters are supported + input: | + # This comment has a ਊ multi-byte character. + { field(arg: "Has a ਊ multi-byte character.") } + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + SelectionSet: [Selection] + - <Field> + Alias: "field" + Name: "field" + Arguments: [Argument] + - <Argument> + Name: "arg" + Value: "Has a ਊ multi-byte character." + +keywords are allowed anywhere a name is: + - name: on + input: | + query on { + ... a + ... on on { field } + } + fragment a on Type { + on(on: $on) + @on(on: on) + } + + - name: subscription + input: | + query subscription { + ... subscription + ... on subscription { field } + } + fragment subscription on Type { + subscription(subscription: $subscription) + @subscription(subscription: subscription) + } + + - name: true + input: | + query true { + ... true + ... on true { field } + } + fragment true on Type { + true(true: $true) + @true(true: true) + } + +operations: + - name: anonymous mutation + input: 'mutation { mutationField }' + + - name: named mutation + input: 'mutation Foo { mutationField }' + + - name: anonymous subscription + input: 'subscription { subscriptionField }' + + - name: named subscription + input: 'subscription Foo { subscriptionField }' + + +ast: + - name: simple query + input: | + { + node(id: 4) { + id, + name + } + } + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + SelectionSet: [Selection] + - <Field> + Alias: "node" + Name: "node" + Arguments: [Argument] + - <Argument> + Name: "id" + Value: 4 + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + - <Field> + Alias: "name" + Name: "name" + + - name: nameless query with no variables + input: | + query { + node { + id + } + } + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + SelectionSet: [Selection] + - <Field> + Alias: "node" + Name: "node" + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + + - name: fragment defined variables + input: 'fragment a($v: Boolean = false) on t { f(v: $v) }' + ast: | + <QueryDocument> + Fragments: [FragmentDefinition] + - <FragmentDefinition> + Name: "a" + VariableDefinition: [VariableDefinition] + - <VariableDefinition> + Variable: "v" + Type: Boolean + DefaultValue: false + TypeCondition: "t" + SelectionSet: [Selection] + - <Field> + Alias: "f" + Name: "f" + Arguments: [Argument] + - <Argument> + Name: "v" + Value: $v + + +values: + - name: null + input: '{ f(id: null) }' + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + SelectionSet: [Selection] + - <Field> + Alias: "f" + Name: "f" + Arguments: [Argument] + - <Argument> + Name: "id" + Value: null + + - name: strings + input: '{ f(long: """long""", short: "short") } ' + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + SelectionSet: [Selection] + - <Field> + Alias: "f" + Name: "f" + Arguments: [Argument] + - <Argument> + Name: "long" + Value: "long" + - <Argument> + Name: "short" + Value: "short" + + - name: list + input: '{ f(id: [1,2]) }' + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + SelectionSet: [Selection] + - <Field> + Alias: "f" + Name: "f" + Arguments: [Argument] + - <Argument> + Name: "id" + Value: [1,2] + +types: + - name: common types + input: 'query ($string: String, $int: Int, $arr: [Arr], $notnull: [Arr!]!) { f }' + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + VariableDefinitions: [VariableDefinition] + - <VariableDefinition> + Variable: "string" + Type: String + - <VariableDefinition> + Variable: "int" + Type: Int + - <VariableDefinition> + Variable: "arr" + Type: [Arr] + - <VariableDefinition> + Variable: "notnull" + Type: [Arr!]! + SelectionSet: [Selection] + - <Field> + Alias: "f" + Name: "f" + +large queries: + - name: kitchen sink + input: | + # Copyright (c) 2015-present, Facebook, Inc. + # + # This source code is licensed under the MIT license found in the + # LICENSE file in the root directory of this source tree. + + query queryName($foo: ComplexType, $site: Site = MOBILE) { + whoever123is: node(id: [123, 456]) { + id , + ... on User @defer { + field2 { + id , + alias: field1(first:10, after:$foo,) @include(if: $foo) { + id, + ...frag + } + } + } + ... @skip(unless: $foo) { + id + } + ... { + id + } + } + } + + mutation likeStory { + like(story: 123) @defer { + story { + id + } + } + } + + subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { + storyLikeSubscribe(input: $input) { + story { + likers { + count + } + likeSentence { + text + } + } + } + } + + fragment frag on Friend { + foo(size: $size, bar: $b, obj: {key: "value", block: """ + block string uses \""" + """}) + } + + { + unnamed(truthy: true, falsey: false, nullish: null), + query + } + ast: | + <QueryDocument> + Operations: [OperationDefinition] + - <OperationDefinition> + Operation: Operation("query") + Name: "queryName" + VariableDefinitions: [VariableDefinition] + - <VariableDefinition> + Variable: "foo" + Type: ComplexType + - <VariableDefinition> + Variable: "site" + Type: Site + DefaultValue: MOBILE + SelectionSet: [Selection] + - <Field> + Alias: "whoever123is" + Name: "node" + Arguments: [Argument] + - <Argument> + Name: "id" + Value: [123,456] + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + - <InlineFragment> + TypeCondition: "User" + Directives: [Directive] + - <Directive> + Name: "defer" + SelectionSet: [Selection] + - <Field> + Alias: "field2" + Name: "field2" + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + - <Field> + Alias: "alias" + Name: "field1" + Arguments: [Argument] + - <Argument> + Name: "first" + Value: 10 + - <Argument> + Name: "after" + Value: $foo + Directives: [Directive] + - <Directive> + Name: "include" + Arguments: [Argument] + - <Argument> + Name: "if" + Value: $foo + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + - <FragmentSpread> + Name: "frag" + - <InlineFragment> + Directives: [Directive] + - <Directive> + Name: "skip" + Arguments: [Argument] + - <Argument> + Name: "unless" + Value: $foo + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + - <InlineFragment> + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + - <OperationDefinition> + Operation: Operation("mutation") + Name: "likeStory" + SelectionSet: [Selection] + - <Field> + Alias: "like" + Name: "like" + Arguments: [Argument] + - <Argument> + Name: "story" + Value: 123 + Directives: [Directive] + - <Directive> + Name: "defer" + SelectionSet: [Selection] + - <Field> + Alias: "story" + Name: "story" + SelectionSet: [Selection] + - <Field> + Alias: "id" + Name: "id" + - <OperationDefinition> + Operation: Operation("subscription") + Name: "StoryLikeSubscription" + VariableDefinitions: [VariableDefinition] + - <VariableDefinition> + Variable: "input" + Type: StoryLikeSubscribeInput + SelectionSet: [Selection] + - <Field> + Alias: "storyLikeSubscribe" + Name: "storyLikeSubscribe" + Arguments: [Argument] + - <Argument> + Name: "input" + Value: $input + SelectionSet: [Selection] + - <Field> + Alias: "story" + Name: "story" + SelectionSet: [Selection] + - <Field> + Alias: "likers" + Name: "likers" + SelectionSet: [Selection] + - <Field> + Alias: "count" + Name: "count" + - <Field> + Alias: "likeSentence" + Name: "likeSentence" + SelectionSet: [Selection] + - <Field> + Alias: "text" + Name: "text" + - <OperationDefinition> + Operation: Operation("query") + SelectionSet: [Selection] + - <Field> + Alias: "unnamed" + Name: "unnamed" + Arguments: [Argument] + - <Argument> + Name: "truthy" + Value: true + - <Argument> + Name: "falsey" + Value: false + - <Argument> + Name: "nullish" + Value: null + - <Field> + Alias: "query" + Name: "query" + Fragments: [FragmentDefinition] + - <FragmentDefinition> + Name: "frag" + TypeCondition: "Friend" + SelectionSet: [Selection] + - <Field> + Alias: "foo" + Name: "foo" + Arguments: [Argument] + - <Argument> + Name: "size" + Value: $size + - <Argument> + Name: "bar" + Value: $b + - <Argument> + Name: "obj" + Value: {"key":"value","block":"block string uses \"\"\""} diff --git a/vendor/github.com/vektah/gqlparser/parser/schema.go b/vendor/github.com/vektah/gqlparser/parser/schema.go new file mode 100644 index 00000000..f409f1f4 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/parser/schema.go @@ -0,0 +1,503 @@ +package parser + +import ( + . "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/lexer" +) + +func ParseSchema(source *Source) (*SchemaDocument, *gqlerror.Error) { + p := parser{ + lexer: lexer.New(source), + } + return p.parseSchemaDocument(), p.err +} + +func (p *parser) parseSchemaDocument() *SchemaDocument { + var doc SchemaDocument + doc.Position = p.peekPos() + for p.peek().Kind != lexer.EOF { + if p.err != nil { + return nil + } + + var description string + if p.peek().Kind == lexer.BlockString || p.peek().Kind == lexer.String { + description = p.parseDescription() + } + + if p.peek().Kind != lexer.Name { + p.unexpectedError() + break + } + + switch p.peek().Value { + case "scalar", "type", "interface", "union", "enum", "input": + doc.Definitions = append(doc.Definitions, p.parseTypeSystemDefinition(description)) + case "schema": + doc.Schema = append(doc.Schema, p.parseSchemaDefinition(description)) + case "directive": + doc.Directives = append(doc.Directives, p.parseDirectiveDefinition(description)) + case "extend": + if description != "" { + p.unexpectedToken(p.prev) + } + p.parseTypeSystemExtension(&doc) + default: + p.unexpectedError() + return nil + } + } + + return &doc +} + +func (p *parser) parseDescription() string { + token := p.peek() + + if token.Kind != lexer.BlockString && token.Kind != lexer.String { + return "" + } + + return p.next().Value +} + +func (p *parser) parseTypeSystemDefinition(description string) *Definition { + tok := p.peek() + if tok.Kind != lexer.Name { + p.unexpectedError() + return nil + } + + switch tok.Value { + case "scalar": + return p.parseScalarTypeDefinition(description) + case "type": + return p.parseObjectTypeDefinition(description) + case "interface": + return p.parseInterfaceTypeDefinition(description) + case "union": + return p.parseUnionTypeDefinition(description) + case "enum": + return p.parseEnumTypeDefinition(description) + case "input": + return p.parseInputObjectTypeDefinition(description) + default: + p.unexpectedError() + return nil + } +} + +func (p *parser) parseSchemaDefinition(description string) *SchemaDefinition { + p.expectKeyword("schema") + + def := SchemaDefinition{Description: description} + def.Position = p.peekPos() + def.Description = description + def.Directives = p.parseDirectives(true) + + p.many(lexer.BraceL, lexer.BraceR, func() { + def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition()) + }) + return &def +} + +func (p *parser) parseOperationTypeDefinition() *OperationTypeDefinition { + var op OperationTypeDefinition + op.Position = p.peekPos() + op.Operation = p.parseOperationType() + p.expect(lexer.Colon) + op.Type = p.parseName() + return &op +} + +func (p *parser) parseScalarTypeDefinition(description string) *Definition { + p.expectKeyword("scalar") + + var def Definition + def.Position = p.peekPos() + def.Kind = Scalar + def.Description = description + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + return &def +} + +func (p *parser) parseObjectTypeDefinition(description string) *Definition { + p.expectKeyword("type") + + var def Definition + def.Position = p.peekPos() + def.Kind = Object + def.Description = description + def.Name = p.parseName() + def.Interfaces = p.parseImplementsInterfaces() + def.Directives = p.parseDirectives(true) + def.Fields = p.parseFieldsDefinition() + return &def +} + +func (p *parser) parseImplementsInterfaces() []string { + var types []string + if p.peek().Value == "implements" { + p.next() + // optional leading ampersand + p.skip(lexer.Amp) + + types = append(types, p.parseName()) + for p.skip(lexer.Amp) && p.err == nil { + types = append(types, p.parseName()) + } + } + return types +} + +func (p *parser) parseFieldsDefinition() FieldList { + var defs FieldList + p.many(lexer.BraceL, lexer.BraceR, func() { + defs = append(defs, p.parseFieldDefinition()) + }) + return defs +} + +func (p *parser) parseFieldDefinition() *FieldDefinition { + var def FieldDefinition + def.Position = p.peekPos() + def.Description = p.parseDescription() + def.Name = p.parseName() + def.Arguments = p.parseArgumentDefs() + p.expect(lexer.Colon) + def.Type = p.parseTypeReference() + def.Directives = p.parseDirectives(true) + + return &def +} + +func (p *parser) parseArgumentDefs() ArgumentDefinitionList { + var args ArgumentDefinitionList + p.many(lexer.ParenL, lexer.ParenR, func() { + args = append(args, p.parseArgumentDef()) + }) + return args +} + +func (p *parser) parseArgumentDef() *ArgumentDefinition { + var def ArgumentDefinition + def.Position = p.peekPos() + def.Description = p.parseDescription() + def.Name = p.parseName() + p.expect(lexer.Colon) + def.Type = p.parseTypeReference() + if p.skip(lexer.Equals) { + def.DefaultValue = p.parseValueLiteral(true) + } + def.Directives = p.parseDirectives(true) + return &def +} + +func (p *parser) parseInputValueDef() *FieldDefinition { + var def FieldDefinition + def.Position = p.peekPos() + def.Description = p.parseDescription() + def.Name = p.parseName() + p.expect(lexer.Colon) + def.Type = p.parseTypeReference() + if p.skip(lexer.Equals) { + def.DefaultValue = p.parseValueLiteral(true) + } + def.Directives = p.parseDirectives(true) + return &def +} + +func (p *parser) parseInterfaceTypeDefinition(description string) *Definition { + p.expectKeyword("interface") + + var def Definition + def.Position = p.peekPos() + def.Kind = Interface + def.Description = description + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + def.Fields = p.parseFieldsDefinition() + return &def +} + +func (p *parser) parseUnionTypeDefinition(description string) *Definition { + p.expectKeyword("union") + + var def Definition + def.Position = p.peekPos() + def.Kind = Union + def.Description = description + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + def.Types = p.parseUnionMemberTypes() + return &def +} + +func (p *parser) parseUnionMemberTypes() []string { + var types []string + if p.skip(lexer.Equals) { + // optional leading pipe + p.skip(lexer.Pipe) + + types = append(types, p.parseName()) + for p.skip(lexer.Pipe) && p.err == nil { + types = append(types, p.parseName()) + } + } + return types +} + +func (p *parser) parseEnumTypeDefinition(description string) *Definition { + p.expectKeyword("enum") + + var def Definition + def.Position = p.peekPos() + def.Kind = Enum + def.Description = description + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + def.EnumValues = p.parseEnumValuesDefinition() + return &def +} + +func (p *parser) parseEnumValuesDefinition() EnumValueList { + var values EnumValueList + p.many(lexer.BraceL, lexer.BraceR, func() { + values = append(values, p.parseEnumValueDefinition()) + }) + return values +} + +func (p *parser) parseEnumValueDefinition() *EnumValueDefinition { + return &EnumValueDefinition{ + Position: p.peekPos(), + Description: p.parseDescription(), + Name: p.parseName(), + Directives: p.parseDirectives(true), + } +} + +func (p *parser) parseInputObjectTypeDefinition(description string) *Definition { + p.expectKeyword("input") + + var def Definition + def.Position = p.peekPos() + def.Kind = InputObject + def.Description = description + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + def.Fields = p.parseInputFieldsDefinition() + return &def +} + +func (p *parser) parseInputFieldsDefinition() FieldList { + var values FieldList + p.many(lexer.BraceL, lexer.BraceR, func() { + values = append(values, p.parseInputValueDef()) + }) + return values +} + +func (p *parser) parseTypeSystemExtension(doc *SchemaDocument) { + p.expectKeyword("extend") + + switch p.peek().Value { + case "schema": + doc.SchemaExtension = append(doc.SchemaExtension, p.parseSchemaExtension()) + case "scalar": + doc.Extensions = append(doc.Extensions, p.parseScalarTypeExtension()) + case "type": + doc.Extensions = append(doc.Extensions, p.parseObjectTypeExtension()) + case "interface": + doc.Extensions = append(doc.Extensions, p.parseInterfaceTypeExtension()) + case "union": + doc.Extensions = append(doc.Extensions, p.parseUnionTypeExtension()) + case "enum": + doc.Extensions = append(doc.Extensions, p.parseEnumTypeExtension()) + case "input": + doc.Extensions = append(doc.Extensions, p.parseInputObjectTypeExtension()) + default: + p.unexpectedError() + } +} + +func (p *parser) parseSchemaExtension() *SchemaDefinition { + p.expectKeyword("schema") + + var def SchemaDefinition + def.Position = p.peekPos() + def.Directives = p.parseDirectives(true) + p.many(lexer.BraceL, lexer.BraceR, func() { + def.OperationTypes = append(def.OperationTypes, p.parseOperationTypeDefinition()) + }) + if len(def.Directives) == 0 && len(def.OperationTypes) == 0 { + p.unexpectedError() + } + return &def +} + +func (p *parser) parseScalarTypeExtension() *Definition { + p.expectKeyword("scalar") + + var def Definition + def.Position = p.peekPos() + def.Kind = Scalar + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + if len(def.Directives) == 0 { + p.unexpectedError() + } + return &def +} + +func (p *parser) parseObjectTypeExtension() *Definition { + p.expectKeyword("type") + + var def Definition + def.Position = p.peekPos() + def.Kind = Object + def.Name = p.parseName() + def.Interfaces = p.parseImplementsInterfaces() + def.Directives = p.parseDirectives(true) + def.Fields = p.parseFieldsDefinition() + if len(def.Interfaces) == 0 && len(def.Directives) == 0 && len(def.Fields) == 0 { + p.unexpectedError() + } + return &def +} + +func (p *parser) parseInterfaceTypeExtension() *Definition { + p.expectKeyword("interface") + + var def Definition + def.Position = p.peekPos() + def.Kind = Interface + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + def.Fields = p.parseFieldsDefinition() + if len(def.Directives) == 0 && len(def.Fields) == 0 { + p.unexpectedError() + } + return &def +} + +func (p *parser) parseUnionTypeExtension() *Definition { + p.expectKeyword("union") + + var def Definition + def.Position = p.peekPos() + def.Kind = Union + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + def.Types = p.parseUnionMemberTypes() + + if len(def.Directives) == 0 && len(def.Types) == 0 { + p.unexpectedError() + } + return &def +} + +func (p *parser) parseEnumTypeExtension() *Definition { + p.expectKeyword("enum") + + var def Definition + def.Position = p.peekPos() + def.Kind = Enum + def.Name = p.parseName() + def.Directives = p.parseDirectives(true) + def.EnumValues = p.parseEnumValuesDefinition() + if len(def.Directives) == 0 && len(def.EnumValues) == 0 { + p.unexpectedError() + } + return &def +} + +func (p *parser) parseInputObjectTypeExtension() *Definition { + p.expectKeyword("input") + + var def Definition + def.Position = p.peekPos() + def.Kind = InputObject + def.Name = p.parseName() + def.Directives = p.parseDirectives(false) + def.Fields = p.parseInputFieldsDefinition() + if len(def.Directives) == 0 && len(def.Fields) == 0 { + p.unexpectedError() + } + return &def +} + +func (p *parser) parseDirectiveDefinition(description string) *DirectiveDefinition { + p.expectKeyword("directive") + p.expect(lexer.At) + + var def DirectiveDefinition + def.Position = p.peekPos() + def.Description = description + def.Name = p.parseName() + def.Arguments = p.parseArgumentDefs() + + p.expectKeyword("on") + def.Locations = p.parseDirectiveLocations() + return &def +} + +func (p *parser) parseDirectiveLocations() []DirectiveLocation { + p.skip(lexer.Pipe) + + locations := []DirectiveLocation{p.parseDirectiveLocation()} + + for p.skip(lexer.Pipe) && p.err == nil { + locations = append(locations, p.parseDirectiveLocation()) + } + + return locations +} + +func (p *parser) parseDirectiveLocation() DirectiveLocation { + name := p.expect(lexer.Name) + + switch name.Value { + case `QUERY`: + return LocationQuery + case `MUTATION`: + return LocationMutation + case `SUBSCRIPTION`: + return LocationSubscription + case `FIELD`: + return LocationField + case `FRAGMENT_DEFINITION`: + return LocationFragmentDefinition + case `FRAGMENT_SPREAD`: + return LocationFragmentSpread + case `INLINE_FRAGMENT`: + return LocationInlineFragment + case `SCHEMA`: + return LocationSchema + case `SCALAR`: + return LocationScalar + case `OBJECT`: + return LocationObject + case `FIELD_DEFINITION`: + return LocationFieldDefinition + case `ARGUMENT_DEFINITION`: + return LocationArgumentDefinition + case `INTERFACE`: + return LocationInterface + case `UNION`: + return LocationUnion + case `ENUM`: + return LocationEnum + case `ENUM_VALUE`: + return LocationEnumValue + case `INPUT_OBJECT`: + return LocationInputObject + case `INPUT_FIELD_DEFINITION`: + return LocationInputFieldDefinition + } + + p.unexpectedToken(name) + return "" +} diff --git a/vendor/github.com/vektah/gqlparser/parser/schema_test.yml b/vendor/github.com/vektah/gqlparser/parser/schema_test.yml new file mode 100644 index 00000000..c65239a5 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/parser/schema_test.yml @@ -0,0 +1,505 @@ +object types: + - name: simple + input: | + type Hello { + world: String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Type: String + + - name: with description + input: | + "Description" + type Hello { + world: String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Description: "Description" + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Type: String + + - name: with block description + input: | + """ + Description + """ + # Even with comments between them + type Hello { + world: String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Description: "Description" + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Type: String + - name: with field arg + input: | + type Hello { + world(flag: Boolean): String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Arguments: [ArgumentDefinition] + - <ArgumentDefinition> + Name: "flag" + Type: Boolean + Type: String + + - name: with field arg and default value + input: | + type Hello { + world(flag: Boolean = true): String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Arguments: [ArgumentDefinition] + - <ArgumentDefinition> + Name: "flag" + DefaultValue: true + Type: Boolean + Type: String + + - name: with field list arg + input: | + type Hello { + world(things: [String]): String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Arguments: [ArgumentDefinition] + - <ArgumentDefinition> + Name: "things" + Type: [String] + Type: String + + - name: with two args + input: | + type Hello { + world(argOne: Boolean, argTwo: Int): String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Arguments: [ArgumentDefinition] + - <ArgumentDefinition> + Name: "argOne" + Type: Boolean + - <ArgumentDefinition> + Name: "argTwo" + Type: Int + Type: String + +type extensions: + - name: Object extension + input: | + extend type Hello { + world: String + } + ast: | + <SchemaDocument> + Extensions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Type: String + + - name: without any fields + input: "extend type Hello implements Greeting" + ast: | + <SchemaDocument> + Extensions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Interfaces: [string] + - "Greeting" + + - name: without fields twice + input: | + extend type Hello implements Greeting + extend type Hello implements SecondGreeting + ast: | + <SchemaDocument> + Extensions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Interfaces: [string] + - "Greeting" + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Interfaces: [string] + - "SecondGreeting" + + - name: without anything errors + input: "extend type Hello" + error: + message: "Unexpected <EOF>" + locations: [{ line: 1, column: 18 }] + + - name: can have descriptions # hmm, this might not be spec compliant... + input: | + "Description" + extend type Hello { + world: String + } + error: + message: 'Unexpected String "Description"' + locations: [{ line: 1, column: 2 }] + + - name: can not have descriptions on types + input: | + extend "Description" type Hello { + world: String + } + error: + message: Unexpected String "Description" + locations: [{ line: 1, column: 9 }] + +schema definition: + - name: simple + input: | + schema { + query: Query + } + ast: | + <SchemaDocument> + Schema: [SchemaDefinition] + - <SchemaDefinition> + OperationTypes: [OperationTypeDefinition] + - <OperationTypeDefinition> + Operation: Operation("query") + Type: "Query" + +schema extensions: + - name: simple + input: | + extend schema { + mutation: Mutation + } + ast: | + <SchemaDocument> + SchemaExtension: [SchemaDefinition] + - <SchemaDefinition> + OperationTypes: [OperationTypeDefinition] + - <OperationTypeDefinition> + Operation: Operation("mutation") + Type: "Mutation" + + - name: directive only + input: "extend schema @directive" + ast: | + <SchemaDocument> + SchemaExtension: [SchemaDefinition] + - <SchemaDefinition> + Directives: [Directive] + - <Directive> + Name: "directive" + + - name: without anything errors + input: "extend schema" + error: + message: "Unexpected <EOF>" + locations: [{ line: 1, column: 14}] + +type extensions: + - name: all can have directives + input: | + extend scalar Foo @deprecated + extend type Foo @deprecated + extend interface Foo @deprecated + extend union Foo @deprecated + extend enum Foo @deprecated + extend input Foo @deprecated + ast: | + <SchemaDocument> + Extensions: [Definition] + - <Definition> + Kind: DefinitionKind("SCALAR") + Name: "Foo" + Directives: [Directive] + - <Directive> + Name: "deprecated" + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Foo" + Directives: [Directive] + - <Directive> + Name: "deprecated" + - <Definition> + Kind: DefinitionKind("INTERFACE") + Name: "Foo" + Directives: [Directive] + - <Directive> + Name: "deprecated" + - <Definition> + Kind: DefinitionKind("UNION") + Name: "Foo" + Directives: [Directive] + - <Directive> + Name: "deprecated" + - <Definition> + Kind: DefinitionKind("ENUM") + Name: "Foo" + Directives: [Directive] + - <Directive> + Name: "deprecated" + - <Definition> + Kind: DefinitionKind("INPUT_OBJECT") + Name: "Foo" + Directives: [Directive] + - <Directive> + Name: "deprecated" + + +inheritance: + - name: single + input: "type Hello implements World { field: String }" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Interfaces: [string] + - "World" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "field" + Type: String + + - name: multi + input: "type Hello implements Wo & rld { field: String }" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Interfaces: [string] + - "Wo" + - "rld" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "field" + Type: String + + - name: multi with leading amp + input: "type Hello implements & Wo & rld { field: String }" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("OBJECT") + Name: "Hello" + Interfaces: [string] + - "Wo" + - "rld" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "field" + Type: String + +enums: + - name: single value + input: "enum Hello { WORLD }" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("ENUM") + Name: "Hello" + EnumValues: [EnumValueDefinition] + - <EnumValueDefinition> + Name: "WORLD" + + - name: double value + input: "enum Hello { WO, RLD }" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("ENUM") + Name: "Hello" + EnumValues: [EnumValueDefinition] + - <EnumValueDefinition> + Name: "WO" + - <EnumValueDefinition> + Name: "RLD" + +interface: + - name: simple + input: | + interface Hello { + world: String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("INTERFACE") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Type: String + +unions: + - name: simple + input: "union Hello = World" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("UNION") + Name: "Hello" + Types: [string] + - "World" + + - name: with two types + input: "union Hello = Wo | Rld" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("UNION") + Name: "Hello" + Types: [string] + - "Wo" + - "Rld" + + - name: with leading pipe + input: "union Hello = | Wo | Rld" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("UNION") + Name: "Hello" + Types: [string] + - "Wo" + - "Rld" + + - name: cant be empty + input: "union Hello = || Wo | Rld" + error: + message: "Expected Name, found |" + locations: [{ line: 1, column: 16 }] + + - name: cant double pipe + input: "union Hello = Wo || Rld" + error: + message: "Expected Name, found |" + locations: [{ line: 1, column: 19 }] + + - name: cant have trailing pipe + input: "union Hello = | Wo | Rld |" + error: + message: "Expected Name, found <EOF>" + locations: [{ line: 1, column: 27 }] + +scalar: + - name: simple + input: "scalar Hello" + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("SCALAR") + Name: "Hello" + +input object: + - name: simple + input: | + input Hello { + world: String + } + ast: | + <SchemaDocument> + Definitions: [Definition] + - <Definition> + Kind: DefinitionKind("INPUT_OBJECT") + Name: "Hello" + Fields: [FieldDefinition] + - <FieldDefinition> + Name: "world" + Type: String + + - name: can not have args + input: | + input Hello { + world(foo: Int): String + } + error: + message: "Expected :, found (" + locations: [{ line: 2, column: 8 }] + +directives: + - name: simple + input: directive @foo on FIELD + ast: | + <SchemaDocument> + Directives: [DirectiveDefinition] + - <DirectiveDefinition> + Name: "foo" + Locations: [DirectiveLocation] + - DirectiveLocation("FIELD") + + - name: invalid location + input: "directive @foo on FIELD | INCORRECT_LOCATION" + error: + message: 'Unexpected Name "INCORRECT_LOCATION"' + locations: [{ line: 1, column: 27 }] + diff --git a/vendor/github.com/vektah/gqlparser/readme.md b/vendor/github.com/vektah/gqlparser/readme.md new file mode 100644 index 00000000..976d202b --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/readme.md @@ -0,0 +1,17 @@ +gqlparser [![CircleCI](https://badgen.net/circleci/github/vektah/gqlparser/master)](https://circleci.com/gh/vektah/gqlparser) [![Go Report Card](https://goreportcard.com/badge/github.com/vektah/gqlparser)](https://goreportcard.com/report/github.com/vektah/gqlparser) [![Coverage Status](https://badgen.net/coveralls/c/github/vektah/gqlparser)](https://coveralls.io/github/vektah/gqlparser?branch=master) +=== + +This is a parser for graphql, written to mirror the graphql-js reference implementation as closely while remaining idiomatic and easy to use. + +spec target: June 2018 (Schema definition language, block strings as descriptions, error paths & extension) + +This parser is used by [gqlgen](https://github.com/99designs/gqlgen), and it should be reasonablly stable. + +Guiding principles: + + - maintainability: It should be easy to stay up to date with the spec + - well tested: It shouldnt need a graphql server to validate itself. Changes to this repo should be self contained. + - server agnostic: It should be usable by any of the graphql server implementations, and any graphql client tooling. + - idiomatic & stable api: It should follow go best practices, especially around forwards compatibility. + - fast: Where it doesnt impact on the above it should be fast. Avoid unnecessary allocs in hot paths. + - close to reference: Where it doesnt impact on the above, it should stay close to the [graphql/graphql-js](github.com/graphql/graphql-js) reference implementation. diff --git a/vendor/github.com/vektah/gqlparser/validator/error.go b/vendor/github.com/vektah/gqlparser/validator/error.go new file mode 100644 index 00000000..f354dee5 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/error.go @@ -0,0 +1,55 @@ +package validator + +import ( + "fmt" + + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" +) + +type ErrorOption func(err *gqlerror.Error) + +func Message(msg string, args ...interface{}) ErrorOption { + return func(err *gqlerror.Error) { + err.Message += fmt.Sprintf(msg, args...) + } +} + +func At(position *ast.Position) ErrorOption { + return func(err *gqlerror.Error) { + if position == nil { + return + } + err.Locations = append(err.Locations, gqlerror.Location{ + Line: position.Line, + Column: position.Column, + }) + if position.Src.Name != "" { + err.SetFile(position.Src.Name) + } + } +} + +func SuggestListQuoted(prefix string, typed string, suggestions []string) ErrorOption { + suggested := SuggestionList(typed, suggestions) + return func(err *gqlerror.Error) { + if len(suggested) > 0 { + err.Message += " " + prefix + " " + QuotedOrList(suggested...) + "?" + } + } +} + +func SuggestListUnquoted(prefix string, typed string, suggestions []string) ErrorOption { + suggested := SuggestionList(typed, suggestions) + return func(err *gqlerror.Error) { + if len(suggested) > 0 { + err.Message += " " + prefix + " " + OrList(suggested...) + "?" + } + } +} + +func Suggestf(suggestion string, args ...interface{}) ErrorOption { + return func(err *gqlerror.Error) { + err.Message += " Did you mean " + fmt.Sprintf(suggestion, args...) + "?" + } +} diff --git a/vendor/github.com/vektah/gqlgen/neelance/tests/testdata/LICENSE b/vendor/github.com/vektah/gqlparser/validator/imported/LICENSE index fce4519e..fce4519e 100644 --- a/vendor/github.com/vektah/gqlgen/neelance/tests/testdata/LICENSE +++ b/vendor/github.com/vektah/gqlparser/validator/imported/LICENSE diff --git a/vendor/github.com/vektah/gqlparser/validator/messaging.go b/vendor/github.com/vektah/gqlparser/validator/messaging.go new file mode 100644 index 00000000..f1ab5873 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/messaging.go @@ -0,0 +1,39 @@ +package validator + +import "bytes" + +// Given [ A, B, C ] return '"A", "B", or "C"'. +func QuotedOrList(items ...string) string { + itemsQuoted := make([]string, len(items)) + for i, item := range items { + itemsQuoted[i] = `"` + item + `"` + } + return OrList(itemsQuoted...) +} + +// Given [ A, B, C ] return 'A, B, or C'. +func OrList(items ...string) string { + var buf bytes.Buffer + + if len(items) > 5 { + items = items[:5] + } + if len(items) == 2 { + buf.WriteString(items[0]) + buf.WriteString(" or ") + buf.WriteString(items[1]) + return buf.String() + } + + for i, item := range items { + if i != 0 { + if i == len(items)-1 { + buf.WriteString(", or ") + } else { + buf.WriteString(", ") + } + } + buf.WriteString(item) + } + return buf.String() +} diff --git a/vendor/github.com/vektah/gqlparser/validator/prelude.go b/vendor/github.com/vektah/gqlparser/validator/prelude.go new file mode 100644 index 00000000..80ce8a21 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/prelude.go @@ -0,0 +1,5 @@ +package validator + +import "github.com/vektah/gqlparser/ast" + +var Prelude = &ast.Source{Name: "prelude.graphql", Input: "# This file defines all the implicitly declared types that are required by the graphql spec. It is implicitly included by calls to LoadSchema\n\n# The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.\nscalar Int\n\n# The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).\nscalar Float\n\n# The `String`scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.\nscalar String\n\n# The `Boolean` scalar type represents ` + \"`\" + `true` + \"`\" + ` or ` + \"`\" + `false` + \"`\" + `.\nscalar Boolean\n\n# The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as \"4\") or integer (such as 4) input value will be accepted as an ID.\nscalar ID\n\n# The @include directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional inclusion during execution as described by the if argument.\ndirective @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT\n\n# The @skip directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional exclusion during execution as described by the if argument.\ndirective @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT\n\n# The @deprecated directive is used within the type system definition language to indicate deprecated portions of a GraphQL service’s schema, such as deprecated fields on a type or deprecated enum values.\ndirective @deprecated(reason: String = \"No longer supported\") on FIELD_DEFINITION | ENUM_VALUE\n\ntype __Schema {\n types: [__Type!]!\n queryType: __Type!\n mutationType: __Type\n subscriptionType: __Type\n directives: [__Directive!]!\n}\n\ntype __Type {\n kind: __TypeKind!\n name: String\n description: String\n\n # OBJECT and INTERFACE only\n fields(includeDeprecated: Boolean = false): [__Field!]\n\n # OBJECT only\n interfaces: [__Type!]\n\n # INTERFACE and UNION only\n possibleTypes: [__Type!]\n\n # ENUM only\n enumValues(includeDeprecated: Boolean = false): [__EnumValue!]\n\n # INPUT_OBJECT only\n inputFields: [__InputValue!]\n\n # NON_NULL and LIST only\n ofType: __Type\n}\n\ntype __Field {\n name: String!\n description: String\n args: [__InputValue!]!\n type: __Type!\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\ntype __InputValue {\n name: String!\n description: String\n type: __Type!\n defaultValue: String\n}\n\ntype __EnumValue {\n name: String!\n description: String\n isDeprecated: Boolean!\n deprecationReason: String\n}\n\nenum __TypeKind {\n SCALAR\n OBJECT\n INTERFACE\n UNION\n ENUM\n INPUT_OBJECT\n LIST\n NON_NULL\n}\n\ntype __Directive {\n name: String!\n description: String\n locations: [__DirectiveLocation!]!\n args: [__InputValue!]!\n}\n\nenum __DirectiveLocation {\n QUERY\n MUTATION\n SUBSCRIPTION\n FIELD\n FRAGMENT_DEFINITION\n FRAGMENT_SPREAD\n INLINE_FRAGMENT\n SCHEMA\n SCALAR\n OBJECT\n FIELD_DEFINITION\n ARGUMENT_DEFINITION\n INTERFACE\n UNION\n ENUM\n ENUM_VALUE\n INPUT_OBJECT\n INPUT_FIELD_DEFINITION\n}\n"} diff --git a/vendor/github.com/vektah/gqlparser/validator/prelude.graphql b/vendor/github.com/vektah/gqlparser/validator/prelude.graphql new file mode 100644 index 00000000..2c7f7c02 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/prelude.graphql @@ -0,0 +1,119 @@ +# This file defines all the implicitly declared types that are required by the graphql spec. It is implicitly included by calls to LoadSchema + +# The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. +scalar Int + +# The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). +scalar Float + +# The `String`scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text. +scalar String + +# The `Boolean` scalar type represents ` + "`" + `true` + "`" + ` or ` + "`" + `false` + "`" + `. +scalar Boolean + +# The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID. +scalar ID + +# The @include directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional inclusion during execution as described by the if argument. +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +# The @skip directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional exclusion during execution as described by the if argument. +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +# The @deprecated directive is used within the type system definition language to indicate deprecated portions of a GraphQL service’s schema, such as deprecated fields on a type or deprecated enum values. +directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE + +type __Schema { + types: [__Type!]! + queryType: __Type! + mutationType: __Type + subscriptionType: __Type + directives: [__Directive!]! +} + +type __Type { + kind: __TypeKind! + name: String + description: String + + # OBJECT and INTERFACE only + fields(includeDeprecated: Boolean = false): [__Field!] + + # OBJECT only + interfaces: [__Type!] + + # INTERFACE and UNION only + possibleTypes: [__Type!] + + # ENUM only + enumValues(includeDeprecated: Boolean = false): [__EnumValue!] + + # INPUT_OBJECT only + inputFields: [__InputValue!] + + # NON_NULL and LIST only + ofType: __Type +} + +type __Field { + name: String! + description: String + args: [__InputValue!]! + type: __Type! + isDeprecated: Boolean! + deprecationReason: String +} + +type __InputValue { + name: String! + description: String + type: __Type! + defaultValue: String +} + +type __EnumValue { + name: String! + description: String + isDeprecated: Boolean! + deprecationReason: String +} + +enum __TypeKind { + SCALAR + OBJECT + INTERFACE + UNION + ENUM + INPUT_OBJECT + LIST + NON_NULL +} + +type __Directive { + name: String! + description: String + locations: [__DirectiveLocation!]! + args: [__InputValue!]! +} + +enum __DirectiveLocation { + QUERY + MUTATION + SUBSCRIPTION + FIELD + FRAGMENT_DEFINITION + FRAGMENT_SPREAD + INLINE_FRAGMENT + SCHEMA + SCALAR + OBJECT + FIELD_DEFINITION + ARGUMENT_DEFINITION + INTERFACE + UNION + ENUM + ENUM_VALUE + INPUT_OBJECT + INPUT_FIELD_DEFINITION +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/fields_on_correct_type.go b/vendor/github.com/vektah/gqlparser/validator/rules/fields_on_correct_type.go new file mode 100644 index 00000000..69148d52 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/fields_on_correct_type.go @@ -0,0 +1,86 @@ +package validator + +import ( + "fmt" + "sort" + + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("FieldsOnCorrectType", func(observers *Events, addError AddErrFunc) { + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.ObjectDefinition == nil || field.Definition != nil { + return + } + + message := fmt.Sprintf(`Cannot query field "%s" on type "%s".`, field.Name, field.ObjectDefinition.Name) + + if suggestedTypeNames := getSuggestedTypeNames(walker, field.ObjectDefinition, field.Name); suggestedTypeNames != nil { + message += " Did you mean to use an inline fragment on " + QuotedOrList(suggestedTypeNames...) + "?" + } else if suggestedFieldNames := getSuggestedFieldNames(field.ObjectDefinition, field.Name); suggestedFieldNames != nil { + message += " Did you mean " + QuotedOrList(suggestedFieldNames...) + "?" + } + + addError( + Message(message), + At(field.Position), + ) + }) + }) +} + +// Go through all of the implementations of type, as well as the interfaces +// that they implement. If any of those types include the provided field, +// suggest them, sorted by how often the type is referenced, starting +// with Interfaces. +func getSuggestedTypeNames(walker *Walker, parent *ast.Definition, name string) []string { + if !parent.IsAbstractType() { + return nil + } + + var suggestedObjectTypes []string + var suggestedInterfaceTypes []string + interfaceUsageCount := map[string]int{} + + for _, possibleType := range walker.Schema.GetPossibleTypes(parent) { + field := possibleType.Fields.ForName(name) + if field == nil { + continue + } + + suggestedObjectTypes = append(suggestedObjectTypes, possibleType.Name) + + for _, possibleInterface := range possibleType.Interfaces { + interfaceField := walker.Schema.Types[possibleInterface] + if interfaceField != nil && interfaceField.Fields.ForName(name) != nil { + if interfaceUsageCount[possibleInterface] == 0 { + suggestedInterfaceTypes = append(suggestedInterfaceTypes, possibleInterface) + } + interfaceUsageCount[possibleInterface]++ + } + } + } + + sort.SliceStable(suggestedInterfaceTypes, func(i, j int) bool { + return interfaceUsageCount[suggestedInterfaceTypes[i]] > interfaceUsageCount[suggestedInterfaceTypes[j]] + }) + + return append(suggestedInterfaceTypes, suggestedObjectTypes...) +} + +// For the field name provided, determine if there are any similar field names +// that may be the result of a typo. +func getSuggestedFieldNames(parent *ast.Definition, name string) []string { + if parent.Kind != ast.Object && parent.Kind != ast.Interface { + return nil + } + + var possibleFieldNames []string + for _, field := range parent.Fields { + possibleFieldNames = append(possibleFieldNames, field.Name) + } + + return SuggestionList(name, possibleFieldNames) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/fragments_on_composite_types.go b/vendor/github.com/vektah/gqlparser/validator/rules/fragments_on_composite_types.go new file mode 100644 index 00000000..a4a48246 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/fragments_on_composite_types.go @@ -0,0 +1,39 @@ +package validator + +import ( + "fmt" + + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("FragmentsOnCompositeTypes", func(observers *Events, addError AddErrFunc) { + observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) { + fragmentType := walker.Schema.Types[inlineFragment.TypeCondition] + if fragmentType == nil || fragmentType.IsCompositeType() { + return + } + + message := fmt.Sprintf(`Fragment cannot condition on non composite type "%s".`, inlineFragment.TypeCondition) + + addError( + Message(message), + At(inlineFragment.Position), + ) + }) + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + if fragment.Definition == nil || fragment.TypeCondition == "" || fragment.Definition.IsCompositeType() { + return + } + + message := fmt.Sprintf(`Fragment "%s" cannot condition on non composite type "%s".`, fragment.Name, fragment.TypeCondition) + + addError( + Message(message), + At(fragment.Position), + ) + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/known_argument_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/known_argument_names.go new file mode 100644 index 00000000..83b47387 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/known_argument_names.go @@ -0,0 +1,57 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("KnownArgumentNames", func(observers *Events, addError AddErrFunc) { + // A GraphQL field is only valid if all supplied arguments are defined by that field. + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.Definition == nil { + return + } + for _, arg := range field.Arguments { + def := field.Definition.Arguments.ForName(arg.Name) + if def != nil { + continue + } + + var suggestions []string + for _, argDef := range field.Definition.Arguments { + suggestions = append(suggestions, argDef.Name) + } + + addError( + Message(`Unknown argument "%s" on field "%s" of type "%s".`, arg.Name, field.Name, field.ObjectDefinition.Name), + SuggestListQuoted("Did you mean", arg.Name, suggestions), + At(field.Position), + ) + } + }) + + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + if directive.Definition == nil { + return + } + for _, arg := range directive.Arguments { + def := directive.Definition.Arguments.ForName(arg.Name) + if def != nil { + continue + } + + var suggestions []string + for _, argDef := range directive.Definition.Arguments { + suggestions = append(suggestions, argDef.Name) + } + + addError( + Message(`Unknown argument "%s" on directive "@%s".`, arg.Name, directive.Name), + SuggestListQuoted("Did you mean", arg.Name, suggestions), + At(directive.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/known_directives.go b/vendor/github.com/vektah/gqlparser/validator/rules/known_directives.go new file mode 100644 index 00000000..dc4353ef --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/known_directives.go @@ -0,0 +1,31 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("KnownDirectives", func(observers *Events, addError AddErrFunc) { + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + if directive.Definition == nil { + addError( + Message(`Unknown directive "%s".`, directive.Name), + At(directive.Position), + ) + return + } + + for _, loc := range directive.Definition.Locations { + if loc == directive.Location { + return + } + } + + addError( + Message(`Directive "%s" may not be used on %s.`, directive.Name, directive.Location), + At(directive.Position), + ) + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/known_fragment_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/known_fragment_names.go new file mode 100644 index 00000000..ec91588c --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/known_fragment_names.go @@ -0,0 +1,19 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("KnownFragmentNames", func(observers *Events, addError AddErrFunc) { + observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) { + if fragmentSpread.Definition == nil { + addError( + Message(`Unknown fragment "%s".`, fragmentSpread.Name), + At(fragmentSpread.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/known_type_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/known_type_names.go new file mode 100644 index 00000000..223086b3 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/known_type_names.go @@ -0,0 +1,61 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("KnownTypeNames", func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + for _, vdef := range operation.VariableDefinitions { + typeName := vdef.Type.Name() + def := walker.Schema.Types[typeName] + if def != nil { + continue + } + + addError( + Message(`Unknown type "%s".`, typeName), + At(operation.Position), + ) + } + }) + + observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) { + typedName := inlineFragment.TypeCondition + if typedName == "" { + return + } + + def := walker.Schema.Types[typedName] + if def != nil { + return + } + + addError( + Message(`Unknown type "%s".`, typedName), + At(inlineFragment.Position), + ) + }) + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + typeName := fragment.TypeCondition + def := walker.Schema.Types[typeName] + if def != nil { + return + } + + var possibleTypes []string + for _, t := range walker.Schema.Types { + possibleTypes = append(possibleTypes, t.Name) + } + + addError( + Message(`Unknown type "%s".`, typeName), + SuggestListQuoted("Did you mean", typeName, possibleTypes), + At(fragment.Position), + ) + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/lone_anonymous_operation.go b/vendor/github.com/vektah/gqlparser/validator/rules/lone_anonymous_operation.go new file mode 100644 index 00000000..dd232142 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/lone_anonymous_operation.go @@ -0,0 +1,19 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("LoneAnonymousOperation", func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + if operation.Name == "" && len(walker.Document.Operations) > 1 { + addError( + Message(`This anonymous operation must be the only defined operation.`), + At(operation.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/no_fragment_cycles.go b/vendor/github.com/vektah/gqlparser/validator/rules/no_fragment_cycles.go new file mode 100644 index 00000000..7511529b --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/no_fragment_cycles.go @@ -0,0 +1,93 @@ +package validator + +import ( + "fmt" + "strings" + + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("NoFragmentCycles", func(observers *Events, addError AddErrFunc) { + visitedFrags := make(map[string]bool) + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + var spreadPath []*ast.FragmentSpread + spreadPathIndexByName := make(map[string]int) + + var recursive func(fragment *ast.FragmentDefinition) + recursive = func(fragment *ast.FragmentDefinition) { + if visitedFrags[fragment.Name] { + return + } + + visitedFrags[fragment.Name] = true + + spreadNodes := getFragmentSpreads(fragment.SelectionSet) + if len(spreadNodes) == 0 { + return + } + spreadPathIndexByName[fragment.Name] = len(spreadPath) + + for _, spreadNode := range spreadNodes { + spreadName := spreadNode.Name + + cycleIndex, ok := spreadPathIndexByName[spreadName] + + spreadPath = append(spreadPath, spreadNode) + if !ok { + spreadFragment := walker.Document.Fragments.ForName(spreadName) + if spreadFragment != nil { + recursive(spreadFragment) + } + } else { + cyclePath := spreadPath[cycleIndex : len(spreadPath)-1] + var fragmentNames []string + for _, fs := range cyclePath { + fragmentNames = append(fragmentNames, fs.Name) + } + var via string + if len(fragmentNames) != 0 { + via = fmt.Sprintf(" via %s", strings.Join(fragmentNames, ", ")) + } + addError( + Message(`Cannot spread fragment "%s" within itself%s.`, spreadName, via), + At(spreadNode.Position), + ) + } + + spreadPath = spreadPath[:len(spreadPath)-1] + } + + delete(spreadPathIndexByName, fragment.Name) + } + + recursive(fragment) + }) + }) +} + +func getFragmentSpreads(node ast.SelectionSet) []*ast.FragmentSpread { + var spreads []*ast.FragmentSpread + + setsToVisit := []ast.SelectionSet{node} + + for len(setsToVisit) != 0 { + set := setsToVisit[len(setsToVisit)-1] + setsToVisit = setsToVisit[:len(setsToVisit)-1] + + for _, selection := range set { + switch selection := selection.(type) { + case *ast.FragmentSpread: + spreads = append(spreads, selection) + case *ast.Field: + setsToVisit = append(setsToVisit, selection.SelectionSet) + case *ast.InlineFragment: + setsToVisit = append(setsToVisit, selection.SelectionSet) + } + } + } + + return spreads +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/no_undefined_variables.go b/vendor/github.com/vektah/gqlparser/validator/rules/no_undefined_variables.go new file mode 100644 index 00000000..505206be --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/no_undefined_variables.go @@ -0,0 +1,28 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("NoUndefinedVariables", func(observers *Events, addError AddErrFunc) { + observers.OnValue(func(walker *Walker, value *ast.Value) { + if walker.CurrentOperation == nil || value.Kind != ast.Variable || value.VariableDefinition != nil { + return + } + + if walker.CurrentOperation.Name != "" { + addError( + Message(`Variable "%s" is not defined by operation "%s".`, value, walker.CurrentOperation.Name), + At(walker.CurrentOperation.Position), + ) + } else { + addError( + Message(`Variable "%s" is not defined.`, value), + At(value.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/no_unused_fragments.go b/vendor/github.com/vektah/gqlparser/validator/rules/no_unused_fragments.go new file mode 100644 index 00000000..4aa835f5 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/no_unused_fragments.go @@ -0,0 +1,30 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("NoUnusedFragments", func(observers *Events, addError AddErrFunc) { + + inFragmentDefinition := false + fragmentNameUsed := make(map[string]bool) + + observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) { + if !inFragmentDefinition { + fragmentNameUsed[fragmentSpread.Name] = true + } + }) + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + inFragmentDefinition = true + if !fragmentNameUsed[fragment.Name] { + addError( + Message(`Fragment "%s" is never used.`, fragment.Name), + At(fragment.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/no_unused_variables.go b/vendor/github.com/vektah/gqlparser/validator/rules/no_unused_variables.go new file mode 100644 index 00000000..28cf7736 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/no_unused_variables.go @@ -0,0 +1,30 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("NoUnusedVariables", func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + for _, varDef := range operation.VariableDefinitions { + if varDef.Used { + continue + } + + if operation.Name != "" { + addError( + Message(`Variable "$%s" is never used in operation "%s".`, varDef.Variable, operation.Name), + At(varDef.Position), + ) + } else { + addError( + Message(`Variable "$%s" is never used.`, varDef.Variable), + At(varDef.Position), + ) + } + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/overlapping_fields_can_be_merged.go b/vendor/github.com/vektah/gqlparser/validator/rules/overlapping_fields_can_be_merged.go new file mode 100644 index 00000000..52eab3a2 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/overlapping_fields_can_be_merged.go @@ -0,0 +1,553 @@ +package validator + +import ( + "bytes" + "fmt" + "reflect" + + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + + AddRule("OverlappingFieldsCanBeMerged", func(observers *Events, addError AddErrFunc) { + /** + * Algorithm: + * + * Conflicts occur when two fields exist in a query which will produce the same + * response name, but represent differing values, thus creating a conflict. + * The algorithm below finds all conflicts via making a series of comparisons + * between fields. In order to compare as few fields as possible, this makes + * a series of comparisons "within" sets of fields and "between" sets of fields. + * + * Given any selection set, a collection produces both a set of fields by + * also including all inline fragments, as well as a list of fragments + * referenced by fragment spreads. + * + * A) Each selection set represented in the document first compares "within" its + * collected set of fields, finding any conflicts between every pair of + * overlapping fields. + * Note: This is the *only time* that a the fields "within" a set are compared + * to each other. After this only fields "between" sets are compared. + * + * B) Also, if any fragment is referenced in a selection set, then a + * comparison is made "between" the original set of fields and the + * referenced fragment. + * + * C) Also, if multiple fragments are referenced, then comparisons + * are made "between" each referenced fragment. + * + * D) When comparing "between" a set of fields and a referenced fragment, first + * a comparison is made between each field in the original set of fields and + * each field in the the referenced set of fields. + * + * E) Also, if any fragment is referenced in the referenced selection set, + * then a comparison is made "between" the original set of fields and the + * referenced fragment (recursively referring to step D). + * + * F) When comparing "between" two fragments, first a comparison is made between + * each field in the first referenced set of fields and each field in the the + * second referenced set of fields. + * + * G) Also, any fragments referenced by the first must be compared to the + * second, and any fragments referenced by the second must be compared to the + * first (recursively referring to step F). + * + * H) When comparing two fields, if both have selection sets, then a comparison + * is made "between" both selection sets, first comparing the set of fields in + * the first selection set with the set of fields in the second. + * + * I) Also, if any fragment is referenced in either selection set, then a + * comparison is made "between" the other set of fields and the + * referenced fragment. + * + * J) Also, if two fragments are referenced in both selection sets, then a + * comparison is made "between" the two fragments. + * + */ + + m := &overlappingFieldsCanBeMergedManager{ + comparedFragmentPairs: pairSet{data: make(map[string]map[string]bool)}, + } + + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + m.walker = walker + conflicts := m.findConflictsWithinSelectionSet(operation.SelectionSet) + for _, conflict := range conflicts { + conflict.addFieldsConflictMessage(addError) + } + }) + observers.OnField(func(walker *Walker, field *ast.Field) { + if walker.CurrentOperation == nil { + // When checking both Operation and Fragment, errors are duplicated when processing FragmentDefinition referenced from Operation + return + } + m.walker = walker + conflicts := m.findConflictsWithinSelectionSet(field.SelectionSet) + for _, conflict := range conflicts { + conflict.addFieldsConflictMessage(addError) + } + }) + observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) { + m.walker = walker + conflicts := m.findConflictsWithinSelectionSet(inlineFragment.SelectionSet) + for _, conflict := range conflicts { + conflict.addFieldsConflictMessage(addError) + } + }) + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + m.walker = walker + conflicts := m.findConflictsWithinSelectionSet(fragment.SelectionSet) + for _, conflict := range conflicts { + conflict.addFieldsConflictMessage(addError) + } + }) + }) +} + +type pairSet struct { + data map[string]map[string]bool +} + +func (pairSet *pairSet) Add(a *ast.FragmentSpread, b *ast.FragmentSpread, areMutuallyExclusive bool) { + add := func(a *ast.FragmentSpread, b *ast.FragmentSpread) { + m := pairSet.data[a.Name] + if m == nil { + m = make(map[string]bool) + pairSet.data[a.Name] = m + } + m[b.Name] = areMutuallyExclusive + } + add(a, b) + add(b, a) +} + +func (pairSet *pairSet) Has(a *ast.FragmentSpread, b *ast.FragmentSpread, areMutuallyExclusive bool) bool { + am, ok := pairSet.data[a.Name] + if !ok { + return false + } + result, ok := am[b.Name] + if !ok { + return false + } + + // areMutuallyExclusive being false is a superset of being true, + // hence if we want to know if this PairSet "has" these two with no + // exclusivity, we have to ensure it was added as such. + if !areMutuallyExclusive { + return !result + } + + return true +} + +type sequentialFieldsMap struct { + // We can't use map[string][]*ast.Field. because map is not stable... + seq []string + data map[string][]*ast.Field +} + +type fieldIterateEntry struct { + ResponseName string + Fields []*ast.Field +} + +func (m *sequentialFieldsMap) Push(responseName string, field *ast.Field) { + fields, ok := m.data[responseName] + if !ok { + m.seq = append(m.seq, responseName) + } + fields = append(fields, field) + m.data[responseName] = fields +} + +func (m *sequentialFieldsMap) Get(responseName string) ([]*ast.Field, bool) { + fields, ok := m.data[responseName] + return fields, ok +} + +func (m *sequentialFieldsMap) Iterator() [][]*ast.Field { + fieldsList := make([][]*ast.Field, 0, len(m.seq)) + for _, responseName := range m.seq { + fields := m.data[responseName] + fieldsList = append(fieldsList, fields) + } + return fieldsList +} + +func (m *sequentialFieldsMap) KeyValueIterator() []*fieldIterateEntry { + fieldEntriesList := make([]*fieldIterateEntry, 0, len(m.seq)) + for _, responseName := range m.seq { + fields := m.data[responseName] + fieldEntriesList = append(fieldEntriesList, &fieldIterateEntry{ + ResponseName: responseName, + Fields: fields, + }) + } + return fieldEntriesList +} + +type conflictMessageContainer struct { + Conflicts []*ConflictMessage +} + +type ConflictMessage struct { + Message string + ResponseName string + Names []string + SubMessage []*ConflictMessage + Position *ast.Position +} + +func (m *ConflictMessage) String(buf *bytes.Buffer) { + if len(m.SubMessage) == 0 { + buf.WriteString(m.Message) + return + } + + for idx, subMessage := range m.SubMessage { + buf.WriteString(`subfields "`) + buf.WriteString(subMessage.ResponseName) + buf.WriteString(`" conflict because `) + subMessage.String(buf) + if idx != len(m.SubMessage)-1 { + buf.WriteString(" and ") + } + } +} + +func (m *ConflictMessage) addFieldsConflictMessage(addError AddErrFunc) { + var buf bytes.Buffer + m.String(&buf) + addError( + Message(`Fields "%s" conflict because %s. Use different aliases on the fields to fetch both if this was intentional.`, m.ResponseName, buf.String()), + At(m.Position), + ) +} + +type overlappingFieldsCanBeMergedManager struct { + walker *Walker + + // per walker + comparedFragmentPairs pairSet + // cachedFieldsAndFragmentNames interface{} + + // per selectionSet + comparedFragments map[string]bool +} + +func (m *overlappingFieldsCanBeMergedManager) findConflictsWithinSelectionSet(selectionSet ast.SelectionSet) []*ConflictMessage { + if len(selectionSet) == 0 { + return nil + } + + fieldsMap, fragmentSpreads := getFieldsAndFragmentNames(selectionSet) + + var conflicts conflictMessageContainer + + // (A) Find find all conflicts "within" the fieldMap of this selection set. + // Note: this is the *only place* `collectConflictsWithin` is called. + m.collectConflictsWithin(&conflicts, fieldsMap) + + m.comparedFragments = make(map[string]bool) + for idx, fragmentSpreadA := range fragmentSpreads { + // (B) Then collect conflicts between these fieldMap and those represented by + // each spread fragment name found. + m.collectConflictsBetweenFieldsAndFragment(&conflicts, false, fieldsMap, fragmentSpreadA) + + for _, fragmentSpreadB := range fragmentSpreads[idx+1:] { + // (C) Then compare this fragment with all other fragments found in this + // selection set to collect conflicts between fragments spread together. + // This compares each item in the list of fragment names to every other + // item in that same list (except for itself). + m.collectConflictsBetweenFragments(&conflicts, false, fragmentSpreadA, fragmentSpreadB) + } + } + + return conflicts.Conflicts +} + +func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFieldsAndFragment(conflicts *conflictMessageContainer, areMutuallyExclusive bool, fieldsMap *sequentialFieldsMap, fragmentSpread *ast.FragmentSpread) { + if m.comparedFragments[fragmentSpread.Name] { + return + } + m.comparedFragments[fragmentSpread.Name] = true + + if fragmentSpread.Definition == nil { + return + } + + fieldsMapB, fragmentSpreads := getFieldsAndFragmentNames(fragmentSpread.Definition.SelectionSet) + + // Do not compare a fragment's fieldMap to itself. + if reflect.DeepEqual(fieldsMap, fieldsMapB) { + return + } + + // (D) First collect any conflicts between the provided collection of fields + // and the collection of fields represented by the given fragment. + m.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsMap, fieldsMapB) + + // (E) Then collect any conflicts between the provided collection of fields + // and any fragment names found in the given fragment. + for _, fragmentSpread := range fragmentSpreads { + m.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsMap, fragmentSpread) + } +} + +func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetweenFragments(conflicts *conflictMessageContainer, areMutuallyExclusive bool, fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) { + + var check func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) + check = func(fragmentSpreadA *ast.FragmentSpread, fragmentSpreadB *ast.FragmentSpread) { + + if fragmentSpreadA.Name == fragmentSpreadB.Name { + return + } + + if m.comparedFragmentPairs.Has(fragmentSpreadA, fragmentSpreadB, areMutuallyExclusive) { + return + } + m.comparedFragmentPairs.Add(fragmentSpreadA, fragmentSpreadB, areMutuallyExclusive) + + if fragmentSpreadA.Definition == nil { + return + } + if fragmentSpreadB.Definition == nil { + return + } + + fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(fragmentSpreadA.Definition.SelectionSet) + fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(fragmentSpreadB.Definition.SelectionSet) + + // (F) First, collect all conflicts between these two collections of fields + // (not including any nested fragments). + m.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsMapA, fieldsMapB) + + // (G) Then collect conflicts between the first fragment and any nested + // fragments spread in the second fragment. + for _, fragmentSpread := range fragmentSpreadsB { + check(fragmentSpreadA, fragmentSpread) + } + // (G) Then collect conflicts between the second fragment and any nested + // fragments spread in the first fragment. + for _, fragmentSpread := range fragmentSpreadsA { + check(fragmentSpread, fragmentSpreadB) + } + } + + check(fragmentSpreadA, fragmentSpreadB) +} + +func (m *overlappingFieldsCanBeMergedManager) findConflictsBetweenSubSelectionSets(areMutuallyExclusive bool, selectionSetA ast.SelectionSet, selectionSetB ast.SelectionSet) *conflictMessageContainer { + var conflicts conflictMessageContainer + + fieldsMapA, fragmentSpreadsA := getFieldsAndFragmentNames(selectionSetA) + fieldsMapB, fragmentSpreadsB := getFieldsAndFragmentNames(selectionSetB) + + // (H) First, collect all conflicts between these two collections of field. + m.collectConflictsBetween(&conflicts, areMutuallyExclusive, fieldsMapA, fieldsMapB) + + // (I) Then collect conflicts between the first collection of fields and + // those referenced by each fragment name associated with the second. + for _, fragmentSpread := range fragmentSpreadsB { + m.comparedFragments = make(map[string]bool) + m.collectConflictsBetweenFieldsAndFragment(&conflicts, areMutuallyExclusive, fieldsMapA, fragmentSpread) + } + + // (I) Then collect conflicts between the second collection of fields and + // those referenced by each fragment name associated with the first. + for _, fragmentSpread := range fragmentSpreadsA { + m.comparedFragments = make(map[string]bool) + m.collectConflictsBetweenFieldsAndFragment(&conflicts, areMutuallyExclusive, fieldsMapB, fragmentSpread) + } + + // (J) Also collect conflicts between any fragment names by the first and + // fragment names by the second. This compares each item in the first set of + // names to each item in the second set of names. + for _, fragmentSpreadA := range fragmentSpreadsA { + for _, fragmentSpreadB := range fragmentSpreadsB { + m.collectConflictsBetweenFragments(&conflicts, areMutuallyExclusive, fragmentSpreadA, fragmentSpreadB) + } + } + + if len(conflicts.Conflicts) == 0 { + return nil + } + + return &conflicts +} + +func (m *overlappingFieldsCanBeMergedManager) collectConflictsWithin(conflicts *conflictMessageContainer, fieldsMap *sequentialFieldsMap) { + for _, fields := range fieldsMap.Iterator() { + for idx, fieldA := range fields { + for _, fieldB := range fields[idx+1:] { + conflict := m.findConflict(false, fieldA, fieldB) + if conflict != nil { + conflicts.Conflicts = append(conflicts.Conflicts, conflict) + } + } + } + } +} + +func (m *overlappingFieldsCanBeMergedManager) collectConflictsBetween(conflicts *conflictMessageContainer, parentFieldsAreMutuallyExclusive bool, fieldsMapA *sequentialFieldsMap, fieldsMapB *sequentialFieldsMap) { + for _, fieldsEntryA := range fieldsMapA.KeyValueIterator() { + fieldsB, ok := fieldsMapB.Get(fieldsEntryA.ResponseName) + if !ok { + continue + } + for _, fieldA := range fieldsEntryA.Fields { + for _, fieldB := range fieldsB { + conflict := m.findConflict(parentFieldsAreMutuallyExclusive, fieldA, fieldB) + if conflict != nil { + conflicts.Conflicts = append(conflicts.Conflicts, conflict) + } + } + } + } +} + +func (m *overlappingFieldsCanBeMergedManager) findConflict(parentFieldsAreMutuallyExclusive bool, fieldA *ast.Field, fieldB *ast.Field) *ConflictMessage { + if fieldA.Definition == nil || fieldA.ObjectDefinition == nil || fieldB.Definition == nil || fieldB.ObjectDefinition == nil { + return nil + } + + areMutuallyExclusive := parentFieldsAreMutuallyExclusive + if !areMutuallyExclusive { + tmp := fieldA.ObjectDefinition.Name != fieldB.ObjectDefinition.Name + tmp = tmp && fieldA.ObjectDefinition.Kind == ast.Object + tmp = tmp && fieldB.ObjectDefinition.Kind == ast.Object + areMutuallyExclusive = tmp + } + + fieldNameA := fieldA.Name + if fieldA.Alias != "" { + fieldNameA = fieldA.Alias + } + + if !areMutuallyExclusive { + // Two aliases must refer to the same field. + if fieldA.Name != fieldB.Name { + return &ConflictMessage{ + ResponseName: fieldNameA, + Message: fmt.Sprintf(`%s and %s are different fields`, fieldA.Name, fieldB.Name), + Position: fieldB.Position, + } + } + + // Two field calls must have the same arguments. + if !sameArguments(fieldA.Arguments, fieldB.Arguments) { + return &ConflictMessage{ + ResponseName: fieldNameA, + Message: "they have differing arguments", + Position: fieldB.Position, + } + } + } + + if doTypesConflict(m.walker, fieldA.Definition.Type, fieldB.Definition.Type) { + return &ConflictMessage{ + ResponseName: fieldNameA, + Message: fmt.Sprintf(`they return conflicting types %s and %s`, fieldA.Definition.Type.String(), fieldB.Definition.Type.String()), + Position: fieldB.Position, + } + } + + // Collect and compare sub-fields. Use the same "visited fragment names" list + // for both collections so fields in a fragment reference are never + // compared to themselves. + conflicts := m.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, fieldA.SelectionSet, fieldB.SelectionSet) + if conflicts == nil { + return nil + } + return &ConflictMessage{ + ResponseName: fieldNameA, + SubMessage: conflicts.Conflicts, + Position: fieldB.Position, + } +} + +func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool { + if len(args1) != len(args2) { + return false + } + for _, arg1 := range args1 { + for _, arg2 := range args2 { + if arg1.Name != arg2.Name { + return false + } + if !sameValue(arg1.Value, arg2.Value) { + return false + } + } + } + return true +} + +func sameValue(value1 *ast.Value, value2 *ast.Value) bool { + if value1.Kind != value2.Kind { + return false + } + if value1.Raw != value2.Raw { + return false + } + return true +} + +func doTypesConflict(walker *Walker, type1 *ast.Type, type2 *ast.Type) bool { + if type1.Elem != nil { + if type2.Elem != nil { + return doTypesConflict(walker, type1.Elem, type2.Elem) + } + return true + } + if type2.Elem != nil { + return true + } + if type1.NonNull && !type2.NonNull { + return true + } + if !type1.NonNull && type2.NonNull { + return true + } + + t1 := walker.Schema.Types[type1.NamedType] + t2 := walker.Schema.Types[type2.NamedType] + if (t1.Kind == ast.Scalar || t1.Kind == ast.Enum) && (t2.Kind == ast.Scalar || t2.Kind == ast.Enum) { + return t1.Name != t2.Name + } + + return false +} + +func getFieldsAndFragmentNames(selectionSet ast.SelectionSet) (*sequentialFieldsMap, []*ast.FragmentSpread) { + fieldsMap := sequentialFieldsMap{ + data: make(map[string][]*ast.Field), + } + var fragmentSpreads []*ast.FragmentSpread + + var walk func(selectionSet ast.SelectionSet) + walk = func(selectionSet ast.SelectionSet) { + for _, selection := range selectionSet { + switch selection := selection.(type) { + case *ast.Field: + responseName := selection.Name + if selection.Alias != "" { + responseName = selection.Alias + } + fieldsMap.Push(responseName, selection) + + case *ast.InlineFragment: + walk(selection.SelectionSet) + + case *ast.FragmentSpread: + fragmentSpreads = append(fragmentSpreads, selection) + } + } + } + walk(selectionSet) + + return &fieldsMap, fragmentSpreads +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/possible_fragment_spreads.go b/vendor/github.com/vektah/gqlparser/validator/rules/possible_fragment_spreads.go new file mode 100644 index 00000000..971decbf --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/possible_fragment_spreads.go @@ -0,0 +1,68 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("PossibleFragmentSpreads", func(observers *Events, addError AddErrFunc) { + + validate := func(walker *Walker, parentDef *ast.Definition, fragmentName string, emitError func()) { + if parentDef == nil { + return + } + + var parentDefs []*ast.Definition + switch parentDef.Kind { + case ast.Object: + parentDefs = []*ast.Definition{parentDef} + case ast.Interface, ast.Union: + parentDefs = walker.Schema.GetPossibleTypes(parentDef) + default: + panic("unexpected type") + } + + fragmentDefType := walker.Schema.Types[fragmentName] + if fragmentDefType == nil { + return + } + if !fragmentDefType.IsCompositeType() { + // checked by FragmentsOnCompositeTypes + return + } + fragmentDefs := walker.Schema.GetPossibleTypes(fragmentDefType) + + for _, fragmentDef := range fragmentDefs { + for _, parentDef := range parentDefs { + if parentDef.Name == fragmentDef.Name { + return + } + } + } + + emitError() + } + + observers.OnInlineFragment(func(walker *Walker, inlineFragment *ast.InlineFragment) { + validate(walker, inlineFragment.ObjectDefinition, inlineFragment.TypeCondition, func() { + addError( + Message(`Fragment cannot be spread here as objects of type "%s" can never be of type "%s".`, inlineFragment.ObjectDefinition.Name, inlineFragment.TypeCondition), + At(inlineFragment.Position), + ) + }) + }) + + observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) { + if fragmentSpread.Definition == nil { + return + } + validate(walker, fragmentSpread.ObjectDefinition, fragmentSpread.Definition.TypeCondition, func() { + addError( + Message(`Fragment "%s" cannot be spread here as objects of type "%s" can never be of type "%s".`, fragmentSpread.Name, fragmentSpread.ObjectDefinition.Name, fragmentSpread.Definition.TypeCondition), + At(fragmentSpread.Position), + ) + }) + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/provided_required_arguments.go b/vendor/github.com/vektah/gqlparser/validator/rules/provided_required_arguments.go new file mode 100644 index 00000000..55791a6b --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/provided_required_arguments.go @@ -0,0 +1,63 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("ProvidedRequiredArguments", func(observers *Events, addError AddErrFunc) { + + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.Definition == nil { + return + } + + argDef: + for _, argDef := range field.Definition.Arguments { + if !argDef.Type.NonNull { + continue + } + if argDef.DefaultValue != nil { + continue + } + for _, arg := range field.Arguments { + if arg.Name == argDef.Name { + continue argDef + } + } + + addError( + Message(`Field "%s" argument "%s" of type "%s" is required but not provided.`, field.Name, argDef.Name, argDef.Type.String()), + At(field.Position), + ) + } + }) + + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + if directive.Definition == nil { + return + } + + argDef: + for _, argDef := range directive.Definition.Arguments { + if !argDef.Type.NonNull { + continue + } + if argDef.DefaultValue != nil { + continue + } + for _, arg := range directive.Arguments { + if arg.Name == argDef.Name { + continue argDef + } + } + + addError( + Message(`Directive "@%s" argument "%s" of type "%s" is required but not provided.`, directive.Definition.Name, argDef.Name, argDef.Type.String()), + At(directive.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/scalar_leafs.go b/vendor/github.com/vektah/gqlparser/validator/rules/scalar_leafs.go new file mode 100644 index 00000000..bb961f44 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/scalar_leafs.go @@ -0,0 +1,36 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("ScalarLeafs", func(observers *Events, addError AddErrFunc) { + observers.OnField(func(walker *Walker, field *ast.Field) { + if field.Definition == nil { + return + } + + fieldType := walker.Schema.Types[field.Definition.Type.Name()] + if fieldType == nil { + return + } + + if fieldType.IsLeafType() && len(field.SelectionSet) > 0 { + addError( + Message(`Field "%s" must not have a selection since type "%s" has no subfields.`, field.Name, fieldType.Name), + At(field.Position), + ) + } + + if !fieldType.IsLeafType() && len(field.SelectionSet) == 0 { + addError( + Message(`Field "%s" of type "%s" must have a selection of subfields.`, field.Name, field.Definition.Type.String()), + Suggestf(`"%s { ... }"`, field.Name), + At(field.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/single_field_subscriptions.go b/vendor/github.com/vektah/gqlparser/validator/rules/single_field_subscriptions.go new file mode 100644 index 00000000..53003c11 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/single_field_subscriptions.go @@ -0,0 +1,30 @@ +package validator + +import ( + "strconv" + + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("SingleFieldSubscriptions", func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + if operation.Operation != ast.Subscription { + return + } + + if len(operation.SelectionSet) > 1 { + name := "Anonymous Subscription" + if operation.Name != "" { + name = `Subscription ` + strconv.Quote(operation.Name) + } + + addError( + Message(`%s must select only one top level field.`, name), + At(operation.SelectionSet[1].GetPosition()), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/unique_argument_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/unique_argument_names.go new file mode 100644 index 00000000..0ddcde72 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/unique_argument_names.go @@ -0,0 +1,33 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("UniqueArgumentNames", func(observers *Events, addError AddErrFunc) { + observers.OnField(func(walker *Walker, field *ast.Field) { + checkUniqueArgs(field.Arguments, addError) + }) + + observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + checkUniqueArgs(directive.Arguments, addError) + }) + }) +} + +func checkUniqueArgs(args ast.ArgumentList, addError AddErrFunc) { + knownArgNames := map[string]bool{} + + for _, arg := range args { + if knownArgNames[arg.Name] { + addError( + Message(`There can be only one argument named "%s".`, arg.Name), + At(arg.Position), + ) + } + + knownArgNames[arg.Name] = true + } +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/unique_directives_per_location.go b/vendor/github.com/vektah/gqlparser/validator/rules/unique_directives_per_location.go new file mode 100644 index 00000000..077c4687 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/unique_directives_per_location.go @@ -0,0 +1,24 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("UniqueDirectivesPerLocation", func(observers *Events, addError AddErrFunc) { + observers.OnDirectiveList(func(walker *Walker, directives []*ast.Directive) { + seen := map[string]bool{} + + for _, dir := range directives { + if seen[dir.Name] { + addError( + Message(`The directive "%s" can only be used once at this location.`, dir.Name), + At(dir.Position), + ) + } + seen[dir.Name] = true + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/unique_fragment_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/unique_fragment_names.go new file mode 100644 index 00000000..46a8b7c7 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/unique_fragment_names.go @@ -0,0 +1,22 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("UniqueFragmentNames", func(observers *Events, addError AddErrFunc) { + seenFragments := map[string]bool{} + + observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + if seenFragments[fragment.Name] { + addError( + Message(`There can be only one fragment named "%s".`, fragment.Name), + At(fragment.Position), + ) + } + seenFragments[fragment.Name] = true + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/unique_input_field_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/unique_input_field_names.go new file mode 100644 index 00000000..f254d588 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/unique_input_field_names.go @@ -0,0 +1,27 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("UniqueInputFieldNames", func(observers *Events, addError AddErrFunc) { + observers.OnValue(func(walker *Walker, value *ast.Value) { + if value.Kind != ast.ObjectValue { + return + } + + seen := map[string]bool{} + for _, field := range value.Children { + if seen[field.Name] { + addError( + Message(`There can be only one input field named "%s".`, field.Name), + At(field.Position), + ) + } + seen[field.Name] = true + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/unique_operation_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/unique_operation_names.go new file mode 100644 index 00000000..c1ab56be --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/unique_operation_names.go @@ -0,0 +1,22 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("UniqueOperationNames", func(observers *Events, addError AddErrFunc) { + seen := map[string]bool{} + + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + if seen[operation.Name] { + addError( + Message(`There can be only one operation named "%s".`, operation.Name), + At(operation.Position), + ) + } + seen[operation.Name] = true + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/unique_variable_names.go b/vendor/github.com/vektah/gqlparser/validator/rules/unique_variable_names.go new file mode 100644 index 00000000..70590a88 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/unique_variable_names.go @@ -0,0 +1,23 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("UniqueVariableNames", func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + seen := map[string]bool{} + for _, def := range operation.VariableDefinitions { + if seen[def.Variable] { + addError( + Message(`There can be only one variable named "%s".`, def.Variable), + At(def.Position), + ) + } + seen[def.Variable] = true + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/values_of_correct_type.go b/vendor/github.com/vektah/gqlparser/validator/rules/values_of_correct_type.go new file mode 100644 index 00000000..d64cc666 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/values_of_correct_type.go @@ -0,0 +1,130 @@ +package validator + +import ( + "fmt" + + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("ValuesOfCorrectType", func(observers *Events, addError AddErrFunc) { + observers.OnValue(func(walker *Walker, value *ast.Value) { + if value.Definition == nil || value.ExpectedType == nil { + return + } + + if value.Definition.Kind == ast.Scalar { + // Skip custom validating scalars + if !value.Definition.OneOf("Int", "Float", "String", "Boolean", "ID") { + return + } + } + + var possibleEnums []string + if value.Definition.Kind == ast.Enum { + for _, val := range value.Definition.EnumValues { + possibleEnums = append(possibleEnums, val.Name) + } + } + + rawVal, err := value.Value(nil) + if err != nil { + unexpectedTypeMessage(addError, value) + } + + switch value.Kind { + case ast.NullValue: + if value.ExpectedType.NonNull { + unexpectedTypeMessage(addError, value) + } + + case ast.ListValue: + if value.ExpectedType.Elem == nil { + unexpectedTypeMessage(addError, value) + return + } + + case ast.IntValue: + if !value.Definition.OneOf("Int", "Float", "ID") { + unexpectedTypeMessage(addError, value) + } + + case ast.FloatValue: + if !value.Definition.OneOf("Float") { + unexpectedTypeMessage(addError, value) + } + + case ast.StringValue, ast.BlockValue: + if value.Definition.Kind == ast.Enum { + rawValStr := fmt.Sprint(rawVal) + addError( + Message("Expected type %s, found %s.", value.ExpectedType.String(), value.String()), + SuggestListUnquoted("Did you mean the enum value", rawValStr, possibleEnums), + At(value.Position), + ) + } else if !value.Definition.OneOf("String", "ID") { + unexpectedTypeMessage(addError, value) + } + + case ast.EnumValue: + if value.Definition.Kind != ast.Enum || value.Definition.EnumValues.ForName(value.Raw) == nil { + rawValStr := fmt.Sprint(rawVal) + addError( + Message("Expected type %s, found %s.", value.ExpectedType.String(), value.String()), + SuggestListUnquoted("Did you mean the enum value", rawValStr, possibleEnums), + At(value.Position), + ) + } + + case ast.BooleanValue: + if !value.Definition.OneOf("Boolean") { + unexpectedTypeMessage(addError, value) + } + + case ast.ObjectValue: + + for _, field := range value.Definition.Fields { + if field.Type.NonNull { + fieldValue := value.Children.ForName(field.Name) + if fieldValue == nil && field.DefaultValue == nil { + addError( + Message("Field %s.%s of required type %s was not provided.", value.Definition.Name, field.Name, field.Type.String()), + At(value.Position), + ) + continue + } + } + } + + for _, fieldValue := range value.Children { + if value.Definition.Fields.ForName(fieldValue.Name) == nil { + var suggestions []string + for _, fieldValue := range value.Definition.Fields { + suggestions = append(suggestions, fieldValue.Name) + } + + addError( + Message(`Field "%s" is not defined by type %s.`, fieldValue.Name, value.Definition.Name), + SuggestListUnquoted("Did you mean", fieldValue.Name, suggestions), + At(fieldValue.Position), + ) + } + } + + case ast.Variable: + return + + default: + panic(fmt.Errorf("unhandled %T", value)) + } + }) + }) +} + +func unexpectedTypeMessage(addError AddErrFunc, v *ast.Value) { + addError( + Message("Expected type %s, found %s.", v.ExpectedType.String(), v.String()), + At(v.Position), + ) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/variables_are_input_types.go b/vendor/github.com/vektah/gqlparser/validator/rules/variables_are_input_types.go new file mode 100644 index 00000000..9d58ae1c --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/variables_are_input_types.go @@ -0,0 +1,28 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("VariablesAreInputTypes", func(observers *Events, addError AddErrFunc) { + observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + for _, def := range operation.VariableDefinitions { + if def.Definition == nil { + continue + } + if !def.Definition.IsInputType() { + addError( + Message( + `Variable "$%s" cannot be non-input type "%s".`, + def.Variable, + def.Type.String(), + ), + At(def.Position), + ) + } + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/rules/variables_in_allowed_position.go b/vendor/github.com/vektah/gqlparser/validator/rules/variables_in_allowed_position.go new file mode 100644 index 00000000..e6d97c9f --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/rules/variables_in_allowed_position.go @@ -0,0 +1,36 @@ +package validator + +import ( + "github.com/vektah/gqlparser/ast" + . "github.com/vektah/gqlparser/validator" +) + +func init() { + AddRule("VariablesInAllowedPosition", func(observers *Events, addError AddErrFunc) { + observers.OnValue(func(walker *Walker, value *ast.Value) { + if value.Kind != ast.Variable || value.ExpectedType == nil || value.VariableDefinition == nil || walker.CurrentOperation == nil { + return + } + + // todo: move me into walk + // If there is a default non nullable types can be null + if value.VariableDefinition.DefaultValue != nil && value.VariableDefinition.DefaultValue.Kind != ast.NullValue { + if value.ExpectedType.NonNull { + value.ExpectedType.NonNull = false + } + } + + if !value.VariableDefinition.Type.IsCompatible(value.ExpectedType) { + addError( + Message( + `Variable "%s" of type "%s" used in position expecting type "%s".`, + value, + value.VariableDefinition.Type.String(), + value.ExpectedType.String(), + ), + At(value.Position), + ) + } + }) + }) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/schema.go b/vendor/github.com/vektah/gqlparser/validator/schema.go new file mode 100644 index 00000000..8fa18d7e --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/schema.go @@ -0,0 +1,212 @@ +//go:generate go run ./inliner/inliner.go + +package validator + +import ( + "strconv" + + . "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" + "github.com/vektah/gqlparser/parser" +) + +func LoadSchema(inputs ...*Source) (*Schema, *gqlerror.Error) { + ast := &SchemaDocument{} + for _, input := range inputs { + inputAst, err := parser.ParseSchema(input) + if err != nil { + return nil, err + } + + ast.Merge(inputAst) + } + + schema := Schema{ + Types: map[string]*Definition{}, + Directives: map[string]*DirectiveDefinition{}, + PossibleTypes: map[string][]*Definition{}, + } + + for i, def := range ast.Definitions { + if schema.Types[def.Name] != nil { + return nil, gqlerror.ErrorPosf(def.Position, "Cannot redeclare type %s.", def.Name) + } + schema.Types[def.Name] = ast.Definitions[i] + + if def.Kind != Interface { + for _, intf := range def.Interfaces { + schema.AddPossibleType(intf, ast.Definitions[i]) + } + schema.AddPossibleType(def.Name, ast.Definitions[i]) + } + } + + for _, ext := range ast.Extensions { + def := schema.Types[ext.Name] + if def == nil { + return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because it does not exist.", ext.Name) + } + + if def.Kind != ext.Kind { + return nil, gqlerror.ErrorPosf(ext.Position, "Cannot extend type %s because the base type is a %s, not %s.", ext.Name, def.Kind, ext.Kind) + } + + def.Directives = append(def.Directives, ext.Directives...) + def.Interfaces = append(def.Interfaces, ext.Interfaces...) + def.Fields = append(def.Fields, ext.Fields...) + def.Types = append(def.Types, ext.Types...) + def.EnumValues = append(def.EnumValues, ext.EnumValues...) + } + + for i, dir := range ast.Directives { + if schema.Directives[dir.Name] != nil { + return nil, gqlerror.ErrorPosf(dir.Position, "Cannot redeclare directive %s.", dir.Name) + } + schema.Directives[dir.Name] = ast.Directives[i] + } + + if len(ast.Schema) > 1 { + return nil, gqlerror.ErrorPosf(ast.Schema[1].Position, "Cannot have multiple schema entry points, consider schema extensions instead.") + } + + if len(ast.Schema) == 1 { + for _, entrypoint := range ast.Schema[0].OperationTypes { + def := schema.Types[entrypoint.Type] + if def == nil { + return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type) + } + switch entrypoint.Operation { + case Query: + schema.Query = def + case Mutation: + schema.Mutation = def + case Subscription: + schema.Subscription = def + } + } + } + + for _, ext := range ast.SchemaExtension { + for _, entrypoint := range ext.OperationTypes { + def := schema.Types[entrypoint.Type] + if def == nil { + return nil, gqlerror.ErrorPosf(entrypoint.Position, "Schema root %s refers to a type %s that does not exist.", entrypoint.Operation, entrypoint.Type) + } + switch entrypoint.Operation { + case Query: + schema.Query = def + case Mutation: + schema.Mutation = def + case Subscription: + schema.Subscription = def + } + } + } + + for _, typ := range schema.Types { + err := validateDefinition(&schema, typ) + if err != nil { + return nil, err + } + } + + for _, dir := range schema.Directives { + err := validateDirective(&schema, dir) + if err != nil { + return nil, err + } + } + + if schema.Query == nil && schema.Types["Query"] != nil { + schema.Query = schema.Types["Query"] + } + + if schema.Mutation == nil && schema.Types["Mutation"] != nil { + schema.Mutation = schema.Types["Mutation"] + } + + if schema.Subscription == nil && schema.Types["Subscription"] != nil { + schema.Subscription = schema.Types["Subscription"] + } + + if schema.Query != nil { + schema.Query.Fields = append( + schema.Query.Fields, + &FieldDefinition{ + Name: "__schema", + Type: NonNullNamedType("__Schema", nil), + }, + &FieldDefinition{ + Name: "__type", + Type: NonNullNamedType("__Type", nil), + Arguments: ArgumentDefinitionList{ + {Name: "name", Type: NamedType("String", nil)}, + }, + }, + ) + } + + return &schema, nil +} + +func validateDirective(schema *Schema, def *DirectiveDefinition) *gqlerror.Error { + return validateArgs(schema, def.Arguments, def) +} + +func validateDefinition(schema *Schema, def *Definition) *gqlerror.Error { + for _, field := range def.Fields { + if err := validateTypeRef(schema, field.Type); err != nil { + return err + } + if err := validateArgs(schema, field.Arguments, nil); err != nil { + return err + } + if err := validateDirectives(schema, field.Directives, nil); err != nil { + return err + } + } + + for _, intf := range def.Interfaces { + intDef := schema.Types[intf] + if intDef == nil { + return gqlerror.ErrorPosf(def.Position, "Undefined type %s.", strconv.Quote(intf)) + } + if intDef.Kind != Interface { + return gqlerror.ErrorPosf(def.Position, "%s is a non interface type %s.", strconv.Quote(intf), intDef.Kind) + } + } + + return validateDirectives(schema, def.Directives, nil) +} + +func validateTypeRef(schema *Schema, typ *Type) *gqlerror.Error { + if schema.Types[typ.Name()] == nil { + return gqlerror.ErrorPosf(typ.Position, "Undefined type %s.", typ.Name()) + } + return nil +} + +func validateArgs(schema *Schema, args ArgumentDefinitionList, currentDirective *DirectiveDefinition) *gqlerror.Error { + for _, arg := range args { + if err := validateTypeRef(schema, arg.Type); err != nil { + return err + } + if err := validateDirectives(schema, arg.Directives, currentDirective); err != nil { + return err + } + } + return nil +} + +func validateDirectives(schema *Schema, dirs DirectiveList, currentDirective *DirectiveDefinition) *gqlerror.Error { + for _, dir := range dirs { + if currentDirective != nil && dir.Name == currentDirective.Name { + return gqlerror.ErrorPosf(dir.Position, "Directive %s cannot refer to itself.", currentDirective.Name) + } + if schema.Directives[dir.Name] == nil { + return gqlerror.ErrorPosf(dir.Position, "Undefined directive %s.", dir.Name) + } + dir.Definition = schema.Directives[dir.Name] + } + return nil +} diff --git a/vendor/github.com/vektah/gqlparser/validator/schema_test.yml b/vendor/github.com/vektah/gqlparser/validator/schema_test.yml new file mode 100644 index 00000000..59e7145c --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/schema_test.yml @@ -0,0 +1,152 @@ +types: + - name: cannot be redeclared + input: | + type A { + name: String + } + type A { + name: String + } + error: + message: "Cannot redeclare type A." + locations: [{line: 4, column: 6}] + +interfaces: + - name: must exist + input: | + type Thing implements Object { + id: ID! + } + + type Query { + Things: [Thing!]! + } + error: + message: 'Undefined type "Object".' + locations: [{line: 1, column: 6}] + + - name: must be an interface + input: | + type Thing implements Object { + id: ID! + } + + type Query { + Things: [Thing!]! + } + + type Object { + name: String + } + error: + message: '"Object" is a non interface type OBJECT.' + locations: [{line: 1, column: 6}] + +type extensions: + - name: cannot extend non existant types + input: | + extend type A { + name: String + } + error: + message: "Cannot extend type A because it does not exist." + locations: [{line: 1, column: 13}] + + - name: cannot extend incorret type existant types + input: | + scalar A + extend type A { + name: String + } + error: + message: "Cannot extend type A because the base type is a SCALAR, not OBJECT." + locations: [{line: 2, column: 13}] + +directives: + - name: cannot redeclare directives + input: | + directive @A on FIELD_DEFINITION + directive @A on FIELD_DEFINITION + error: + message: "Cannot redeclare directive A." + locations: [{line: 2, column: 12}] + + - name: must be declared + input: | + type User { + name: String @foo + } + error: + message: "Undefined directive foo." + locations: [{line: 2, column: 17}] + + - name: cannot be self-referential + input: | + directive @A(foo: Int! @A) on FIELD_DEFINITION + error: + message: "Directive A cannot refer to itself." + locations: [{line: 1, column: 25}] + +entry points: + - name: multiple schema entry points + input: | + schema { + query: Query + } + schema { + query: Query + } + scalar Query + error: + message: "Cannot have multiple schema entry points, consider schema extensions instead." + locations: [{line: 4, column: 8}] + + - name: Undefined schema entrypoint + input: | + schema { + query: Query + } + error: + message: "Schema root query refers to a type Query that does not exist." + locations: [{line: 2, column: 3}] + +entry point extensions: + - name: Undefined schema entrypoint + input: | + schema { + query: Query + } + scalar Query + extend schema { + mutation: Mutation + } + error: + message: "Schema root mutation refers to a type Mutation that does not exist." + locations: [{line: 6, column: 3}] + +type references: + - name: Field types + input: | + type User { + posts: Post + } + error: + message: "Undefined type Post." + locations: [{line: 2, column: 10}] + + - name: Arg types + input: | + type User { + posts(foo: FooBar): String + } + error: + message: "Undefined type FooBar." + locations: [{line: 2, column: 14}] + + - name: Directive arg types + input: | + directive @Foo(foo: FooBar) on FIELD_DEFINITION + + error: + message: "Undefined type FooBar." + locations: [{line: 1, column: 21}] diff --git a/vendor/github.com/vektah/gqlparser/validator/suggestionList.go b/vendor/github.com/vektah/gqlparser/validator/suggestionList.go new file mode 100644 index 00000000..f58d0fc2 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/suggestionList.go @@ -0,0 +1,69 @@ +package validator + +import ( + "sort" + "strings" + + "github.com/agnivade/levenshtein" +) + +// Given an invalid input string and a list of valid options, returns a filtered +// list of valid options sorted based on their similarity with the input. +func SuggestionList(input string, options []string) []string { + var results []string + optionsByDistance := map[string]int{} + + for _, option := range options { + distance := lexicalDistance(input, option) + threshold := calcThreshold(input, option) + if distance <= threshold { + results = append(results, option) + optionsByDistance[option] = distance + } + } + + sort.Slice(results, func(i, j int) bool { + return optionsByDistance[results[i]] < optionsByDistance[results[j]] + }) + return results +} + +func calcThreshold(a, b string) (threshold int) { + if len(a) >= len(b) { + threshold = len(a) / 2 + } else { + threshold = len(b) / 2 + } + if threshold < 1 { + threshold = 1 + } + return +} + +// Computes the lexical distance between strings A and B. +// +// The "distance" between two strings is given by counting the minimum number +// of edits needed to transform string A into string B. An edit can be an +// insertion, deletion, or substitution of a single character, or a swap of two +// adjacent characters. +// +// Includes a custom alteration from Damerau-Levenshtein to treat case changes +// as a single edit which helps identify mis-cased values with an edit distance +// of 1. +// +// This distance can be useful for detecting typos in input or sorting +func lexicalDistance(a, b string) int { + if a == b { + return 0 + } + + a = strings.ToLower(a) + b = strings.ToLower(b) + + // Any case change counts as a single edit + if a == b { + return 1 + } + + return levenshtein.ComputeDistance(a, b) +} diff --git a/vendor/github.com/vektah/gqlparser/validator/validator.go b/vendor/github.com/vektah/gqlparser/validator/validator.go new file mode 100644 index 00000000..bbacec6f --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/validator.go @@ -0,0 +1,44 @@ +package validator + +import ( + . "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" +) + +type AddErrFunc func(options ...ErrorOption) + +type ruleFunc func(observers *Events, addError AddErrFunc) + +type rule struct { + name string + rule ruleFunc +} + +var rules []rule + +// addRule to rule set. +// f is called once each time `Validate` is executed. +func AddRule(name string, f ruleFunc) { + rules = append(rules, rule{name: name, rule: f}) +} + +func Validate(schema *Schema, doc *QueryDocument) gqlerror.List { + var errs gqlerror.List + + observers := &Events{} + for i := range rules { + rule := rules[i] + rule.rule(observers, func(options ...ErrorOption) { + err := &gqlerror.Error{ + Rule: rule.name, + } + for _, o := range options { + o(err) + } + errs = append(errs, err) + }) + } + + Walk(schema, doc, observers) + return errs +} diff --git a/vendor/github.com/vektah/gqlparser/validator/vars.go b/vendor/github.com/vektah/gqlparser/validator/vars.go new file mode 100644 index 00000000..0743f5cc --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/vars.go @@ -0,0 +1,195 @@ +package validator + +import ( + "reflect" + + "fmt" + + "github.com/vektah/gqlparser/ast" + "github.com/vektah/gqlparser/gqlerror" +) + +var UnexpectedType = fmt.Errorf("Unexpected Type") + +// VariableValues coerces and validates variable values +func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, *gqlerror.Error) { + coercedVars := map[string]interface{}{} + + validator := varValidator{ + path: []interface{}{"variable"}, + schema: schema, + } + + for _, v := range op.VariableDefinitions { + validator.path = append(validator.path, v.Variable) + + if !v.Definition.IsInputType() { + return nil, gqlerror.ErrorPathf(validator.path, "must an input type") + } + + val, hasValue := variables[v.Variable] + if !hasValue { + if v.DefaultValue != nil { + var err error + val, err = v.DefaultValue.Value(nil) + if err != nil { + return nil, gqlerror.WrapPath(validator.path, err) + } + hasValue = true + } else if v.Type.NonNull { + return nil, gqlerror.ErrorPathf(validator.path, "must be defined") + } + } + + if hasValue { + if val == nil { + if v.Type.NonNull { + return nil, gqlerror.ErrorPathf(validator.path, "cannot be null") + } + coercedVars[v.Variable] = nil + } else { + rv := reflect.ValueOf(val) + if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface { + rv = rv.Elem() + } + + if err := validator.validateVarType(v.Type, rv); err != nil { + return nil, err + } + + coercedVars[v.Variable] = val + } + } + + validator.path = validator.path[0 : len(validator.path)-1] + } + + return coercedVars, nil +} + +type varValidator struct { + path []interface{} + schema *ast.Schema +} + +func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) *gqlerror.Error { + if typ.Elem != nil { + if val.Kind() != reflect.Slice { + return gqlerror.ErrorPathf(v.path, "must be an array") + } + + for i := 0; i < val.Len(); i++ { + v.path = append(v.path, i) + field := val.Index(i) + + if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface { + if typ.Elem.NonNull && field.IsNil() { + return gqlerror.ErrorPathf(v.path, "cannot be null") + } + field = field.Elem() + } + + if err := v.validateVarType(typ.Elem, field); err != nil { + return err + } + + v.path = v.path[0 : len(v.path)-1] + } + + return nil + } + + def := v.schema.Types[typ.NamedType] + if def == nil { + panic(fmt.Errorf("missing def for %s", typ.NamedType)) + } + + switch def.Kind { + case ast.Enum: + kind := val.Type().Kind() + if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String { + return nil + } + return gqlerror.ErrorPathf(v.path, "enums must be ints or strings") + case ast.Scalar: + kind := val.Type().Kind() + switch typ.NamedType { + case "Int": + if kind == reflect.String || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 { + return nil + } + case "Float": + if kind == reflect.String || kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 { + return nil + } + case "String": + if kind == reflect.String { + return nil + } + + case "Boolean": + if kind == reflect.Bool { + return nil + } + + case "ID": + if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String { + return nil + } + default: + // assume custom scalars are ok + return nil + } + return gqlerror.ErrorPathf(v.path, "cannot use %s as %s", kind.String(), typ.NamedType) + case ast.InputObject: + if val.Kind() != reflect.Map { + return gqlerror.ErrorPathf(v.path, "must be a %s", def.Name) + } + + // check for unknown fields + for _, name := range val.MapKeys() { + val.MapIndex(name) + fieldDef := def.Fields.ForName(name.String()) + v.path = append(v.path, name) + + if fieldDef == nil { + return gqlerror.ErrorPathf(v.path, "unknown field") + } + v.path = v.path[0 : len(v.path)-1] + } + + for _, fieldDef := range def.Fields { + v.path = append(v.path, fieldDef.Name) + + field := val.MapIndex(reflect.ValueOf(fieldDef.Name)) + if !field.IsValid() { + if fieldDef.Type.NonNull { + return gqlerror.ErrorPathf(v.path, "must be defined") + } + continue + } + + if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface { + if fieldDef.Type.NonNull && field.IsNil() { + return gqlerror.ErrorPathf(v.path, "cannot be null") + } + //allow null object field and skip it + if !fieldDef.Type.NonNull && field.IsNil() { + continue + } + field = field.Elem() + } + + err := v.validateVarType(fieldDef.Type, field) + if err != nil { + return err + } + + v.path = v.path[0 : len(v.path)-1] + } + default: + panic(fmt.Errorf("unsupported type %s", def.Kind)) + } + + return nil +} diff --git a/vendor/github.com/vektah/gqlparser/validator/walk.go b/vendor/github.com/vektah/gqlparser/validator/walk.go new file mode 100644 index 00000000..751ba1f1 --- /dev/null +++ b/vendor/github.com/vektah/gqlparser/validator/walk.go @@ -0,0 +1,286 @@ +package validator + +import ( + "context" + "fmt" + + "github.com/vektah/gqlparser/ast" +) + +type Events struct { + operationVisitor []func(walker *Walker, operation *ast.OperationDefinition) + field []func(walker *Walker, field *ast.Field) + fragment []func(walker *Walker, fragment *ast.FragmentDefinition) + inlineFragment []func(walker *Walker, inlineFragment *ast.InlineFragment) + fragmentSpread []func(walker *Walker, fragmentSpread *ast.FragmentSpread) + directive []func(walker *Walker, directive *ast.Directive) + directiveList []func(walker *Walker, directives []*ast.Directive) + value []func(walker *Walker, value *ast.Value) +} + +func (o *Events) OnOperation(f func(walker *Walker, operation *ast.OperationDefinition)) { + o.operationVisitor = append(o.operationVisitor, f) +} +func (o *Events) OnField(f func(walker *Walker, field *ast.Field)) { + o.field = append(o.field, f) +} +func (o *Events) OnFragment(f func(walker *Walker, fragment *ast.FragmentDefinition)) { + o.fragment = append(o.fragment, f) +} +func (o *Events) OnInlineFragment(f func(walker *Walker, inlineFragment *ast.InlineFragment)) { + o.inlineFragment = append(o.inlineFragment, f) +} +func (o *Events) OnFragmentSpread(f func(walker *Walker, fragmentSpread *ast.FragmentSpread)) { + o.fragmentSpread = append(o.fragmentSpread, f) +} +func (o *Events) OnDirective(f func(walker *Walker, directive *ast.Directive)) { + o.directive = append(o.directive, f) +} +func (o *Events) OnDirectiveList(f func(walker *Walker, directives []*ast.Directive)) { + o.directiveList = append(o.directiveList, f) +} +func (o *Events) OnValue(f func(walker *Walker, value *ast.Value)) { + o.value = append(o.value, f) +} + +func Walk(schema *ast.Schema, document *ast.QueryDocument, observers *Events) { + w := Walker{ + Observers: observers, + Schema: schema, + Document: document, + } + + w.walk() +} + +type Walker struct { + Context context.Context + Observers *Events + Schema *ast.Schema + Document *ast.QueryDocument + + validatedFragmentSpreads map[string]bool + CurrentOperation *ast.OperationDefinition +} + +func (w *Walker) walk() { + for _, child := range w.Document.Operations { + w.validatedFragmentSpreads = make(map[string]bool) + w.walkOperation(child) + } + for _, child := range w.Document.Fragments { + w.validatedFragmentSpreads = make(map[string]bool) + w.walkFragment(child) + } +} + +func (w *Walker) walkOperation(operation *ast.OperationDefinition) { + w.CurrentOperation = operation + for _, varDef := range operation.VariableDefinitions { + varDef.Definition = w.Schema.Types[varDef.Type.Name()] + + if varDef.DefaultValue != nil { + varDef.DefaultValue.ExpectedType = varDef.Type + varDef.DefaultValue.Definition = w.Schema.Types[varDef.Type.Name()] + } + } + + var def *ast.Definition + var loc ast.DirectiveLocation + switch operation.Operation { + case ast.Query, "": + def = w.Schema.Query + loc = ast.LocationQuery + case ast.Mutation: + def = w.Schema.Mutation + loc = ast.LocationMutation + case ast.Subscription: + def = w.Schema.Subscription + loc = ast.LocationSubscription + } + + w.walkDirectives(def, operation.Directives, loc) + + for _, varDef := range operation.VariableDefinitions { + if varDef.DefaultValue != nil { + w.walkValue(varDef.DefaultValue) + } + } + + w.walkSelectionSet(def, operation.SelectionSet) + + for _, v := range w.Observers.operationVisitor { + v(w, operation) + } + w.CurrentOperation = nil +} + +func (w *Walker) walkFragment(it *ast.FragmentDefinition) { + def := w.Schema.Types[it.TypeCondition] + + it.Definition = def + + w.walkDirectives(def, it.Directives, ast.LocationFragmentDefinition) + w.walkSelectionSet(def, it.SelectionSet) + + for _, v := range w.Observers.fragment { + v(w, it) + } +} + +func (w *Walker) walkDirectives(parentDef *ast.Definition, directives []*ast.Directive, location ast.DirectiveLocation) { + for _, dir := range directives { + def := w.Schema.Directives[dir.Name] + dir.Definition = def + dir.ParentDefinition = parentDef + dir.Location = location + + for _, arg := range dir.Arguments { + var argDef *ast.ArgumentDefinition + if def != nil { + argDef = def.Arguments.ForName(arg.Name) + } + + w.walkArgument(argDef, arg) + } + + for _, v := range w.Observers.directive { + v(w, dir) + } + } + + for _, v := range w.Observers.directiveList { + v(w, directives) + } +} + +func (w *Walker) walkValue(value *ast.Value) { + if value.Kind == ast.Variable && w.CurrentOperation != nil { + value.VariableDefinition = w.CurrentOperation.VariableDefinitions.ForName(value.Raw) + if value.VariableDefinition != nil { + value.VariableDefinition.Used = true + } + } + + if value.Kind == ast.ObjectValue { + for _, child := range value.Children { + if value.Definition != nil { + fieldDef := value.Definition.Fields.ForName(child.Name) + if fieldDef != nil { + child.Value.ExpectedType = fieldDef.Type + child.Value.Definition = w.Schema.Types[fieldDef.Type.Name()] + } + } + w.walkValue(child.Value) + } + } + + if value.Kind == ast.ListValue { + for _, child := range value.Children { + if value.ExpectedType != nil && value.ExpectedType.Elem != nil { + child.Value.ExpectedType = value.ExpectedType.Elem + child.Value.Definition = value.Definition + } + + w.walkValue(child.Value) + } + } + + for _, v := range w.Observers.value { + v(w, value) + } +} + +func (w *Walker) walkArgument(argDef *ast.ArgumentDefinition, arg *ast.Argument) { + if argDef != nil { + arg.Value.ExpectedType = argDef.Type + arg.Value.Definition = w.Schema.Types[argDef.Type.Name()] + } + + w.walkValue(arg.Value) +} + +func (w *Walker) walkSelectionSet(parentDef *ast.Definition, it ast.SelectionSet) { + for _, child := range it { + w.walkSelection(parentDef, child) + } +} + +func (w *Walker) walkSelection(parentDef *ast.Definition, it ast.Selection) { + switch it := it.(type) { + case *ast.Field: + var def *ast.FieldDefinition + if it.Name == "__typename" { + def = &ast.FieldDefinition{ + Name: "__typename", + Type: ast.NamedType("String", nil), + } + } else if parentDef != nil { + def = parentDef.Fields.ForName(it.Name) + } + + it.Definition = def + it.ObjectDefinition = parentDef + + var nextParentDef *ast.Definition + if def != nil { + nextParentDef = w.Schema.Types[def.Type.Name()] + } + + for _, arg := range it.Arguments { + var argDef *ast.ArgumentDefinition + if def != nil { + argDef = def.Arguments.ForName(arg.Name) + } + + w.walkArgument(argDef, arg) + } + + w.walkDirectives(nextParentDef, it.Directives, ast.LocationField) + w.walkSelectionSet(nextParentDef, it.SelectionSet) + + for _, v := range w.Observers.field { + v(w, it) + } + + case *ast.InlineFragment: + it.ObjectDefinition = parentDef + + nextParentDef := parentDef + if it.TypeCondition != "" { + nextParentDef = w.Schema.Types[it.TypeCondition] + } + + w.walkDirectives(nextParentDef, it.Directives, ast.LocationInlineFragment) + w.walkSelectionSet(nextParentDef, it.SelectionSet) + + for _, v := range w.Observers.inlineFragment { + v(w, it) + } + + case *ast.FragmentSpread: + def := w.Document.Fragments.ForName(it.Name) + it.Definition = def + it.ObjectDefinition = parentDef + + var nextParentDef *ast.Definition + if def != nil { + nextParentDef = w.Schema.Types[def.TypeCondition] + } + + w.walkDirectives(nextParentDef, it.Directives, ast.LocationFragmentSpread) + + if def != nil && !w.validatedFragmentSpreads[def.Name] { + // prevent inifinite recursion + w.validatedFragmentSpreads[def.Name] = true + w.walkSelectionSet(nextParentDef, def.SelectionSet) + } + + for _, v := range w.Observers.fragmentSpread { + v(w, it) + } + + default: + panic(fmt.Errorf("unsupported %T", it)) + } +} |