diff --git a/src/execution/IncrementalPublisher.ts b/src/execution/IncrementalPublisher.ts index 760043c78b..0caf8bf674 100644 --- a/src/execution/IncrementalPublisher.ts +++ b/src/execution/IncrementalPublisher.ts @@ -301,25 +301,20 @@ export class IncrementalPublisher { initialResultRecord: InitialResultRecord, data: ObjMap | null, ): ExecutionResult | ExperimentalIncrementalExecutionResults { + const pendingSources = new Set(); for (const child of initialResultRecord.children) { if (child.filtered) { continue; } - this._publish(child); + const maybePendingSource = this._publish(child); + if (maybePendingSource) { + pendingSources.add(maybePendingSource); + } } const errors = initialResultRecord.errors; const initialResult = errors.length === 0 ? { data } : { errors, data }; - const pending = this._pending; - if (pending.size > 0) { - const pendingSources = new Set(); - for (const subsequentResultRecord of pending) { - const pendingSource = isStreamItemsRecord(subsequentResultRecord) - ? subsequentResultRecord.streamRecord - : subsequentResultRecord; - pendingSources.add(pendingSource); - } - + if (pendingSources.size > 0) { return { initialResult: { ...initialResult, @@ -542,13 +537,10 @@ export class IncrementalPublisher { if (child.filtered) { continue; } - const pendingSource = isStreamItemsRecord(child) - ? child.streamRecord - : child; - if (!pendingSource.pendingSent) { - newPendingSources.add(pendingSource); + const maybePendingSource = this._publish(child); + if (maybePendingSource) { + newPendingSources.add(maybePendingSource); } - this._publish(child); } if (isStreamItemsRecord(subsequentResultRecord)) { if (subsequentResultRecord.isFinalRecord) { @@ -655,14 +647,20 @@ export class IncrementalPublisher { return result; } - private _publish(subsequentResultRecord: SubsequentResultRecord): void { + private _publish( + subsequentResultRecord: SubsequentResultRecord, + ): DeferredFragmentRecord | StreamRecord | undefined { if (isStreamItemsRecord(subsequentResultRecord)) { if (subsequentResultRecord.isCompleted) { this._push(subsequentResultRecord); - return; + } else { + this._introduce(subsequentResultRecord); } - this._introduce(subsequentResultRecord); + const stream = subsequentResultRecord.streamRecord; + if (!stream.pendingSent) { + return stream; + } return; } @@ -673,6 +671,12 @@ export class IncrementalPublisher { subsequentResultRecord.children.size > 0 ) { this._push(subsequentResultRecord); + } else { + return; + } + + if (!subsequentResultRecord.pendingSent) { + return subsequentResultRecord; } } diff --git a/src/execution/__tests__/defer-test.ts b/src/execution/__tests__/defer-test.ts index 261db67df9..fc44cdb228 100644 --- a/src/execution/__tests__/defer-test.ts +++ b/src/execution/__tests__/defer-test.ts @@ -64,6 +64,7 @@ const anotherNestedObject = new GraphQLObjectType({ const hero = { name: 'Luke', + lastName: 'SkyWalker', id: 1, friends, nestedObject, @@ -112,6 +113,7 @@ const heroType = new GraphQLObjectType({ fields: { id: { type: GraphQLID }, name: { type: GraphQLString }, + lastName: { type: GraphQLString }, nonNullName: { type: new GraphQLNonNull(GraphQLString) }, friends: { type: new GraphQLList(friendType), @@ -566,6 +568,58 @@ describe('Execute: defer directive', () => { ]); }); + it('Separately emits defer fragments with different labels with varying subfields with superimposed masked defer', async () => { + const document = parse(` + query HeroNameQuery { + ... @defer(label: "DeferID") { + hero { + id + } + } + ... @defer(label: "DeferName") { + hero { + name + lastName + ... @defer { + lastName + } + } + } + } + `); + const result = await complete(document); + expectJSON(result).toDeepEqual([ + { + data: {}, + pending: [ + { id: '0', path: [], label: 'DeferID' }, + { id: '1', path: [], label: 'DeferName' }, + ], + hasNext: true, + }, + { + incremental: [ + { + data: { hero: {} }, + id: '0', + }, + { + data: { id: '1' }, + id: '0', + subPath: ['hero'], + }, + { + data: { name: 'Luke', lastName: 'SkyWalker' }, + id: '1', + subPath: ['hero'], + }, + ], + completed: [{ id: '0' }, { id: '1' }], + hasNext: false, + }, + ]); + }); + it('Separately emits defer fragments with different labels with varying subfields that return promises', async () => { const document = parse(` query HeroNameQuery {