This commit is contained in:
Michael Muré 2018-07-29 18:58:42 +02:00
parent 6363518c85
commit 8fa0b258ac
No known key found for this signature in database
GPG Key ID: A4457C029293126F
74 changed files with 18 additions and 15058 deletions

26
Gopkg.lock generated
View File

@ -43,30 +43,6 @@
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
name = "github.com/graphql-go/graphql"
packages = [
".",
"gqlerrors",
"language/ast",
"language/kinds",
"language/lexer",
"language/location",
"language/parser",
"language/printer",
"language/source",
"language/typeInfo",
"language/visitor"
]
revision = "1e23489041ba90a66f317fe0deccb236a2fff3cb"
version = "v0.7.5"
[[projects]]
name = "github.com/graphql-go/handler"
packages = ["."]
revision = "f0393b2c10daeebea900292b8e092e73475399d9"
version = "v0.2.1"
[[projects]]
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
@ -160,6 +136,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "c70340117a5b5a1d50ad4e8c20e51b01ff6cbec9e3c49911a066e6fd1115b854"
inputs-digest = "8eef4e36cf2c25bf253e53fb2a2a276a39715a847a6a05bb329870fe39af15a7"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -2,7 +2,7 @@ package commands
import (
"fmt"
"github.com/MichaelMure/git-bug/graphql2"
"github.com/MichaelMure/git-bug/graphql"
"github.com/MichaelMure/git-bug/webui"
"github.com/gorilla/mux"
"github.com/phayes/freeport"
@ -33,7 +33,7 @@ func runWebUI(cmd *cobra.Command, args []string) error {
// Routes
router.Path("/playground").Handler(handler.Playground("git-bug", "/graphql"))
router.Path("/graphql").Handler(graphql2.NewHandler(repo))
router.Path("/graphql").Handler(graphql.NewHandler(repo))
router.PathPrefix("/").Handler(http.FileServer(webui.WebUIAssets))
open.Run(webUiAddr)

View File

@ -1,17 +1,12 @@
schema: schema.graphql
exec:
filename: resolvers/generated_graph.go
filename: resolvers/gen_graph.go
model:
filename: resolvers/generated_model.go
filename: resolvers/gen_model.go
models:
Repository:
fields:
bug:
resolver: true
allBugs:
resolver: true
# model: github.com/MichaelMure/git-bug/graphql2/resolvers.repoResolver
model: github.com/MichaelMure/git-bug/graphql/resolvers.repoResolver
Bug:
model: github.com/MichaelMure/git-bug/bug.Snapshot
Comment:

View File

@ -1,42 +1,18 @@
//go:generate gorunpkg github.com/vektah/gqlgen
package graphql
import (
"context"
"net/http"
"github.com/MichaelMure/git-bug/cache"
"github.com/MichaelMure/git-bug/graphql/resolvers"
"github.com/MichaelMure/git-bug/repository"
graphqlHandler "github.com/graphql-go/handler"
"github.com/vektah/gqlgen/handler"
"net/http"
)
type Handler struct {
handler *graphqlHandler.Handler
cache cache.Cacher
}
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "cache", h.cache)
h.handler.ContextHandler(ctx, w, r)
}
func NewHandler(repo repository.Repo) (*Handler, error) {
schema, err := graphqlSchema()
if err != nil {
return nil, err
}
h := graphqlHandler.New(&graphqlHandler.Config{
Schema: &schema,
Pretty: true,
GraphiQL: true,
})
c := cache.NewCache()
c.RegisterDefaultRepository(repo)
return &Handler{
handler: h,
cache: c,
}, nil
func NewHandler(repo repository.Repo) http.Handler {
backend := resolvers.NewRootResolver()
backend.RegisterDefaultRepository(repo)
return handler.GraphQL(resolvers.NewExecutableSchema(backend))
}

View File

@ -1,4 +1,4 @@
package graphql2
package graphql
import (
"encoding/base64"

View File

@ -1,7 +0,0 @@
package graphql
import "github.com/graphql-go/graphql"
func resolveBug(p graphql.ResolveParams) (interface{}, error) {
return "world", nil
}

View File

@ -1,54 +0,0 @@
package graphql
import (
"github.com/MichaelMure/git-bug/bug"
"github.com/MichaelMure/git-bug/repository"
"github.com/graphql-go/graphql"
)
func graphqlSchema() (graphql.Schema, error) {
fields := graphql.Fields{
"bug": &graphql.Field{
Type: bugType,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: bugIdScalar,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
repo := p.Context.Value("repo").(repository.Repo)
id, _ := p.Args["id"].(string)
b, err := bug.FindLocalBug(repo, id)
if err != nil {
return nil, err
}
snapshot := b.Compile()
return snapshot, nil
},
},
// TODO: provide a relay-like schema with pagination
"allBugs": &graphql.Field{
Type: graphql.NewList(bugType),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
repo := p.Context.Value("repo").(repository.Repo)
var snapshots []bug.Snapshot
for sb := range bug.ReadAllLocalBugs(repo) {
if sb.Err != nil {
return nil, sb.Err
}
snapshots = append(snapshots, sb.Bug.Compile())
}
return snapshots, nil
},
},
}
rootQuery := graphql.ObjectConfig{Name: "RootQuery", Fields: fields}
schemaConfig := graphql.SchemaConfig{Query: graphql.NewObject(rootQuery)}
return graphql.NewSchema(schemaConfig)
}

View File

@ -1,85 +0,0 @@
package graphql
import (
"fmt"
"github.com/graphql-go/graphql"
"github.com/graphql-go/graphql/language/ast"
"github.com/MichaelMure/git-bug/bug"
)
func coerceString(value interface{}) interface{} {
if v, ok := value.(*string); ok {
return *v
}
return fmt.Sprintf("%v", value)
}
var bugIdScalar = graphql.NewScalar(graphql.ScalarConfig{
Name: "BugID",
Description: "TODO",
Serialize: coerceString,
ParseValue: coerceString,
ParseLiteral: func(valueAST ast.Value) interface{} {
switch valueAST := valueAST.(type) {
case *ast.StringValue:
return valueAST.Value
}
return nil
},
})
// Internally, it's the Snapshot
var bugType = graphql.NewObject(graphql.ObjectConfig{
Name: "Bug",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: bugIdScalar,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return p.Source.(bug.Snapshot).Id(), nil
},
},
"status": &graphql.Field{
Type: graphql.String,
},
"title": &graphql.Field{
Type: graphql.String,
},
"comments": &graphql.Field{
Type: graphql.NewList(commentType),
},
"labels": &graphql.Field{
Type: graphql.NewList(graphql.String),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return p.Source.(bug.Snapshot).Labels, nil
},
},
// TODO: operations
},
})
var commentType = graphql.NewObject(graphql.ObjectConfig{
Name: "Comment",
Fields: graphql.Fields{
"author": &graphql.Field{
Type: personType,
},
"message": &graphql.Field{
Type: graphql.String,
},
// TODO: time
},
})
var personType = graphql.NewObject(graphql.ObjectConfig{
Name: "Person",
Fields: graphql.Fields{
"name": &graphql.Field{
Type: graphql.String,
},
"email": &graphql.Field{
Type: graphql.String,
},
},
})

View File

@ -1,18 +0,0 @@
//go:generate gorunpkg github.com/vektah/gqlgen
package graphql2
import (
"github.com/MichaelMure/git-bug/graphql2/resolvers"
"github.com/MichaelMure/git-bug/repository"
"github.com/vektah/gqlgen/handler"
"net/http"
)
func NewHandler(repo repository.Repo) http.Handler {
backend := resolvers.NewRootResolver()
backend.RegisterDefaultRepository(repo)
return handler.GraphQL(resolvers.NewExecutableSchema(backend))
}

View File

@ -1,2 +0,0 @@
.DS_Store
.idea

View File

@ -1,20 +0,0 @@
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 ./...

View File

@ -1,139 +0,0 @@
# 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")

View File

@ -1,21 +0,0 @@
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.

View File

@ -1,75 +0,0 @@
# 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/)

File diff suppressed because it is too large Load Diff

View File

@ -1,156 +0,0 @@
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,
},
})

View File

@ -1,921 +0,0 @@
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 fields 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]
}

View File

@ -1,68 +0,0 @@
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,
}
}

View File

@ -1,51 +0,0 @@
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
}

View File

@ -1,39 +0,0 @@
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
}

View File

@ -1,30 +0,0 @@
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
}

View File

@ -1,69 +0,0 @@
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
}

View File

@ -1,63 +0,0 @@
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,
})
}

View File

@ -1,763 +0,0 @@
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),
})
}

View File

@ -1,52 +0,0 @@
# 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
}

View File

@ -1,33 +0,0 @@
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
}

View File

@ -1,247 +0,0 @@
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
}

View File

@ -1,33 +0,0 @@
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
}

View File

@ -1,31 +0,0 @@
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
}

View File

@ -1,22 +0,0 @@
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,
}
}

View File

@ -1,31 +0,0 @@
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
}

View File

@ -1,45 +0,0 @@
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)

View File

@ -1,144 +0,0 @@
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
}

View File

@ -1,529 +0,0 @@
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
}

View File

@ -1,106 +0,0 @@
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()
}

View File

@ -1,305 +0,0 @@
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
}

View File

@ -1,59 +0,0 @@
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"
)

View File

@ -1,656 +0,0 @@
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]
}

View File

@ -1,35 +0,0 @@
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}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,821 +0,0 @@
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
}

View File

@ -1,20 +0,0 @@
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
}

View File

@ -1,11 +0,0 @@
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)
}

View File

@ -1,873 +0,0 @@
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
}

View File

@ -1,37 +0,0 @@
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
}

File diff suppressed because it is too large Load Diff

View File

@ -1,706 +0,0 @@
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
}

View File

@ -1,329 +0,0 @@
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
},
})

View File

@ -1,70 +0,0 @@
# 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

View File

@ -1,566 +0,0 @@
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
}

View File

@ -1,276 +0,0 @@
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
}

View File

@ -1,16 +0,0 @@
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
}

View File

@ -1,180 +0,0 @@
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
}

View File

@ -1,288 +0,0 @@
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()
}

View File

@ -1,476 +0,0 @@
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
}

View File

@ -1,25 +0,0 @@
# 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

View File

@ -1,12 +0,0 @@
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

View File

@ -1,22 +0,0 @@
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.

View File

@ -1,79 +0,0 @@
# 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 ./...

View File

@ -1,199 +0,0 @@
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 }}
`

View File

@ -1,189 +0,0 @@
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,
}
}