Skip to content

Commit d792c40

Browse files
committed
return async iterables in the non incremental delivery case (#4144)
catching errors
1 parent 800be2b commit d792c40

File tree

2 files changed

+78
-39
lines changed

2 files changed

+78
-39
lines changed

src/execution/__tests__/lists-test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect } from 'chai';
1+
import { assert, expect } from 'chai';
22
import { describe, it } from 'mocha';
33

44
import { expectJSON } from '../../__testUtils__/expectJSON.js';
@@ -245,6 +245,34 @@ describe('Execute: Accepts async iterables as list value', () => {
245245
errors,
246246
});
247247
});
248+
249+
it('Returns async iterable when list nulls', async () => {
250+
const values = [1, null, 2];
251+
let i = 0;
252+
let returned = false;
253+
const listField = {
254+
[Symbol.asyncIterator]: () => ({
255+
next: () => Promise.resolve({ value: values[i++], done: false }),
256+
return: () => {
257+
returned = true;
258+
return Promise.resolve({ value: undefined, done: true });
259+
},
260+
}),
261+
};
262+
const errors = [
263+
{
264+
message: 'Cannot return null for non-nullable field Query.listField.',
265+
locations: [{ line: 1, column: 3 }],
266+
path: ['listField', 1],
267+
},
268+
];
269+
270+
expectJSON(await complete({ listField }, '[Int!]')).toDeepEqual({
271+
data: { listField: null },
272+
errors,
273+
});
274+
assert(returned);
275+
});
248276
});
249277

250278
describe('Execute: Handles list nullability', () => {

src/execution/execute.ts

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -779,56 +779,67 @@ async function completeAsyncIteratorValue(
779779
let containsPromise = false;
780780
const completedResults: Array<unknown> = [];
781781
let index = 0;
782-
// eslint-disable-next-line no-constant-condition
783-
while (true) {
784-
const itemPath = addPath(path, index, undefined);
785-
let iteration;
786-
try {
787-
// eslint-disable-next-line no-await-in-loop
788-
iteration = await asyncIterator.next();
782+
const earlyReturn = asyncIterator.return?.bind(asyncIterator);
783+
try {
784+
// eslint-disable-next-line no-constant-condition
785+
while (true) {
786+
const itemPath = addPath(path, index, undefined);
787+
let iteration;
788+
try {
789+
// eslint-disable-next-line no-await-in-loop
790+
iteration = await asyncIterator.next();
791+
} catch (rawError) {
792+
throw locatedError(rawError, fieldGroup, pathToArray(path));
793+
}
794+
795+
// TODO: add test case for stream returning done before initialCount
796+
/* c8 ignore next 3 */
789797
if (iteration.done) {
790798
break;
791799
}
792-
} catch (rawError) {
793-
throw locatedError(rawError, fieldGroup, pathToArray(path));
794-
}
795800

796-
// TODO: add test case for stream returning done before initialCount
797-
/* c8 ignore next 3 */
798-
if (iteration.done) {
799-
break;
800-
}
801-
802-
const item = iteration.value;
803-
// TODO: add tests for stream backed by asyncIterator that returns a promise
804-
/* c8 ignore start */
805-
if (isPromise(item)) {
806-
completedResults.push(
807-
completePromisedListItemValue(
801+
const item = iteration.value;
802+
// TODO: add tests for stream backed by asyncIterator that returns a promise
803+
/* c8 ignore start */
804+
if (isPromise(item)) {
805+
completedResults.push(
806+
completePromisedListItemValue(
807+
item,
808+
exeContext,
809+
itemType,
810+
fieldGroup,
811+
info,
812+
itemPath,
813+
),
814+
);
815+
containsPromise = true;
816+
} else if (
817+
/* c8 ignore stop */
818+
completeListItemValue(
808819
item,
820+
completedResults,
809821
exeContext,
810822
itemType,
811823
fieldGroup,
812824
info,
813825
itemPath,
814-
),
815-
);
816-
containsPromise = true;
817-
} else if (
826+
)
827+
// TODO: add tests for stream backed by asyncIterator that completes to a promise
828+
/* c8 ignore start */
829+
) {
830+
containsPromise = true;
831+
}
818832
/* c8 ignore stop */
819-
completeListItemValue(
820-
item,
821-
completedResults,
822-
exeContext,
823-
itemType,
824-
fieldGroup,
825-
info,
826-
itemPath,
827-
)
828-
) {
829-
containsPromise = true;
833+
index++;
830834
}
831-
index += 1;
835+
} catch (error) {
836+
if (earlyReturn !== undefined) {
837+
earlyReturn().catch(() => {
838+
/* c8 ignore next 1 */
839+
// ignore error
840+
});
841+
}
842+
throw error;
832843
}
833844
return containsPromise ? Promise.all(completedResults) : completedResults;
834845
}

0 commit comments

Comments
 (0)