Skip to content

Commit

Permalink
more flexible API for creating a schema
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed Oct 31, 2016
1 parent bd20a16 commit 0a7a37d
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 82 deletions.
44 changes: 33 additions & 11 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,49 @@ import (
"github.com/neelance/graphql-go/internal/schema"
)

type Schema struct {
func ParseSchema(schemaString string, resolver interface{}) (*Schema, error) {
b, err := New().Parse(schemaString)
if err != nil {
return nil, err
}
return b.ApplyResolver(resolver)
}

type SchemaBuilder struct {
schema *schema.Schema
exec *exec.Exec
}

func ParseSchema(schemaString string, resolver interface{}) (*Schema, error) {
s, err := schema.Parse(schemaString)
if err != nil {
func New() *SchemaBuilder {
s := schema.New()
exec.AddBuiltinScalars(s)
return &SchemaBuilder{
schema: s,
}
}

func (b *SchemaBuilder) Parse(schemaString string) (*SchemaBuilder, error) {
if err := b.schema.Parse(schemaString); err != nil {
return nil, err
}
return b, nil
}

e, err2 := exec.Make(s, resolver)
func (b *SchemaBuilder) ApplyResolver(resolver interface{}) (*Schema, error) {
e, err2 := exec.Make(b.schema, resolver)
if err2 != nil {
return nil, err2
}
return &Schema{
schema: s,
schema: b.schema,
exec: e,
}, nil
}

type Schema struct {
schema *schema.Schema
exec *exec.Exec
}

type Response struct {
Data interface{} `json:"data,omitempty"`
Errors []*errors.QueryError `json:"errors,omitempty"`
Expand Down Expand Up @@ -70,13 +92,13 @@ func (s *Schema) Exec(ctx context.Context, queryString string, operationName str
}

func SchemaToJSON(schemaString string) ([]byte, error) {
s, err := schema.Parse(schemaString)
if err != nil {
s := schema.New()
if err := s.Parse(schemaString); err != nil {
return nil, err
}

result, err2 := exec.IntrospectSchema(s)
if err2 != nil {
result, err := exec.IntrospectSchema(s)
if err != nil {
return nil, err
}

Expand Down
32 changes: 7 additions & 25 deletions internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"log"
"math"
"reflect"
"runtime"
"strings"
Expand Down Expand Up @@ -93,14 +92,6 @@ func makeExec(target *iExec, s *schema.Schema, t common.Type, resolverType refle
return nil
}

var scalarTypes = map[string]reflect.Type{
"Int": reflect.TypeOf(int32(0)),
"Float": reflect.TypeOf(float64(0)),
"String": reflect.TypeOf(""),
"Boolean": reflect.TypeOf(true),
"ID": reflect.TypeOf(""),
}

func makeExec2(s *schema.Schema, t common.Type, resolverType reflect.Type, typeRefMap map[typeRefMapKey]*typeRef) (iExec, error) {
var nonNull bool
t, nonNull = unwrapNonNull(t)
Expand All @@ -112,11 +103,11 @@ func makeExec2(s *schema.Schema, t common.Type, resolverType reflect.Type, typeR
}

switch t := t.(type) {
case *schema.Scalar:
case *scalar:
if !nonNull {
resolverType = resolverType.Elem()
}
scalarType := scalarTypes[t.Name]
scalarType := t.reflectType
if resolverType != scalarType {
return nil, fmt.Errorf("expected %s, got %s", scalarType, resolverType)
}
Expand Down Expand Up @@ -291,28 +282,26 @@ func makeInputObjectExec(s *schema.Schema, obj *common.InputMap, typ reflect.Typ
return nil
}
switch ft := ft.(type) {
case *schema.Scalar:
want := scalarTypes[ft.Name]
case *scalar:
want := ft.reflectType
if !nonNull {
want = reflect.PtrTo(want)
}
if err := expectType(sf.Type, want); err != nil {
return nil, err
}
fe.exec = &scalarInputExec{
scalar: ft,
nonNull: nonNull,
}
case *schema.Enum:
want := scalarTypes["String"]
want := reflect.TypeOf("string")
if !nonNull {
want = reflect.PtrTo(want)
}
if err := expectType(sf.Type, want); err != nil {
return nil, err
}
fe.exec = &scalarInputExec{
scalar: &schema.Scalar{Name: "String"},
nonNull: nonNull,
}
case *schema.InputObject:
Expand Down Expand Up @@ -472,14 +461,8 @@ func coerceInputObject(io *common.InputMap, variables map[string]interface{}) (m
func coerceInputValue(iv *common.InputValue, value interface{}) (interface{}, *errors.QueryError) {
t, _ := unwrapNonNull(iv.Type)
switch t := t.(type) {
case *schema.Scalar:
if t.Name == "Int" {
i := value.(int)
if i < math.MinInt32 || i > math.MaxInt32 {
return nil, errors.Errorf("not a 32-bit integer: %d", i)
}
return int32(i), nil
}
case *scalar:
return t.coerceInput(value)
case *schema.InputObject:
return coerceInputObject(&t.InputMap, value.(map[string]interface{}))
}
Expand Down Expand Up @@ -724,7 +707,6 @@ func (e *inputObjectExec) eval(value interface{}) reflect.Value {
}

type scalarInputExec struct {
scalar *schema.Scalar
nonNull bool
}

Expand Down
10 changes: 4 additions & 6 deletions internal/exec/introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,10 @@ var schemaExec iExec
var typeExec iExec

func init() {
{
var err *errors.QueryError
metaSchema, err = schema.Parse(metaSchemaSrc)
if err != nil {
panic(err)
}
metaSchema = schema.New()
AddBuiltinScalars(metaSchema)
if err := metaSchema.Parse(metaSchemaSrc); err != nil {
panic(err)
}

{
Expand Down
66 changes: 66 additions & 0 deletions internal/exec/scalar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package exec

import (
"math"
"reflect"

"github.com/neelance/graphql-go/errors"
"github.com/neelance/graphql-go/internal/schema"
)

type scalar struct {
name string
reflectType reflect.Type
coerceInput func(input interface{}) (interface{}, *errors.QueryError)
}

func (*scalar) Kind() string { return "SCALAR" }
func (t *scalar) TypeName() string { return t.name }

var builtinScalars = []*scalar{
&scalar{
name: "Int",
reflectType: reflect.TypeOf(int32(0)),
coerceInput: func(input interface{}) (interface{}, *errors.QueryError) {
i := input.(int)
if i < math.MinInt32 || i > math.MaxInt32 {
return nil, errors.Errorf("not a 32-bit integer: %d", i)
}
return int32(i), nil
},
},
&scalar{
name: "Float",
reflectType: reflect.TypeOf(float64(0)),
coerceInput: func(input interface{}) (interface{}, *errors.QueryError) {
return input, nil // TODO
},
},
&scalar{
name: "String",
reflectType: reflect.TypeOf(""),
coerceInput: func(input interface{}) (interface{}, *errors.QueryError) {
return input, nil // TODO
},
},
&scalar{
name: "Boolean",
reflectType: reflect.TypeOf(true),
coerceInput: func(input interface{}) (interface{}, *errors.QueryError) {
return input, nil // TODO
},
},
&scalar{
name: "ID",
reflectType: reflect.TypeOf(""),
coerceInput: func(input interface{}) (interface{}, *errors.QueryError) {
return input, nil // TODO
},
},
}

func AddBuiltinScalars(s *schema.Schema) {
for _, scalar := range builtinScalars {
s.Types[scalar.name] = scalar
}
}
Loading

0 comments on commit 0a7a37d

Please sign in to comment.