1 package validator
2
3 import (
4 "encoding/json"
5 "fmt"
6 "reflect"
7 "strconv"
8 "strings"
9
10 "github.com/vektah/gqlparser/v2/ast"
11 "github.com/vektah/gqlparser/v2/gqlerror"
12 )
13
14 var ErrUnexpectedType = fmt.Errorf("Unexpected Type")
15
16
17 func VariableValues(schema *ast.Schema, op *ast.OperationDefinition, variables map[string]interface{}) (map[string]interface{}, error) {
18 coercedVars := map[string]interface{}{}
19
20 validator := varValidator{
21 path: ast.Path{ast.PathName("variable")},
22 schema: schema,
23 }
24
25 for _, v := range op.VariableDefinitions {
26 validator.path = append(validator.path, ast.PathName(v.Variable))
27
28 if !v.Definition.IsInputType() {
29 return nil, gqlerror.ErrorPathf(validator.path, "must an input type")
30 }
31
32 val, hasValue := variables[v.Variable]
33
34 if !hasValue {
35 if v.DefaultValue != nil {
36 var err error
37 val, err = v.DefaultValue.Value(nil)
38 if err != nil {
39 return nil, gqlerror.WrapPath(validator.path, err)
40 }
41 hasValue = true
42 } else if v.Type.NonNull {
43 return nil, gqlerror.ErrorPathf(validator.path, "must be defined")
44 }
45 }
46
47 if hasValue {
48 if val == nil {
49 if v.Type.NonNull {
50 return nil, gqlerror.ErrorPathf(validator.path, "cannot be null")
51 }
52 coercedVars[v.Variable] = nil
53 } else {
54 rv := reflect.ValueOf(val)
55
56 jsonNumber, isJSONNumber := val.(json.Number)
57 if isJSONNumber {
58 if v.Type.NamedType == "Int" {
59 n, err := jsonNumber.Int64()
60 if err != nil {
61 return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %d as %s", n, v.Type.NamedType)
62 }
63 rv = reflect.ValueOf(n)
64 } else if v.Type.NamedType == "Float" {
65 f, err := jsonNumber.Float64()
66 if err != nil {
67 return nil, gqlerror.ErrorPathf(validator.path, "cannot use value %f as %s", f, v.Type.NamedType)
68 }
69 rv = reflect.ValueOf(f)
70
71 }
72 }
73 if rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
74 rv = rv.Elem()
75 }
76
77 rval, err := validator.validateVarType(v.Type, rv)
78 if err != nil {
79 return nil, err
80 }
81 coercedVars[v.Variable] = rval.Interface()
82 }
83 }
84
85 validator.path = validator.path[0 : len(validator.path)-1]
86 }
87 return coercedVars, nil
88 }
89
90 type varValidator struct {
91 path ast.Path
92 schema *ast.Schema
93 }
94
95 func (v *varValidator) validateVarType(typ *ast.Type, val reflect.Value) (reflect.Value, *gqlerror.Error) {
96 currentPath := v.path
97 resetPath := func() {
98 v.path = currentPath
99 }
100 defer resetPath()
101 if typ.Elem != nil {
102 if val.Kind() != reflect.Slice {
103
104
105 slc := reflect.MakeSlice(reflect.SliceOf(val.Type()), 0, 0)
106 slc = reflect.Append(slc, val)
107 val = slc
108 }
109 for i := 0; i < val.Len(); i++ {
110 resetPath()
111 v.path = append(v.path, ast.PathIndex(i))
112 field := val.Index(i)
113 if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
114 if typ.Elem.NonNull && field.IsNil() {
115 return val, gqlerror.ErrorPathf(v.path, "cannot be null")
116 }
117 field = field.Elem()
118 }
119 _, err := v.validateVarType(typ.Elem, field)
120 if err != nil {
121 return val, err
122 }
123 }
124 return val, nil
125 }
126 def := v.schema.Types[typ.NamedType]
127 if def == nil {
128 panic(fmt.Errorf("missing def for %s", typ.NamedType))
129 }
130
131 if !typ.NonNull && !val.IsValid() {
132
133 return val, nil
134 }
135
136 switch def.Kind {
137 case ast.Enum:
138 kind := val.Type().Kind()
139 if kind != reflect.Int && kind != reflect.Int32 && kind != reflect.Int64 && kind != reflect.String {
140 return val, gqlerror.ErrorPathf(v.path, "enums must be ints or strings")
141 }
142 isValidEnum := false
143 for _, enumVal := range def.EnumValues {
144 if strings.EqualFold(val.String(), enumVal.Name) {
145 isValidEnum = true
146 }
147 }
148 if !isValidEnum {
149 return val, gqlerror.ErrorPathf(v.path, "%s is not a valid %s", val.String(), def.Name)
150 }
151 return val, nil
152 case ast.Scalar:
153 kind := val.Type().Kind()
154 switch typ.NamedType {
155 case "Int":
156 if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.Float32 || kind == reflect.Float64 || IsValidIntString(val, kind) {
157 return val, nil
158 }
159 case "Float":
160 if kind == reflect.Float32 || kind == reflect.Float64 || kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || IsValidFloatString(val, kind) {
161 return val, nil
162 }
163 case "String":
164 if kind == reflect.String {
165 return val, nil
166 }
167
168 case "Boolean":
169 if kind == reflect.Bool {
170 return val, nil
171 }
172
173 case "ID":
174 if kind == reflect.Int || kind == reflect.Int32 || kind == reflect.Int64 || kind == reflect.String {
175 return val, nil
176 }
177 default:
178
179 return val, nil
180 }
181 return val, gqlerror.ErrorPathf(v.path, "cannot use %s as %s", kind.String(), typ.NamedType)
182 case ast.InputObject:
183 if val.Kind() != reflect.Map {
184 return val, gqlerror.ErrorPathf(v.path, "must be a %s", def.Name)
185 }
186
187
188 for _, name := range val.MapKeys() {
189 val.MapIndex(name)
190 fieldDef := def.Fields.ForName(name.String())
191 resetPath()
192 v.path = append(v.path, ast.PathName(name.String()))
193
194 switch {
195 case name.String() == "__typename":
196 continue
197 case fieldDef == nil:
198 return val, gqlerror.ErrorPathf(v.path, "unknown field")
199 }
200 }
201
202 for _, fieldDef := range def.Fields {
203 resetPath()
204 v.path = append(v.path, ast.PathName(fieldDef.Name))
205
206 field := val.MapIndex(reflect.ValueOf(fieldDef.Name))
207 if !field.IsValid() {
208 if fieldDef.Type.NonNull {
209 if fieldDef.DefaultValue != nil {
210 var err error
211 _, err = fieldDef.DefaultValue.Value(nil)
212 if err == nil {
213 continue
214 }
215 }
216 return val, gqlerror.ErrorPathf(v.path, "must be defined")
217 }
218 continue
219 }
220
221 if field.Kind() == reflect.Ptr || field.Kind() == reflect.Interface {
222 if fieldDef.Type.NonNull && field.IsNil() {
223 return val, gqlerror.ErrorPathf(v.path, "cannot be null")
224 }
225
226 if !fieldDef.Type.NonNull && field.IsNil() {
227 continue
228 }
229 field = field.Elem()
230 }
231 cval, err := v.validateVarType(fieldDef.Type, field)
232 if err != nil {
233 return val, err
234 }
235 val.SetMapIndex(reflect.ValueOf(fieldDef.Name), cval)
236 }
237 default:
238 panic(fmt.Errorf("unsupported type %s", def.Kind))
239 }
240 return val, nil
241 }
242
243 func IsValidIntString(val reflect.Value, kind reflect.Kind) bool {
244 if kind != reflect.String {
245 return false
246 }
247 _, e := strconv.ParseInt(fmt.Sprintf("%v", val.Interface()), 10, 64)
248
249 return e == nil
250 }
251
252 func IsValidFloatString(val reflect.Value, kind reflect.Kind) bool {
253 if kind != reflect.String {
254 return false
255 }
256 _, e := strconv.ParseFloat(fmt.Sprintf("%v", val.Interface()), 64)
257 return e == nil
258 }
259
View as plain text