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}