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}