@@ -14,6 +14,9 @@ import (
1414 "github.com/graphql-go/graphql/language/printer"
1515)
1616
17+ // Used to detect the difference between a "null" literal and not present
18+ type nullValue struct {}
19+
1720// Prepares an object map of variableValues of the correct type based on the
1821// provided variable definitions and arbitrary input. If the input cannot be
1922// parsed to match the variable definitions, a GraphQLError will be returned.
@@ -27,7 +30,7 @@ func getVariableValues(
2730 continue
2831 }
2932 varName := defAST .Variable .Name .Value
30- if varValue , err := getVariableValue (schema , defAST , inputs [ varName ] ); err != nil {
33+ if varValue , err := getVariableValue (schema , defAST , getValueOrNull ( inputs , varName ) ); err != nil {
3134 return values , err
3235 } else {
3336 values [varName ] = varValue
@@ -36,6 +39,25 @@ func getVariableValues(
3639 return values , nil
3740}
3841
42+ func getValueOrNull (values map [string ]interface {}, name string ) interface {} {
43+ if tmp , ok := values [name ]; ok { // Is present
44+ if tmp == nil {
45+ return nullValue {} // Null value
46+ } else {
47+ return tmp
48+ }
49+ }
50+ return nil // Not present
51+ }
52+
53+ func addValueOrNull (values map [string ]interface {}, name string , value interface {}) {
54+ if _ , ok := value .(nullValue ); ok { // Null value
55+ values [name ] = nil
56+ } else if ! isNullish (value ) { // Not present
57+ values [name ] = value
58+ }
59+ }
60+
3961// Prepares an object map of argument values given a list of argument
4062// definitions and list of argument AST nodes.
4163func getArgumentValues (
@@ -60,9 +82,7 @@ func getArgumentValues(
6082 if tmp = valueFromAST (value , argDef .Type , variableValues ); isNullish (tmp ) {
6183 tmp = argDef .DefaultValue
6284 }
63- if ! isNullish (tmp ) {
64- results [argDef .PrivateName ] = tmp
65- }
85+ addValueOrNull (results , argDef .PrivateName , tmp )
6686 }
6787 return results
6888}
@@ -97,7 +117,7 @@ func getVariableValue(schema Schema, definitionAST *ast.VariableDefinition, inpu
97117 }
98118 return coerceValue (ttype , input ), nil
99119 }
100- if isNullish (input ) {
120+ if _ , ok := input .( nullValue ); ok || isNullish (input ) {
101121 return "" , gqlerrors .NewError (
102122 fmt .Sprintf (`Variable "$%v" of required type ` +
103123 `"%v" was not provided.` , variable .Name .Value , printer .Print (definitionAST .Type )),
@@ -134,6 +154,11 @@ func coerceValue(ttype Input, value interface{}) interface{} {
134154 if isNullish (value ) {
135155 return nil
136156 }
157+
158+ if _ , ok := value .(nullValue ); ok {
159+ return nullValue {}
160+ }
161+
137162 switch ttype := ttype .(type ) {
138163 case * NonNull :
139164 return coerceValue (ttype .OfType , value )
@@ -156,13 +181,11 @@ func coerceValue(ttype Input, value interface{}) interface{} {
156181 }
157182
158183 for name , field := range ttype .Fields () {
159- fieldValue := coerceValue (field .Type , valueMap [ name ] )
184+ fieldValue := coerceValue (field .Type , getValueOrNull ( valueMap , name ) )
160185 if isNullish (fieldValue ) {
161186 fieldValue = field .DefaultValue
162187 }
163- if ! isNullish (fieldValue ) {
164- obj [name ] = fieldValue
165- }
188+ addValueOrNull (obj , name , fieldValue )
166189 }
167190 return obj
168191 case * Scalar :
@@ -212,7 +235,7 @@ func typeFromAST(schema Schema, inputTypeAST ast.Type) (Type, error) {
212235// accepted for that type. This is primarily useful for validating the
213236// runtime values of query variables.
214237func isValidInputValue (value interface {}, ttype Input ) (bool , []string ) {
215- if isNullish (value ) {
238+ if _ , ok := value .( nullValue ); ok || isNullish (value ) {
216239 if ttype , ok := ttype .(* NonNull ); ok {
217240 if ttype .OfType .Name () != "" {
218241 return false , []string {fmt .Sprintf (`Expected "%v!", found null.` , ttype .OfType .Name ())}
@@ -233,9 +256,14 @@ func isValidInputValue(value interface{}, ttype Input) (bool, []string) {
233256 messagesReduce := []string {}
234257 for i := 0 ; i < valType .Len (); i ++ {
235258 val := valType .Index (i ).Interface ()
236- _ , messages := isValidInputValue (val , ttype .OfType )
237- for idx , message := range messages {
238- messagesReduce = append (messagesReduce , fmt .Sprintf (`In element #%v: %v` , idx + 1 , message ))
259+ var messages []string
260+ if _ , ok := val .(nullValue ); ok {
261+ messages = []string {"Unexpected null value." }
262+ } else {
263+ _ , messages = isValidInputValue (val , ttype .OfType )
264+ }
265+ for _ , message := range messages {
266+ messagesReduce = append (messagesReduce , fmt .Sprintf (`In element #%v: %v` , i + 1 , message ))
239267 }
240268 }
241269 return (len (messagesReduce ) == 0 ), messagesReduce
@@ -352,6 +380,11 @@ func valueFromAST(valueAST ast.Value, ttype Input, variables map[string]interfac
352380 if valueAST == nil {
353381 return nil
354382 }
383+
384+ if valueAST .GetKind () == kinds .NullValue {
385+ return nullValue {}
386+ }
387+
355388 // precedence: value > type
356389 if valueAST , ok := valueAST .(* ast.Variable ); ok {
357390 if valueAST .Name == nil || variables == nil {
@@ -398,9 +431,7 @@ func valueFromAST(valueAST ast.Value, ttype Input, variables map[string]interfac
398431 } else {
399432 value = field .DefaultValue
400433 }
401- if ! isNullish (value ) {
402- obj [name ] = value
403- }
434+ addValueOrNull (obj , name , value )
404435 }
405436 return obj
406437 case * Scalar :
0 commit comments