Skip to content

Commit

Permalink
added support for variables
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed Oct 14, 2016
1 parent 78065ec commit 89b0665
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 46 deletions.
51 changes: 38 additions & 13 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package graphql

import (
"encoding/json"
"fmt"
"reflect"
"strings"

Expand All @@ -14,7 +15,12 @@ type Schema struct {
resolver reflect.Value
}

func NewSchema(schemaString string, filename string, resolver interface{}) (res *Schema, errRes error) {
type request struct {
*query.Document
Variables map[string]interface{}
}

func NewSchema(schemaString string, filename string, resolver interface{}) (*Schema, error) {
s, err := schema.Parse(schemaString, filename)
if err != nil {
return nil, err
Expand All @@ -27,24 +33,36 @@ func NewSchema(schemaString string, filename string, resolver interface{}) (res
}, nil
}

func (s *Schema) Exec(queryString string) (res []byte, errRes error) {
q, err := query.Parse(queryString)
func (s *Schema) Exec(queryString string, operationName string, variables map[string]interface{}) ([]byte, error) {
d, err := query.Parse(queryString)
if err != nil {
return nil, err
}

rawRes := exec(s, q, s.Types[s.EntryPoints["query"]], q.Root, s.resolver)
if operationName == "" && len(d.Operations) == 1 {
for name := range d.Operations {
operationName = name
}
}

op, ok := d.Operations[operationName]
if !ok {
return nil, fmt.Errorf("no operation with name %q", operationName)
}

r := &request{Document: d, Variables: variables}
rawRes := exec(s, r, s.Types[s.EntryPoints["query"]], op.SelSet, s.resolver)
return json.Marshal(rawRes)
}

func exec(s *Schema, q *query.Query, t schema.Type, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
func exec(s *Schema, r *request, t schema.Type, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
switch t := t.(type) {
case *schema.Scalar:
return resolver.Interface()

case *schema.Object:
result := make(map[string]interface{})
execSelectionSet(s, q, t, selSet, resolver, result)
execSelectionSet(s, r, t, selSet, resolver, result)
return result

case *schema.Enum:
Expand All @@ -53,19 +71,19 @@ func exec(s *Schema, q *query.Query, t schema.Type, selSet *query.SelectionSet,
case *schema.List:
a := make([]interface{}, resolver.Len())
for i := range a {
a[i] = exec(s, q, t.Elem, selSet, resolver.Index(i))
a[i] = exec(s, r, t.Elem, selSet, resolver.Index(i))
}
return a

case *schema.TypeReference:
return exec(s, q, s.Types[t.Name], selSet, resolver)
return exec(s, r, s.Types[t.Name], selSet, resolver)

default:
panic("invalid type")
}
}

func execSelectionSet(s *Schema, q *query.Query, t *schema.Object, selSet *query.SelectionSet, resolver reflect.Value, result map[string]interface{}) {
func execSelectionSet(s *Schema, r *request, t *schema.Object, selSet *query.SelectionSet, resolver reflect.Value, result map[string]interface{}) {
for _, sel := range selSet.Selections {
switch sel := sel.(type) {
case *query.Field:
Expand All @@ -77,17 +95,24 @@ func execSelectionSet(s *Schema, q *query.Query, t *schema.Object, selSet *query
for name, param := range sf.Parameters {
value, ok := sel.Arguments[name]
if !ok {
value = &query.Value{Value: param.Default}
value = &query.Literal{Value: param.Default}
}
rf := args.Elem().FieldByNameFunc(func(n string) bool { return strings.EqualFold(n, name) })
rf.Set(reflect.ValueOf(value.Value))
switch v := value.(type) {
case *query.Variable:
rf.Set(reflect.ValueOf(r.Variables[v.Name]))
case *query.Literal:
rf.Set(reflect.ValueOf(v.Value))
default:
panic("invalid value")
}
}
in = []reflect.Value{args.Elem()}
}
result[sel.Alias] = exec(s, q, sf.Type, sel.SelSet, m.Call(in)[0])
result[sel.Alias] = exec(s, r, sf.Type, sel.SelSet, m.Call(in)[0])

case *query.FragmentSpread:
execSelectionSet(s, q, t, q.Fragments[sel.Name].SelSet, resolver, result)
execSelectionSet(s, r, t, r.Fragments[sel.Name].SelSet, resolver, result)

default:
panic("invalid type")
Expand Down
59 changes: 53 additions & 6 deletions graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,12 @@ func resolveCharacters(ids []string) []characterResolver {
}

var tests = []struct {
name string
schema string
resolver interface{}
query string
result string
name string
schema string
variables map[string]interface{}
resolver interface{}
query string
result string
}{
{
name: "HelloWorld",
Expand Down Expand Up @@ -570,6 +571,52 @@ var tests = []struct {
}
`,
},

{
name: "StarWarsVariables1",
schema: starWarsSchema,
resolver: &starWarsResolver{},
query: `
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
}
}
`,
variables: map[string]interface{}{
"episode": "JEDI",
},
result: `
{
"hero": {
"name": "R2-D2"
}
}
`,
},

{
name: "StarWarsVariables2",
schema: starWarsSchema,
resolver: &starWarsResolver{},
query: `
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
}
}
`,
variables: map[string]interface{}{
"episode": "EMPIRE",
},
result: `
{
"hero": {
"name": "Luke Skywalker"
}
}
`,
},
}

func TestAll(t *testing.T) {
Expand All @@ -580,7 +627,7 @@ func TestAll(t *testing.T) {
t.Fatal(err)
}

got, err := schema.Exec(test.query)
got, err := schema.Exec(test.query, "", test.variables)
if err != nil {
t.Fatal(err)
}
Expand Down
5 changes: 5 additions & 0 deletions internal/lexer/lexer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func (l *Lexer) Peek() rune {

func (l *Lexer) Consume() {
l.next = l.sc.Scan()
if l.next == ',' {
l.Consume()
return
}
if l.next == '#' {
for {
next := l.sc.Next()
Expand All @@ -33,6 +37,7 @@ func (l *Lexer) Consume() {
}
}
l.Consume()
return
}
}

Expand Down
Loading

0 comments on commit 89b0665

Please sign in to comment.