Skip to content

Commit

Permalink
refactor: schema types for interface and input
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed Oct 19, 2016
1 parent 0c8c943 commit e2c58f2
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 75 deletions.
62 changes: 41 additions & 21 deletions internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ var scalarTypes = map[string]iExec{
var scalarTypeNames = []string{"Int", "Float", "String", "Boolean", "ID"}

func Make(s *schema.Schema, resolver interface{}) (*Exec, error) {
t := s.Types[s.EntryPoints["query"]]
t := s.AllTypes[s.EntryPoints["query"]]
e, err := makeExec(s, t, reflect.TypeOf(resolver), make(map[typeRefMapKey]*typeRefExec))
if err != nil {
return nil, err
Expand Down Expand Up @@ -61,33 +61,27 @@ func (e *Exec) Exec(document *query.Document, variables map[string]interface{},
func makeExec(s *schema.Schema, t schema.Type, resolverType reflect.Type, typeRefMap map[typeRefMapKey]*typeRefExec) (iExec, error) {
switch t := t.(type) {
case *schema.Object:
fields := make(map[string]*fieldExec)
for name, f := range t.Fields {
methodIndex := findMethod(resolverType, name)
if methodIndex == -1 {
return nil, fmt.Errorf("%s does not resolve %q: missing method for field %q", resolverType, t.Name, name)
}
fields, err := makeFieldExecs(s, t.Name, t.Fields, resolverType, typeRefMap)
if err != nil {
return nil, err
}

m := resolverType.Method(methodIndex)
if m.Type.NumOut() != 1 {
return nil, fmt.Errorf("method %q of %s must have exactly one return value", m.Name, resolverType)
}
return &objectExec{
name: t.Name,
fields: fields,
}, nil

ve, err := makeExec(s, f.Type, m.Type.Out(0), typeRefMap)
if err != nil {
return nil, err
}
fields[name] = &fieldExec{
field: f,
methodIndex: methodIndex,
valueExec: ve,
}
case *schema.Interface:
fields, err := makeFieldExecs(s, t.Name, t.Fields, resolverType, typeRefMap)
if err != nil {
return nil, err
}

typeAssertions, err := makeTypeAssertions(s, t.Name, t.ImplementedBy, resolverType, typeRefMap)
if err != nil {
return nil, err
}

return &objectExec{
name: t.Name,
fields: fields,
Expand Down Expand Up @@ -131,6 +125,32 @@ func makeExec(s *schema.Schema, t schema.Type, resolverType reflect.Type, typeRe
}
}

func makeFieldExecs(s *schema.Schema, typeName string, fields map[string]*schema.Field, resolverType reflect.Type, typeRefMap map[typeRefMapKey]*typeRefExec) (map[string]*fieldExec, error) {
fieldExecs := make(map[string]*fieldExec)
for name, f := range fields {
methodIndex := findMethod(resolverType, name)
if methodIndex == -1 {
return nil, fmt.Errorf("%s does not resolve %q: missing method for field %q", resolverType, typeName, name)
}

m := resolverType.Method(methodIndex)
if m.Type.NumOut() != 1 {
return nil, fmt.Errorf("method %q of %s must have exactly one return value", m.Name, resolverType)
}

ve, err := makeExec(s, f.Type, m.Type.Out(0), typeRefMap)
if err != nil {
return nil, err
}
fieldExecs[name] = &fieldExec{
field: f,
methodIndex: methodIndex,
valueExec: ve,
}
}
return fieldExecs, nil
}

func makeTypeAssertions(s *schema.Schema, typeName string, impls []string, resolverType reflect.Type, typeRefMap map[typeRefMapKey]*typeRefExec) (map[string]*typeAssertExec, error) {
typeAssertions := make(map[string]*typeAssertExec)
for _, impl := range impls {
Expand All @@ -151,7 +171,7 @@ func makeTypeAssertions(s *schema.Schema, typeName string, impls []string, resol
}

func resolveType(s *schema.Schema, name string, resolverType reflect.Type, typeRefMap map[typeRefMapKey]*typeRefExec) (*typeRefExec, error) {
refT, ok := s.Types[name]
refT, ok := s.AllTypes[name]
if !ok {
return nil, fmt.Errorf("type %q not found", name)
}
Expand Down
18 changes: 11 additions & 7 deletions internal/exec/introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ func init() {
panic(err)
}

schemaExec, err = makeExec(metaSchema, metaSchema.Types["__Schema"], reflect.TypeOf(&schemaResolver{}), make(map[typeRefMapKey]*typeRefExec))
schemaExec, err = makeExec(metaSchema, metaSchema.AllTypes["__Schema"], reflect.TypeOf(&schemaResolver{}), make(map[typeRefMapKey]*typeRefExec))
if err != nil {
panic(err)
}

typeExec, err = makeExec(metaSchema, metaSchema.Types["__Type"], reflect.TypeOf(&typeResolver{}), make(map[typeRefMapKey]*typeRefExec))
typeExec, err = makeExec(metaSchema, metaSchema.AllTypes["__Type"], reflect.TypeOf(&typeResolver{}), make(map[typeRefMapKey]*typeRefExec))
if err != nil {
panic(err)
}
Expand All @@ -35,7 +35,7 @@ func introspectSchema(r *request, selSet *query.SelectionSet) interface{} {
}

func introspectType(r *request, name string, selSet *query.SelectionSet) interface{} {
t, ok := r.Schema.Types[name]
t, ok := r.Schema.AllTypes[name]
if !ok {
return nil
}
Expand Down Expand Up @@ -133,12 +133,12 @@ func (r *schemaResolver) Types() []*typeResolver {
var l []*typeResolver
addTypes := func(s *schema.Schema) {
var names []string
for name := range s.Types {
for name := range s.AllTypes {
names = append(names, name)
}
sort.Strings(names)
for _, name := range names {
l = append(l, &typeResolver{typ: s.Types[name]})
l = append(l, &typeResolver{typ: s.AllTypes[name]})
}
}
addTypes(r.schema)
Expand All @@ -150,11 +150,11 @@ func (r *schemaResolver) Types() []*typeResolver {
}

func (r *schemaResolver) QueryType() *typeResolver {
return &typeResolver{typ: r.schema.Types[r.schema.EntryPoints["query"]]}
return &typeResolver{typ: r.schema.AllTypes[r.schema.EntryPoints["query"]]}
}

func (r *schemaResolver) MutationType() *typeResolver {
return &typeResolver{typ: r.schema.Types[r.schema.EntryPoints["mutation"]]}
return &typeResolver{typ: r.schema.AllTypes[r.schema.EntryPoints["mutation"]]}
}

func (r *schemaResolver) Directives() []*directiveResolver {
Expand All @@ -177,10 +177,14 @@ func (r *typeResolver) Name() string {
switch t := r.typ.(type) {
case *schema.Object:
return t.Name
case *schema.Interface:
return t.Name
case *schema.Union:
return t.Name
case *schema.Enum:
return t.Name
case *schema.Input:
return t.Name
default:
panic("unreachable")
}
Expand Down
123 changes: 76 additions & 47 deletions internal/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@ import (

type Schema struct {
EntryPoints map[string]string
Types map[string]Type
Interfaces map[string]*Object
AllTypes map[string]Type
Objects map[string]*Object
Interfaces map[string]*Interface
}

type Type interface {
isType()
}

type Object struct {
Name string
Implements string
Fields map[string]*Field
}

type Interface struct {
Name string
Implements string
ImplementedBy []string
Fields map[string]*Field
}
Expand All @@ -36,6 +42,11 @@ type Enum struct {
Values []string
}

type Input struct {
Name string
Fields map[string]*Field
}

type List struct {
Elem Type
}
Expand All @@ -45,8 +56,10 @@ type TypeReference struct {
}

func (Object) isType() {}
func (Interface) isType() {}
func (Union) isType() {}
func (Enum) isType() {}
func (Input) isType() {}
func (List) isType() {}
func (TypeReference) isType() {}

Expand Down Expand Up @@ -81,8 +94,8 @@ func Parse(schemaString string, filename string) (res *Schema, errRes error) {

s := parseSchema(lexer.New(sc))

for _, t := range s.Types {
if obj, ok := t.(*Object); ok && obj.Implements != "" {
for _, obj := range s.Objects {
if obj.Implements != "" {
intf, ok := s.Interfaces[obj.Implements]
if !ok {
return nil, fmt.Errorf("interface %q not found", obj.Implements)
Expand All @@ -97,8 +110,9 @@ func Parse(schemaString string, filename string) (res *Schema, errRes error) {
func parseSchema(l *lexer.Lexer) *Schema {
s := &Schema{
EntryPoints: make(map[string]string),
Types: make(map[string]Type),
Interfaces: make(map[string]*Object),
AllTypes: make(map[string]Type),
Objects: make(map[string]*Object),
Interfaces: make(map[string]*Interface),
}

for l.Peek() != scanner.EOF {
Expand All @@ -113,21 +127,22 @@ func parseSchema(l *lexer.Lexer) *Schema {
}
l.ConsumeToken('}')
case "type":
obj := parseTypeDecl(l)
s.Types[obj.Name] = obj
obj := parseObjectDecl(l)
s.AllTypes[obj.Name] = obj
s.Objects[obj.Name] = obj
case "interface":
obj := parseTypeDecl(l) // TODO
s.Types[obj.Name] = obj
s.Interfaces[obj.Name] = obj
intf := parseInterfaceDecl(l)
s.AllTypes[intf.Name] = intf
s.Interfaces[intf.Name] = intf
case "union":
union := parseUnionDecl(l)
s.Types[union.Name] = union
s.AllTypes[union.Name] = union
case "enum":
enum := parseEnumDecl(l)
s.Types[enum.Name] = enum
s.AllTypes[enum.Name] = enum
case "input":
obj := parseTypeDecl(l) // TODO
s.Types[obj.Name] = obj
input := parseInputDecl(l)
s.AllTypes[input.Name] = input
default:
l.SyntaxError(fmt.Sprintf(`unexpected %q, expecting "schema", "type", "enum", "interface", "union" or "input"`, x))
}
Expand All @@ -136,36 +151,26 @@ func parseSchema(l *lexer.Lexer) *Schema {
return s
}

func parseTypeDecl(l *lexer.Lexer) *Object {
o := &Object{
Fields: make(map[string]*Field),
}

func parseObjectDecl(l *lexer.Lexer) *Object {
o := &Object{}
o.Name = l.ConsumeIdent()
if l.Peek() == scanner.Ident {
l.ConsumeKeyword("implements")
o.Implements = l.ConsumeIdent()
}
l.ConsumeToken('{')

for l.Peek() != '}' {
f := parseField(l)
o.Fields[f.Name] = f
}
o.Fields = parseFields(l)
l.ConsumeToken('}')

return o
}

func parseEnumDecl(l *lexer.Lexer) *Enum {
enum := &Enum{}
enum.Name = l.ConsumeIdent()
func parseInterfaceDecl(l *lexer.Lexer) *Interface {
i := &Interface{}
i.Name = l.ConsumeIdent()
l.ConsumeToken('{')
for l.Peek() != '}' {
enum.Values = append(enum.Values, l.ConsumeIdent())
}
i.Fields = parseFields(l)
l.ConsumeToken('}')
return enum
return i
}

func parseUnionDecl(l *lexer.Lexer) *Union {
Expand All @@ -180,21 +185,45 @@ func parseUnionDecl(l *lexer.Lexer) *Union {
return union
}

func parseField(l *lexer.Lexer) *Field {
f := &Field{}
f.Name = l.ConsumeIdent()
if l.Peek() == '(' {
f.Parameters = make(map[string]*Parameter)
l.ConsumeToken('(')
for l.Peek() != ')' {
p := parseParameter(l)
f.Parameters[p.Name] = p
func parseInputDecl(l *lexer.Lexer) *Input {
i := &Input{}
i.Name = l.ConsumeIdent()
l.ConsumeToken('{')
i.Fields = parseFields(l)
l.ConsumeToken('}')
return i
}

func parseEnumDecl(l *lexer.Lexer) *Enum {
enum := &Enum{}
enum.Name = l.ConsumeIdent()
l.ConsumeToken('{')
for l.Peek() != '}' {
enum.Values = append(enum.Values, l.ConsumeIdent())
}
l.ConsumeToken('}')
return enum
}

func parseFields(l *lexer.Lexer) map[string]*Field {
fields := make(map[string]*Field)
for l.Peek() != '}' {
f := &Field{}
f.Name = l.ConsumeIdent()
if l.Peek() == '(' {
f.Parameters = make(map[string]*Parameter)
l.ConsumeToken('(')
for l.Peek() != ')' {
p := parseParameter(l)
f.Parameters[p.Name] = p
}
l.ConsumeToken(')')
}
l.ConsumeToken(')')
l.ConsumeToken(':')
f.Type = parseType(l)
fields[f.Name] = f
}
l.ConsumeToken(':')
f.Type = parseType(l)
return f
return fields
}

func parseParameter(l *lexer.Lexer) *Parameter {
Expand Down

0 comments on commit e2c58f2

Please sign in to comment.