Skip to content

Commit

Permalink
deduplicate leaf fields from initial payload
Browse files Browse the repository at this point in the history
  • Loading branch information
yaacovCR committed Jan 15, 2023
1 parent cdbfbde commit 86f9166
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 29 deletions.
20 changes: 5 additions & 15 deletions src/execution/__tests__/defer-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ describe('Execute: defer directive', () => {
incremental: [
{
data: {
id: '1',
name: 'Luke',
},
path: ['hero'],
Expand Down Expand Up @@ -410,9 +409,7 @@ describe('Execute: defer directive', () => {
{
incremental: [
{
data: {
name: 'Luke',
},
data: {},
path: ['hero'],
},
],
Expand Down Expand Up @@ -447,9 +444,7 @@ describe('Execute: defer directive', () => {
{
incremental: [
{
data: {
name: 'Luke',
},
data: {},
path: ['hero'],
},
],
Expand Down Expand Up @@ -527,7 +522,7 @@ describe('Execute: defer directive', () => {
]);
});

it('Does not deduplicate leaf fields present in the initial payload', async () => {
it('Can deduplicate leaf fields present in the initial payload', async () => {
const document = parse(`
query {
hero {
Expand Down Expand Up @@ -585,9 +580,7 @@ describe('Execute: defer directive', () => {
},
},
anotherNestedObject: {
deeperObject: {
foo: 'foo',
},
deeperObject: {},
},
},
path: ['hero'],
Expand All @@ -598,7 +591,7 @@ describe('Execute: defer directive', () => {
]);
});

it('Does not deduplicate fields with deferred fragments at multiple levels', async () => {
it('Can deduplicate fields with deferred fragments at multiple levels', async () => {
const document = parse(`
query {
hero {
Expand Down Expand Up @@ -649,7 +642,6 @@ describe('Execute: defer directive', () => {
incremental: [
{
data: {
foo: 'foo',
bar: 'bar',
baz: 'baz',
bak: 'bak',
Expand All @@ -659,7 +651,6 @@ describe('Execute: defer directive', () => {
{
data: {
deeperObject: {
foo: 'foo',
bar: 'bar',
baz: 'baz',
},
Expand All @@ -670,7 +661,6 @@ describe('Execute: defer directive', () => {
data: {
nestedObject: {
deeperObject: {
foo: 'foo',
bar: 'bar',
},
},
Expand Down
58 changes: 44 additions & 14 deletions src/execution/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import type {
GraphQLTypeResolver,
} from '../type/definition.js';
import {
getNamedType,
isAbstractType,
isLeafType,
isListType,
Expand Down Expand Up @@ -630,19 +631,26 @@ function executeFieldsSerially(
(results, [responseName, fieldGroup]) => {
const fieldPath = exeContext.addPath(path, responseName, parentType.name);

if (!shouldExecute(fieldGroup)) {
const fieldName = fieldGroup[0].fieldNode.name.value;
const fieldDef = exeContext.schema.getField(parentType, fieldName);
if (!fieldDef) {
return results;
}

const returnType = fieldDef.type;

if (!shouldExecute(fieldGroup, returnType)) {
return results;
}
const result = executeField(
exeContext,
parentType,
fieldDef,
returnType,
sourceValue,
fieldGroup,
fieldPath,
);
if (result === undefined) {
return results;
}
if (isPromise(result)) {
return result.then((resolvedResult) => {
results[responseName] = resolvedResult;
Expand All @@ -658,11 +666,25 @@ function executeFieldsSerially(

function shouldExecute(
fieldGroup: FieldGroup,
returnType: GraphQLOutputType,
deferDepth?: number | undefined,
): boolean {
return fieldGroup.some(
({ deferDepth: fieldDeferDepth }) => fieldDeferDepth === deferDepth,
);
if (deferDepth === undefined || !isLeafType(getNamedType(returnType))) {
return fieldGroup.some(
({ deferDepth: fieldDeferDepth }) => fieldDeferDepth === deferDepth,
);
}

let hasDepth = false;
for (const { deferDepth: fieldDeferDepth } of fieldGroup) {
if (fieldDeferDepth === undefined) {
return false;
}
if (fieldDeferDepth === deferDepth) {
hasDepth = true;
}
}
return hasDepth;
}

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

if (shouldExecute(fieldGroup, asyncPayloadRecord?.deferDepth)) {
const fieldName = fieldGroup[0].fieldNode.name.value;
const fieldDef = exeContext.schema.getField(parentType, fieldName);
if (!fieldDef) {
continue;
}

const returnType = fieldDef.type;

if (
shouldExecute(fieldGroup, returnType, asyncPayloadRecord?.deferDepth)
) {
const result = executeField(
exeContext,
parentType,
fieldDef,
returnType,
sourceValue,
fieldGroup,
fieldPath,
Expand Down Expand Up @@ -735,19 +769,15 @@ function toNodes(fieldGroup: FieldGroup): ReadonlyArray<FieldNode> {
function executeField(
exeContext: ExecutionContext,
parentType: GraphQLObjectType,
fieldDef: GraphQLField<unknown, unknown>,
returnType: GraphQLOutputType,
source: unknown,
fieldGroup: FieldGroup,
path: Path,
asyncPayloadRecord?: AsyncPayloadRecord,
): PromiseOrValue<unknown> {
const errors = asyncPayloadRecord?.errors ?? exeContext.errors;
const fieldName = fieldGroup[0].fieldNode.name.value;
const fieldDef = exeContext.schema.getField(parentType, fieldName);
if (!fieldDef) {
return;
}

const returnType = fieldDef.type;
const resolveFn = fieldDef.resolve ?? exeContext.fieldResolver;

const info = buildResolveInfo(
Expand Down

0 comments on commit 86f9166

Please sign in to comment.