Skip to content

Commit 32f1c98

Browse files
Mikempenick
authored andcommitted
Add support for null literals
1 parent 02caa89 commit 32f1c98

File tree

9 files changed

+73
-10
lines changed

9 files changed

+73
-10
lines changed

definition.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ func GetNullable(ttype Type) Nullable {
158158
return ttype
159159
}
160160

161+
// NullValue to be able to detect if a value is set to null or if it is omitted
162+
type NullValue struct {}
163+
161164
// Named interface for types that do not include modifiers like List or NonNull.
162165
type Named interface {
163166
String() string

language/ast/node.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var _ Node = (*IntValue)(nil)
2222
var _ Node = (*FloatValue)(nil)
2323
var _ Node = (*StringValue)(nil)
2424
var _ Node = (*BooleanValue)(nil)
25+
var _ Node = (*NullValue)(nil)
2526
var _ Node = (*EnumValue)(nil)
2627
var _ Node = (*ListValue)(nil)
2728
var _ Node = (*ObjectValue)(nil)

language/ast/values.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var _ Value = (*IntValue)(nil)
1616
var _ Value = (*FloatValue)(nil)
1717
var _ Value = (*StringValue)(nil)
1818
var _ Value = (*BooleanValue)(nil)
19+
var _ Value = (*NullValue)(nil)
1920
var _ Value = (*EnumValue)(nil)
2021
var _ Value = (*ListValue)(nil)
2122
var _ Value = (*ObjectValue)(nil)
@@ -172,6 +173,34 @@ func (v *BooleanValue) GetValue() interface{} {
172173
return v.Value
173174
}
174175

176+
// NullValue implements Node, Value
177+
type NullValue struct {
178+
Kind string
179+
Loc *Location
180+
Value interface{}
181+
}
182+
183+
func NewNullValue(v *NullValue) *NullValue {
184+
185+
return &NullValue{
186+
Kind: kinds.NullValue,
187+
Loc: v.Loc,
188+
Value: v.Value,
189+
}
190+
}
191+
192+
func (v *NullValue) GetKind() string {
193+
return v.Kind
194+
}
195+
196+
func (v *NullValue) GetLoc() *Location {
197+
return v.Loc
198+
}
199+
200+
func (v *NullValue) GetValue() interface{} {
201+
return nil
202+
}
203+
175204
// EnumValue implements Node, Value
176205
type EnumValue struct {
177206
Kind string

language/kinds/kinds.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
FloatValue = "FloatValue"
2424
StringValue = "StringValue"
2525
BooleanValue = "BooleanValue"
26+
NullValue = "NullValue"
2627
EnumValue = "EnumValue"
2728
ListValue = "ListValue"
2829
ObjectValue = "ObjectValue"

language/parser/parser.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,15 @@ func parseValueLiteral(parser *Parser, isConst bool) (ast.Value, error) {
610610
Value: value,
611611
Loc: loc(parser, token.Start),
612612
}), nil
613-
} else if token.Value != "null" {
613+
} else if token.Value == "null" {
614+
if err := advance(parser); err != nil {
615+
return nil, err
616+
}
617+
return ast.NewNullValue(&ast.NullValue{
618+
Value: nil,
619+
Loc: loc(parser, token.Start),
620+
}), nil
621+
} else {
614622
if err := advance(parser); err != nil {
615623
return nil, err
616624
}

language/parser/parser_test.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,6 @@ func TestDoesNotAcceptFragmentsSpreadOfOn(t *testing.T) {
183183
testErrorMessage(t, test)
184184
}
185185

186-
func TestDoesNotAllowNullAsValue(t *testing.T) {
187-
test := errorMessageTest{
188-
`{ fieldWithNullableStringInput(input: null) }'`,
189-
`Syntax Error GraphQL (1:39) Unexpected Name "null"`,
190-
false,
191-
}
192-
testErrorMessage(t, test)
193-
}
194-
195186
func TestParsesMultiByteCharacters_Unicode(t *testing.T) {
196187

197188
doc := `
@@ -367,6 +358,7 @@ func TestAllowsNonKeywordsAnywhereNameIsAllowed(t *testing.T) {
367358
"subscription",
368359
"true",
369360
"false",
361+
"null",
370362
}
371363
for _, keyword := range nonKeywords {
372364
fragmentName := keyword

language/printer/printer.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,15 @@ var printDocASTReducer = map[string]visitor.VisitFunc{
367367
}
368368
return visitor.ActionNoChange, nil
369369
},
370+
"NullValue": func(p visitor.VisitFuncParams) (string, interface{}) {
371+
switch node := p.Node.(type) {
372+
case *ast.NullValue:
373+
return visitor.ActionUpdate, fmt.Sprintf("%v", node.Value)
374+
case map[string]interface{}:
375+
return visitor.ActionUpdate, getMapValueString(node, "Value")
376+
}
377+
return visitor.ActionNoChange, nil
378+
},
370379
"EnumValue": func(p visitor.VisitFuncParams) (string, interface{}) {
371380
switch node := p.Node.(type) {
372381
case *ast.EnumValue:

rules.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,12 @@ func isValidLiteralValue(ttype Input, valueAST ast.Value) (bool, []string) {
17301730
return true, nil
17311731
}
17321732

1733+
// This function only tests literals, and assumes variables will provide
1734+
// values of the correct type.
1735+
if valueAST.GetKind() == kinds.NullValue {
1736+
return true, nil
1737+
}
1738+
17331739
// This function only tests literals, and assumes variables will provide
17341740
// values of the correct type.
17351741
if valueAST.GetKind() == kinds.Variable {

scalars.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ var Int = NewScalar(ScalarConfig{
162162
if intValue, err := strconv.Atoi(valueAST.Value); err == nil {
163163
return intValue
164164
}
165+
case *ast.NullValue:
166+
return NullValue{}
165167
}
166168
return nil
167169
},
@@ -299,6 +301,8 @@ var Float = NewScalar(ScalarConfig{
299301
if floatValue, err := strconv.ParseFloat(valueAST.Value, 64); err == nil {
300302
return floatValue
301303
}
304+
case *ast.NullValue:
305+
return NullValue{}
302306
}
303307
return nil
304308
},
@@ -326,7 +330,10 @@ var String = NewScalar(ScalarConfig{
326330
switch valueAST := valueAST.(type) {
327331
case *ast.StringValue:
328332
return valueAST.Value
333+
case *ast.NullValue:
334+
return NullValue{}
329335
}
336+
330337
return nil
331338
},
332339
})
@@ -485,6 +492,8 @@ var Boolean = NewScalar(ScalarConfig{
485492
switch valueAST := valueAST.(type) {
486493
case *ast.BooleanValue:
487494
return valueAST.Value
495+
case *ast.NullValue:
496+
return NullValue{}
488497
}
489498
return nil
490499
},
@@ -506,6 +515,8 @@ var ID = NewScalar(ScalarConfig{
506515
return valueAST.Value
507516
case *ast.StringValue:
508517
return valueAST.Value
518+
case *ast.NullValue:
519+
return NullValue{}
509520
}
510521
return nil
511522
},
@@ -564,6 +575,9 @@ var DateTime = NewScalar(ScalarConfig{
564575
switch valueAST := valueAST.(type) {
565576
case *ast.StringValue:
566577
return unserializeDateTime(valueAST.Value)
578+
return valueAST.Value
579+
case *ast.NullValue:
580+
return NullValue{}
567581
}
568582
return nil
569583
},

0 commit comments

Comments
 (0)