Skip to content

Commit 9d4aa85

Browse files
committed
ensure typenames, fix test case expectations
fix test expectation
1 parent a8a7bff commit 9d4aa85

8 files changed

Lines changed: 467 additions & 50 deletions

File tree

execution/engine/execution_engine_test.go

Lines changed: 218 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7052,6 +7052,12 @@ func TestExecutionEngine_Execute(t *testing.T) {
70527052
id: ID!
70537053
name: String!
70547054
title: String!
7055+
info: Info!
7056+
}
7057+
7058+
type Info {
7059+
email: String!
7060+
phone: String!
70557061
}
70567062
70577063
type Query {
@@ -7072,10 +7078,34 @@ func TestExecutionEngine_Execute(t *testing.T) {
70727078
statusCode: 200,
70737079
body: `{"data":{"user":{"name":"Black"}}}`,
70747080
},
7081+
`{"query":"{user {__internal__typename_placeholder: __typename}}"}`: {
7082+
statusCode: 200,
7083+
body: `{"data":{"user":{"__internal__typename_placeholder":"User"}}}`,
7084+
},
70757085
`{"query":"{user {title}}"}`: {
70767086
statusCode: 200,
70777087
body: `{"data":{"user":{"title":"Sabbat"}}}`,
70787088
},
7089+
`{"query":"{user {id}}"}`: {
7090+
statusCode: 200,
7091+
body: `{"data":{"user":{"id":"1"}}}`,
7092+
},
7093+
`{"query":"{user {title id}}"}`: {
7094+
statusCode: 200,
7095+
body: `{"data":{"user":{"title":"Sabbat","id":"1"}}}`,
7096+
},
7097+
`{"query":"{user {name title id}}"}`: {
7098+
statusCode: 200,
7099+
body: `{"data":{"user":{"name":"Black","title":"Sabbat","id":"1"}}}`,
7100+
},
7101+
`{"query":"{user {info {email phone}}}"}`: {
7102+
statusCode: 200,
7103+
body: `{"data":{"user":{"info":{"email":"black@sabbat","phone":"123"}}}}`,
7104+
},
7105+
`{"query":"{user {name info {__internal__typename_placeholder: __typename}}}"}`: {
7106+
statusCode: 200,
7107+
body: `{"data":{"user":{"name":"Black","info":{"__internal__typename_placeholder":"Info"}}}}`,
7108+
},
70797109
},
70807110
}),
70817111
),
@@ -7089,7 +7119,11 @@ func TestExecutionEngine_Execute(t *testing.T) {
70897119
ChildNodes: []plan.TypeField{
70907120
{
70917121
TypeName: "User",
7092-
FieldNames: []string{"id", "title", "name"},
7122+
FieldNames: []string{"id", "title", "name", "info"},
7123+
},
7124+
{
7125+
TypeName: "Info",
7126+
FieldNames: []string{"email", "phone"},
70937127
},
70947128
},
70957129
},
@@ -7111,7 +7145,121 @@ func TestExecutionEngine_Execute(t *testing.T) {
71117145
}
71127146
}
71137147

7114-
t.Run("run", runWithoutError(ExecutionEngineTestCase{
7148+
t.Run("single deffered field", runWithoutError(ExecutionEngineTestCase{
7149+
schema: func(t *testing.T) *graphql.Schema {
7150+
t.Helper()
7151+
parseSchema, err := graphql.NewSchemaFromString(definition)
7152+
require.NoError(t, err)
7153+
return parseSchema
7154+
}(t),
7155+
operation: func(t *testing.T) graphql.Request {
7156+
return graphql.Request{
7157+
OperationName: "DeferUserTitle",
7158+
Query: `
7159+
query DeferUserTitle {
7160+
user {
7161+
name
7162+
... @defer {
7163+
title
7164+
}
7165+
}
7166+
}`,
7167+
}
7168+
},
7169+
dataSources: makeDataSource(t, false),
7170+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
7171+
{"incremental":[{"data":{"title":"Sabbat"},"path":["user"]}],"hasNext":false}
7172+
`,
7173+
}, withStreamingResponse()))
7174+
7175+
t.Run("multiple deffered fields", runWithoutError(ExecutionEngineTestCase{
7176+
schema: func(t *testing.T) *graphql.Schema {
7177+
t.Helper()
7178+
parseSchema, err := graphql.NewSchemaFromString(definition)
7179+
require.NoError(t, err)
7180+
return parseSchema
7181+
}(t),
7182+
operation: func(t *testing.T) graphql.Request {
7183+
return graphql.Request{
7184+
OperationName: "DeferUserTitle",
7185+
Query: `
7186+
query DeferUserTitle {
7187+
user {
7188+
name
7189+
... @defer {
7190+
title
7191+
id
7192+
}
7193+
}
7194+
}`,
7195+
}
7196+
},
7197+
dataSources: makeDataSource(t, false),
7198+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
7199+
{"incremental":[{"data":{"title":"Sabbat","id":"1"},"path":["user"]}],"hasNext":false}
7200+
`,
7201+
}, withStreamingResponse()))
7202+
7203+
t.Run("multiple deffered fields - all object fields deferred", runWithoutError(ExecutionEngineTestCase{
7204+
schema: func(t *testing.T) *graphql.Schema {
7205+
t.Helper()
7206+
parseSchema, err := graphql.NewSchemaFromString(definition)
7207+
require.NoError(t, err)
7208+
return parseSchema
7209+
}(t),
7210+
operation: func(t *testing.T) graphql.Request {
7211+
return graphql.Request{
7212+
OperationName: "DeferUserTitle",
7213+
Query: `
7214+
query DeferUserTitle {
7215+
user {
7216+
... @defer {
7217+
name
7218+
title
7219+
id
7220+
}
7221+
}
7222+
}`,
7223+
}
7224+
},
7225+
dataSources: makeDataSource(t, false),
7226+
expectedResponse: `{"data":{"user":{}},"hasNext":true}
7227+
{"incremental":[{"data":{"name":"Black","title":"Sabbat","id":"1"},"path":["user"]}],"hasNext":false}
7228+
`,
7229+
}, withStreamingResponse()))
7230+
7231+
t.Run("nested defers", runWithoutError(ExecutionEngineTestCase{
7232+
schema: func(t *testing.T) *graphql.Schema {
7233+
t.Helper()
7234+
parseSchema, err := graphql.NewSchemaFromString(definition)
7235+
require.NoError(t, err)
7236+
return parseSchema
7237+
}(t),
7238+
operation: func(t *testing.T) graphql.Request {
7239+
return graphql.Request{
7240+
OperationName: "DeferUserTitle",
7241+
Query: `
7242+
query DeferUserTitle {
7243+
user {
7244+
name
7245+
... @defer {
7246+
title
7247+
... @defer {
7248+
id
7249+
}
7250+
}
7251+
}
7252+
}`,
7253+
}
7254+
},
7255+
dataSources: makeDataSource(t, false),
7256+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
7257+
{"incremental":[{"data":{"title":"Sabbat"},"path":["user"]}],"hasNext":true}
7258+
{"incremental":[{"data":{"id":"1"},"path":["user"]}],"hasNext":false}
7259+
`,
7260+
}, withStreamingResponse()))
7261+
7262+
t.Run("parallel defers", runWithoutError(ExecutionEngineTestCase{
71157263
schema: func(t *testing.T) *graphql.Schema {
71167264
t.Helper()
71177265
parseSchema, err := graphql.NewSchemaFromString(definition)
@@ -7128,12 +7276,78 @@ func TestExecutionEngine_Execute(t *testing.T) {
71287276
... @defer {
71297277
title
71307278
}
7279+
... @defer {
7280+
id
7281+
}
7282+
}
7283+
}`,
7284+
}
7285+
},
7286+
dataSources: makeDataSource(t, false),
7287+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
7288+
{"incremental":[{"data":{"title":"Sabbat"},"path":["user"]}],"hasNext":true}
7289+
{"incremental":[{"data":{"id":"1"},"path":["user"]}],"hasNext":false}
7290+
`,
7291+
}, withStreamingResponse()))
7292+
7293+
t.Run("defer nested object", runWithoutError(ExecutionEngineTestCase{
7294+
schema: func(t *testing.T) *graphql.Schema {
7295+
t.Helper()
7296+
parseSchema, err := graphql.NewSchemaFromString(definition)
7297+
require.NoError(t, err)
7298+
return parseSchema
7299+
}(t),
7300+
operation: func(t *testing.T) graphql.Request {
7301+
return graphql.Request{
7302+
OperationName: "DeferUserTitle",
7303+
Query: `
7304+
query DeferUserTitle {
7305+
user {
7306+
name
7307+
... @defer {
7308+
info {
7309+
email
7310+
phone
7311+
}
7312+
}
7313+
}
7314+
}`,
7315+
}
7316+
},
7317+
dataSources: makeDataSource(t, false),
7318+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
7319+
{"incremental":[{"data":{"info":{"email":"black@sabbat","phone":"123"}},"path":["user"]}],"hasNext":false}
7320+
`,
7321+
}, withStreamingResponse()))
7322+
7323+
t.Run("defer nested object fields", runWithoutError(ExecutionEngineTestCase{
7324+
schema: func(t *testing.T) *graphql.Schema {
7325+
t.Helper()
7326+
parseSchema, err := graphql.NewSchemaFromString(definition)
7327+
require.NoError(t, err)
7328+
return parseSchema
7329+
}(t),
7330+
operation: func(t *testing.T) graphql.Request {
7331+
return graphql.Request{
7332+
OperationName: "DeferUserTitle",
7333+
Query: `
7334+
query DeferUserTitle {
7335+
user {
7336+
name
7337+
info {
7338+
... @defer {
7339+
email
7340+
phone
7341+
}
7342+
}
71317343
}
71327344
}`,
71337345
}
71347346
},
7135-
dataSources: makeDataSource(t, false),
7136-
expectedResponse: `{"data":{"user":{"name":"Black"}}}{"name":"Black"}{"data":{{"name":"Black"}}}`,
7347+
dataSources: makeDataSource(t, false),
7348+
expectedResponse: `{"data":{"user":{"name":"Black","info":{}}},"hasNext":true}
7349+
{"incremental":[{"data":{"email":"black@sabbat","phone":"123"},"path":["user","info"]}],"hasNext":false}
7350+
`,
71377351
}, withStreamingResponse()))
71387352
})
71397353
})

v2/pkg/astnormalization/astnormalization.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ func (o *OperationNormalizer) setupOperationWalkers() {
251251
}
252252

253253
if o.options.inlineDefer {
254-
inlineDefer := astvisitor.NewWalker(8)
254+
inlineDefer := astvisitor.NewWalkerWithID(8, "Inline defer")
255+
deferEnsureTypename(&inlineDefer)
255256
inlineFragmentExpandDefer(&inlineDefer)
256257
o.operationWalkers = append(o.operationWalkers, walkerStage{
257258
name: "inlineDefer",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package astnormalization
2+
3+
import (
4+
"github.com/wundergraph/graphql-go-tools/v2/pkg/ast"
5+
"github.com/wundergraph/graphql-go-tools/v2/pkg/astvisitor"
6+
"github.com/wundergraph/graphql-go-tools/v2/pkg/lexer/literal"
7+
)
8+
9+
// deferEnsureTypename registers a visitor that
10+
// adds internal typename to a selection set of non deferred field
11+
// if all it's fields are deferred
12+
func deferEnsureTypename(walker *astvisitor.Walker) {
13+
visitor := deferEnsureTypenameVisitor{
14+
Walker: walker,
15+
}
16+
walker.RegisterEnterDocumentVisitor(&visitor)
17+
walker.RegisterEnterSelectionSetVisitor(&visitor)
18+
}
19+
20+
type deferEnsureTypenameVisitor struct {
21+
*astvisitor.Walker
22+
operation *ast.Document
23+
}
24+
25+
func (f *deferEnsureTypenameVisitor) EnterDocument(operation, _ *ast.Document) {
26+
f.operation = operation
27+
}
28+
29+
func (f *deferEnsureTypenameVisitor) EnterSelectionSet(ref int) {
30+
fieldSelectionRefs := f.operation.SelectionSetFieldSelections(ref)
31+
// if there are some fields in the current selection set, nothing to do
32+
if len(fieldSelectionRefs) > 0 {
33+
return
34+
}
35+
36+
inlineFragmentSelectionsRefs := f.operation.SelectionSetInlineFragmentSelections(ref)
37+
38+
allFragmentsHasDefer := true
39+
for _, inlineFragmentSelectionRef := range inlineFragmentSelectionsRefs {
40+
fragmentRef := f.operation.Selections[inlineFragmentSelectionRef].Ref
41+
// fragment has directives?
42+
if !f.operation.InlineFragmentHasDirectives(fragmentRef) {
43+
allFragmentsHasDefer = false
44+
break
45+
}
46+
47+
// has defer directive?
48+
_, exists := f.operation.InlineFragmentDirectiveByName(fragmentRef, literal.DEFER)
49+
if !exists {
50+
allFragmentsHasDefer = false
51+
break
52+
}
53+
}
54+
55+
if allFragmentsHasDefer {
56+
addInternalTypeNamePlaceholder(f.operation, ref)
57+
}
58+
}

0 commit comments

Comments
 (0)