diff options
Diffstat (limited to 'vendor')
52 files changed, 14823 insertions, 0 deletions
diff --git a/vendor/github.com/graphql-go/graphql/.gitignore b/vendor/github.com/graphql-go/graphql/.gitignore new file mode 100644 index 00000000..3d725761 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/.gitignore @@ -0,0 +1,2 @@ +.DS_Store +.idea
\ No newline at end of file diff --git a/vendor/github.com/graphql-go/graphql/.travis.yml b/vendor/github.com/graphql-go/graphql/.travis.yml new file mode 100644 index 00000000..cae10b97 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/.travis.yml @@ -0,0 +1,20 @@ +language: go + +go: + - 1.8.x + - 1.9.x + - 1.10.x + - tip + +matrix: + allow_failures: + - go: tip + fast_finish: true + +before_install: + - go get github.com/axw/gocov/gocov + - go get github.com/mattn/goveralls + +script: + - $HOME/gopath/bin/goveralls -race -service=travis-ci + - go vet ./... diff --git a/vendor/github.com/graphql-go/graphql/CONTRIBUTING.md b/vendor/github.com/graphql-go/graphql/CONTRIBUTING.md new file mode 100644 index 00000000..8174bc68 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/CONTRIBUTING.md @@ -0,0 +1,139 @@ +# Contributing to graphql + +This document is based on the [Node.js contribution guidelines](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md) + +## Chat room + +[![Join the chat at https://gitter.im/graphql-go/graphql](https://badges.gitter.im/Join%20Chat.svg)] +(https://gitter.im/graphql-go/graphql?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Feel free to participate in the chat room for informal discussions and queries. + +Just drop by and say hi! + +## Issue Contributions + +When opening new issues or commenting on existing issues on this repository +please make sure discussions are related to concrete technical issues with the +`graphql` implementation. + +## Code Contributions + +The `graphql` project welcomes new contributors. + +This document will guide you through the contribution process. + +What do you want to contribute? + +- I want to otherwise correct or improve the docs or examples +- I want to report a bug +- I want to add some feature or functionality to an existing hardware platform +- I want to add support for a new hardware platform + +Descriptions for each of these will eventually be provided below. + +## General Guidelines +* Reading up on [CodeReviewComments](https://github.com/golang/go/wiki/CodeReviewComments) would be a great start. +* Submit a Github Pull Request to the appropriate branch and ideally discuss the changes with us in the [chat room](#chat-room). +* We will look at the patch, test it out, and give you feedback. +* Avoid doing minor whitespace changes, renaming, etc. along with merged content. These will be done by the maintainers from time to time but they can complicate merges and should be done separately. +* Take care to maintain the existing coding style. +* Always `golint` and `go fmt` your code. +* Add unit tests for any new or changed functionality, especially for public APIs. +* Run `go test` before submitting a PR. +* For git help see [progit](http://git-scm.com/book) which is an awesome (and free) book on git + + +## Creating Pull Requests +Because `graphql` makes use of self-referencing import paths, you will want +to implement the local copy of your fork as a remote on your copy of the +original `graphql` repo. Katrina Owen has [an excellent post on this workflow](https://splice.com/blog/contributing-open-source-git-repositories-go/). + +The basics are as follows: + +1. Fork the project via the GitHub UI + +2. `go get` the upstream repo and set it up as the `upstream` remote and your own repo as the `origin` remote: + +```bash +$ go get github.com/graphql-go/graphql +$ cd $GOPATH/src/github.com/graphql-go/graphql +$ git remote rename origin upstream +$ git remote add origin git@github.com/YOUR_GITHUB_NAME/graphql +``` +All import paths should now work fine assuming that you've got the +proper branch checked out. + + +## Landing Pull Requests +(This is for committers only. If you are unsure whether you are a committer, you are not.) + +1. Set the contributor's fork as an upstream on your checkout + + ```git remote add contrib1 https://github.com/contrib1/graphql``` + +2. Fetch the contributor's repo + + ```git fetch contrib1``` + +3. Checkout a copy of the PR branch + + ```git checkout pr-1234 --track contrib1/branch-for-pr-1234``` + +4. Review the PR as normal + +5. Land when you're ready via the GitHub UI + +## Developer's Certificate of Origin 1.0 + +By making a contribution to this project, I certify that: + +* (a) The contribution was created in whole or in part by me and I +have the right to submit it under the open source license indicated +in the file; or +* (b) The contribution is based upon previous work that, to the best +of my knowledge, is covered under an appropriate open source license +and I have the right under that license to submit that work with +modifications, whether created in whole or in part by me, under the +same open source license (unless I am permitted to submit under a +different license), as indicated in the file; or +* (c) The contribution was provided directly to me by some other +person who certified (a), (b) or (c) and I have not modified it. + + +## Code of Conduct + +This Code of Conduct is adapted from [Rust's wonderful +CoC](http://www.rust-lang.org/conduct.html). + +* We are committed to providing a friendly, safe and welcoming +environment for all, regardless of gender, sexual orientation, +disability, ethnicity, religion, or similar personal characteristic. +* Please avoid using overtly sexual nicknames or other nicknames that +might detract from a friendly, safe and welcoming environment for +all. +* Please be kind and courteous. There's no need to be mean or rude. +* Respect that people have differences of opinion and that every +design or implementation choice carries a trade-off and numerous +costs. There is seldom a right answer. +* Please keep unstructured critique to a minimum. If you have solid +ideas you want to experiment with, make a fork and see how it works. +* We will exclude you from interaction if you insult, demean or harass +anyone. That is not welcome behaviour. We interpret the term +"harassment" as including the definition in the [Citizen Code of +Conduct](http://citizencodeofconduct.org/); if you have any lack of +clarity about what might be included in that concept, please read +their definition. In particular, we don't tolerate behavior that +excludes people in socially marginalized groups. +* Private harassment is also unacceptable. No matter who you are, if +you feel you have been or are being harassed or made uncomfortable +by a community member, please contact one of the channel ops or any +of the TC members immediately with a capture (log, photo, email) of +the harassment if possible. Whether you're a regular contributor or +a newcomer, we care about making this community a safe place for you +and we've got your back. +* Likewise any spamming, trolling, flaming, baiting or other +attention-stealing behaviour is not welcome. +* Avoid the use of personal pronouns in code comments or +documentation. There is no need to address persons when explaining +code (e.g. "When the developer") diff --git a/vendor/github.com/graphql-go/graphql/LICENSE b/vendor/github.com/graphql-go/graphql/LICENSE new file mode 100644 index 00000000..71102ac3 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Chris Ramón + +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/graphql-go/graphql/README.md b/vendor/github.com/graphql-go/graphql/README.md new file mode 100644 index 00000000..5aff88d6 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/README.md @@ -0,0 +1,75 @@ +# graphql [![Build Status](https://travis-ci.org/graphql-go/graphql.svg)](https://travis-ci.org/graphql-go/graphql) [![GoDoc](https://godoc.org/graphql.co/graphql?status.svg)](https://godoc.org/github.com/graphql-go/graphql) [![Coverage Status](https://coveralls.io/repos/graphql-go/graphql/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-go/graphql?branch=master) [![Join the chat at https://gitter.im/graphql-go/graphql](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/graphql-go/graphql?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +An implementation of GraphQL in Go. Follows the official reference implementation [`graphql-js`](https://github.com/graphql/graphql-js). + +Supports: queries, mutations & subscriptions. + +### Documentation + +godoc: https://godoc.org/github.com/graphql-go/graphql + +### Getting Started + +To install the library, run: +```bash +go get github.com/graphql-go/graphql +``` + +The following is a simple example which defines a schema with a single `hello` string-type field and a `Resolve` method which returns the string `world`. A GraphQL query is performed against this schema with the resulting output printed in JSON format. + +```go +package main + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/graphql-go/graphql" +) + +func main() { + // Schema + fields := graphql.Fields{ + "hello": &graphql.Field{ + Type: graphql.String, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + return "world", nil + }, + }, + } + rootQuery := graphql.ObjectConfig{Name: "RootQuery", Fields: fields} + schemaConfig := graphql.SchemaConfig{Query: graphql.NewObject(rootQuery)} + schema, err := graphql.NewSchema(schemaConfig) + if err != nil { + log.Fatalf("failed to create new schema, error: %v", err) + } + + // Query + query := ` + { + hello + } + ` + params := graphql.Params{Schema: schema, RequestString: query} + r := graphql.Do(params) + if len(r.Errors) > 0 { + log.Fatalf("failed to execute graphql operation, errors: %+v", r.Errors) + } + rJSON, _ := json.Marshal(r) + fmt.Printf("%s \n", rJSON) // {“data”:{“hello”:”world”}} +} +``` +For more complex examples, refer to the [examples/](https://github.com/graphql-go/graphql/tree/master/examples/) directory and [graphql_test.go](https://github.com/graphql-go/graphql/blob/master/graphql_test.go). + +### Third Party Libraries +| Name | Author | Description | +|:-------------:|:-------------:|:------------:| +| [graphql-go-handler](https://github.com/graphql-go/graphql-go-handler) | [Hafiz Ismail](https://github.com/sogko) | Middleware to handle GraphQL queries through HTTP requests. | +| [graphql-relay-go](https://github.com/graphql-go/graphql-relay-go) | [Hafiz Ismail](https://github.com/sogko) | Lib to construct a graphql-go server supporting react-relay. | +| [golang-relay-starter-kit](https://github.com/sogko/golang-relay-starter-kit) | [Hafiz Ismail](https://github.com/sogko) | Barebones starting point for a Relay application with Golang GraphQL server. | +| [dataloader](https://github.com/nicksrandall/dataloader) | [Nick Randall](https://github.com/nicksrandall) | [DataLoader](https://github.com/facebook/dataloader) implementation in Go. | + +### Blog Posts +- [Golang + GraphQL + Relay](http://wehavefaces.net/) + diff --git a/vendor/github.com/graphql-go/graphql/definition.go b/vendor/github.com/graphql-go/graphql/definition.go new file mode 100644 index 00000000..dffea080 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/definition.go @@ -0,0 +1,1340 @@ +package graphql + +import ( + "context" + "fmt" + "reflect" + "regexp" + + "github.com/graphql-go/graphql/language/ast" +) + +// Type interface for all of the possible kinds of GraphQL types +type Type interface { + Name() string + Description() string + String() string + Error() error +} + +var _ Type = (*Scalar)(nil) +var _ Type = (*Object)(nil) +var _ Type = (*Interface)(nil) +var _ Type = (*Union)(nil) +var _ Type = (*Enum)(nil) +var _ Type = (*InputObject)(nil) +var _ Type = (*List)(nil) +var _ Type = (*NonNull)(nil) +var _ Type = (*Argument)(nil) + +// Input interface for types that may be used as input types for arguments and directives. +type Input interface { + Name() string + Description() string + String() string + Error() error +} + +var _ Input = (*Scalar)(nil) +var _ Input = (*Enum)(nil) +var _ Input = (*InputObject)(nil) +var _ Input = (*List)(nil) +var _ Input = (*NonNull)(nil) + +// IsInputType determines if given type is a GraphQLInputType +func IsInputType(ttype Type) bool { + named := GetNamed(ttype) + if _, ok := named.(*Scalar); ok { + return true + } + if _, ok := named.(*Enum); ok { + return true + } + if _, ok := named.(*InputObject); ok { + return true + } + return false +} + +// IsOutputType determines if given type is a GraphQLOutputType +func IsOutputType(ttype Type) bool { + name := GetNamed(ttype) + if _, ok := name.(*Scalar); ok { + return true + } + if _, ok := name.(*Object); ok { + return true + } + if _, ok := name.(*Interface); ok { + return true + } + if _, ok := name.(*Union); ok { + return true + } + if _, ok := name.(*Enum); ok { + return true + } + return false +} + +// Leaf interface for types that may be leaf values +type Leaf interface { + Name() string + Description() string + String() string + Error() error + Serialize(value interface{}) interface{} +} + +var _ Leaf = (*Scalar)(nil) +var _ Leaf = (*Enum)(nil) + +// IsLeafType determines if given type is a leaf value +func IsLeafType(ttype Type) bool { + named := GetNamed(ttype) + if _, ok := named.(*Scalar); ok { + return true + } + if _, ok := named.(*Enum); ok { + return true + } + return false +} + +// Output interface for types that may be used as output types as the result of fields. +type Output interface { + Name() string + Description() string + String() string + Error() error +} + +var _ Output = (*Scalar)(nil) +var _ Output = (*Object)(nil) +var _ Output = (*Interface)(nil) +var _ Output = (*Union)(nil) +var _ Output = (*Enum)(nil) +var _ Output = (*List)(nil) +var _ Output = (*NonNull)(nil) + +// Composite interface for types that may describe the parent context of a selection set. +type Composite interface { + Name() string + Description() string + String() string + Error() error +} + +var _ Composite = (*Object)(nil) +var _ Composite = (*Interface)(nil) +var _ Composite = (*Union)(nil) + +// IsCompositeType determines if given type is a GraphQLComposite type +func IsCompositeType(ttype interface{}) bool { + if _, ok := ttype.(*Object); ok { + return true + } + if _, ok := ttype.(*Interface); ok { + return true + } + if _, ok := ttype.(*Union); ok { + return true + } + return false +} + +// Abstract interface for types that may describe the parent context of a selection set. +type Abstract interface { + Name() string +} + +var _ Abstract = (*Interface)(nil) +var _ Abstract = (*Union)(nil) + +func IsAbstractType(ttype interface{}) bool { + if _, ok := ttype.(*Interface); ok { + return true + } + if _, ok := ttype.(*Union); ok { + return true + } + return false +} + +// Nullable interface for types that can accept null as a value. +type Nullable interface { +} + +var _ Nullable = (*Scalar)(nil) +var _ Nullable = (*Object)(nil) +var _ Nullable = (*Interface)(nil) +var _ Nullable = (*Union)(nil) +var _ Nullable = (*Enum)(nil) +var _ Nullable = (*InputObject)(nil) +var _ Nullable = (*List)(nil) + +// GetNullable returns the Nullable type of the given GraphQL type +func GetNullable(ttype Type) Nullable { + if ttype, ok := ttype.(*NonNull); ok { + return ttype.OfType + } + return ttype +} + +// Named interface for types that do not include modifiers like List or NonNull. +type Named interface { + String() string +} + +var _ Named = (*Scalar)(nil) +var _ Named = (*Object)(nil) +var _ Named = (*Interface)(nil) +var _ Named = (*Union)(nil) +var _ Named = (*Enum)(nil) +var _ Named = (*InputObject)(nil) + +// GetNamed returns the Named type of the given GraphQL type +func GetNamed(ttype Type) Named { + unmodifiedType := ttype + for { + if ttype, ok := unmodifiedType.(*List); ok { + unmodifiedType = ttype.OfType + continue + } + if ttype, ok := unmodifiedType.(*NonNull); ok { + unmodifiedType = ttype.OfType + continue + } + break + } + return unmodifiedType +} + +// Scalar Type Definition +// +// The leaf values of any request and input values to arguments are +// Scalars (or Enums) and are defined with a name and a series of functions +// used to parse input from ast or variables and to ensure validity. +// +// Example: +// +// var OddType = new Scalar({ +// name: 'Odd', +// serialize(value) { +// return value % 2 === 1 ? value : null; +// } +// }); +// +type Scalar struct { + PrivateName string `json:"name"` + PrivateDescription string `json:"description"` + + scalarConfig ScalarConfig + err error +} + +// SerializeFn is a function type for serializing a GraphQLScalar type value +type SerializeFn func(value interface{}) interface{} + +// ParseValueFn is a function type for parsing the value of a GraphQLScalar type +type ParseValueFn func(value interface{}) interface{} + +// ParseLiteralFn is a function type for parsing the literal value of a GraphQLScalar type +type ParseLiteralFn func(valueAST ast.Value) interface{} + +// ScalarConfig options for creating a new GraphQLScalar +type ScalarConfig struct { + Name string `json:"name"` + Description string `json:"description"` + Serialize SerializeFn + ParseValue ParseValueFn + ParseLiteral ParseLiteralFn +} + +// NewScalar creates a new GraphQLScalar +func NewScalar(config ScalarConfig) *Scalar { + st := &Scalar{} + err := invariant(config.Name != "", "Type must be named.") + if err != nil { + st.err = err + return st + } + + err = assertValidName(config.Name) + if err != nil { + st.err = err + return st + } + + st.PrivateName = config.Name + st.PrivateDescription = config.Description + + err = invariantf( + config.Serialize != nil, + `%v must provide "serialize" function. If this custom Scalar is `+ + `also used as an input type, ensure "parseValue" and "parseLiteral" `+ + `functions are also provided.`, st, + ) + if err != nil { + st.err = err + return st + } + if config.ParseValue != nil || config.ParseLiteral != nil { + err = invariantf( + config.ParseValue != nil && config.ParseLiteral != nil, + `%v must provide both "parseValue" and "parseLiteral" functions.`, st, + ) + if err != nil { + st.err = err + return st + } + } + + st.scalarConfig = config + return st +} +func (st *Scalar) Serialize(value interface{}) interface{} { + if st.scalarConfig.Serialize == nil { + return value + } + return st.scalarConfig.Serialize(value) +} +func (st *Scalar) ParseValue(value interface{}) interface{} { + if st.scalarConfig.ParseValue == nil { + return value + } + return st.scalarConfig.ParseValue(value) +} +func (st *Scalar) ParseLiteral(valueAST ast.Value) interface{} { + if st.scalarConfig.ParseLiteral == nil { + return nil + } + return st.scalarConfig.ParseLiteral(valueAST) +} +func (st *Scalar) Name() string { + return st.PrivateName +} +func (st *Scalar) Description() string { + return st.PrivateDescription + +} +func (st *Scalar) String() string { + return st.PrivateName +} +func (st *Scalar) Error() error { + return st.err +} + +// Object Type Definition +// +// Almost all of the GraphQL types you define will be object Object types +// have a name, but most importantly describe their fields. +// Example: +// +// var AddressType = new Object({ +// name: 'Address', +// fields: { +// street: { type: String }, +// number: { type: Int }, +// formatted: { +// type: String, +// resolve(obj) { +// return obj.number + ' ' + obj.street +// } +// } +// } +// }); +// +// When two types need to refer to each other, or a type needs to refer to +// itself in a field, you can use a function expression (aka a closure or a +// thunk) to supply the fields lazily. +// +// Example: +// +// var PersonType = new Object({ +// name: 'Person', +// fields: () => ({ +// name: { type: String }, +// bestFriend: { type: PersonType }, +// }) +// }); +// +// / +type Object struct { + PrivateName string `json:"name"` + PrivateDescription string `json:"description"` + IsTypeOf IsTypeOfFn + + typeConfig ObjectConfig + initialisedFields bool + fields FieldDefinitionMap + initialisedInterfaces bool + interfaces []*Interface + // Interim alternative to throwing an error during schema definition at run-time + err error +} + +// IsTypeOfParams Params for IsTypeOfFn() +type IsTypeOfParams struct { + // Value that needs to be resolve. + // Use this to decide which GraphQLObject this value maps to. + Value interface{} + + // Info is a collection of information about the current execution state. + Info ResolveInfo + + // Context argument is a context value that is provided to every resolve function within an execution. + // It is commonly + // used to represent an authenticated user, or request-specific caches. + Context context.Context +} + +type IsTypeOfFn func(p IsTypeOfParams) bool + +type InterfacesThunk func() []*Interface + +type ObjectConfig struct { + Name string `json:"name"` + Interfaces interface{} `json:"interfaces"` + Fields interface{} `json:"fields"` + IsTypeOf IsTypeOfFn `json:"isTypeOf"` + Description string `json:"description"` +} + +type FieldsThunk func() Fields + +func NewObject(config ObjectConfig) *Object { + objectType := &Object{} + + err := invariant(config.Name != "", "Type must be named.") + if err != nil { + objectType.err = err + return objectType + } + err = assertValidName(config.Name) + if err != nil { + objectType.err = err + return objectType + } + + objectType.PrivateName = config.Name + objectType.PrivateDescription = config.Description + objectType.IsTypeOf = config.IsTypeOf + objectType.typeConfig = config + + return objectType +} +func (gt *Object) AddFieldConfig(fieldName string, fieldConfig *Field) { + if fieldName == "" || fieldConfig == nil { + return + } + switch gt.typeConfig.Fields.(type) { + case Fields: + gt.typeConfig.Fields.(Fields)[fieldName] = fieldConfig + gt.initialisedFields = false + } +} +func (gt *Object) Name() string { + return gt.PrivateName +} +func (gt *Object) Description() string { + return "" +} +func (gt *Object) String() string { + return gt.PrivateName +} +func (gt *Object) Fields() FieldDefinitionMap { + if gt.initialisedFields { + return gt.fields + } + + var configureFields Fields + switch gt.typeConfig.Fields.(type) { + case Fields: + configureFields = gt.typeConfig.Fields.(Fields) + case FieldsThunk: + configureFields = gt.typeConfig.Fields.(FieldsThunk)() + } + + fields, err := defineFieldMap(gt, configureFields) + gt.err = err + gt.fields = fields + gt.initialisedFields = true + return gt.fields +} + +func (gt *Object) Interfaces() []*Interface { + if gt.initialisedInterfaces { + return gt.interfaces + } + + var configInterfaces []*Interface + switch gt.typeConfig.Interfaces.(type) { + case InterfacesThunk: + configInterfaces = gt.typeConfig.Interfaces.(InterfacesThunk)() + case []*Interface: + configInterfaces = gt.typeConfig.Interfaces.([]*Interface) + case nil: + default: + gt.err = fmt.Errorf("Unknown Object.Interfaces type: %T", gt.typeConfig.Interfaces) + gt.initialisedInterfaces = true + return nil + } + + interfaces, err := defineInterfaces(gt, configInterfaces) + gt.err = err + gt.interfaces = interfaces + gt.initialisedInterfaces = true + return gt.interfaces +} + +func (gt *Object) Error() error { + return gt.err +} + +func defineInterfaces(ttype *Object, interfaces []*Interface) ([]*Interface, error) { + ifaces := []*Interface{} + + if len(interfaces) == 0 { + return ifaces, nil + } + for _, iface := range interfaces { + err := invariantf( + iface != nil, + `%v may only implement Interface types, it cannot implement: %v.`, ttype, iface, + ) + if err != nil { + return ifaces, err + } + if iface.ResolveType != nil { + err = invariantf( + iface.ResolveType != nil, + `Interface Type %v does not provide a "resolveType" function `+ + `and implementing Type %v does not provide a "isTypeOf" `+ + `function. There is no way to resolve this implementing type `+ + `during execution.`, iface, ttype, + ) + if err != nil { + return ifaces, err + } + } + ifaces = append(ifaces, iface) + } + + return ifaces, nil +} + +func defineFieldMap(ttype Named, fieldMap Fields) (FieldDefinitionMap, error) { + resultFieldMap := FieldDefinitionMap{} + + err := invariantf( + len(fieldMap) > 0, + `%v fields must be an object with field names as keys or a function which return such an object.`, ttype, + ) + if err != nil { + return resultFieldMap, err + } + + for fieldName, field := range fieldMap { + if field == nil { + continue + } + err = invariantf( + field.Type != nil, + `%v.%v field type must be Output Type but got: %v.`, ttype, fieldName, field.Type, + ) + if err != nil { + return resultFieldMap, err + } + if field.Type.Error() != nil { + return resultFieldMap, field.Type.Error() + } + err = assertValidName(fieldName) + if err != nil { + return resultFieldMap, err + } + fieldDef := &FieldDefinition{ + Name: fieldName, + Description: field.Description, + Type: field.Type, + Resolve: field.Resolve, + DeprecationReason: field.DeprecationReason, + } + + fieldDef.Args = []*Argument{} + for argName, arg := range field.Args { + err := assertValidName(argName) + if err != nil { + return resultFieldMap, err + } + err = invariantf( + arg != nil, + `%v.%v args must be an object with argument names as keys.`, ttype, fieldName, + ) + if err != nil { + return resultFieldMap, err + } + err = invariantf( + arg.Type != nil, + `%v.%v(%v:) argument type must be Input Type but got: %v.`, ttype, fieldName, argName, arg.Type, + ) + if err != nil { + return resultFieldMap, err + } + fieldArg := &Argument{ + PrivateName: argName, + PrivateDescription: arg.Description, + Type: arg.Type, + DefaultValue: arg.DefaultValue, + } + fieldDef.Args = append(fieldDef.Args, fieldArg) + } + resultFieldMap[fieldName] = fieldDef + } + return resultFieldMap, nil +} + +// ResolveParams Params for FieldResolveFn() +type ResolveParams struct { + // Source is the source value + Source interface{} + + // Args is a map of arguments for current GraphQL request + Args map[string]interface{} + + // Info is a collection of information about the current execution state. + Info ResolveInfo + + // Context argument is a context value that is provided to every resolve function within an execution. + // It is commonly + // used to represent an authenticated user, or request-specific caches. + Context context.Context +} + +type FieldResolveFn func(p ResolveParams) (interface{}, error) + +type ResolveInfo struct { + FieldName string + FieldASTs []*ast.Field + ReturnType Output + ParentType Composite + Schema Schema + Fragments map[string]ast.Definition + RootValue interface{} + Operation ast.Definition + VariableValues map[string]interface{} +} + +type Fields map[string]*Field + +type Field struct { + Name string `json:"name"` // used by graphlql-relay + Type Output `json:"type"` + Args FieldConfigArgument `json:"args"` + Resolve FieldResolveFn `json:"-"` + DeprecationReason string `json:"deprecationReason"` + Description string `json:"description"` +} + +type FieldConfigArgument map[string]*ArgumentConfig + +type ArgumentConfig struct { + Type Input `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + Description string `json:"description"` +} + +type FieldDefinitionMap map[string]*FieldDefinition +type FieldDefinition struct { + Name string `json:"name"` + Description string `json:"description"` + Type Output `json:"type"` + Args []*Argument `json:"args"` + Resolve FieldResolveFn `json:"-"` + DeprecationReason string `json:"deprecationReason"` +} + +type FieldArgument struct { + Name string `json:"name"` + Type Type `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + Description string `json:"description"` +} + +type Argument struct { + PrivateName string `json:"name"` + Type Input `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + PrivateDescription string `json:"description"` +} + +func (st *Argument) Name() string { + return st.PrivateName +} +func (st *Argument) Description() string { + return st.PrivateDescription + +} +func (st *Argument) String() string { + return st.PrivateName +} +func (st *Argument) Error() error { + return nil +} + +// Interface Type Definition +// +// When a field can return one of a heterogeneous set of types, a Interface type +// is used to describe what types are possible, what fields are in common across +// all types, as well as a function to determine which type is actually used +// when the field is resolved. +// +// Example: +// +// var EntityType = new Interface({ +// name: 'Entity', +// fields: { +// name: { type: String } +// } +// }); +// +// +type Interface struct { + PrivateName string `json:"name"` + PrivateDescription string `json:"description"` + ResolveType ResolveTypeFn + + typeConfig InterfaceConfig + initialisedFields bool + fields FieldDefinitionMap + err error +} +type InterfaceConfig struct { + Name string `json:"name"` + Fields interface{} `json:"fields"` + ResolveType ResolveTypeFn + Description string `json:"description"` +} + +// ResolveTypeParams Params for ResolveTypeFn() +type ResolveTypeParams struct { + // Value that needs to be resolve. + // Use this to decide which GraphQLObject this value maps to. + Value interface{} + + // Info is a collection of information about the current execution state. + Info ResolveInfo + + // Context argument is a context value that is provided to every resolve function within an execution. + // It is commonly + // used to represent an authenticated user, or request-specific caches. + Context context.Context +} + +type ResolveTypeFn func(p ResolveTypeParams) *Object + +func NewInterface(config InterfaceConfig) *Interface { + it := &Interface{} + + err := invariant(config.Name != "", "Type must be named.") + if err != nil { + it.err = err + return it + } + err = assertValidName(config.Name) + if err != nil { + it.err = err + return it + } + it.PrivateName = config.Name + it.PrivateDescription = config.Description + it.ResolveType = config.ResolveType + it.typeConfig = config + + return it +} + +func (it *Interface) AddFieldConfig(fieldName string, fieldConfig *Field) { + if fieldName == "" || fieldConfig == nil { + return + } + switch it.typeConfig.Fields.(type) { + case Fields: + it.typeConfig.Fields.(Fields)[fieldName] = fieldConfig + it.initialisedFields = false + } +} + +func (it *Interface) Name() string { + return it.PrivateName +} + +func (it *Interface) Description() string { + return it.PrivateDescription +} + +func (it *Interface) Fields() (fields FieldDefinitionMap) { + if it.initialisedFields { + return it.fields + } + + var configureFields Fields + switch it.typeConfig.Fields.(type) { + case Fields: + configureFields = it.typeConfig.Fields.(Fields) + case FieldsThunk: + configureFields = it.typeConfig.Fields.(FieldsThunk)() + } + + fields, err := defineFieldMap(it, configureFields) + it.err = err + it.fields = fields + it.initialisedFields = true + return it.fields +} + +func (it *Interface) String() string { + return it.PrivateName +} + +func (it *Interface) Error() error { + return it.err +} + +// Union Type Definition +// +// When a field can return one of a heterogeneous set of types, a Union type +// is used to describe what types are possible as well as providing a function +// to determine which type is actually used when the field is resolved. +// +// Example: +// +// var PetType = new Union({ +// name: 'Pet', +// types: [ DogType, CatType ], +// resolveType(value) { +// if (value instanceof Dog) { +// return DogType; +// } +// if (value instanceof Cat) { +// return CatType; +// } +// } +// }); +type Union struct { + PrivateName string `json:"name"` + PrivateDescription string `json:"description"` + ResolveType ResolveTypeFn + + typeConfig UnionConfig + types []*Object + possibleTypes map[string]bool + + err error +} +type UnionConfig struct { + Name string `json:"name"` + Types []*Object `json:"types"` + ResolveType ResolveTypeFn + Description string `json:"description"` +} + +func NewUnion(config UnionConfig) *Union { + objectType := &Union{} + + err := invariant(config.Name != "", "Type must be named.") + if err != nil { + objectType.err = err + return objectType + } + err = assertValidName(config.Name) + if err != nil { + objectType.err = err + return objectType + } + objectType.PrivateName = config.Name + objectType.PrivateDescription = config.Description + objectType.ResolveType = config.ResolveType + + err = invariantf( + len(config.Types) > 0, + `Must provide Array of types for Union %v.`, config.Name, + ) + if err != nil { + objectType.err = err + return objectType + } + for _, ttype := range config.Types { + err := invariantf( + ttype != nil, + `%v may only contain Object types, it cannot contain: %v.`, objectType, ttype, + ) + if err != nil { + objectType.err = err + return objectType + } + if objectType.ResolveType == nil { + err = invariantf( + ttype.IsTypeOf != nil, + `Union Type %v does not provide a "resolveType" function `+ + `and possible Type %v does not provide a "isTypeOf" `+ + `function. There is no way to resolve this possible type `+ + `during execution.`, objectType, ttype, + ) + if err != nil { + objectType.err = err + return objectType + } + } + } + objectType.types = config.Types + objectType.typeConfig = config + + return objectType +} +func (ut *Union) Types() []*Object { + return ut.types +} +func (ut *Union) String() string { + return ut.PrivateName +} +func (ut *Union) Name() string { + return ut.PrivateName +} +func (ut *Union) Description() string { + return ut.PrivateDescription +} +func (ut *Union) Error() error { + return ut.err +} + +// Enum Type Definition +// +// Some leaf values of requests and input values are Enums. GraphQL serializes +// Enum values as strings, however internally Enums can be represented by any +// kind of type, often integers. +// +// Example: +// +// var RGBType = new Enum({ +// name: 'RGB', +// values: { +// RED: { value: 0 }, +// GREEN: { value: 1 }, +// BLUE: { value: 2 } +// } +// }); +// +// Note: If a value is not provided in a definition, the name of the enum value +// will be used as its internal value. + +type Enum struct { + PrivateName string `json:"name"` + PrivateDescription string `json:"description"` + + enumConfig EnumConfig + values []*EnumValueDefinition + valuesLookup map[interface{}]*EnumValueDefinition + nameLookup map[string]*EnumValueDefinition + + err error +} +type EnumValueConfigMap map[string]*EnumValueConfig +type EnumValueConfig struct { + Value interface{} `json:"value"` + DeprecationReason string `json:"deprecationReason"` + Description string `json:"description"` +} +type EnumConfig struct { + Name string `json:"name"` + Values EnumValueConfigMap `json:"values"` + Description string `json:"description"` +} +type EnumValueDefinition struct { + Name string `json:"name"` + Value interface{} `json:"value"` + DeprecationReason string `json:"deprecationReason"` + Description string `json:"description"` +} + +func NewEnum(config EnumConfig) *Enum { + gt := &Enum{} + gt.enumConfig = config + + err := assertValidName(config.Name) + if err != nil { + gt.err = err + return gt + } + + gt.PrivateName = config.Name + gt.PrivateDescription = config.Description + gt.values, err = gt.defineEnumValues(config.Values) + if err != nil { + gt.err = err + return gt + } + + return gt +} +func (gt *Enum) defineEnumValues(valueMap EnumValueConfigMap) ([]*EnumValueDefinition, error) { + values := []*EnumValueDefinition{} + + err := invariantf( + len(valueMap) > 0, + `%v values must be an object with value names as keys.`, gt, + ) + if err != nil { + return values, err + } + + for valueName, valueConfig := range valueMap { + err := invariantf( + valueConfig != nil, + `%v.%v must refer to an object with a "value" key `+ + `representing an internal value but got: %v.`, gt, valueName, valueConfig, + ) + if err != nil { + return values, err + } + err = assertValidName(valueName) + if err != nil { + return values, err + } + value := &EnumValueDefinition{ + Name: valueName, + Value: valueConfig.Value, + DeprecationReason: valueConfig.DeprecationReason, + Description: valueConfig.Description, + } + if value.Value == nil { + value.Value = valueName + } + values = append(values, value) + } + return values, nil +} +func (gt *Enum) Values() []*EnumValueDefinition { + return gt.values +} +func (gt *Enum) Serialize(value interface{}) interface{} { + v := value + if reflect.ValueOf(v).Kind() == reflect.Ptr { + v = reflect.Indirect(reflect.ValueOf(v)).Interface() + } + if enumValue, ok := gt.getValueLookup()[v]; ok { + return enumValue.Name + } + return nil +} +func (gt *Enum) ParseValue(value interface{}) interface{} { + var v string + + switch value := value.(type) { + case string: + v = value + case *string: + v = *value + default: + return nil + } + if enumValue, ok := gt.getNameLookup()[v]; ok { + return enumValue.Value + } + return nil +} +func (gt *Enum) ParseLiteral(valueAST ast.Value) interface{} { + if valueAST, ok := valueAST.(*ast.EnumValue); ok { + if enumValue, ok := gt.getNameLookup()[valueAST.Value]; ok { + return enumValue.Value + } + } + return nil +} +func (gt *Enum) Name() string { + return gt.PrivateName +} +func (gt *Enum) Description() string { + return gt.PrivateDescription +} +func (gt *Enum) String() string { + return gt.PrivateName +} +func (gt *Enum) Error() error { + return gt.err +} +func (gt *Enum) getValueLookup() map[interface{}]*EnumValueDefinition { + if len(gt.valuesLookup) > 0 { + return gt.valuesLookup + } + valuesLookup := map[interface{}]*EnumValueDefinition{} + for _, value := range gt.Values() { + valuesLookup[value.Value] = value + } + gt.valuesLookup = valuesLookup + return gt.valuesLookup +} + +func (gt *Enum) getNameLookup() map[string]*EnumValueDefinition { + if len(gt.nameLookup) > 0 { + return gt.nameLookup + } + nameLookup := map[string]*EnumValueDefinition{} + for _, value := range gt.Values() { + nameLookup[value.Name] = value + } + gt.nameLookup = nameLookup + return gt.nameLookup +} + +// InputObject Type Definition +// +// An input object defines a structured collection of fields which may be +// supplied to a field argument. +// +// Using `NonNull` will ensure that a value must be provided by the query +// +// Example: +// +// var GeoPoint = new InputObject({ +// name: 'GeoPoint', +// fields: { +// lat: { type: new NonNull(Float) }, +// lon: { type: new NonNull(Float) }, +// alt: { type: Float, defaultValue: 0 }, +// } +// }); +type InputObject struct { + PrivateName string `json:"name"` + PrivateDescription string `json:"description"` + + typeConfig InputObjectConfig + fields InputObjectFieldMap + init bool + err error +} +type InputObjectFieldConfig struct { + Type Input `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + Description string `json:"description"` +} +type InputObjectField struct { + PrivateName string `json:"name"` + Type Input `json:"type"` + DefaultValue interface{} `json:"defaultValue"` + PrivateDescription string `json:"description"` +} + +func (st *InputObjectField) Name() string { + return st.PrivateName +} +func (st *InputObjectField) Description() string { + return st.PrivateDescription + +} +func (st *InputObjectField) String() string { + return st.PrivateName +} +func (st *InputObjectField) Error() error { + return nil +} + +type InputObjectConfigFieldMap map[string]*InputObjectFieldConfig +type InputObjectFieldMap map[string]*InputObjectField +type InputObjectConfigFieldMapThunk func() InputObjectConfigFieldMap +type InputObjectConfig struct { + Name string `json:"name"` + Fields interface{} `json:"fields"` + Description string `json:"description"` +} + +func NewInputObject(config InputObjectConfig) *InputObject { + gt := &InputObject{} + err := invariant(config.Name != "", "Type must be named.") + if err != nil { + gt.err = err + return gt + } + + gt.PrivateName = config.Name + gt.PrivateDescription = config.Description + gt.typeConfig = config + //gt.fields = gt.defineFieldMap() + return gt +} + +func (gt *InputObject) defineFieldMap() InputObjectFieldMap { + var fieldMap InputObjectConfigFieldMap + switch gt.typeConfig.Fields.(type) { + case InputObjectConfigFieldMap: + fieldMap = gt.typeConfig.Fields.(InputObjectConfigFieldMap) + case InputObjectConfigFieldMapThunk: + fieldMap = gt.typeConfig.Fields.(InputObjectConfigFieldMapThunk)() + } + resultFieldMap := InputObjectFieldMap{} + + err := invariantf( + len(fieldMap) > 0, + `%v fields must be an object with field names as keys or a function which return such an object.`, gt, + ) + if err != nil { + gt.err = err + return resultFieldMap + } + + for fieldName, fieldConfig := range fieldMap { + if fieldConfig == nil { + continue + } + err := assertValidName(fieldName) + if err != nil { + continue + } + err = invariantf( + fieldConfig.Type != nil, + `%v.%v field type must be Input Type but got: %v.`, gt, fieldName, fieldConfig.Type, + ) + if err != nil { + gt.err = err + return resultFieldMap + } + field := &InputObjectField{} + field.PrivateName = fieldName + field.Type = fieldConfig.Type + field.PrivateDescription = fieldConfig.Description + field.DefaultValue = fieldConfig.DefaultValue + resultFieldMap[fieldName] = field + } + gt.init = true + return resultFieldMap +} + +func (gt *InputObject) Fields() InputObjectFieldMap { + if !gt.init { + gt.fields = gt.defineFieldMap() + } + return gt.fields +} +func (gt *InputObject) Name() string { + return gt.PrivateName +} +func (gt *InputObject) Description() string { + return gt.PrivateDescription +} +func (gt *InputObject) String() string { + return gt.PrivateName +} +func (gt *InputObject) Error() error { + return gt.err +} + +// List Modifier +// +// A list is a kind of type marker, a wrapping type which points to another +// type. Lists are often created within the context of defining the fields of +// an object type. +// +// Example: +// +// var PersonType = new Object({ +// name: 'Person', +// fields: () => ({ +// parents: { type: new List(Person) }, +// children: { type: new List(Person) }, +// }) +// }) +// +type List struct { + OfType Type `json:"ofType"` + + err error +} + +func NewList(ofType Type) *List { + gl := &List{} + + err := invariantf(ofType != nil, `Can only create List of a Type but got: %v.`, ofType) + if err != nil { + gl.err = err + return gl + } + + gl.OfType = ofType + return gl +} +func (gl *List) Name() string { + return fmt.Sprintf("%v", gl.OfType) +} +func (gl *List) Description() string { + return "" +} +func (gl *List) String() string { + if gl.OfType != nil { + return fmt.Sprintf("[%v]", gl.OfType) + } + return "" +} +func (gl *List) Error() error { + return gl.err +} + +// NonNull Modifier +// +// A non-null is a kind of type marker, a wrapping type which points to another +// type. Non-null types enforce that their values are never null and can ensure +// an error is raised if this ever occurs during a request. It is useful for +// fields which you can make a strong guarantee on non-nullability, for example +// usually the id field of a database row will never be null. +// +// Example: +// +// var RowType = new Object({ +// name: 'Row', +// fields: () => ({ +// id: { type: new NonNull(String) }, +// }) +// }) +// +// Note: the enforcement of non-nullability occurs within the executor. +type NonNull struct { + OfType Type `json:"ofType"` + + err error +} + +func NewNonNull(ofType Type) *NonNull { + gl := &NonNull{} + + _, isOfTypeNonNull := ofType.(*NonNull) + err := invariantf(ofType != nil && !isOfTypeNonNull, `Can only create NonNull of a Nullable Type but got: %v.`, ofType) + if err != nil { + gl.err = err + return gl + } + gl.OfType = ofType + return gl +} +func (gl *NonNull) Name() string { + return fmt.Sprintf("%v!", gl.OfType) +} +func (gl *NonNull) Description() string { + return "" +} +func (gl *NonNull) String() string { + if gl.OfType != nil { + return gl.Name() + } + return "" +} +func (gl *NonNull) Error() error { + return gl.err +} + +var NameRegExp, _ = regexp.Compile("^[_a-zA-Z][_a-zA-Z0-9]*$") + +func assertValidName(name string) error { + return invariantf( + NameRegExp.MatchString(name), + `Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "%v" does not.`, name) + +} diff --git a/vendor/github.com/graphql-go/graphql/directives.go b/vendor/github.com/graphql-go/graphql/directives.go new file mode 100644 index 00000000..7ed839f7 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/directives.go @@ -0,0 +1,156 @@ +package graphql + +const ( + // Operations + DirectiveLocationQuery = "QUERY" + DirectiveLocationMutation = "MUTATION" + DirectiveLocationSubscription = "SUBSCRIPTION" + DirectiveLocationField = "FIELD" + DirectiveLocationFragmentDefinition = "FRAGMENT_DEFINITION" + DirectiveLocationFragmentSpread = "FRAGMENT_SPREAD" + DirectiveLocationInlineFragment = "INLINE_FRAGMENT" + + // Schema Definitions + DirectiveLocationSchema = "SCHEMA" + DirectiveLocationScalar = "SCALAR" + DirectiveLocationObject = "OBJECT" + DirectiveLocationFieldDefinition = "FIELD_DEFINITION" + DirectiveLocationArgumentDefinition = "ARGUMENT_DEFINITION" + DirectiveLocationInterface = "INTERFACE" + DirectiveLocationUnion = "UNION" + DirectiveLocationEnum = "ENUM" + DirectiveLocationEnumValue = "ENUM_VALUE" + DirectiveLocationInputObject = "INPUT_OBJECT" + DirectiveLocationInputFieldDefinition = "INPUT_FIELD_DEFINITION" +) + +// DefaultDeprecationReason Constant string used for default reason for a deprecation. +const DefaultDeprecationReason = "No longer supported" + +// SpecifiedRules The full list of specified directives. +var SpecifiedDirectives = []*Directive{ + IncludeDirective, + SkipDirective, + DeprecatedDirective, +} + +// Directive structs are used by the GraphQL runtime as a way of modifying execution +// behavior. Type system creators will usually not create these directly. +type Directive struct { + Name string `json:"name"` + Description string `json:"description"` + Locations []string `json:"locations"` + Args []*Argument `json:"args"` + + err error +} + +// DirectiveConfig options for creating a new GraphQLDirective +type DirectiveConfig struct { + Name string `json:"name"` + Description string `json:"description"` + Locations []string `json:"locations"` + Args FieldConfigArgument `json:"args"` +} + +func NewDirective(config DirectiveConfig) *Directive { + dir := &Directive{} + + // Ensure directive is named + err := invariant(config.Name != "", "Directive must be named.") + if err != nil { + dir.err = err + return dir + } + + // Ensure directive name is valid + err = assertValidName(config.Name) + if err != nil { + dir.err = err + return dir + } + + // Ensure locations are provided for directive + err = invariant(len(config.Locations) > 0, "Must provide locations for directive.") + if err != nil { + dir.err = err + return dir + } + + args := []*Argument{} + + for argName, argConfig := range config.Args { + err := assertValidName(argName) + if err != nil { + dir.err = err + return dir + } + args = append(args, &Argument{ + PrivateName: argName, + PrivateDescription: argConfig.Description, + Type: argConfig.Type, + DefaultValue: argConfig.DefaultValue, + }) + } + + dir.Name = config.Name + dir.Description = config.Description + dir.Locations = config.Locations + dir.Args = args + return dir +} + +// IncludeDirective is used to conditionally include fields or fragments. +var IncludeDirective = NewDirective(DirectiveConfig{ + Name: "include", + Description: "Directs the executor to include this field or fragment only when " + + "the `if` argument is true.", + Locations: []string{ + DirectiveLocationField, + DirectiveLocationFragmentSpread, + DirectiveLocationInlineFragment, + }, + Args: FieldConfigArgument{ + "if": &ArgumentConfig{ + Type: NewNonNull(Boolean), + Description: "Included when true.", + }, + }, +}) + +// SkipDirective Used to conditionally skip (exclude) fields or fragments. +var SkipDirective = NewDirective(DirectiveConfig{ + Name: "skip", + Description: "Directs the executor to skip this field or fragment when the `if` " + + "argument is true.", + Args: FieldConfigArgument{ + "if": &ArgumentConfig{ + Type: NewNonNull(Boolean), + Description: "Skipped when true.", + }, + }, + Locations: []string{ + DirectiveLocationField, + DirectiveLocationFragmentSpread, + DirectiveLocationInlineFragment, + }, +}) + +// DeprecatedDirective Used to declare element of a GraphQL schema as deprecated. +var DeprecatedDirective = NewDirective(DirectiveConfig{ + Name: "deprecated", + Description: "Marks an element of a GraphQL schema as no longer supported.", + Args: FieldConfigArgument{ + "reason": &ArgumentConfig{ + Type: String, + Description: "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/).", + DefaultValue: DefaultDeprecationReason, + }, + }, + Locations: []string{ + DirectiveLocationFieldDefinition, + DirectiveLocationEnumValue, + }, +}) diff --git a/vendor/github.com/graphql-go/graphql/executor.go b/vendor/github.com/graphql-go/graphql/executor.go new file mode 100644 index 00000000..a7a7535d --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/executor.go @@ -0,0 +1,921 @@ +package graphql + +import ( + "context" + "errors" + "fmt" + "reflect" + "strings" + + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/ast" +) + +type ExecuteParams struct { + Schema Schema + Root interface{} + AST *ast.Document + OperationName string + Args map[string]interface{} + + // Context may be provided to pass application-specific per-request + // information to resolve functions. + Context context.Context +} + +func Execute(p ExecuteParams) (result *Result) { + // Use background context if no context was provided + ctx := p.Context + if ctx == nil { + ctx = context.Background() + } + + resultChannel := make(chan *Result) + + go func(out chan<- *Result, done <-chan struct{}) { + result := &Result{} + + exeContext, err := buildExecutionContext(buildExecutionCtxParams{ + Schema: p.Schema, + Root: p.Root, + AST: p.AST, + OperationName: p.OperationName, + Args: p.Args, + Errors: nil, + Result: result, + Context: p.Context, + }) + + if err != nil { + result.Errors = append(result.Errors, gqlerrors.FormatError(err)) + select { + case out <- result: + case <-done: + } + return + } + + defer func() { + if r := recover(); r != nil { + var err error + if r, ok := r.(error); ok { + err = gqlerrors.FormatError(r) + } + exeContext.Errors = append(exeContext.Errors, gqlerrors.FormatError(err)) + result.Errors = exeContext.Errors + select { + case out <- result: + case <-done: + } + } + }() + + result = executeOperation(executeOperationParams{ + ExecutionContext: exeContext, + Root: p.Root, + Operation: exeContext.Operation, + }) + select { + case out <- result: + case <-done: + } + + }(resultChannel, ctx.Done()) + + select { + case <-ctx.Done(): + result = &Result{} + result.Errors = append(result.Errors, gqlerrors.FormatError(ctx.Err())) + case r := <-resultChannel: + result = r + } + return +} + +type buildExecutionCtxParams struct { + Schema Schema + Root interface{} + AST *ast.Document + OperationName string + Args map[string]interface{} + Errors []gqlerrors.FormattedError + Result *Result + Context context.Context +} + +type executionContext struct { + Schema Schema + Fragments map[string]ast.Definition + Root interface{} + Operation ast.Definition + VariableValues map[string]interface{} + Errors []gqlerrors.FormattedError + Context context.Context +} + +func buildExecutionContext(p buildExecutionCtxParams) (*executionContext, error) { + eCtx := &executionContext{} + var operation *ast.OperationDefinition + fragments := map[string]ast.Definition{} + + for _, definition := range p.AST.Definitions { + switch definition := definition.(type) { + case *ast.OperationDefinition: + if (p.OperationName == "") && operation != nil { + return nil, errors.New("Must provide operation name if query contains multiple operations.") + } + if p.OperationName == "" || definition.GetName() != nil && definition.GetName().Value == p.OperationName { + operation = definition + } + case *ast.FragmentDefinition: + key := "" + if definition.GetName() != nil && definition.GetName().Value != "" { + key = definition.GetName().Value + } + fragments[key] = definition + default: + return nil, fmt.Errorf("GraphQL cannot execute a request containing a %v", definition.GetKind()) + } + } + + if operation == nil { + if p.OperationName != "" { + return nil, fmt.Errorf(`Unknown operation named "%v".`, p.OperationName) + } + return nil, fmt.Errorf(`Must provide an operation.`) + } + + variableValues, err := getVariableValues(p.Schema, operation.GetVariableDefinitions(), p.Args) + if err != nil { + return nil, err + } + + eCtx.Schema = p.Schema + eCtx.Fragments = fragments + eCtx.Root = p.Root + eCtx.Operation = operation + eCtx.VariableValues = variableValues + eCtx.Errors = p.Errors + eCtx.Context = p.Context + return eCtx, nil +} + +type executeOperationParams struct { + ExecutionContext *executionContext + Root interface{} + Operation ast.Definition +} + +func executeOperation(p executeOperationParams) *Result { + operationType, err := getOperationRootType(p.ExecutionContext.Schema, p.Operation) + if err != nil { + return &Result{Errors: gqlerrors.FormatErrors(err)} + } + + fields := collectFields(collectFieldsParams{ + ExeContext: p.ExecutionContext, + RuntimeType: operationType, + SelectionSet: p.Operation.GetSelectionSet(), + }) + + executeFieldsParams := executeFieldsParams{ + ExecutionContext: p.ExecutionContext, + ParentType: operationType, + Source: p.Root, + Fields: fields, + } + + if p.Operation.GetOperation() == ast.OperationTypeMutation { + return executeFieldsSerially(executeFieldsParams) + } + return executeFields(executeFieldsParams) + +} + +// Extracts the root type of the operation from the schema. +func getOperationRootType(schema Schema, operation ast.Definition) (*Object, error) { + if operation == nil { + return nil, errors.New("Can only execute queries and mutations") + } + + switch operation.GetOperation() { + case ast.OperationTypeQuery: + return schema.QueryType(), nil + case ast.OperationTypeMutation: + mutationType := schema.MutationType() + if mutationType.PrivateName == "" { + return nil, gqlerrors.NewError( + "Schema is not configured for mutations", + []ast.Node{operation}, + "", + nil, + []int{}, + nil, + ) + } + return mutationType, nil + case ast.OperationTypeSubscription: + subscriptionType := schema.SubscriptionType() + if subscriptionType.PrivateName == "" { + return nil, gqlerrors.NewError( + "Schema is not configured for subscriptions", + []ast.Node{operation}, + "", + nil, + []int{}, + nil, + ) + } + return subscriptionType, nil + default: + return nil, gqlerrors.NewError( + "Can only execute queries, mutations and subscription", + []ast.Node{operation}, + "", + nil, + []int{}, + nil, + ) + } +} + +type executeFieldsParams struct { + ExecutionContext *executionContext + ParentType *Object + Source interface{} + Fields map[string][]*ast.Field +} + +// Implements the "Evaluating selection sets" section of the spec for "write" mode. +func executeFieldsSerially(p executeFieldsParams) *Result { + if p.Source == nil { + p.Source = map[string]interface{}{} + } + if p.Fields == nil { + p.Fields = map[string][]*ast.Field{} + } + + finalResults := map[string]interface{}{} + for responseName, fieldASTs := range p.Fields { + resolved, state := resolveField(p.ExecutionContext, p.ParentType, p.Source, fieldASTs) + if state.hasNoFieldDefs { + continue + } + finalResults[responseName] = resolved + } + + return &Result{ + Data: finalResults, + Errors: p.ExecutionContext.Errors, + } +} + +// Implements the "Evaluating selection sets" section of the spec for "read" mode. +func executeFields(p executeFieldsParams) *Result { + if p.Source == nil { + p.Source = map[string]interface{}{} + } + if p.Fields == nil { + p.Fields = map[string][]*ast.Field{} + } + + finalResults := map[string]interface{}{} + for responseName, fieldASTs := range p.Fields { + resolved, state := resolveField(p.ExecutionContext, p.ParentType, p.Source, fieldASTs) + if state.hasNoFieldDefs { + continue + } + finalResults[responseName] = resolved + } + + return &Result{ + Data: finalResults, + Errors: p.ExecutionContext.Errors, + } +} + +type collectFieldsParams struct { + ExeContext *executionContext + RuntimeType *Object // previously known as OperationType + SelectionSet *ast.SelectionSet + Fields map[string][]*ast.Field + VisitedFragmentNames map[string]bool +} + +// Given a selectionSet, adds all of the fields in that selection to +// the passed in map of fields, and returns it at the end. +// CollectFields requires the "runtime type" of an object. For a field which +// returns and Interface or Union type, the "runtime type" will be the actual +// Object type returned by that field. +func collectFields(p collectFieldsParams) map[string][]*ast.Field { + + fields := p.Fields + if fields == nil { + fields = map[string][]*ast.Field{} + } + if p.VisitedFragmentNames == nil { + p.VisitedFragmentNames = map[string]bool{} + } + if p.SelectionSet == nil { + return fields + } + for _, iSelection := range p.SelectionSet.Selections { + switch selection := iSelection.(type) { + case *ast.Field: + if !shouldIncludeNode(p.ExeContext, selection.Directives) { + continue + } + name := getFieldEntryKey(selection) + if _, ok := fields[name]; !ok { + fields[name] = []*ast.Field{} + } + fields[name] = append(fields[name], selection) + case *ast.InlineFragment: + + if !shouldIncludeNode(p.ExeContext, selection.Directives) || + !doesFragmentConditionMatch(p.ExeContext, selection, p.RuntimeType) { + continue + } + innerParams := collectFieldsParams{ + ExeContext: p.ExeContext, + RuntimeType: p.RuntimeType, + SelectionSet: selection.SelectionSet, + Fields: fields, + VisitedFragmentNames: p.VisitedFragmentNames, + } + collectFields(innerParams) + case *ast.FragmentSpread: + fragName := "" + if selection.Name != nil { + fragName = selection.Name.Value + } + if visited, ok := p.VisitedFragmentNames[fragName]; (ok && visited) || + !shouldIncludeNode(p.ExeContext, selection.Directives) { + continue + } + p.VisitedFragmentNames[fragName] = true + fragment, hasFragment := p.ExeContext.Fragments[fragName] + if !hasFragment { + continue + } + + if fragment, ok := fragment.(*ast.FragmentDefinition); ok { + if !doesFragmentConditionMatch(p.ExeContext, fragment, p.RuntimeType) { + continue + } + innerParams := collectFieldsParams{ + ExeContext: p.ExeContext, + RuntimeType: p.RuntimeType, + SelectionSet: fragment.GetSelectionSet(), + Fields: fields, + VisitedFragmentNames: p.VisitedFragmentNames, + } + collectFields(innerParams) + } + } + } + return fields +} + +// Determines if a field should be included based on the @include and @skip +// directives, where @skip has higher precedence than @include. +func shouldIncludeNode(eCtx *executionContext, directives []*ast.Directive) bool { + + defaultReturnValue := true + + var skipAST *ast.Directive + var includeAST *ast.Directive + for _, directive := range directives { + if directive == nil || directive.Name == nil { + continue + } + if directive.Name.Value == SkipDirective.Name { + skipAST = directive + break + } + } + if skipAST != nil { + argValues, err := getArgumentValues( + SkipDirective.Args, + skipAST.Arguments, + eCtx.VariableValues, + ) + if err != nil { + return defaultReturnValue + } + if skipIf, ok := argValues["if"].(bool); ok { + if skipIf == true { + return false + } + } + } + for _, directive := range directives { + if directive == nil || directive.Name == nil { + continue + } + if directive.Name.Value == IncludeDirective.Name { + includeAST = directive + break + } + } + if includeAST != nil { + argValues, err := getArgumentValues( + IncludeDirective.Args, + includeAST.Arguments, + eCtx.VariableValues, + ) + if err != nil { + return defaultReturnValue + } + if includeIf, ok := argValues["if"].(bool); ok { + if includeIf == false { + return false + } + } + } + return defaultReturnValue +} + +// Determines if a fragment is applicable to the given type. +func doesFragmentConditionMatch(eCtx *executionContext, fragment ast.Node, ttype *Object) bool { + + switch fragment := fragment.(type) { + case *ast.FragmentDefinition: + typeConditionAST := fragment.TypeCondition + if typeConditionAST == nil { + return true + } + conditionalType, err := typeFromAST(eCtx.Schema, typeConditionAST) + if err != nil { + return false + } + if conditionalType == ttype { + return true + } + if conditionalType.Name() == ttype.Name() { + return true + } + if conditionalType, ok := conditionalType.(*Interface); ok { + return eCtx.Schema.IsPossibleType(conditionalType, ttype) + } + if conditionalType, ok := conditionalType.(*Union); ok { + return eCtx.Schema.IsPossibleType(conditionalType, ttype) + } + case *ast.InlineFragment: + typeConditionAST := fragment.TypeCondition + if typeConditionAST == nil { + return true + } + conditionalType, err := typeFromAST(eCtx.Schema, typeConditionAST) + if err != nil { + return false + } + if conditionalType == ttype { + return true + } + if conditionalType.Name() == ttype.Name() { + return true + } + if conditionalType, ok := conditionalType.(*Interface); ok { + return eCtx.Schema.IsPossibleType(conditionalType, ttype) + } + if conditionalType, ok := conditionalType.(*Union); ok { + return eCtx.Schema.IsPossibleType(conditionalType, ttype) + } + } + + return false +} + +// Implements the logic to compute the key of a given field’s entry +func getFieldEntryKey(node *ast.Field) string { + + if node.Alias != nil && node.Alias.Value != "" { + return node.Alias.Value + } + if node.Name != nil && node.Name.Value != "" { + return node.Name.Value + } + return "" +} + +// Internal resolveField state +type resolveFieldResultState struct { + hasNoFieldDefs bool +} + +// Resolves the field on the given source object. In particular, this +// figures out the value that the field returns by calling its resolve function, +// then calls completeValue to complete promises, serialize scalars, or execute +// the sub-selection-set for objects. +func resolveField(eCtx *executionContext, parentType *Object, source interface{}, fieldASTs []*ast.Field) (result interface{}, resultState resolveFieldResultState) { + // catch panic from resolveFn + var returnType Output + defer func() (interface{}, resolveFieldResultState) { + if r := recover(); r != nil { + + var err error + if r, ok := r.(string); ok { + err = NewLocatedError( + fmt.Sprintf("%v", r), + FieldASTsToNodeASTs(fieldASTs), + ) + } + if r, ok := r.(error); ok { + err = gqlerrors.FormatError(r) + } + // send panic upstream + if _, ok := returnType.(*NonNull); ok { + panic(gqlerrors.FormatError(err)) + } + eCtx.Errors = append(eCtx.Errors, gqlerrors.FormatError(err)) + return result, resultState + } + return result, resultState + }() + + fieldAST := fieldASTs[0] + fieldName := "" + if fieldAST.Name != nil { + fieldName = fieldAST.Name.Value + } + + fieldDef := getFieldDef(eCtx.Schema, parentType, fieldName) + if fieldDef == nil { + resultState.hasNoFieldDefs = true + return nil, resultState + } + returnType = fieldDef.Type + resolveFn := fieldDef.Resolve + if resolveFn == nil { + resolveFn = DefaultResolveFn + } + + // Build a map of arguments from the field.arguments AST, using the + // variables scope to fulfill any variable references. + // TODO: find a way to memoize, in case this field is within a List type. + args, _ := getArgumentValues(fieldDef.Args, fieldAST.Arguments, eCtx.VariableValues) + + info := ResolveInfo{ + FieldName: fieldName, + FieldASTs: fieldASTs, + ReturnType: returnType, + ParentType: parentType, + Schema: eCtx.Schema, + Fragments: eCtx.Fragments, + RootValue: eCtx.Root, + Operation: eCtx.Operation, + VariableValues: eCtx.VariableValues, + } + + var resolveFnError error + + result, resolveFnError = resolveFn(ResolveParams{ + Source: source, + Args: args, + Info: info, + Context: eCtx.Context, + }) + + if resolveFnError != nil { + panic(gqlerrors.FormatError(resolveFnError)) + } + + completed := completeValueCatchingError(eCtx, returnType, fieldASTs, info, result) + return completed, resultState +} + +func completeValueCatchingError(eCtx *executionContext, returnType Type, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) (completed interface{}) { + // catch panic + defer func() interface{} { + if r := recover(); r != nil { + //send panic upstream + if _, ok := returnType.(*NonNull); ok { + panic(r) + } + if err, ok := r.(gqlerrors.FormattedError); ok { + eCtx.Errors = append(eCtx.Errors, err) + } + return completed + } + return completed + }() + + if returnType, ok := returnType.(*NonNull); ok { + completed := completeValue(eCtx, returnType, fieldASTs, info, result) + return completed + } + completed = completeValue(eCtx, returnType, fieldASTs, info, result) + return completed +} + +func completeValue(eCtx *executionContext, returnType Type, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} { + + resultVal := reflect.ValueOf(result) + if resultVal.IsValid() && resultVal.Type().Kind() == reflect.Func { + if propertyFn, ok := result.(func() interface{}); ok { + return propertyFn() + } + err := gqlerrors.NewFormattedError("Error resolving func. Expected `func() interface{}` signature") + panic(gqlerrors.FormatError(err)) + } + + // If field type is NonNull, complete for inner type, and throw field error + // if result is null. + if returnType, ok := returnType.(*NonNull); ok { + completed := completeValue(eCtx, returnType.OfType, fieldASTs, info, result) + if completed == nil { + err := NewLocatedError( + fmt.Sprintf("Cannot return null for non-nullable field %v.%v.", info.ParentType, info.FieldName), + FieldASTsToNodeASTs(fieldASTs), + ) + panic(gqlerrors.FormatError(err)) + } + return completed + } + + // If result value is null-ish (null, undefined, or NaN) then return null. + if isNullish(result) { + return nil + } + + // If field type is List, complete each item in the list with the inner type + if returnType, ok := returnType.(*List); ok { + return completeListValue(eCtx, returnType, fieldASTs, info, result) + } + + // If field type is a leaf type, Scalar or Enum, serialize to a valid value, + // returning null if serialization is not possible. + if returnType, ok := returnType.(*Scalar); ok { + return completeLeafValue(returnType, result) + } + if returnType, ok := returnType.(*Enum); ok { + return completeLeafValue(returnType, result) + } + + // If field type is an abstract type, Interface or Union, determine the + // runtime Object type and complete for that type. + if returnType, ok := returnType.(*Union); ok { + return completeAbstractValue(eCtx, returnType, fieldASTs, info, result) + } + if returnType, ok := returnType.(*Interface); ok { + return completeAbstractValue(eCtx, returnType, fieldASTs, info, result) + } + + // If field type is Object, execute and complete all sub-selections. + if returnType, ok := returnType.(*Object); ok { + return completeObjectValue(eCtx, returnType, fieldASTs, info, result) + } + + // Not reachable. All possible output types have been considered. + err := invariantf(false, + `Cannot complete value of unexpected type "%v."`, returnType) + + if err != nil { + panic(gqlerrors.FormatError(err)) + } + return nil +} + +// completeAbstractValue completes value of an Abstract type (Union / Interface) by determining the runtime type +// of that value, then completing based on that type. +func completeAbstractValue(eCtx *executionContext, returnType Abstract, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} { + + var runtimeType *Object + + resolveTypeParams := ResolveTypeParams{ + Value: result, + Info: info, + Context: eCtx.Context, + } + if unionReturnType, ok := returnType.(*Union); ok && unionReturnType.ResolveType != nil { + runtimeType = unionReturnType.ResolveType(resolveTypeParams) + } else if interfaceReturnType, ok := returnType.(*Interface); ok && interfaceReturnType.ResolveType != nil { + runtimeType = interfaceReturnType.ResolveType(resolveTypeParams) + } else { + runtimeType = defaultResolveTypeFn(resolveTypeParams, returnType) + } + + err := invariant(runtimeType != nil, + fmt.Sprintf(`Abstract type %v must resolve to an Object type at runtime `+ + `for field %v.%v with value "%v", received "%v".`, + returnType, info.ParentType, info.FieldName, result, runtimeType), + ) + if err != nil { + panic(err) + } + + if !eCtx.Schema.IsPossibleType(returnType, runtimeType) { + panic(gqlerrors.NewFormattedError( + fmt.Sprintf(`Runtime Object type "%v" is not a possible type `+ + `for "%v".`, runtimeType, returnType), + )) + } + + return completeObjectValue(eCtx, runtimeType, fieldASTs, info, result) +} + +// completeObjectValue complete an Object value by executing all sub-selections. +func completeObjectValue(eCtx *executionContext, returnType *Object, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} { + + // If there is an isTypeOf predicate function, call it with the + // current result. If isTypeOf returns false, then raise an error rather + // than continuing execution. + if returnType.IsTypeOf != nil { + p := IsTypeOfParams{ + Value: result, + Info: info, + Context: eCtx.Context, + } + if !returnType.IsTypeOf(p) { + panic(gqlerrors.NewFormattedError( + fmt.Sprintf(`Expected value of type "%v" but got: %T.`, returnType, result), + )) + } + } + + // Collect sub-fields to execute to complete this value. + subFieldASTs := map[string][]*ast.Field{} + visitedFragmentNames := map[string]bool{} + for _, fieldAST := range fieldASTs { + if fieldAST == nil { + continue + } + selectionSet := fieldAST.SelectionSet + if selectionSet != nil { + innerParams := collectFieldsParams{ + ExeContext: eCtx, + RuntimeType: returnType, + SelectionSet: selectionSet, + Fields: subFieldASTs, + VisitedFragmentNames: visitedFragmentNames, + } + subFieldASTs = collectFields(innerParams) + } + } + executeFieldsParams := executeFieldsParams{ + ExecutionContext: eCtx, + ParentType: returnType, + Source: result, + Fields: subFieldASTs, + } + results := executeFields(executeFieldsParams) + + return results.Data + +} + +// completeLeafValue complete a leaf value (Scalar / Enum) by serializing to a valid value, returning nil if serialization is not possible. +func completeLeafValue(returnType Leaf, result interface{}) interface{} { + serializedResult := returnType.Serialize(result) + if isNullish(serializedResult) { + return nil + } + return serializedResult +} + +// completeListValue complete a list value by completing each item in the list with the inner type +func completeListValue(eCtx *executionContext, returnType *List, fieldASTs []*ast.Field, info ResolveInfo, result interface{}) interface{} { + resultVal := reflect.ValueOf(result) + parentTypeName := "" + if info.ParentType != nil { + parentTypeName = info.ParentType.Name() + } + err := invariantf( + resultVal.IsValid() && resultVal.Type().Kind() == reflect.Slice, + "User Error: expected iterable, but did not find one "+ + "for field %v.%v.", parentTypeName, info.FieldName) + + if err != nil { + panic(gqlerrors.FormatError(err)) + } + + itemType := returnType.OfType + completedResults := []interface{}{} + for i := 0; i < resultVal.Len(); i++ { + val := resultVal.Index(i).Interface() + completedItem := completeValueCatchingError(eCtx, itemType, fieldASTs, info, val) + completedResults = append(completedResults, completedItem) + } + return completedResults +} + +// defaultResolveTypeFn If a resolveType function is not given, then a default resolve behavior is +// used which tests each possible type for the abstract type by calling +// isTypeOf for the object being coerced, returning the first type that matches. +func defaultResolveTypeFn(p ResolveTypeParams, abstractType Abstract) *Object { + possibleTypes := p.Info.Schema.PossibleTypes(abstractType) + for _, possibleType := range possibleTypes { + if possibleType.IsTypeOf == nil { + continue + } + isTypeOfParams := IsTypeOfParams{ + Value: p.Value, + Info: p.Info, + Context: p.Context, + } + if res := possibleType.IsTypeOf(isTypeOfParams); res { + return possibleType + } + } + return nil +} + +// FieldResolver is used in DefaultResolveFn when the the source value implements this interface. +type FieldResolver interface { + // Resolve resolves the value for the given ResolveParams. It has the same semantics as FieldResolveFn. + Resolve(p ResolveParams) (interface{}, error) +} + +// defaultResolveFn If a resolve function is not given, then a default resolve behavior is used +// which takes the property of the source object of the same name as the field +// and returns it as the result, or if it's a function, returns the result +// of calling that function. +func DefaultResolveFn(p ResolveParams) (interface{}, error) { + // try to resolve p.Source as a struct first + sourceVal := reflect.ValueOf(p.Source) + if sourceVal.IsValid() && sourceVal.Type().Kind() == reflect.Ptr { + sourceVal = sourceVal.Elem() + } + if !sourceVal.IsValid() { + return nil, nil + } + + // Check if value implements 'Resolver' interface + if resolver, ok := sourceVal.Interface().(FieldResolver); ok { + return resolver.Resolve(p) + } + + if sourceVal.Type().Kind() == reflect.Struct { + for i := 0; i < sourceVal.NumField(); i++ { + valueField := sourceVal.Field(i) + typeField := sourceVal.Type().Field(i) + // try matching the field name first + if typeField.Name == p.Info.FieldName { + return valueField.Interface(), nil + } + tag := typeField.Tag + checkTag := func(tagName string) bool { + t := tag.Get(tagName) + tOptions := strings.Split(t, ",") + if len(tOptions) == 0 { + return false + } + if tOptions[0] != p.Info.FieldName { + return false + } + return true + } + if checkTag("json") || checkTag("graphql") { + return valueField.Interface(), nil + } else { + continue + } + } + return nil, nil + } + + // try p.Source as a map[string]interface + if sourceMap, ok := p.Source.(map[string]interface{}); ok { + property := sourceMap[p.Info.FieldName] + val := reflect.ValueOf(property) + if val.IsValid() && val.Type().Kind() == reflect.Func { + // try type casting the func to the most basic func signature + // for more complex signatures, user have to define ResolveFn + if propertyFn, ok := property.(func() interface{}); ok { + return propertyFn(), nil + } + } + return property, nil + } + + // last resort, return nil + return nil, nil +} + +// This method looks up the field on the given type definition. +// It has special casing for the two introspection fields, __schema +// and __typename. __typename is special because it can always be +// queried as a field, even in situations where no other fields +// are allowed, like on a Union. __schema could get automatically +// added to the query type, but that would require mutating type +// definitions, which would cause issues. +func getFieldDef(schema Schema, parentType *Object, fieldName string) *FieldDefinition { + + if parentType == nil { + return nil + } + + if fieldName == SchemaMetaFieldDef.Name && + schema.QueryType() == parentType { + return SchemaMetaFieldDef + } + if fieldName == TypeMetaFieldDef.Name && + schema.QueryType() == parentType { + return TypeMetaFieldDef + } + if fieldName == TypeNameMetaFieldDef.Name { + return TypeNameMetaFieldDef + } + return parentType.Fields()[fieldName] +} diff --git a/vendor/github.com/graphql-go/graphql/gqlerrors/error.go b/vendor/github.com/graphql-go/graphql/gqlerrors/error.go new file mode 100644 index 00000000..1b289bbb --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/gqlerrors/error.go @@ -0,0 +1,68 @@ +package gqlerrors + +import ( + "fmt" + "reflect" + + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/location" + "github.com/graphql-go/graphql/language/source" +) + +type Error struct { + Message string + Stack string + Nodes []ast.Node + Source *source.Source + Positions []int + Locations []location.SourceLocation + OriginalError error +} + +// implements Golang's built-in `error` interface +func (g Error) Error() string { + return fmt.Sprintf("%v", g.Message) +} + +func NewError(message string, nodes []ast.Node, stack string, source *source.Source, positions []int, origError error) *Error { + if stack == "" && message != "" { + stack = message + } + if source == nil { + for _, node := range nodes { + // get source from first node + if node == nil || reflect.ValueOf(node).IsNil() { + continue + } + if node.GetLoc() != nil { + source = node.GetLoc().Source + } + break + } + } + if len(positions) == 0 && len(nodes) > 0 { + for _, node := range nodes { + if node == nil || reflect.ValueOf(node).IsNil() { + continue + } + if node.GetLoc() == nil { + continue + } + positions = append(positions, node.GetLoc().Start) + } + } + locations := []location.SourceLocation{} + for _, pos := range positions { + loc := location.GetLocation(source, pos) + locations = append(locations, loc) + } + return &Error{ + Message: message, + Stack: stack, + Nodes: nodes, + Source: source, + Positions: positions, + Locations: locations, + OriginalError: origError, + } +} diff --git a/vendor/github.com/graphql-go/graphql/gqlerrors/formatted.go b/vendor/github.com/graphql-go/graphql/gqlerrors/formatted.go new file mode 100644 index 00000000..3a1f8853 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/gqlerrors/formatted.go @@ -0,0 +1,51 @@ +package gqlerrors + +import ( + "errors" + + "github.com/graphql-go/graphql/language/location" +) + +type FormattedError struct { + Message string `json:"message"` + Locations []location.SourceLocation `json:"locations"` +} + +func (g FormattedError) Error() string { + return g.Message +} + +func NewFormattedError(message string) FormattedError { + err := errors.New(message) + return FormatError(err) +} + +func FormatError(err error) FormattedError { + switch err := err.(type) { + case FormattedError: + return err + case *Error: + return FormattedError{ + Message: err.Error(), + Locations: err.Locations, + } + case Error: + return FormattedError{ + Message: err.Error(), + Locations: err.Locations, + } + default: + return FormattedError{ + Message: err.Error(), + Locations: []location.SourceLocation{}, + } + } +} + +func FormatErrors(errs ...error) []FormattedError { + formattedErrors := []FormattedError{} + for _, err := range errs { + formattedErrors = append(formattedErrors, FormatError(err)) + } + return formattedErrors +} diff --git a/vendor/github.com/graphql-go/graphql/gqlerrors/located.go b/vendor/github.com/graphql-go/graphql/gqlerrors/located.go new file mode 100644 index 00000000..b02fcd8a --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/gqlerrors/located.go @@ -0,0 +1,39 @@ +package gqlerrors + +import ( + "errors" + "github.com/graphql-go/graphql/language/ast" +) + +// NewLocatedError creates a graphql.Error with location info +// @deprecated 0.4.18 +// Already exists in `graphql.NewLocatedError()` +func NewLocatedError(err interface{}, nodes []ast.Node) *Error { + var origError error + message := "An unknown error occurred." + if err, ok := err.(error); ok { + message = err.Error() + origError = err + } + if err, ok := err.(string); ok { + message = err + origError = errors.New(err) + } + stack := message + return NewError( + message, + nodes, + stack, + nil, + []int{}, + origError, + ) +} + +func FieldASTsToNodeASTs(fieldASTs []*ast.Field) []ast.Node { + nodes := []ast.Node{} + for _, fieldAST := range fieldASTs { + nodes = append(nodes, fieldAST) + } + return nodes +} diff --git a/vendor/github.com/graphql-go/graphql/gqlerrors/sortutil.go b/vendor/github.com/graphql-go/graphql/gqlerrors/sortutil.go new file mode 100644 index 00000000..300a9407 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/gqlerrors/sortutil.go @@ -0,0 +1,30 @@ +package gqlerrors + +import "bytes" + +type FormattedErrors []FormattedError + +func (errs FormattedErrors) Len() int { + return len(errs) +} + +func (errs FormattedErrors) Swap(i, j int) { + errs[i], errs[j] = errs[j], errs[i] +} + +func (errs FormattedErrors) Less(i, j int) bool { + mCompare := bytes.Compare([]byte(errs[i].Message), []byte(errs[j].Message)) + lesserLine := errs[i].Locations[0].Line < errs[j].Locations[0].Line + eqLine := errs[i].Locations[0].Line == errs[j].Locations[0].Line + lesserColumn := errs[i].Locations[0].Column < errs[j].Locations[0].Column + if mCompare < 0 { + return true + } + if mCompare == 0 && lesserLine { + return true + } + if mCompare == 0 && eqLine && lesserColumn { + return true + } + return false +} diff --git a/vendor/github.com/graphql-go/graphql/gqlerrors/syntax.go b/vendor/github.com/graphql-go/graphql/gqlerrors/syntax.go new file mode 100644 index 00000000..abad6ade --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/gqlerrors/syntax.go @@ -0,0 +1,69 @@ +package gqlerrors + +import ( + "fmt" + "regexp" + "strings" + + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/location" + "github.com/graphql-go/graphql/language/source" +) + +func NewSyntaxError(s *source.Source, position int, description string) *Error { + l := location.GetLocation(s, position) + return NewError( + fmt.Sprintf("Syntax Error %s (%d:%d) %s\n\n%s", s.Name, l.Line, l.Column, description, highlightSourceAtLocation(s, l)), + []ast.Node{}, + "", + s, + []int{position}, + nil, + ) +} + +// printCharCode here is slightly different from lexer.printCharCode() +func printCharCode(code rune) string { + // print as ASCII for printable range + if code >= 0x0020 { + return fmt.Sprintf(`%c`, code) + } + // Otherwise print the escaped form. e.g. `"\\u0007"` + return fmt.Sprintf(`\u%04X`, code) +} +func printLine(str string) string { + strSlice := []string{} + for _, runeValue := range str { + strSlice = append(strSlice, printCharCode(runeValue)) + } + return fmt.Sprintf(`%s`, strings.Join(strSlice, "")) +} +func highlightSourceAtLocation(s *source.Source, l location.SourceLocation) string { + line := l.Line + prevLineNum := fmt.Sprintf("%d", (line - 1)) + lineNum := fmt.Sprintf("%d", line) + nextLineNum := fmt.Sprintf("%d", (line + 1)) + padLen := len(nextLineNum) + lines := regexp.MustCompile("\r\n|[\n\r]").Split(string(s.Body), -1) + var highlight string + if line >= 2 { + highlight += fmt.Sprintf("%s: %s\n", lpad(padLen, prevLineNum), printLine(lines[line-2])) + } + highlight += fmt.Sprintf("%s: %s\n", lpad(padLen, lineNum), printLine(lines[line-1])) + for i := 1; i < (2 + padLen + l.Column); i++ { + highlight += " " + } + highlight += "^\n" + if line < len(lines) { + highlight += fmt.Sprintf("%s: %s\n", lpad(padLen, nextLineNum), printLine(lines[line])) + } + return highlight +} + +func lpad(l int, s string) string { + var r string + for i := 1; i < (l - len(s) + 1); i++ { + r += " " + } + return r + s +} diff --git a/vendor/github.com/graphql-go/graphql/graphql.go b/vendor/github.com/graphql-go/graphql/graphql.go new file mode 100644 index 00000000..c9bdb168 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/graphql.go @@ -0,0 +1,63 @@ +package graphql + +import ( + "context" + + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/parser" + "github.com/graphql-go/graphql/language/source" +) + +type Params struct { + // The GraphQL type system to use when validating and executing a query. + Schema Schema + + // A GraphQL language formatted string representing the requested operation. + RequestString string + + // The value provided as the first argument to resolver functions on the top + // level type (e.g. the query object type). + RootObject map[string]interface{} + + // A mapping of variable name to runtime value to use for all variables + // defined in the requestString. + VariableValues map[string]interface{} + + // The name of the operation to use if requestString contains multiple + // possible operations. Can be omitted if requestString contains only + // one operation. + OperationName string + + // Context may be provided to pass application-specific per-request + // information to resolve functions. + Context context.Context +} + +func Do(p Params) *Result { + source := source.NewSource(&source.Source{ + Body: []byte(p.RequestString), + Name: "GraphQL request", + }) + AST, err := parser.Parse(parser.ParseParams{Source: source}) + if err != nil { + return &Result{ + Errors: gqlerrors.FormatErrors(err), + } + } + validationResult := ValidateDocument(&p.Schema, AST, nil) + + if !validationResult.IsValid { + return &Result{ + Errors: validationResult.Errors, + } + } + + return Execute(ExecuteParams{ + Schema: p.Schema, + Root: p.RootObject, + AST: AST, + OperationName: p.OperationName, + Args: p.VariableValues, + Context: p.Context, + }) +} diff --git a/vendor/github.com/graphql-go/graphql/introspection.go b/vendor/github.com/graphql-go/graphql/introspection.go new file mode 100644 index 00000000..5e628e45 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/introspection.go @@ -0,0 +1,763 @@ +package graphql + +import ( + "fmt" + "reflect" + "sort" + + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/printer" +) + +const ( + TypeKindScalar = "SCALAR" + TypeKindObject = "OBJECT" + TypeKindInterface = "INTERFACE" + TypeKindUnion = "UNION" + TypeKindEnum = "ENUM" + TypeKindInputObject = "INPUT_OBJECT" + TypeKindList = "LIST" + TypeKindNonNull = "NON_NULL" +) + +// SchemaType is type definition for __Schema +var SchemaType *Object + +// DirectiveType is type definition for __Directive +var DirectiveType *Object + +// TypeType is type definition for __Type +var TypeType *Object + +// FieldType is type definition for __Field +var FieldType *Object + +// InputValueType is type definition for __InputValue +var InputValueType *Object + +// EnumValueType is type definition for __EnumValue +var EnumValueType *Object + +// TypeKindEnumType is type definition for __TypeKind +var TypeKindEnumType *Enum + +// DirectiveLocationEnumType is type definition for __DirectiveLocation +var DirectiveLocationEnumType *Enum + +// Meta-field definitions. + +// SchemaMetaFieldDef Meta field definition for Schema +var SchemaMetaFieldDef *FieldDefinition + +// TypeMetaFieldDef Meta field definition for types +var TypeMetaFieldDef *FieldDefinition + +// TypeNameMetaFieldDef Meta field definition for type names +var TypeNameMetaFieldDef *FieldDefinition + +func init() { + + TypeKindEnumType = NewEnum(EnumConfig{ + Name: "__TypeKind", + Description: "An enum describing what kind of type a given `__Type` is", + Values: EnumValueConfigMap{ + "SCALAR": &EnumValueConfig{ + Value: TypeKindScalar, + Description: "Indicates this type is a scalar.", + }, + "OBJECT": &EnumValueConfig{ + Value: TypeKindObject, + Description: "Indicates this type is an object. " + + "`fields` and `interfaces` are valid fields.", + }, + "INTERFACE": &EnumValueConfig{ + Value: TypeKindInterface, + Description: "Indicates this type is an interface. " + + "`fields` and `possibleTypes` are valid fields.", + }, + "UNION": &EnumValueConfig{ + Value: TypeKindUnion, + Description: "Indicates this type is a union. " + + "`possibleTypes` is a valid field.", + }, + "ENUM": &EnumValueConfig{ + Value: TypeKindEnum, + Description: "Indicates this type is an enum. " + + "`enumValues` is a valid field.", + }, + "INPUT_OBJECT": &EnumValueConfig{ + Value: TypeKindInputObject, + Description: "Indicates this type is an input object. " + + "`inputFields` is a valid field.", + }, + "LIST": &EnumValueConfig{ + Value: TypeKindList, + Description: "Indicates this type is a list. " + + "`ofType` is a valid field.", + }, + "NON_NULL": &EnumValueConfig{ + Value: TypeKindNonNull, + Description: "Indicates this type is a non-null. " + + "`ofType` is a valid field.", + }, + }, + }) + + DirectiveLocationEnumType = NewEnum(EnumConfig{ + Name: "__DirectiveLocation", + Description: "A Directive can be adjacent to many parts of the GraphQL language, a " + + "__DirectiveLocation describes one such possible adjacencies.", + Values: EnumValueConfigMap{ + "QUERY": &EnumValueConfig{ + Value: DirectiveLocationQuery, + Description: "Location adjacent to a query operation.", + }, + "MUTATION": &EnumValueConfig{ + Value: DirectiveLocationMutation, + Description: "Location adjacent to a mutation operation.", + }, + "SUBSCRIPTION": &EnumValueConfig{ + Value: DirectiveLocationSubscription, + Description: "Location adjacent to a subscription operation.", + }, + "FIELD": &EnumValueConfig{ + Value: DirectiveLocationField, + Description: "Location adjacent to a field.", + }, + "FRAGMENT_DEFINITION": &EnumValueConfig{ + Value: DirectiveLocationFragmentDefinition, + Description: "Location adjacent to a fragment definition.", + }, + "FRAGMENT_SPREAD": &EnumValueConfig{ + Value: DirectiveLocationFragmentSpread, + Description: "Location adjacent to a fragment spread.", + }, + "INLINE_FRAGMENT": &EnumValueConfig{ + Value: DirectiveLocationInlineFragment, + Description: "Location adjacent to an inline fragment.", + }, + "SCHEMA": &EnumValueConfig{ + Value: DirectiveLocationSchema, + Description: "Location adjacent to a schema definition.", + }, + "SCALAR": &EnumValueConfig{ + Value: DirectiveLocationScalar, + Description: "Location adjacent to a scalar definition.", + }, + "OBJECT": &EnumValueConfig{ + Value: DirectiveLocationObject, + Description: "Location adjacent to a object definition.", + }, + "FIELD_DEFINITION": &EnumValueConfig{ + Value: DirectiveLocationFieldDefinition, + Description: "Location adjacent to a field definition.", + }, + "ARGUMENT_DEFINITION": &EnumValueConfig{ + Value: DirectiveLocationArgumentDefinition, + Description: "Location adjacent to an argument definition.", + }, + "INTERFACE": &EnumValueConfig{ + Value: DirectiveLocationInterface, + Description: "Location adjacent to an interface definition.", + }, + "UNION": &EnumValueConfig{ + Value: DirectiveLocationUnion, + Description: "Location adjacent to a union definition.", + }, + "ENUM": &EnumValueConfig{ + Value: DirectiveLocationEnum, + Description: "Location adjacent to an enum definition.", + }, + "ENUM_VALUE": &EnumValueConfig{ + Value: DirectiveLocationEnumValue, + Description: "Location adjacent to an enum value definition.", + }, + "INPUT_OBJECT": &EnumValueConfig{ + Value: DirectiveLocationInputObject, + Description: "Location adjacent to an input object type definition.", + }, + "INPUT_FIELD_DEFINITION": &EnumValueConfig{ + Value: DirectiveLocationInputFieldDefinition, + Description: "Location adjacent to an input object field definition.", + }, + }, + }) + + // Note: some fields (for e.g "fields", "interfaces") are defined later due to cyclic reference + TypeType = NewObject(ObjectConfig{ + Name: "__Type", + Description: "The fundamental unit of any GraphQL Schema is the type. There are " + + "many kinds of types in GraphQL as represented by the `__TypeKind` enum." + + "\n\nDepending 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.", + + Fields: Fields{ + "kind": &Field{ + Type: NewNonNull(TypeKindEnumType), + Resolve: func(p ResolveParams) (interface{}, error) { + switch p.Source.(type) { + case *Scalar: + return TypeKindScalar, nil + case *Object: + return TypeKindObject, nil + case *Interface: + return TypeKindInterface, nil + case *Union: + return TypeKindUnion, nil + case *Enum: + return TypeKindEnum, nil + case *InputObject: + return TypeKindInputObject, nil + case *List: + return TypeKindList, nil + case *NonNull: + return TypeKindNonNull, nil + } + return nil, fmt.Errorf("Unknown kind of type: %v", p.Source) + }, + }, + "name": &Field{ + Type: String, + }, + "description": &Field{ + Type: String, + }, + "fields": &Field{}, + "interfaces": &Field{}, + "possibleTypes": &Field{}, + "enumValues": &Field{}, + "inputFields": &Field{}, + "ofType": &Field{}, + }, + }) + + InputValueType = NewObject(ObjectConfig{ + Name: "__InputValue", + Description: "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.", + Fields: Fields{ + "name": &Field{ + Type: NewNonNull(String), + }, + "description": &Field{ + Type: String, + }, + "type": &Field{ + Type: NewNonNull(TypeType), + }, + "defaultValue": &Field{ + Type: String, + Description: "A GraphQL-formatted string representing the default value for this " + + "input value.", + Resolve: func(p ResolveParams) (interface{}, error) { + if inputVal, ok := p.Source.(*Argument); ok { + if inputVal.DefaultValue == nil { + return nil, nil + } + if isNullish(inputVal.DefaultValue) { + return nil, nil + } + astVal := astFromValue(inputVal.DefaultValue, inputVal) + return printer.Print(astVal), nil + } + if inputVal, ok := p.Source.(*InputObjectField); ok { + if inputVal.DefaultValue == nil { + return nil, nil + } + astVal := astFromValue(inputVal.DefaultValue, inputVal) + return printer.Print(astVal), nil + } + return nil, nil + }, + }, + }, + }) + + FieldType = NewObject(ObjectConfig{ + Name: "__Field", + Description: "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.", + Fields: Fields{ + "name": &Field{ + Type: NewNonNull(String), + }, + "description": &Field{ + Type: String, + }, + "args": &Field{ + Type: NewNonNull(NewList(NewNonNull(InputValueType))), + Resolve: func(p ResolveParams) (interface{}, error) { + if field, ok := p.Source.(*FieldDefinition); ok { + return field.Args, nil + } + return []interface{}{}, nil + }, + }, + "type": &Field{ + Type: NewNonNull(TypeType), + }, + "isDeprecated": &Field{ + Type: NewNonNull(Boolean), + Resolve: func(p ResolveParams) (interface{}, error) { + if field, ok := p.Source.(*FieldDefinition); ok { + return (field.DeprecationReason != ""), nil + } + return false, nil + }, + }, + "deprecationReason": &Field{ + Type: String, + }, + }, + }) + + DirectiveType = NewObject(ObjectConfig{ + Name: "__Directive", + Description: "A Directive provides a way to describe alternate runtime execution and " + + "type validation behavior in a GraphQL document. " + + "\n\nIn 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.", + Fields: Fields{ + "name": &Field{ + Type: NewNonNull(String), + }, + "description": &Field{ + Type: String, + }, + "locations": &Field{ + Type: NewNonNull(NewList( + NewNonNull(DirectiveLocationEnumType), + )), + }, + "args": &Field{ + Type: NewNonNull(NewList( + NewNonNull(InputValueType), + )), + }, + // NOTE: the following three fields are deprecated and are no longer part + // of the GraphQL specification. + "onOperation": &Field{ + DeprecationReason: "Use `locations`.", + Type: NewNonNull(Boolean), + Resolve: func(p ResolveParams) (interface{}, error) { + if dir, ok := p.Source.(*Directive); ok { + res := false + for _, loc := range dir.Locations { + if loc == DirectiveLocationQuery || + loc == DirectiveLocationMutation || + loc == DirectiveLocationSubscription { + res = true + break + } + } + return res, nil + } + return false, nil + }, + }, + "onFragment": &Field{ + DeprecationReason: "Use `locations`.", + Type: NewNonNull(Boolean), + Resolve: func(p ResolveParams) (interface{}, error) { + if dir, ok := p.Source.(*Directive); ok { + res := false + for _, loc := range dir.Locations { + if loc == DirectiveLocationFragmentSpread || + loc == DirectiveLocationInlineFragment || + loc == DirectiveLocationFragmentDefinition { + res = true + break + } + } + return res, nil + } + return false, nil + }, + }, + "onField": &Field{ + DeprecationReason: "Use `locations`.", + Type: NewNonNull(Boolean), + Resolve: func(p ResolveParams) (interface{}, error) { + if dir, ok := p.Source.(*Directive); ok { + res := false + for _, loc := range dir.Locations { + if loc == DirectiveLocationField { + res = true + break + } + } + return res, nil + } + return false, nil + }, + }, + }, + }) + + SchemaType = NewObject(ObjectConfig{ + Name: "__Schema", + Description: `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.`, + Fields: Fields{ + "types": &Field{ + Description: "A list of all types supported by this server.", + Type: NewNonNull(NewList( + NewNonNull(TypeType), + )), + Resolve: func(p ResolveParams) (interface{}, error) { + if schema, ok := p.Source.(Schema); ok { + results := []Type{} + for _, ttype := range schema.TypeMap() { + results = append(results, ttype) + } + return results, nil + } + return []Type{}, nil + }, + }, + "queryType": &Field{ + Description: "The type that query operations will be rooted at.", + Type: NewNonNull(TypeType), + Resolve: func(p ResolveParams) (interface{}, error) { + if schema, ok := p.Source.(Schema); ok { + return schema.QueryType(), nil + } + return nil, nil + }, + }, + "mutationType": &Field{ + Description: `If this server supports mutation, the type that ` + + `mutation operations will be rooted at.`, + Type: TypeType, + Resolve: func(p ResolveParams) (interface{}, error) { + if schema, ok := p.Source.(Schema); ok { + if schema.MutationType() != nil { + return schema.MutationType(), nil + } + } + return nil, nil + }, + }, + "subscriptionType": &Field{ + Description: `If this server supports subscription, the type that ` + + `subscription operations will be rooted at.`, + Type: TypeType, + Resolve: func(p ResolveParams) (interface{}, error) { + if schema, ok := p.Source.(Schema); ok { + if schema.SubscriptionType() != nil { + return schema.SubscriptionType(), nil + } + } + return nil, nil + }, + }, + "directives": &Field{ + Description: `A list of all directives supported by this server.`, + Type: NewNonNull(NewList( + NewNonNull(DirectiveType), + )), + Resolve: func(p ResolveParams) (interface{}, error) { + if schema, ok := p.Source.(Schema); ok { + return schema.Directives(), nil + } + return nil, nil + }, + }, + }, + }) + + EnumValueType = NewObject(ObjectConfig{ + Name: "__EnumValue", + Description: "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.", + Fields: Fields{ + "name": &Field{ + Type: NewNonNull(String), + }, + "description": &Field{ + Type: String, + }, + "isDeprecated": &Field{ + Type: NewNonNull(Boolean), + Resolve: func(p ResolveParams) (interface{}, error) { + if field, ok := p.Source.(*EnumValueDefinition); ok { + return (field.DeprecationReason != ""), nil + } + return false, nil + }, + }, + "deprecationReason": &Field{ + Type: String, + }, + }, + }) + + // Again, adding field configs to __Type that have cyclic reference here + // because golang don't like them too much during init/compile-time + TypeType.AddFieldConfig("fields", &Field{ + Type: NewList(NewNonNull(FieldType)), + Args: FieldConfigArgument{ + "includeDeprecated": &ArgumentConfig{ + Type: Boolean, + DefaultValue: false, + }, + }, + Resolve: func(p ResolveParams) (interface{}, error) { + includeDeprecated, _ := p.Args["includeDeprecated"].(bool) + switch ttype := p.Source.(type) { + case *Object: + if ttype == nil { + return nil, nil + } + fields := []*FieldDefinition{} + var fieldNames sort.StringSlice + for name, field := range ttype.Fields() { + if !includeDeprecated && field.DeprecationReason != "" { + continue + } + fieldNames = append(fieldNames, name) + } + sort.Sort(fieldNames) + for _, name := range fieldNames { + fields = append(fields, ttype.Fields()[name]) + } + return fields, nil + case *Interface: + if ttype == nil { + return nil, nil + } + fields := []*FieldDefinition{} + for _, field := range ttype.Fields() { + if !includeDeprecated && field.DeprecationReason != "" { + continue + } + fields = append(fields, field) + } + return fields, nil + } + return nil, nil + }, + }) + TypeType.AddFieldConfig("interfaces", &Field{ + Type: NewList(NewNonNull(TypeType)), + Resolve: func(p ResolveParams) (interface{}, error) { + switch ttype := p.Source.(type) { + case *Object: + return ttype.Interfaces(), nil + } + return nil, nil + }, + }) + TypeType.AddFieldConfig("possibleTypes", &Field{ + Type: NewList(NewNonNull(TypeType)), + Resolve: func(p ResolveParams) (interface{}, error) { + switch ttype := p.Source.(type) { + case *Interface: + return p.Info.Schema.PossibleTypes(ttype), nil + case *Union: + return p.Info.Schema.PossibleTypes(ttype), nil + } + return nil, nil + }, + }) + TypeType.AddFieldConfig("enumValues", &Field{ + Type: NewList(NewNonNull(EnumValueType)), + Args: FieldConfigArgument{ + "includeDeprecated": &ArgumentConfig{ + Type: Boolean, + DefaultValue: false, + }, + }, + Resolve: func(p ResolveParams) (interface{}, error) { + includeDeprecated, _ := p.Args["includeDeprecated"].(bool) + switch ttype := p.Source.(type) { + case *Enum: + if includeDeprecated { + return ttype.Values(), nil + } + values := []*EnumValueDefinition{} + for _, value := range ttype.Values() { + if value.DeprecationReason != "" { + continue + } + values = append(values, value) + } + return values, nil + } + return nil, nil + }, + }) + TypeType.AddFieldConfig("inputFields", &Field{ + Type: NewList(NewNonNull(InputValueType)), + Resolve: func(p ResolveParams) (interface{}, error) { + switch ttype := p.Source.(type) { + case *InputObject: + fields := []*InputObjectField{} + for _, field := range ttype.Fields() { + fields = append(fields, field) + } + return fields, nil + } + return nil, nil + }, + }) + TypeType.AddFieldConfig("ofType", &Field{ + Type: TypeType, + }) + + // Note that these are FieldDefinition and not FieldConfig, + // so the format for args is different. + SchemaMetaFieldDef = &FieldDefinition{ + Name: "__schema", + Type: NewNonNull(SchemaType), + Description: "Access the current type schema of this server.", + Args: []*Argument{}, + Resolve: func(p ResolveParams) (interface{}, error) { + return p.Info.Schema, nil + }, + } + TypeMetaFieldDef = &FieldDefinition{ + Name: "__type", + Type: TypeType, + Description: "Request the type information of a single type.", + Args: []*Argument{ + { + PrivateName: "name", + Type: NewNonNull(String), + }, + }, + Resolve: func(p ResolveParams) (interface{}, error) { + name, ok := p.Args["name"].(string) + if !ok { + return nil, nil + } + return p.Info.Schema.Type(name), nil + }, + } + + TypeNameMetaFieldDef = &FieldDefinition{ + Name: "__typename", + Type: NewNonNull(String), + Description: "The name of the current Object type at runtime.", + Args: []*Argument{}, + Resolve: func(p ResolveParams) (interface{}, error) { + return p.Info.ParentType.Name(), nil + }, + } + +} + +// Produces a GraphQL Value AST given a Golang value. +// +// Optionally, a GraphQL type may be provided, which will be used to +// disambiguate between value primitives. +// +// | JSON Value | GraphQL Value | +// | ------------- | -------------------- | +// | Object | Input Object | +// | Array | List | +// | Boolean | Boolean | +// | String | String / Enum Value | +// | Number | Int / Float | + +func astFromValue(value interface{}, ttype Type) ast.Value { + + if ttype, ok := ttype.(*NonNull); ok { + // Note: we're not checking that the result is non-null. + // This function is not responsible for validating the input value. + val := astFromValue(value, ttype.OfType) + return val + } + if isNullish(value) { + return nil + } + valueVal := reflect.ValueOf(value) + if !valueVal.IsValid() { + return nil + } + if valueVal.Type().Kind() == reflect.Ptr { + valueVal = valueVal.Elem() + } + if !valueVal.IsValid() { + return nil + } + + // Convert Golang slice to GraphQL list. If the Type is a list, but + // the value is not an array, convert the value using the list's item type. + if ttype, ok := ttype.(*List); ok { + if valueVal.Type().Kind() == reflect.Slice { + itemType := ttype.OfType + values := []ast.Value{} + for i := 0; i < valueVal.Len(); i++ { + item := valueVal.Index(i).Interface() + itemAST := astFromValue(item, itemType) + if itemAST != nil { + values = append(values, itemAST) + } + } + return ast.NewListValue(&ast.ListValue{ + Values: values, + }) + } + // Because GraphQL will accept single values as a "list of one" when + // expecting a list, if there's a non-array value and an expected list type, + // create an AST using the list's item type. + val := astFromValue(value, ttype.OfType) + return val + } + + if valueVal.Type().Kind() == reflect.Map { + // TODO: implement astFromValue from Map to Value + } + + if value, ok := value.(bool); ok { + return ast.NewBooleanValue(&ast.BooleanValue{ + Value: value, + }) + } + if value, ok := value.(int); ok { + if ttype == Float { + return ast.NewIntValue(&ast.IntValue{ + Value: fmt.Sprintf("%v.0", value), + }) + } + return ast.NewIntValue(&ast.IntValue{ + Value: fmt.Sprintf("%v", value), + }) + } + if value, ok := value.(float32); ok { + return ast.NewFloatValue(&ast.FloatValue{ + Value: fmt.Sprintf("%v", value), + }) + } + if value, ok := value.(float64); ok { + return ast.NewFloatValue(&ast.FloatValue{ + Value: fmt.Sprintf("%v", value), + }) + } + + if value, ok := value.(string); ok { + if _, ok := ttype.(*Enum); ok { + return ast.NewEnumValue(&ast.EnumValue{ + Value: fmt.Sprintf("%v", value), + }) + } + return ast.NewStringValue(&ast.StringValue{ + Value: fmt.Sprintf("%v", value), + }) + } + + // fallback, treat as string + return ast.NewStringValue(&ast.StringValue{ + Value: fmt.Sprintf("%v", value), + }) +} diff --git a/vendor/github.com/graphql-go/graphql/kitchen-sink.graphql b/vendor/github.com/graphql-go/graphql/kitchen-sink.graphql new file mode 100644 index 00000000..d075edfd --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/kitchen-sink.graphql @@ -0,0 +1,52 @@ +# Filename: kitchen-sink.graphql + +query namedQuery($foo: ComplexFooType, $bar: Bar = DefaultBarValue) { + customUser: user(id: [987, 654]) { + id, + ... on User @defer { + field2 { + id , + alias: field1(first:10, after:$foo,) @include(if: $foo) { + id, + ...frag + } + } + } + ... @skip(unless: $foo) { + id + } + ... { + id + } + } +} + +mutation favPost { + fav(post: 123) @defer { + post { + id + } + } +} + +subscription PostFavSubscription($input: StoryLikeSubscribeInput) { + postFavSubscribe(input: $input) { + post { + favers { + count + } + favSentence { + text + } + } + } +} + +fragment frag on Follower { + foo(size: $size, bar: $b, obj: {key: "value"}) +} + +{ + unnamed(truthyVal: true, falseyVal: false), + query +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/arguments.go b/vendor/github.com/graphql-go/graphql/language/ast/arguments.go new file mode 100644 index 00000000..5f7ef0d2 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/arguments.go @@ -0,0 +1,33 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +// Argument implements Node +type Argument struct { + Kind string + Loc *Location + Name *Name + Value Value +} + +func NewArgument(arg *Argument) *Argument { + if arg == nil { + arg = &Argument{} + } + return &Argument{ + Kind: kinds.Argument, + Loc: arg.Loc, + Name: arg.Name, + Value: arg.Value, + } +} + +func (arg *Argument) GetKind() string { + return arg.Kind +} + +func (arg *Argument) GetLoc() *Location { + return arg.Loc +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/definitions.go b/vendor/github.com/graphql-go/graphql/language/ast/definitions.go new file mode 100644 index 00000000..cd527f0a --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/definitions.go @@ -0,0 +1,247 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +type Definition interface { + GetOperation() string + GetVariableDefinitions() []*VariableDefinition + GetSelectionSet() *SelectionSet + GetKind() string + GetLoc() *Location +} + +// Ensure that all definition types implements Definition interface +var _ Definition = (*OperationDefinition)(nil) +var _ Definition = (*FragmentDefinition)(nil) +var _ Definition = (TypeSystemDefinition)(nil) // experimental non-spec addition. + +// Note: subscription is an experimental non-spec addition. +const ( + OperationTypeQuery = "query" + OperationTypeMutation = "mutation" + OperationTypeSubscription = "subscription" +) + +// OperationDefinition implements Node, Definition +type OperationDefinition struct { + Kind string + Loc *Location + Operation string + Name *Name + VariableDefinitions []*VariableDefinition + Directives []*Directive + SelectionSet *SelectionSet +} + +func NewOperationDefinition(op *OperationDefinition) *OperationDefinition { + if op == nil { + op = &OperationDefinition{} + } + return &OperationDefinition{ + Kind: kinds.OperationDefinition, + Loc: op.Loc, + Operation: op.Operation, + Name: op.Name, + VariableDefinitions: op.VariableDefinitions, + Directives: op.Directives, + SelectionSet: op.SelectionSet, + } +} + +func (op *OperationDefinition) GetKind() string { + return op.Kind +} + +func (op *OperationDefinition) GetLoc() *Location { + return op.Loc +} + +func (op *OperationDefinition) GetOperation() string { + return op.Operation +} + +func (op *OperationDefinition) GetName() *Name { + return op.Name +} + +func (op *OperationDefinition) GetVariableDefinitions() []*VariableDefinition { + return op.VariableDefinitions +} + +func (op *OperationDefinition) GetDirectives() []*Directive { + return op.Directives +} + +func (op *OperationDefinition) GetSelectionSet() *SelectionSet { + return op.SelectionSet +} + +// FragmentDefinition implements Node, Definition +type FragmentDefinition struct { + Kind string + Loc *Location + Operation string + Name *Name + VariableDefinitions []*VariableDefinition + TypeCondition *Named + Directives []*Directive + SelectionSet *SelectionSet +} + +func NewFragmentDefinition(fd *FragmentDefinition) *FragmentDefinition { + if fd == nil { + fd = &FragmentDefinition{} + } + return &FragmentDefinition{ + Kind: kinds.FragmentDefinition, + Loc: fd.Loc, + Operation: fd.Operation, + Name: fd.Name, + VariableDefinitions: fd.VariableDefinitions, + TypeCondition: fd.TypeCondition, + Directives: fd.Directives, + SelectionSet: fd.SelectionSet, + } +} + +func (fd *FragmentDefinition) GetKind() string { + return fd.Kind +} + +func (fd *FragmentDefinition) GetLoc() *Location { + return fd.Loc +} + +func (fd *FragmentDefinition) GetOperation() string { + return fd.Operation +} + +func (fd *FragmentDefinition) GetName() *Name { + return fd.Name +} + +func (fd *FragmentDefinition) GetVariableDefinitions() []*VariableDefinition { + return fd.VariableDefinitions +} + +func (fd *FragmentDefinition) GetSelectionSet() *SelectionSet { + return fd.SelectionSet +} + +// VariableDefinition implements Node +type VariableDefinition struct { + Kind string + Loc *Location + Variable *Variable + Type Type + DefaultValue Value +} + +func NewVariableDefinition(vd *VariableDefinition) *VariableDefinition { + if vd == nil { + vd = &VariableDefinition{} + } + return &VariableDefinition{ + Kind: kinds.VariableDefinition, + Loc: vd.Loc, + Variable: vd.Variable, + Type: vd.Type, + DefaultValue: vd.DefaultValue, + } +} + +func (vd *VariableDefinition) GetKind() string { + return vd.Kind +} + +func (vd *VariableDefinition) GetLoc() *Location { + return vd.Loc +} + +// TypeExtensionDefinition implements Node, Definition +type TypeExtensionDefinition struct { + Kind string + Loc *Location + Definition *ObjectDefinition +} + +func NewTypeExtensionDefinition(def *TypeExtensionDefinition) *TypeExtensionDefinition { + if def == nil { + def = &TypeExtensionDefinition{} + } + return &TypeExtensionDefinition{ + Kind: kinds.TypeExtensionDefinition, + Loc: def.Loc, + Definition: def.Definition, + } +} + +func (def *TypeExtensionDefinition) GetKind() string { + return def.Kind +} + +func (def *TypeExtensionDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *TypeExtensionDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *TypeExtensionDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *TypeExtensionDefinition) GetOperation() string { + return "" +} + +// DirectiveDefinition implements Node, Definition +type DirectiveDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Arguments []*InputValueDefinition + Locations []*Name +} + +func NewDirectiveDefinition(def *DirectiveDefinition) *DirectiveDefinition { + if def == nil { + def = &DirectiveDefinition{} + } + return &DirectiveDefinition{ + Kind: kinds.DirectiveDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Arguments: def.Arguments, + Locations: def.Locations, + } +} + +func (def *DirectiveDefinition) GetKind() string { + return def.Kind +} + +func (def *DirectiveDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *DirectiveDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *DirectiveDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *DirectiveDefinition) GetOperation() string { + return "" +} + +func (def *DirectiveDefinition) GetDescription() *StringValue { + return def.Description +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/directives.go b/vendor/github.com/graphql-go/graphql/language/ast/directives.go new file mode 100644 index 00000000..0c8a8c0e --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/directives.go @@ -0,0 +1,33 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +// Directive implements Node +type Directive struct { + Kind string + Loc *Location + Name *Name + Arguments []*Argument +} + +func NewDirective(dir *Directive) *Directive { + if dir == nil { + dir = &Directive{} + } + return &Directive{ + Kind: kinds.Directive, + Loc: dir.Loc, + Name: dir.Name, + Arguments: dir.Arguments, + } +} + +func (dir *Directive) GetKind() string { + return dir.Kind +} + +func (dir *Directive) GetLoc() *Location { + return dir.Loc +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/document.go b/vendor/github.com/graphql-go/graphql/language/ast/document.go new file mode 100644 index 00000000..dcb67034 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/document.go @@ -0,0 +1,31 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +// Document implements Node +type Document struct { + Kind string + Loc *Location + Definitions []Node +} + +func NewDocument(d *Document) *Document { + if d == nil { + d = &Document{} + } + return &Document{ + Kind: kinds.Document, + Loc: d.Loc, + Definitions: d.Definitions, + } +} + +func (node *Document) GetKind() string { + return node.Kind +} + +func (node *Document) GetLoc() *Location { + return node.Loc +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/location.go b/vendor/github.com/graphql-go/graphql/language/ast/location.go new file mode 100644 index 00000000..266dc847 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/location.go @@ -0,0 +1,22 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/source" +) + +type Location struct { + Start int + End int + Source *source.Source +} + +func NewLocation(loc *Location) *Location { + if loc == nil { + loc = &Location{} + } + return &Location{ + Start: loc.Start, + End: loc.End, + Source: loc.Source, + } +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/name.go b/vendor/github.com/graphql-go/graphql/language/ast/name.go new file mode 100644 index 00000000..00fddbcd --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/name.go @@ -0,0 +1,31 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +// Name implements Node +type Name struct { + Kind string + Loc *Location + Value string +} + +func NewName(node *Name) *Name { + if node == nil { + node = &Name{} + } + return &Name{ + Kind: kinds.Name, + Value: node.Value, + Loc: node.Loc, + } +} + +func (node *Name) GetKind() string { + return node.Kind +} + +func (node *Name) GetLoc() *Location { + return node.Loc +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/node.go b/vendor/github.com/graphql-go/graphql/language/ast/node.go new file mode 100644 index 00000000..cd63a0fc --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/node.go @@ -0,0 +1,45 @@ +package ast + +type Node interface { + GetKind() string + GetLoc() *Location +} + +// The list of all possible AST node graphql. +// Ensure that all node types implements Node interface +var _ Node = (*Name)(nil) +var _ Node = (*Document)(nil) +var _ Node = (*OperationDefinition)(nil) +var _ Node = (*VariableDefinition)(nil) +var _ Node = (*Variable)(nil) +var _ Node = (*SelectionSet)(nil) +var _ Node = (*Field)(nil) +var _ Node = (*Argument)(nil) +var _ Node = (*FragmentSpread)(nil) +var _ Node = (*InlineFragment)(nil) +var _ Node = (*FragmentDefinition)(nil) +var _ Node = (*IntValue)(nil) +var _ Node = (*FloatValue)(nil) +var _ Node = (*StringValue)(nil) +var _ Node = (*BooleanValue)(nil) +var _ Node = (*EnumValue)(nil) +var _ Node = (*ListValue)(nil) +var _ Node = (*ObjectValue)(nil) +var _ Node = (*ObjectField)(nil) +var _ Node = (*Directive)(nil) +var _ Node = (*Named)(nil) +var _ Node = (*List)(nil) +var _ Node = (*NonNull)(nil) +var _ Node = (*SchemaDefinition)(nil) +var _ Node = (*OperationTypeDefinition)(nil) +var _ Node = (*ScalarDefinition)(nil) +var _ Node = (*ObjectDefinition)(nil) +var _ Node = (*FieldDefinition)(nil) +var _ Node = (*InputValueDefinition)(nil) +var _ Node = (*InterfaceDefinition)(nil) +var _ Node = (*UnionDefinition)(nil) +var _ Node = (*EnumDefinition)(nil) +var _ Node = (*EnumValueDefinition)(nil) +var _ Node = (*InputObjectDefinition)(nil) +var _ Node = (*TypeExtensionDefinition)(nil) +var _ Node = (*DirectiveDefinition)(nil) diff --git a/vendor/github.com/graphql-go/graphql/language/ast/selections.go b/vendor/github.com/graphql-go/graphql/language/ast/selections.go new file mode 100644 index 00000000..0dc0ea12 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/selections.go @@ -0,0 +1,144 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +type Selection interface { + GetSelectionSet() *SelectionSet +} + +// Ensure that all definition types implements Selection interface +var _ Selection = (*Field)(nil) +var _ Selection = (*FragmentSpread)(nil) +var _ Selection = (*InlineFragment)(nil) + +// Field implements Node, Selection +type Field struct { + Kind string + Loc *Location + Alias *Name + Name *Name + Arguments []*Argument + Directives []*Directive + SelectionSet *SelectionSet +} + +func NewField(f *Field) *Field { + if f == nil { + f = &Field{} + } + return &Field{ + Kind: kinds.Field, + Loc: f.Loc, + Alias: f.Alias, + Name: f.Name, + Arguments: f.Arguments, + Directives: f.Directives, + SelectionSet: f.SelectionSet, + } +} + +func (f *Field) GetKind() string { + return f.Kind +} + +func (f *Field) GetLoc() *Location { + return f.Loc +} + +func (f *Field) GetSelectionSet() *SelectionSet { + return f.SelectionSet +} + +// FragmentSpread implements Node, Selection +type FragmentSpread struct { + Kind string + Loc *Location + Name *Name + Directives []*Directive +} + +func NewFragmentSpread(fs *FragmentSpread) *FragmentSpread { + if fs == nil { + fs = &FragmentSpread{} + } + return &FragmentSpread{ + Kind: kinds.FragmentSpread, + Loc: fs.Loc, + Name: fs.Name, + Directives: fs.Directives, + } +} + +func (fs *FragmentSpread) GetKind() string { + return fs.Kind +} + +func (fs *FragmentSpread) GetLoc() *Location { + return fs.Loc +} + +func (fs *FragmentSpread) GetSelectionSet() *SelectionSet { + return nil +} + +// InlineFragment implements Node, Selection +type InlineFragment struct { + Kind string + Loc *Location + TypeCondition *Named + Directives []*Directive + SelectionSet *SelectionSet +} + +func NewInlineFragment(f *InlineFragment) *InlineFragment { + if f == nil { + f = &InlineFragment{} + } + return &InlineFragment{ + Kind: kinds.InlineFragment, + Loc: f.Loc, + TypeCondition: f.TypeCondition, + Directives: f.Directives, + SelectionSet: f.SelectionSet, + } +} + +func (f *InlineFragment) GetKind() string { + return f.Kind +} + +func (f *InlineFragment) GetLoc() *Location { + return f.Loc +} + +func (f *InlineFragment) GetSelectionSet() *SelectionSet { + return f.SelectionSet +} + +// SelectionSet implements Node +type SelectionSet struct { + Kind string + Loc *Location + Selections []Selection +} + +func NewSelectionSet(ss *SelectionSet) *SelectionSet { + if ss == nil { + ss = &SelectionSet{} + } + return &SelectionSet{ + Kind: kinds.SelectionSet, + Loc: ss.Loc, + Selections: ss.Selections, + } +} + +func (ss *SelectionSet) GetKind() string { + return ss.Kind +} + +func (ss *SelectionSet) GetLoc() *Location { + return ss.Loc +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/type_definitions.go b/vendor/github.com/graphql-go/graphql/language/ast/type_definitions.go new file mode 100644 index 00000000..aefa70ed --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/type_definitions.go @@ -0,0 +1,529 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +// DescribableNode are nodes that have descriptions associated with them. +type DescribableNode interface { + GetDescription() *StringValue +} + +type TypeDefinition interface { + DescribableNode + GetOperation() string + GetVariableDefinitions() []*VariableDefinition + GetSelectionSet() *SelectionSet + GetKind() string + GetLoc() *Location +} + +var _ TypeDefinition = (*ScalarDefinition)(nil) +var _ TypeDefinition = (*ObjectDefinition)(nil) +var _ TypeDefinition = (*InterfaceDefinition)(nil) +var _ TypeDefinition = (*UnionDefinition)(nil) +var _ TypeDefinition = (*EnumDefinition)(nil) +var _ TypeDefinition = (*InputObjectDefinition)(nil) + +type TypeSystemDefinition interface { + GetOperation() string + GetVariableDefinitions() []*VariableDefinition + GetSelectionSet() *SelectionSet + GetKind() string + GetLoc() *Location +} + +var _ TypeSystemDefinition = (*SchemaDefinition)(nil) +var _ TypeSystemDefinition = (TypeDefinition)(nil) +var _ TypeSystemDefinition = (*TypeExtensionDefinition)(nil) +var _ TypeSystemDefinition = (*DirectiveDefinition)(nil) + +// SchemaDefinition implements Node, Definition +type SchemaDefinition struct { + Kind string + Loc *Location + Directives []*Directive + OperationTypes []*OperationTypeDefinition +} + +func NewSchemaDefinition(def *SchemaDefinition) *SchemaDefinition { + if def == nil { + def = &SchemaDefinition{} + } + return &SchemaDefinition{ + Kind: kinds.SchemaDefinition, + Loc: def.Loc, + Directives: def.Directives, + OperationTypes: def.OperationTypes, + } +} + +func (def *SchemaDefinition) GetKind() string { + return def.Kind +} + +func (def *SchemaDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *SchemaDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *SchemaDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *SchemaDefinition) GetOperation() string { + return "" +} + +// OperationTypeDefinition implements Node, Definition +type OperationTypeDefinition struct { + Kind string + Loc *Location + Operation string + Type *Named +} + +func NewOperationTypeDefinition(def *OperationTypeDefinition) *OperationTypeDefinition { + if def == nil { + def = &OperationTypeDefinition{} + } + return &OperationTypeDefinition{ + Kind: kinds.OperationTypeDefinition, + Loc: def.Loc, + Operation: def.Operation, + Type: def.Type, + } +} + +func (def *OperationTypeDefinition) GetKind() string { + return def.Kind +} + +func (def *OperationTypeDefinition) GetLoc() *Location { + return def.Loc +} + +// ScalarDefinition implements Node, Definition +type ScalarDefinition struct { + Kind string + Loc *Location + Description *StringValue + Name *Name + Directives []*Directive +} + +func NewScalarDefinition(def *ScalarDefinition) *ScalarDefinition { + if def == nil { + def = &ScalarDefinition{} + } + return &ScalarDefinition{ + Kind: kinds.ScalarDefinition, + Loc: def.Loc, + Description: def.Description, + Name: def.Name, + Directives: def.Directives, + } +} + +func (def *ScalarDefinition) GetKind() string { + return def.Kind +} + +func (def *ScalarDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *ScalarDefinition) GetName() *Name { + return def.Name +} + +func (def *ScalarDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *ScalarDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *ScalarDefinition) GetOperation() string { + return "" +} + +func (def *ScalarDefinition) GetDescription() *StringValue { + return def.Description +} + +// ObjectDefinition implements Node, Definition +type ObjectDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Interfaces []*Named + Directives []*Directive + Fields []*FieldDefinition +} + +func NewObjectDefinition(def *ObjectDefinition) *ObjectDefinition { + if def == nil { + def = &ObjectDefinition{} + } + return &ObjectDefinition{ + Kind: kinds.ObjectDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Interfaces: def.Interfaces, + Directives: def.Directives, + Fields: def.Fields, + } +} + +func (def *ObjectDefinition) GetKind() string { + return def.Kind +} + +func (def *ObjectDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *ObjectDefinition) GetName() *Name { + return def.Name +} + +func (def *ObjectDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *ObjectDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *ObjectDefinition) GetOperation() string { + return "" +} + +func (def *ObjectDefinition) GetDescription() *StringValue { + return def.Description +} + +// FieldDefinition implements Node +type FieldDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Arguments []*InputValueDefinition + Type Type + Directives []*Directive +} + +func NewFieldDefinition(def *FieldDefinition) *FieldDefinition { + if def == nil { + def = &FieldDefinition{} + } + return &FieldDefinition{ + Kind: kinds.FieldDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Arguments: def.Arguments, + Type: def.Type, + Directives: def.Directives, + } +} + +func (def *FieldDefinition) GetKind() string { + return def.Kind +} + +func (def *FieldDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *FieldDefinition) GetDescription() *StringValue { + return def.Description +} + +// InputValueDefinition implements Node +type InputValueDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Type Type + DefaultValue Value + Directives []*Directive +} + +func NewInputValueDefinition(def *InputValueDefinition) *InputValueDefinition { + if def == nil { + def = &InputValueDefinition{} + } + return &InputValueDefinition{ + Kind: kinds.InputValueDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Type: def.Type, + DefaultValue: def.DefaultValue, + Directives: def.Directives, + } +} + +func (def *InputValueDefinition) GetKind() string { + return def.Kind +} + +func (def *InputValueDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *InputValueDefinition) GetDescription() *StringValue { + return def.Description +} + +// InterfaceDefinition implements Node, Definition +type InterfaceDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Directives []*Directive + Fields []*FieldDefinition +} + +func NewInterfaceDefinition(def *InterfaceDefinition) *InterfaceDefinition { + if def == nil { + def = &InterfaceDefinition{} + } + return &InterfaceDefinition{ + Kind: kinds.InterfaceDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Directives: def.Directives, + Fields: def.Fields, + } +} + +func (def *InterfaceDefinition) GetKind() string { + return def.Kind +} + +func (def *InterfaceDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *InterfaceDefinition) GetName() *Name { + return def.Name +} + +func (def *InterfaceDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *InterfaceDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *InterfaceDefinition) GetOperation() string { + return "" +} + +func (def *InterfaceDefinition) GetDescription() *StringValue { + return def.Description +} + +// UnionDefinition implements Node, Definition +type UnionDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Directives []*Directive + Types []*Named +} + +func NewUnionDefinition(def *UnionDefinition) *UnionDefinition { + if def == nil { + def = &UnionDefinition{} + } + return &UnionDefinition{ + Kind: kinds.UnionDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Directives: def.Directives, + Types: def.Types, + } +} + +func (def *UnionDefinition) GetKind() string { + return def.Kind +} + +func (def *UnionDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *UnionDefinition) GetName() *Name { + return def.Name +} + +func (def *UnionDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *UnionDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *UnionDefinition) GetOperation() string { + return "" +} + +func (def *UnionDefinition) GetDescription() *StringValue { + return def.Description +} + +// EnumDefinition implements Node, Definition +type EnumDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Directives []*Directive + Values []*EnumValueDefinition +} + +func NewEnumDefinition(def *EnumDefinition) *EnumDefinition { + if def == nil { + def = &EnumDefinition{} + } + return &EnumDefinition{ + Kind: kinds.EnumDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Directives: def.Directives, + Values: def.Values, + } +} + +func (def *EnumDefinition) GetKind() string { + return def.Kind +} + +func (def *EnumDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *EnumDefinition) GetName() *Name { + return def.Name +} + +func (def *EnumDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *EnumDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *EnumDefinition) GetOperation() string { + return "" +} + +func (def *EnumDefinition) GetDescription() *StringValue { + return def.Description +} + +// EnumValueDefinition implements Node, Definition +type EnumValueDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Directives []*Directive +} + +func NewEnumValueDefinition(def *EnumValueDefinition) *EnumValueDefinition { + if def == nil { + def = &EnumValueDefinition{} + } + return &EnumValueDefinition{ + Kind: kinds.EnumValueDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Directives: def.Directives, + } +} + +func (def *EnumValueDefinition) GetKind() string { + return def.Kind +} + +func (def *EnumValueDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *EnumValueDefinition) GetDescription() *StringValue { + return def.Description +} + +// InputObjectDefinition implements Node, Definition +type InputObjectDefinition struct { + Kind string + Loc *Location + Name *Name + Description *StringValue + Directives []*Directive + Fields []*InputValueDefinition +} + +func NewInputObjectDefinition(def *InputObjectDefinition) *InputObjectDefinition { + if def == nil { + def = &InputObjectDefinition{} + } + return &InputObjectDefinition{ + Kind: kinds.InputObjectDefinition, + Loc: def.Loc, + Name: def.Name, + Description: def.Description, + Directives: def.Directives, + Fields: def.Fields, + } +} + +func (def *InputObjectDefinition) GetKind() string { + return def.Kind +} + +func (def *InputObjectDefinition) GetLoc() *Location { + return def.Loc +} + +func (def *InputObjectDefinition) GetName() *Name { + return def.Name +} + +func (def *InputObjectDefinition) GetVariableDefinitions() []*VariableDefinition { + return []*VariableDefinition{} +} + +func (def *InputObjectDefinition) GetSelectionSet() *SelectionSet { + return &SelectionSet{} +} + +func (def *InputObjectDefinition) GetOperation() string { + return "" +} + +func (def *InputObjectDefinition) GetDescription() *StringValue { + return def.Description +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/types.go b/vendor/github.com/graphql-go/graphql/language/ast/types.go new file mode 100644 index 00000000..27f00997 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/types.go @@ -0,0 +1,106 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +type Type interface { + GetKind() string + GetLoc() *Location + String() string +} + +// Ensure that all value types implements Value interface +var _ Type = (*Named)(nil) +var _ Type = (*List)(nil) +var _ Type = (*NonNull)(nil) + +// Named implements Node, Type +type Named struct { + Kind string + Loc *Location + Name *Name +} + +func NewNamed(t *Named) *Named { + if t == nil { + t = &Named{} + } + return &Named{ + Kind: kinds.Named, + Loc: t.Loc, + Name: t.Name, + } +} + +func (t *Named) GetKind() string { + return t.Kind +} + +func (t *Named) GetLoc() *Location { + return t.Loc +} + +func (t *Named) String() string { + return t.GetKind() +} + +// List implements Node, Type +type List struct { + Kind string + Loc *Location + Type Type +} + +func NewList(t *List) *List { + if t == nil { + t = &List{} + } + return &List{ + Kind: kinds.List, + Loc: t.Loc, + Type: t.Type, + } +} + +func (t *List) GetKind() string { + return t.Kind +} + +func (t *List) GetLoc() *Location { + return t.Loc +} + +func (t *List) String() string { + return t.GetKind() +} + +// NonNull implements Node, Type +type NonNull struct { + Kind string + Loc *Location + Type Type +} + +func NewNonNull(t *NonNull) *NonNull { + if t == nil { + t = &NonNull{} + } + return &NonNull{ + Kind: kinds.NonNull, + Loc: t.Loc, + Type: t.Type, + } +} + +func (t *NonNull) GetKind() string { + return t.Kind +} + +func (t *NonNull) GetLoc() *Location { + return t.Loc +} + +func (t *NonNull) String() string { + return t.GetKind() +} diff --git a/vendor/github.com/graphql-go/graphql/language/ast/values.go b/vendor/github.com/graphql-go/graphql/language/ast/values.go new file mode 100644 index 00000000..67912bdc --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/ast/values.go @@ -0,0 +1,305 @@ +package ast + +import ( + "github.com/graphql-go/graphql/language/kinds" +) + +type Value interface { + GetValue() interface{} + GetKind() string + GetLoc() *Location +} + +// Ensure that all value types implements Value interface +var _ Value = (*Variable)(nil) +var _ Value = (*IntValue)(nil) +var _ Value = (*FloatValue)(nil) +var _ Value = (*StringValue)(nil) +var _ Value = (*BooleanValue)(nil) +var _ Value = (*EnumValue)(nil) +var _ Value = (*ListValue)(nil) +var _ Value = (*ObjectValue)(nil) + +// Variable implements Node, Value +type Variable struct { + Kind string + Loc *Location + Name *Name +} + +func NewVariable(v *Variable) *Variable { + if v == nil { + v = &Variable{} + } + return &Variable{ + Kind: kinds.Variable, + Loc: v.Loc, + Name: v.Name, + } +} + +func (v *Variable) GetKind() string { + return v.Kind +} + +func (v *Variable) GetLoc() *Location { + return v.Loc +} + +// GetValue alias to Variable.GetName() +func (v *Variable) GetValue() interface{} { + return v.GetName() +} + +func (v *Variable) GetName() interface{} { + return v.Name +} + +// IntValue implements Node, Value +type IntValue struct { + Kind string + Loc *Location + Value string +} + +func NewIntValue(v *IntValue) *IntValue { + if v == nil { + v = &IntValue{} + } + return &IntValue{ + Kind: kinds.IntValue, + Loc: v.Loc, + Value: v.Value, + } +} + +func (v *IntValue) GetKind() string { + return v.Kind +} + +func (v *IntValue) GetLoc() *Location { + return v.Loc +} + +func (v *IntValue) GetValue() interface{} { + return v.Value +} + +// FloatValue implements Node, Value +type FloatValue struct { + Kind string + Loc *Location + Value string +} + +func NewFloatValue(v *FloatValue) *FloatValue { + if v == nil { + v = &FloatValue{} + } + return &FloatValue{ + Kind: kinds.FloatValue, + Loc: v.Loc, + Value: v.Value, + } +} + +func (v *FloatValue) GetKind() string { + return v.Kind +} + +func (v *FloatValue) GetLoc() *Location { + return v.Loc +} + +func (v *FloatValue) GetValue() interface{} { + return v.Value +} + +// StringValue implements Node, Value +type StringValue struct { + Kind string + Loc *Location + Value string +} + +func NewStringValue(v *StringValue) *StringValue { + if v == nil { + v = &StringValue{} + } + return &StringValue{ + Kind: kinds.StringValue, + Loc: v.Loc, + Value: v.Value, + } +} + +func (v *StringValue) GetKind() string { + return v.Kind +} + +func (v *StringValue) GetLoc() *Location { + return v.Loc +} + +func (v *StringValue) GetValue() interface{} { + return v.Value +} + +// BooleanValue implements Node, Value +type BooleanValue struct { + Kind string + Loc *Location + Value bool +} + +func NewBooleanValue(v *BooleanValue) *BooleanValue { + if v == nil { + v = &BooleanValue{} + } + return &BooleanValue{ + Kind: kinds.BooleanValue, + Loc: v.Loc, + Value: v.Value, + } +} + +func (v *BooleanValue) GetKind() string { + return v.Kind +} + +func (v *BooleanValue) GetLoc() *Location { + return v.Loc +} + +func (v *BooleanValue) GetValue() interface{} { + return v.Value +} + +// EnumValue implements Node, Value +type EnumValue struct { + Kind string + Loc *Location + Value string +} + +func NewEnumValue(v *EnumValue) *EnumValue { + if v == nil { + v = &EnumValue{} + } + return &EnumValue{ + Kind: kinds.EnumValue, + Loc: v.Loc, + Value: v.Value, + } +} + +func (v *EnumValue) GetKind() string { + return v.Kind +} + +func (v *EnumValue) GetLoc() *Location { + return v.Loc +} + +func (v *EnumValue) GetValue() interface{} { + return v.Value +} + +// ListValue implements Node, Value +type ListValue struct { + Kind string + Loc *Location + Values []Value +} + +func NewListValue(v *ListValue) *ListValue { + if v == nil { + v = &ListValue{} + } + return &ListValue{ + Kind: kinds.ListValue, + Loc: v.Loc, + Values: v.Values, + } +} + +func (v *ListValue) GetKind() string { + return v.Kind +} + +func (v *ListValue) GetLoc() *Location { + return v.Loc +} + +// GetValue alias to ListValue.GetValues() +func (v *ListValue) GetValue() interface{} { + return v.GetValues() +} + +func (v *ListValue) GetValues() interface{} { + // TODO: verify ObjectValue.GetValue() + return v.Values +} + +// ObjectValue implements Node, Value +type ObjectValue struct { + Kind string + Loc *Location + Fields []*ObjectField +} + +func NewObjectValue(v *ObjectValue) *ObjectValue { + if v == nil { + v = &ObjectValue{} + } + return &ObjectValue{ + Kind: kinds.ObjectValue, + Loc: v.Loc, + Fields: v.Fields, + } +} + +func (v *ObjectValue) GetKind() string { + return v.Kind +} + +func (v *ObjectValue) GetLoc() *Location { + return v.Loc +} + +func (v *ObjectValue) GetValue() interface{} { + // TODO: verify ObjectValue.GetValue() + return v.Fields +} + +// ObjectField implements Node, Value +type ObjectField struct { + Kind string + Name *Name + Loc *Location + Value Value +} + +func NewObjectField(f *ObjectField) *ObjectField { + if f == nil { + f = &ObjectField{} + } + return &ObjectField{ + Kind: kinds.ObjectField, + Loc: f.Loc, + Name: f.Name, + Value: f.Value, + } +} + +func (f *ObjectField) GetKind() string { + return f.Kind +} + +func (f *ObjectField) GetLoc() *Location { + return f.Loc +} + +func (f *ObjectField) GetValue() interface{} { + return f.Value +} diff --git a/vendor/github.com/graphql-go/graphql/language/kinds/kinds.go b/vendor/github.com/graphql-go/graphql/language/kinds/kinds.go new file mode 100644 index 00000000..40bc994e --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/kinds/kinds.go @@ -0,0 +1,59 @@ +package kinds + +const ( + // Name + Name = "Name" + + // Document + Document = "Document" + OperationDefinition = "OperationDefinition" + VariableDefinition = "VariableDefinition" + Variable = "Variable" + SelectionSet = "SelectionSet" + Field = "Field" + Argument = "Argument" + + // Fragments + FragmentSpread = "FragmentSpread" + InlineFragment = "InlineFragment" + FragmentDefinition = "FragmentDefinition" + + // Values + IntValue = "IntValue" + FloatValue = "FloatValue" + StringValue = "StringValue" + BooleanValue = "BooleanValue" + EnumValue = "EnumValue" + ListValue = "ListValue" + ObjectValue = "ObjectValue" + ObjectField = "ObjectField" + + // Directives + Directive = "Directive" + + // Types + Named = "Named" // previously NamedType + List = "List" // previously ListType + NonNull = "NonNull" // previously NonNull + + // Type System Definitions + SchemaDefinition = "SchemaDefinition" + OperationTypeDefinition = "OperationTypeDefinition" + + // Types Definitions + ScalarDefinition = "ScalarDefinition" // previously ScalarTypeDefinition + ObjectDefinition = "ObjectDefinition" // previously ObjectTypeDefinition + FieldDefinition = "FieldDefinition" + InputValueDefinition = "InputValueDefinition" + InterfaceDefinition = "InterfaceDefinition" // previously InterfaceTypeDefinition + UnionDefinition = "UnionDefinition" // previously UnionTypeDefinition + EnumDefinition = "EnumDefinition" // previously EnumTypeDefinition + EnumValueDefinition = "EnumValueDefinition" + InputObjectDefinition = "InputObjectDefinition" // previously InputObjectTypeDefinition + + // Types Extensions + TypeExtensionDefinition = "TypeExtensionDefinition" + + // Directive Definitions + DirectiveDefinition = "DirectiveDefinition" +) diff --git a/vendor/github.com/graphql-go/graphql/language/lexer/lexer.go b/vendor/github.com/graphql-go/graphql/language/lexer/lexer.go new file mode 100644 index 00000000..62589f4e --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/lexer/lexer.go @@ -0,0 +1,656 @@ +package lexer + +import ( + "bytes" + "fmt" + "regexp" + "strings" + "unicode/utf8" + + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/source" +) + +const ( + EOF = iota + 1 + BANG + DOLLAR + PAREN_L + PAREN_R + SPREAD + COLON + EQUALS + AT + BRACKET_L + BRACKET_R + BRACE_L + PIPE + BRACE_R + NAME + INT + FLOAT + STRING + BLOCK_STRING +) + +var TokenKind map[int]int +var tokenDescription map[int]string + +func init() { + TokenKind = make(map[int]int) + tokenDescription = make(map[int]string) + TokenKind[EOF] = EOF + TokenKind[BANG] = BANG + TokenKind[DOLLAR] = DOLLAR + TokenKind[PAREN_L] = PAREN_L + TokenKind[PAREN_R] = PAREN_R + TokenKind[SPREAD] = SPREAD + TokenKind[COLON] = COLON + TokenKind[EQUALS] = EQUALS + TokenKind[AT] = AT + TokenKind[BRACKET_L] = BRACKET_L + TokenKind[BRACKET_R] = BRACKET_R + TokenKind[BRACE_L] = BRACE_L + TokenKind[PIPE] = PIPE + TokenKind[BRACE_R] = BRACE_R + TokenKind[NAME] = NAME + TokenKind[INT] = INT + TokenKind[FLOAT] = FLOAT + TokenKind[STRING] = STRING + TokenKind[BLOCK_STRING] = BLOCK_STRING + tokenDescription[TokenKind[EOF]] = "EOF" + tokenDescription[TokenKind[BANG]] = "!" + tokenDescription[TokenKind[DOLLAR]] = "$" + tokenDescription[TokenKind[PAREN_L]] = "(" + tokenDescription[TokenKind[PAREN_R]] = ")" + tokenDescription[TokenKind[SPREAD]] = "..." + tokenDescription[TokenKind[COLON]] = ":" + tokenDescription[TokenKind[EQUALS]] = "=" + tokenDescription[TokenKind[AT]] = "@" + tokenDescription[TokenKind[BRACKET_L]] = "[" + tokenDescription[TokenKind[BRACKET_R]] = "]" + tokenDescription[TokenKind[BRACE_L]] = "{" + tokenDescription[TokenKind[PIPE]] = "|" + tokenDescription[TokenKind[BRACE_R]] = "}" + tokenDescription[TokenKind[NAME]] = "Name" + tokenDescription[TokenKind[INT]] = "Int" + tokenDescription[TokenKind[FLOAT]] = "Float" + tokenDescription[TokenKind[STRING]] = "String" + tokenDescription[TokenKind[BLOCK_STRING]] = "BlockString" +} + +// Token is a representation of a lexed Token. Value only appears for non-punctuation +// tokens: NAME, INT, FLOAT, and STRING. +type Token struct { + Kind int + Start int + End int + Value string +} + +type Lexer func(resetPosition int) (Token, error) + +func Lex(s *source.Source) Lexer { + var prevPosition int + return func(resetPosition int) (Token, error) { + if resetPosition == 0 { + resetPosition = prevPosition + } + token, err := readToken(s, resetPosition) + if err != nil { + return token, err + } + prevPosition = token.End + return token, nil + } +} + +// Reads an alphanumeric + underscore name from the source. +// [_A-Za-z][_0-9A-Za-z]* +// position: Points to the byte position in the byte array +// runePosition: Points to the rune position in the byte array +func readName(source *source.Source, position, runePosition int) Token { + body := source.Body + bodyLength := len(body) + endByte := position + 1 + endRune := runePosition + 1 + for { + code, _ := runeAt(body, endByte) + if (endByte != bodyLength) && + (code == '_' || // _ + code >= '0' && code <= '9' || // 0-9 + code >= 'A' && code <= 'Z' || // A-Z + code >= 'a' && code <= 'z') { // a-z + endByte++ + endRune++ + continue + } else { + break + } + } + return makeToken(TokenKind[NAME], runePosition, endRune, string(body[position:endByte])) +} + +// Reads a number token from the source file, 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 readNumber(s *source.Source, start int, firstCode rune, codeLength int) (Token, error) { + code := firstCode + body := s.Body + position := start + isFloat := false + if code == '-' { // - + position += codeLength + code, codeLength = runeAt(body, position) + } + if code == '0' { // 0 + position += codeLength + code, codeLength = runeAt(body, position) + if code >= '0' && code <= '9' { + description := fmt.Sprintf("Invalid number, unexpected digit after 0: %v.", printCharCode(code)) + return Token{}, gqlerrors.NewSyntaxError(s, position, description) + } + } else { + p, err := readDigits(s, position, code, codeLength) + if err != nil { + return Token{}, err + } + position = p + code, codeLength = runeAt(body, position) + } + if code == '.' { // . + isFloat = true + position += codeLength + code, codeLength = runeAt(body, position) + p, err := readDigits(s, position, code, codeLength) + if err != nil { + return Token{}, err + } + position = p + code, codeLength = runeAt(body, position) + } + if code == 'E' || code == 'e' { // E e + isFloat = true + position += codeLength + code, codeLength = runeAt(body, position) + if code == '+' || code == '-' { // + - + position += codeLength + code, codeLength = runeAt(body, position) + } + p, err := readDigits(s, position, code, codeLength) + if err != nil { + return Token{}, err + } + position = p + } + kind := TokenKind[INT] + if isFloat { + kind = TokenKind[FLOAT] + } + + return makeToken(kind, start, position, string(body[start:position])), nil +} + +// Returns the new position in the source after reading digits. +func readDigits(s *source.Source, start int, firstCode rune, codeLength int) (int, error) { + body := s.Body + position := start + code := firstCode + if code >= '0' && code <= '9' { // 0 - 9 + for { + if code >= '0' && code <= '9' { // 0 - 9 + position += codeLength + code, codeLength = runeAt(body, position) + continue + } else { + break + } + } + return position, nil + } + var description string + description = fmt.Sprintf("Invalid number, expected digit but got: %v.", printCharCode(code)) + return position, gqlerrors.NewSyntaxError(s, position, description) +} + +func readString(s *source.Source, start int) (Token, error) { + body := s.Body + position := start + 1 + runePosition := start + 1 + chunkStart := position + var code rune + var n int + var valueBuffer bytes.Buffer + for { + code, n = runeAt(body, position) + if position < len(body) && + // not LineTerminator + code != 0x000A && code != 0x000D && + // not Quote (") + code != '"' { + + // SourceCharacter + if code < 0x0020 && code != 0x0009 { + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf(`Invalid character within String: %v.`, printCharCode(code))) + } + position += n + runePosition++ + if code == '\\' { // \ + valueBuffer.Write(body[chunkStart : position-1]) + code, n = runeAt(body, position) + switch code { + case '"': + valueBuffer.WriteRune('"') + break + case '/': + valueBuffer.WriteRune('/') + break + case '\\': + valueBuffer.WriteRune('\\') + break + case 'b': + valueBuffer.WriteRune('\b') + break + case 'f': + valueBuffer.WriteRune('\f') + break + case 'n': + valueBuffer.WriteRune('\n') + break + case 'r': + valueBuffer.WriteRune('\r') + break + case 't': + valueBuffer.WriteRune('\t') + break + case 'u': + // Check if there are at least 4 bytes available + if len(body) <= position+4 { + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, + fmt.Sprintf("Invalid character escape sequence: "+ + "\\u%v", string(body[position+1:]))) + } + charCode := uniCharCode( + rune(body[position+1]), + rune(body[position+2]), + rune(body[position+3]), + rune(body[position+4]), + ) + if charCode < 0 { + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, + fmt.Sprintf("Invalid character escape sequence: "+ + "\\u%v", string(body[position+1:position+5]))) + } + valueBuffer.WriteRune(charCode) + position += 4 + runePosition += 4 + break + default: + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, + fmt.Sprintf(`Invalid character escape sequence: \\%c.`, code)) + } + position += n + runePosition++ + chunkStart = position + } + continue + } else { + break + } + } + if code != '"' { // quote (") + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, "Unterminated string.") + } + stringContent := body[chunkStart:position] + valueBuffer.Write(stringContent) + value := valueBuffer.String() + return makeToken(TokenKind[STRING], start, position+1, value), nil +} + +// readBlockString reads a block string token from the source file. +// +// """("?"?(\\"""|\\(?!=""")|[^"\\]))*""" +func readBlockString(s *source.Source, start int) (Token, error) { + body := s.Body + position := start + 3 + runePosition := start + 3 + chunkStart := position + var valueBuffer bytes.Buffer + + for { + // Stop if we've reached the end of the buffer + if position >= len(body) { + break + } + + code, n := runeAt(body, position) + + // Closing Triple-Quote (""") + if code == '"' { + x, _ := runeAt(body, position+1) + y, _ := runeAt(body, position+2) + if x == '"' && y == '"' { + stringContent := body[chunkStart:position] + valueBuffer.Write(stringContent) + value := blockStringValue(valueBuffer.String()) + return makeToken(TokenKind[BLOCK_STRING], start, position+3, value), nil + } + } + + // SourceCharacter + if code < 0x0020 && + code != 0x0009 && + code != 0x000a && + code != 0x000d { + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf(`Invalid character within String: %v.`, printCharCode(code))) + } + + // Escape Triple-Quote (\""") + if code == '\\' { // \ + x, _ := runeAt(body, position+1) + y, _ := runeAt(body, position+2) + z, _ := runeAt(body, position+3) + if x == '"' && y == '"' && z == '"' { + stringContent := append(body[chunkStart:position], []byte(`"""`)...) + valueBuffer.Write(stringContent) + position += 4 // account for `"""` characters + runePosition += 4 // " " " " + chunkStart = position + continue + } + } + + position += n + runePosition++ + } + + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, "Unterminated string.") +} + +var splitLinesRegex = regexp.MustCompile("\r\n|[\n\r]") + +// This implements the GraphQL spec's BlockStringValue() static algorithm. +// +// 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. +// +// Spec: http://facebook.github.io/graphql/draft/#BlockStringValue() +// Heavily borrows from: https://github.com/graphql/graphql-js/blob/8e0c599ceccfa8c40d6edf3b72ee2a71490b10e0/src/language/blockStringValue.js +func blockStringValue(in string) string { + // Expand a block string's raw value into independent lines. + lines := splitLinesRegex.Split(in, -1) + + // Remove common indentation from all lines but first + commonIndent := -1 + for i := 1; i < len(lines); i++ { + line := lines[i] + indent := leadingWhitespaceLen(line) + if indent < len(line) && (commonIndent == -1 || indent < commonIndent) { + commonIndent = indent + if commonIndent == 0 { + break + } + } + } + if commonIndent > 0 { + for i, line := range lines { + if commonIndent > len(line) { + continue + } + lines[i] = line[commonIndent:] + } + } + + // Remove leading blank lines. + for { + if isBlank := lineIsBlank(lines[0]); !isBlank { + break + } + lines = lines[1:] + } + + // Remove trailing blank lines. + for { + i := len(lines) - 1 + if isBlank := lineIsBlank(lines[i]); !isBlank { + break + } + lines = append(lines[:i], lines[i+1:]...) + } + + // Return a string of the lines joined with U+000A. + return strings.Join(lines, "\n") +} + +// leadingWhitespaceLen returns count of whitespace characters on given line. +func leadingWhitespaceLen(in string) (n int) { + for _, ch := range in { + if ch == ' ' || ch == '\t' { + n++ + } else { + break + } + } + return +} + +// lineIsBlank returns true when given line has no content. +func lineIsBlank(in string) bool { + return leadingWhitespaceLen(in) == len(in) +} + +// Converts four hexidecimal chars to the integer that the +// string represents. For example, uniCharCode('0','0','0','f') +// will return 15, and uniCharCode('0','0','f','f') returns 255. +// Returns a negative number on error, if a char was invalid. +// This is implemented by noting that char2hex() returns -1 on error, +// which means the result of ORing the char2hex() will also be negative. +func uniCharCode(a, b, c, d rune) rune { + return rune(char2hex(a)<<12 | char2hex(b)<<8 | char2hex(c)<<4 | char2hex(d)) +} + +// Converts a hex character to its integer value. +// '0' becomes 0, '9' becomes 9 +// 'A' becomes 10, 'F' becomes 15 +// 'a' becomes 10, 'f' becomes 15 +// Returns -1 on error. +func char2hex(a rune) int { + if a >= 48 && a <= 57 { // 0-9 + return int(a) - 48 + } else if a >= 65 && a <= 70 { // A-F + return int(a) - 55 + } else if a >= 97 && a <= 102 { + // a-f + return int(a) - 87 + } + return -1 +} + +func makeToken(kind int, start int, end int, value string) Token { + return Token{Kind: kind, Start: start, End: end, Value: value} +} + +func printCharCode(code rune) string { + // NaN/undefined represents access beyond the end of the file. + if code < 0 { + return "<EOF>" + } + // print as ASCII for printable range + if code >= 0x0020 && code < 0x007F { + return fmt.Sprintf(`"%c"`, code) + } + // Otherwise print the escaped form. e.g. `"\\u0007"` + return fmt.Sprintf(`"\\u%04X"`, code) +} + +func readToken(s *source.Source, fromPosition int) (Token, error) { + body := s.Body + bodyLength := len(body) + position, runePosition := positionAfterWhitespace(body, fromPosition) + if position >= bodyLength { + return makeToken(TokenKind[EOF], position, position, ""), nil + } + code, codeLength := runeAt(body, position) + + // SourceCharacter + if code < 0x0020 && code != 0x0009 && code != 0x000A && code != 0x000D { + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, fmt.Sprintf(`Invalid character %v`, printCharCode(code))) + } + + switch code { + // ! + case '!': + return makeToken(TokenKind[BANG], position, position+1, ""), nil + // $ + case '$': + return makeToken(TokenKind[DOLLAR], position, position+1, ""), nil + // ( + case '(': + return makeToken(TokenKind[PAREN_L], position, position+1, ""), nil + // ) + case ')': + return makeToken(TokenKind[PAREN_R], position, position+1, ""), nil + // . + case '.': + next1, _ := runeAt(body, position+1) + next2, _ := runeAt(body, position+2) + if next1 == '.' && next2 == '.' { + return makeToken(TokenKind[SPREAD], position, position+3, ""), nil + } + break + // : + case ':': + return makeToken(TokenKind[COLON], position, position+1, ""), nil + // = + case '=': + return makeToken(TokenKind[EQUALS], position, position+1, ""), nil + // @ + case '@': + return makeToken(TokenKind[AT], position, position+1, ""), nil + // [ + case '[': + return makeToken(TokenKind[BRACKET_L], position, position+1, ""), nil + // ] + case ']': + return makeToken(TokenKind[BRACKET_R], position, position+1, ""), nil + // { + case '{': + return makeToken(TokenKind[BRACE_L], position, position+1, ""), nil + // | + case '|': + return makeToken(TokenKind[PIPE], position, position+1, ""), nil + // } + case '}': + return makeToken(TokenKind[BRACE_R], position, position+1, ""), nil + // A-Z + 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': + return readName(s, position, runePosition), nil + // _ + // a-z + 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': + return readName(s, position, runePosition), nil + // - + // 0-9 + case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + token, err := readNumber(s, position, code, codeLength) + if err != nil { + return token, err + } + return token, nil + // " + case '"': + var token Token + var err error + x, _ := runeAt(body, position+1) + y, _ := runeAt(body, position+2) + if x == '"' && y == '"' { + token, err = readBlockString(s, position) + } else { + token, err = readString(s, position) + } + return token, err + } + description := fmt.Sprintf("Unexpected character %v.", printCharCode(code)) + return Token{}, gqlerrors.NewSyntaxError(s, runePosition, description) +} + +// Gets the rune from the byte array at given byte position and it's width in bytes +func runeAt(body []byte, position int) (code rune, charWidth int) { + if len(body) <= position { + // <EOF> + return -1, utf8.RuneError + } + + c := body[position] + if c < utf8.RuneSelf { + return rune(c), 1 + } + + r, n := utf8.DecodeRune(body[position:]) + return r, n +} + +// Reads from body starting at startPosition until it finds a non-whitespace +// or commented character, then returns the position of that character for lexing. +// lexing. +// Returns both byte positions and rune position +func positionAfterWhitespace(body []byte, startPosition int) (position int, runePosition int) { + bodyLength := len(body) + position = startPosition + runePosition = startPosition + for { + if position < bodyLength { + code, n := runeAt(body, position) + + // Skip Ignored + if code == 0xFEFF || // BOM + // White Space + code == 0x0009 || // tab + code == 0x0020 || // space + // Line Terminator + code == 0x000A || // new line + code == 0x000D || // carriage return + // Comma + code == 0x002C { + position += n + runePosition++ + } else if code == 35 { // # + position += n + runePosition++ + for { + code, n := runeAt(body, position) + if position < bodyLength && + code != 0 && + // SourceCharacter but not LineTerminator + (code > 0x001F || code == 0x0009) && code != 0x000A && code != 0x000D { + position += n + runePosition++ + continue + } else { + break + } + } + } else { + break + } + continue + } else { + break + } + } + return position, runePosition +} + +func GetTokenDesc(token Token) string { + if token.Value == "" { + return GetTokenKindDesc(token.Kind) + } + return fmt.Sprintf("%s \"%s\"", GetTokenKindDesc(token.Kind), token.Value) +} + +func GetTokenKindDesc(kind int) string { + return tokenDescription[kind] +} diff --git a/vendor/github.com/graphql-go/graphql/language/location/location.go b/vendor/github.com/graphql-go/graphql/language/location/location.go new file mode 100644 index 00000000..04bbde6e --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/location/location.go @@ -0,0 +1,35 @@ +package location + +import ( + "regexp" + + "github.com/graphql-go/graphql/language/source" +) + +type SourceLocation struct { + Line int `json:"line"` + Column int `json:"column"` +} + +func GetLocation(s *source.Source, position int) SourceLocation { + body := []byte{} + if s != nil { + body = s.Body + } + line := 1 + column := position + 1 + lineRegexp := regexp.MustCompile("\r\n|[\n\r]") + matches := lineRegexp.FindAllIndex(body, -1) + for _, match := range matches { + matchIndex := match[0] + if matchIndex < position { + line++ + l := len(s.Body[match[0]:match[1]]) + column = position + 1 - (matchIndex + l) + continue + } else { + break + } + } + return SourceLocation{Line: line, Column: column} +} diff --git a/vendor/github.com/graphql-go/graphql/language/parser/parser.go b/vendor/github.com/graphql-go/graphql/language/parser/parser.go new file mode 100644 index 00000000..32beef37 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/parser/parser.go @@ -0,0 +1,1636 @@ +package parser + +import ( + "fmt" + + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/lexer" + "github.com/graphql-go/graphql/language/source" +) + +type parseFn func(parser *Parser) (interface{}, error) + +type ParseOptions struct { + NoLocation bool + NoSource bool +} + +type ParseParams struct { + Source interface{} + Options ParseOptions +} + +type Parser struct { + LexToken lexer.Lexer + Source *source.Source + Options ParseOptions + PrevEnd int + Token lexer.Token +} + +func Parse(p ParseParams) (*ast.Document, error) { + var sourceObj *source.Source + switch p.Source.(type) { + case *source.Source: + sourceObj = p.Source.(*source.Source) + default: + body, _ := p.Source.(string) + sourceObj = source.NewSource(&source.Source{Body: []byte(body)}) + } + parser, err := makeParser(sourceObj, p.Options) + if err != nil { + return nil, err + } + doc, err := parseDocument(parser) + if err != nil { + return nil, err + } + return doc, nil +} + +// TODO: test and expose parseValue as a public +func parseValue(p ParseParams) (ast.Value, error) { + var value ast.Value + var sourceObj *source.Source + switch p.Source.(type) { + case *source.Source: + sourceObj = p.Source.(*source.Source) + default: + body, _ := p.Source.(string) + sourceObj = source.NewSource(&source.Source{Body: []byte(body)}) + } + parser, err := makeParser(sourceObj, p.Options) + if err != nil { + return value, err + } + value, err = parseValueLiteral(parser, false) + if err != nil { + return value, err + } + return value, nil +} + +// Converts a name lex token into a name parse node. +func parseName(parser *Parser) (*ast.Name, error) { + token, err := expect(parser, lexer.TokenKind[lexer.NAME]) + if err != nil { + return nil, err + } + return ast.NewName(&ast.Name{ + Value: token.Value, + Loc: loc(parser, token.Start), + }), nil +} + +func makeParser(s *source.Source, opts ParseOptions) (*Parser, error) { + lexToken := lexer.Lex(s) + token, err := lexToken(0) + if err != nil { + return &Parser{}, err + } + return &Parser{ + LexToken: lexToken, + Source: s, + Options: opts, + PrevEnd: 0, + Token: token, + }, nil +} + +/* Implements the parsing rules in the Document section. */ + +func parseDocument(parser *Parser) (*ast.Document, error) { + start := parser.Token.Start + var nodes []ast.Node + for { + if skp, err := skip(parser, lexer.TokenKind[lexer.EOF]); err != nil { + return nil, err + } else if skp { + break + } + if peek(parser, lexer.TokenKind[lexer.BRACE_L]) { + node, err := parseOperationDefinition(parser) + if err != nil { + return nil, err + } + nodes = append(nodes, node) + } else if peek(parser, lexer.TokenKind[lexer.NAME]) { + switch parser.Token.Value { + case "query": + fallthrough + case "mutation": + fallthrough + case "subscription": // Note: subscription is an experimental non-spec addition. + node, err := parseOperationDefinition(parser) + if err != nil { + return nil, err + } + nodes = append(nodes, node) + case "fragment": + node, err := parseFragmentDefinition(parser) + if err != nil { + return nil, err + } + nodes = append(nodes, node) + + // Note: the Type System IDL is an experimental non-spec addition. + case "schema": + fallthrough + case "scalar": + fallthrough + case "type": + fallthrough + case "interface": + fallthrough + case "union": + fallthrough + case "enum": + fallthrough + case "input": + fallthrough + case "extend": + fallthrough + case "directive": + node, err := parseTypeSystemDefinition(parser) + if err != nil { + return nil, err + } + nodes = append(nodes, node) + default: + if err := unexpected(parser, lexer.Token{}); err != nil { + return nil, err + } + } + } else if peekDescription(parser) { + node, err := parseTypeSystemDefinition(parser) + if err != nil { + return nil, err + } + nodes = append(nodes, node) + } else { + if err := unexpected(parser, lexer.Token{}); err != nil { + return nil, err + } + } + } + return ast.NewDocument(&ast.Document{ + Loc: loc(parser, start), + Definitions: nodes, + }), nil +} + +/* Implements the parsing rules in the Operations section. */ + +/** + * OperationDefinition : + * - SelectionSet + * - OperationType Name? VariableDefinitions? Directives? SelectionSet + */ +func parseOperationDefinition(parser *Parser) (*ast.OperationDefinition, error) { + start := parser.Token.Start + if peek(parser, lexer.TokenKind[lexer.BRACE_L]) { + selectionSet, err := parseSelectionSet(parser) + if err != nil { + return nil, err + } + return ast.NewOperationDefinition(&ast.OperationDefinition{ + Operation: ast.OperationTypeQuery, + Directives: []*ast.Directive{}, + SelectionSet: selectionSet, + Loc: loc(parser, start), + }), nil + } + operation, err := parseOperationType(parser) + if err != nil { + return nil, err + } + + var name *ast.Name + if peek(parser, lexer.TokenKind[lexer.NAME]) { + name, err = parseName(parser) + } + variableDefinitions, err := parseVariableDefinitions(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + selectionSet, err := parseSelectionSet(parser) + if err != nil { + return nil, err + } + return ast.NewOperationDefinition(&ast.OperationDefinition{ + Operation: operation, + Name: name, + VariableDefinitions: variableDefinitions, + Directives: directives, + SelectionSet: selectionSet, + Loc: loc(parser, start), + }), nil +} + +/** + * OperationType : one of query mutation subscription + */ +func parseOperationType(parser *Parser) (string, error) { + operationToken, err := expect(parser, lexer.TokenKind[lexer.NAME]) + if err != nil { + return "", err + } + switch operationToken.Value { + case ast.OperationTypeQuery: + return operationToken.Value, nil + case ast.OperationTypeMutation: + return operationToken.Value, nil + case ast.OperationTypeSubscription: + return operationToken.Value, nil + default: + return "", unexpected(parser, operationToken) + } +} + +/** + * VariableDefinitions : ( VariableDefinition+ ) + */ +func parseVariableDefinitions(parser *Parser) ([]*ast.VariableDefinition, error) { + variableDefinitions := []*ast.VariableDefinition{} + if peek(parser, lexer.TokenKind[lexer.PAREN_L]) { + vdefs, err := many(parser, lexer.TokenKind[lexer.PAREN_L], parseVariableDefinition, lexer.TokenKind[lexer.PAREN_R]) + for _, vdef := range vdefs { + if vdef != nil { + variableDefinitions = append(variableDefinitions, vdef.(*ast.VariableDefinition)) + } + } + if err != nil { + return variableDefinitions, err + } + return variableDefinitions, nil + } + return variableDefinitions, nil +} + +/** + * VariableDefinition : Variable : Type DefaultValue? + */ +func parseVariableDefinition(parser *Parser) (interface{}, error) { + start := parser.Token.Start + variable, err := parseVariable(parser) + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.COLON]) + if err != nil { + return nil, err + } + ttype, err := parseType(parser) + if err != nil { + return nil, err + } + var defaultValue ast.Value + if skp, err := skip(parser, lexer.TokenKind[lexer.EQUALS]); err != nil { + return nil, err + } else if skp { + dv, err := parseValueLiteral(parser, true) + if err != nil { + return nil, err + } + defaultValue = dv + } + return ast.NewVariableDefinition(&ast.VariableDefinition{ + Variable: variable, + Type: ttype, + DefaultValue: defaultValue, + Loc: loc(parser, start), + }), nil +} + +/** + * Variable : $ Name + */ +func parseVariable(parser *Parser) (*ast.Variable, error) { + start := parser.Token.Start + _, err := expect(parser, lexer.TokenKind[lexer.DOLLAR]) + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + return ast.NewVariable(&ast.Variable{ + Name: name, + Loc: loc(parser, start), + }), nil +} + +/** + * SelectionSet : { Selection+ } + */ +func parseSelectionSet(parser *Parser) (*ast.SelectionSet, error) { + start := parser.Token.Start + iSelections, err := many(parser, lexer.TokenKind[lexer.BRACE_L], parseSelection, lexer.TokenKind[lexer.BRACE_R]) + if err != nil { + return nil, err + } + selections := []ast.Selection{} + for _, iSelection := range iSelections { + if iSelection != nil { + // type assert interface{} into Selection interface + selections = append(selections, iSelection.(ast.Selection)) + } + } + + return ast.NewSelectionSet(&ast.SelectionSet{ + Selections: selections, + Loc: loc(parser, start), + }), nil +} + +/** + * Selection : + * - Field + * - FragmentSpread + * - InlineFragment + */ +func parseSelection(parser *Parser) (interface{}, error) { + if peek(parser, lexer.TokenKind[lexer.SPREAD]) { + r, err := parseFragment(parser) + return r, err + } + return parseField(parser) +} + +/** + * Field : Alias? Name Arguments? Directives? SelectionSet? + * + * Alias : Name : + */ +func parseField(parser *Parser) (*ast.Field, error) { + start := parser.Token.Start + nameOrAlias, err := parseName(parser) + if err != nil { + return nil, err + } + var ( + name *ast.Name + alias *ast.Name + ) + if skp, err := skip(parser, lexer.TokenKind[lexer.COLON]); err != nil { + return nil, err + } else if skp { + alias = nameOrAlias + n, err := parseName(parser) + if err != nil { + return nil, err + } + name = n + } else { + name = nameOrAlias + } + arguments, err := parseArguments(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + var selectionSet *ast.SelectionSet + if peek(parser, lexer.TokenKind[lexer.BRACE_L]) { + sSet, err := parseSelectionSet(parser) + if err != nil { + return nil, err + } + selectionSet = sSet + } + return ast.NewField(&ast.Field{ + Alias: alias, + Name: name, + Arguments: arguments, + Directives: directives, + SelectionSet: selectionSet, + Loc: loc(parser, start), + }), nil +} + +/** + * Arguments : ( Argument+ ) + */ +func parseArguments(parser *Parser) ([]*ast.Argument, error) { + arguments := []*ast.Argument{} + if peek(parser, lexer.TokenKind[lexer.PAREN_L]) { + iArguments, err := many(parser, lexer.TokenKind[lexer.PAREN_L], parseArgument, lexer.TokenKind[lexer.PAREN_R]) + if err != nil { + return arguments, err + } + for _, iArgument := range iArguments { + if iArgument != nil { + arguments = append(arguments, iArgument.(*ast.Argument)) + } + } + return arguments, nil + } + return arguments, nil +} + +/** + * Argument : Name : Value + */ +func parseArgument(parser *Parser) (interface{}, error) { + start := parser.Token.Start + name, err := parseName(parser) + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.COLON]) + if err != nil { + return nil, err + } + value, err := parseValueLiteral(parser, false) + if err != nil { + return nil, err + } + return ast.NewArgument(&ast.Argument{ + Name: name, + Value: value, + Loc: loc(parser, start), + }), nil +} + +/* Implements the parsing rules in the Fragments section. */ + +/** + * Corresponds to both FragmentSpread and InlineFragment in the spec. + * + * FragmentSpread : ... FragmentName Directives? + * + * InlineFragment : ... TypeCondition? Directives? SelectionSet + */ +func parseFragment(parser *Parser) (interface{}, error) { + start := parser.Token.Start + _, err := expect(parser, lexer.TokenKind[lexer.SPREAD]) + if err != nil { + return nil, err + } + if peek(parser, lexer.TokenKind[lexer.NAME]) && parser.Token.Value != "on" { + name, err := parseFragmentName(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + return ast.NewFragmentSpread(&ast.FragmentSpread{ + Name: name, + Directives: directives, + Loc: loc(parser, start), + }), nil + } + var typeCondition *ast.Named + if parser.Token.Value == "on" { + if err := advance(parser); err != nil { + return nil, err + } + name, err := parseNamed(parser) + if err != nil { + return nil, err + } + typeCondition = name + + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + selectionSet, err := parseSelectionSet(parser) + if err != nil { + return nil, err + } + return ast.NewInlineFragment(&ast.InlineFragment{ + TypeCondition: typeCondition, + Directives: directives, + SelectionSet: selectionSet, + Loc: loc(parser, start), + }), nil +} + +/** + * FragmentDefinition : + * - fragment FragmentName on TypeCondition Directives? SelectionSet + * + * TypeCondition : NamedType + */ +func parseFragmentDefinition(parser *Parser) (*ast.FragmentDefinition, error) { + start := parser.Token.Start + _, err := expectKeyWord(parser, "fragment") + if err != nil { + return nil, err + } + name, err := parseFragmentName(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "on") + if err != nil { + return nil, err + } + typeCondition, err := parseNamed(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + selectionSet, err := parseSelectionSet(parser) + if err != nil { + return nil, err + } + return ast.NewFragmentDefinition(&ast.FragmentDefinition{ + Name: name, + TypeCondition: typeCondition, + Directives: directives, + SelectionSet: selectionSet, + Loc: loc(parser, start), + }), nil +} + +/** + * FragmentName : Name but not `on` + */ +func parseFragmentName(parser *Parser) (*ast.Name, error) { + if parser.Token.Value == "on" { + return nil, unexpected(parser, lexer.Token{}) + } + return parseName(parser) +} + +/* Implements the parsing rules in the Values section. */ + +/** + * Value[Const] : + * - [~Const] Variable + * - IntValue + * - FloatValue + * - StringValue + * - BooleanValue + * - EnumValue + * - ListValue[?Const] + * - ObjectValue[?Const] + * + * BooleanValue : one of `true` `false` + * + * EnumValue : Name but not `true`, `false` or `null` + */ +func parseValueLiteral(parser *Parser, isConst bool) (ast.Value, error) { + token := parser.Token + switch token.Kind { + case lexer.TokenKind[lexer.BRACKET_L]: + return parseList(parser, isConst) + case lexer.TokenKind[lexer.BRACE_L]: + return parseObject(parser, isConst) + case lexer.TokenKind[lexer.INT]: + if err := advance(parser); err != nil { + return nil, err + } + return ast.NewIntValue(&ast.IntValue{ + Value: token.Value, + Loc: loc(parser, token.Start), + }), nil + case lexer.TokenKind[lexer.FLOAT]: + if err := advance(parser); err != nil { + return nil, err + } + return ast.NewFloatValue(&ast.FloatValue{ + Value: token.Value, + Loc: loc(parser, token.Start), + }), nil + case lexer.TokenKind[lexer.BLOCK_STRING]: + fallthrough + case lexer.TokenKind[lexer.STRING]: + return parseStringLiteral(parser) + case lexer.TokenKind[lexer.NAME]: + if token.Value == "true" || token.Value == "false" { + if err := advance(parser); err != nil { + return nil, err + } + value := true + if token.Value == "false" { + value = false + } + return ast.NewBooleanValue(&ast.BooleanValue{ + Value: value, + Loc: loc(parser, token.Start), + }), nil + } else if token.Value != "null" { + if err := advance(parser); err != nil { + return nil, err + } + return ast.NewEnumValue(&ast.EnumValue{ + Value: token.Value, + Loc: loc(parser, token.Start), + }), nil + } + case lexer.TokenKind[lexer.DOLLAR]: + if !isConst { + return parseVariable(parser) + } + } + if err := unexpected(parser, lexer.Token{}); err != nil { + return nil, err + } + return nil, nil +} + +func parseConstValue(parser *Parser) (interface{}, error) { + value, err := parseValueLiteral(parser, true) + if err != nil { + return value, err + } + return value, nil +} + +func parseValueValue(parser *Parser) (interface{}, error) { + return parseValueLiteral(parser, false) +} + +/** + * ListValue[Const] : + * - [ ] + * - [ Value[?Const]+ ] + */ +func parseList(parser *Parser, isConst bool) (*ast.ListValue, error) { + start := parser.Token.Start + var item parseFn + if isConst { + item = parseConstValue + } else { + item = parseValueValue + } + iValues, err := any(parser, lexer.TokenKind[lexer.BRACKET_L], item, lexer.TokenKind[lexer.BRACKET_R]) + if err != nil { + return nil, err + } + values := []ast.Value{} + for _, iValue := range iValues { + values = append(values, iValue.(ast.Value)) + } + return ast.NewListValue(&ast.ListValue{ + Values: values, + Loc: loc(parser, start), + }), nil +} + +/** + * ObjectValue[Const] : + * - { } + * - { ObjectField[?Const]+ } + */ +func parseObject(parser *Parser, isConst bool) (*ast.ObjectValue, error) { + start := parser.Token.Start + _, err := expect(parser, lexer.TokenKind[lexer.BRACE_L]) + if err != nil { + return nil, err + } + fields := []*ast.ObjectField{} + for { + if skp, err := skip(parser, lexer.TokenKind[lexer.BRACE_R]); err != nil { + return nil, err + } else if skp { + break + } + field, err := parseObjectField(parser, isConst) + if err != nil { + return nil, err + } + fields = append(fields, field) + } + return ast.NewObjectValue(&ast.ObjectValue{ + Fields: fields, + Loc: loc(parser, start), + }), nil +} + +/** + * ObjectField[Const] : Name : Value[?Const] + */ +func parseObjectField(parser *Parser, isConst bool) (*ast.ObjectField, error) { + start := parser.Token.Start + name, err := parseName(parser) + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.COLON]) + if err != nil { + return nil, err + } + value, err := parseValueLiteral(parser, isConst) + if err != nil { + return nil, err + } + return ast.NewObjectField(&ast.ObjectField{ + Name: name, + Value: value, + Loc: loc(parser, start), + }), nil +} + +/* Implements the parsing rules in the Directives section. */ + +/** + * Directives : Directive+ + */ +func parseDirectives(parser *Parser) ([]*ast.Directive, error) { + directives := []*ast.Directive{} + for { + if !peek(parser, lexer.TokenKind[lexer.AT]) { + break + } + directive, err := parseDirective(parser) + if err != nil { + return directives, err + } + directives = append(directives, directive) + } + return directives, nil +} + +/** + * Directive : @ Name Arguments? + */ +func parseDirective(parser *Parser) (*ast.Directive, error) { + start := parser.Token.Start + _, err := expect(parser, lexer.TokenKind[lexer.AT]) + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + args, err := parseArguments(parser) + if err != nil { + return nil, err + } + return ast.NewDirective(&ast.Directive{ + Name: name, + Arguments: args, + Loc: loc(parser, start), + }), nil +} + +/* Implements the parsing rules in the Types section. */ + +/** + * Type : + * - NamedType + * - ListType + * - NonNullType + */ +func parseType(parser *Parser) (ast.Type, error) { + start := parser.Token.Start + var ttype ast.Type + if skp, err := skip(parser, lexer.TokenKind[lexer.BRACKET_L]); err != nil { + return nil, err + } else if skp { + t, err := parseType(parser) + if err != nil { + return t, err + } + ttype = t + _, err = expect(parser, lexer.TokenKind[lexer.BRACKET_R]) + if err != nil { + return ttype, err + } + ttype = ast.NewList(&ast.List{ + Type: ttype, + Loc: loc(parser, start), + }) + } else { + name, err := parseNamed(parser) + if err != nil { + return ttype, err + } + ttype = name + } + if skp, err := skip(parser, lexer.TokenKind[lexer.BANG]); err != nil { + return nil, err + } else if skp { + ttype = ast.NewNonNull(&ast.NonNull{ + Type: ttype, + Loc: loc(parser, start), + }) + return ttype, nil + } + return ttype, nil +} + +/** + * NamedType : Name + */ +func parseNamed(parser *Parser) (*ast.Named, error) { + start := parser.Token.Start + name, err := parseName(parser) + if err != nil { + return nil, err + } + return ast.NewNamed(&ast.Named{ + Name: name, + Loc: loc(parser, start), + }), nil +} + +/* Implements the parsing rules in the Type Definition section. */ + +/** + * TypeSystemDefinition : + * - SchemaDefinition + * - TypeDefinition + * - TypeExtension + * - DirectiveDefinition + * + * TypeDefinition : + * - ScalarTypeDefinition + * - ObjectTypeDefinition + * - InterfaceTypeDefinition + * - UnionTypeDefinition + * - EnumTypeDefinition + * - InputObjectTypeDefinition + */ +func parseTypeSystemDefinition(parser *Parser) (ast.Node, error) { + var err error + + // Many definitions begin with a description and require a lookahead. + keywordToken := parser.Token + if peekDescription(parser) { + keywordToken, err = lookahead(parser) + if err != nil { + return nil, err + } + } + + if keywordToken.Kind == lexer.NAME { + switch keywordToken.Value { + case "schema": + return parseSchemaDefinition(parser) + case "scalar": + return parseScalarTypeDefinition(parser) + case "type": + return parseObjectTypeDefinition(parser) + case "interface": + return parseInterfaceTypeDefinition(parser) + case "union": + return parseUnionTypeDefinition(parser) + case "enum": + return parseEnumTypeDefinition(parser) + case "input": + return parseInputObjectTypeDefinition(parser) + case "extend": + return parseTypeExtensionDefinition(parser) + case "directive": + return parseDirectiveDefinition(parser) + } + } + + return nil, unexpected(parser, keywordToken) +} + +/** + * SchemaDefinition : schema Directives? { OperationTypeDefinition+ } + * + * OperationTypeDefinition : OperationType : NamedType + */ +func parseSchemaDefinition(parser *Parser) (*ast.SchemaDefinition, error) { + start := parser.Token.Start + _, err := expectKeyWord(parser, "schema") + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + operationTypesI, err := many( + parser, + lexer.TokenKind[lexer.BRACE_L], + parseOperationTypeDefinition, + lexer.TokenKind[lexer.BRACE_R], + ) + if err != nil { + return nil, err + } + operationTypes := []*ast.OperationTypeDefinition{} + for _, op := range operationTypesI { + if op, ok := op.(*ast.OperationTypeDefinition); ok { + operationTypes = append(operationTypes, op) + } + } + return ast.NewSchemaDefinition(&ast.SchemaDefinition{ + OperationTypes: operationTypes, + Directives: directives, + Loc: loc(parser, start), + }), nil +} + +func parseOperationTypeDefinition(parser *Parser) (interface{}, error) { + start := parser.Token.Start + operation, err := parseOperationType(parser) + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.COLON]) + if err != nil { + return nil, err + } + ttype, err := parseNamed(parser) + if err != nil { + return nil, err + } + return ast.NewOperationTypeDefinition(&ast.OperationTypeDefinition{ + Operation: operation, + Type: ttype, + Loc: loc(parser, start), + }), nil +} + +/** + * ScalarTypeDefinition : Description? scalar Name Directives? + */ +func parseScalarTypeDefinition(parser *Parser) (*ast.ScalarDefinition, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "scalar") + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + def := ast.NewScalarDefinition(&ast.ScalarDefinition{ + Name: name, + Description: description, + Directives: directives, + Loc: loc(parser, start), + }) + return def, nil +} + +/** + * ObjectTypeDefinition : + * Description? + * type Name ImplementsInterfaces? Directives? { FieldDefinition+ } + */ +func parseObjectTypeDefinition(parser *Parser) (*ast.ObjectDefinition, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "type") + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + interfaces, err := parseImplementsInterfaces(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + iFields, err := any(parser, lexer.TokenKind[lexer.BRACE_L], parseFieldDefinition, lexer.TokenKind[lexer.BRACE_R]) + if err != nil { + return nil, err + } + fields := []*ast.FieldDefinition{} + for _, iField := range iFields { + if iField != nil { + fields = append(fields, iField.(*ast.FieldDefinition)) + } + } + return ast.NewObjectDefinition(&ast.ObjectDefinition{ + Name: name, + Description: description, + Loc: loc(parser, start), + Interfaces: interfaces, + Directives: directives, + Fields: fields, + }), nil +} + +/** + * ImplementsInterfaces : implements NamedType+ + */ +func parseImplementsInterfaces(parser *Parser) ([]*ast.Named, error) { + types := []*ast.Named{} + if parser.Token.Value == "implements" { + if err := advance(parser); err != nil { + return nil, err + } + for { + ttype, err := parseNamed(parser) + if err != nil { + return types, err + } + types = append(types, ttype) + if !peek(parser, lexer.TokenKind[lexer.NAME]) { + break + } + } + } + return types, nil +} + +/** + * FieldDefinition : Description? Name ArgumentsDefinition? : Type Directives? + */ +func parseFieldDefinition(parser *Parser) (interface{}, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + args, err := parseArgumentDefs(parser) + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.COLON]) + if err != nil { + return nil, err + } + ttype, err := parseType(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + return ast.NewFieldDefinition(&ast.FieldDefinition{ + Name: name, + Description: description, + Arguments: args, + Type: ttype, + Directives: directives, + Loc: loc(parser, start), + }), nil +} + +/** + * ArgumentsDefinition : ( InputValueDefinition+ ) + */ +func parseArgumentDefs(parser *Parser) ([]*ast.InputValueDefinition, error) { + inputValueDefinitions := []*ast.InputValueDefinition{} + + if !peek(parser, lexer.TokenKind[lexer.PAREN_L]) { + return inputValueDefinitions, nil + } + iInputValueDefinitions, err := many(parser, lexer.TokenKind[lexer.PAREN_L], parseInputValueDef, lexer.TokenKind[lexer.PAREN_R]) + if err != nil { + return inputValueDefinitions, err + } + for _, iInputValueDefinition := range iInputValueDefinitions { + if iInputValueDefinition != nil { + inputValueDefinitions = append(inputValueDefinitions, iInputValueDefinition.(*ast.InputValueDefinition)) + } + } + return inputValueDefinitions, err +} + +/** + * InputValueDefinition : Description? Name : Type DefaultValue? Directives? + */ +func parseInputValueDef(parser *Parser) (interface{}, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.COLON]) + if err != nil { + return nil, err + } + ttype, err := parseType(parser) + if err != nil { + return nil, err + } + var defaultValue ast.Value + if skp, err := skip(parser, lexer.TokenKind[lexer.EQUALS]); err != nil { + return nil, err + } else if skp { + val, err := parseConstValue(parser) + if err != nil { + return nil, err + } + if val, ok := val.(ast.Value); ok { + defaultValue = val + } + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + return ast.NewInputValueDefinition(&ast.InputValueDefinition{ + Name: name, + Description: description, + Type: ttype, + DefaultValue: defaultValue, + Directives: directives, + Loc: loc(parser, start), + }), nil +} + +/** + * InterfaceTypeDefinition : + * Description? + * interface Name Directives? { FieldDefinition+ } + */ +func parseInterfaceTypeDefinition(parser *Parser) (*ast.InterfaceDefinition, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "interface") + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + iFields, err := any(parser, lexer.TokenKind[lexer.BRACE_L], parseFieldDefinition, lexer.TokenKind[lexer.BRACE_R]) + if err != nil { + return nil, err + } + fields := []*ast.FieldDefinition{} + for _, iField := range iFields { + if iField != nil { + fields = append(fields, iField.(*ast.FieldDefinition)) + } + } + return ast.NewInterfaceDefinition(&ast.InterfaceDefinition{ + Name: name, + Description: description, + Directives: directives, + Loc: loc(parser, start), + Fields: fields, + }), nil +} + +/** + * UnionTypeDefinition : Description? union Name Directives? = UnionMembers + */ +func parseUnionTypeDefinition(parser *Parser) (*ast.UnionDefinition, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "union") + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.EQUALS]) + if err != nil { + return nil, err + } + types, err := parseUnionMembers(parser) + if err != nil { + return nil, err + } + return ast.NewUnionDefinition(&ast.UnionDefinition{ + Name: name, + Description: description, + Directives: directives, + Loc: loc(parser, start), + Types: types, + }), nil +} + +/** + * UnionMembers : + * - NamedType + * - UnionMembers | NamedType + */ +func parseUnionMembers(parser *Parser) ([]*ast.Named, error) { + members := []*ast.Named{} + for { + member, err := parseNamed(parser) + if err != nil { + return members, err + } + members = append(members, member) + if skp, err := skip(parser, lexer.TokenKind[lexer.PIPE]); err != nil { + return nil, err + } else if !skp { + break + } + } + return members, nil +} + +/** + * EnumTypeDefinition : Description? enum Name Directives? { EnumValueDefinition+ } + */ +func parseEnumTypeDefinition(parser *Parser) (*ast.EnumDefinition, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "enum") + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + iEnumValueDefs, err := any(parser, lexer.TokenKind[lexer.BRACE_L], parseEnumValueDefinition, lexer.TokenKind[lexer.BRACE_R]) + if err != nil { + return nil, err + } + values := []*ast.EnumValueDefinition{} + for _, iEnumValueDef := range iEnumValueDefs { + if iEnumValueDef != nil { + values = append(values, iEnumValueDef.(*ast.EnumValueDefinition)) + } + } + return ast.NewEnumDefinition(&ast.EnumDefinition{ + Name: name, + Description: description, + Directives: directives, + Loc: loc(parser, start), + Values: values, + }), nil +} + +/** + * EnumValueDefinition : Description? EnumValue Directives? + * + * EnumValue : Name + */ +func parseEnumValueDefinition(parser *Parser) (interface{}, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + return ast.NewEnumValueDefinition(&ast.EnumValueDefinition{ + Name: name, + Description: description, + Directives: directives, + Loc: loc(parser, start), + }), nil +} + +/** + * InputObjectTypeDefinition : + * - Description? input Name Directives? { InputValueDefinition+ } + */ +func parseInputObjectTypeDefinition(parser *Parser) (*ast.InputObjectDefinition, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "input") + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + directives, err := parseDirectives(parser) + if err != nil { + return nil, err + } + iInputValueDefinitions, err := any(parser, lexer.TokenKind[lexer.BRACE_L], parseInputValueDef, lexer.TokenKind[lexer.BRACE_R]) + if err != nil { + return nil, err + } + fields := []*ast.InputValueDefinition{} + for _, iInputValueDefinition := range iInputValueDefinitions { + if iInputValueDefinition != nil { + fields = append(fields, iInputValueDefinition.(*ast.InputValueDefinition)) + } + } + return ast.NewInputObjectDefinition(&ast.InputObjectDefinition{ + Name: name, + Description: description, + Directives: directives, + Loc: loc(parser, start), + Fields: fields, + }), nil +} + +/** + * TypeExtensionDefinition : extend ObjectTypeDefinition + */ +func parseTypeExtensionDefinition(parser *Parser) (*ast.TypeExtensionDefinition, error) { + start := parser.Token.Start + _, err := expectKeyWord(parser, "extend") + if err != nil { + return nil, err + } + + definition, err := parseObjectTypeDefinition(parser) + if err != nil { + return nil, err + } + return ast.NewTypeExtensionDefinition(&ast.TypeExtensionDefinition{ + Loc: loc(parser, start), + Definition: definition, + }), nil +} + +/** + * DirectiveDefinition : + * - directive @ Name ArgumentsDefinition? on DirectiveLocations + */ +func parseDirectiveDefinition(parser *Parser) (*ast.DirectiveDefinition, error) { + start := parser.Token.Start + description, err := parseDescription(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "directive") + if err != nil { + return nil, err + } + _, err = expect(parser, lexer.TokenKind[lexer.AT]) + if err != nil { + return nil, err + } + name, err := parseName(parser) + if err != nil { + return nil, err + } + args, err := parseArgumentDefs(parser) + if err != nil { + return nil, err + } + _, err = expectKeyWord(parser, "on") + if err != nil { + return nil, err + } + locations, err := parseDirectiveLocations(parser) + if err != nil { + return nil, err + } + + return ast.NewDirectiveDefinition(&ast.DirectiveDefinition{ + Loc: loc(parser, start), + Name: name, + Description: description, + Arguments: args, + Locations: locations, + }), nil +} + +/** + * DirectiveLocations : + * - Name + * - DirectiveLocations | Name + */ +func parseDirectiveLocations(parser *Parser) ([]*ast.Name, error) { + locations := []*ast.Name{} + for { + name, err := parseName(parser) + if err != nil { + return locations, err + } + locations = append(locations, name) + + hasPipe, err := skip(parser, lexer.TokenKind[lexer.PIPE]) + if err != nil { + return locations, err + } + if !hasPipe { + break + } + } + return locations, nil +} + +func parseStringLiteral(parser *Parser) (*ast.StringValue, error) { + token := parser.Token + if err := advance(parser); err != nil { + return nil, err + } + return ast.NewStringValue(&ast.StringValue{ + Value: token.Value, + Loc: loc(parser, token.Start), + }), nil +} + +/** + * Description : StringValue + */ +func parseDescription(parser *Parser) (*ast.StringValue, error) { + if peekDescription(parser) { + return parseStringLiteral(parser) + } + return nil, nil +} + +/* Core parsing utility functions */ + +// Returns a location object, used to identify the place in +// the source that created a given parsed object. +func loc(parser *Parser, start int) *ast.Location { + if parser.Options.NoLocation { + return nil + } + if parser.Options.NoSource { + return ast.NewLocation(&ast.Location{ + Start: start, + End: parser.PrevEnd, + }) + } + return ast.NewLocation(&ast.Location{ + Start: start, + End: parser.PrevEnd, + Source: parser.Source, + }) +} + +// Moves the internal parser object to the next lexed token. +func advance(parser *Parser) error { + prevEnd := parser.Token.End + parser.PrevEnd = prevEnd + token, err := parser.LexToken(prevEnd) + if err != nil { + return err + } + parser.Token = token + return nil +} + +// lookahead retrieves the next token +func lookahead(parser *Parser) (lexer.Token, error) { + prevEnd := parser.Token.End + return parser.LexToken(prevEnd) +} + +// Determines if the next token is of a given kind +func peek(parser *Parser, Kind int) bool { + return parser.Token.Kind == Kind +} + +// peekDescription determines if the next token is a string value +func peekDescription(parser *Parser) bool { + return peek(parser, lexer.STRING) || peek(parser, lexer.BLOCK_STRING) +} + +// If the next token is of the given kind, return true after advancing +// the parser. Otherwise, do not change the parser state and return false. +func skip(parser *Parser, Kind int) (bool, error) { + if parser.Token.Kind == Kind { + err := advance(parser) + return true, err + } + return false, nil +} + +// If the next token is of the given kind, return that token after advancing +// the parser. Otherwise, do not change the parser state and return error. +func expect(parser *Parser, kind int) (lexer.Token, error) { + token := parser.Token + if token.Kind == kind { + err := advance(parser) + return token, err + } + descp := fmt.Sprintf("Expected %s, found %s", lexer.GetTokenKindDesc(kind), lexer.GetTokenDesc(token)) + return token, gqlerrors.NewSyntaxError(parser.Source, token.Start, descp) +} + +// If the next token is a keyword with the given value, return that token after +// advancing the parser. Otherwise, do not change the parser state and return false. +func expectKeyWord(parser *Parser, value string) (lexer.Token, error) { + token := parser.Token + if token.Kind == lexer.TokenKind[lexer.NAME] && token.Value == value { + err := advance(parser) + return token, err + } + descp := fmt.Sprintf("Expected \"%s\", found %s", value, lexer.GetTokenDesc(token)) + return token, gqlerrors.NewSyntaxError(parser.Source, token.Start, descp) +} + +// Helper function for creating an error when an unexpected lexed token +// is encountered. +func unexpected(parser *Parser, atToken lexer.Token) error { + var token lexer.Token + if (atToken == lexer.Token{}) { + token = parser.Token + } else { + token = parser.Token + } + description := fmt.Sprintf("Unexpected %v", lexer.GetTokenDesc(token)) + return gqlerrors.NewSyntaxError(parser.Source, token.Start, description) +} + +// Returns a possibly empty list of parse nodes, determined by +// the parseFn. This list begins with a lex token of openKind +// and ends with a lex token of closeKind. Advances the parser +// to the next lex token after the closing token. +func any(parser *Parser, openKind int, parseFn parseFn, closeKind int) ([]interface{}, error) { + var nodes []interface{} + _, err := expect(parser, openKind) + if err != nil { + return nodes, nil + } + for { + if skp, err := skip(parser, closeKind); err != nil { + return nil, err + } else if skp { + break + } + n, err := parseFn(parser) + if err != nil { + return nodes, err + } + nodes = append(nodes, n) + } + return nodes, nil +} + +// Returns a non-empty list of parse nodes, determined by +// the parseFn. This list begins with a lex token of openKind +// and ends with a lex token of closeKind. Advances the parser +// to the next lex token after the closing token. +func many(parser *Parser, openKind int, parseFn parseFn, closeKind int) ([]interface{}, error) { + _, err := expect(parser, openKind) + if err != nil { + return nil, err + } + var nodes []interface{} + node, err := parseFn(parser) + if err != nil { + return nodes, err + } + nodes = append(nodes, node) + for { + if skp, err := skip(parser, closeKind); err != nil { + return nil, err + } else if skp { + break + } + node, err := parseFn(parser) + if err != nil { + return nodes, err + } + nodes = append(nodes, node) + } + return nodes, nil +} diff --git a/vendor/github.com/graphql-go/graphql/language/printer/printer.go b/vendor/github.com/graphql-go/graphql/language/printer/printer.go new file mode 100644 index 00000000..3add3852 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/printer/printer.go @@ -0,0 +1,821 @@ +package printer + +import ( + "fmt" + "strings" + + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/visitor" + "reflect" +) + +func getMapValue(m map[string]interface{}, key string) interface{} { + tokens := strings.Split(key, ".") + valMap := m + for _, token := range tokens { + v, ok := valMap[token] + if !ok { + return nil + } + switch v := v.(type) { + case []interface{}: + return v + case map[string]interface{}: + valMap = v + continue + default: + return v + } + } + return valMap +} +func getMapSliceValue(m map[string]interface{}, key string) []interface{} { + tokens := strings.Split(key, ".") + valMap := m + for _, token := range tokens { + v, ok := valMap[token] + if !ok { + return []interface{}{} + } + switch v := v.(type) { + case []interface{}: + return v + default: + return []interface{}{} + } + } + return []interface{}{} +} +func getMapValueString(m map[string]interface{}, key string) string { + tokens := strings.Split(key, ".") + valMap := m + for _, token := range tokens { + v, ok := valMap[token] + if !ok { + return "" + } + if v == nil { + return "" + } + switch v := v.(type) { + case map[string]interface{}: + valMap = v + continue + case string: + return v + default: + return fmt.Sprintf("%v", v) + } + } + return "" +} + +func toSliceString(slice interface{}) []string { + if slice == nil { + return []string{} + } + res := []string{} + switch reflect.TypeOf(slice).Kind() { + case reflect.Slice: + s := reflect.ValueOf(slice) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + elemInterface := elem.Interface() + if elem, ok := elemInterface.(string); ok { + res = append(res, elem) + } + } + return res + default: + return res + } +} + +func join(str []string, sep string) string { + ss := []string{} + // filter out empty strings + for _, s := range str { + if s == "" { + continue + } + ss = append(ss, s) + } + return strings.Join(ss, sep) +} + +func wrap(start, maybeString, end string) string { + if maybeString == "" { + return maybeString + } + return start + maybeString + end +} + +// Given array, print each item on its own line, wrapped in an indented "{ }" block. +func block(maybeArray interface{}) string { + s := toSliceString(maybeArray) + if len(s) == 0 { + return "{}" + } + return indent("{\n"+join(s, "\n")) + "\n}" +} + +func indent(maybeString interface{}) string { + if maybeString == nil { + return "" + } + switch str := maybeString.(type) { + case string: + return strings.Replace(str, "\n", "\n ", -1) + } + return "" +} + +var printDocASTReducer = map[string]visitor.VisitFunc{ + "Name": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.Name: + return visitor.ActionUpdate, node.Value + case map[string]interface{}: + return visitor.ActionUpdate, getMapValue(node, "Value") + } + return visitor.ActionNoChange, nil + }, + "Variable": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.Variable: + return visitor.ActionUpdate, fmt.Sprintf("$%v", node.Name) + case map[string]interface{}: + return visitor.ActionUpdate, "$" + getMapValueString(node, "Name") + } + return visitor.ActionNoChange, nil + }, + + // Document + "Document": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.Document: + definitions := toSliceString(node.Definitions) + return visitor.ActionUpdate, join(definitions, "\n\n") + "\n" + case map[string]interface{}: + definitions := toSliceString(getMapValue(node, "Definitions")) + return visitor.ActionUpdate, join(definitions, "\n\n") + "\n" + } + return visitor.ActionNoChange, nil + }, + "OperationDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.OperationDefinition: + op := string(node.Operation) + name := fmt.Sprintf("%v", node.Name) + + varDefs := wrap("(", join(toSliceString(node.VariableDefinitions), ", "), ")") + directives := join(toSliceString(node.Directives), " ") + selectionSet := fmt.Sprintf("%v", node.SelectionSet) + // Anonymous queries with no directives or variable definitions can use + // the query short form. + str := "" + if name == "" && directives == "" && varDefs == "" && op == ast.OperationTypeQuery { + str = selectionSet + } else { + str = join([]string{ + op, + join([]string{name, varDefs}, ""), + directives, + selectionSet, + }, " ") + } + return visitor.ActionUpdate, str + case map[string]interface{}: + + op := getMapValueString(node, "Operation") + name := getMapValueString(node, "Name") + + varDefs := wrap("(", join(toSliceString(getMapValue(node, "VariableDefinitions")), ", "), ")") + directives := join(toSliceString(getMapValue(node, "Directives")), " ") + selectionSet := getMapValueString(node, "SelectionSet") + str := "" + if name == "" && directives == "" && varDefs == "" && op == ast.OperationTypeQuery { + str = selectionSet + } else { + str = join([]string{ + op, + join([]string{name, varDefs}, ""), + directives, + selectionSet, + }, " ") + } + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "VariableDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.VariableDefinition: + variable := fmt.Sprintf("%v", node.Variable) + ttype := fmt.Sprintf("%v", node.Type) + defaultValue := fmt.Sprintf("%v", node.DefaultValue) + + return visitor.ActionUpdate, variable + ": " + ttype + wrap(" = ", defaultValue, "") + case map[string]interface{}: + + variable := getMapValueString(node, "Variable") + ttype := getMapValueString(node, "Type") + defaultValue := getMapValueString(node, "DefaultValue") + + return visitor.ActionUpdate, variable + ": " + ttype + wrap(" = ", defaultValue, "") + + } + return visitor.ActionNoChange, nil + }, + "SelectionSet": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.SelectionSet: + str := block(node.Selections) + return visitor.ActionUpdate, str + case map[string]interface{}: + selections := getMapValue(node, "Selections") + str := block(selections) + return visitor.ActionUpdate, str + + } + return visitor.ActionNoChange, nil + }, + "Field": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.Argument: + name := fmt.Sprintf("%v", node.Name) + value := fmt.Sprintf("%v", node.Value) + return visitor.ActionUpdate, name + ": " + value + case map[string]interface{}: + + alias := getMapValueString(node, "Alias") + name := getMapValueString(node, "Name") + args := toSliceString(getMapValue(node, "Arguments")) + directives := toSliceString(getMapValue(node, "Directives")) + selectionSet := getMapValueString(node, "SelectionSet") + + str := join( + []string{ + wrap("", alias, ": ") + name + wrap("(", join(args, ", "), ")"), + join(directives, " "), + selectionSet, + }, + " ", + ) + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "Argument": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.FragmentSpread: + name := fmt.Sprintf("%v", node.Name) + directives := toSliceString(node.Directives) + return visitor.ActionUpdate, "..." + name + wrap(" ", join(directives, " "), "") + case map[string]interface{}: + name := getMapValueString(node, "Name") + value := getMapValueString(node, "Value") + return visitor.ActionUpdate, name + ": " + value + } + return visitor.ActionNoChange, nil + }, + + // Fragments + "FragmentSpread": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.InlineFragment: + typeCondition := fmt.Sprintf("%v", node.TypeCondition) + directives := toSliceString(node.Directives) + selectionSet := fmt.Sprintf("%v", node.SelectionSet) + return visitor.ActionUpdate, "... on " + typeCondition + " " + wrap("", join(directives, " "), " ") + selectionSet + case map[string]interface{}: + name := getMapValueString(node, "Name") + directives := toSliceString(getMapValue(node, "Directives")) + return visitor.ActionUpdate, "..." + name + wrap(" ", join(directives, " "), "") + } + return visitor.ActionNoChange, nil + }, + "InlineFragment": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case map[string]interface{}: + typeCondition := getMapValueString(node, "TypeCondition") + directives := toSliceString(getMapValue(node, "Directives")) + selectionSet := getMapValueString(node, "SelectionSet") + return visitor.ActionUpdate, + join([]string{ + "...", + wrap("on ", typeCondition, ""), + join(directives, " "), + selectionSet, + }, " ") + } + return visitor.ActionNoChange, nil + }, + "FragmentDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.FragmentDefinition: + name := fmt.Sprintf("%v", node.Name) + typeCondition := fmt.Sprintf("%v", node.TypeCondition) + directives := toSliceString(node.Directives) + selectionSet := fmt.Sprintf("%v", node.SelectionSet) + return visitor.ActionUpdate, "fragment " + name + " on " + typeCondition + " " + wrap("", join(directives, " "), " ") + selectionSet + case map[string]interface{}: + name := getMapValueString(node, "Name") + typeCondition := getMapValueString(node, "TypeCondition") + directives := toSliceString(getMapValue(node, "Directives")) + selectionSet := getMapValueString(node, "SelectionSet") + return visitor.ActionUpdate, "fragment " + name + " on " + typeCondition + " " + wrap("", join(directives, " "), " ") + selectionSet + } + return visitor.ActionNoChange, nil + }, + + // Value + "IntValue": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.IntValue: + return visitor.ActionUpdate, fmt.Sprintf("%v", node.Value) + case map[string]interface{}: + return visitor.ActionUpdate, getMapValueString(node, "Value") + } + return visitor.ActionNoChange, nil + }, + "FloatValue": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.FloatValue: + return visitor.ActionUpdate, fmt.Sprintf("%v", node.Value) + case map[string]interface{}: + return visitor.ActionUpdate, getMapValueString(node, "Value") + } + return visitor.ActionNoChange, nil + }, + "StringValue": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.StringValue: + return visitor.ActionUpdate, `"` + fmt.Sprintf("%v", node.Value) + `"` + case map[string]interface{}: + return visitor.ActionUpdate, `"` + getMapValueString(node, "Value") + `"` + } + return visitor.ActionNoChange, nil + }, + "BooleanValue": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.BooleanValue: + return visitor.ActionUpdate, fmt.Sprintf("%v", node.Value) + case map[string]interface{}: + return visitor.ActionUpdate, getMapValueString(node, "Value") + } + return visitor.ActionNoChange, nil + }, + "EnumValue": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.EnumValue: + return visitor.ActionUpdate, fmt.Sprintf("%v", node.Value) + case map[string]interface{}: + return visitor.ActionUpdate, getMapValueString(node, "Value") + } + return visitor.ActionNoChange, nil + }, + "ListValue": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.ListValue: + return visitor.ActionUpdate, "[" + join(toSliceString(node.Values), ", ") + "]" + case map[string]interface{}: + return visitor.ActionUpdate, "[" + join(toSliceString(getMapValue(node, "Values")), ", ") + "]" + } + return visitor.ActionNoChange, nil + }, + "ObjectValue": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.ObjectValue: + return visitor.ActionUpdate, "{" + join(toSliceString(node.Fields), ", ") + "}" + case map[string]interface{}: + return visitor.ActionUpdate, "{" + join(toSliceString(getMapValue(node, "Fields")), ", ") + "}" + } + return visitor.ActionNoChange, nil + }, + "ObjectField": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.ObjectField: + name := fmt.Sprintf("%v", node.Name) + value := fmt.Sprintf("%v", node.Value) + return visitor.ActionUpdate, name + ": " + value + case map[string]interface{}: + name := getMapValueString(node, "Name") + value := getMapValueString(node, "Value") + return visitor.ActionUpdate, name + ": " + value + } + return visitor.ActionNoChange, nil + }, + + // Directive + "Directive": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.Directive: + name := fmt.Sprintf("%v", node.Name) + args := toSliceString(node.Arguments) + return visitor.ActionUpdate, "@" + name + wrap("(", join(args, ", "), ")") + case map[string]interface{}: + name := getMapValueString(node, "Name") + args := toSliceString(getMapValue(node, "Arguments")) + return visitor.ActionUpdate, "@" + name + wrap("(", join(args, ", "), ")") + } + return visitor.ActionNoChange, nil + }, + + // Type + "Named": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.Named: + return visitor.ActionUpdate, fmt.Sprintf("%v", node.Name) + case map[string]interface{}: + return visitor.ActionUpdate, getMapValueString(node, "Name") + } + return visitor.ActionNoChange, nil + }, + "List": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.List: + return visitor.ActionUpdate, "[" + fmt.Sprintf("%v", node.Type) + "]" + case map[string]interface{}: + return visitor.ActionUpdate, "[" + getMapValueString(node, "Type") + "]" + } + return visitor.ActionNoChange, nil + }, + "NonNull": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.NonNull: + return visitor.ActionUpdate, fmt.Sprintf("%v", node.Type) + "!" + case map[string]interface{}: + return visitor.ActionUpdate, getMapValueString(node, "Type") + "!" + } + return visitor.ActionNoChange, nil + }, + + // Type System Definitions + "SchemaDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.SchemaDefinition: + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + "schema", + join(directives, " "), + block(node.OperationTypes), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + operationTypes := toSliceString(getMapValue(node, "OperationTypes")) + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + "schema", + join(directives, " "), + block(operationTypes), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "OperationTypeDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.OperationTypeDefinition: + str := fmt.Sprintf("%v: %v", node.Operation, node.Type) + return visitor.ActionUpdate, str + case map[string]interface{}: + operation := getMapValueString(node, "Operation") + ttype := getMapValueString(node, "Type") + str := fmt.Sprintf("%v: %v", operation, ttype) + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "ScalarDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.ScalarDefinition: + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + "scalar", + fmt.Sprintf("%v", node.Name), + join(directives, " "), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + "scalar", + name, + join(directives, " "), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "ObjectDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.ObjectDefinition: + name := fmt.Sprintf("%v", node.Name) + interfaces := toSliceString(node.Interfaces) + fields := node.Fields + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + "type", + name, + wrap("implements ", join(interfaces, ", "), ""), + join(directives, " "), + block(fields), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + interfaces := toSliceString(getMapValue(node, "Interfaces")) + fields := getMapValue(node, "Fields") + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + "type", + name, + wrap("implements ", join(interfaces, ", "), ""), + join(directives, " "), + block(fields), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "FieldDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.FieldDefinition: + name := fmt.Sprintf("%v", node.Name) + ttype := fmt.Sprintf("%v", node.Type) + args := toSliceString(node.Arguments) + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := name + wrap("(", join(args, ", "), ")") + ": " + ttype + wrap(" ", join(directives, " "), "") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + ttype := getMapValueString(node, "Type") + args := toSliceString(getMapValue(node, "Arguments")) + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := name + wrap("(", join(args, ", "), ")") + ": " + ttype + wrap(" ", join(directives, " "), "") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "InputValueDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.InputValueDefinition: + name := fmt.Sprintf("%v", node.Name) + ttype := fmt.Sprintf("%v", node.Type) + defaultValue := fmt.Sprintf("%v", node.DefaultValue) + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + name + ": " + ttype, + wrap("= ", defaultValue, ""), + join(directives, " "), + }, " ") + + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + ttype := getMapValueString(node, "Type") + defaultValue := getMapValueString(node, "DefaultValue") + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + name + ": " + ttype, + wrap("= ", defaultValue, ""), + join(directives, " "), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "InterfaceDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.InterfaceDefinition: + name := fmt.Sprintf("%v", node.Name) + fields := node.Fields + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + "interface", + name, + join(directives, " "), + block(fields), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + fields := getMapValue(node, "Fields") + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + "interface", + name, + join(directives, " "), + block(fields), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "UnionDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.UnionDefinition: + name := fmt.Sprintf("%v", node.Name) + types := toSliceString(node.Types) + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + "union", + name, + join(directives, " "), + "= " + join(types, " | "), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + types := toSliceString(getMapValue(node, "Types")) + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + "union", + name, + join(directives, " "), + "= " + join(types, " | "), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "EnumDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.EnumDefinition: + name := fmt.Sprintf("%v", node.Name) + values := node.Values + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + "enum", + name, + join(directives, " "), + block(values), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + values := getMapValue(node, "Values") + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + "enum", + name, + join(directives, " "), + block(values), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "EnumValueDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.EnumValueDefinition: + name := fmt.Sprintf("%v", node.Name) + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + name, + join(directives, " "), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + name, + join(directives, " "), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "InputObjectDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.InputObjectDefinition: + name := fmt.Sprintf("%v", node.Name) + fields := node.Fields + directives := []string{} + for _, directive := range node.Directives { + directives = append(directives, fmt.Sprintf("%v", directive.Name)) + } + str := join([]string{ + "input", + name, + join(directives, " "), + block(fields), + }, " ") + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + fields := getMapValue(node, "Fields") + directives := []string{} + for _, directive := range getMapSliceValue(node, "Directives") { + directives = append(directives, fmt.Sprintf("%v", directive)) + } + str := join([]string{ + "input", + name, + join(directives, " "), + block(fields), + }, " ") + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "TypeExtensionDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.TypeExtensionDefinition: + definition := fmt.Sprintf("%v", node.Definition) + str := "extend " + definition + return visitor.ActionUpdate, str + case map[string]interface{}: + definition := getMapValueString(node, "Definition") + str := "extend " + definition + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, + "DirectiveDefinition": func(p visitor.VisitFuncParams) (string, interface{}) { + switch node := p.Node.(type) { + case *ast.DirectiveDefinition: + args := wrap("(", join(toSliceString(node.Arguments), ", "), ")") + str := fmt.Sprintf("directive @%v%v on %v", node.Name, args, join(toSliceString(node.Locations), " | ")) + return visitor.ActionUpdate, str + case map[string]interface{}: + name := getMapValueString(node, "Name") + locations := toSliceString(getMapValue(node, "Locations")) + args := toSliceString(getMapValue(node, "Arguments")) + argsStr := wrap("(", join(args, ", "), ")") + str := fmt.Sprintf("directive @%v%v on %v", name, argsStr, join(locations, " | ")) + return visitor.ActionUpdate, str + } + return visitor.ActionNoChange, nil + }, +} + +func Print(astNode ast.Node) (printed interface{}) { + defer func() interface{} { + if r := recover(); r != nil { + return fmt.Sprintf("%v", astNode) + } + return printed + }() + printed = visitor.Visit(astNode, &visitor.VisitorOptions{ + LeaveKindMap: printDocASTReducer, + }, nil) + return printed +} diff --git a/vendor/github.com/graphql-go/graphql/language/source/source.go b/vendor/github.com/graphql-go/graphql/language/source/source.go new file mode 100644 index 00000000..f14af003 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/source/source.go @@ -0,0 +1,20 @@ +package source + +const ( + name = "GraphQL" +) + +type Source struct { + Body []byte + Name string +} + +func NewSource(s *Source) *Source { + if s == nil { + s = &Source{Name: name} + } + if s.Name == "" { + s.Name = name + } + return s +} diff --git a/vendor/github.com/graphql-go/graphql/language/typeInfo/type_info.go b/vendor/github.com/graphql-go/graphql/language/typeInfo/type_info.go new file mode 100644 index 00000000..e012ee02 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/typeInfo/type_info.go @@ -0,0 +1,11 @@ +package typeInfo + +import ( + "github.com/graphql-go/graphql/language/ast" +) + +// TypeInfoI defines the interface for TypeInfo Implementation +type TypeInfoI interface { + Enter(node ast.Node) + Leave(node ast.Node) +} diff --git a/vendor/github.com/graphql-go/graphql/language/visitor/visitor.go b/vendor/github.com/graphql-go/graphql/language/visitor/visitor.go new file mode 100644 index 00000000..9a1c2ac2 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/language/visitor/visitor.go @@ -0,0 +1,873 @@ +package visitor + +import ( + "encoding/json" + "fmt" + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/typeInfo" + "reflect" +) + +const ( + ActionNoChange = "" + ActionBreak = "BREAK" + ActionSkip = "SKIP" + ActionUpdate = "UPDATE" +) + +type KeyMap map[string][]string + +// note that the keys are in Capital letters, equivalent to the ast.Node field Names +var QueryDocumentKeys = KeyMap{ + "Name": []string{}, + "Document": []string{"Definitions"}, + "OperationDefinition": []string{ + "Name", + "VariableDefinitions", + "Directives", + "SelectionSet", + }, + "VariableDefinition": []string{ + "Variable", + "Type", + "DefaultValue", + }, + "Variable": []string{"Name"}, + "SelectionSet": []string{"Selections"}, + "Field": []string{ + "Alias", + "Name", + "Arguments", + "Directives", + "SelectionSet", + }, + "Argument": []string{ + "Name", + "Value", + }, + + "FragmentSpread": []string{ + "Name", + "Directives", + }, + "InlineFragment": []string{ + "TypeCondition", + "Directives", + "SelectionSet", + }, + "FragmentDefinition": []string{ + "Name", + "TypeCondition", + "Directives", + "SelectionSet", + }, + + "IntValue": []string{}, + "FloatValue": []string{}, + "StringValue": []string{}, + "BooleanValue": []string{}, + "EnumValue": []string{}, + "ListValue": []string{"Values"}, + "ObjectValue": []string{"Fields"}, + "ObjectField": []string{ + "Name", + "Value", + }, + + "Directive": []string{ + "Name", + "Arguments", + }, + + "Named": []string{"Name"}, + "List": []string{"Type"}, + "NonNull": []string{"Type"}, + + "SchemaDefinition": []string{ + "Directives", + "OperationTypes", + }, + "OperationTypeDefinition": []string{"Type"}, + + "ScalarDefinition": []string{ + "Name", + "Directives", + }, + "ObjectDefinition": []string{ + "Name", + "Interfaces", + "Directives", + "Fields", + }, + "FieldDefinition": []string{ + "Name", + "Arguments", + "Type", + "Directives", + }, + "InputValueDefinition": []string{ + "Name", + "Type", + "DefaultValue", + "Directives", + }, + "InterfaceDefinition": []string{ + "Name", + "Directives", + "Fields", + }, + "UnionDefinition": []string{ + "Name", + "Directives", + "Types", + }, + "EnumDefinition": []string{ + "Name", + "Directives", + "Values", + }, + "EnumValueDefinition": []string{ + "Name", + "Directives", + }, + "InputObjectDefinition": []string{ + "Name", + "Directives", + "Fields", + }, + + "TypeExtensionDefinition": []string{"Definition"}, + + "DirectiveDefinition": []string{"Name", "Arguments", "Locations"}, +} + +type stack struct { + Index int + Keys []interface{} + Edits []*edit + inSlice bool + Prev *stack +} +type edit struct { + Key interface{} + Value interface{} +} + +type VisitFuncParams struct { + Node interface{} + Key interface{} + Parent ast.Node + Path []interface{} + Ancestors []ast.Node +} + +type VisitFunc func(p VisitFuncParams) (string, interface{}) + +type NamedVisitFuncs struct { + Kind VisitFunc // 1) Named visitors triggered when entering a node a specific kind. + Leave VisitFunc // 2) Named visitors that trigger upon entering and leaving a node of + Enter VisitFunc // 2) Named visitors that trigger upon entering and leaving a node of +} + +type VisitorOptions struct { + KindFuncMap map[string]NamedVisitFuncs + Enter VisitFunc // 3) Generic visitors that trigger upon entering and leaving any node. + Leave VisitFunc // 3) Generic visitors that trigger upon entering and leaving any node. + + EnterKindMap map[string]VisitFunc // 4) Parallel visitors for entering and leaving nodes of a specific kind + LeaveKindMap map[string]VisitFunc // 4) Parallel visitors for entering and leaving nodes of a specific kind +} + +func Visit(root ast.Node, visitorOpts *VisitorOptions, keyMap KeyMap) interface{} { + visitorKeys := keyMap + if visitorKeys == nil { + visitorKeys = QueryDocumentKeys + } + + var result interface{} + var newRoot = root + var sstack *stack + var parent interface{} + var parentSlice []interface{} + inSlice := false + prevInSlice := false + keys := []interface{}{newRoot} + index := -1 + edits := []*edit{} + path := []interface{}{} + ancestors := []interface{}{} + ancestorsSlice := [][]interface{}{} +Loop: + for { + index = index + 1 + + isLeaving := (len(keys) == index) + var key interface{} // string for structs or int for slices + var node interface{} // ast.Node or can be anything + var nodeSlice []interface{} + isEdited := (isLeaving && len(edits) != 0) + + if isLeaving { + if !inSlice { + if len(ancestors) == 0 { + key = nil + } else { + key, path = pop(path) + } + } else { + if len(ancestorsSlice) == 0 { + key = nil + } else { + key, path = pop(path) + } + } + + node = parent + parent, ancestors = pop(ancestors) + nodeSlice = parentSlice + parentSlice, ancestorsSlice = popNodeSlice(ancestorsSlice) + + if isEdited { + prevInSlice = inSlice + editOffset := 0 + for _, edit := range edits { + arrayEditKey := 0 + if inSlice { + keyInt := edit.Key.(int) + edit.Key = keyInt - editOffset + arrayEditKey = edit.Key.(int) + } + if inSlice && isNilNode(edit.Value) { + nodeSlice = spliceNode(nodeSlice, arrayEditKey) + editOffset = editOffset + 1 + } else { + if inSlice { + nodeSlice[arrayEditKey] = edit.Value + } else { + key, _ := edit.Key.(string) + + var updatedNode interface{} + if !isSlice(edit.Value) { + if isStructNode(edit.Value) { + updatedNode = updateNodeField(node, key, edit.Value) + } else { + var todoNode map[string]interface{} + b, err := json.Marshal(node) + if err != nil { + panic(fmt.Sprintf("Invalid root AST Node: %v", root)) + } + err = json.Unmarshal(b, &todoNode) + if err != nil { + panic(fmt.Sprintf("Invalid root AST Node (2): %v", root)) + } + todoNode[key] = edit.Value + updatedNode = todoNode + } + } else { + isSliceOfNodes := true + + // check if edit.value slice is ast.nodes + switch reflect.TypeOf(edit.Value).Kind() { + case reflect.Slice: + s := reflect.ValueOf(edit.Value) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + if !isStructNode(elem.Interface()) { + isSliceOfNodes = false + } + } + } + + // is a slice of real nodes + if isSliceOfNodes { + // the node we are writing to is an ast.Node + updatedNode = updateNodeField(node, key, edit.Value) + } else { + var todoNode map[string]interface{} + b, err := json.Marshal(node) + if err != nil { + panic(fmt.Sprintf("Invalid root AST Node: %v", root)) + } + err = json.Unmarshal(b, &todoNode) + if err != nil { + panic(fmt.Sprintf("Invalid root AST Node (2): %v", root)) + } + todoNode[key] = edit.Value + updatedNode = todoNode + } + + } + node = updatedNode + } + } + } + } + index = sstack.Index + keys = sstack.Keys + edits = sstack.Edits + inSlice = sstack.inSlice + sstack = sstack.Prev + } else { + // get key + if !inSlice { + if !isNilNode(parent) { + key = getFieldValue(keys, index) + } else { + // initial conditions + key = nil + } + } else { + key = index + } + // get node + if !inSlice { + if !isNilNode(parent) { + fieldValue := getFieldValue(parent, key) + if isNode(fieldValue) { + node = fieldValue.(ast.Node) + } + if isSlice(fieldValue) { + nodeSlice = toSliceInterfaces(fieldValue) + } + } else { + // initial conditions + node = newRoot + } + } else { + if len(parentSlice) != 0 { + fieldValue := getFieldValue(parentSlice, key) + if isNode(fieldValue) { + node = fieldValue.(ast.Node) + } + if isSlice(fieldValue) { + nodeSlice = toSliceInterfaces(fieldValue) + } + } else { + // initial conditions + nodeSlice = []interface{}{} + } + } + + if isNilNode(node) && len(nodeSlice) == 0 { + continue + } + + if !inSlice { + if !isNilNode(parent) { + path = append(path, key) + } + } else { + if len(parentSlice) != 0 { + path = append(path, key) + } + } + } + + // get result from visitFn for a node if set + var result interface{} + resultIsUndefined := true + if !isNilNode(node) { + if !isNode(node) { // is node-ish. + panic(fmt.Sprintf("Invalid AST Node (4): %v", node)) + } + + // Try to pass in current node as ast.Node + // Note that since user can potentially return a non-ast.Node from visit functions. + // In that case, we try to unmarshal map[string]interface{} into ast.Node + var nodeIn interface{} + if _, ok := node.(map[string]interface{}); ok { + b, err := json.Marshal(node) + if err != nil { + panic(fmt.Sprintf("Invalid root AST Node: %v", root)) + } + err = json.Unmarshal(b, &nodeIn) + if err != nil { + panic(fmt.Sprintf("Invalid root AST Node (2a): %v", root)) + } + } else { + nodeIn = node + } + parentConcrete, _ := parent.(ast.Node) + // ancestorsConcrete slice may contain nil values + ancestorsConcrete := []ast.Node{} + for _, ancestor := range ancestors { + if ancestorConcrete, ok := ancestor.(ast.Node); ok { + ancestorsConcrete = append(ancestorsConcrete, ancestorConcrete) + } else { + ancestorsConcrete = append(ancestorsConcrete, nil) + } + } + + kind := "" + if node, ok := node.(map[string]interface{}); ok { + kind, _ = node["Kind"].(string) + } + if node, ok := node.(ast.Node); ok { + kind = node.GetKind() + } + + visitFn := GetVisitFn(visitorOpts, kind, isLeaving) + if visitFn != nil { + p := VisitFuncParams{ + Node: nodeIn, + Key: key, + Parent: parentConcrete, + Path: path, + Ancestors: ancestorsConcrete, + } + action := ActionUpdate + action, result = visitFn(p) + if action == ActionBreak { + break Loop + } + if action == ActionSkip { + if !isLeaving { + _, path = pop(path) + continue + } + } + if action != ActionNoChange { + resultIsUndefined = false + edits = append(edits, &edit{ + Key: key, + Value: result, + }) + if !isLeaving { + if isNode(result) { + node = result + } else { + _, path = pop(path) + continue + } + } + } else { + resultIsUndefined = true + } + } + + } + + // collect back edits on the way out + if resultIsUndefined && isEdited { + if !prevInSlice { + edits = append(edits, &edit{ + Key: key, + Value: node, + }) + } else { + edits = append(edits, &edit{ + Key: key, + Value: nodeSlice, + }) + } + } + if !isLeaving { + + // add to stack + prevStack := sstack + sstack = &stack{ + inSlice: inSlice, + Index: index, + Keys: keys, + Edits: edits, + Prev: prevStack, + } + + // replace keys + inSlice = false + if len(nodeSlice) > 0 { + inSlice = true + } + keys = []interface{}{} + + if inSlice { + // get keys + for _, m := range nodeSlice { + keys = append(keys, m) + } + } else { + if !isNilNode(node) { + if node, ok := node.(ast.Node); ok { + kind := node.GetKind() + if n, ok := visitorKeys[kind]; ok { + for _, m := range n { + keys = append(keys, m) + } + } + } + + } + + } + index = -1 + edits = []*edit{} + + ancestors = append(ancestors, parent) + parent = node + ancestorsSlice = append(ancestorsSlice, parentSlice) + parentSlice = nodeSlice + + } + + // loop guard + if sstack == nil { + break Loop + } + } + if len(edits) != 0 { + result = edits[len(edits)-1].Value + } + return result +} + +func pop(a []interface{}) (x interface{}, aa []interface{}) { + if len(a) == 0 { + return x, aa + } + x, aa = a[len(a)-1], a[:len(a)-1] + return x, aa +} +func popNodeSlice(a [][]interface{}) (x []interface{}, aa [][]interface{}) { + if len(a) == 0 { + return x, aa + } + x, aa = a[len(a)-1], a[:len(a)-1] + return x, aa +} +func spliceNode(a interface{}, i int) (result []interface{}) { + if i < 0 { + return result + } + typeOf := reflect.TypeOf(a) + if typeOf == nil { + return result + } + switch typeOf.Kind() { + case reflect.Slice: + s := reflect.ValueOf(a) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + elemInterface := elem.Interface() + result = append(result, elemInterface) + } + if i >= s.Len() { + return result + } + return append(result[:i], result[i+1:]...) + default: + return result + } +} + +func getFieldValue(obj interface{}, key interface{}) interface{} { + val := reflect.ValueOf(obj) + if val.Type().Kind() == reflect.Ptr { + val = val.Elem() + } + if val.Type().Kind() == reflect.Struct { + key, ok := key.(string) + if !ok { + return nil + } + valField := val.FieldByName(key) + if valField.IsValid() { + return valField.Interface() + } + return nil + } + if val.Type().Kind() == reflect.Slice { + key, ok := key.(int) + if !ok { + return nil + } + if key >= val.Len() { + return nil + } + valField := val.Index(key) + if valField.IsValid() { + return valField.Interface() + } + return nil + } + if val.Type().Kind() == reflect.Map { + keyVal := reflect.ValueOf(key) + valField := val.MapIndex(keyVal) + if valField.IsValid() { + return valField.Interface() + } + return nil + } + return nil +} + +func updateNodeField(value interface{}, fieldName string, fieldValue interface{}) (retVal interface{}) { + retVal = value + val := reflect.ValueOf(value) + + isPtr := false + if val.IsValid() && val.Type().Kind() == reflect.Ptr { + val = val.Elem() + isPtr = true + } + if !val.IsValid() { + return retVal + } + if val.Type().Kind() == reflect.Struct { + for i := 0; i < val.NumField(); i++ { + valueField := val.Field(i) + typeField := val.Type().Field(i) + + // try matching the field name + if typeField.Name == fieldName { + fieldValueVal := reflect.ValueOf(fieldValue) + if valueField.CanSet() { + + if fieldValueVal.IsValid() { + if valueField.Type().Kind() == fieldValueVal.Type().Kind() { + if fieldValueVal.Type().Kind() == reflect.Slice { + newSliceValue := reflect.MakeSlice(reflect.TypeOf(valueField.Interface()), fieldValueVal.Len(), fieldValueVal.Len()) + for i := 0; i < newSliceValue.Len(); i++ { + dst := newSliceValue.Index(i) + src := fieldValueVal.Index(i) + srcValue := reflect.ValueOf(src.Interface()) + if dst.CanSet() { + dst.Set(srcValue) + } + } + valueField.Set(newSliceValue) + + } else { + valueField.Set(fieldValueVal) + } + } + } else { + valueField.Set(reflect.New(valueField.Type()).Elem()) + } + if isPtr == true { + retVal = val.Addr().Interface() + return retVal + } + retVal = val.Interface() + return retVal + + } + } + } + } + return retVal +} +func toSliceInterfaces(slice interface{}) (result []interface{}) { + switch reflect.TypeOf(slice).Kind() { + case reflect.Slice: + s := reflect.ValueOf(slice) + for i := 0; i < s.Len(); i++ { + elem := s.Index(i) + elemInterface := elem.Interface() + if elem, ok := elemInterface.(ast.Node); ok { + result = append(result, elem) + } + } + return result + default: + return result + } +} + +func isSlice(value interface{}) bool { + val := reflect.ValueOf(value) + if val.IsValid() && val.Type().Kind() == reflect.Slice { + return true + } + return false +} +func isNode(node interface{}) bool { + val := reflect.ValueOf(node) + if val.IsValid() && val.Type().Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return false + } + if val.Type().Kind() == reflect.Map { + keyVal := reflect.ValueOf("Kind") + valField := val.MapIndex(keyVal) + return valField.IsValid() + } + if val.Type().Kind() == reflect.Struct { + valField := val.FieldByName("Kind") + return valField.IsValid() + } + return false +} +func isStructNode(node interface{}) bool { + val := reflect.ValueOf(node) + if val.IsValid() && val.Type().Kind() == reflect.Ptr { + val = val.Elem() + } + if !val.IsValid() { + return false + } + if val.Type().Kind() == reflect.Struct { + valField := val.FieldByName("Kind") + return valField.IsValid() + } + return false +} + +func isNilNode(node interface{}) bool { + val := reflect.ValueOf(node) + if !val.IsValid() { + return true + } + if val.Type().Kind() == reflect.Ptr { + return val.IsNil() + } + if val.Type().Kind() == reflect.Slice { + return val.Len() == 0 + } + if val.Type().Kind() == reflect.Map { + return val.Len() == 0 + } + if val.Type().Kind() == reflect.Bool { + return val.Interface().(bool) + } + return val.Interface() == nil +} + +// VisitInParallel Creates a new visitor instance which delegates to many visitors to run in +// parallel. Each visitor will be visited for each node before moving on. +// +// If a prior visitor edits a node, no following visitors will see that node. +func VisitInParallel(visitorOptsSlice ...*VisitorOptions) *VisitorOptions { + skipping := map[int]interface{}{} + + return &VisitorOptions{ + Enter: func(p VisitFuncParams) (string, interface{}) { + for i, visitorOpts := range visitorOptsSlice { + if _, ok := skipping[i]; !ok { + switch node := p.Node.(type) { + case ast.Node: + kind := node.GetKind() + fn := GetVisitFn(visitorOpts, kind, false) + if fn != nil { + action, result := fn(p) + if action == ActionSkip { + skipping[i] = node + } else if action == ActionBreak { + skipping[i] = ActionBreak + } else if action == ActionUpdate { + return ActionUpdate, result + } + } + } + } + } + return ActionNoChange, nil + }, + Leave: func(p VisitFuncParams) (string, interface{}) { + for i, visitorOpts := range visitorOptsSlice { + skippedNode, ok := skipping[i] + if !ok { + switch node := p.Node.(type) { + case ast.Node: + kind := node.GetKind() + fn := GetVisitFn(visitorOpts, kind, true) + if fn != nil { + action, result := fn(p) + if action == ActionBreak { + skipping[i] = ActionBreak + } else if action == ActionUpdate { + return ActionUpdate, result + } + } + } + } else if skippedNode == p.Node { + delete(skipping, i) + } + } + return ActionNoChange, nil + }, + } +} + +// VisitWithTypeInfo Creates a new visitor instance which maintains a provided TypeInfo instance +// along with visiting visitor. +func VisitWithTypeInfo(ttypeInfo typeInfo.TypeInfoI, visitorOpts *VisitorOptions) *VisitorOptions { + return &VisitorOptions{ + Enter: func(p VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(ast.Node); ok { + ttypeInfo.Enter(node) + fn := GetVisitFn(visitorOpts, node.GetKind(), false) + if fn != nil { + action, result := fn(p) + if action == ActionUpdate { + ttypeInfo.Leave(node) + if isNode(result) { + if result, ok := result.(ast.Node); ok { + ttypeInfo.Enter(result) + } + } + } + return action, result + } + } + return ActionNoChange, nil + }, + Leave: func(p VisitFuncParams) (string, interface{}) { + action := ActionNoChange + var result interface{} + if node, ok := p.Node.(ast.Node); ok { + fn := GetVisitFn(visitorOpts, node.GetKind(), true) + if fn != nil { + action, result = fn(p) + } + ttypeInfo.Leave(node) + } + return action, result + }, + } +} + +// GetVisitFn Given a visitor instance, if it is leaving or not, and a node kind, return +// the function the visitor runtime should call. +func GetVisitFn(visitorOpts *VisitorOptions, kind string, isLeaving bool) VisitFunc { + if visitorOpts == nil { + return nil + } + kindVisitor, ok := visitorOpts.KindFuncMap[kind] + if ok { + if !isLeaving && kindVisitor.Kind != nil { + // { Kind() {} } + return kindVisitor.Kind + } + if isLeaving { + // { Kind: { leave() {} } } + return kindVisitor.Leave + } + // { Kind: { enter() {} } } + return kindVisitor.Enter + + } + if isLeaving { + // { enter() {} } + specificVisitor := visitorOpts.Leave + if specificVisitor != nil { + return specificVisitor + } + if specificKindVisitor, ok := visitorOpts.LeaveKindMap[kind]; ok { + // { leave: { Kind() {} } } + return specificKindVisitor + } + + } + // { leave() {} } + specificVisitor := visitorOpts.Enter + if specificVisitor != nil { + return specificVisitor + } + if specificKindVisitor, ok := visitorOpts.EnterKindMap[kind]; ok { + // { enter: { Kind() {} } } + return specificKindVisitor + } + return nil +} diff --git a/vendor/github.com/graphql-go/graphql/located.go b/vendor/github.com/graphql-go/graphql/located.go new file mode 100644 index 00000000..6ed8ec83 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/located.go @@ -0,0 +1,37 @@ +package graphql + +import ( + "errors" + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/ast" +) + +func NewLocatedError(err interface{}, nodes []ast.Node) *gqlerrors.Error { + var origError error + message := "An unknown error occurred." + if err, ok := err.(error); ok { + message = err.Error() + origError = err + } + if err, ok := err.(string); ok { + message = err + origError = errors.New(err) + } + stack := message + return gqlerrors.NewError( + message, + nodes, + stack, + nil, + []int{}, + origError, + ) +} + +func FieldASTsToNodeASTs(fieldASTs []*ast.Field) []ast.Node { + nodes := []ast.Node{} + for _, fieldAST := range fieldASTs { + nodes = append(nodes, fieldAST) + } + return nodes +} diff --git a/vendor/github.com/graphql-go/graphql/rules.go b/vendor/github.com/graphql-go/graphql/rules.go new file mode 100644 index 00000000..95d80aad --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/rules.go @@ -0,0 +1,1907 @@ +package graphql + +import ( + "fmt" + "math" + "sort" + "strings" + + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/kinds" + "github.com/graphql-go/graphql/language/printer" + "github.com/graphql-go/graphql/language/visitor" +) + +// SpecifiedRules set includes all validation rules defined by the GraphQL spec. +var SpecifiedRules = []ValidationRuleFn{ + ArgumentsOfCorrectTypeRule, + DefaultValuesOfCorrectTypeRule, + FieldsOnCorrectTypeRule, + FragmentsOnCompositeTypesRule, + KnownArgumentNamesRule, + KnownDirectivesRule, + KnownFragmentNamesRule, + KnownTypeNamesRule, + LoneAnonymousOperationRule, + NoFragmentCyclesRule, + NoUndefinedVariablesRule, + NoUnusedFragmentsRule, + NoUnusedVariablesRule, + OverlappingFieldsCanBeMergedRule, + PossibleFragmentSpreadsRule, + ProvidedNonNullArgumentsRule, + ScalarLeafsRule, + UniqueArgumentNamesRule, + UniqueFragmentNamesRule, + UniqueInputFieldNamesRule, + UniqueOperationNamesRule, + UniqueVariableNamesRule, + VariablesAreInputTypesRule, + VariablesInAllowedPositionRule, +} + +type ValidationRuleInstance struct { + VisitorOpts *visitor.VisitorOptions +} +type ValidationRuleFn func(context *ValidationContext) *ValidationRuleInstance + +func newValidationError(message string, nodes []ast.Node) *gqlerrors.Error { + return gqlerrors.NewError( + message, + nodes, + "", + nil, + []int{}, + nil, // TODO: this is interim, until we port "better-error-messages-for-inputs" + ) +} + +func reportError(context *ValidationContext, message string, nodes []ast.Node) (string, interface{}) { + context.ReportError(newValidationError(message, nodes)) + return visitor.ActionNoChange, nil +} + +// ArgumentsOfCorrectTypeRule Argument values of correct type +// +// A GraphQL document is only valid if all field argument literal values are +// of the type expected by their position. +func ArgumentsOfCorrectTypeRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Argument: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if argAST, ok := p.Node.(*ast.Argument); ok { + value := argAST.Value + argDef := context.Argument() + if argDef != nil { + isValid, messages := isValidLiteralValue(argDef.Type, value) + if !isValid { + argNameValue := "" + if argAST.Name != nil { + argNameValue = argAST.Name.Value + } + + messagesStr := "" + if len(messages) > 0 { + messagesStr = "\n" + strings.Join(messages, "\n") + } + reportError( + context, + fmt.Sprintf(`Argument "%v" has invalid value %v.%v`, + argNameValue, printer.Print(value), messagesStr), + []ast.Node{value}, + ) + } + + } + } + return visitor.ActionSkip, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// DefaultValuesOfCorrectTypeRule Variable default values of correct type +// +// A GraphQL document is only valid if all variable default values are of the +// type expected by their definition. +func DefaultValuesOfCorrectTypeRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.VariableDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if varDefAST, ok := p.Node.(*ast.VariableDefinition); ok { + name := "" + if varDefAST.Variable != nil && varDefAST.Variable.Name != nil { + name = varDefAST.Variable.Name.Value + } + defaultValue := varDefAST.DefaultValue + ttype := context.InputType() + + if ttype, ok := ttype.(*NonNull); ok && defaultValue != nil { + reportError( + context, + fmt.Sprintf(`Variable "$%v" of type "%v" is required and will not use the default value. Perhaps you meant to use type "%v".`, + name, ttype, ttype.OfType), + []ast.Node{defaultValue}, + ) + } + isValid, messages := isValidLiteralValue(ttype, defaultValue) + if ttype != nil && defaultValue != nil && !isValid { + messagesStr := "" + if len(messages) > 0 { + messagesStr = "\n" + strings.Join(messages, "\n") + } + reportError( + context, + fmt.Sprintf(`Variable "$%v" has invalid default value: %v.%v`, + name, printer.Print(defaultValue), messagesStr), + []ast.Node{defaultValue}, + ) + } + } + return visitor.ActionSkip, nil + }, + }, + kinds.SelectionSet: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.FragmentDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} +func quoteStrings(slice []string) []string { + quoted := []string{} + for _, s := range slice { + quoted = append(quoted, fmt.Sprintf(`"%v"`, s)) + } + return quoted +} + +// quotedOrList Given [ A, B, C ] return '"A", "B", or "C"'. +// Notice oxford comma +func quotedOrList(slice []string) string { + maxLength := 5 + if len(slice) == 0 { + return "" + } + quoted := quoteStrings(slice) + if maxLength > len(quoted) { + maxLength = len(quoted) + } + if maxLength > 2 { + return fmt.Sprintf("%v, or %v", strings.Join(quoted[0:maxLength-1], ", "), quoted[maxLength-1]) + } + if maxLength > 1 { + return fmt.Sprintf("%v or %v", strings.Join(quoted[0:maxLength-1], ", "), quoted[maxLength-1]) + } + return quoted[0] +} +func UndefinedFieldMessage(fieldName string, ttypeName string, suggestedTypeNames []string, suggestedFieldNames []string) string { + message := fmt.Sprintf(`Cannot query field "%v" on type "%v".`, fieldName, ttypeName) + if len(suggestedTypeNames) > 0 { + message = fmt.Sprintf(`%v Did you mean to use an inline fragment on %v?`, message, quotedOrList(suggestedTypeNames)) + } else if len(suggestedFieldNames) > 0 { + message = fmt.Sprintf(`%v Did you mean %v?`, message, quotedOrList(suggestedFieldNames)) + } + return message +} + +// FieldsOnCorrectTypeRule Fields on correct type +// +// A GraphQL document is only valid if all fields selected are defined by the +// parent type, or are an allowed meta field such as __typenamme +func FieldsOnCorrectTypeRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Field: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + var action = visitor.ActionNoChange + var result interface{} + if node, ok := p.Node.(*ast.Field); ok { + ttype := context.ParentType() + if ttype == nil { + return action, result + } + if t, ok := ttype.(*Object); ok && t == nil { + return action, result + } + if t, ok := ttype.(*Interface); ok && t == nil { + return action, result + } + if t, ok := ttype.(*Union); ok && t == nil { + return action, result + } + fieldDef := context.FieldDef() + if fieldDef == nil { + // This field doesn't exist, lets look for suggestions. + nodeName := "" + if node.Name != nil { + nodeName = node.Name.Value + } + // First determine if there are any suggested types to condition on. + suggestedTypeNames := getSuggestedTypeNames(context.Schema(), ttype, nodeName) + + // If there are no suggested types, then perhaps this was a typo? + suggestedFieldNames := []string{} + if len(suggestedTypeNames) == 0 { + suggestedFieldNames = getSuggestedFieldNames(context.Schema(), ttype, nodeName) + } + reportError( + context, + UndefinedFieldMessage(nodeName, ttype.Name(), suggestedTypeNames, suggestedFieldNames), + []ast.Node{node}, + ) + } + } + return action, result + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// getSuggestedTypeNames 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(schema *Schema, ttype Output, fieldName string) []string { + + possibleTypes := schema.PossibleTypes(ttype) + + suggestedObjectTypes := []string{} + suggestedInterfaces := []*suggestedInterface{} + // stores a map of interface name => index in suggestedInterfaces + suggestedInterfaceMap := map[string]int{} + // stores a maps of object name => true to remove duplicates from results + suggestedObjectMap := map[string]bool{} + + for _, possibleType := range possibleTypes { + if field, ok := possibleType.Fields()[fieldName]; !ok || field == nil { + continue + } + // This object type defines this field. + suggestedObjectTypes = append(suggestedObjectTypes, possibleType.Name()) + suggestedObjectMap[possibleType.Name()] = true + + for _, possibleInterface := range possibleType.Interfaces() { + if field, ok := possibleInterface.Fields()[fieldName]; !ok || field == nil { + continue + } + + // This interface type defines this field. + + // - find the index of the suggestedInterface and retrieving the interface + // - increase count + index, ok := suggestedInterfaceMap[possibleInterface.Name()] + if !ok { + suggestedInterfaces = append(suggestedInterfaces, &suggestedInterface{ + name: possibleInterface.Name(), + count: 0, + }) + index = len(suggestedInterfaces) - 1 + suggestedInterfaceMap[possibleInterface.Name()] = index + } + if index < len(suggestedInterfaces) { + s := suggestedInterfaces[index] + if s.name == possibleInterface.Name() { + s.count = s.count + 1 + } + } + } + } + + // sort results (by count usage for interfaces, alphabetical order for objects) + sort.Sort(suggestedInterfaceSortedSlice(suggestedInterfaces)) + sort.Sort(sort.StringSlice(suggestedObjectTypes)) + + // return concatenated slices of both interface and object type names + // and removing duplicates + // ordered by: interface (sorted) and object (sorted) + results := []string{} + for _, s := range suggestedInterfaces { + if _, ok := suggestedObjectMap[s.name]; !ok { + results = append(results, s.name) + + } + } + results = append(results, suggestedObjectTypes...) + return results +} + +// getSuggestedFieldNames For the field name provided, determine if there are any similar field names +// that may be the result of a typo. +func getSuggestedFieldNames(schema *Schema, ttype Output, fieldName string) []string { + + fields := FieldDefinitionMap{} + switch ttype := ttype.(type) { + case *Object: + fields = ttype.Fields() + case *Interface: + fields = ttype.Fields() + default: + return []string{} + } + + possibleFieldNames := []string{} + for possibleFieldName := range fields { + possibleFieldNames = append(possibleFieldNames, possibleFieldName) + } + return suggestionList(fieldName, possibleFieldNames) +} + +// suggestedInterface an internal struct to sort interface by usage count +type suggestedInterface struct { + name string + count int +} +type suggestedInterfaceSortedSlice []*suggestedInterface + +func (s suggestedInterfaceSortedSlice) Len() int { + return len(s) +} +func (s suggestedInterfaceSortedSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} +func (s suggestedInterfaceSortedSlice) Less(i, j int) bool { + if s[i].count == s[j].count { + return s[i].name < s[j].name + } + return s[i].count > s[j].count +} + +// FragmentsOnCompositeTypesRule Fragments on composite type +// +// Fragments use a type condition to determine if they apply, since fragments +// can only be spread into a composite type (object, interface, or union), the +// type condition must also be a composite type. +func FragmentsOnCompositeTypesRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.InlineFragment: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.InlineFragment); ok { + ttype := context.Type() + if node.TypeCondition != nil && ttype != nil && !IsCompositeType(ttype) { + reportError( + context, + fmt.Sprintf(`Fragment cannot condition on non composite type "%v".`, ttype), + []ast.Node{node.TypeCondition}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + kinds.FragmentDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.FragmentDefinition); ok { + ttype := context.Type() + if ttype != nil && !IsCompositeType(ttype) { + nodeName := "" + if node.Name != nil { + nodeName = node.Name.Value + } + reportError( + context, + fmt.Sprintf(`Fragment "%v" cannot condition on non composite type "%v".`, nodeName, printer.Print(node.TypeCondition)), + []ast.Node{node.TypeCondition}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func unknownArgMessage(argName string, fieldName string, parentTypeName string, suggestedArgs []string) string { + message := fmt.Sprintf(`Unknown argument "%v" on field "%v" of type "%v".`, argName, fieldName, parentTypeName) + + if len(suggestedArgs) > 0 { + message = fmt.Sprintf(`%v Did you mean %v?`, message, quotedOrList(suggestedArgs)) + } + + return message +} + +func unknownDirectiveArgMessage(argName string, directiveName string, suggestedArgs []string) string { + message := fmt.Sprintf(`Unknown argument "%v" on directive "@%v".`, argName, directiveName) + + if len(suggestedArgs) > 0 { + message = fmt.Sprintf(`%v Did you mean %v?`, message, quotedOrList(suggestedArgs)) + } + + return message +} + +// KnownArgumentNamesRule Known argument names +// +// A GraphQL field is only valid if all supplied arguments are defined by +// that field. +func KnownArgumentNamesRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Argument: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + var action = visitor.ActionNoChange + var result interface{} + if node, ok := p.Node.(*ast.Argument); ok { + var argumentOf ast.Node + if len(p.Ancestors) > 0 { + argumentOf = p.Ancestors[len(p.Ancestors)-1] + } + if argumentOf == nil { + return action, result + } + var fieldArgDef *Argument + if argumentOf.GetKind() == kinds.Field { + fieldDef := context.FieldDef() + if fieldDef == nil { + return action, result + } + nodeName := "" + if node.Name != nil { + nodeName = node.Name.Value + } + argNames := []string{} + for _, arg := range fieldDef.Args { + argNames = append(argNames, arg.Name()) + if arg.Name() == nodeName { + fieldArgDef = arg + } + } + if fieldArgDef == nil { + parentType := context.ParentType() + parentTypeName := "" + if parentType != nil { + parentTypeName = parentType.Name() + } + reportError( + context, + unknownArgMessage(nodeName, fieldDef.Name, parentTypeName, suggestionList(nodeName, argNames)), + []ast.Node{node}, + ) + } + } else if argumentOf.GetKind() == kinds.Directive { + directive := context.Directive() + if directive == nil { + return action, result + } + nodeName := "" + if node.Name != nil { + nodeName = node.Name.Value + } + argNames := []string{} + var directiveArgDef *Argument + for _, arg := range directive.Args { + argNames = append(argNames, arg.Name()) + if arg.Name() == nodeName { + directiveArgDef = arg + } + } + if directiveArgDef == nil { + reportError( + context, + unknownDirectiveArgMessage(nodeName, directive.Name, suggestionList(nodeName, argNames)), + []ast.Node{node}, + ) + } + } + + } + return action, result + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func MisplaceDirectiveMessage(directiveName string, location string) string { + return fmt.Sprintf(`Directive "%v" may not be used on %v.`, directiveName, location) +} + +// KnownDirectivesRule Known directives +// +// A GraphQL document is only valid if all `@directives` are known by the +// schema and legally positioned. +func KnownDirectivesRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Directive: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + var action = visitor.ActionNoChange + var result interface{} + if node, ok := p.Node.(*ast.Directive); ok { + + nodeName := "" + if node.Name != nil { + nodeName = node.Name.Value + } + + var directiveDef *Directive + for _, def := range context.Schema().Directives() { + if def.Name == nodeName { + directiveDef = def + } + } + if directiveDef == nil { + return reportError( + context, + fmt.Sprintf(`Unknown directive "%v".`, nodeName), + []ast.Node{node}, + ) + } + + candidateLocation := getDirectiveLocationForASTPath(p.Ancestors) + + directiveHasLocation := false + for _, loc := range directiveDef.Locations { + if loc == candidateLocation { + directiveHasLocation = true + break + } + } + + if candidateLocation == "" { + reportError( + context, + MisplaceDirectiveMessage(nodeName, node.GetKind()), + []ast.Node{node}, + ) + } else if !directiveHasLocation { + reportError( + context, + MisplaceDirectiveMessage(nodeName, candidateLocation), + []ast.Node{node}, + ) + } + + } + return action, result + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func getDirectiveLocationForASTPath(ancestors []ast.Node) string { + var appliedTo ast.Node + if len(ancestors) > 0 { + appliedTo = ancestors[len(ancestors)-1] + } + if appliedTo == nil { + return "" + } + kind := appliedTo.GetKind() + if kind == kinds.OperationDefinition { + appliedTo, _ := appliedTo.(*ast.OperationDefinition) + if appliedTo.Operation == ast.OperationTypeQuery { + return DirectiveLocationQuery + } + if appliedTo.Operation == ast.OperationTypeMutation { + return DirectiveLocationMutation + } + if appliedTo.Operation == ast.OperationTypeSubscription { + return DirectiveLocationSubscription + } + } + if kind == kinds.Field { + return DirectiveLocationField + } + if kind == kinds.FragmentSpread { + return DirectiveLocationFragmentSpread + } + if kind == kinds.InlineFragment { + return DirectiveLocationInlineFragment + } + if kind == kinds.FragmentDefinition { + return DirectiveLocationFragmentDefinition + } + if kind == kinds.SchemaDefinition { + return DirectiveLocationSchema + } + if kind == kinds.ScalarDefinition { + return DirectiveLocationScalar + } + if kind == kinds.ObjectDefinition { + return DirectiveLocationObject + } + if kind == kinds.FieldDefinition { + return DirectiveLocationFieldDefinition + } + if kind == kinds.InterfaceDefinition { + return DirectiveLocationInterface + } + if kind == kinds.UnionDefinition { + return DirectiveLocationUnion + } + if kind == kinds.EnumDefinition { + return DirectiveLocationEnum + } + if kind == kinds.EnumValueDefinition { + return DirectiveLocationEnumValue + } + if kind == kinds.InputObjectDefinition { + return DirectiveLocationInputObject + } + if kind == kinds.InputValueDefinition { + var parentNode ast.Node + if len(ancestors) >= 3 { + parentNode = ancestors[len(ancestors)-3] + } + if parentNode.GetKind() == kinds.InputObjectDefinition { + return DirectiveLocationInputFieldDefinition + } else { + return DirectiveLocationArgumentDefinition + } + } + return "" +} + +// KnownFragmentNamesRule Known fragment names +// +// A GraphQL document is only valid if all `...Fragment` fragment spreads refer +// to fragments defined in the same document. +func KnownFragmentNamesRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.FragmentSpread: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + var action = visitor.ActionNoChange + var result interface{} + if node, ok := p.Node.(*ast.FragmentSpread); ok { + + fragmentName := "" + if node.Name != nil { + fragmentName = node.Name.Value + } + + fragment := context.Fragment(fragmentName) + if fragment == nil { + reportError( + context, + fmt.Sprintf(`Unknown fragment "%v".`, fragmentName), + []ast.Node{node.Name}, + ) + } + } + return action, result + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func unknownTypeMessage(typeName string, suggestedTypes []string) string { + message := fmt.Sprintf(`Unknown type "%v".`, typeName) + if len(suggestedTypes) > 0 { + message = fmt.Sprintf(`%v Did you mean %v?`, message, quotedOrList(suggestedTypes)) + } + + return message +} + +// KnownTypeNamesRule Known type names +// +// A GraphQL document is only valid if referenced types (specifically +// variable definitions and fragment conditions) are defined by the type schema. +func KnownTypeNamesRule(context *ValidationContext) *ValidationRuleInstance { + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.ObjectDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.InterfaceDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.UnionDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.InputObjectDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.Named: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.Named); ok { + typeNameValue := "" + typeName := node.Name + if typeName != nil { + typeNameValue = typeName.Value + } + ttype := context.Schema().Type(typeNameValue) + if ttype == nil { + suggestedTypes := []string{} + for key := range context.Schema().TypeMap() { + suggestedTypes = append(suggestedTypes, key) + } + reportError( + context, + unknownTypeMessage(typeNameValue, suggestionList(typeNameValue, suggestedTypes)), + []ast.Node{node}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// LoneAnonymousOperationRule Lone anonymous operation +// +// A GraphQL document is only valid if when it contains an anonymous operation +// (the query short-hand) that it contains only that one operation definition. +func LoneAnonymousOperationRule(context *ValidationContext) *ValidationRuleInstance { + var operationCount = 0 + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Document: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.Document); ok { + operationCount = 0 + for _, definition := range node.Definitions { + if definition.GetKind() == kinds.OperationDefinition { + operationCount = operationCount + 1 + } + } + } + return visitor.ActionNoChange, nil + }, + }, + kinds.OperationDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.OperationDefinition); ok { + if node.Name == nil && operationCount > 1 { + reportError( + context, + `This anonymous operation must be the only defined operation.`, + []ast.Node{node}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func CycleErrorMessage(fragName string, spreadNames []string) string { + via := "" + if len(spreadNames) > 0 { + via = " via " + strings.Join(spreadNames, ", ") + } + return fmt.Sprintf(`Cannot spread fragment "%v" within itself%v.`, fragName, via) +} + +// NoFragmentCyclesRule No fragment cycles +func NoFragmentCyclesRule(context *ValidationContext) *ValidationRuleInstance { + + // Tracks already visited fragments to maintain O(N) and to ensure that cycles + // are not redundantly reported. + visitedFrags := map[string]bool{} + + // Array of AST nodes used to produce meaningful errors + spreadPath := []*ast.FragmentSpread{} + + // Position in the spread path + spreadPathIndexByName := map[string]int{} + + // This does a straight-forward DFS to find cycles. + // It does not terminate when a cycle was found but continues to explore + // the graph to find all possible cycles. + var detectCycleRecursive func(fragment *ast.FragmentDefinition) + detectCycleRecursive = func(fragment *ast.FragmentDefinition) { + + fragmentName := "" + if fragment.Name != nil { + fragmentName = fragment.Name.Value + } + visitedFrags[fragmentName] = true + + spreadNodes := context.FragmentSpreads(fragment.SelectionSet) + if len(spreadNodes) == 0 { + return + } + + spreadPathIndexByName[fragmentName] = len(spreadPath) + + for _, spreadNode := range spreadNodes { + + spreadName := "" + if spreadNode.Name != nil { + spreadName = spreadNode.Name.Value + } + cycleIndex, ok := spreadPathIndexByName[spreadName] + if !ok { + spreadPath = append(spreadPath, spreadNode) + if visited, ok := visitedFrags[spreadName]; !ok || !visited { + spreadFragment := context.Fragment(spreadName) + if spreadFragment != nil { + detectCycleRecursive(spreadFragment) + } + } + spreadPath = spreadPath[:len(spreadPath)-1] + } else { + cyclePath := spreadPath[cycleIndex:] + + spreadNames := []string{} + for _, s := range cyclePath { + name := "" + if s.Name != nil { + name = s.Name.Value + } + spreadNames = append(spreadNames, name) + } + + nodes := []ast.Node{} + for _, c := range cyclePath { + nodes = append(nodes, c) + } + nodes = append(nodes, spreadNode) + + reportError( + context, + CycleErrorMessage(spreadName, spreadNames), + nodes, + ) + } + + } + delete(spreadPathIndexByName, fragmentName) + + } + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.FragmentDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.FragmentDefinition); ok && node != nil { + nodeName := "" + if node.Name != nil { + nodeName = node.Name.Value + } + if _, ok := visitedFrags[nodeName]; !ok { + detectCycleRecursive(node) + } + } + return visitor.ActionSkip, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func UndefinedVarMessage(varName string, opName string) string { + if opName != "" { + return fmt.Sprintf(`Variable "$%v" is not defined by operation "%v".`, varName, opName) + } + return fmt.Sprintf(`Variable "$%v" is not defined.`, varName) +} + +// NoUndefinedVariablesRule No undefined variables +// +// A GraphQL operation is only valid if all variables encountered, both directly +// and via fragment spreads, are defined by that operation. +func NoUndefinedVariablesRule(context *ValidationContext) *ValidationRuleInstance { + var variableNameDefined = map[string]bool{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Enter: func(p visitor.VisitFuncParams) (string, interface{}) { + variableNameDefined = map[string]bool{} + return visitor.ActionNoChange, nil + }, + Leave: func(p visitor.VisitFuncParams) (string, interface{}) { + if operation, ok := p.Node.(*ast.OperationDefinition); ok && operation != nil { + usages := context.RecursiveVariableUsages(operation) + + for _, usage := range usages { + if usage == nil { + continue + } + if usage.Node == nil { + continue + } + varName := "" + if usage.Node.Name != nil { + varName = usage.Node.Name.Value + } + opName := "" + if operation.Name != nil { + opName = operation.Name.Value + } + if res, ok := variableNameDefined[varName]; !ok || !res { + reportError( + context, + UndefinedVarMessage(varName, opName), + []ast.Node{usage.Node, operation}, + ) + } + } + } + return visitor.ActionNoChange, nil + }, + }, + kinds.VariableDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.VariableDefinition); ok && node != nil { + variableName := "" + if node.Variable != nil && node.Variable.Name != nil { + variableName = node.Variable.Name.Value + } + variableNameDefined[variableName] = true + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// NoUnusedFragmentsRule No unused fragments +// +// A GraphQL document is only valid if all fragment definitions are spread +// within operations, or spread within other fragments spread within operations. +func NoUnusedFragmentsRule(context *ValidationContext) *ValidationRuleInstance { + + var fragmentDefs = []*ast.FragmentDefinition{} + var operationDefs = []*ast.OperationDefinition{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.OperationDefinition); ok && node != nil { + operationDefs = append(operationDefs, node) + } + return visitor.ActionSkip, nil + }, + }, + kinds.FragmentDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.FragmentDefinition); ok && node != nil { + fragmentDefs = append(fragmentDefs, node) + } + return visitor.ActionSkip, nil + }, + }, + kinds.Document: { + Leave: func(p visitor.VisitFuncParams) (string, interface{}) { + fragmentNameUsed := map[string]bool{} + for _, operation := range operationDefs { + fragments := context.RecursivelyReferencedFragments(operation) + for _, fragment := range fragments { + fragName := "" + if fragment.Name != nil { + fragName = fragment.Name.Value + } + fragmentNameUsed[fragName] = true + } + } + + for _, def := range fragmentDefs { + defName := "" + if def.Name != nil { + defName = def.Name.Value + } + + isFragNameUsed, ok := fragmentNameUsed[defName] + if !ok || isFragNameUsed != true { + reportError( + context, + fmt.Sprintf(`Fragment "%v" is never used.`, defName), + []ast.Node{def}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func UnusedVariableMessage(varName string, opName string) string { + if opName != "" { + return fmt.Sprintf(`Variable "$%v" is never used in operation "%v".`, varName, opName) + } + return fmt.Sprintf(`Variable "$%v" is never used.`, varName) +} + +// NoUnusedVariablesRule No unused variables +// +// A GraphQL operation is only valid if all variables defined by an operation +// are used, either directly or within a spread fragment. +func NoUnusedVariablesRule(context *ValidationContext) *ValidationRuleInstance { + + var variableDefs = []*ast.VariableDefinition{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Enter: func(p visitor.VisitFuncParams) (string, interface{}) { + variableDefs = []*ast.VariableDefinition{} + return visitor.ActionNoChange, nil + }, + Leave: func(p visitor.VisitFuncParams) (string, interface{}) { + if operation, ok := p.Node.(*ast.OperationDefinition); ok && operation != nil { + variableNameUsed := map[string]bool{} + usages := context.RecursiveVariableUsages(operation) + + for _, usage := range usages { + varName := "" + if usage != nil && usage.Node != nil && usage.Node.Name != nil { + varName = usage.Node.Name.Value + } + if varName != "" { + variableNameUsed[varName] = true + } + } + for _, variableDef := range variableDefs { + variableName := "" + if variableDef != nil && variableDef.Variable != nil && variableDef.Variable.Name != nil { + variableName = variableDef.Variable.Name.Value + } + opName := "" + if operation.Name != nil { + opName = operation.Name.Value + } + if res, ok := variableNameUsed[variableName]; !ok || !res { + reportError( + context, + UnusedVariableMessage(variableName, opName), + []ast.Node{variableDef}, + ) + } + } + + } + + return visitor.ActionNoChange, nil + }, + }, + kinds.VariableDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if def, ok := p.Node.(*ast.VariableDefinition); ok && def != nil { + variableDefs = append(variableDefs, def) + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +func getFragmentType(context *ValidationContext, name string) Type { + frag := context.Fragment(name) + if frag == nil { + return nil + } + ttype, _ := typeFromAST(*context.Schema(), frag.TypeCondition) + return ttype +} + +func doTypesOverlap(schema *Schema, t1 Type, t2 Type) bool { + if t1 == t2 { + return true + } + if _, ok := t1.(*Object); ok { + if _, ok := t2.(*Object); ok { + return false + } + if t2, ok := t2.(Abstract); ok { + for _, ttype := range schema.PossibleTypes(t2) { + if ttype == t1 { + return true + } + } + return false + } + } + if t1, ok := t1.(Abstract); ok { + if _, ok := t2.(*Object); ok { + for _, ttype := range schema.PossibleTypes(t1) { + if ttype == t2 { + return true + } + } + return false + } + t1TypeNames := map[string]bool{} + for _, ttype := range schema.PossibleTypes(t1) { + t1TypeNames[ttype.Name()] = true + } + if t2, ok := t2.(Abstract); ok { + for _, ttype := range schema.PossibleTypes(t2) { + if hasT1TypeName, _ := t1TypeNames[ttype.Name()]; hasT1TypeName { + return true + } + } + return false + } + } + return false +} + +// PossibleFragmentSpreadsRule Possible fragment spread +// +// A fragment spread is only valid if the type condition could ever possibly +// be true: if there is a non-empty intersection of the possible parent types, +// and possible types which pass the type condition. +func PossibleFragmentSpreadsRule(context *ValidationContext) *ValidationRuleInstance { + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.InlineFragment: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.InlineFragment); ok && node != nil { + fragType := context.Type() + parentType, _ := context.ParentType().(Type) + + if fragType != nil && parentType != nil && !doTypesOverlap(context.Schema(), fragType, parentType) { + reportError( + context, + fmt.Sprintf(`Fragment cannot be spread here as objects of `+ + `type "%v" can never be of type "%v".`, parentType, fragType), + []ast.Node{node}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + kinds.FragmentSpread: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.FragmentSpread); ok && node != nil { + fragName := "" + if node.Name != nil { + fragName = node.Name.Value + } + fragType := getFragmentType(context, fragName) + parentType, _ := context.ParentType().(Type) + if fragType != nil && parentType != nil && !doTypesOverlap(context.Schema(), fragType, parentType) { + reportError( + context, + fmt.Sprintf(`Fragment "%v" cannot be spread here as objects of `+ + `type "%v" can never be of type "%v".`, fragName, parentType, fragType), + []ast.Node{node}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// ProvidedNonNullArgumentsRule Provided required arguments +// +// A field or directive is only valid if all required (non-null) field arguments +// have been provided. +func ProvidedNonNullArgumentsRule(context *ValidationContext) *ValidationRuleInstance { + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Field: { + Leave: func(p visitor.VisitFuncParams) (string, interface{}) { + // Validate on leave to allow for deeper errors to appear first. + if fieldAST, ok := p.Node.(*ast.Field); ok && fieldAST != nil { + fieldDef := context.FieldDef() + if fieldDef == nil { + return visitor.ActionSkip, nil + } + + argASTs := fieldAST.Arguments + + argASTMap := map[string]*ast.Argument{} + for _, arg := range argASTs { + name := "" + if arg.Name != nil { + name = arg.Name.Value + } + argASTMap[name] = arg + } + for _, argDef := range fieldDef.Args { + argAST, _ := argASTMap[argDef.Name()] + if argAST == nil { + if argDefType, ok := argDef.Type.(*NonNull); ok { + fieldName := "" + if fieldAST.Name != nil { + fieldName = fieldAST.Name.Value + } + reportError( + context, + fmt.Sprintf(`Field "%v" argument "%v" of type "%v" `+ + `is required but not provided.`, fieldName, argDef.Name(), argDefType), + []ast.Node{fieldAST}, + ) + } + } + } + } + return visitor.ActionNoChange, nil + }, + }, + kinds.Directive: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + // Validate on leave to allow for deeper errors to appear first. + + if directiveAST, ok := p.Node.(*ast.Directive); ok && directiveAST != nil { + directiveDef := context.Directive() + if directiveDef == nil { + return visitor.ActionSkip, nil + } + argASTs := directiveAST.Arguments + + argASTMap := map[string]*ast.Argument{} + for _, arg := range argASTs { + name := "" + if arg.Name != nil { + name = arg.Name.Value + } + argASTMap[name] = arg + } + + for _, argDef := range directiveDef.Args { + argAST, _ := argASTMap[argDef.Name()] + if argAST == nil { + if argDefType, ok := argDef.Type.(*NonNull); ok { + directiveName := "" + if directiveAST.Name != nil { + directiveName = directiveAST.Name.Value + } + reportError( + context, + fmt.Sprintf(`Directive "@%v" argument "%v" of type `+ + `"%v" is required but not provided.`, directiveName, argDef.Name(), argDefType), + []ast.Node{directiveAST}, + ) + } + } + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// ScalarLeafsRule Scalar leafs +// +// A GraphQL document is valid only if all leaf fields (fields without +// sub selections) are of scalar or enum types. +func ScalarLeafsRule(context *ValidationContext) *ValidationRuleInstance { + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Field: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.Field); ok && node != nil { + nodeName := "" + if node.Name != nil { + nodeName = node.Name.Value + } + ttype := context.Type() + if ttype != nil { + if IsLeafType(ttype) { + if node.SelectionSet != nil { + reportError( + context, + fmt.Sprintf(`Field "%v" of type "%v" must not have a sub selection.`, nodeName, ttype), + []ast.Node{node.SelectionSet}, + ) + } + } else if node.SelectionSet == nil { + reportError( + context, + fmt.Sprintf(`Field "%v" of type "%v" must have a sub selection.`, nodeName, ttype), + []ast.Node{node}, + ) + } + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// UniqueArgumentNamesRule Unique argument names +// +// A GraphQL field or directive is only valid if all supplied arguments are +// uniquely named. +func UniqueArgumentNamesRule(context *ValidationContext) *ValidationRuleInstance { + knownArgNames := map[string]*ast.Name{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.Field: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + knownArgNames = map[string]*ast.Name{} + return visitor.ActionNoChange, nil + }, + }, + kinds.Directive: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + knownArgNames = map[string]*ast.Name{} + return visitor.ActionNoChange, nil + }, + }, + kinds.Argument: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.Argument); ok { + argName := "" + if node.Name != nil { + argName = node.Name.Value + } + if nameAST, ok := knownArgNames[argName]; ok { + reportError( + context, + fmt.Sprintf(`There can be only one argument named "%v".`, argName), + []ast.Node{nameAST, node.Name}, + ) + } else { + knownArgNames[argName] = node.Name + } + } + return visitor.ActionSkip, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// UniqueFragmentNamesRule Unique fragment names +// +// A GraphQL document is only valid if all defined fragments have unique names. +func UniqueFragmentNamesRule(context *ValidationContext) *ValidationRuleInstance { + knownFragmentNames := map[string]*ast.Name{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.FragmentDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.FragmentDefinition); ok && node != nil { + fragmentName := "" + if node.Name != nil { + fragmentName = node.Name.Value + } + if nameAST, ok := knownFragmentNames[fragmentName]; ok { + reportError( + context, + fmt.Sprintf(`There can only be one fragment named "%v".`, fragmentName), + []ast.Node{nameAST, node.Name}, + ) + } else { + knownFragmentNames[fragmentName] = node.Name + } + } + return visitor.ActionSkip, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// UniqueInputFieldNamesRule Unique input field names +// +// A GraphQL input object value is only valid if all supplied fields are +// uniquely named. +func UniqueInputFieldNamesRule(context *ValidationContext) *ValidationRuleInstance { + knownNameStack := []map[string]*ast.Name{} + knownNames := map[string]*ast.Name{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.ObjectValue: { + Enter: func(p visitor.VisitFuncParams) (string, interface{}) { + knownNameStack = append(knownNameStack, knownNames) + knownNames = map[string]*ast.Name{} + return visitor.ActionNoChange, nil + }, + Leave: func(p visitor.VisitFuncParams) (string, interface{}) { + // pop + knownNames, knownNameStack = knownNameStack[len(knownNameStack)-1], knownNameStack[:len(knownNameStack)-1] + return visitor.ActionNoChange, nil + }, + }, + kinds.ObjectField: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.ObjectField); ok { + fieldName := "" + if node.Name != nil { + fieldName = node.Name.Value + } + if knownNameAST, ok := knownNames[fieldName]; ok { + reportError( + context, + fmt.Sprintf(`There can be only one input field named "%v".`, fieldName), + []ast.Node{knownNameAST, node.Name}, + ) + } else { + knownNames[fieldName] = node.Name + } + + } + return visitor.ActionSkip, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// UniqueOperationNamesRule Unique operation names +// +// A GraphQL document is only valid if all defined operations have unique names. +func UniqueOperationNamesRule(context *ValidationContext) *ValidationRuleInstance { + knownOperationNames := make(map[string]ast.Node) + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.OperationDefinition); ok && node != nil { + operationName := "" + if node.Name != nil { + operationName = node.Name.Value + } + var errNode ast.Node = node + if node.Name != nil { + errNode = node.Name + } + if nameAST, ok := knownOperationNames[operationName]; ok { + reportError( + context, + fmt.Sprintf(`There can only be one operation named "%v".`, operationName), + []ast.Node{nameAST, errNode}, + ) + } else { + knownOperationNames[operationName] = errNode + } + } + return visitor.ActionSkip, nil + }, + }, + kinds.FragmentDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// UniqueVariableNamesRule Unique variable names +// +// A GraphQL operation is only valid if all its variables are uniquely named. +func UniqueVariableNamesRule(context *ValidationContext) *ValidationRuleInstance { + knownVariableNames := map[string]*ast.Name{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.OperationDefinition); ok && node != nil { + knownVariableNames = map[string]*ast.Name{} + } + return visitor.ActionNoChange, nil + }, + }, + kinds.VariableDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.VariableDefinition); ok && node != nil { + variableName := "" + var variableNameAST *ast.Name + if node.Variable != nil && node.Variable.Name != nil { + variableNameAST = node.Variable.Name + variableName = node.Variable.Name.Value + } + if nameAST, ok := knownVariableNames[variableName]; ok { + reportError( + context, + fmt.Sprintf(`There can only be one variable named "%v".`, variableName), + []ast.Node{nameAST, variableNameAST}, + ) + } else { + knownVariableNames[variableName] = variableNameAST + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// VariablesAreInputTypesRule Variables are input types +// +// A GraphQL operation is only valid if all the variables it defines are of +// input types (scalar, enum, or input object). +func VariablesAreInputTypesRule(context *ValidationContext) *ValidationRuleInstance { + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.VariableDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.VariableDefinition); ok && node != nil { + ttype, _ := typeFromAST(*context.Schema(), node.Type) + + // If the variable type is not an input type, return an error. + if ttype != nil && !IsInputType(ttype) { + variableName := "" + if node.Variable != nil && node.Variable.Name != nil { + variableName = node.Variable.Name.Value + } + reportError( + context, + fmt.Sprintf(`Variable "$%v" cannot be non-input type "%v".`, + variableName, printer.Print(node.Type)), + []ast.Node{node.Type}, + ) + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// If a variable definition has a default value, it's effectively non-null. +func effectiveType(varType Type, varDef *ast.VariableDefinition) Type { + if varDef.DefaultValue == nil { + return varType + } + if _, ok := varType.(*NonNull); ok { + return varType + } + return NewNonNull(varType) +} + +// VariablesInAllowedPositionRule Variables passed to field arguments conform to type +func VariablesInAllowedPositionRule(context *ValidationContext) *ValidationRuleInstance { + + varDefMap := map[string]*ast.VariableDefinition{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.OperationDefinition: { + Enter: func(p visitor.VisitFuncParams) (string, interface{}) { + varDefMap = map[string]*ast.VariableDefinition{} + return visitor.ActionNoChange, nil + }, + Leave: func(p visitor.VisitFuncParams) (string, interface{}) { + if operation, ok := p.Node.(*ast.OperationDefinition); ok { + + usages := context.RecursiveVariableUsages(operation) + for _, usage := range usages { + varName := "" + if usage != nil && usage.Node != nil && usage.Node.Name != nil { + varName = usage.Node.Name.Value + } + varDef, _ := varDefMap[varName] + if varDef != nil && usage.Type != nil { + varType, err := typeFromAST(*context.Schema(), varDef.Type) + if err != nil { + varType = nil + } + if varType != nil && !isTypeSubTypeOf(context.Schema(), effectiveType(varType, varDef), usage.Type) { + reportError( + context, + fmt.Sprintf(`Variable "$%v" of type "%v" used in position `+ + `expecting type "%v".`, varName, varType, usage.Type), + []ast.Node{varDef, usage.Node}, + ) + } + } + } + + } + return visitor.ActionNoChange, nil + }, + }, + kinds.VariableDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if varDefAST, ok := p.Node.(*ast.VariableDefinition); ok { + defName := "" + if varDefAST.Variable != nil && varDefAST.Variable.Name != nil { + defName = varDefAST.Variable.Name.Value + } + if defName != "" { + varDefMap[defName] = varDefAST + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +// Utility for validators which determines if a value literal AST is valid given +// an input type. +// +// Note that this only validates literal values, variables are assumed to +// provide values of the correct type. +func isValidLiteralValue(ttype Input, valueAST ast.Value) (bool, []string) { + // A value must be provided if the type is non-null. + if ttype, ok := ttype.(*NonNull); ok { + if e := ttype.Error(); e != nil { + return false, []string{e.Error()} + } + if valueAST == nil { + if ttype.OfType.Name() != "" { + return false, []string{fmt.Sprintf(`Expected "%v!", found null.`, ttype.OfType.Name())} + } + return false, []string{"Expected non-null value, found null."} + } + ofType, _ := ttype.OfType.(Input) + return isValidLiteralValue(ofType, valueAST) + } + + if valueAST == nil { + return true, nil + } + + // This function only tests literals, and assumes variables will provide + // values of the correct type. + if valueAST.GetKind() == kinds.Variable { + return true, nil + } + + // Lists accept a non-list value as a list of one. + if ttype, ok := ttype.(*List); ok { + itemType, _ := ttype.OfType.(Input) + if valueAST, ok := valueAST.(*ast.ListValue); ok { + messagesReduce := []string{} + for _, value := range valueAST.Values { + _, messages := isValidLiteralValue(itemType, value) + for idx, message := range messages { + messagesReduce = append(messagesReduce, fmt.Sprintf(`In element #%v: %v`, idx+1, message)) + } + } + return (len(messagesReduce) == 0), messagesReduce + } + return isValidLiteralValue(itemType, valueAST) + + } + + // Input objects check each defined field and look for undefined fields. + if ttype, ok := ttype.(*InputObject); ok { + valueAST, ok := valueAST.(*ast.ObjectValue) + if !ok { + return false, []string{fmt.Sprintf(`Expected "%v", found not an object.`, ttype.Name())} + } + fields := ttype.Fields() + messagesReduce := []string{} + + // Ensure every provided field is defined. + fieldASTs := valueAST.Fields + fieldASTMap := map[string]*ast.ObjectField{} + for _, fieldAST := range fieldASTs { + fieldASTName := "" + if fieldAST.Name != nil { + fieldASTName = fieldAST.Name.Value + } + + fieldASTMap[fieldASTName] = fieldAST + + field, ok := fields[fieldASTName] + if !ok || field == nil { + messagesReduce = append(messagesReduce, fmt.Sprintf(`In field "%v": Unknown field.`, fieldASTName)) + } + } + // Ensure every defined field is valid. + for fieldName, field := range fields { + fieldAST, _ := fieldASTMap[fieldName] + var fieldASTValue ast.Value + if fieldAST != nil { + fieldASTValue = fieldAST.Value + } + if isValid, messages := isValidLiteralValue(field.Type, fieldASTValue); !isValid { + for _, message := range messages { + messagesReduce = append(messagesReduce, fmt.Sprintf("In field \"%v\": %v", fieldName, message)) + } + } + } + return (len(messagesReduce) == 0), messagesReduce + } + + if ttype, ok := ttype.(*Scalar); ok { + if isNullish(ttype.ParseLiteral(valueAST)) { + return false, []string{fmt.Sprintf(`Expected type "%v", found %v.`, ttype.Name(), printer.Print(valueAST))} + } + } + if ttype, ok := ttype.(*Enum); ok { + if isNullish(ttype.ParseLiteral(valueAST)) { + return false, []string{fmt.Sprintf(`Expected type "%v", found %v.`, ttype.Name(), printer.Print(valueAST))} + } + } + + return true, nil +} + +// Internal struct to sort results from suggestionList() +type suggestionListResult struct { + Options []string + Distances []float64 +} + +func (s suggestionListResult) Len() int { + return len(s.Options) +} +func (s suggestionListResult) Swap(i, j int) { + s.Options[i], s.Options[j] = s.Options[j], s.Options[i] +} +func (s suggestionListResult) Less(i, j int) bool { + return s.Distances[i] < s.Distances[j] +} + +// suggestionList 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 { + dists := []float64{} + filteredOpts := []string{} + inputThreshold := float64(len(input) / 2) + + for _, opt := range options { + dist := lexicalDistance(input, opt) + threshold := math.Max(inputThreshold, float64(len(opt)/2)) + threshold = math.Max(threshold, 1) + if dist <= threshold { + filteredOpts = append(filteredOpts, opt) + dists = append(dists, dist) + } + } + //sort results + suggested := suggestionListResult{filteredOpts, dists} + sort.Sort(suggested) + return suggested.Options +} + +// lexicalDistance 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. +// This distance can be useful for detecting typos in input or sorting +func lexicalDistance(a, b string) float64 { + d := [][]float64{} + aLen := len(a) + bLen := len(b) + for i := 0; i <= aLen; i++ { + d = append(d, []float64{float64(i)}) + } + for k := 1; k <= bLen; k++ { + d[0] = append(d[0], float64(k)) + } + + for i := 1; i <= aLen; i++ { + for k := 1; k <= bLen; k++ { + cost := 1.0 + if a[i-1] == b[k-1] { + cost = 0.0 + } + minCostFloat := math.Min( + d[i-1][k]+1.0, + d[i][k-1]+1.0, + ) + minCostFloat = math.Min( + minCostFloat, + d[i-1][k-1]+cost, + ) + d[i] = append(d[i], minCostFloat) + + if i > 1 && k < 1 && + a[i-1] == b[k-2] && + a[i-2] == b[k-1] { + d[i][k] = math.Min(d[i][k], d[i-2][k-2]+cost) + } + } + } + + return d[aLen][bLen] +} diff --git a/vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go b/vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go new file mode 100644 index 00000000..836fb43f --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/rules_overlapping_fields_can_be_merged.go @@ -0,0 +1,706 @@ +package graphql + +import ( + "fmt" + "strings" + + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/kinds" + "github.com/graphql-go/graphql/language/printer" + "github.com/graphql-go/graphql/language/visitor" +) + +func fieldsConflictMessage(responseName string, reason conflictReason) string { + return fmt.Sprintf(`Fields "%v" conflict because %v. `+ + `Use different aliases on the fields to fetch both if this was intentional.`, + responseName, + fieldsConflictReasonMessage(reason), + ) +} + +func fieldsConflictReasonMessage(message interface{}) string { + switch reason := message.(type) { + case string: + return reason + case conflictReason: + return fieldsConflictReasonMessage(reason.Message) + case []conflictReason: + messages := []string{} + for _, r := range reason { + messages = append(messages, fmt.Sprintf( + `subfields "%v" conflict because %v`, + r.Name, + fieldsConflictReasonMessage(r.Message), + )) + } + return strings.Join(messages, " and ") + } + return "" +} + +// OverlappingFieldsCanBeMergedRule Overlapping fields can be merged +// +// A selection set is only valid if all fields (including spreading any +// fragments) either correspond to distinct response names or can be merged +// without ambiguity. +func OverlappingFieldsCanBeMergedRule(context *ValidationContext) *ValidationRuleInstance { + + // A memoization for when two fragments are compared "between" each other for + // conflicts. Two fragments may be compared many times, so memoizing this can + // dramatically improve the performance of this validator. + comparedSet := newPairSet() + + // A cache for the "field map" and list of fragment names found in any given + // selection set. Selection sets may be asked for this information multiple + // times, so this improves the performance of this validator. + cacheMap := map[*ast.SelectionSet]*fieldsAndFragmentNames{} + + visitorOpts := &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.SelectionSet: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if selectionSet, ok := p.Node.(*ast.SelectionSet); ok && selectionSet != nil { + parentType, _ := context.ParentType().(Named) + + rule := &overlappingFieldsCanBeMergedRule{ + context: context, + comparedSet: comparedSet, + cacheMap: cacheMap, + } + conflicts := rule.findConflictsWithinSelectionSet(parentType, selectionSet) + if len(conflicts) > 0 { + for _, c := range conflicts { + responseName := c.Reason.Name + reason := c.Reason + reportError( + context, + fieldsConflictMessage(responseName, reason), + append(c.FieldsLeft, c.FieldsRight...), + ) + } + return visitor.ActionNoChange, nil + } + } + return visitor.ActionNoChange, nil + }, + }, + }, + } + return &ValidationRuleInstance{ + VisitorOpts: visitorOpts, + } +} + +/** + * 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. + * + */ + +type overlappingFieldsCanBeMergedRule struct { + context *ValidationContext + + // A memoization for when two fragments are compared "between" each other for + // conflicts. Two fragments may be compared many times, so memoizing this can + // dramatically improve the performance of this validator. + comparedSet *pairSet + + // A cache for the "field map" and list of fragment names found in any given + // selection set. Selection sets may be asked for this information multiple + // times, so this improves the performance of this validator. + cacheMap map[*ast.SelectionSet]*fieldsAndFragmentNames +} + +// Find all conflicts found "within" a selection set, including those found +// via spreading in fragments. Called when visiting each SelectionSet in the +// GraphQL Document. +func (rule *overlappingFieldsCanBeMergedRule) findConflictsWithinSelectionSet(parentType Named, selectionSet *ast.SelectionSet) []conflict { + conflicts := []conflict{} + + fieldsInfo := rule.getFieldsAndFragmentNames(parentType, selectionSet) + + // (A) Find find all conflicts "within" the fields of this selection set. + // Note: this is the *only place* `collectConflictsWithin` is called. + conflicts = rule.collectConflictsWithin(conflicts, fieldsInfo) + + // (B) Then collect conflicts between these fields and those represented by + // each spread fragment name found. + for i := 0; i < len(fieldsInfo.fragmentNames); i++ { + + conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, false, fieldsInfo, fieldsInfo.fragmentNames[i]) + + // (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). + for k := i + 1; k < len(fieldsInfo.fragmentNames); k++ { + conflicts = rule.collectConflictsBetweenFragments(conflicts, false, fieldsInfo.fragmentNames[i], fieldsInfo.fragmentNames[k]) + } + } + return conflicts +} + +// Collect all conflicts found between a set of fields and a fragment reference +// including via spreading in any nested fragments. +func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFieldsAndFragment(conflicts []conflict, areMutuallyExclusive bool, fieldsInfo *fieldsAndFragmentNames, fragmentName string) []conflict { + fragment := rule.context.Fragment(fragmentName) + if fragment == nil { + return conflicts + } + + fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment) + + // (D) First collect any conflicts between the provided collection of fields + // and the collection of fields represented by the given fragment. + conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo, fieldsInfo2) + + // (E) Then collect any conflicts between the provided collection of fields + // and any fragment names found in the given fragment. + for _, fragmentName2 := range fieldsInfo2.fragmentNames { + conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName2) + } + + return conflicts + +} + +// Collect all conflicts found between two fragments, including via spreading in +// any nested fragments. +func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetweenFragments(conflicts []conflict, areMutuallyExclusive bool, fragmentName1 string, fragmentName2 string) []conflict { + fragment1 := rule.context.Fragment(fragmentName1) + fragment2 := rule.context.Fragment(fragmentName2) + + if fragment1 == nil || fragment2 == nil { + return conflicts + } + + // No need to compare a fragment to itself. + if fragment1 == fragment2 { + return conflicts + } + + // Memoize so two fragments are not compared for conflicts more than once. + if rule.comparedSet.Has(fragmentName1, fragmentName2, areMutuallyExclusive) { + return conflicts + } + rule.comparedSet.Add(fragmentName1, fragmentName2, areMutuallyExclusive) + + fieldsInfo1 := rule.getReferencedFieldsAndFragmentNames(fragment1) + fieldsInfo2 := rule.getReferencedFieldsAndFragmentNames(fragment2) + + // (F) First, collect all conflicts between these two collections of fields + // (not including any nested fragments). + conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2) + + // (G) Then collect conflicts between the first fragment and any nested + // fragments spread in the second fragment. + for _, innerFragmentName2 := range fieldsInfo2.fragmentNames { + conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, innerFragmentName2) + } + + // (G) Then collect conflicts between the second fragment and any nested + // fragments spread in the first fragment. + for _, innerFragmentName1 := range fieldsInfo1.fragmentNames { + conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, innerFragmentName1, fragmentName2) + } + + return conflicts +} + +// Find all conflicts found between two selection sets, including those found +// via spreading in fragments. Called when determining if conflicts exist +// between the sub-fields of two overlapping fields. +func (rule *overlappingFieldsCanBeMergedRule) findConflictsBetweenSubSelectionSets(areMutuallyExclusive bool, parentType1 Named, selectionSet1 *ast.SelectionSet, parentType2 Named, selectionSet2 *ast.SelectionSet) []conflict { + conflicts := []conflict{} + + fieldsInfo1 := rule.getFieldsAndFragmentNames(parentType1, selectionSet1) + fieldsInfo2 := rule.getFieldsAndFragmentNames(parentType2, selectionSet2) + + // (H) First, collect all conflicts between these two collections of field. + conflicts = rule.collectConflictsBetween(conflicts, areMutuallyExclusive, fieldsInfo1, fieldsInfo2) + + // (I) Then collect conflicts between the first collection of fields and + // those referenced by each fragment name associated with the second. + for _, fragmentName2 := range fieldsInfo2.fragmentNames { + conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo1, fragmentName2) + } + + // (I) Then collect conflicts between the second collection of fields and + // those referenced by each fragment name associated with the first. + for _, fragmentName1 := range fieldsInfo1.fragmentNames { + conflicts = rule.collectConflictsBetweenFieldsAndFragment(conflicts, areMutuallyExclusive, fieldsInfo2, fragmentName1) + } + + // (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 _, fragmentName1 := range fieldsInfo1.fragmentNames { + for _, fragmentName2 := range fieldsInfo2.fragmentNames { + conflicts = rule.collectConflictsBetweenFragments(conflicts, areMutuallyExclusive, fragmentName1, fragmentName2) + } + } + return conflicts +} + +// Collect all Conflicts "within" one collection of fields. +func (rule *overlappingFieldsCanBeMergedRule) collectConflictsWithin(conflicts []conflict, fieldsInfo *fieldsAndFragmentNames) []conflict { + // A field map is a keyed collection, where each key represents a response + // name and the value at that key is a list of all fields which provide that + // response name. For every response name, if there are multiple fields, they + // must be compared to find a potential conflict. + for _, responseName := range fieldsInfo.fieldsOrder { + fields, ok := fieldsInfo.fieldMap[responseName] + if !ok { + continue + } + // This compares every field in the list to every other field in this list + // (except to itself). If the list only has one item, nothing needs to + // be compared. + if len(fields) <= 1 { + continue + } + for i := 0; i < len(fields); i++ { + for k := i + 1; k < len(fields); k++ { + // within one collection is never mutually exclusive + isMutuallyExclusive := false + conflict := rule.findConflict(isMutuallyExclusive, responseName, fields[i], fields[k]) + if conflict != nil { + conflicts = append(conflicts, *conflict) + } + } + } + } + return conflicts +} + +// Collect all Conflicts between two collections of fields. This is similar to, +// but different from the `collectConflictsWithin` function above. This check +// assumes that `collectConflictsWithin` has already been called on each +// provided collection of fields. This is true because this validator traverses +// each individual selection set. +func (rule *overlappingFieldsCanBeMergedRule) collectConflictsBetween(conflicts []conflict, parentFieldsAreMutuallyExclusive bool, + fieldsInfo1 *fieldsAndFragmentNames, + fieldsInfo2 *fieldsAndFragmentNames) []conflict { + // A field map is a keyed collection, where each key represents a response + // name and the value at that key is a list of all fields which provide that + // response name. For any response name which appears in both provided field + // maps, each field from the first field map must be compared to every field + // in the second field map to find potential conflicts. + for _, responseName := range fieldsInfo1.fieldsOrder { + fields1, ok1 := fieldsInfo1.fieldMap[responseName] + fields2, ok2 := fieldsInfo2.fieldMap[responseName] + if !ok1 || !ok2 { + continue + } + for i := 0; i < len(fields1); i++ { + for k := 0; k < len(fields2); k++ { + conflict := rule.findConflict(parentFieldsAreMutuallyExclusive, responseName, fields1[i], fields2[k]) + if conflict != nil { + conflicts = append(conflicts, *conflict) + } + } + } + } + return conflicts +} + +// findConflict Determines if there is a conflict between two particular fields. +func (rule *overlappingFieldsCanBeMergedRule) findConflict(parentFieldsAreMutuallyExclusive bool, responseName string, field *fieldDefPair, field2 *fieldDefPair) *conflict { + + parentType1 := field.ParentType + ast1 := field.Field + def1 := field.FieldDef + + parentType2 := field2.ParentType + ast2 := field2.Field + def2 := field2.FieldDef + + // If it is known that two fields could not possibly apply at the same + // time, due to the parent types, then it is safe to permit them to diverge + // in aliased field or arguments used as they will not present any ambiguity + // by differing. + // It is known that two parent types could never overlap if they are + // different Object types. Interface or Union types might overlap - if not + // in the current state of the schema, then perhaps in some future version, + // thus may not safely diverge. + _, isParentType1Object := parentType1.(*Object) + _, isParentType2Object := parentType2.(*Object) + areMutuallyExclusive := parentFieldsAreMutuallyExclusive || parentType1 != parentType2 && isParentType1Object && isParentType2Object + + // The return type for each field. + var type1 Type + var type2 Type + if def1 != nil { + type1 = def1.Type + } + if def2 != nil { + type2 = def2.Type + } + + if !areMutuallyExclusive { + // Two aliases must refer to the same field. + name1 := "" + name2 := "" + + if ast1.Name != nil { + name1 = ast1.Name.Value + } + if ast2.Name != nil { + name2 = ast2.Name.Value + } + if name1 != name2 { + return &conflict{ + Reason: conflictReason{ + Name: responseName, + Message: fmt.Sprintf(`%v and %v are different fields`, name1, name2), + }, + FieldsLeft: []ast.Node{ast1}, + FieldsRight: []ast.Node{ast2}, + } + } + + // Two field calls must have the same arguments. + if !sameArguments(ast1.Arguments, ast2.Arguments) { + return &conflict{ + Reason: conflictReason{ + Name: responseName, + Message: `they have differing arguments`, + }, + FieldsLeft: []ast.Node{ast1}, + FieldsRight: []ast.Node{ast2}, + } + } + } + + if type1 != nil && type2 != nil && doTypesConflict(type1, type2) { + return &conflict{ + Reason: conflictReason{ + Name: responseName, + Message: fmt.Sprintf(`they return conflicting types %v and %v`, type1, type2), + }, + FieldsLeft: []ast.Node{ast1}, + FieldsRight: []ast.Node{ast2}, + } + } + + // 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. + selectionSet1 := ast1.SelectionSet + selectionSet2 := ast2.SelectionSet + if selectionSet1 != nil && selectionSet2 != nil { + conflicts := rule.findConflictsBetweenSubSelectionSets(areMutuallyExclusive, GetNamed(type1), selectionSet1, GetNamed(type2), selectionSet2) + return subfieldConflicts(conflicts, responseName, ast1, ast2) + } + return nil +} + +// Given a selection set, return the collection of fields (a mapping of response +// name to field ASTs and definitions) as well as a list of fragment names +// referenced via fragment spreads. +func (rule *overlappingFieldsCanBeMergedRule) getFieldsAndFragmentNames(parentType Named, selectionSet *ast.SelectionSet) *fieldsAndFragmentNames { + if cached, ok := rule.cacheMap[selectionSet]; ok && cached != nil { + return cached + } + + astAndDefs := astAndDefCollection{} + fieldsOrder := []string{} + fragmentNames := []string{} + fragmentNamesMap := map[string]bool{} + + var collectFieldsAndFragmentNames func(parentType Named, selectionSet *ast.SelectionSet) + collectFieldsAndFragmentNames = func(parentType Named, selectionSet *ast.SelectionSet) { + for _, selection := range selectionSet.Selections { + switch selection := selection.(type) { + case *ast.Field: + fieldName := "" + if selection.Name != nil { + fieldName = selection.Name.Value + } + var fieldDef *FieldDefinition + if parentType, ok := parentType.(*Object); ok && parentType != nil { + fieldDef, _ = parentType.Fields()[fieldName] + } + if parentType, ok := parentType.(*Interface); ok && parentType != nil { + fieldDef, _ = parentType.Fields()[fieldName] + } + + responseName := fieldName + if selection.Alias != nil { + responseName = selection.Alias.Value + } + + fieldDefPairs, ok := astAndDefs[responseName] + if !ok || fieldDefPairs == nil { + fieldDefPairs = []*fieldDefPair{} + fieldsOrder = append(fieldsOrder, responseName) + } + + fieldDefPairs = append(fieldDefPairs, &fieldDefPair{ + ParentType: parentType, + Field: selection, + FieldDef: fieldDef, + }) + astAndDefs[responseName] = fieldDefPairs + case *ast.FragmentSpread: + fieldName := "" + if selection.Name != nil { + fieldName = selection.Name.Value + } + if val, ok := fragmentNamesMap[fieldName]; !ok || !val { + fragmentNamesMap[fieldName] = true + fragmentNames = append(fragmentNames, fieldName) + } + case *ast.InlineFragment: + typeCondition := selection.TypeCondition + inlineFragmentType := parentType + if typeCondition != nil { + ttype, err := typeFromAST(*(rule.context.Schema()), typeCondition) + if err == nil { + inlineFragmentType, _ = ttype.(Named) + } + } + collectFieldsAndFragmentNames(inlineFragmentType, selection.SelectionSet) + } + } + } + collectFieldsAndFragmentNames(parentType, selectionSet) + + cached := &fieldsAndFragmentNames{ + fieldMap: astAndDefs, + fieldsOrder: fieldsOrder, + fragmentNames: fragmentNames, + } + + rule.cacheMap[selectionSet] = cached + return cached +} + +func (rule *overlappingFieldsCanBeMergedRule) getReferencedFieldsAndFragmentNames(fragment *ast.FragmentDefinition) *fieldsAndFragmentNames { + // Short-circuit building a type from the AST if possible. + if cached, ok := rule.cacheMap[fragment.SelectionSet]; ok && cached != nil { + return cached + } + fragmentType, err := typeFromAST(*(rule.context.Schema()), fragment.TypeCondition) + if err != nil { + return nil + } + return rule.getFieldsAndFragmentNames(fragmentType, fragment.SelectionSet) +} + +type conflictReason struct { + Name string + Message interface{} // conflictReason || []conflictReason +} +type conflict struct { + Reason conflictReason + FieldsLeft []ast.Node + FieldsRight []ast.Node +} + +// a.k.a AstAndDef +type fieldDefPair struct { + ParentType Named + Field *ast.Field + FieldDef *FieldDefinition +} +type astAndDefCollection map[string][]*fieldDefPair + +// cache struct for fields, its order and fragments names +type fieldsAndFragmentNames struct { + fieldMap astAndDefCollection + fieldsOrder []string // stores the order of field names in fieldMap + fragmentNames []string +} + +// pairSet A way to keep track of pairs of things when the ordering of the pair does +// not matter. We do this by maintaining a sort of double adjacency sets. +type pairSet struct { + data map[string]map[string]bool +} + +func newPairSet() *pairSet { + return &pairSet{ + data: map[string]map[string]bool{}, + } +} +func (pair *pairSet) Has(a string, b string, areMutuallyExclusive bool) bool { + first, ok := pair.data[a] + if !ok || first == nil { + return false + } + res, ok := first[b] + 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 res == false + } + return true +} +func (pair *pairSet) Add(a string, b string, areMutuallyExclusive bool) { + pair.data = pairSetAdd(pair.data, a, b, areMutuallyExclusive) + pair.data = pairSetAdd(pair.data, b, a, areMutuallyExclusive) +} +func pairSetAdd(data map[string]map[string]bool, a, b string, areMutuallyExclusive bool) map[string]map[string]bool { + set, ok := data[a] + if !ok || set == nil { + set = map[string]bool{} + } + set[b] = areMutuallyExclusive + data[a] = set + return data +} + +func sameArguments(args1 []*ast.Argument, args2 []*ast.Argument) bool { + if len(args1) != len(args2) { + return false + } + + for _, arg1 := range args1 { + arg1Name := "" + if arg1.Name != nil { + arg1Name = arg1.Name.Value + } + + var foundArgs2 *ast.Argument + for _, arg2 := range args2 { + arg2Name := "" + if arg2.Name != nil { + arg2Name = arg2.Name.Value + } + if arg1Name == arg2Name { + foundArgs2 = arg2 + } + break + } + if foundArgs2 == nil { + return false + } + if sameValue(arg1.Value, foundArgs2.Value) == false { + return false + } + } + + return true +} + +func sameValue(value1 ast.Value, value2 ast.Value) bool { + if value1 == nil && value2 == nil { + return true + } + val1 := printer.Print(value1) + val2 := printer.Print(value2) + + return val1 == val2 +} + +// Two types conflict if both types could not apply to a value simultaneously. +// Composite types are ignored as their individual field types will be compared +// later recursively. However List and Non-Null types must match. +func doTypesConflict(type1 Output, type2 Output) bool { + if type1, ok := type1.(*List); ok { + if type2, ok := type2.(*List); ok { + return doTypesConflict(type1.OfType, type2.OfType) + } + return true + } + if type2, ok := type2.(*List); ok { + if type1, ok := type1.(*List); ok { + return doTypesConflict(type1.OfType, type2.OfType) + } + return true + } + if type1, ok := type1.(*NonNull); ok { + if type2, ok := type2.(*NonNull); ok { + return doTypesConflict(type1.OfType, type2.OfType) + } + return true + } + if type2, ok := type2.(*NonNull); ok { + if type1, ok := type1.(*NonNull); ok { + return doTypesConflict(type1.OfType, type2.OfType) + } + return true + } + if IsLeafType(type1) || IsLeafType(type2) { + return type1 != type2 + } + return false +} + +// subfieldConflicts Given a series of Conflicts which occurred between two sub-fields, generate a single Conflict. +func subfieldConflicts(conflicts []conflict, responseName string, ast1 *ast.Field, ast2 *ast.Field) *conflict { + if len(conflicts) > 0 { + conflictReasons := []conflictReason{} + conflictFieldsLeft := []ast.Node{ast1} + conflictFieldsRight := []ast.Node{ast2} + for _, c := range conflicts { + conflictReasons = append(conflictReasons, c.Reason) + conflictFieldsLeft = append(conflictFieldsLeft, c.FieldsLeft...) + conflictFieldsRight = append(conflictFieldsRight, c.FieldsRight...) + } + + return &conflict{ + Reason: conflictReason{ + Name: responseName, + Message: conflictReasons, + }, + FieldsLeft: conflictFieldsLeft, + FieldsRight: conflictFieldsRight, + } + } + return nil +} diff --git a/vendor/github.com/graphql-go/graphql/scalars.go b/vendor/github.com/graphql-go/graphql/scalars.go new file mode 100644 index 00000000..0465d1b2 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/scalars.go @@ -0,0 +1,329 @@ +package graphql + +import ( + "fmt" + "math" + "strconv" + "time" + + "github.com/graphql-go/graphql/language/ast" +) + +// As per the GraphQL Spec, Integers are only treated as valid when a valid +// 32-bit signed integer, providing the broadest support across platforms. +// +// n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because +// they are internally represented as IEEE 754 doubles. +func coerceInt(value interface{}) interface{} { + switch value := value.(type) { + case bool: + if value == true { + return 1 + } + return 0 + case int: + if value < int(math.MinInt32) || value > int(math.MaxInt32) { + return nil + } + return value + case *int: + return coerceInt(*value) + case int8: + return int(value) + case *int8: + return int(*value) + case int16: + return int(value) + case *int16: + return int(*value) + case int32: + return int(value) + case *int32: + return int(*value) + case int64: + if value < int64(math.MinInt32) || value > int64(math.MaxInt32) { + return nil + } + return int(value) + case *int64: + return coerceInt(*value) + case uint: + if value > math.MaxInt32 { + return nil + } + return int(value) + case *uint: + return coerceInt(*value) + case uint8: + return int(value) + case *uint8: + return int(*value) + case uint16: + return int(value) + case *uint16: + return int(*value) + case uint32: + if value > uint32(math.MaxInt32) { + return nil + } + return int(value) + case *uint32: + return coerceInt(*value) + case uint64: + if value > uint64(math.MaxInt32) { + return nil + } + return int(value) + case *uint64: + return coerceInt(*value) + case float32: + if value < float32(math.MinInt32) || value > float32(math.MaxInt32) { + return nil + } + return int(value) + case *float32: + return coerceInt(*value) + case float64: + if value < float64(math.MinInt32) || value > float64(math.MaxInt32) { + return nil + } + return int(value) + case *float64: + return coerceInt(*value) + case string: + val, err := strconv.ParseFloat(value, 0) + if err != nil { + return nil + } + return coerceInt(val) + case *string: + return coerceInt(*value) + } + + // If the value cannot be transformed into an int, return nil instead of '0' + // to denote 'no integer found' + return nil +} + +// Int is the GraphQL Integer type definition. +var Int = NewScalar(ScalarConfig{ + Name: "Int", + Description: "The `Int` scalar type represents non-fractional signed whole numeric " + + "values. Int can represent values between -(2^31) and 2^31 - 1. ", + Serialize: coerceInt, + ParseValue: coerceInt, + ParseLiteral: func(valueAST ast.Value) interface{} { + switch valueAST := valueAST.(type) { + case *ast.IntValue: + if intValue, err := strconv.Atoi(valueAST.Value); err == nil { + return intValue + } + } + return nil + }, +}) + +func coerceFloat(value interface{}) interface{} { + switch value := value.(type) { + case bool: + if value == true { + return 1.0 + } + return 0.0 + case *bool: + return coerceFloat(*value) + case int: + return float64(value) + case *int32: + return coerceFloat(*value) + case float32: + return value + case *float32: + return coerceFloat(*value) + case float64: + return value + case *float64: + return coerceFloat(*value) + case string: + val, err := strconv.ParseFloat(value, 0) + if err != nil { + return nil + } + return val + case *string: + return coerceFloat(*value) + } + return 0.0 +} + +// Float is the GraphQL float type definition. +var Float = NewScalar(ScalarConfig{ + Name: "Float", + Description: "The `Float` scalar type represents signed double-precision fractional " + + "values as specified by " + + "[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ", + Serialize: coerceFloat, + ParseValue: coerceFloat, + ParseLiteral: func(valueAST ast.Value) interface{} { + switch valueAST := valueAST.(type) { + case *ast.FloatValue: + if floatValue, err := strconv.ParseFloat(valueAST.Value, 32); err == nil { + return floatValue + } + case *ast.IntValue: + if floatValue, err := strconv.ParseFloat(valueAST.Value, 32); err == nil { + return floatValue + } + } + return nil + }, +}) + +func coerceString(value interface{}) interface{} { + if v, ok := value.(*string); ok { + return *v + } + return fmt.Sprintf("%v", value) +} + +// String is the GraphQL string type definition +var String = NewScalar(ScalarConfig{ + Name: "String", + Description: "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.", + Serialize: coerceString, + ParseValue: coerceString, + ParseLiteral: func(valueAST ast.Value) interface{} { + switch valueAST := valueAST.(type) { + case *ast.StringValue: + return valueAST.Value + } + return nil + }, +}) + +func coerceBool(value interface{}) interface{} { + switch value := value.(type) { + case bool: + return value + case *bool: + return *value + case string: + switch value { + case "", "false": + return false + } + return true + case *string: + return coerceBool(*value) + case float64: + if value != 0 { + return true + } + return false + case *float64: + return coerceBool(*value) + case float32: + if value != 0 { + return true + } + return false + case *float32: + return coerceBool(*value) + case int: + if value != 0 { + return true + } + return false + case *int: + return coerceBool(*value) + } + return false +} + +// Boolean is the GraphQL boolean type definition +var Boolean = NewScalar(ScalarConfig{ + Name: "Boolean", + Description: "The `Boolean` scalar type represents `true` or `false`.", + Serialize: coerceBool, + ParseValue: coerceBool, + ParseLiteral: func(valueAST ast.Value) interface{} { + switch valueAST := valueAST.(type) { + case *ast.BooleanValue: + return valueAST.Value + } + return nil + }, +}) + +// ID is the GraphQL id type definition +var ID = NewScalar(ScalarConfig{ + Name: "ID", + Description: "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.", + Serialize: coerceString, + ParseValue: coerceString, + ParseLiteral: func(valueAST ast.Value) interface{} { + switch valueAST := valueAST.(type) { + case *ast.IntValue: + return valueAST.Value + case *ast.StringValue: + return valueAST.Value + } + return nil + }, +}) + +func serializeDateTime(value interface{}) interface{} { + switch value := value.(type) { + case time.Time: + buff, err := value.MarshalText() + if err != nil { + return nil + } + + return string(buff) + case *time.Time: + return serializeDateTime(*value) + default: + return nil + } +} + +func unserializeDateTime(value interface{}) interface{} { + switch value := value.(type) { + case []byte: + t := time.Time{} + err := t.UnmarshalText(value) + if err != nil { + return nil + } + + return t + case string: + return unserializeDateTime([]byte(value)) + case *string: + return unserializeDateTime([]byte(*value)) + default: + return nil + } +} + +var DateTime = NewScalar(ScalarConfig{ + Name: "DateTime", + Description: "The `DateTime` scalar type represents a DateTime." + + " The DateTime is serialized as an RFC 3339 quoted string", + Serialize: serializeDateTime, + ParseValue: unserializeDateTime, + ParseLiteral: func(valueAST ast.Value) interface{} { + switch valueAST := valueAST.(type) { + case *ast.StringValue: + return valueAST.Value + } + return nil + }, +}) diff --git a/vendor/github.com/graphql-go/graphql/schema-kitchen-sink.graphql b/vendor/github.com/graphql-go/graphql/schema-kitchen-sink.graphql new file mode 100644 index 00000000..582d94ae --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/schema-kitchen-sink.graphql @@ -0,0 +1,70 @@ +# Filename: schema-kitchen-sink.graphql + +schema { + query: QueryType + mutation: MutationType +} + +type Foo implements Bar { + one: Type + two(argument: InputType!): Type + three(argument: InputType, other: String): Int + four(argument: String = "string"): String + five(argument: [String] = ["string", "string"]): String + six(argument: InputType = {key: "value"}): Type +} + +type AnnotatedObject @onObject(arg: "value") { + annotatedField(arg: Type = "default" @onArg): Type @onField +} + +interface Bar { + one: Type + four(argument: String = "string"): String +} + +interface AnnotatedInterface @onInterface { + annotatedField(arg: Type @onArg): Type @onField +} + +union Feed = Story | Article | Advert + +union AnnotatedUnion @onUnion = A | B + +scalar CustomScalar + +scalar AnnotatedScalar @onScalar + +enum Site { + DESKTOP + MOBILE +} + +enum AnnotatedEnum @onEnum { + ANNOTATED_VALUE @onEnumValue + OTHER_VALUE +} + +input InputType { + key: String! + answer: Int = 42 +} + +input AnnotatedInput @onInputObjectType { + annotatedField: Type @onField +} + +extend type Foo { + seven(argument: [String]): Type +} + +extend type Foo @onType {} + +type NoFields {} + +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @include(if: Boolean!) + on FIELD + | FRAGMENT_SPREAD + | INLINE_FRAGMENT diff --git a/vendor/github.com/graphql-go/graphql/schema.go b/vendor/github.com/graphql-go/graphql/schema.go new file mode 100644 index 00000000..f0e6a954 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/schema.go @@ -0,0 +1,566 @@ +package graphql + +import ( + "fmt" +) + +type SchemaConfig struct { + Query *Object + Mutation *Object + Subscription *Object + Types []Type + Directives []*Directive +} + +type TypeMap map[string]Type + +// Schema Definition +// A Schema is created by supplying the root types of each type of operation, +// query, mutation (optional) and subscription (optional). A schema definition is then supplied to the +// validator and executor. +// Example: +// myAppSchema, err := NewSchema(SchemaConfig({ +// Query: MyAppQueryRootType, +// Mutation: MyAppMutationRootType, +// Subscription: MyAppSubscriptionRootType, +// }); +// Note: If an array of `directives` are provided to GraphQLSchema, that will be +// the exact list of directives represented and allowed. If `directives` is not +// provided then a default set of the specified directives (e.g. @include and +// @skip) will be used. If you wish to provide *additional* directives to these +// specified directives, you must explicitly declare them. Example: +// +// const MyAppSchema = new GraphQLSchema({ +// ... +// directives: specifiedDirectives.concat([ myCustomDirective ]), +// }) +type Schema struct { + typeMap TypeMap + directives []*Directive + + queryType *Object + mutationType *Object + subscriptionType *Object + implementations map[string][]*Object + possibleTypeMap map[string]map[string]bool +} + +func NewSchema(config SchemaConfig) (Schema, error) { + var err error + + schema := Schema{} + + err = invariant(config.Query != nil, "Schema query must be Object Type but got: nil.") + if err != nil { + return schema, err + } + + // if schema config contains error at creation time, return those errors + if config.Query != nil && config.Query.err != nil { + return schema, config.Query.err + } + if config.Mutation != nil && config.Mutation.err != nil { + return schema, config.Mutation.err + } + + schema.queryType = config.Query + schema.mutationType = config.Mutation + schema.subscriptionType = config.Subscription + + // Provide specified directives (e.g. @include and @skip) by default. + schema.directives = config.Directives + if len(schema.directives) == 0 { + schema.directives = SpecifiedDirectives + } + // Ensure directive definitions are error-free + for _, dir := range schema.directives { + if dir.err != nil { + return schema, dir.err + } + } + + // Build type map now to detect any errors within this schema. + typeMap := TypeMap{} + initialTypes := []Type{} + if schema.QueryType() != nil { + initialTypes = append(initialTypes, schema.QueryType()) + } + if schema.MutationType() != nil { + initialTypes = append(initialTypes, schema.MutationType()) + } + if schema.SubscriptionType() != nil { + initialTypes = append(initialTypes, schema.SubscriptionType()) + } + if SchemaType != nil { + initialTypes = append(initialTypes, SchemaType) + } + + for _, ttype := range config.Types { + // assume that user will never add a nil object to config + initialTypes = append(initialTypes, ttype) + } + + for _, ttype := range initialTypes { + if ttype.Error() != nil { + return schema, ttype.Error() + } + typeMap, err = typeMapReducer(&schema, typeMap, ttype) + if err != nil { + return schema, err + } + } + + schema.typeMap = typeMap + + // Keep track of all implementations by interface name. + if schema.implementations == nil { + schema.implementations = map[string][]*Object{} + } + for _, ttype := range schema.typeMap { + if ttype, ok := ttype.(*Object); ok { + for _, iface := range ttype.Interfaces() { + impls, ok := schema.implementations[iface.Name()] + if impls == nil || !ok { + impls = []*Object{} + } + impls = append(impls, ttype) + schema.implementations[iface.Name()] = impls + } + } + } + + // Enforce correct interface implementations + for _, ttype := range schema.typeMap { + if ttype, ok := ttype.(*Object); ok { + for _, iface := range ttype.Interfaces() { + err := assertObjectImplementsInterface(&schema, ttype, iface) + if err != nil { + return schema, err + } + } + } + } + + return schema, nil +} + + + +//Added Check implementation of interfaces at runtime.. +//Add Implementations at Runtime.. +func (gq *Schema) AddImplementation() error{ + + // Keep track of all implementations by interface name. + if gq.implementations == nil { + gq.implementations = map[string][]*Object{} + } + for _, ttype := range gq.typeMap { + if ttype, ok := ttype.(*Object); ok { + for _, iface := range ttype.Interfaces() { + impls, ok := gq.implementations[iface.Name()] + if impls == nil || !ok { + impls = []*Object{} + } + impls = append(impls, ttype) + gq.implementations[iface.Name()] = impls + } + } + } + + // Enforce correct interface implementations + for _, ttype := range gq.typeMap { + if ttype, ok := ttype.(*Object); ok { + for _, iface := range ttype.Interfaces() { + err := assertObjectImplementsInterface(gq, ttype, iface) + if err != nil { + return err + } + } + } + } + + return nil +} + + +//Edited. To check add Types at RunTime.. +//Append Runtime schema to typeMap +func (gq *Schema)AppendType(objectType Type) error { + if objectType.Error() != nil { + return objectType.Error() + } + var err error + gq.typeMap, err = typeMapReducer(gq, gq.typeMap, objectType) + if err != nil { + return err + } + //Now Add interface implementation.. + return gq.AddImplementation() +} + + + + +func (gq *Schema) QueryType() *Object { + return gq.queryType +} + +func (gq *Schema) MutationType() *Object { + return gq.mutationType +} + +func (gq *Schema) SubscriptionType() *Object { + return gq.subscriptionType +} + +func (gq *Schema) Directives() []*Directive { + return gq.directives +} + +func (gq *Schema) Directive(name string) *Directive { + for _, directive := range gq.Directives() { + if directive.Name == name { + return directive + } + } + return nil +} + +func (gq *Schema) TypeMap() TypeMap { + return gq.typeMap +} + +func (gq *Schema) Type(name string) Type { + return gq.TypeMap()[name] +} + +func (gq *Schema) PossibleTypes(abstractType Abstract) []*Object { + if abstractType, ok := abstractType.(*Union); ok { + return abstractType.Types() + } + if abstractType, ok := abstractType.(*Interface); ok { + if impls, ok := gq.implementations[abstractType.Name()]; ok { + return impls + } + } + return []*Object{} +} +func (gq *Schema) IsPossibleType(abstractType Abstract, possibleType *Object) bool { + possibleTypeMap := gq.possibleTypeMap + if possibleTypeMap == nil { + possibleTypeMap = map[string]map[string]bool{} + } + + if typeMap, ok := possibleTypeMap[abstractType.Name()]; !ok { + typeMap = map[string]bool{} + for _, possibleType := range gq.PossibleTypes(abstractType) { + typeMap[possibleType.Name()] = true + } + possibleTypeMap[abstractType.Name()] = typeMap + } + + gq.possibleTypeMap = possibleTypeMap + if typeMap, ok := possibleTypeMap[abstractType.Name()]; ok { + isPossible, _ := typeMap[possibleType.Name()] + return isPossible + } + return false +} +func typeMapReducer(schema *Schema, typeMap TypeMap, objectType Type) (TypeMap, error) { + var err error + if objectType == nil || objectType.Name() == "" { + return typeMap, nil + } + + switch objectType := objectType.(type) { + case *List: + if objectType.OfType != nil { + return typeMapReducer(schema, typeMap, objectType.OfType) + } + case *NonNull: + if objectType.OfType != nil { + return typeMapReducer(schema, typeMap, objectType.OfType) + } + case *Object: + if objectType.err != nil { + return typeMap, objectType.err + } + } + + if mappedObjectType, ok := typeMap[objectType.Name()]; ok { + err = invariantf( + mappedObjectType == objectType, + `Schema must contain unique named types but contains multiple types named "%v".`, objectType.Name()) + + if err != nil { + return typeMap, err + } + return typeMap, err + } + if objectType.Name() == "" { + return typeMap, nil + } + + typeMap[objectType.Name()] = objectType + + switch objectType := objectType.(type) { + case *Union: + types := schema.PossibleTypes(objectType) + if objectType.err != nil { + return typeMap, objectType.err + } + for _, innerObjectType := range types { + if innerObjectType.err != nil { + return typeMap, innerObjectType.err + } + typeMap, err = typeMapReducer(schema, typeMap, innerObjectType) + if err != nil { + return typeMap, err + } + } + case *Interface: + types := schema.PossibleTypes(objectType) + if objectType.err != nil { + return typeMap, objectType.err + } + for _, innerObjectType := range types { + if innerObjectType.err != nil { + return typeMap, innerObjectType.err + } + typeMap, err = typeMapReducer(schema, typeMap, innerObjectType) + if err != nil { + return typeMap, err + } + } + case *Object: + interfaces := objectType.Interfaces() + if objectType.err != nil { + return typeMap, objectType.err + } + for _, innerObjectType := range interfaces { + if innerObjectType.err != nil { + return typeMap, innerObjectType.err + } + typeMap, err = typeMapReducer(schema, typeMap, innerObjectType) + if err != nil { + return typeMap, err + } + } + } + + switch objectType := objectType.(type) { + case *Object: + fieldMap := objectType.Fields() + if objectType.err != nil { + return typeMap, objectType.err + } + for _, field := range fieldMap { + for _, arg := range field.Args { + typeMap, err = typeMapReducer(schema, typeMap, arg.Type) + if err != nil { + return typeMap, err + } + } + typeMap, err = typeMapReducer(schema, typeMap, field.Type) + if err != nil { + return typeMap, err + } + } + case *Interface: + fieldMap := objectType.Fields() + if objectType.err != nil { + return typeMap, objectType.err + } + for _, field := range fieldMap { + for _, arg := range field.Args { + typeMap, err = typeMapReducer(schema, typeMap, arg.Type) + if err != nil { + return typeMap, err + } + } + typeMap, err = typeMapReducer(schema, typeMap, field.Type) + if err != nil { + return typeMap, err + } + } + case *InputObject: + fieldMap := objectType.Fields() + if objectType.err != nil { + return typeMap, objectType.err + } + for _, field := range fieldMap { + typeMap, err = typeMapReducer(schema, typeMap, field.Type) + if err != nil { + return typeMap, err + } + } + } + return typeMap, nil +} + +func assertObjectImplementsInterface(schema *Schema, object *Object, iface *Interface) error { + objectFieldMap := object.Fields() + ifaceFieldMap := iface.Fields() + + // Assert each interface field is implemented. + for fieldName := range ifaceFieldMap { + objectField := objectFieldMap[fieldName] + ifaceField := ifaceFieldMap[fieldName] + + // Assert interface field exists on object. + err := invariantf( + objectField != nil, + `"%v" expects field "%v" but "%v" does not `+ + `provide it.`, iface, fieldName, object) + + if err != nil { + return err + } + + // Assert interface field type is satisfied by object field type, by being + // a valid subtype. (covariant) + err = invariant( + isTypeSubTypeOf(schema, objectField.Type, ifaceField.Type), + fmt.Sprintf(`%v.%v expects type "%v" but `+ + `%v.%v provides type "%v".`, + iface, fieldName, ifaceField.Type, + object, fieldName, objectField.Type), + ) + if err != nil { + return err + } + + // Assert each interface field arg is implemented. + for _, ifaceArg := range ifaceField.Args { + argName := ifaceArg.PrivateName + var objectArg *Argument + for _, arg := range objectField.Args { + if arg.PrivateName == argName { + objectArg = arg + break + } + } + // Assert interface field arg exists on object field. + err = invariant( + objectArg != nil, + fmt.Sprintf(`%v.%v expects argument "%v" but `+ + `%v.%v does not provide it.`, + iface, fieldName, argName, + object, fieldName), + ) + if err != nil { + return err + } + + // Assert interface field arg type matches object field arg type. + // (invariant) + err = invariant( + isEqualType(ifaceArg.Type, objectArg.Type), + fmt.Sprintf( + `%v.%v(%v:) expects type "%v" `+ + `but %v.%v(%v:) provides `+ + `type "%v".`, + iface, fieldName, argName, ifaceArg.Type, + object, fieldName, argName, objectArg.Type), + ) + if err != nil { + return err + } + } + // Assert additional arguments must not be required. + for _, objectArg := range objectField.Args { + argName := objectArg.PrivateName + var ifaceArg *Argument + for _, arg := range ifaceField.Args { + if arg.PrivateName == argName { + ifaceArg = arg + break + } + } + + if ifaceArg == nil { + _, ok := objectArg.Type.(*NonNull) + err = invariant( + !ok, + fmt.Sprintf(`%v.%v(%v:) is of required type `+ + `"%v" but is not also provided by the interface %v.%v.`, + object, fieldName, argName, + objectArg.Type, iface, fieldName), + ) + if err != nil { + return err + } + } + } + } + return nil +} + +func isEqualType(typeA Type, typeB Type) bool { + // Equivalent type is a valid subtype + if typeA == typeB { + return true + } + // If either type is non-null, the other must also be non-null. + if typeA, ok := typeA.(*NonNull); ok { + if typeB, ok := typeB.(*NonNull); ok { + return isEqualType(typeA.OfType, typeB.OfType) + } + } + // If either type is a list, the other must also be a list. + if typeA, ok := typeA.(*List); ok { + if typeB, ok := typeB.(*List); ok { + return isEqualType(typeA.OfType, typeB.OfType) + } + } + // Otherwise the types are not equal. + return false +} + +// isTypeSubTypeOf Provided a type and a super type, return true if the first type is either +// equal or a subset of the second super type (covariant). +func isTypeSubTypeOf(schema *Schema, maybeSubType Type, superType Type) bool { + // Equivalent type is a valid subtype + if maybeSubType == superType { + return true + } + + // If superType is non-null, maybeSubType must also be nullable. + if superType, ok := superType.(*NonNull); ok { + if maybeSubType, ok := maybeSubType.(*NonNull); ok { + return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType) + } + return false + } + if maybeSubType, ok := maybeSubType.(*NonNull); ok { + // If superType is nullable, maybeSubType may be non-null. + return isTypeSubTypeOf(schema, maybeSubType.OfType, superType) + } + + // If superType type is a list, maybeSubType type must also be a list. + if superType, ok := superType.(*List); ok { + if maybeSubType, ok := maybeSubType.(*List); ok { + return isTypeSubTypeOf(schema, maybeSubType.OfType, superType.OfType) + } + return false + } else if _, ok := maybeSubType.(*List); ok { + // If superType is not a list, maybeSubType must also be not a list. + return false + } + + // If superType type is an abstract type, maybeSubType type may be a currently + // possible object type. + if superType, ok := superType.(*Interface); ok { + if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) { + return true + } + } + if superType, ok := superType.(*Union); ok { + if maybeSubType, ok := maybeSubType.(*Object); ok && schema.IsPossibleType(superType, maybeSubType) { + return true + } + } + + // Otherwise, the child type is not a valid subtype of the parent type. + return false +} diff --git a/vendor/github.com/graphql-go/graphql/type_info.go b/vendor/github.com/graphql-go/graphql/type_info.go new file mode 100644 index 00000000..bcdc84a8 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/type_info.go @@ -0,0 +1,276 @@ +package graphql + +import ( + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/kinds" +) + +// TODO: can move TypeInfo to a utils package if there ever is one +/** + * TypeInfo is a utility class which, given a GraphQL schema, can keep track + * of the current field and type definitions at any point in a GraphQL document + * AST during a recursive descent by calling `enter(node)` and `leave(node)`. + */ +type fieldDefFn func(schema *Schema, parentType Type, fieldAST *ast.Field) *FieldDefinition + +type TypeInfo struct { + schema *Schema + typeStack []Output + parentTypeStack []Composite + inputTypeStack []Input + fieldDefStack []*FieldDefinition + directive *Directive + argument *Argument + getFieldDef fieldDefFn +} + +type TypeInfoConfig struct { + Schema *Schema + + // NOTE: this experimental optional second parameter is only needed in order + // to support non-spec-compliant codebases. You should never need to use it. + // It may disappear in the future. + FieldDefFn fieldDefFn +} + +func NewTypeInfo(opts *TypeInfoConfig) *TypeInfo { + getFieldDef := opts.FieldDefFn + if getFieldDef == nil { + getFieldDef = DefaultTypeInfoFieldDef + } + return &TypeInfo{ + schema: opts.Schema, + getFieldDef: getFieldDef, + } +} + +func (ti *TypeInfo) Type() Output { + if len(ti.typeStack) > 0 { + return ti.typeStack[len(ti.typeStack)-1] + } + return nil +} + +func (ti *TypeInfo) ParentType() Composite { + if len(ti.parentTypeStack) > 0 { + return ti.parentTypeStack[len(ti.parentTypeStack)-1] + } + return nil +} + +func (ti *TypeInfo) InputType() Input { + if len(ti.inputTypeStack) > 0 { + return ti.inputTypeStack[len(ti.inputTypeStack)-1] + } + return nil +} +func (ti *TypeInfo) FieldDef() *FieldDefinition { + if len(ti.fieldDefStack) > 0 { + return ti.fieldDefStack[len(ti.fieldDefStack)-1] + } + return nil +} + +func (ti *TypeInfo) Directive() *Directive { + return ti.directive +} + +func (ti *TypeInfo) Argument() *Argument { + return ti.argument +} + +func (ti *TypeInfo) Enter(node ast.Node) { + + schema := ti.schema + var ttype Type + switch node := node.(type) { + case *ast.SelectionSet: + namedType := GetNamed(ti.Type()) + var compositeType Composite + if IsCompositeType(namedType) { + compositeType, _ = namedType.(Composite) + } + ti.parentTypeStack = append(ti.parentTypeStack, compositeType) + case *ast.Field: + parentType := ti.ParentType() + var fieldDef *FieldDefinition + if parentType != nil { + fieldDef = ti.getFieldDef(schema, parentType.(Type), node) + } + ti.fieldDefStack = append(ti.fieldDefStack, fieldDef) + if fieldDef != nil { + ti.typeStack = append(ti.typeStack, fieldDef.Type) + } else { + ti.typeStack = append(ti.typeStack, nil) + } + case *ast.Directive: + nameVal := "" + if node.Name != nil { + nameVal = node.Name.Value + } + ti.directive = schema.Directive(nameVal) + case *ast.OperationDefinition: + if node.Operation == ast.OperationTypeQuery { + ttype = schema.QueryType() + } else if node.Operation == ast.OperationTypeMutation { + ttype = schema.MutationType() + } else if node.Operation == ast.OperationTypeSubscription { + ttype = schema.SubscriptionType() + } + ti.typeStack = append(ti.typeStack, ttype) + case *ast.InlineFragment: + typeConditionAST := node.TypeCondition + if typeConditionAST != nil { + ttype, _ = typeFromAST(*schema, node.TypeCondition) + ti.typeStack = append(ti.typeStack, ttype) + } else { + ti.typeStack = append(ti.typeStack, ti.Type()) + } + case *ast.FragmentDefinition: + typeConditionAST := node.TypeCondition + if typeConditionAST != nil { + ttype, _ = typeFromAST(*schema, typeConditionAST) + ti.typeStack = append(ti.typeStack, ttype) + } else { + ti.typeStack = append(ti.typeStack, ti.Type()) + } + case *ast.VariableDefinition: + ttype, _ = typeFromAST(*schema, node.Type) + ti.inputTypeStack = append(ti.inputTypeStack, ttype) + case *ast.Argument: + nameVal := "" + if node.Name != nil { + nameVal = node.Name.Value + } + var argType Input + var argDef *Argument + directive := ti.Directive() + fieldDef := ti.FieldDef() + if directive != nil { + for _, arg := range directive.Args { + if arg.Name() == nameVal { + argDef = arg + } + } + } else if fieldDef != nil { + for _, arg := range fieldDef.Args { + if arg.Name() == nameVal { + argDef = arg + } + } + } + if argDef != nil { + argType = argDef.Type + } + ti.argument = argDef + ti.inputTypeStack = append(ti.inputTypeStack, argType) + case *ast.ListValue: + listType := GetNullable(ti.InputType()) + if list, ok := listType.(*List); ok { + ti.inputTypeStack = append(ti.inputTypeStack, list.OfType) + } else { + ti.inputTypeStack = append(ti.inputTypeStack, nil) + } + case *ast.ObjectField: + var fieldType Input + objectType := GetNamed(ti.InputType()) + + if objectType, ok := objectType.(*InputObject); ok { + nameVal := "" + if node.Name != nil { + nameVal = node.Name.Value + } + if inputField, ok := objectType.Fields()[nameVal]; ok { + fieldType = inputField.Type + } + } + ti.inputTypeStack = append(ti.inputTypeStack, fieldType) + } +} +func (ti *TypeInfo) Leave(node ast.Node) { + kind := node.GetKind() + switch kind { + case kinds.SelectionSet: + // pop ti.parentTypeStack + if len(ti.parentTypeStack) > 0 { + _, ti.parentTypeStack = ti.parentTypeStack[len(ti.parentTypeStack)-1], ti.parentTypeStack[:len(ti.parentTypeStack)-1] + } + case kinds.Field: + // pop ti.fieldDefStack + if len(ti.fieldDefStack) > 0 { + _, ti.fieldDefStack = ti.fieldDefStack[len(ti.fieldDefStack)-1], ti.fieldDefStack[:len(ti.fieldDefStack)-1] + } + // pop ti.typeStack + if len(ti.typeStack) > 0 { + _, ti.typeStack = ti.typeStack[len(ti.typeStack)-1], ti.typeStack[:len(ti.typeStack)-1] + } + case kinds.Directive: + ti.directive = nil + case kinds.OperationDefinition: + fallthrough + case kinds.InlineFragment: + fallthrough + case kinds.FragmentDefinition: + // pop ti.typeStack + if len(ti.typeStack) > 0 { + _, ti.typeStack = ti.typeStack[len(ti.typeStack)-1], ti.typeStack[:len(ti.typeStack)-1] + } + case kinds.VariableDefinition: + // pop ti.inputTypeStack + if len(ti.inputTypeStack) > 0 { + _, ti.inputTypeStack = ti.inputTypeStack[len(ti.inputTypeStack)-1], ti.inputTypeStack[:len(ti.inputTypeStack)-1] + } + case kinds.Argument: + ti.argument = nil + // pop ti.inputTypeStack + if len(ti.inputTypeStack) > 0 { + _, ti.inputTypeStack = ti.inputTypeStack[len(ti.inputTypeStack)-1], ti.inputTypeStack[:len(ti.inputTypeStack)-1] + } + case kinds.ListValue: + fallthrough + case kinds.ObjectField: + // pop ti.inputTypeStack + if len(ti.inputTypeStack) > 0 { + _, ti.inputTypeStack = ti.inputTypeStack[len(ti.inputTypeStack)-1], ti.inputTypeStack[:len(ti.inputTypeStack)-1] + } + } +} + +// DefaultTypeInfoFieldDef Not exactly the same as the executor's definition of FieldDef, in this +// statically evaluated environment we do not always have an Object type, +// and need to handle Interface and Union types. +func DefaultTypeInfoFieldDef(schema *Schema, parentType Type, fieldAST *ast.Field) *FieldDefinition { + name := "" + if fieldAST.Name != nil { + name = fieldAST.Name.Value + } + if name == SchemaMetaFieldDef.Name && + schema.QueryType() == parentType { + return SchemaMetaFieldDef + } + if name == TypeMetaFieldDef.Name && + schema.QueryType() == parentType { + return TypeMetaFieldDef + } + if name == TypeNameMetaFieldDef.Name && parentType != nil { + if t, ok := parentType.(*Object); ok && t != nil { + return TypeNameMetaFieldDef + } + if t, ok := parentType.(*Interface); ok && t != nil { + return TypeNameMetaFieldDef + } + if t, ok := parentType.(*Union); ok && t != nil { + return TypeNameMetaFieldDef + } + } + + if parentType, ok := parentType.(*Object); ok && parentType != nil { + field, _ := parentType.Fields()[name] + return field + } + if parentType, ok := parentType.(*Interface); ok && parentType != nil { + field, _ := parentType.Fields()[name] + return field + } + return nil +} diff --git a/vendor/github.com/graphql-go/graphql/types.go b/vendor/github.com/graphql-go/graphql/types.go new file mode 100644 index 00000000..8bd1fa68 --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/types.go @@ -0,0 +1,16 @@ +package graphql + +import ( + "github.com/graphql-go/graphql/gqlerrors" +) + +// type Schema interface{} + +type Result struct { + Data interface{} `json:"data"` + Errors []gqlerrors.FormattedError `json:"errors,omitempty"` +} + +func (r *Result) HasErrors() bool { + return len(r.Errors) > 0 +} diff --git a/vendor/github.com/graphql-go/graphql/util.go b/vendor/github.com/graphql-go/graphql/util.go new file mode 100644 index 00000000..dd4f370e --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/util.go @@ -0,0 +1,180 @@ +package graphql + +import ( + "fmt" + "reflect" + "strings" +) + +const TAG = "json" + +// can't take recursive slice type +// e.g +// type Person struct{ +// Friends []Person +// } +// it will throw panic stack-overflow +func BindFields(obj interface{}) Fields { + v := reflect.ValueOf(obj) + fields := make(map[string]*Field) + + for i := 0; i < v.NumField(); i++ { + typeField := v.Type().Field(i) + + tag := extractTag(typeField.Tag) + if tag == "-" { + continue + } + + var graphType Output + if typeField.Type.Kind() == reflect.Struct { + + structFields := BindFields(v.Field(i).Interface()) + if tag == "" { + fields = appendFields(fields, structFields) + continue + } else { + graphType = NewObject(ObjectConfig{ + Name: tag, + Fields: structFields, + }) + } + } + + if tag == "" { + continue + } + + if graphType == nil { + graphType = getGraphType(typeField.Type) + } + fields[tag] = &Field{ + Type: graphType, + Resolve: func(p ResolveParams) (interface{}, error) { + return extractValue(tag, p.Source), nil + }, + } + } + return fields +} + +func getGraphType(tipe reflect.Type) Output { + kind := tipe.Kind() + switch kind { + case reflect.String: + return String + case reflect.Int: + fallthrough + case reflect.Int8: + fallthrough + case reflect.Int32: + fallthrough + case reflect.Int64: + return Int + case reflect.Float32: + fallthrough + case reflect.Float64: + return Float + case reflect.Bool: + return Boolean + case reflect.Slice: + return getGraphList(tipe) + } + return String +} + +func getGraphList(tipe reflect.Type) *List { + if tipe.Kind() == reflect.Slice { + switch tipe.Elem().Kind() { + case reflect.Int: + fallthrough + case reflect.Int8: + fallthrough + case reflect.Int32: + fallthrough + case reflect.Int64: + return NewList(Int) + case reflect.Bool: + return NewList(Boolean) + case reflect.Float32: + fallthrough + case reflect.Float64: + return NewList(Float) + case reflect.String: + return NewList(String) + } + } + // finaly bind object + t := reflect.New(tipe.Elem()) + name := strings.Replace(fmt.Sprint(tipe.Elem()), ".", "_", -1) + obj := NewObject(ObjectConfig{ + Name: name, + Fields: BindFields(t.Elem().Interface()), + }) + return NewList(obj) +} + +func appendFields(dest, origin Fields) Fields { + for key, value := range origin { + dest[key] = value + } + return dest +} + +func extractValue(originTag string, obj interface{}) interface{} { + val := reflect.ValueOf(obj) + + for j := 0; j < val.NumField(); j++ { + typeField := val.Type().Field(j) + if typeField.Type.Kind() == reflect.Struct { + res := extractValue(originTag, val.Field(j).Interface()) + if res != nil { + return res + } + } + + if originTag == extractTag(typeField.Tag) { + return val.Field(j).Interface() + } + } + return nil +} + +func extractTag(tag reflect.StructTag) string { + t := tag.Get(TAG) + if t != "" { + t = strings.Split(t, ",")[0] + } + return t +} + +// lazy way of binding args +func BindArg(obj interface{}, tags ...string) FieldConfigArgument { + v := reflect.ValueOf(obj) + var config = make(FieldConfigArgument) + for i := 0; i < v.NumField(); i++ { + typeField := v.Type().Field(i) + + mytag := extractTag(typeField.Tag) + if inArray(tags, mytag) { + config[mytag] = &ArgumentConfig{ + Type: getGraphType(typeField.Type), + } + } + } + return config +} + +func inArray(slice interface{}, item interface{}) bool { + s := reflect.ValueOf(slice) + if s.Kind() != reflect.Slice { + panic("inArray() given a non-slice type") + } + + for i := 0; i < s.Len(); i++ { + if reflect.DeepEqual(item, s.Index(i).Interface()) { + return true + } + } + return false +} diff --git a/vendor/github.com/graphql-go/graphql/validator.go b/vendor/github.com/graphql-go/graphql/validator.go new file mode 100644 index 00000000..73c213eb --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/validator.go @@ -0,0 +1,288 @@ +package graphql + +import ( + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/kinds" + "github.com/graphql-go/graphql/language/visitor" +) + +type ValidationResult struct { + IsValid bool + Errors []gqlerrors.FormattedError +} + +/** + * Implements the "Validation" section of the spec. + * + * Validation runs synchronously, returning an array of encountered errors, or + * an empty array if no errors were encountered and the document is valid. + * + * A list of specific validation rules may be provided. If not provided, the + * default list of rules defined by the GraphQL specification will be used. + * + * Each validation rules is a function which returns a visitor + * (see the language/visitor API). Visitor methods are expected to return + * GraphQLErrors, or Arrays of GraphQLErrors when invalid. + */ + +func ValidateDocument(schema *Schema, astDoc *ast.Document, rules []ValidationRuleFn) (vr ValidationResult) { + if len(rules) == 0 { + rules = SpecifiedRules + } + + vr.IsValid = false + if schema == nil { + vr.Errors = append(vr.Errors, gqlerrors.NewFormattedError("Must provide schema")) + return vr + } + if astDoc == nil { + vr.Errors = append(vr.Errors, gqlerrors.NewFormattedError("Must provide document")) + return vr + } + + typeInfo := NewTypeInfo(&TypeInfoConfig{ + Schema: schema, + }) + vr.Errors = VisitUsingRules(schema, typeInfo, astDoc, rules) + if len(vr.Errors) == 0 { + vr.IsValid = true + } + return vr +} + +// VisitUsingRules This uses a specialized visitor which runs multiple visitors in parallel, +// while maintaining the visitor skip and break API. +// +// @internal +// Had to expose it to unit test experimental customizable validation feature, +// but not meant for public consumption +func VisitUsingRules(schema *Schema, typeInfo *TypeInfo, astDoc *ast.Document, rules []ValidationRuleFn) []gqlerrors.FormattedError { + + context := NewValidationContext(schema, astDoc, typeInfo) + visitors := []*visitor.VisitorOptions{} + + for _, rule := range rules { + instance := rule(context) + visitors = append(visitors, instance.VisitorOpts) + } + + // Visit the whole document with each instance of all provided rules. + visitor.Visit(astDoc, visitor.VisitWithTypeInfo(typeInfo, visitor.VisitInParallel(visitors...)), nil) + return context.Errors() +} + +type HasSelectionSet interface { + GetKind() string + GetLoc() *ast.Location + GetSelectionSet() *ast.SelectionSet +} + +var _ HasSelectionSet = (*ast.OperationDefinition)(nil) +var _ HasSelectionSet = (*ast.FragmentDefinition)(nil) + +type VariableUsage struct { + Node *ast.Variable + Type Input +} + +type ValidationContext struct { + schema *Schema + astDoc *ast.Document + typeInfo *TypeInfo + errors []gqlerrors.FormattedError + fragments map[string]*ast.FragmentDefinition + variableUsages map[HasSelectionSet][]*VariableUsage + recursiveVariableUsages map[*ast.OperationDefinition][]*VariableUsage + recursivelyReferencedFragments map[*ast.OperationDefinition][]*ast.FragmentDefinition + fragmentSpreads map[*ast.SelectionSet][]*ast.FragmentSpread +} + +func NewValidationContext(schema *Schema, astDoc *ast.Document, typeInfo *TypeInfo) *ValidationContext { + return &ValidationContext{ + schema: schema, + astDoc: astDoc, + typeInfo: typeInfo, + fragments: map[string]*ast.FragmentDefinition{}, + variableUsages: map[HasSelectionSet][]*VariableUsage{}, + recursiveVariableUsages: map[*ast.OperationDefinition][]*VariableUsage{}, + recursivelyReferencedFragments: map[*ast.OperationDefinition][]*ast.FragmentDefinition{}, + fragmentSpreads: map[*ast.SelectionSet][]*ast.FragmentSpread{}, + } +} + +func (ctx *ValidationContext) ReportError(err error) { + formattedErr := gqlerrors.FormatError(err) + ctx.errors = append(ctx.errors, formattedErr) +} +func (ctx *ValidationContext) Errors() []gqlerrors.FormattedError { + return ctx.errors +} + +func (ctx *ValidationContext) Schema() *Schema { + return ctx.schema +} +func (ctx *ValidationContext) Document() *ast.Document { + return ctx.astDoc +} +func (ctx *ValidationContext) Fragment(name string) *ast.FragmentDefinition { + if len(ctx.fragments) == 0 { + if ctx.Document() == nil { + return nil + } + defs := ctx.Document().Definitions + fragments := map[string]*ast.FragmentDefinition{} + for _, def := range defs { + if def, ok := def.(*ast.FragmentDefinition); ok { + defName := "" + if def.Name != nil { + defName = def.Name.Value + } + fragments[defName] = def + } + } + ctx.fragments = fragments + } + f, _ := ctx.fragments[name] + return f +} +func (ctx *ValidationContext) FragmentSpreads(node *ast.SelectionSet) []*ast.FragmentSpread { + if spreads, ok := ctx.fragmentSpreads[node]; ok && spreads != nil { + return spreads + } + + spreads := []*ast.FragmentSpread{} + setsToVisit := []*ast.SelectionSet{node} + + for { + if len(setsToVisit) == 0 { + break + } + var set *ast.SelectionSet + // pop + set, setsToVisit = setsToVisit[len(setsToVisit)-1], setsToVisit[:len(setsToVisit)-1] + if set.Selections != nil { + for _, selection := range set.Selections { + switch selection := selection.(type) { + case *ast.FragmentSpread: + spreads = append(spreads, selection) + case *ast.Field: + if selection.SelectionSet != nil { + setsToVisit = append(setsToVisit, selection.SelectionSet) + } + case *ast.InlineFragment: + if selection.SelectionSet != nil { + setsToVisit = append(setsToVisit, selection.SelectionSet) + } + } + } + } + ctx.fragmentSpreads[node] = spreads + } + return spreads +} + +func (ctx *ValidationContext) RecursivelyReferencedFragments(operation *ast.OperationDefinition) []*ast.FragmentDefinition { + if fragments, ok := ctx.recursivelyReferencedFragments[operation]; ok && fragments != nil { + return fragments + } + + fragments := []*ast.FragmentDefinition{} + collectedNames := map[string]bool{} + nodesToVisit := []*ast.SelectionSet{operation.SelectionSet} + + for { + if len(nodesToVisit) == 0 { + break + } + + var node *ast.SelectionSet + + node, nodesToVisit = nodesToVisit[len(nodesToVisit)-1], nodesToVisit[:len(nodesToVisit)-1] + spreads := ctx.FragmentSpreads(node) + for _, spread := range spreads { + fragName := "" + if spread.Name != nil { + fragName = spread.Name.Value + } + if res, ok := collectedNames[fragName]; !ok || !res { + collectedNames[fragName] = true + fragment := ctx.Fragment(fragName) + if fragment != nil { + fragments = append(fragments, fragment) + nodesToVisit = append(nodesToVisit, fragment.SelectionSet) + } + } + + } + } + + ctx.recursivelyReferencedFragments[operation] = fragments + return fragments +} +func (ctx *ValidationContext) VariableUsages(node HasSelectionSet) []*VariableUsage { + if usages, ok := ctx.variableUsages[node]; ok && usages != nil { + return usages + } + usages := []*VariableUsage{} + typeInfo := NewTypeInfo(&TypeInfoConfig{ + Schema: ctx.schema, + }) + + visitor.Visit(node, visitor.VisitWithTypeInfo(typeInfo, &visitor.VisitorOptions{ + KindFuncMap: map[string]visitor.NamedVisitFuncs{ + kinds.VariableDefinition: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + return visitor.ActionSkip, nil + }, + }, + kinds.Variable: { + Kind: func(p visitor.VisitFuncParams) (string, interface{}) { + if node, ok := p.Node.(*ast.Variable); ok && node != nil { + usages = append(usages, &VariableUsage{ + Node: node, + Type: typeInfo.InputType(), + }) + } + return visitor.ActionNoChange, nil + }, + }, + }, + }), nil) + + ctx.variableUsages[node] = usages + return usages +} +func (ctx *ValidationContext) RecursiveVariableUsages(operation *ast.OperationDefinition) []*VariableUsage { + if usages, ok := ctx.recursiveVariableUsages[operation]; ok && usages != nil { + return usages + } + usages := ctx.VariableUsages(operation) + + fragments := ctx.RecursivelyReferencedFragments(operation) + for _, fragment := range fragments { + fragmentUsages := ctx.VariableUsages(fragment) + usages = append(usages, fragmentUsages...) + } + + ctx.recursiveVariableUsages[operation] = usages + return usages +} +func (ctx *ValidationContext) Type() Output { + return ctx.typeInfo.Type() +} +func (ctx *ValidationContext) ParentType() Composite { + return ctx.typeInfo.ParentType() +} +func (ctx *ValidationContext) InputType() Input { + return ctx.typeInfo.InputType() +} +func (ctx *ValidationContext) FieldDef() *FieldDefinition { + return ctx.typeInfo.FieldDef() +} +func (ctx *ValidationContext) Directive() *Directive { + return ctx.typeInfo.Directive() +} +func (ctx *ValidationContext) Argument() *Argument { + return ctx.typeInfo.Argument() +} diff --git a/vendor/github.com/graphql-go/graphql/values.go b/vendor/github.com/graphql-go/graphql/values.go new file mode 100644 index 00000000..36cba6ec --- /dev/null +++ b/vendor/github.com/graphql-go/graphql/values.go @@ -0,0 +1,476 @@ +package graphql + +import ( + "encoding/json" + "fmt" + "math" + "reflect" + "strings" + + "github.com/graphql-go/graphql/gqlerrors" + "github.com/graphql-go/graphql/language/ast" + "github.com/graphql-go/graphql/language/kinds" + "github.com/graphql-go/graphql/language/printer" + "sort" +) + +// Prepares an object map of variableValues of the correct type based on the +// provided variable definitions and arbitrary input. If the input cannot be +// parsed to match the variable definitions, a GraphQLError will be returned. +func getVariableValues(schema Schema, definitionASTs []*ast.VariableDefinition, inputs map[string]interface{}) (map[string]interface{}, error) { + values := map[string]interface{}{} + for _, defAST := range definitionASTs { + if defAST == nil || defAST.Variable == nil || defAST.Variable.Name == nil { + continue + } + varName := defAST.Variable.Name.Value + varValue, err := getVariableValue(schema, defAST, inputs[varName]) + if err != nil { + return values, err + } + values[varName] = varValue + } + return values, nil +} + +// Prepares an object map of argument values given a list of argument +// definitions and list of argument AST nodes. +func getArgumentValues(argDefs []*Argument, argASTs []*ast.Argument, variableVariables map[string]interface{}) (map[string]interface{}, error) { + + argASTMap := map[string]*ast.Argument{} + for _, argAST := range argASTs { + if argAST.Name != nil { + argASTMap[argAST.Name.Value] = argAST + } + } + results := map[string]interface{}{} + for _, argDef := range argDefs { + + name := argDef.PrivateName + var valueAST ast.Value + if argAST, ok := argASTMap[name]; ok { + valueAST = argAST.Value + } + value := valueFromAST(valueAST, argDef.Type, variableVariables) + if isNullish(value) { + value = argDef.DefaultValue + } + if !isNullish(value) { + results[name] = value + } + } + return results, nil +} + +// Given a variable definition, and any value of input, return a value which +// adheres to the variable definition, or throw an error. +func getVariableValue(schema Schema, definitionAST *ast.VariableDefinition, input interface{}) (interface{}, error) { + ttype, err := typeFromAST(schema, definitionAST.Type) + if err != nil { + return nil, err + } + variable := definitionAST.Variable + + if ttype == nil || !IsInputType(ttype) { + return "", gqlerrors.NewError( + fmt.Sprintf(`Variable "$%v" expected value of type `+ + `"%v" which cannot be used as an input type.`, variable.Name.Value, printer.Print(definitionAST.Type)), + []ast.Node{definitionAST}, + "", + nil, + []int{}, + nil, + ) + } + + isValid, messages := isValidInputValue(input, ttype) + if isValid { + if isNullish(input) { + defaultValue := definitionAST.DefaultValue + if defaultValue != nil { + variables := map[string]interface{}{} + val := valueFromAST(defaultValue, ttype, variables) + return val, nil + } + } + return coerceValue(ttype, input), nil + } + if isNullish(input) { + return "", gqlerrors.NewError( + fmt.Sprintf(`Variable "$%v" of required type `+ + `"%v" was not provided.`, variable.Name.Value, printer.Print(definitionAST.Type)), + []ast.Node{definitionAST}, + "", + nil, + []int{}, + nil, + ) + } + // convert input interface into string for error message + inputStr := "" + b, err := json.Marshal(input) + if err == nil { + inputStr = string(b) + } + messagesStr := "" + if len(messages) > 0 { + messagesStr = "\n" + strings.Join(messages, "\n") + } + + return "", gqlerrors.NewError( + fmt.Sprintf(`Variable "$%v" got invalid value `+ + `%v.%v`, variable.Name.Value, inputStr, messagesStr), + []ast.Node{definitionAST}, + "", + nil, + []int{}, + nil, + ) +} + +// Given a type and any value, return a runtime value coerced to match the type. +func coerceValue(ttype Input, value interface{}) interface{} { + if ttype, ok := ttype.(*NonNull); ok { + return coerceValue(ttype.OfType, value) + } + if isNullish(value) { + return nil + } + if ttype, ok := ttype.(*List); ok { + itemType := ttype.OfType + valType := reflect.ValueOf(value) + if valType.Kind() == reflect.Slice { + values := []interface{}{} + for i := 0; i < valType.Len(); i++ { + val := valType.Index(i).Interface() + v := coerceValue(itemType, val) + values = append(values, v) + } + return values + } + val := coerceValue(itemType, value) + return []interface{}{val} + } + if ttype, ok := ttype.(*InputObject); ok { + + valueMap, ok := value.(map[string]interface{}) + if !ok { + valueMap = map[string]interface{}{} + } + + obj := map[string]interface{}{} + for fieldName, field := range ttype.Fields() { + value, _ := valueMap[fieldName] + fieldValue := coerceValue(field.Type, value) + if isNullish(fieldValue) { + fieldValue = field.DefaultValue + } + if !isNullish(fieldValue) { + obj[fieldName] = fieldValue + } + } + return obj + } + + switch ttype := ttype.(type) { + case *Scalar: + parsed := ttype.ParseValue(value) + if !isNullish(parsed) { + return parsed + } + case *Enum: + parsed := ttype.ParseValue(value) + if !isNullish(parsed) { + return parsed + } + } + return nil +} + +// graphql-js/src/utilities.js` +// TODO: figure out where to organize utils +// TODO: change to *Schema +func typeFromAST(schema Schema, inputTypeAST ast.Type) (Type, error) { + switch inputTypeAST := inputTypeAST.(type) { + case *ast.List: + innerType, err := typeFromAST(schema, inputTypeAST.Type) + if err != nil { + return nil, err + } + return NewList(innerType), nil + case *ast.NonNull: + innerType, err := typeFromAST(schema, inputTypeAST.Type) + if err != nil { + return nil, err + } + return NewNonNull(innerType), nil + case *ast.Named: + nameValue := "" + if inputTypeAST.Name != nil { + nameValue = inputTypeAST.Name.Value + } + ttype := schema.Type(nameValue) + return ttype, nil + default: + return nil, invariant(inputTypeAST.GetKind() == kinds.Named, "Must be a named type.") + } +} + +// isValidInputValue alias isValidJSValue +// Given a value and a GraphQL type, determine if the value will be +// accepted for that type. This is primarily useful for validating the +// runtime values of query variables. +func isValidInputValue(value interface{}, ttype Input) (bool, []string) { + if ttype, ok := ttype.(*NonNull); ok { + if isNullish(value) { + if ttype.OfType.Name() != "" { + return false, []string{fmt.Sprintf(`Expected "%v!", found null.`, ttype.OfType.Name())} + } + return false, []string{"Expected non-null value, found null."} + } + return isValidInputValue(value, ttype.OfType) + } + + if isNullish(value) { + return true, nil + } + + switch ttype := ttype.(type) { + case *List: + itemType := ttype.OfType + valType := reflect.ValueOf(value) + if valType.Kind() == reflect.Ptr { + valType = valType.Elem() + } + if valType.Kind() == reflect.Slice { + messagesReduce := []string{} + for i := 0; i < valType.Len(); i++ { + val := valType.Index(i).Interface() + _, messages := isValidInputValue(val, itemType) + for idx, message := range messages { + messagesReduce = append(messagesReduce, fmt.Sprintf(`In element #%v: %v`, idx+1, message)) + } + } + return (len(messagesReduce) == 0), messagesReduce + } + return isValidInputValue(value, itemType) + + case *InputObject: + messagesReduce := []string{} + + valueMap, ok := value.(map[string]interface{}) + if !ok { + return false, []string{fmt.Sprintf(`Expected "%v", found not an object.`, ttype.Name())} + } + fields := ttype.Fields() + + // to ensure stable order of field evaluation + fieldNames := []string{} + valueMapFieldNames := []string{} + + for fieldName := range fields { + fieldNames = append(fieldNames, fieldName) + } + sort.Strings(fieldNames) + + for fieldName := range valueMap { + valueMapFieldNames = append(valueMapFieldNames, fieldName) + } + sort.Strings(valueMapFieldNames) + + // Ensure every provided field is defined. + for _, fieldName := range valueMapFieldNames { + if _, ok := fields[fieldName]; !ok { + messagesReduce = append(messagesReduce, fmt.Sprintf(`In field "%v": Unknown field.`, fieldName)) + } + } + + // Ensure every defined field is valid. + for _, fieldName := range fieldNames { + _, messages := isValidInputValue(valueMap[fieldName], fields[fieldName].Type) + if messages != nil { + for _, message := range messages { + messagesReduce = append(messagesReduce, fmt.Sprintf(`In field "%v": %v`, fieldName, message)) + } + } + } + return (len(messagesReduce) == 0), messagesReduce + } + + switch ttype := ttype.(type) { + case *Scalar: + parsedVal := ttype.ParseValue(value) + if isNullish(parsedVal) { + return false, []string{fmt.Sprintf(`Expected type "%v", found "%v".`, ttype.Name(), value)} + } + return true, nil + + case *Enum: + parsedVal := ttype.ParseValue(value) + if isNullish(parsedVal) { + return false, []string{fmt.Sprintf(`Expected type "%v", found "%v".`, ttype.Name(), value)} + } + return true, nil + } + return true, nil +} + +// Returns true if a value is null, undefined, or NaN. +func isNullish(value interface{}) bool { + if value, ok := value.(*string); ok { + if value == nil { + return true + } + return *value == "" + } + if value, ok := value.(int); ok { + return math.IsNaN(float64(value)) + } + if value, ok := value.(*int); ok { + if value == nil { + return true + } + return math.IsNaN(float64(*value)) + } + if value, ok := value.(float32); ok { + return math.IsNaN(float64(value)) + } + if value, ok := value.(*float32); ok { + if value == nil { + return true + } + return math.IsNaN(float64(*value)) + } + if value, ok := value.(float64); ok { + return math.IsNaN(value) + } + if value, ok := value.(*float64); ok { + if value == nil { + return true + } + return math.IsNaN(*value) + } + return value == nil +} + +/** + * Produces a value given a GraphQL Value AST. + * + * A GraphQL type must be provided, which will be used to interpret different + * GraphQL Value literals. + * + * | GraphQL Value | JSON Value | + * | -------------------- | ------------- | + * | Input Object | Object | + * | List | Array | + * | Boolean | Boolean | + * | String / Enum Value | String | + * | Int / Float | Number | + * + */ +func valueFromAST(valueAST ast.Value, ttype Input, variables map[string]interface{}) interface{} { + + if ttype, ok := ttype.(*NonNull); ok { + val := valueFromAST(valueAST, ttype.OfType, variables) + return val + } + + if valueAST == nil { + return nil + } + + if valueAST, ok := valueAST.(*ast.Variable); ok && valueAST.Kind == kinds.Variable { + if valueAST.Name == nil { + return nil + } + if variables == nil { + return nil + } + variableName := valueAST.Name.Value + variableVal, ok := variables[variableName] + if !ok { + return nil + } + // Note: we're not doing any checking that this variable is correct. We're + // assuming that this query has been validated and the variable usage here + // is of the correct type. + return variableVal + } + + if ttype, ok := ttype.(*List); ok { + itemType := ttype.OfType + if valueAST, ok := valueAST.(*ast.ListValue); ok && valueAST.Kind == kinds.ListValue { + values := []interface{}{} + for _, itemAST := range valueAST.Values { + v := valueFromAST(itemAST, itemType, variables) + values = append(values, v) + } + return values + } + v := valueFromAST(valueAST, itemType, variables) + return []interface{}{v} + } + + if ttype, ok := ttype.(*InputObject); ok { + valueAST, ok := valueAST.(*ast.ObjectValue) + if !ok { + return nil + } + fieldASTs := map[string]*ast.ObjectField{} + for _, fieldAST := range valueAST.Fields { + if fieldAST.Name == nil { + continue + } + fieldName := fieldAST.Name.Value + fieldASTs[fieldName] = fieldAST + + } + obj := map[string]interface{}{} + for fieldName, field := range ttype.Fields() { + fieldAST, ok := fieldASTs[fieldName] + fieldValue := field.DefaultValue + if !ok || fieldAST == nil { + if fieldValue == nil { + continue + } + } else { + fieldValue = valueFromAST(fieldAST.Value, field.Type, variables) + } + if isNullish(fieldValue) { + fieldValue = field.DefaultValue + } + if !isNullish(fieldValue) { + obj[fieldName] = fieldValue + } + } + return obj + } + + switch ttype := ttype.(type) { + case *Scalar: + parsed := ttype.ParseLiteral(valueAST) + if !isNullish(parsed) { + return parsed + } + case *Enum: + parsed := ttype.ParseLiteral(valueAST) + if !isNullish(parsed) { + return parsed + } + } + return nil +} + +func invariant(condition bool, message string) error { + if !condition { + return gqlerrors.NewFormattedError(message) + } + return nil +} + +func invariantf(condition bool, format string, a ...interface{}) error { + if !condition { + return gqlerrors.NewFormattedError(fmt.Sprintf(format, a...)) + } + return nil +} diff --git a/vendor/github.com/graphql-go/handler/.gitignore b/vendor/github.com/graphql-go/handler/.gitignore new file mode 100644 index 00000000..3a709f36 --- /dev/null +++ b/vendor/github.com/graphql-go/handler/.gitignore @@ -0,0 +1,25 @@ +# 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 +*.prof +*.swp diff --git a/vendor/github.com/graphql-go/handler/.travis.yml b/vendor/github.com/graphql-go/handler/.travis.yml new file mode 100644 index 00000000..3fee687b --- /dev/null +++ b/vendor/github.com/graphql-go/handler/.travis.yml @@ -0,0 +1,12 @@ +language: go + +go: + - 1.x + - tip + +before_install: + - go get github.com/axw/gocov/gocov + - go get github.com/mattn/goveralls + +script: + - $HOME/gopath/bin/goveralls -service=travis-ci diff --git a/vendor/github.com/graphql-go/handler/LICENSE b/vendor/github.com/graphql-go/handler/LICENSE new file mode 100644 index 00000000..4bcca848 --- /dev/null +++ b/vendor/github.com/graphql-go/handler/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hafiz Ismail + +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/graphql-go/handler/README.md b/vendor/github.com/graphql-go/handler/README.md new file mode 100644 index 00000000..a6986c5e --- /dev/null +++ b/vendor/github.com/graphql-go/handler/README.md @@ -0,0 +1,79 @@ +# graphql-go-handler [![Build Status](https://travis-ci.org/graphql-go/handler.svg)](https://travis-ci.org/graphql-go/handler) [![GoDoc](https://godoc.org/graphql-go/handler?status.svg)](https://godoc.org/github.com/graphql-go/handler) [![Coverage Status](https://coveralls.io/repos/graphql-go/handler/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-go/handler?branch=master) [![Join the chat at https://gitter.im/graphql-go/graphql](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/graphql-go/graphql?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +Golang HTTP.Handler for [graphl-go](https://github.com/graphql-go/graphql) + +### Notes: +This is based on alpha version of `graphql-go` and `graphql-relay-go`. +Be sure to watch both repositories for latest changes. + +### Usage + +```go +package main + +import ( + "net/http" + "github.com/graphql-go/handler" +) + +func main() { + + // define GraphQL schema using relay library helpers + schema := graphql.NewSchema(...) + + h := handler.New(&handler.Config{ + Schema: &schema, + Pretty: true, + GraphiQL: true, + }) + + // serve HTTP + http.Handle("/graphql", h) + http.ListenAndServe(":8080", nil) +} +``` + +### Details + +The handler will accept requests with +the parameters: + + * **`query`**: A string GraphQL document to be executed. + + * **`variables`**: The runtime values to use for any GraphQL query variables + as a JSON object. + + * **`operationName`**: If the provided `query` contains multiple named + operations, this specifies which operation should be executed. If not + provided, an 400 error will be returned if the `query` contains multiple + named operations. + +GraphQL will first look for each parameter in the URL's query-string: + +``` +/graphql?query=query+getUser($id:ID){user(id:$id){name}}&variables={"id":"4"} +``` + +If not found in the query-string, it will look in the POST request body. +The `handler` will interpret it +depending on the provided `Content-Type` header. + + * **`application/json`**: the POST body will be parsed as a JSON + object of parameters. + + * **`application/x-www-form-urlencoded`**: this POST body will be + parsed as a url-encoded string of key-value pairs. + + * **`application/graphql`**: The POST body will be parsed as GraphQL + query string, which provides the `query` parameter. + + +### Examples +- [golang-graphql-playground](https://github.com/graphql-go/playground) +- [golang-relay-starter-kit](https://github.com/sogko/golang-relay-starter-kit) +- [todomvc-relay-go](https://github.com/sogko/todomvc-relay-go) + +### Test +```bash +$ go get github.com/graphql-go/handler +$ go build && go test ./... diff --git a/vendor/github.com/graphql-go/handler/graphiql.go b/vendor/github.com/graphql-go/handler/graphiql.go new file mode 100644 index 00000000..ace949b4 --- /dev/null +++ b/vendor/github.com/graphql-go/handler/graphiql.go @@ -0,0 +1,199 @@ +package handler + +import ( + "encoding/json" + "html/template" + "net/http" + + "github.com/graphql-go/graphql" +) + +// page is the page data structure of the rendered GraphiQL page +type graphiqlPage struct { + GraphiqlVersion string + QueryString string + ResultString string + VariablesString string + OperationName string +} + +// renderGraphiQL renders the GraphiQL GUI +func renderGraphiQL(w http.ResponseWriter, params graphql.Params) { + t := template.New("GraphiQL") + t, err := t.Parse(graphiqlTemplate) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Create variables string + vars, err := json.MarshalIndent(params.VariableValues, "", " ") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + varsString := string(vars) + if varsString == "null" { + varsString = "" + } + + // Create result string + var resString string + if params.RequestString == "" { + resString = "" + } else { + result, err := json.MarshalIndent(graphql.Do(params), "", " ") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + resString = string(result) + } + + p := graphiqlPage{ + GraphiqlVersion: graphiqlVersion, + QueryString: params.RequestString, + ResultString: resString, + VariablesString: varsString, + OperationName: params.OperationName, + } + + err = t.ExecuteTemplate(w, "index", p) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + return +} + +// graphiqlVersion is the current version of GraphiQL +const graphiqlVersion = "0.11.3" + +// tmpl is the page template to render GraphiQL +const graphiqlTemplate = ` +{{ define "index" }} +<!-- +The request to this GraphQL server provided the header "Accept: text/html" +and as a result has been presented GraphiQL - an in-browser IDE for +exploring GraphQL. + +If you wish to receive JSON, provide the header "Accept: application/json" or +add "&raw" to the end of the URL within a browser. +--> +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <title>GraphiQL</title> + <meta name="robots" content="noindex" /> + <style> + html, body { + height: 100%; + margin: 0; + overflow: hidden; + width: 100%; + } + </style> + <link href="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.css" rel="stylesheet" /> + <script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script> + <script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script> + <script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script> + <script src="//cdn.jsdelivr.net/npm/graphiql@{{ .GraphiqlVersion }}/graphiql.min.js"></script> +</head> +<body> + <script> + // Collect the URL parameters + var parameters = {}; + window.location.search.substr(1).split('&').forEach(function (entry) { + var eq = entry.indexOf('='); + if (eq >= 0) { + parameters[decodeURIComponent(entry.slice(0, eq))] = + decodeURIComponent(entry.slice(eq + 1)); + } + }); + + // Produce a Location query string from a parameter object. + function locationQuery(params) { + return '?' + Object.keys(params).filter(function (key) { + return Boolean(params[key]); + }).map(function (key) { + return encodeURIComponent(key) + '=' + + encodeURIComponent(params[key]); + }).join('&'); + } + + // Derive a fetch URL from the current URL, sans the GraphQL parameters. + var graphqlParamNames = { + query: true, + variables: true, + operationName: true + }; + + var otherParams = {}; + for (var k in parameters) { + if (parameters.hasOwnProperty(k) && graphqlParamNames[k] !== true) { + otherParams[k] = parameters[k]; + } + } + var fetchURL = locationQuery(otherParams); + + // Defines a GraphQL fetcher using the fetch API. + function graphQLFetcher(graphQLParams) { + return fetch(fetchURL, { + method: 'post', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(graphQLParams), + credentials: 'include', + }).then(function (response) { + return response.text(); + }).then(function (responseBody) { + try { + return JSON.parse(responseBody); + } catch (error) { + return responseBody; + } + }); + } + + // When the query and variables string is edited, update the URL bar so + // that it can be easily shared. + function onEditQuery(newQuery) { + parameters.query = newQuery; + updateURL(); + } + + function onEditVariables(newVariables) { + parameters.variables = newVariables; + updateURL(); + } + + function onEditOperationName(newOperationName) { + parameters.operationName = newOperationName; + updateURL(); + } + + function updateURL() { + history.replaceState(null, null, locationQuery(parameters)); + } + + // Render <GraphiQL /> into the body. + ReactDOM.render( + React.createElement(GraphiQL, { + fetcher: graphQLFetcher, + onEditQuery: onEditQuery, + onEditVariables: onEditVariables, + onEditOperationName: onEditOperationName, + query: {{ .QueryString }}, + response: {{ .ResultString }}, + variables: {{ .VariablesString }}, + operationName: {{ .OperationName }}, + }), + document.body + ); + </script> +</body> +</html> +{{ end }} +` diff --git a/vendor/github.com/graphql-go/handler/handler.go b/vendor/github.com/graphql-go/handler/handler.go new file mode 100644 index 00000000..cfcb1ca9 --- /dev/null +++ b/vendor/github.com/graphql-go/handler/handler.go @@ -0,0 +1,189 @@ +package handler + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "net/url" + "strings" + + "github.com/graphql-go/graphql" + + "context" +) + +const ( + ContentTypeJSON = "application/json" + ContentTypeGraphQL = "application/graphql" + ContentTypeFormURLEncoded = "application/x-www-form-urlencoded" +) + +type Handler struct { + Schema *graphql.Schema + pretty bool + graphiql bool +} +type RequestOptions struct { + Query string `json:"query" url:"query" schema:"query"` + Variables map[string]interface{} `json:"variables" url:"variables" schema:"variables"` + OperationName string `json:"operationName" url:"operationName" schema:"operationName"` +} + +// a workaround for getting`variables` as a JSON string +type requestOptionsCompatibility struct { + Query string `json:"query" url:"query" schema:"query"` + Variables string `json:"variables" url:"variables" schema:"variables"` + OperationName string `json:"operationName" url:"operationName" schema:"operationName"` +} + +func getFromForm(values url.Values) *RequestOptions { + query := values.Get("query") + if query != "" { + // get variables map + variables := make(map[string]interface{}, len(values)) + variablesStr := values.Get("variables") + json.Unmarshal([]byte(variablesStr), &variables) + + return &RequestOptions{ + Query: query, + Variables: variables, + OperationName: values.Get("operationName"), + } + } + + return nil +} + +// RequestOptions Parses a http.Request into GraphQL request options struct +func NewRequestOptions(r *http.Request) *RequestOptions { + if reqOpt := getFromForm(r.URL.Query()); reqOpt != nil { + return reqOpt + } + + if r.Method != "POST" { + return &RequestOptions{} + } + + if r.Body == nil { + return &RequestOptions{} + } + + // TODO: improve Content-Type handling + contentTypeStr := r.Header.Get("Content-Type") + contentTypeTokens := strings.Split(contentTypeStr, ";") + contentType := contentTypeTokens[0] + + switch contentType { + case ContentTypeGraphQL: + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return &RequestOptions{} + } + return &RequestOptions{ + Query: string(body), + } + case ContentTypeFormURLEncoded: + if err := r.ParseForm(); err != nil { + return &RequestOptions{} + } + + if reqOpt := getFromForm(r.PostForm); reqOpt != nil { + return reqOpt + } + + return &RequestOptions{} + + case ContentTypeJSON: + fallthrough + default: + var opts RequestOptions + body, err := ioutil.ReadAll(r.Body) + if err != nil { + return &opts + } + err = json.Unmarshal(body, &opts) + if err != nil { + // Probably `variables` was sent as a string instead of an object. + // So, we try to be polite and try to parse that as a JSON string + var optsCompatible requestOptionsCompatibility + json.Unmarshal(body, &optsCompatible) + json.Unmarshal([]byte(optsCompatible.Variables), &opts.Variables) + } + return &opts + } +} + +// ContextHandler provides an entrypoint into executing graphQL queries with a +// user-provided context. +func (h *Handler) ContextHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) { + // get query + opts := NewRequestOptions(r) + + // execute graphql query + params := graphql.Params{ + Schema: *h.Schema, + RequestString: opts.Query, + VariableValues: opts.Variables, + OperationName: opts.OperationName, + Context: ctx, + } + result := graphql.Do(params) + + if h.graphiql { + acceptHeader := r.Header.Get("Accept") + _, raw := r.URL.Query()["raw"] + if !raw && !strings.Contains(acceptHeader, "application/json") && strings.Contains(acceptHeader, "text/html") { + renderGraphiQL(w, params) + return + } + } + + // use proper JSON Header + w.Header().Add("Content-Type", "application/json; charset=utf-8") + + if h.pretty { + w.WriteHeader(http.StatusOK) + buff, _ := json.MarshalIndent(result, "", "\t") + + w.Write(buff) + } else { + w.WriteHeader(http.StatusOK) + buff, _ := json.Marshal(result) + + w.Write(buff) + } +} + +// ServeHTTP provides an entrypoint into executing graphQL queries. +func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.ContextHandler(r.Context(), w, r) +} + +type Config struct { + Schema *graphql.Schema + Pretty bool + GraphiQL bool +} + +func NewConfig() *Config { + return &Config{ + Schema: nil, + Pretty: true, + GraphiQL: true, + } +} + +func New(p *Config) *Handler { + if p == nil { + p = NewConfig() + } + if p.Schema == nil { + panic("undefined GraphQL schema") + } + + return &Handler{ + Schema: p.Schema, + pretty: p.Pretty, + graphiql: p.GraphiQL, + } +} |