Skip to content

Commit

Permalink
refactored exec.Request
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed Mar 24, 2017
1 parent 9dd714e commit 58d3d5b
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 85 deletions.
56 changes: 47 additions & 9 deletions graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func (s *Schema) Exec(ctx context.Context, queryString string, operationName str
}
}

span, subCtx := opentracing.StartSpanFromContext(ctx, "GraphQL request")
span, spanCtx := opentracing.StartSpanFromContext(ctx, "GraphQL request")
span.SetTag(OpenTracingTagQuery, queryString)
if operationName != "" {
span.SetTag(OpenTracingTagOperationName, operationName)
Expand All @@ -117,14 +117,11 @@ func (s *Schema) Exec(ctx context.Context, queryString string, operationName str
}
defer span.Finish()

var data interface{}
errs := validation.Validate(s.schema, document)
if len(errs) == 0 {
data, errs = exec.ExecuteRequest(subCtx, s.exec, document, operationName, variables, s.MaxParallelism)
if len(errs) != 0 {
ext.Error.Set(span, true)
span.SetTag(OpenTracingTagError, errs)
}
data, errs := s.doExec(spanCtx, document, operationName, variables)

if len(errs) != 0 {
ext.Error.Set(span, true)
span.SetTag(OpenTracingTagError, errs)
}

return &Response{
Expand All @@ -133,6 +130,47 @@ func (s *Schema) Exec(ctx context.Context, queryString string, operationName str
}
}

func (s *Schema) doExec(ctx context.Context, doc *query.Document, operationName string, vars map[string]interface{}) (interface{}, []*errors.QueryError) {
errs := validation.Validate(s.schema, doc)
if len(errs) != 0 {
return nil, errs
}

op, err := getOperation(doc, operationName)
if err != nil {
return nil, []*errors.QueryError{errors.Errorf("%s", err)}
}

r := &exec.Request{
Doc: doc,
Vars: vars,
Schema: s.schema,
Limiter: make(chan struct{}, s.MaxParallelism),
}
return r.Execute(ctx, s.exec, op)
}

func getOperation(document *query.Document, operationName string) (*query.Operation, error) {
if len(document.Operations) == 0 {
return nil, fmt.Errorf("no operations in query document")
}

if operationName == "" {
if len(document.Operations) > 1 {
return nil, fmt.Errorf("more than one operation in query document and no operation name given")
}
for _, op := range document.Operations {
return op, nil // return the one and only operation
}
}

op := document.Operations.Get(operationName)
if op == nil {
return nil, fmt.Errorf("no operation with name %q", operationName)
}
return op, nil
}

// Inspect allows inspection of the given schema.
func (s *Schema) Inspect() *introspection.Schema {
return introspection.WrapSchema(s.schema)
Expand Down
89 changes: 27 additions & 62 deletions internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,25 +326,23 @@ func findMethod(t reflect.Type, name string) int {
return -1
}

type semaphore chan struct{}

type request struct {
doc *query.Document
vars map[string]interface{}
schema *schema.Schema
limiter semaphore
type Request struct {
Doc *query.Document
Vars map[string]interface{}
Schema *schema.Schema
Limiter chan struct{}
wg sync.WaitGroup
mu sync.Mutex
errs []*errors.QueryError
}

func (r *request) addError(err *errors.QueryError) {
func (r *Request) addError(err *errors.QueryError) {
r.mu.Lock()
r.errs = append(r.errs, err)
r.mu.Unlock()
}

func (r *request) handlePanic() {
func (r *Request) handlePanic() {
if err := recover(); err != nil {
r.addError(makePanicError(err))
}
Expand All @@ -359,26 +357,14 @@ func makePanicError(value interface{}) *errors.QueryError {
return err
}

func (r *request) resolveVar(value interface{}) interface{} {
func (r *Request) resolveVar(value interface{}) interface{} {
if v, ok := value.(lexer.Variable); ok {
value = r.vars[string(v)]
value = r.Vars[string(v)]
}
return value
}

func ExecuteRequest(ctx context.Context, e *Exec, document *query.Document, operationName string, variables map[string]interface{}, maxParallelism int) (interface{}, []*errors.QueryError) {
op, err := getOperation(document, operationName)
if err != nil {
return nil, []*errors.QueryError{errors.Errorf("%s", err)}
}

r := &request{
doc: document,
vars: variables,
schema: e.schema,
limiter: make(semaphore, maxParallelism),
}

func (r *Request) Execute(ctx context.Context, e *Exec, op *query.Operation) (interface{}, []*errors.QueryError) {
var opExec *objectExec
var serially bool
switch op.Type {
Expand All @@ -404,34 +390,13 @@ func ExecuteRequest(ctx context.Context, e *Exec, document *query.Document, oper
return results, r.errs
}

func getOperation(document *query.Document, operationName string) (*query.Operation, error) {
if len(document.Operations) == 0 {
return nil, fmt.Errorf("no operations in query document")
}

if operationName == "" {
if len(document.Operations) > 1 {
return nil, fmt.Errorf("more than one operation in query document and no operation name given")
}
for _, op := range document.Operations {
return op, nil // return the one and only operation
}
}

op := document.Operations.Get(operationName)
if op == nil {
return nil, fmt.Errorf("no operation with name %q", operationName)
}
return op, nil
}

type iExec interface {
exec(ctx context.Context, r *request, selSet *query.SelectionSet, resolver reflect.Value) interface{}
exec(ctx context.Context, r *Request, selSet *query.SelectionSet, resolver reflect.Value) interface{}
}

type scalarExec struct{}

func (e *scalarExec) exec(ctx context.Context, r *request, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
func (e *scalarExec) exec(ctx context.Context, r *Request, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
return resolver.Interface()
}

Expand All @@ -440,7 +405,7 @@ type listExec struct {
nonNull bool
}

func (e *listExec) exec(ctx context.Context, r *request, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
func (e *listExec) exec(ctx context.Context, r *Request, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
if !e.nonNull {
if resolver.IsNil() {
return nil
Expand All @@ -462,7 +427,7 @@ type objectExec struct {
}

type fieldExec interface {
execField(ctx context.Context, r *request, e *objectExec, f *query.Field, resolver reflect.Value) interface{}
execField(ctx context.Context, r *Request, e *objectExec, f *query.Field, resolver reflect.Value) interface{}
}

type normalFieldExec struct {
Expand All @@ -477,13 +442,13 @@ type normalFieldExec struct {
spanLabel string
}

type metaFieldExec func(ctx context.Context, r *request, e *objectExec, f *query.Field, resolver reflect.Value) interface{}
type metaFieldExec func(ctx context.Context, r *Request, e *objectExec, f *query.Field, resolver reflect.Value) interface{}

func (fe metaFieldExec) execField(ctx context.Context, r *request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
func (fe metaFieldExec) execField(ctx context.Context, r *Request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
return fe(ctx, r, e, f, resolver)
}

var typenameFieldExec = metaFieldExec(func(ctx context.Context, r *request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
var typenameFieldExec = metaFieldExec(func(ctx context.Context, r *Request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
if len(e.typeAssertions) == 0 {
return e.name
}
Expand All @@ -497,11 +462,11 @@ var typenameFieldExec = metaFieldExec(func(ctx context.Context, r *request, e *o
return nil
})

var schemaFieldExec = metaFieldExec(func(ctx context.Context, r *request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
var schemaFieldExec = metaFieldExec(func(ctx context.Context, r *Request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
return introspectSchema(ctx, r, f.SelSet)
})

var typeFieldExec = metaFieldExec(func(ctx context.Context, r *request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
var typeFieldExec = metaFieldExec(func(ctx context.Context, r *Request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
p := valuePacker{valueType: reflect.TypeOf("")}
v, err := p.pack(r, r.resolveVar(f.Arguments.MustGet("name").Value))
if err != nil {
Expand All @@ -511,7 +476,7 @@ var typeFieldExec = metaFieldExec(func(ctx context.Context, r *request, e *objec
return introspectType(ctx, r, v.String(), f.SelSet)
})

func (e *objectExec) exec(ctx context.Context, r *request, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
func (e *objectExec) exec(ctx context.Context, r *Request, selSet *query.SelectionSet, resolver reflect.Value) interface{} {
if resolver.IsNil() {
if e.nonNull {
r.addError(errors.Errorf("got nil for non-null %q", e.name))
Expand All @@ -523,7 +488,7 @@ func (e *objectExec) exec(ctx context.Context, r *request, selSet *query.Selecti
return results
}

func (e *objectExec) execSelectionSet(ctx context.Context, r *request, selSet *query.SelectionSet, resolver reflect.Value, results map[string]interface{}, serially bool) {
func (e *objectExec) execSelectionSet(ctx context.Context, r *Request, selSet *query.SelectionSet, resolver reflect.Value, results map[string]interface{}, serially bool) {
for _, sel := range selSet.Selections {
switch sel := sel.(type) {
case *query.Field:
Expand All @@ -549,15 +514,15 @@ func (e *objectExec) execSelectionSet(ctx context.Context, r *request, selSet *q
if skipByDirective(r, spread.Directives) {
continue
}
e.execFragment(ctx, r, &r.doc.Fragments.Get(spread.Name.Name).Fragment, resolver, results)
e.execFragment(ctx, r, &r.Doc.Fragments.Get(spread.Name.Name).Fragment, resolver, results)

default:
panic("invalid type")
}
}
}

func (fe *normalFieldExec) execField(ctx context.Context, r *request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
func (fe *normalFieldExec) execField(ctx context.Context, r *Request, e *objectExec, f *query.Field, resolver reflect.Value) interface{} {
var args map[string]interface{}
var packedArgs reflect.Value
if fe.argsPacker != nil {
Expand All @@ -575,7 +540,7 @@ func (fe *normalFieldExec) execField(ctx context.Context, r *request, e *objectE

do := func(applyLimiter bool) interface{} {
if applyLimiter {
r.limiter <- struct{}{}
r.Limiter <- struct{}{}
}

span, spanCtx := opentracing.StartSpanFromContext(ctx, fe.spanLabel)
Expand Down Expand Up @@ -620,7 +585,7 @@ func (fe *normalFieldExec) execField(ctx context.Context, r *request, e *objectE
}()

if applyLimiter {
<-r.limiter
<-r.Limiter
}

if err != nil {
Expand All @@ -646,7 +611,7 @@ func (fe *normalFieldExec) execField(ctx context.Context, r *request, e *objectE
return result
}

func (e *objectExec) execFragment(ctx context.Context, r *request, frag *query.Fragment, resolver reflect.Value, results map[string]interface{}) {
func (e *objectExec) execFragment(ctx context.Context, r *Request, frag *query.Fragment, resolver reflect.Value, results map[string]interface{}) {
if frag.On.Name != "" && frag.On.Name != e.name {
a, ok := e.typeAssertions[frag.On.Name]
if !ok {
Expand All @@ -667,7 +632,7 @@ type typeAssertExec struct {
typeExec iExec
}

func skipByDirective(r *request, directives common.DirectiveList) bool {
func skipByDirective(r *Request, directives common.DirectiveList) bool {
if d := directives.Get("skip"); d != nil {
p := valuePacker{valueType: reflect.TypeOf(false)}
v, err := p.pack(r, r.resolveVar(d.Args.MustGet("if").Value))
Expand Down
16 changes: 8 additions & 8 deletions internal/exec/introspection.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,20 @@ func init() {
}

func IntrospectSchema(s *schema.Schema) (interface{}, error) {
r := &request{
schema: s,
doc: introspectionQuery,
limiter: make(semaphore, 10),
r := &Request{
Schema: s,
Doc: introspectionQuery,
Limiter: make(chan struct{}, 10),
}
return introspectSchema(context.Background(), r, introspectionQuery.Operations.Get("IntrospectionQuery").SelSet), nil
}

func introspectSchema(ctx context.Context, r *request, selSet *query.SelectionSet) interface{} {
return schemaExec.exec(ctx, r, selSet, reflect.ValueOf(introspection.WrapSchema(r.schema)))
func introspectSchema(ctx context.Context, r *Request, selSet *query.SelectionSet) interface{} {
return schemaExec.exec(ctx, r, selSet, reflect.ValueOf(introspection.WrapSchema(r.Schema)))
}

func introspectType(ctx context.Context, r *request, name string, selSet *query.SelectionSet) interface{} {
t, ok := r.schema.Types[name]
func introspectType(ctx context.Context, r *Request, name string, selSet *query.SelectionSet) interface{} {
t, ok := r.Schema.Types[name]
if !ok {
return nil
}
Expand Down
12 changes: 6 additions & 6 deletions internal/exec/packer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

type packer interface {
pack(r *request, value interface{}) (reflect.Value, error)
pack(r *Request, value interface{}) (reflect.Value, error)
}

func (b *execBuilder) assignPacker(target *packer, schemaType common.Type, reflectType reflect.Type) error {
Expand Down Expand Up @@ -162,7 +162,7 @@ type structPackerField struct {
fieldPacker packer
}

func (p *structPacker) pack(r *request, value interface{}) (reflect.Value, error) {
func (p *structPacker) pack(r *Request, value interface{}) (reflect.Value, error) {
if value == nil {
return reflect.Value{}, errors.Errorf("got null for non-null")
}
Expand All @@ -187,7 +187,7 @@ type listPacker struct {
elem packer
}

func (e *listPacker) pack(r *request, value interface{}) (reflect.Value, error) {
func (e *listPacker) pack(r *Request, value interface{}) (reflect.Value, error) {
list, ok := value.([]interface{})
if !ok {
list = []interface{}{value}
Expand All @@ -210,7 +210,7 @@ type nullPacker struct {
addPtr bool
}

func (p *nullPacker) pack(r *request, value interface{}) (reflect.Value, error) {
func (p *nullPacker) pack(r *Request, value interface{}) (reflect.Value, error) {
if value == nil {
return reflect.Zero(p.valueType), nil
}
Expand All @@ -233,7 +233,7 @@ type valuePacker struct {
valueType reflect.Type
}

func (p *valuePacker) pack(r *request, value interface{}) (reflect.Value, error) {
func (p *valuePacker) pack(r *Request, value interface{}) (reflect.Value, error) {
if value == nil {
return reflect.Value{}, errors.Errorf("got null for non-null")
}
Expand All @@ -253,7 +253,7 @@ type unmarshalerPacker struct {
valueType reflect.Type
}

func (p *unmarshalerPacker) pack(r *request, value interface{}) (reflect.Value, error) {
func (p *unmarshalerPacker) pack(r *Request, value interface{}) (reflect.Value, error) {
if value == nil {
return reflect.Value{}, errors.Errorf("got null for non-null")
}
Expand Down

0 comments on commit 58d3d5b

Please sign in to comment.