Skip to content

Commit 714fb3e

Browse files
authored
fix: invalid enum values with value completion flag (#1104)
1 parent 978f242 commit 714fb3e

File tree

2 files changed

+139
-1
lines changed

2 files changed

+139
-1
lines changed

v2/pkg/engine/resolve/resolvable.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,11 @@ func (r *Resolvable) walkEnum(e *Enum, value *astjson.Value) bool {
10841084
* and not the second walk (which prints the data).
10851085
*/
10861086
if !r.print {
1087-
r.addErrorWithCodeAndPath(fmt.Sprintf(`Enum "%s" cannot represent value: "%s"`, e.TypeName, valueString), errorcodes.InternalServerError, e.Path)
1087+
if r.options.ApolloCompatibilityValueCompletionInExtensions {
1088+
r.renderInaccessibleEnumValueError(e)
1089+
} else {
1090+
r.addErrorWithCodeAndPath(fmt.Sprintf(`Enum "%s" cannot represent value: "%s"`, e.TypeName, valueString), errorcodes.InternalServerError, e.Path)
1091+
}
10881092
}
10891093
if e.Nullable {
10901094
return r.walkNull()

v2/pkg/engine/resolve/resolvable_test.go

+134
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,140 @@ func TestResolvable_ResolveWithErrorBubbleUpUntilData(t *testing.T) {
619619
assert.Equal(t, `{"errors":[{"message":"Cannot return null for non-nullable field 'Query.topProducts.reviews.author.name'.","path":["topProducts",0,"reviews",1,"author","name"]}],"data":null}`, out.String())
620620
}
621621

622+
func TestResolvable_InvalidEnumValues(t *testing.T) {
623+
t.Run("Invalid enum value", func(t *testing.T) {
624+
enum := `{"enum":"B"}`
625+
res := NewResolvable(ResolvableOptions{})
626+
ctx := &Context{
627+
Variables: nil,
628+
}
629+
err := res.Init(ctx, []byte(enum), ast.OperationTypeQuery)
630+
assert.NoError(t, err)
631+
assert.NotNil(t, res)
632+
object := &Object{
633+
Fields: []*Field{
634+
{
635+
Name: []byte("enum"),
636+
Value: &Enum{
637+
InaccessibleValues: make([]string, 0),
638+
Values: []string{
639+
"A",
640+
},
641+
TypeName: "Enum",
642+
Path: []string{"enum"},
643+
},
644+
},
645+
},
646+
}
647+
648+
out := &bytes.Buffer{}
649+
err = res.Resolve(context.Background(), object, nil, out)
650+
assert.NoError(t, err)
651+
assert.Equal(t, `{"errors":[{"message":"Enum \"Enum\" cannot represent value: \"B\"","path":["enum"],"extensions":{"code":"INTERNAL_SERVER_ERROR"}}],"data":null}`, out.String())
652+
})
653+
654+
t.Run("Inaccessible enum value", func(t *testing.T) {
655+
enum := `{"enum":"B"}`
656+
res := NewResolvable(ResolvableOptions{})
657+
ctx := &Context{
658+
Variables: nil,
659+
}
660+
err := res.Init(ctx, []byte(enum), ast.OperationTypeQuery)
661+
assert.NoError(t, err)
662+
assert.NotNil(t, res)
663+
object := &Object{
664+
Fields: []*Field{
665+
{
666+
Name: []byte("enum"),
667+
Value: &Enum{
668+
InaccessibleValues: []string{
669+
"B",
670+
},
671+
Values: []string{
672+
"A", "B",
673+
},
674+
TypeName: "Enum",
675+
Path: []string{"enum"},
676+
},
677+
},
678+
},
679+
}
680+
681+
out := &bytes.Buffer{}
682+
err = res.Resolve(context.Background(), object, nil, out)
683+
assert.NoError(t, err)
684+
assert.Equal(t, `{"errors":[{"message":"Invalid value found for field Query.enum.","path":["enum"],"extensions":{"code":"INVALID_GRAPHQL"}}],"data":null}`, out.String())
685+
})
686+
687+
t.Run("Invalid enum value with value completion Apollo compatibility flag", func(t *testing.T) {
688+
enum := `{"enum":"B"}`
689+
res := NewResolvable(ResolvableOptions{
690+
ApolloCompatibilityValueCompletionInExtensions: true,
691+
})
692+
ctx := &Context{
693+
Variables: nil,
694+
}
695+
err := res.Init(ctx, []byte(enum), ast.OperationTypeQuery)
696+
assert.NoError(t, err)
697+
assert.NotNil(t, res)
698+
object := &Object{
699+
Fields: []*Field{
700+
{
701+
Name: []byte("enum"),
702+
Value: &Enum{
703+
InaccessibleValues: make([]string, 0),
704+
Values: []string{
705+
"A",
706+
},
707+
TypeName: "Enum",
708+
Path: []string{"enum"},
709+
},
710+
},
711+
},
712+
}
713+
714+
out := &bytes.Buffer{}
715+
err = res.Resolve(context.Background(), object, nil, out)
716+
assert.NoError(t, err)
717+
assert.Equal(t, `{"data":null,"extensions":{"valueCompletion":[{"message":"Invalid value found for field Query.enum.","path":["enum"],"extensions":{"code":"INVALID_GRAPHQL"}}]}}`, out.String())
718+
})
719+
720+
t.Run("Inaccessible enum value with value completion Apollo compatibility flag", func(t *testing.T) {
721+
enum := `{"enum":"B"}`
722+
res := NewResolvable(ResolvableOptions{
723+
ApolloCompatibilityValueCompletionInExtensions: true,
724+
})
725+
ctx := &Context{
726+
Variables: nil,
727+
}
728+
err := res.Init(ctx, []byte(enum), ast.OperationTypeQuery)
729+
assert.NoError(t, err)
730+
assert.NotNil(t, res)
731+
object := &Object{
732+
Fields: []*Field{
733+
{
734+
Name: []byte("enum"),
735+
Value: &Enum{
736+
InaccessibleValues: []string{
737+
"B",
738+
},
739+
Values: []string{
740+
"A", "B",
741+
},
742+
TypeName: "Enum",
743+
Path: []string{"enum"},
744+
},
745+
},
746+
},
747+
}
748+
749+
out := &bytes.Buffer{}
750+
err = res.Resolve(context.Background(), object, nil, out)
751+
assert.NoError(t, err)
752+
assert.Equal(t, `{"data":null,"extensions":{"valueCompletion":[{"message":"Invalid value found for field Query.enum.","path":["enum"],"extensions":{"code":"INVALID_GRAPHQL"}}]}}`, out.String())
753+
})
754+
}
755+
622756
func BenchmarkResolvable_Resolve(b *testing.B) {
623757
topProducts := `{"topProducts":[{"name":"Table","__typename":"Product","upc":"1","reviews":[{"body":"Love Table!","author":{"__typename":"User","id":"1","name":"user-1"}},{"body":"Prefer other Table.","author":{"__typename":"User","id":"2","name":"user-2"}}],"stock":8},{"name":"Couch","__typename":"Product","upc":"2","reviews":[{"body":"Couch Too expensive.","author":{"__typename":"User","id":"1","name":"user-1"}}],"stock":2},{"name":"Chair","__typename":"Product","upc":"3","reviews":[{"body":"Chair Could be better.","author":{"__typename":"User","id":"2","name":"user-2"}}],"stock":5}]}`
624758
res := NewResolvable(ResolvableOptions{})

0 commit comments

Comments
 (0)