vars.go

  1package validator
  2
  3import (
  4	"reflect"
  5
  6	"fmt"
  7
  8	"github.com/vektah/gqlparser/ast"
  9	"github.com/vektah/gqlparser/gqlerror"
 10)
 11
 12var UnexpectedType = fmt.Errorf("Unexpected Type")
 13
 14// VariableValues coerces and validates variable values
 15func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, *gqlerror.Error) {
 16	coercedVars := map[string]interface{}{}
 17
 18	validator := varValidator{
 19		path:   []interface{}{"variable"},
 20		schema: schema,
 21	}
 22
 23	for _, v := range op.VariableDefinitions {
 24		validator.path = append(validator.path, v.Variable)
 25
 26		if !v.Definition.IsInputType() {
 27			return nil, gqlerror.ErrorPathf(validator.path, "must an input type")
 28		}
 29
 30		val, hasValue := variables[v.Variable]
 31		if !hasValue {
 32			if v.DefaultValue != nil {
 33				var err error
 34				val, err = v.DefaultValue.Value(nil)
 35				if err != nil {
 36					return nil, gqlerror.WrapPath(validator.path, err)
 37				}
 38				hasValue = true
 39			} else if v.Type.NonNull {
 40				return nil, gqlerror.ErrorPathf(validator.path, "must be defined")
 41			}
 42		}
 43
 44		if hasValue {
 45			if val == nil {
 46				if v.Type.NonNull {
 47					return nil, gqlerror.ErrorPathf(validator.path, "cannot be null")
 48				}
 49				coercedVars[v.Variable] = nil
 50			} else {
 51				rv := reflect.ValueOf(val)
 52				if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
 53					rv = rv.Elem()
 54				}
 55
 56				if err := validator.validateVarType(v.Type, rv); err != nil {
 57					return nil, err
 58				}
 59
 60				coercedVars[v.Variable] = val
 61			}
 62		}
 63
 64		validator.path = validator.path[0 : len(validator.path)-1]
 65	}
 66
 67	return coercedVars, nil
 68}
 69
 70type varValidator struct {
 71	path   []interface{}
 72	schema *ast.Schema
 73}
 74
 75func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) *gqlerror.Error {
 76	currentPath := v.path
 77	resetPath := func() {
 78		v.path = currentPath
 79	}
 80	defer resetPath()
 81
 82	if typ.Elem != nil {
 83		if val.Kind() != reflect.Slice {
 84			return gqlerror.ErrorPathf(v.path, "must be an array")
 85		}
 86
 87		for i := 0; i < val.Len(); i++ {
 88			resetPath()
 89			v.path = append(v.path, i)
 90			field := val.Index(i)
 91
 92			if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
 93				if typ.Elem.NonNull && field.IsNil() {
 94					return gqlerror.ErrorPathf(v.path, "cannot be null")
 95				}
 96				field = field.Elem()
 97			}
 98
 99			if err := v.validateVarType(typ.Elem, field); err != nil {
100				return err
101			}
102		}
103
104		return nil
105	}
106
107	def := v.schema.Types[typ.NamedType]
108	if def == nil {
109		panic(fmt.Errorf("missing def for %s", typ.NamedType))
110	}
111
112	switch def.Kind {
113	case ast.Enum:
114		kind := val.Type().Kind()
115		if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
116			return nil
117		}
118		return gqlerror.ErrorPathf(v.path, "enums must be ints or strings")
119	case ast.Scalar:
120		kind := val.Type().Kind()
121		switch typ.NamedType {
122		case "Int":
123			if kind == reflect.String || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 {
124				return nil
125			}
126		case "Float":
127			if kind == reflect.String || kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 {
128				return nil
129			}
130		case "String":
131			if kind == reflect.String {
132				return nil
133			}
134
135		case "Boolean":
136			if kind == reflect.Bool {
137				return nil
138			}
139
140		case "ID":
141			if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
142				return nil
143			}
144		default:
145			// assume custom scalars are ok
146			return nil
147		}
148		return gqlerror.ErrorPathf(v.path, "cannot use %s as %s", kind.String(), typ.NamedType)
149	case ast.InputObject:
150		if val.Kind() != reflect.Map {
151			return gqlerror.ErrorPathf(v.path, "must be a %s", def.Name)
152		}
153
154		// check for unknown fields
155		for _, name := range val.MapKeys() {
156			val.MapIndex(name)
157			fieldDef := def.Fields.ForName(name.String())
158			resetPath()
159			v.path = append(v.path, name.String())
160
161			if fieldDef == nil {
162				return gqlerror.ErrorPathf(v.path, "unknown field")
163			}
164		}
165
166		for _, fieldDef := range def.Fields {
167			resetPath()
168			v.path = append(v.path, fieldDef.Name)
169
170			field := val.MapIndex(reflect.ValueOf(fieldDef.Name))
171			if !field.IsValid() {
172				if fieldDef.Type.NonNull {
173					return gqlerror.ErrorPathf(v.path, "must be defined")
174				}
175				continue
176			}
177
178			if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
179				if fieldDef.Type.NonNull && field.IsNil() {
180					return gqlerror.ErrorPathf(v.path, "cannot be null")
181				}
182				//allow null object field and skip it
183				if !fieldDef.Type.NonNull && field.IsNil() {
184					continue
185				}
186				field = field.Elem()
187			}
188
189			err := v.validateVarType(fieldDef.Type, field)
190			if err != nil {
191				return err
192			}
193		}
194	default:
195		panic(fmt.Errorf("unsupported type %s", def.Kind))
196	}
197
198	return nil
199}