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	if typ.Elem != nil {
 77		if val.Kind() != reflect.Slice {
 78			return gqlerror.ErrorPathf(v.path, "must be an array")
 79		}
 80
 81		for i := 0; i < val.Len(); i++ {
 82			v.path = append(v.path, i)
 83			field := val.Index(i)
 84
 85			if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
 86				if typ.Elem.NonNull && field.IsNil() {
 87					return gqlerror.ErrorPathf(v.path, "cannot be null")
 88				}
 89				field = field.Elem()
 90			}
 91
 92			if err := v.validateVarType(typ.Elem, field); err != nil {
 93				return err
 94			}
 95
 96			v.path = v.path[0 : len(v.path)-1]
 97		}
 98
 99		return nil
100	}
101
102	def := v.schema.Types[typ.NamedType]
103	if def == nil {
104		panic(fmt.Errorf("missing def for %s", typ.NamedType))
105	}
106
107	switch def.Kind {
108	case ast.Enum:
109		kind := val.Type().Kind()
110		if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
111			return nil
112		}
113		return gqlerror.ErrorPathf(v.path, "enums must be ints or strings")
114	case ast.Scalar:
115		kind := val.Type().Kind()
116		switch typ.NamedType {
117		case "Int":
118			if kind == reflect.String || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 {
119				return nil
120			}
121		case "Float":
122			if kind == reflect.String || kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 {
123				return nil
124			}
125		case "String":
126			if kind == reflect.String {
127				return nil
128			}
129
130		case "Boolean":
131			if kind == reflect.Bool {
132				return nil
133			}
134
135		case "ID":
136			if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
137				return nil
138			}
139		default:
140			// assume custom scalars are ok
141			return nil
142		}
143		return gqlerror.ErrorPathf(v.path, "cannot use %s as %s", kind.String(), typ.NamedType)
144	case ast.InputObject:
145		if val.Kind() != reflect.Map {
146			return gqlerror.ErrorPathf(v.path, "must be a %s", def.Name)
147		}
148
149		// check for unknown fields
150		for _, name := range val.MapKeys() {
151			val.MapIndex(name)
152			fieldDef := def.Fields.ForName(name.String())
153			v.path = append(v.path, name)
154
155			if fieldDef == nil {
156				return gqlerror.ErrorPathf(v.path, "unknown field")
157			}
158			v.path = v.path[0 : len(v.path)-1]
159		}
160
161		for _, fieldDef := range def.Fields {
162			v.path = append(v.path, fieldDef.Name)
163
164			field := val.MapIndex(reflect.ValueOf(fieldDef.Name))
165			if !field.IsValid() {
166				if fieldDef.Type.NonNull {
167					return gqlerror.ErrorPathf(v.path, "must be defined")
168				}
169				continue
170			}
171
172			if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
173				if fieldDef.Type.NonNull && field.IsNil() {
174					return gqlerror.ErrorPathf(v.path, "cannot be null")
175				}
176				//allow null object field and skip it
177				if !fieldDef.Type.NonNull && field.IsNil() {
178					continue
179				}
180				field = field.Elem()
181			}
182
183			err := v.validateVarType(fieldDef.Type, field)
184			if err != nil {
185				return err
186			}
187
188			v.path = v.path[0 : len(v.path)-1]
189		}
190	default:
191		panic(fmt.Errorf("unsupported type %s", def.Kind))
192	}
193
194	return nil
195}