Skip to content

Commit 1fb60f2

Browse files
committed
use complete helpers within executeField
The optimized helpers change the order of promise resolution and affect the value of hasNext.
1 parent ed82aea commit 1fb60f2

File tree

3 files changed

+62
-64
lines changed

3 files changed

+62
-64
lines changed

src/execution/__tests__/nonnull-test.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,16 @@ describe('Execute: handles non-nullable types', () => {
259259
path: ['syncNest', 'syncNest', 'sync'],
260260
locations: [{ line: 6, column: 22 }],
261261
},
262+
{
263+
message: promiseError.message,
264+
path: ['syncNest', 'promise'],
265+
locations: [{ line: 5, column: 11 }],
266+
},
267+
{
268+
message: promiseError.message,
269+
path: ['syncNest', 'syncNest', 'promise'],
270+
locations: [{ line: 6, column: 27 }],
271+
},
262272
{
263273
message: syncError.message,
264274
path: ['syncNest', 'promiseNest', 'sync'],
@@ -274,21 +284,6 @@ describe('Execute: handles non-nullable types', () => {
274284
path: ['promiseNest', 'syncNest', 'sync'],
275285
locations: [{ line: 12, column: 22 }],
276286
},
277-
{
278-
message: promiseError.message,
279-
path: ['syncNest', 'promise'],
280-
locations: [{ line: 5, column: 11 }],
281-
},
282-
{
283-
message: promiseError.message,
284-
path: ['syncNest', 'syncNest', 'promise'],
285-
locations: [{ line: 6, column: 27 }],
286-
},
287-
{
288-
message: syncError.message,
289-
path: ['promiseNest', 'promiseNest', 'sync'],
290-
locations: [{ line: 13, column: 25 }],
291-
},
292287
{
293288
message: promiseError.message,
294289
path: ['syncNest', 'promiseNest', 'promise'],
@@ -304,6 +299,11 @@ describe('Execute: handles non-nullable types', () => {
304299
path: ['promiseNest', 'syncNest', 'promise'],
305300
locations: [{ line: 12, column: 27 }],
306301
},
302+
{
303+
message: syncError.message,
304+
path: ['promiseNest', 'promiseNest', 'sync'],
305+
locations: [{ line: 13, column: 25 }],
306+
},
307307
{
308308
message: promiseError.message,
309309
path: ['promiseNest', 'promiseNest', 'promise'],

src/execution/__tests__/stream-test.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,9 @@ describe('Execute: stream directive', () => {
10371037
],
10381038
},
10391039
],
1040+
hasNext: true,
1041+
},
1042+
{
10401043
hasNext: false,
10411044
},
10421045
]);
@@ -1060,19 +1063,25 @@ describe('Execute: stream directive', () => {
10601063
} /* c8 ignore stop */,
10611064
},
10621065
});
1063-
expectJSON(result).toDeepEqual({
1064-
errors: [
1065-
{
1066-
message:
1067-
'Cannot return null for non-nullable field NestedObject.nonNullScalarField.',
1068-
locations: [{ line: 4, column: 11 }],
1069-
path: ['nestedObject', 'nonNullScalarField'],
1066+
expectJSON(result).toDeepEqual([
1067+
{
1068+
errors: [
1069+
{
1070+
message:
1071+
'Cannot return null for non-nullable field NestedObject.nonNullScalarField.',
1072+
locations: [{ line: 4, column: 11 }],
1073+
path: ['nestedObject', 'nonNullScalarField'],
1074+
},
1075+
],
1076+
data: {
1077+
nestedObject: null,
10701078
},
1071-
],
1072-
data: {
1073-
nestedObject: null,
1079+
hasNext: true,
10741080
},
1075-
});
1081+
{
1082+
hasNext: false,
1083+
},
1084+
]);
10761085
});
10771086
it('Filters payloads that are nulled by a later synchronous error', async () => {
10781087
const document = parse(`
@@ -1213,6 +1222,9 @@ describe('Execute: stream directive', () => {
12131222
],
12141223
},
12151224
],
1225+
hasNext: true,
1226+
},
1227+
{
12161228
hasNext: false,
12171229
},
12181230
]);

src/execution/execute.ts

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ function executeField(
686686
);
687687

688688
// Get the resolve function, regardless of if its result is normal or abrupt (error).
689+
let result: PromiseOrValue<unknown>;
689690
try {
690691
// Build a JS object of arguments from the field.arguments AST, using the
691692
// variables scope to fulfill any variable references.
@@ -701,50 +702,35 @@ function executeField(
701702
// used to represent an authenticated user, or request-specific caches.
702703
const contextValue = exeContext.contextValue;
703704

704-
const result = resolveFn(source, args, contextValue, info);
705-
706-
let completed;
707-
if (isPromise(result)) {
708-
completed = result.then((resolved) =>
709-
completeValue(
710-
exeContext,
711-
returnType,
712-
fieldNodes,
713-
info,
714-
path,
715-
resolved,
716-
asyncPayloadRecord,
717-
),
718-
);
719-
} else {
720-
completed = completeValue(
721-
exeContext,
722-
returnType,
723-
fieldNodes,
724-
info,
725-
path,
726-
result,
727-
asyncPayloadRecord,
728-
);
729-
}
730-
731-
if (isPromise(completed)) {
732-
// Note: we don't rely on a `catch` method, but we do expect "thenable"
733-
// to take a second callback for the error case.
734-
return completed.then(undefined, (rawError) => {
735-
const error = locatedError(rawError, fieldNodes, pathToArray(path));
736-
const handledError = handleFieldError(error, returnType, errors);
737-
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
738-
return handledError;
739-
});
740-
}
741-
return completed;
705+
result = resolveFn(source, args, contextValue, info);
742706
} catch (rawError) {
743707
const error = locatedError(rawError, fieldNodes, pathToArray(path));
744708
const handledError = handleFieldError(error, returnType, errors);
745709
filterSubsequentPayloads(exeContext, path, asyncPayloadRecord);
746710
return handledError;
747711
}
712+
713+
if (isPromise(result)) {
714+
return completePromiseCatchingErrors(
715+
exeContext,
716+
returnType,
717+
fieldNodes,
718+
info,
719+
path,
720+
result,
721+
asyncPayloadRecord,
722+
);
723+
}
724+
725+
return completeValueCatchingErrors(
726+
exeContext,
727+
returnType,
728+
fieldNodes,
729+
info,
730+
path,
731+
result,
732+
asyncPayloadRecord,
733+
);
748734
}
749735

750736
/**

0 commit comments

Comments
 (0)