Repository: graphql/graphql-js
Pull request #3747 (2022-09-22 20:02:44)

add filtering for asyncIterable lists
when not streamed!

depends on #3746

netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *compassionate-pike-271cb3* processing.


|  Name | Link |
|---------------------------------|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | b09cdf9af0e7b454c91fe85c14398a393bef8acb |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/compassionate-pike-271cb3/deploys/632cbf66b62d980008da2f44 |
github-actions[bot]: Hi @yaacovCR, I'm @github-actions bot happy to help you with this PR 👋

<details>
<summary> Supported commands </summary>

Please post this commands in separate comments and only one per comment:
* `@github-actions run-benchmark` - Run benchmark comparing base and merge commits for this PR
* `@github-actions publish-pr-on-npm` - Build package from this PR and publish it on NPM
</details>


Patch:
diff --git a/src/execution/execute.ts b/src/execution/execute.ts
index 486a80fe..5e19e662 100644
--- a/src/execution/execute.ts
+++ b/src/execution/execute.ts
@@ -1027,6 +1027,7 @@ async function completeAsyncIteratorValue(
                 pathToArray(fieldPath),
               );
               const handledError = handleFieldError(error, itemType, errors);
+              filterSubsequentPayloads(exeContext, fieldPath);
               return handledError;
             }),
           );
@@ -1040,6 +1041,7 @@ async function completeAsyncIteratorValue(
           fieldNodes,
           pathToArray(fieldPath),
         );
+        filterSubsequentPayloads(exeContext, fieldPath);
         handleFieldError(error, itemType, errors);
       }
     } catch (rawError) {


Test patch:
diff --git a/src/execution/__tests__/defer-test.ts b/src/execution/__tests__/defer-test.ts
index 95c54479..417be985 100644
--- a/src/execution/__tests__/defer-test.ts
+++ b/src/execution/__tests__/defer-test.ts
@@ -26,6 +26,10 @@ const friendType = new GraphQLObjectType({
   fields: {
     id: { type: GraphQLID },
     name: { type: GraphQLString },
+    promiseNonNullErrorField: {
+      type: new GraphQLNonNull(GraphQLString),
+      resolve: () => Promise.resolve(null),
+    },
   },
   name: 'Friend',
 });
@@ -65,6 +69,12 @@ const heroType = new GraphQLObjectType({
       type: new GraphQLList(friendType),
       resolve: () => friends,
     },
+    asyncFriends: {
+      type: new GraphQLList(friendType),
+      async *resolve() {
+        yield await Promise.resolve(friends[0]);
+      },
+    },
   },
   name: 'Hero',
 });
@@ -657,6 +667,38 @@ describe('Execute: defer directive', () => {
     ]);
   });
 
+  it('Filters deferred payloads when a list item returned by an async iterable is nulled', async () => {
+    const document = parse(`
+    query {
+      hero {
+        asyncFriends {
+          promiseNonNullErrorField
+          ...NameFragment @defer 
+        }
+      }
+    }
+    fragment NameFragment on Friend {
+      name
+    }
+  `);
+    const result = await complete(document);
+    expectJSON(result).toDeepEqual({
+      data: {
+        hero: {
+          asyncFriends: [null],
+        },
+      },
+      errors: [
+        {
+          message:
+            'Cannot return null for non-nullable field Friend.promiseNonNullErrorField.',
+          locations: [{ line: 5, column: 11 }],
+          path: ['hero', 'asyncFriends', 0, 'promiseNonNullErrorField'],
+        },
+      ],
+    });
+  });
+
   it('original execute function throws error if anything is deferred and everything else is sync', () => {
     const doc = `
     query Deferred {