Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
chris-ramon committed Jul 17, 2018
2 parents 1368492 + 4e9599f commit f147048
Show file tree
Hide file tree
Showing 10 changed files with 1,125 additions and 16 deletions.
4 changes: 0 additions & 4 deletions abstract_test.go
Expand Up @@ -158,7 +158,6 @@ func TestIsTypeOfUsedToResolveRuntimeTypeForInterface(t *testing.T) {
}
}


func TestAppendTypeUsedToAddRuntimeCustomScalarTypeForInterface(t *testing.T) {

petType := graphql.NewInterface(graphql.InterfaceConfig{
Expand Down Expand Up @@ -247,7 +246,6 @@ func TestAppendTypeUsedToAddRuntimeCustomScalarTypeForInterface(t *testing.T) {
},
},
}),

})
if err != nil {
t.Fatalf("Error in schema %v", err.Error())
Expand Down Expand Up @@ -297,8 +295,6 @@ func TestAppendTypeUsedToAddRuntimeCustomScalarTypeForInterface(t *testing.T) {
}
}



func TestIsTypeOfUsedToResolveRuntimeTypeForUnion(t *testing.T) {

dogType := graphql.NewObject(graphql.ObjectConfig{
Expand Down
141 changes: 141 additions & 0 deletions examples/custom-scalar-type/main.go
@@ -0,0 +1,141 @@
package main

import (
"encoding/json"
"fmt"
"log"

"github.com/graphql-go/graphql"
"github.com/graphql-go/graphql/language/ast"
)

type CustomID struct {
value string
}

func (id *CustomID) String() string {
return id.value
}

func NewCustomID(v string) *CustomID {
return &CustomID{value: v}
}

var CustomScalarType = graphql.NewScalar(graphql.ScalarConfig{
Name: "CustomScalarType",
Description: "The `CustomScalarType` scalar type represents an ID Object.",
// Serialize serializes `CustomID` to string.
Serialize: func(value interface{}) interface{} {
switch value := value.(type) {
case CustomID:
return value.String()
case *CustomID:
v := *value
return v.String()
default:
return nil
}
},
// ParseValue parses GraphQL variables from `string` to `CustomID`.
ParseValue: func(value interface{}) interface{} {
switch value := value.(type) {
case string:
return NewCustomID(value)
case *string:
return NewCustomID(*value)
default:
return nil
}
},
// ParseLiteral parses GraphQL AST value to `CustomID`.
ParseLiteral: func(valueAST ast.Value) interface{} {
switch valueAST := valueAST.(type) {
case *ast.StringValue:
return NewCustomID(valueAST.Value)
default:
return nil
}
},
})

type Customer struct {
ID *CustomID `json:"id"`
}

var CustomerType = graphql.NewObject(graphql.ObjectConfig{
Name: "Customer",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: CustomScalarType,
},
},
})

func main() {
schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"customers": &graphql.Field{
Type: graphql.NewList(CustomerType),
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: CustomScalarType,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
// id := p.Args["id"]
// log.Printf("id from arguments: %+v", id)
customers := []Customer{
Customer{ID: NewCustomID("fb278f2a4a13f")},
}
return customers, nil
},
},
},
}),
})
if err != nil {
log.Fatal(err)
}
query := `
query {
customers {
id
}
}
`
/*
queryWithVariable := `
query($id: CustomScalarType) {
customers(id: $id) {
id
}
}
`
*/
/*
queryWithArgument := `
query {
customers(id: "5b42ba57289") {
id
}
}
`
*/
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
VariableValues: map[string]interface{}{
"id": "5b42ba57289",
},
})
if len(result.Errors) > 0 {
log.Fatal(result)
}
b, err := json.Marshal(result)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
}
75 changes: 75 additions & 0 deletions examples/modify-context/main.go
@@ -0,0 +1,75 @@
package main

import (
"context"
"encoding/json"
"fmt"
"log"

"github.com/graphql-go/graphql"
)

type User struct {
ID int `json:"id"`
}

var UserType = graphql.NewObject(graphql.ObjectConfig{
Name: "User",
Fields: graphql.Fields{
"id": &graphql.Field{
Type: graphql.Int,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
rootValue := p.Info.RootValue.(map[string]interface{})
if rootValue["data-from-parent"] == "ok" &&
rootValue["data-before-execution"] == "ok" {
user := p.Source.(User)
return user.ID, nil
}
return nil, nil
},
},
},
})

func main() {
schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"users": &graphql.Field{
Type: graphql.NewList(UserType),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
rootValue := p.Info.RootValue.(map[string]interface{})
rootValue["data-from-parent"] = "ok"
result := []User{
User{ID: 1},
}
return result, nil

},
},
},
}),
})
if err != nil {
log.Fatal(err)
}
ctx := context.WithValue(context.Background(), "currentUser", User{ID: 100})
// Instead of trying to modify context within a resolve function, use:
// `graphql.Params.RootObject` is a mutable optional variable and available on
// each resolve function via: `graphql.ResolveParams.Info.RootValue`.
rootObject := map[string]interface{}{
"data-before-execution": "ok",
}
result := graphql.Do(graphql.Params{
Context: ctx,
RequestString: "{ users { id } }",
RootObject: rootObject,
Schema: schema,
})
b, err := json.Marshal(result)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", string(b)) // {"data":{"users":[{"id":1}]}}
}
12 changes: 7 additions & 5 deletions executor.go
Expand Up @@ -567,12 +567,14 @@ func completeValueCatchingError(eCtx *executionContext, returnType Type, fieldAS
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 {
for resultVal.IsValid() && resultVal.Type().Kind() == reflect.Func {
if propertyFn, ok := result.(func() interface{}); ok {
return propertyFn()
result = propertyFn()
resultVal = reflect.ValueOf(result)
} else {
err := gqlerrors.NewFormattedError("Error resolving func. Expected `func() interface{}` signature")
panic(gqlerrors.FormatError(err))
}
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
Expand Down Expand Up @@ -737,7 +739,7 @@ func completeListValue(eCtx *executionContext, returnType *List, fieldASTs []*as
parentTypeName = info.ParentType.Name()
}
err := invariantf(
resultVal.IsValid() && resultVal.Type().Kind() == reflect.Slice,
resultVal.IsValid() && isIterable(result),
"User Error: expected iterable, but did not find one "+
"for field %v.%v.", parentTypeName, info.FieldName)

Expand Down
87 changes: 87 additions & 0 deletions executor_test.go
Expand Up @@ -1807,3 +1807,90 @@ func TestContextDeadline(t *testing.T) {
t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedErrors, result.Errors))
}
}

func TestThunkResultsProcessedCorrectly(t *testing.T) {
barType := graphql.NewObject(graphql.ObjectConfig{
Name: "Bar",
Fields: graphql.Fields{
"bazA": &graphql.Field{
Type: graphql.String,
},
"bazB": &graphql.Field{
Type: graphql.String,
},
},
})

fooType := graphql.NewObject(graphql.ObjectConfig{
Name: "Foo",
Fields: graphql.Fields{
"bar": &graphql.Field{
Type: barType,
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
var bar struct {
BazA string
BazB string
}
bar.BazA = "A"
bar.BazB = "B"

thunk := func() interface{} { return &bar }
return thunk, nil
},
},
},
})

queryType := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"foo": &graphql.Field{
Type: fooType,
Resolve: func(params graphql.ResolveParams) (interface{}, error) {
var foo struct{}
return foo, nil
},
},
},
})

expectNoError := func(err error) {
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
}

schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: queryType,
})
expectNoError(err)

query := "{ foo { bar { bazA bazB } } }"
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
})
if len(result.Errors) != 0 {
t.Fatalf("expected no errors, got %v", result.Errors)
}

foo := result.Data.(map[string]interface{})["foo"].(map[string]interface{})
bar, ok := foo["bar"].(map[string]interface{})

if !ok {
t.Errorf("expected bar to be a map[string]interface{}: actual = %v", reflect.TypeOf(foo["bar"]))
} else {
if got, want := bar["bazA"], "A"; got != want {
t.Errorf("foo.bar.bazA: got=%v, want=%v", got, want)
}
if got, want := bar["bazB"], "B"; got != want {
t.Errorf("foo.bar.bazB: got=%v, want=%v", got, want)
}
}

if t.Failed() {
b, err := json.Marshal(result.Data)
expectNoError(err)
t.Log(string(b))
}
}

0 comments on commit f147048

Please sign in to comment.