1package graphql
2
3import (
4 "encoding/json"
5 "fmt"
6 "math"
7 "reflect"
8 "strings"
9
10 "github.com/graphql-go/graphql/gqlerrors"
11 "github.com/graphql-go/graphql/language/ast"
12 "github.com/graphql-go/graphql/language/kinds"
13 "github.com/graphql-go/graphql/language/printer"
14 "sort"
15)
16
17// Prepares an object map of variableValues of the correct type based on the
18// provided variable definitions and arbitrary input. If the input cannot be
19// parsed to match the variable definitions, a GraphQLError will be returned.
20func getVariableValues(schema Schema, definitionASTs []*ast.VariableDefinition, inputs map[string]interface{}) (map[string]interface{}, error) {
21 values := map[string]interface{}{}
22 for _, defAST := range definitionASTs {
23 if defAST == nil || defAST.Variable == nil || defAST.Variable.Name == nil {
24 continue
25 }
26 varName := defAST.Variable.Name.Value
27 varValue, err := getVariableValue(schema, defAST, inputs[varName])
28 if err != nil {
29 return values, err
30 }
31 values[varName] = varValue
32 }
33 return values, nil
34}
35
36// Prepares an object map of argument values given a list of argument
37// definitions and list of argument AST nodes.
38func getArgumentValues(argDefs []*Argument, argASTs []*ast.Argument, variableVariables map[string]interface{}) (map[string]interface{}, error) {
39
40 argASTMap := map[string]*ast.Argument{}
41 for _, argAST := range argASTs {
42 if argAST.Name != nil {
43 argASTMap[argAST.Name.Value] = argAST
44 }
45 }
46 results := map[string]interface{}{}
47 for _, argDef := range argDefs {
48
49 name := argDef.PrivateName
50 var valueAST ast.Value
51 if argAST, ok := argASTMap[name]; ok {
52 valueAST = argAST.Value
53 }
54 value := valueFromAST(valueAST, argDef.Type, variableVariables)
55 if isNullish(value) {
56 value = argDef.DefaultValue
57 }
58 if !isNullish(value) {
59 results[name] = value
60 }
61 }
62 return results, nil
63}
64
65// Given a variable definition, and any value of input, return a value which
66// adheres to the variable definition, or throw an error.
67func getVariableValue(schema Schema, definitionAST *ast.VariableDefinition, input interface{}) (interface{}, error) {
68 ttype, err := typeFromAST(schema, definitionAST.Type)
69 if err != nil {
70 return nil, err
71 }
72 variable := definitionAST.Variable
73
74 if ttype == nil || !IsInputType(ttype) {
75 return "", gqlerrors.NewError(
76 fmt.Sprintf(`Variable "$%v" expected value of type `+
77 `"%v" which cannot be used as an input type.`, variable.Name.Value, printer.Print(definitionAST.Type)),
78 []ast.Node{definitionAST},
79 "",
80 nil,
81 []int{},
82 nil,
83 )
84 }
85
86 isValid, messages := isValidInputValue(input, ttype)
87 if isValid {
88 if isNullish(input) {
89 defaultValue := definitionAST.DefaultValue
90 if defaultValue != nil {
91 variables := map[string]interface{}{}
92 val := valueFromAST(defaultValue, ttype, variables)
93 return val, nil
94 }
95 }
96 return coerceValue(ttype, input), nil
97 }
98 if isNullish(input) {
99 return "", gqlerrors.NewError(
100 fmt.Sprintf(`Variable "$%v" of required type `+
101 `"%v" was not provided.`, variable.Name.Value, printer.Print(definitionAST.Type)),
102 []ast.Node{definitionAST},
103 "",
104 nil,
105 []int{},
106 nil,
107 )
108 }
109 // convert input interface into string for error message
110 inputStr := ""
111 b, err := json.Marshal(input)
112 if err == nil {
113 inputStr = string(b)
114 }
115 messagesStr := ""
116 if len(messages) > 0 {
117 messagesStr = "\n" + strings.Join(messages, "\n")
118 }
119
120 return "", gqlerrors.NewError(
121 fmt.Sprintf(`Variable "$%v" got invalid value `+
122 `%v.%v`, variable.Name.Value, inputStr, messagesStr),
123 []ast.Node{definitionAST},
124 "",
125 nil,
126 []int{},
127 nil,
128 )
129}
130
131// Given a type and any value, return a runtime value coerced to match the type.
132func coerceValue(ttype Input, value interface{}) interface{} {
133 if ttype, ok := ttype.(*NonNull); ok {
134 return coerceValue(ttype.OfType, value)
135 }
136 if isNullish(value) {
137 return nil
138 }
139 if ttype, ok := ttype.(*List); ok {
140 itemType := ttype.OfType
141 valType := reflect.ValueOf(value)
142 if valType.Kind() == reflect.Slice {
143 values := []interface{}{}
144 for i := 0; i < valType.Len(); i++ {
145 val := valType.Index(i).Interface()
146 v := coerceValue(itemType, val)
147 values = append(values, v)
148 }
149 return values
150 }
151 val := coerceValue(itemType, value)
152 return []interface{}{val}
153 }
154 if ttype, ok := ttype.(*InputObject); ok {
155
156 valueMap, ok := value.(map[string]interface{})
157 if !ok {
158 valueMap = map[string]interface{}{}
159 }
160
161 obj := map[string]interface{}{}
162 for fieldName, field := range ttype.Fields() {
163 value, _ := valueMap[fieldName]
164 fieldValue := coerceValue(field.Type, value)
165 if isNullish(fieldValue) {
166 fieldValue = field.DefaultValue
167 }
168 if !isNullish(fieldValue) {
169 obj[fieldName] = fieldValue
170 }
171 }
172 return obj
173 }
174
175 switch ttype := ttype.(type) {
176 case *Scalar:
177 parsed := ttype.ParseValue(value)
178 if !isNullish(parsed) {
179 return parsed
180 }
181 case *Enum:
182 parsed := ttype.ParseValue(value)
183 if !isNullish(parsed) {
184 return parsed
185 }
186 }
187 return nil
188}
189
190// graphql-js/src/utilities.js`
191// TODO: figure out where to organize utils
192// TODO: change to *Schema
193func typeFromAST(schema Schema, inputTypeAST ast.Type) (Type, error) {
194 switch inputTypeAST := inputTypeAST.(type) {
195 case *ast.List:
196 innerType, err := typeFromAST(schema, inputTypeAST.Type)
197 if err != nil {
198 return nil, err
199 }
200 return NewList(innerType), nil
201 case *ast.NonNull:
202 innerType, err := typeFromAST(schema, inputTypeAST.Type)
203 if err != nil {
204 return nil, err
205 }
206 return NewNonNull(innerType), nil
207 case *ast.Named:
208 nameValue := ""
209 if inputTypeAST.Name != nil {
210 nameValue = inputTypeAST.Name.Value
211 }
212 ttype := schema.Type(nameValue)
213 return ttype, nil
214 default:
215 return nil, invariant(inputTypeAST.GetKind() == kinds.Named, "Must be a named type.")
216 }
217}
218
219// isValidInputValue alias isValidJSValue
220// Given a value and a GraphQL type, determine if the value will be
221// accepted for that type. This is primarily useful for validating the
222// runtime values of query variables.
223func isValidInputValue(value interface{}, ttype Input) (bool, []string) {
224 if ttype, ok := ttype.(*NonNull); ok {
225 if isNullish(value) {
226 if ttype.OfType.Name() != "" {
227 return false, []string{fmt.Sprintf(`Expected "%v!", found null.`, ttype.OfType.Name())}
228 }
229 return false, []string{"Expected non-null value, found null."}
230 }
231 return isValidInputValue(value, ttype.OfType)
232 }
233
234 if isNullish(value) {
235 return true, nil
236 }
237
238 switch ttype := ttype.(type) {
239 case *List:
240 itemType := ttype.OfType
241 valType := reflect.ValueOf(value)
242 if valType.Kind() == reflect.Ptr {
243 valType = valType.Elem()
244 }
245 if valType.Kind() == reflect.Slice {
246 messagesReduce := []string{}
247 for i := 0; i < valType.Len(); i++ {
248 val := valType.Index(i).Interface()
249 _, messages := isValidInputValue(val, itemType)
250 for idx, message := range messages {
251 messagesReduce = append(messagesReduce, fmt.Sprintf(`In element #%v: %v`, idx+1, message))
252 }
253 }
254 return (len(messagesReduce) == 0), messagesReduce
255 }
256 return isValidInputValue(value, itemType)
257
258 case *InputObject:
259 messagesReduce := []string{}
260
261 valueMap, ok := value.(map[string]interface{})
262 if !ok {
263 return false, []string{fmt.Sprintf(`Expected "%v", found not an object.`, ttype.Name())}
264 }
265 fields := ttype.Fields()
266
267 // to ensure stable order of field evaluation
268 fieldNames := []string{}
269 valueMapFieldNames := []string{}
270
271 for fieldName := range fields {
272 fieldNames = append(fieldNames, fieldName)
273 }
274 sort.Strings(fieldNames)
275
276 for fieldName := range valueMap {
277 valueMapFieldNames = append(valueMapFieldNames, fieldName)
278 }
279 sort.Strings(valueMapFieldNames)
280
281 // Ensure every provided field is defined.
282 for _, fieldName := range valueMapFieldNames {
283 if _, ok := fields[fieldName]; !ok {
284 messagesReduce = append(messagesReduce, fmt.Sprintf(`In field "%v": Unknown field.`, fieldName))
285 }
286 }
287
288 // Ensure every defined field is valid.
289 for _, fieldName := range fieldNames {
290 _, messages := isValidInputValue(valueMap[fieldName], fields[fieldName].Type)
291 if messages != nil {
292 for _, message := range messages {
293 messagesReduce = append(messagesReduce, fmt.Sprintf(`In field "%v": %v`, fieldName, message))
294 }
295 }
296 }
297 return (len(messagesReduce) == 0), messagesReduce
298 }
299
300 switch ttype := ttype.(type) {
301 case *Scalar:
302 parsedVal := ttype.ParseValue(value)
303 if isNullish(parsedVal) {
304 return false, []string{fmt.Sprintf(`Expected type "%v", found "%v".`, ttype.Name(), value)}
305 }
306 return true, nil
307
308 case *Enum:
309 parsedVal := ttype.ParseValue(value)
310 if isNullish(parsedVal) {
311 return false, []string{fmt.Sprintf(`Expected type "%v", found "%v".`, ttype.Name(), value)}
312 }
313 return true, nil
314 }
315 return true, nil
316}
317
318// Returns true if a value is null, undefined, or NaN.
319func isNullish(value interface{}) bool {
320 if value, ok := value.(*string); ok {
321 if value == nil {
322 return true
323 }
324 return *value == ""
325 }
326 if value, ok := value.(int); ok {
327 return math.IsNaN(float64(value))
328 }
329 if value, ok := value.(*int); ok {
330 if value == nil {
331 return true
332 }
333 return math.IsNaN(float64(*value))
334 }
335 if value, ok := value.(float32); ok {
336 return math.IsNaN(float64(value))
337 }
338 if value, ok := value.(*float32); ok {
339 if value == nil {
340 return true
341 }
342 return math.IsNaN(float64(*value))
343 }
344 if value, ok := value.(float64); ok {
345 return math.IsNaN(value)
346 }
347 if value, ok := value.(*float64); ok {
348 if value == nil {
349 return true
350 }
351 return math.IsNaN(*value)
352 }
353 return value == nil
354}
355
356/**
357 * Produces a value given a GraphQL Value AST.
358 *
359 * A GraphQL type must be provided, which will be used to interpret different
360 * GraphQL Value literals.
361 *
362 * | GraphQL Value | JSON Value |
363 * | -------------------- | ------------- |
364 * | Input Object | Object |
365 * | List | Array |
366 * | Boolean | Boolean |
367 * | String / Enum Value | String |
368 * | Int / Float | Number |
369 *
370 */
371func valueFromAST(valueAST ast.Value, ttype Input, variables map[string]interface{}) interface{} {
372
373 if ttype, ok := ttype.(*NonNull); ok {
374 val := valueFromAST(valueAST, ttype.OfType, variables)
375 return val
376 }
377
378 if valueAST == nil {
379 return nil
380 }
381
382 if valueAST, ok := valueAST.(*ast.Variable); ok && valueAST.Kind == kinds.Variable {
383 if valueAST.Name == nil {
384 return nil
385 }
386 if variables == nil {
387 return nil
388 }
389 variableName := valueAST.Name.Value
390 variableVal, ok := variables[variableName]
391 if !ok {
392 return nil
393 }
394 // Note: we're not doing any checking that this variable is correct. We're
395 // assuming that this query has been validated and the variable usage here
396 // is of the correct type.
397 return variableVal
398 }
399
400 if ttype, ok := ttype.(*List); ok {
401 itemType := ttype.OfType
402 if valueAST, ok := valueAST.(*ast.ListValue); ok && valueAST.Kind == kinds.ListValue {
403 values := []interface{}{}
404 for _, itemAST := range valueAST.Values {
405 v := valueFromAST(itemAST, itemType, variables)
406 values = append(values, v)
407 }
408 return values
409 }
410 v := valueFromAST(valueAST, itemType, variables)
411 return []interface{}{v}
412 }
413
414 if ttype, ok := ttype.(*InputObject); ok {
415 valueAST, ok := valueAST.(*ast.ObjectValue)
416 if !ok {
417 return nil
418 }
419 fieldASTs := map[string]*ast.ObjectField{}
420 for _, fieldAST := range valueAST.Fields {
421 if fieldAST.Name == nil {
422 continue
423 }
424 fieldName := fieldAST.Name.Value
425 fieldASTs[fieldName] = fieldAST
426
427 }
428 obj := map[string]interface{}{}
429 for fieldName, field := range ttype.Fields() {
430 fieldAST, ok := fieldASTs[fieldName]
431 fieldValue := field.DefaultValue
432 if !ok || fieldAST == nil {
433 if fieldValue == nil {
434 continue
435 }
436 } else {
437 fieldValue = valueFromAST(fieldAST.Value, field.Type, variables)
438 }
439 if isNullish(fieldValue) {
440 fieldValue = field.DefaultValue
441 }
442 if !isNullish(fieldValue) {
443 obj[fieldName] = fieldValue
444 }
445 }
446 return obj
447 }
448
449 switch ttype := ttype.(type) {
450 case *Scalar:
451 parsed := ttype.ParseLiteral(valueAST)
452 if !isNullish(parsed) {
453 return parsed
454 }
455 case *Enum:
456 parsed := ttype.ParseLiteral(valueAST)
457 if !isNullish(parsed) {
458 return parsed
459 }
460 }
461 return nil
462}
463
464func invariant(condition bool, message string) error {
465 if !condition {
466 return gqlerrors.NewFormattedError(message)
467 }
468 return nil
469}
470
471func invariantf(condition bool, format string, a ...interface{}) error {
472 if !condition {
473 return gqlerrors.NewFormattedError(fmt.Sprintf(format, a...))
474 }
475 return nil
476}