Skip to content

Commit 088a1d5

Browse files
committed
ensure typenames, fix test case expectations
fix test expectation
1 parent 86aff15 commit 088a1d5

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
@@ -5632,6 +5632,12 @@ func TestExecutionEngine_Execute(t *testing.T) {
56325632
id: ID!
56335633
name: String!
56345634
title: String!
5635+
info: Info!
5636+
}
5637+
5638+
type Info {
5639+
email: String!
5640+
phone: String!
56355641
}
56365642
56375643
type Query {
@@ -5652,10 +5658,34 @@ func TestExecutionEngine_Execute(t *testing.T) {
56525658
statusCode: 200,
56535659
body: `{"data":{"user":{"name":"Black"}}}`,
56545660
},
5661+
`{"query":"{user {__internal__typename_placeholder: __typename}}"}`: {
5662+
statusCode: 200,
5663+
body: `{"data":{"user":{"__internal__typename_placeholder":"User"}}}`,
5664+
},
56555665
`{"query":"{user {title}}"}`: {
56565666
statusCode: 200,
56575667
body: `{"data":{"user":{"title":"Sabbat"}}}`,
56585668
},
5669+
`{"query":"{user {id}}"}`: {
5670+
statusCode: 200,
5671+
body: `{"data":{"user":{"id":"1"}}}`,
5672+
},
5673+
`{"query":"{user {title id}}"}`: {
5674+
statusCode: 200,
5675+
body: `{"data":{"user":{"title":"Sabbat","id":"1"}}}`,
5676+
},
5677+
`{"query":"{user {name title id}}"}`: {
5678+
statusCode: 200,
5679+
body: `{"data":{"user":{"name":"Black","title":"Sabbat","id":"1"}}}`,
5680+
},
5681+
`{"query":"{user {info {email phone}}}"}`: {
5682+
statusCode: 200,
5683+
body: `{"data":{"user":{"info":{"email":"black@sabbat","phone":"123"}}}}`,
5684+
},
5685+
`{"query":"{user {name info {__internal__typename_placeholder: __typename}}}"}`: {
5686+
statusCode: 200,
5687+
body: `{"data":{"user":{"name":"Black","info":{"__internal__typename_placeholder":"Info"}}}}`,
5688+
},
56595689
},
56605690
}),
56615691
),
@@ -5669,7 +5699,11 @@ func TestExecutionEngine_Execute(t *testing.T) {
56695699
ChildNodes: []plan.TypeField{
56705700
{
56715701
TypeName: "User",
5672-
FieldNames: []string{"id", "title", "name"},
5702+
FieldNames: []string{"id", "title", "name", "info"},
5703+
},
5704+
{
5705+
TypeName: "Info",
5706+
FieldNames: []string{"email", "phone"},
56735707
},
56745708
},
56755709
},
@@ -5691,7 +5725,34 @@ func TestExecutionEngine_Execute(t *testing.T) {
56915725
}
56925726
}
56935727

5694-
t.Run("run", runWithoutError(ExecutionEngineTestCase{
5728+
t.Run("single deffered field", runWithoutError(ExecutionEngineTestCase{
5729+
schema: func(t *testing.T) *graphql.Schema {
5730+
t.Helper()
5731+
parseSchema, err := graphql.NewSchemaFromString(definition)
5732+
require.NoError(t, err)
5733+
return parseSchema
5734+
}(t),
5735+
operation: func(t *testing.T) graphql.Request {
5736+
return graphql.Request{
5737+
OperationName: "DeferUserTitle",
5738+
Query: `
5739+
query DeferUserTitle {
5740+
user {
5741+
name
5742+
... @defer {
5743+
title
5744+
}
5745+
}
5746+
}`,
5747+
}
5748+
},
5749+
dataSources: makeDataSource(t, false),
5750+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
5751+
{"incremental":[{"data":{"title":"Sabbat"},"path":["user"]}],"hasNext":false}
5752+
`,
5753+
}, withStreamingResponse()))
5754+
5755+
t.Run("multiple deffered fields", runWithoutError(ExecutionEngineTestCase{
56955756
schema: func(t *testing.T) *graphql.Schema {
56965757
t.Helper()
56975758
parseSchema, err := graphql.NewSchemaFromString(definition)
@@ -5707,13 +5768,166 @@ func TestExecutionEngine_Execute(t *testing.T) {
57075768
name
57085769
... @defer {
57095770
title
5771+
id
5772+
}
5773+
}
5774+
}`,
5775+
}
5776+
},
5777+
dataSources: makeDataSource(t, false),
5778+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
5779+
{"incremental":[{"data":{"title":"Sabbat","id":"1"},"path":["user"]}],"hasNext":false}
5780+
`,
5781+
}, withStreamingResponse()))
5782+
5783+
t.Run("multiple deffered fields - all object fields deferred", runWithoutError(ExecutionEngineTestCase{
5784+
schema: func(t *testing.T) *graphql.Schema {
5785+
t.Helper()
5786+
parseSchema, err := graphql.NewSchemaFromString(definition)
5787+
require.NoError(t, err)
5788+
return parseSchema
5789+
}(t),
5790+
operation: func(t *testing.T) graphql.Request {
5791+
return graphql.Request{
5792+
OperationName: "DeferUserTitle",
5793+
Query: `
5794+
query DeferUserTitle {
5795+
user {
5796+
... @defer {
5797+
name
5798+
title
5799+
id
5800+
}
5801+
}
5802+
}`,
5803+
}
5804+
},
5805+
dataSources: makeDataSource(t, false),
5806+
expectedResponse: `{"data":{"user":{}},"hasNext":true}
5807+
{"incremental":[{"data":{"name":"Black","title":"Sabbat","id":"1"},"path":["user"]}],"hasNext":false}
5808+
`,
5809+
}, withStreamingResponse()))
5810+
5811+
t.Run("nested defers", runWithoutError(ExecutionEngineTestCase{
5812+
schema: func(t *testing.T) *graphql.Schema {
5813+
t.Helper()
5814+
parseSchema, err := graphql.NewSchemaFromString(definition)
5815+
require.NoError(t, err)
5816+
return parseSchema
5817+
}(t),
5818+
operation: func(t *testing.T) graphql.Request {
5819+
return graphql.Request{
5820+
OperationName: "DeferUserTitle",
5821+
Query: `
5822+
query DeferUserTitle {
5823+
user {
5824+
name
5825+
... @defer {
5826+
title
5827+
... @defer {
5828+
id
5829+
}
5830+
}
5831+
}
5832+
}`,
5833+
}
5834+
},
5835+
dataSources: makeDataSource(t, false),
5836+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
5837+
{"incremental":[{"data":{"title":"Sabbat"},"path":["user"]}],"hasNext":true}
5838+
{"incremental":[{"data":{"id":"1"},"path":["user"]}],"hasNext":false}
5839+
`,
5840+
}, withStreamingResponse()))
5841+
5842+
t.Run("parallel defers", runWithoutError(ExecutionEngineTestCase{
5843+
schema: func(t *testing.T) *graphql.Schema {
5844+
t.Helper()
5845+
parseSchema, err := graphql.NewSchemaFromString(definition)
5846+
require.NoError(t, err)
5847+
return parseSchema
5848+
}(t),
5849+
operation: func(t *testing.T) graphql.Request {
5850+
return graphql.Request{
5851+
OperationName: "DeferUserTitle",
5852+
Query: `
5853+
query DeferUserTitle {
5854+
user {
5855+
name
5856+
... @defer {
5857+
title
5858+
}
5859+
... @defer {
5860+
id
5861+
}
5862+
}
5863+
}`,
5864+
}
5865+
},
5866+
dataSources: makeDataSource(t, false),
5867+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
5868+
{"incremental":[{"data":{"title":"Sabbat"},"path":["user"]}],"hasNext":true}
5869+
{"incremental":[{"data":{"id":"1"},"path":["user"]}],"hasNext":false}
5870+
`,
5871+
}, withStreamingResponse()))
5872+
5873+
t.Run("defer nested object", runWithoutError(ExecutionEngineTestCase{
5874+
schema: func(t *testing.T) *graphql.Schema {
5875+
t.Helper()
5876+
parseSchema, err := graphql.NewSchemaFromString(definition)
5877+
require.NoError(t, err)
5878+
return parseSchema
5879+
}(t),
5880+
operation: func(t *testing.T) graphql.Request {
5881+
return graphql.Request{
5882+
OperationName: "DeferUserTitle",
5883+
Query: `
5884+
query DeferUserTitle {
5885+
user {
5886+
name
5887+
... @defer {
5888+
info {
5889+
email
5890+
phone
5891+
}
5892+
}
5893+
}
5894+
}`,
5895+
}
5896+
},
5897+
dataSources: makeDataSource(t, false),
5898+
expectedResponse: `{"data":{"user":{"name":"Black"}},"hasNext":true}
5899+
{"incremental":[{"data":{"info":{"email":"black@sabbat","phone":"123"}},"path":["user"]}],"hasNext":false}
5900+
`,
5901+
}, withStreamingResponse()))
5902+
5903+
t.Run("defer nested object fields", runWithoutError(ExecutionEngineTestCase{
5904+
schema: func(t *testing.T) *graphql.Schema {
5905+
t.Helper()
5906+
parseSchema, err := graphql.NewSchemaFromString(definition)
5907+
require.NoError(t, err)
5908+
return parseSchema
5909+
}(t),
5910+
operation: func(t *testing.T) graphql.Request {
5911+
return graphql.Request{
5912+
OperationName: "DeferUserTitle",
5913+
Query: `
5914+
query DeferUserTitle {
5915+
user {
5916+
name
5917+
info {
5918+
... @defer {
5919+
email
5920+
phone
5921+
}
57105922
}
57115923
}
57125924
}`,
57135925
}
57145926
},
5715-
dataSources: makeDataSource(t, false),
5716-
expectedResponse: `{"data":{"user":{"name":"Black"}}}{"name":"Black"}{"data":{{"name":"Black"}}}`,
5927+
dataSources: makeDataSource(t, false),
5928+
expectedResponse: `{"data":{"user":{"name":"Black","info":{}}},"hasNext":true}
5929+
{"incremental":[{"data":{"email":"black@sabbat","phone":"123"},"path":["user","info"]}],"hasNext":false}
5930+
`,
57175931
}, withStreamingResponse()))
57185932
})
57195933
})

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)