Skip to content

Commit 54d2684

Browse files
committed
deduplicate leaf fields from initial payload
1 parent 8bbf2d8 commit 54d2684

File tree

2 files changed

+49
-29
lines changed

2 files changed

+49
-29
lines changed

src/execution/__tests__/defer-test.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ describe('Execute: defer directive', () => {
205205
incremental: [
206206
{
207207
data: {
208-
id: '1',
209208
name: 'Luke',
210209
},
211210
path: ['hero'],
@@ -410,9 +409,7 @@ describe('Execute: defer directive', () => {
410409
{
411410
incremental: [
412411
{
413-
data: {
414-
name: 'Luke',
415-
},
412+
data: {},
416413
path: ['hero'],
417414
},
418415
],
@@ -447,9 +444,7 @@ describe('Execute: defer directive', () => {
447444
{
448445
incremental: [
449446
{
450-
data: {
451-
name: 'Luke',
452-
},
447+
data: {},
453448
path: ['hero'],
454449
},
455450
],
@@ -527,7 +522,7 @@ describe('Execute: defer directive', () => {
527522
]);
528523
});
529524

530-
it('Does not deduplicate leaf fields present in the initial payload', async () => {
525+
it('Can deduplicate leaf fields present in the initial payload', async () => {
531526
const document = parse(`
532527
query {
533528
hero {
@@ -585,9 +580,7 @@ describe('Execute: defer directive', () => {
585580
},
586581
},
587582
anotherNestedObject: {
588-
deeperObject: {
589-
foo: 'foo',
590-
},
583+
deeperObject: {},
591584
},
592585
},
593586
path: ['hero'],
@@ -598,7 +591,7 @@ describe('Execute: defer directive', () => {
598591
]);
599592
});
600593

601-
it('Does not deduplicate fields with deferred fragments at multiple levels', async () => {
594+
it('Can deduplicate fields with deferred fragments at multiple levels', async () => {
602595
const document = parse(`
603596
query {
604597
hero {
@@ -649,7 +642,6 @@ describe('Execute: defer directive', () => {
649642
incremental: [
650643
{
651644
data: {
652-
foo: 'foo',
653645
bar: 'bar',
654646
baz: 'baz',
655647
bak: 'bak',
@@ -659,7 +651,6 @@ describe('Execute: defer directive', () => {
659651
{
660652
data: {
661653
deeperObject: {
662-
foo: 'foo',
663654
bar: 'bar',
664655
baz: 'baz',
665656
},
@@ -670,7 +661,6 @@ describe('Execute: defer directive', () => {
670661
data: {
671662
nestedObject: {
672663
deeperObject: {
673-
foo: 'foo',
674664
bar: 'bar',
675665
},
676666
},

src/execution/execute.ts

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import type {
3838
GraphQLTypeResolver,
3939
} from '../type/definition.js';
4040
import {
41+
getNamedType,
4142
isAbstractType,
4243
isLeafType,
4344
isListType,
@@ -630,19 +631,26 @@ function executeFieldsSerially(
630631
(results, [responseName, fieldGroup]) => {
631632
const fieldPath = exeContext.addPath(path, responseName, parentType.name);
632633

633-
if (!shouldExecute(fieldGroup)) {
634+
const fieldName = fieldGroup[0].fieldNode.name.value;
635+
const fieldDef = exeContext.schema.getField(parentType, fieldName);
636+
if (!fieldDef) {
637+
return results;
638+
}
639+
640+
const returnType = fieldDef.type;
641+
642+
if (!shouldExecute(fieldGroup, returnType)) {
634643
return results;
635644
}
636645
const result = executeField(
637646
exeContext,
638647
parentType,
648+
fieldDef,
649+
returnType,
639650
sourceValue,
640651
fieldGroup,
641652
fieldPath,
642653
);
643-
if (result === undefined) {
644-
return results;
645-
}
646654
if (isPromise(result)) {
647655
return result.then((resolvedResult) => {
648656
results[responseName] = resolvedResult;
@@ -658,11 +666,25 @@ function executeFieldsSerially(
658666

659667
function shouldExecute(
660668
fieldGroup: FieldGroup,
669+
returnType: GraphQLOutputType,
661670
deferDepth?: number | undefined,
662671
): boolean {
663-
return fieldGroup.some(
664-
({ deferDepth: fieldDeferDepth }) => fieldDeferDepth === deferDepth,
665-
);
672+
if (deferDepth === undefined || !isLeafType(getNamedType(returnType))) {
673+
return fieldGroup.some(
674+
({ deferDepth: fieldDeferDepth }) => fieldDeferDepth === deferDepth,
675+
);
676+
}
677+
678+
let hasDepth = false;
679+
for (const { deferDepth: fieldDeferDepth } of fieldGroup) {
680+
if (fieldDeferDepth === undefined) {
681+
return false;
682+
}
683+
if (fieldDeferDepth === deferDepth) {
684+
hasDepth = true;
685+
}
686+
}
687+
return hasDepth;
666688
}
667689

668690
/**
@@ -684,10 +706,22 @@ function executeFields(
684706
for (const [responseName, fieldGroup] of groupedFieldSet) {
685707
const fieldPath = exeContext.addPath(path, responseName, parentType.name);
686708

687-
if (shouldExecute(fieldGroup, asyncPayloadRecord?.deferDepth)) {
709+
const fieldName = fieldGroup[0].fieldNode.name.value;
710+
const fieldDef = exeContext.schema.getField(parentType, fieldName);
711+
if (!fieldDef) {
712+
continue;
713+
}
714+
715+
const returnType = fieldDef.type;
716+
717+
if (
718+
shouldExecute(fieldGroup, returnType, asyncPayloadRecord?.deferDepth)
719+
) {
688720
const result = executeField(
689721
exeContext,
690722
parentType,
723+
fieldDef,
724+
returnType,
691725
sourceValue,
692726
fieldGroup,
693727
fieldPath,
@@ -735,19 +769,15 @@ function toNodes(fieldGroup: FieldGroup): ReadonlyArray<FieldNode> {
735769
function executeField(
736770
exeContext: ExecutionContext,
737771
parentType: GraphQLObjectType,
772+
fieldDef: GraphQLField<unknown, unknown>,
773+
returnType: GraphQLOutputType,
738774
source: unknown,
739775
fieldGroup: FieldGroup,
740776
path: Path,
741777
asyncPayloadRecord?: AsyncPayloadRecord,
742778
): PromiseOrValue<unknown> {
743779
const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
744-
const fieldName = fieldGroup[0].fieldNode.name.value;
745-
const fieldDef = exeContext.schema.getField(parentType, fieldName);
746-
if (!fieldDef) {
747-
return;
748-
}
749780

750-
const returnType = fieldDef.type;
751781
const resolveFn = fieldDef.resolve ?? exeContext.fieldResolver;
752782

753783
const info = buildResolveInfo(

0 commit comments

Comments
 (0)