From 1a959516cacda256f52b3ba2f5cf2e7fd03c85da Mon Sep 17 00:00:00 2001 From: Richard Musiol Date: Tue, 25 Oct 2016 14:42:39 +0200 Subject: [PATCH] refactor --- example/starwars/starwars.go | 12 ++++++++ graphql.go | 29 +++++++++++++----- graphql_test.go | 29 ++++++++++++++++++ internal/exec/exec.go | 54 ++++++++++++++++++++++++---------- internal/exec/introspection.go | 6 ++-- internal/schema/schema.go | 22 ++++++++++---- 6 files changed, 121 insertions(+), 31 deletions(-) diff --git a/example/starwars/starwars.go b/example/starwars/starwars.go index 78e079cd55e..38284078040 100644 --- a/example/starwars/starwars.go +++ b/example/starwars/starwars.go @@ -339,6 +339,13 @@ func (r *Resolver) Starship(args struct{ ID string }) *starshipResolver { return nil } +func (r *Resolver) CreateReview(args struct { + Episode string + Review *reviewInput +}) *reviewResolver { + panic("TODO") +} + type friendsConenctionArgs struct { First int32 After string @@ -618,3 +625,8 @@ func (r *pageInfoResolver) EndCursor() *string { func (r *pageInfoResolver) HasNextPage() bool { return r.hasNextPage } + +type reviewInput struct { + Stars int32 + Commentary string +} diff --git a/graphql.go b/graphql.go index 8636d15eb9a..3915b76b352 100644 --- a/graphql.go +++ b/graphql.go @@ -42,20 +42,33 @@ func (s *Schema) Exec(ctx context.Context, queryString string, operationName str } } - if operationName == "" && len(d.Operations) == 1 { - for name := range d.Operations { - operationName = name + if len(d.Operations) == 0 { + return &Response{ + Errors: []*errors.GraphQLError{errors.Errorf("no operations in query document")}, } } - op, ok := d.Operations[operationName] - if !ok { - return &Response{ - Errors: []*errors.GraphQLError{errors.Errorf("no operation with name %q", operationName)}, + var op *query.Operation + if operationName == "" { + if len(d.Operations) > 1 { + return &Response{ + Errors: []*errors.GraphQLError{errors.Errorf("more than one operation in query document and no operation name given")}, + } + } + for _, op2 := range d.Operations { + op = op2 + } + } else { + var ok bool + op, ok = d.Operations[operationName] + if !ok { + return &Response{ + Errors: []*errors.GraphQLError{errors.Errorf("no operation with name %q", operationName)}, + } } } - data, errs := s.exec.Exec(ctx, d, variables, op.SelSet) + data, errs := s.exec.Exec(ctx, d, variables, op) return &Response{ Data: data, Errors: errs, diff --git a/graphql_test.go b/graphql_test.go index 3dd38d0dcec..ca76488ffbf 100644 --- a/graphql_test.go +++ b/graphql_test.go @@ -655,6 +655,35 @@ var tests = []struct { `, }, + // { + // name: "StarWarsMutation1", + // schema: starwars.Schema, + // resolver: &starwars.Resolver{}, + // query: ` + // mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { + // createReview(episode: $ep, review: $review) { + // stars + // commentary + // } + // } + // `, + // variables: map[string]interface{}{ + // "ep": "JEDI", + // "review": map[string]interface{}{ + // "stars": 5, + // "commentary": "This is a great movie!", + // }, + // }, + // result: ` + // { + // "createReview": { + // "stars": 5, + // "commentary": "This is a great movie!" + // } + // } + // `, + // }, + { name: "StarWarsIntrospection1", schema: starwars.Schema, diff --git a/internal/exec/exec.go b/internal/exec/exec.go index 1ea6eefa505..ff3b595445d 100644 --- a/internal/exec/exec.go +++ b/internal/exec/exec.go @@ -15,22 +15,35 @@ import ( ) type Exec struct { - iExec - schema *schema.Schema - resolver reflect.Value + queryExec iExec + mutationExec iExec + schema *schema.Schema + resolver reflect.Value } func Make(s *schema.Schema, resolver interface{}) (*Exec, error) { - t := s.Types[s.EntryPoints["query"]] - e, err := makeWithType(s, t, resolver) - if err != nil { - return nil, err - } - return &Exec{ - iExec: e, + e := &Exec{ schema: s, resolver: reflect.ValueOf(resolver), - }, nil + } + + if t, ok := s.EntryPoints["query"]; ok { + var err error + e.queryExec, err = makeWithType(s, t, resolver) + if err != nil { + return nil, err + } + } + + if t, ok := s.EntryPoints["mutation"]; ok { + var err error + e.mutationExec, err = makeWithType(s, t, resolver) + if err != nil { + return nil, err + } + } + + return e, nil } type typeRefMapKey struct { @@ -206,6 +219,7 @@ func makeFieldExecs(s *schema.Schema, typeName string, fields map[string]*schema for _, arg := range f.Args { ae := &argExec{ name: arg.Name, + typ: arg.Type, } sf, ok := argsType.FieldByNameFunc(func(n string) bool { return strings.EqualFold(n, arg.Name) }) @@ -310,7 +324,7 @@ func (r *request) handlePanic() { } } -func (e *Exec) Exec(ctx context.Context, document *query.Document, variables map[string]interface{}, selSet *query.SelectionSet) (interface{}, []*errors.GraphQLError) { +func (e *Exec) Exec(ctx context.Context, document *query.Document, variables map[string]interface{}, op *query.Operation) (interface{}, []*errors.GraphQLError) { r := &request{ ctx: ctx, doc: document, @@ -318,9 +332,17 @@ func (e *Exec) Exec(ctx context.Context, document *query.Document, variables map schema: e.schema, } + var opExec iExec + switch op.Type { + case query.Query: + opExec = e.queryExec + case query.Mutation: + opExec = e.mutationExec + } + data := func() interface{} { defer r.handlePanic() - return e.exec(r, selSet, e.resolver) + return opExec.exec(r, op.SelSet, e.resolver) }() return data, r.errs @@ -484,6 +506,7 @@ type fieldExec struct { type argExec struct { name string + typ schema.Type fieldIndex []int defaultVal reflect.Value } @@ -505,7 +528,8 @@ func (e *fieldExec) execField(r *request, f *query.Field, resolver reflect.Value } continue } - argsValue.FieldByIndex(arg.fieldIndex).Set(reflect.ValueOf(execValue(r, value))) + v := execValue(r, value) + argsValue.FieldByIndex(arg.fieldIndex).Set(reflect.ValueOf(v)) } in = append(in, argsValue) } @@ -562,6 +586,6 @@ func checkType(st schema.Type, rt reflect.Type) bool { case *schema.Enum: return rt == scalarTypes["String"] default: - panic("TODO") + return true } } diff --git a/internal/exec/introspection.go b/internal/exec/introspection.go index df16e847897..e13912742ff 100644 --- a/internal/exec/introspection.go +++ b/internal/exec/introspection.go @@ -221,7 +221,7 @@ func (r *schemaResolver) Types() []*typeResolver { } func (r *schemaResolver) QueryType() *typeResolver { - t, ok := r.schema.Types[r.schema.EntryPoints["query"]] + t, ok := r.schema.EntryPoints["query"] if !ok { return nil } @@ -229,7 +229,7 @@ func (r *schemaResolver) QueryType() *typeResolver { } func (r *schemaResolver) MutationType() *typeResolver { - t, ok := r.schema.Types[r.schema.EntryPoints["mutation"]] + t, ok := r.schema.EntryPoints["mutation"] if !ok { return nil } @@ -237,7 +237,7 @@ func (r *schemaResolver) MutationType() *typeResolver { } func (r *schemaResolver) SubscriptionType() *typeResolver { - t, ok := r.schema.Types[r.schema.EntryPoints["subscription"]] + t, ok := r.schema.EntryPoints["subscription"] if !ok { return nil } diff --git a/internal/schema/schema.go b/internal/schema/schema.go index a51f2310c81..2748e02ca7d 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -10,11 +10,12 @@ import ( ) type Schema struct { - EntryPoints map[string]string + EntryPoints map[string]Type Types map[string]Type - objects []*Object - unions []*Union + entryPointNames map[string]string + objects []*Object + unions []*Union } type Type interface { @@ -114,6 +115,17 @@ func Parse(schemaString string) (s *Schema, err *errors.GraphQLError) { } } + s.EntryPoints = make(map[string]Type) + for key, name := range s.entryPointNames { + t, ok := s.Types[name] + if !ok { + if !ok { + return nil, errors.Errorf("type %q not found", name) + } + } + s.EntryPoints[key] = t + } + for _, obj := range s.objects { obj.Interfaces = make([]*Interface, len(obj.interfaceNames)) for i, intfName := range obj.interfaceNames { @@ -221,7 +233,7 @@ func resolveTypeRef(s *Schema, t Type) (Type, *errors.GraphQLError) { func parseSchema(l *lexer.Lexer) *Schema { s := &Schema{ - EntryPoints: make(map[string]string), + entryPointNames: make(map[string]string), Types: map[string]Type{ "Int": &Scalar{Name: "Int"}, "Float": &Scalar{Name: "Float"}, @@ -239,7 +251,7 @@ func parseSchema(l *lexer.Lexer) *Schema { name := l.ConsumeIdent() l.ConsumeToken(':') typ := l.ConsumeIdent() - s.EntryPoints[name] = typ + s.entryPointNames[name] = typ } l.ConsumeToken('}') case "type":