@@ -14,6 +14,9 @@ import (
14
14
"github.com/graphql-go/graphql/language/printer"
15
15
)
16
16
17
+ // Used to detect the difference between a "null" literal and not present
18
+ type nullValue struct {}
19
+
17
20
// Prepares an object map of variableValues of the correct type based on the
18
21
// provided variable definitions and arbitrary input. If the input cannot be
19
22
// parsed to match the variable definitions, a GraphQLError will be returned.
@@ -27,7 +30,7 @@ func getVariableValues(
27
30
continue
28
31
}
29
32
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 {
31
34
return values , err
32
35
} else {
33
36
values [varName ] = varValue
@@ -36,6 +39,25 @@ func getVariableValues(
36
39
return values , nil
37
40
}
38
41
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
+
39
61
// Prepares an object map of argument values given a list of argument
40
62
// definitions and list of argument AST nodes.
41
63
func getArgumentValues (
@@ -60,9 +82,7 @@ func getArgumentValues(
60
82
if tmp = valueFromAST (value , argDef .Type , variableValues ); isNullish (tmp ) {
61
83
tmp = argDef .DefaultValue
62
84
}
63
- if ! isNullish (tmp ) {
64
- results [argDef .PrivateName ] = tmp
65
- }
85
+ addValueOrNull (results , argDef .PrivateName , tmp )
66
86
}
67
87
return results
68
88
}
@@ -97,7 +117,7 @@ func getVariableValue(schema Schema, definitionAST *ast.VariableDefinition, inpu
97
117
}
98
118
return coerceValue (ttype , input ), nil
99
119
}
100
- if isNullish (input ) {
120
+ if _ , ok := input .( nullValue ); ok || isNullish (input ) {
101
121
return "" , gqlerrors .NewError (
102
122
fmt .Sprintf (`Variable "$%v" of required type ` +
103
123
`"%v" was not provided.` , variable .Name .Value , printer .Print (definitionAST .Type )),
@@ -134,6 +154,11 @@ func coerceValue(ttype Input, value interface{}) interface{} {
134
154
if isNullish (value ) {
135
155
return nil
136
156
}
157
+
158
+ if _ , ok := value .(nullValue ); ok {
159
+ return nullValue {}
160
+ }
161
+
137
162
switch ttype := ttype .(type ) {
138
163
case * NonNull :
139
164
return coerceValue (ttype .OfType , value )
@@ -156,13 +181,11 @@ func coerceValue(ttype Input, value interface{}) interface{} {
156
181
}
157
182
158
183
for name , field := range ttype .Fields () {
159
- fieldValue := coerceValue (field .Type , valueMap [ name ] )
184
+ fieldValue := coerceValue (field .Type , getValueOrNull ( valueMap , name ) )
160
185
if isNullish (fieldValue ) {
161
186
fieldValue = field .DefaultValue
162
187
}
163
- if ! isNullish (fieldValue ) {
164
- obj [name ] = fieldValue
165
- }
188
+ addValueOrNull (obj , name , fieldValue )
166
189
}
167
190
return obj
168
191
case * Scalar :
@@ -212,7 +235,7 @@ func typeFromAST(schema Schema, inputTypeAST ast.Type) (Type, error) {
212
235
// accepted for that type. This is primarily useful for validating the
213
236
// runtime values of query variables.
214
237
func isValidInputValue (value interface {}, ttype Input ) (bool , []string ) {
215
- if isNullish (value ) {
238
+ if _ , ok := value .( nullValue ); ok || isNullish (value ) {
216
239
if ttype , ok := ttype .(* NonNull ); ok {
217
240
if ttype .OfType .Name () != "" {
218
241
return false , []string {fmt .Sprintf (`Expected "%v!", found null.` , ttype .OfType .Name ())}
@@ -233,9 +256,14 @@ func isValidInputValue(value interface{}, ttype Input) (bool, []string) {
233
256
messagesReduce := []string {}
234
257
for i := 0 ; i < valType .Len (); i ++ {
235
258
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 ))
239
267
}
240
268
}
241
269
return (len (messagesReduce ) == 0 ), messagesReduce
@@ -352,6 +380,11 @@ func valueFromAST(valueAST ast.Value, ttype Input, variables map[string]interfac
352
380
if valueAST == nil {
353
381
return nil
354
382
}
383
+
384
+ if valueAST .GetKind () == kinds .NullValue {
385
+ return nullValue {}
386
+ }
387
+
355
388
// precedence: value > type
356
389
if valueAST , ok := valueAST .(* ast.Variable ); ok {
357
390
if valueAST .Name == nil || variables == nil {
@@ -398,9 +431,7 @@ func valueFromAST(valueAST ast.Value, ttype Input, variables map[string]interfac
398
431
} else {
399
432
value = field .DefaultValue
400
433
}
401
- if ! isNullish (value ) {
402
- obj [name ] = value
403
- }
434
+ addValueOrNull (obj , name , value )
404
435
}
405
436
return obj
406
437
case * Scalar :
0 commit comments