values.go

  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}