Skip to content

Commit

Permalink
better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
neelance committed Nov 1, 2016
1 parent e3386b0 commit cec7cea
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 65 deletions.
33 changes: 10 additions & 23 deletions internal/common/values.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type InputMap struct {
type InputValue struct {
Name string
Type Type
Default Value
Default interface{}
}

func ParseInputValue(l *lexer.Lexer) *InputValue {
Expand All @@ -29,42 +29,29 @@ func ParseInputValue(l *lexer.Lexer) *InputValue {
return p
}

type Value interface {
isValue()
}

type Variable struct {
Name string
}

type Literal struct {
Value interface{}
}

func (*Variable) isValue() {}
func (*Literal) isValue() {}
type Variable string

func ParseValue(l *lexer.Lexer, constOnly bool) Value {
func ParseValue(l *lexer.Lexer, constOnly bool) interface{} {
if !constOnly && l.Peek() == '$' {
l.ConsumeToken('$')
return &Variable{Name: l.ConsumeIdent()}
return Variable(l.ConsumeIdent())
}

switch l.Peek() {
case scanner.Int:
return &Literal{Value: l.ConsumeInt()}
return l.ConsumeInt()
case scanner.Float:
return &Literal{Value: l.ConsumeFloat()}
return l.ConsumeFloat()
case scanner.String:
return &Literal{Value: l.ConsumeString()}
return l.ConsumeString()
case scanner.Ident:
switch ident := l.ConsumeIdent(); ident {
case "true":
return &Literal{Value: true}
return true
case "false":
return &Literal{Value: false}
return false
default:
return &Literal{Value: ident}
return ident
}
default:
l.SyntaxError("invalid value")
Expand Down
93 changes: 56 additions & 37 deletions internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,11 @@ func makeStructPacker(s *schema.Schema, obj *common.InputMap, typ reflect.Type)
}

if f.Default != nil {
defaultValue := fe.fieldPacker.pack(evalValue(f.Type, f.Default, nil))
defaultStruct.FieldByIndex(fe.fieldIndex).Set(defaultValue)
defaultValue, err := coerceValue(nil, f.Type, f.Default)
if err != nil {
return nil, err
}
defaultStruct.FieldByIndex(fe.fieldIndex).Set(fe.fieldPacker.pack(defaultValue))
}

fields = append(fields, fe)
Expand Down Expand Up @@ -388,7 +391,7 @@ func ExecuteRequest(ctx context.Context, e *Exec, document *query.Document, oper
return nil, []*errors.QueryError{errors.Errorf("%s", err)}
}

coercedVariables, err := coerceMap(&op.Vars, variables)
coercedVariables, err := coerceMap(nil, &op.Vars, variables)
if err != nil {
return nil, []*errors.QueryError{errors.Errorf("%s", err)}
}
Expand Down Expand Up @@ -539,7 +542,13 @@ func (e *objectExec) execSelectionSet(ctx context.Context, r *request, selSet *q
addResult(f.Alias, introspectSchema(ctx, r, f.SelSet))

case "__type":
addResult(f.Alias, introspectType(ctx, r, evalValue(stringScalar, f.Arguments["name"], r.vars).(string), f.SelSet))
v, err := coerceValue(r, stringScalar, f.Arguments["name"])
if err != nil {
r.addError(errors.Errorf("%s", err))
addResult(f.Alias, nil)
return
}
addResult(f.Alias, introspectType(ctx, r, v.(string), f.SelSet))

default:
fe, ok := e.fields[f.Name]
Expand Down Expand Up @@ -612,6 +621,21 @@ func (e *fieldExec) execField(ctx context.Context, r *request, f *query.Field, r
defer span.Finish()
}

result, err := e.execField2(ctx, r, f, resolver, span)

if err != nil {
r.addError(errors.Errorf("%s", err))
addResult(f.Alias, nil) // TODO handle non-nil

ext.Error.Set(span, true)
span.SetTag("errorMsg", err)
return
}

addResult(f.Alias, result)
}

func (e *fieldExec) execField2(ctx context.Context, r *request, f *query.Field, resolver reflect.Value, span opentracing.Span) (interface{}, error) {
var in []reflect.Value

if e.hasContext {
Expand All @@ -621,7 +645,10 @@ func (e *fieldExec) execField(ctx context.Context, r *request, f *query.Field, r
if e.argsPacker != nil {
values := make(map[string]interface{})
for name, arg := range f.Arguments {
v := evalValue(e.field.Args.Fields[name].Type, arg, r.vars)
v, err := coerceValue(r, e.field.Args.Fields[name].Type, arg)
if err != nil {
return nil, err
}
values[name] = v
span.SetTag(name, v)
}
Expand All @@ -631,50 +658,28 @@ func (e *fieldExec) execField(ctx context.Context, r *request, f *query.Field, r
m := resolver.Method(e.methodIndex)
out := m.Call(in)
if e.hasError && !out[1].IsNil() {
err := out[1].Interface().(error)
r.addError(errors.Errorf("%s", err))
addResult(f.Alias, nil) // TODO handle non-nil

ext.Error.Set(span, true)
span.SetTag("errorMsg", err)

return
return nil, out[1].Interface().(error)
}
addResult(f.Alias, e.valueExec.exec(ctx, r, f.SelSet, out[0], false))

return e.valueExec.exec(ctx, r, f.SelSet, out[0], false), nil
}

type typeAssertExec struct {
methodIndex int
typeExec iExec
}

func evalValue(t common.Type, v common.Value, vars map[string]interface{}) interface{} {
switch v := v.(type) {
case *common.Variable:
return vars[v.Name]
case *common.Literal:
coerced, err := coerceValue(t, v.Value)
if err != nil {
panic(err) // TODO proper error handling
}
return coerced
default:
panic("unreachable")
}
}

func coerceMap(io *common.InputMap, m map[string]interface{}) (map[string]interface{}, error) {
func coerceMap(r *request, io *common.InputMap, m map[string]interface{}) (map[string]interface{}, error) {
coerced := make(map[string]interface{})
for _, iv := range io.Fields {
value, ok := m[iv.Name]
if !ok {
if iv.Default == nil {
return nil, errors.Errorf("missing %q", iv.Name)
}
coerced[iv.Name] = evalValue(iv.Type, iv.Default, nil)
continue
value = iv.Default
}
c, err := coerceValue(iv.Type, value)
c, err := coerceValue(r, iv.Type, value)
if err != nil {
return nil, err
}
Expand All @@ -683,7 +688,11 @@ func coerceMap(io *common.InputMap, m map[string]interface{}) (map[string]interf
return coerced, nil
}

func coerceValue(typ common.Type, value interface{}) (interface{}, error) {
func coerceValue(r *request, typ common.Type, value interface{}) (interface{}, error) {
if v, ok := value.(common.Variable); ok {
return r.vars[string(v)], nil
}

t, _ := unwrapNonNull(typ)
switch t := t.(type) {
case *scalar:
Expand All @@ -693,7 +702,7 @@ func coerceValue(typ common.Type, value interface{}) (interface{}, error) {
}
return v, nil
case *schema.InputObject:
return coerceMap(&t.InputMap, value.(map[string]interface{}))
return coerceMap(r, &t.InputMap, value.(map[string]interface{}))
}
return value, nil
}
Expand Down Expand Up @@ -743,15 +752,25 @@ func (e *valuePacker) pack(value interface{}) reflect.Value {

func skipByDirective(r *request, d map[string]*query.Directive) bool {
if skip, ok := d["skip"]; ok {
if evalValue(booleanScalar, skip.Arguments["if"], r.vars).(bool) {
v, err := coerceValue(r, booleanScalar, skip.Arguments["if"])
if err != nil {
r.addError(errors.Errorf("%s", err))
}
if err == nil && v.(bool) {
return true
}
}

if include, ok := d["include"]; ok {
if !evalValue(booleanScalar, include.Arguments["if"], r.vars).(bool) {
v, err := coerceValue(r, booleanScalar, include.Arguments["if"])
if err != nil {
r.addError(errors.Errorf("%s", err))
}
if err == nil && !v.(bool) {
return true
}
}

return false
}

Expand Down
10 changes: 5 additions & 5 deletions internal/query/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ type Selection interface {
type Field struct {
Alias string
Name string
Arguments map[string]common.Value
Arguments map[string]interface{}
Directives map[string]*Directive
SelSet *SelectionSet
}

type Directive struct {
Name string
Arguments map[string]common.Value
Arguments map[string]interface{}
}

type FragmentSpread struct {
Expand Down Expand Up @@ -202,8 +202,8 @@ func parseField(l *lexer.Lexer) *Field {
return f
}

func parseArguments(l *lexer.Lexer) map[string]common.Value {
args := make(map[string]common.Value)
func parseArguments(l *lexer.Lexer) map[string]interface{} {
args := make(map[string]interface{})
l.ConsumeToken('(')
if l.Peek() != ')' {
name, value := parseArgument(l)
Expand Down Expand Up @@ -257,7 +257,7 @@ func parseSpread(l *lexer.Lexer) Selection {
return fs
}

func parseArgument(l *lexer.Lexer) (string, common.Value) {
func parseArgument(l *lexer.Lexer) (string, interface{}) {
name := l.ConsumeIdent()
l.ConsumeToken(':')
value := common.ParseValue(l, false)
Expand Down

0 comments on commit cec7cea

Please sign in to comment.