diff --git a/src/__tests__/starWarsDeferredQuery-test.js b/src/__tests__/starWarsDeferredQuery-test.js index e7754787de..548c062e42 100644 --- a/src/__tests__/starWarsDeferredQuery-test.js +++ b/src/__tests__/starWarsDeferredQuery-test.js @@ -106,8 +106,8 @@ describe('Star Wars Query Deferred Tests', () => { `; const result = await graphql(StarWarsSchemaDeferStreamEnabled, query); - const { patches: patchesIterable, ...rest } = result; - expect(rest).to.deep.equal({ + const { patches: patchesIterable, ...initial } = result; + expect(initial).to.deep.equal({ data: { hero: { id: '2001', @@ -149,6 +149,49 @@ describe('Star Wars Query Deferred Tests', () => { // TODO // describe('Using aliases to change the key in the response', () => {}); + describe('Inline Fragments', () => { + it('Allows us to defer an inline fragment', async () => { + const query = ` + query UserFragment { + human(id: "1003") { + id + ... on Human @defer(label: "InlineDeferred"){ + name + homePlanet + } + } + } + + `; + const result = await graphql(StarWarsSchemaDeferStreamEnabled, query); + const { patches: patchesIterable, ...initial } = result; + expect(initial).to.deep.equal({ + data: { + human: { + id: '1003', + }, + }, + }); + + const patches = []; + + if (patchesIterable) { + await forAwaitEach(patchesIterable, patch => { + patches.push(patch); + }); + } + expect(patches).to.have.lengthOf(1); + expect(patches[0]).to.deep.equal({ + label: 'InlineDeferred', + path: ['human'], + data: { + name: 'Leia Organa', + homePlanet: 'Alderaan', + }, + }); + }); + }); + describe('Uses fragments to express more complex queries', () => { it('Allows us to use a fragment to avoid duplicating content', async () => { const query = ` @@ -183,9 +226,9 @@ describe('Star Wars Query Deferred Tests', () => { } `; const result = await graphql(StarWarsSchemaDeferStreamEnabled, query); - const { patches: patchesIterable, ...rest } = result; + const { patches: patchesIterable, ...initial } = result; - expect(rest).to.deep.equal({ + expect(initial).to.deep.equal({ data: { han: { __typename: 'Human', diff --git a/src/execution/execute.js b/src/execution/execute.js index 10431d8b1e..b7f7c397fb 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.js @@ -545,26 +545,47 @@ export function collectFields( ) { continue; } - collectFields( - exeContext, - runtimeType, - selection.selectionSet, - fields, - patches, - visitedFragmentNames, - ); + + const patchLabel = exeContext.schema.__experimentalDeferFragmentSpreads + ? getDeferredNodeLabel(exeContext, selection) + : ''; + + if (patchLabel) { + const { fields: patchFields } = collectFields( + exeContext, + runtimeType, + selection.selectionSet, + Object.create(null), + patches, + visitedFragmentNames, + ); + patches.push({ + label: patchLabel, + fields: patchFields, + }); + } else { + collectFields( + exeContext, + runtimeType, + selection.selectionSet, + fields, + patches, + visitedFragmentNames, + ); + } break; } case Kind.FRAGMENT_SPREAD: { const fragName = selection.name.value; + if (!shouldIncludeNode(exeContext, selection)) { + continue; + } + const patchLabel = exeContext.schema.__experimentalDeferFragmentSpreads ? getDeferredNodeLabel(exeContext, selection) : ''; - if (!shouldIncludeNode(exeContext, selection)) { - continue; - } if ( visitedFragmentNames[fragName] && // Cannot continue in this case because fields must be recollected for patch diff --git a/src/type/directives.js b/src/type/directives.js index 36f0e6c847..d7dcfd6bd3 100644 --- a/src/type/directives.js +++ b/src/type/directives.js @@ -176,7 +176,6 @@ export const GraphQLDeferDirective = new GraphQLDirective({ 'Directs the executor to defer this fragment when the `if` argument is true or undefined.', locations: [ DirectiveLocation.FRAGMENT_SPREAD, - // TODO: Do we want to support on inline fragments? (Can we? How would you make label unique?) DirectiveLocation.INLINE_FRAGMENT, ], args: {