Skip to content

Commit dba344b

Browse files
committed
Merge branch 'master' of github.com:mpenick/graphql into nullish
2 parents f2b39ca + 50ac617 commit dba344b

12 files changed

+409
-36
lines changed

graphql_test.go

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,300 @@ func TestEmptyStringIsNotNull(t *testing.T) {
268268
t.Errorf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
269269
}
270270
}
271+
272+
func TestNullLiteralArguments(t *testing.T) {
273+
checkForNull := func(p graphql.ResolveParams) (interface{}, error) {
274+
arg, ok := p.Args["arg"]
275+
if !ok || arg != nil {
276+
t.Errorf("expected null for input arg, got %#v", arg)
277+
}
278+
return "yay", nil
279+
}
280+
schema, err := graphql.NewSchema(graphql.SchemaConfig{
281+
Query: graphql.NewObject(graphql.ObjectConfig{
282+
Name: "Query",
283+
Fields: graphql.Fields{
284+
"checkNullStringArg": &graphql.Field{
285+
Type: graphql.String,
286+
Args: graphql.FieldConfigArgument{
287+
"arg": &graphql.ArgumentConfig{Type: graphql.String},
288+
},
289+
Resolve: checkForNull,
290+
},
291+
"checkNullIntArg": &graphql.Field{
292+
Type: graphql.String,
293+
Args: graphql.FieldConfigArgument{
294+
"arg": &graphql.ArgumentConfig{Type: graphql.Int},
295+
},
296+
Resolve: checkForNull,
297+
},
298+
"checkNullBooleanArg": &graphql.Field{
299+
Type: graphql.String,
300+
Args: graphql.FieldConfigArgument{
301+
"arg": &graphql.ArgumentConfig{Type: graphql.Boolean},
302+
},
303+
Resolve: checkForNull,
304+
},
305+
"checkNullListArg": &graphql.Field{
306+
Type: graphql.String,
307+
Args: graphql.FieldConfigArgument{
308+
"arg": &graphql.ArgumentConfig{Type: graphql.NewList(graphql.String)},
309+
},
310+
Resolve: checkForNull,
311+
},
312+
"checkNullInputObjectArg": &graphql.Field{
313+
Type: graphql.String,
314+
Args: graphql.FieldConfigArgument{
315+
"arg": &graphql.ArgumentConfig{Type: graphql.NewInputObject(
316+
graphql.InputObjectConfig{
317+
Name: "InputType",
318+
Fields: graphql.InputObjectConfigFieldMap{
319+
"field1": {Type: graphql.String},
320+
"field2": {Type: graphql.Int},
321+
},
322+
})},
323+
},
324+
Resolve: checkForNull,
325+
},
326+
},
327+
}),
328+
})
329+
if err != nil {
330+
t.Fatalf("wrong result, unexpected errors: %v", err.Error())
331+
}
332+
query := `{ checkNullStringArg(arg:null) checkNullIntArg(arg:null) checkNullBooleanArg(arg:null) checkNullListArg(arg:null) checkNullInputObjectArg(arg:null) }`
333+
334+
result := graphql.Do(graphql.Params{
335+
Schema: schema,
336+
RequestString: query,
337+
})
338+
if len(result.Errors) > 0 {
339+
t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
340+
}
341+
expected := map[string]interface{}{
342+
"checkNullStringArg": "yay", "checkNullIntArg": "yay",
343+
"checkNullBooleanArg": "yay", "checkNullListArg": "yay",
344+
"checkNullInputObjectArg": "yay"}
345+
if !reflect.DeepEqual(result.Data, expected) {
346+
t.Errorf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
347+
}
348+
}
349+
350+
func TestNullLiteralDefaultVariableValue(t *testing.T) {
351+
checkForNull := func(p graphql.ResolveParams) (interface{}, error) {
352+
arg, ok := p.Args["arg"]
353+
if !ok || arg != nil {
354+
t.Errorf("expected null for input arg, got %#v", arg)
355+
}
356+
return "yay", nil
357+
}
358+
schema, err := graphql.NewSchema(graphql.SchemaConfig{
359+
Query: graphql.NewObject(graphql.ObjectConfig{
360+
Name: "Query",
361+
Fields: graphql.Fields{
362+
"checkNullStringArg": &graphql.Field{
363+
Type: graphql.String,
364+
Args: graphql.FieldConfigArgument{
365+
"arg": &graphql.ArgumentConfig{Type: graphql.String},
366+
},
367+
Resolve: checkForNull,
368+
},
369+
},
370+
}),
371+
})
372+
if err != nil {
373+
t.Fatalf("wrong result, unexpected errors: %v", err.Error())
374+
}
375+
query := `query Test($value: String = null) { checkNullStringArg(arg: $value) }`
376+
377+
result := graphql.Do(graphql.Params{
378+
Schema: schema,
379+
RequestString: query,
380+
VariableValues: map[string]interface{}{"value2": nil},
381+
})
382+
if len(result.Errors) > 0 {
383+
t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
384+
}
385+
expected := map[string]interface{}{ "checkNullStringArg": "yay", }
386+
if !reflect.DeepEqual(result.Data, expected) {
387+
t.Errorf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
388+
}
389+
}
390+
391+
func TestNullLiteralVariables(t *testing.T) {
392+
checkForNull := func(p graphql.ResolveParams) (interface{}, error) {
393+
arg, ok := p.Args["arg"]
394+
if !ok || arg != nil {
395+
t.Errorf("expected null for input arg, got %#v", arg)
396+
}
397+
return "yay", nil
398+
}
399+
schema, err := graphql.NewSchema(graphql.SchemaConfig{
400+
Query: graphql.NewObject(graphql.ObjectConfig{
401+
Name: "Query",
402+
Fields: graphql.Fields{
403+
"checkNullStringArg": &graphql.Field{
404+
Type: graphql.String,
405+
Args: graphql.FieldConfigArgument{
406+
"arg": &graphql.ArgumentConfig{Type: graphql.String},
407+
},
408+
Resolve: checkForNull,
409+
},
410+
},
411+
}),
412+
})
413+
if err != nil {
414+
t.Fatalf("wrong result, unexpected errors: %v", err.Error())
415+
}
416+
query := `query Test($value: String) { checkNullStringArg(arg: $value) }`
417+
418+
result := graphql.Do(graphql.Params{
419+
Schema: schema,
420+
RequestString: query,
421+
VariableValues: map[string]interface{}{"value": nil},
422+
})
423+
if len(result.Errors) > 0 {
424+
t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
425+
}
426+
expected := map[string]interface{}{ "checkNullStringArg": "yay", }
427+
if !reflect.DeepEqual(result.Data, expected) {
428+
t.Errorf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
429+
}
430+
}
431+
432+
func TestErrorNullLiteralForNotNullArgument(t *testing.T) {
433+
checkNotCalled := func(p graphql.ResolveParams) (interface{}, error) {
434+
t.Error("shouldn't have been called")
435+
return nil, nil
436+
}
437+
schema, err := graphql.NewSchema(graphql.SchemaConfig{
438+
Query: graphql.NewObject(graphql.ObjectConfig{
439+
Name: "Query",
440+
Fields: graphql.Fields{
441+
"checkNotNullArg": &graphql.Field{
442+
Type: graphql.String,
443+
Args: graphql.FieldConfigArgument{
444+
"arg": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String) },
445+
},
446+
Resolve: checkNotCalled,
447+
},
448+
},
449+
}),
450+
})
451+
if err != nil {
452+
t.Fatalf("wrong result, unexpected errors: %v", err.Error())
453+
}
454+
query := `{ checkNotNullArg(arg:null) }`
455+
456+
result := graphql.Do(graphql.Params{
457+
Schema: schema,
458+
RequestString: query,
459+
})
460+
461+
if len(result.Errors) == 0 {
462+
t.Fatalf("expected errors, got: %v", result)
463+
}
464+
465+
expectedMessage := `Argument "arg" has invalid value <nil>.
466+
Expected "String!", found null.`;
467+
468+
if result.Errors[0].Message != expectedMessage {
469+
t.Fatalf("unexpected error.\nexpected:\n%s\ngot:\n%s\n", expectedMessage, result.Errors[0].Message)
470+
}
471+
}
472+
473+
func TestNullInputObjectFields(t *testing.T) {
474+
checkForNull := func(p graphql.ResolveParams) (interface{}, error) {
475+
arg := p.Args["arg"]
476+
expectedValue := map[string]interface{}{ "field1": nil, "field2": nil, "field3": nil, "field4" : "abc", "field5": 42, "field6": true}
477+
if value, ok := arg.(map[string]interface{}); !ok {
478+
t.Errorf("expected map[string]interface{} for input arg, got %#v", arg)
479+
} else if !reflect.DeepEqual(expectedValue, value) {
480+
t.Errorf("unexpected input object, diff: %v", testutil.Diff(expectedValue, value))
481+
}
482+
return "yay", nil
483+
}
484+
schema, err := graphql.NewSchema(graphql.SchemaConfig{
485+
Query: graphql.NewObject(graphql.ObjectConfig{
486+
Name: "Query",
487+
Fields: graphql.Fields{
488+
"checkNullInputObjectFields": &graphql.Field{
489+
Type: graphql.String,
490+
Args: graphql.FieldConfigArgument{
491+
"arg": &graphql.ArgumentConfig{Type: graphql.NewInputObject(
492+
graphql.InputObjectConfig{
493+
Name: "InputType",
494+
Fields: graphql.InputObjectConfigFieldMap{
495+
"field1": {Type: graphql.String},
496+
"field2": {Type: graphql.Int},
497+
"field3": {Type: graphql.Boolean},
498+
"field4": {Type: graphql.String},
499+
"field5": {Type: graphql.Int},
500+
"field6": {Type: graphql.Boolean},
501+
},
502+
})},
503+
},
504+
Resolve: checkForNull,
505+
},
506+
},
507+
}),
508+
})
509+
if err != nil {
510+
t.Fatalf("wrong result, unexpected errors: %v", err.Error())
511+
}
512+
query := `{ checkNullInputObjectFields(arg: {field1: null, field2: null, field3: null, field4: "abc", field5: 42, field6: true }) }`
513+
514+
result := graphql.Do(graphql.Params{
515+
Schema: schema,
516+
RequestString: query,
517+
})
518+
if len(result.Errors) > 0 {
519+
t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
520+
}
521+
expected := map[string]interface{}{ "checkNullInputObjectFields": "yay" }
522+
if !reflect.DeepEqual(result.Data, expected) {
523+
t.Errorf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
524+
}
525+
}
526+
527+
func TestErrorNullInList(t *testing.T) {
528+
checkNotCalled := func(p graphql.ResolveParams) (interface{}, error) {
529+
t.Error("shouldn't have been called")
530+
return nil, nil
531+
}
532+
schema, err := graphql.NewSchema(graphql.SchemaConfig{
533+
Query: graphql.NewObject(graphql.ObjectConfig{
534+
Name: "Query",
535+
Fields: graphql.Fields{
536+
"checkNotNullInListArg": &graphql.Field{
537+
Type: graphql.String,
538+
Args: graphql.FieldConfigArgument{
539+
"arg": &graphql.ArgumentConfig{Type: graphql.NewList(graphql.String) },
540+
},
541+
Resolve: checkNotCalled,
542+
},
543+
},
544+
}),
545+
})
546+
if err != nil {
547+
t.Fatalf("wrong result, unexpected errors: %v", err.Error())
548+
}
549+
query := `{ checkNotNullInListArg(arg: [null, null]) }`
550+
551+
result := graphql.Do(graphql.Params{
552+
Schema: schema,
553+
RequestString: query,
554+
})
555+
556+
if len(result.Errors) == 0 {
557+
t.Fatalf("expected errors, got: %v", result)
558+
}
559+
560+
expectedMessage := `Argument "arg" has invalid value [<nil>, <nil>].
561+
In element #1: Unexpected null literal.
562+
In element #2: Unexpected null literal.`
563+
564+
if result.Errors[0].Message != expectedMessage {
565+
t.Fatalf("unexpected error.\nexpected:\n%s\ngot:\n%s\n", expectedMessage, result.Errors[0].Message)
566+
}
567+
}

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
@@ -606,7 +606,15 @@ func parseValueLiteral(parser *Parser, isConst bool) (ast.Value, error) {
606606
Value: value,
607607
Loc: loc(parser, token.Start),
608608
}), nil
609-
} else if token.Value != "null" {
609+
} else if token.Value == "null" {
610+
if err := advance(parser); err != nil {
611+
return nil, err
612+
}
613+
return ast.NewNullValue(&ast.NullValue{
614+
Value: nil,
615+
Loc: loc(parser, token.Start),
616+
}), nil
617+
} else {
610618
if err := advance(parser); err != nil {
611619
return nil, err
612620
}

0 commit comments

Comments
 (0)