|
1 | 1 | import { describe, it } from 'mocha'; |
2 | 2 |
|
| 3 | +import { invariant } from '../../jsutils/invariant'; |
3 | 4 | import { isAsyncIterable } from '../../jsutils/isAsyncIterable'; |
4 | 5 | import { parse } from '../../language/parser'; |
5 | 6 |
|
@@ -91,6 +92,36 @@ const query = new GraphQLObjectType({ |
91 | 92 | yield await Promise.resolve({}); |
92 | 93 | }, |
93 | 94 | }, |
| 95 | + asyncIterableListDelayed: { |
| 96 | + type: new GraphQLList(friendType), |
| 97 | + async *resolve() { |
| 98 | + for (const friend of friends) { |
| 99 | + // pause an additional ms before yielding to allow time |
| 100 | + // for tests to return or throw before next value is processed. |
| 101 | + // eslint-disable-next-line no-await-in-loop |
| 102 | + await new Promise((r) => setTimeout(r, 1)); |
| 103 | + yield friend; |
| 104 | + } |
| 105 | + }, |
| 106 | + }, |
| 107 | + asyncIterableListNoReturn: { |
| 108 | + type: new GraphQLList(friendType), |
| 109 | + resolve() { |
| 110 | + let i = 0; |
| 111 | + return { |
| 112 | + [Symbol.asyncIterator]: () => ({ |
| 113 | + async next() { |
| 114 | + const friend = friends[i++]; |
| 115 | + if (friend) { |
| 116 | + await new Promise((r) => setTimeout(r, 1)); |
| 117 | + return { value: friend, done: false }; |
| 118 | + } |
| 119 | + return { value: undefined, done: true }; |
| 120 | + }, |
| 121 | + }), |
| 122 | + }; |
| 123 | + }, |
| 124 | + }, |
94 | 125 | asyncIterableListDelayedClose: { |
95 | 126 | type: new GraphQLList(friendType), |
96 | 127 | async *resolve() { |
@@ -869,4 +900,172 @@ describe('Execute: stream directive', () => { |
869 | 900 | }, |
870 | 901 | ]); |
871 | 902 | }); |
| 903 | + it('Returns underlying async iterables when dispatcher is returned', async () => { |
| 904 | + const document = parse(` |
| 905 | + query { |
| 906 | + asyncIterableListDelayed @stream(initialCount: 1) { |
| 907 | + name |
| 908 | + id |
| 909 | + } |
| 910 | + } |
| 911 | + `); |
| 912 | + const schema = new GraphQLSchema({ query }); |
| 913 | + |
| 914 | + const executeResult = await execute({ schema, document, rootValue: {} }); |
| 915 | + invariant(isAsyncIterable(executeResult)); |
| 916 | + const iterator = executeResult[Symbol.asyncIterator](); |
| 917 | + |
| 918 | + const result1 = await iterator.next(); |
| 919 | + expectJSON(result1).toDeepEqual({ |
| 920 | + done: false, |
| 921 | + value: { |
| 922 | + data: { |
| 923 | + asyncIterableListDelayed: [ |
| 924 | + { |
| 925 | + id: '1', |
| 926 | + name: 'Luke', |
| 927 | + }, |
| 928 | + ], |
| 929 | + }, |
| 930 | + hasNext: true, |
| 931 | + }, |
| 932 | + }); |
| 933 | + |
| 934 | + iterator.return?.(); |
| 935 | + |
| 936 | + // this result had started processing before return was called |
| 937 | + const result2 = await iterator.next(); |
| 938 | + expectJSON(result2).toDeepEqual({ |
| 939 | + done: false, |
| 940 | + value: { |
| 941 | + data: { |
| 942 | + id: '2', |
| 943 | + name: 'Han', |
| 944 | + }, |
| 945 | + hasNext: true, |
| 946 | + path: ['asyncIterableListDelayed', 1], |
| 947 | + }, |
| 948 | + }); |
| 949 | + |
| 950 | + // third result is not returned because async iterator has returned |
| 951 | + const result3 = await iterator.next(); |
| 952 | + expectJSON(result3).toDeepEqual({ |
| 953 | + done: false, |
| 954 | + value: { |
| 955 | + hasNext: false, |
| 956 | + }, |
| 957 | + }); |
| 958 | + }); |
| 959 | + it('Can return async iterable when underlying iterable does not have a return method', async () => { |
| 960 | + const document = parse(` |
| 961 | + query { |
| 962 | + asyncIterableListNoReturn @stream(initialCount: 1) { |
| 963 | + name |
| 964 | + id |
| 965 | + } |
| 966 | + } |
| 967 | + `); |
| 968 | + const schema = new GraphQLSchema({ query }); |
| 969 | + |
| 970 | + const executeResult = await execute({ schema, document, rootValue: {} }); |
| 971 | + invariant(isAsyncIterable(executeResult)); |
| 972 | + const iterator = executeResult[Symbol.asyncIterator](); |
| 973 | + |
| 974 | + const result1 = await iterator.next(); |
| 975 | + expectJSON(result1).toDeepEqual({ |
| 976 | + done: false, |
| 977 | + value: { |
| 978 | + data: { |
| 979 | + asyncIterableListNoReturn: [ |
| 980 | + { |
| 981 | + id: '1', |
| 982 | + name: 'Luke', |
| 983 | + }, |
| 984 | + ], |
| 985 | + }, |
| 986 | + hasNext: true, |
| 987 | + }, |
| 988 | + }); |
| 989 | + |
| 990 | + iterator.return?.(); |
| 991 | + |
| 992 | + // this result had started processing before return was called |
| 993 | + const result2 = await iterator.next(); |
| 994 | + expectJSON(result2).toDeepEqual({ |
| 995 | + done: false, |
| 996 | + value: { |
| 997 | + data: { |
| 998 | + id: '2', |
| 999 | + name: 'Han', |
| 1000 | + }, |
| 1001 | + hasNext: true, |
| 1002 | + path: ['asyncIterableListNoReturn', 1], |
| 1003 | + }, |
| 1004 | + }); |
| 1005 | + |
| 1006 | + // third result is not returned because async iterator has returned |
| 1007 | + const result3 = await iterator.next(); |
| 1008 | + expectJSON(result3).toDeepEqual({ |
| 1009 | + done: false, |
| 1010 | + value: { |
| 1011 | + hasNext: false, |
| 1012 | + }, |
| 1013 | + }); |
| 1014 | + }); |
| 1015 | + it('Returns underlying async iterables when dispatcher is thrown', async () => { |
| 1016 | + const document = parse(` |
| 1017 | + query { |
| 1018 | + asyncIterableListDelayed @stream(initialCount: 1) { |
| 1019 | + name |
| 1020 | + id |
| 1021 | + } |
| 1022 | + } |
| 1023 | + `); |
| 1024 | + const schema = new GraphQLSchema({ query }); |
| 1025 | + |
| 1026 | + const executeResult = await execute({ schema, document, rootValue: {} }); |
| 1027 | + invariant(isAsyncIterable(executeResult)); |
| 1028 | + const iterator = executeResult[Symbol.asyncIterator](); |
| 1029 | + |
| 1030 | + const result1 = await iterator.next(); |
| 1031 | + expectJSON(result1).toDeepEqual({ |
| 1032 | + done: false, |
| 1033 | + value: { |
| 1034 | + data: { |
| 1035 | + asyncIterableListDelayed: [ |
| 1036 | + { |
| 1037 | + id: '1', |
| 1038 | + name: 'Luke', |
| 1039 | + }, |
| 1040 | + ], |
| 1041 | + }, |
| 1042 | + hasNext: true, |
| 1043 | + }, |
| 1044 | + }); |
| 1045 | + |
| 1046 | + iterator.throw?.(new Error('bad')); |
| 1047 | + |
| 1048 | + // this result had started processing before return was called |
| 1049 | + const result2 = await iterator.next(); |
| 1050 | + expectJSON(result2).toDeepEqual({ |
| 1051 | + done: false, |
| 1052 | + value: { |
| 1053 | + data: { |
| 1054 | + id: '2', |
| 1055 | + name: 'Han', |
| 1056 | + }, |
| 1057 | + hasNext: true, |
| 1058 | + path: ['asyncIterableListDelayed', 1], |
| 1059 | + }, |
| 1060 | + }); |
| 1061 | + |
| 1062 | + // third result is not returned because async iterator has returned |
| 1063 | + const result3 = await iterator.next(); |
| 1064 | + expectJSON(result3).toDeepEqual({ |
| 1065 | + done: false, |
| 1066 | + value: { |
| 1067 | + hasNext: false, |
| 1068 | + }, |
| 1069 | + }); |
| 1070 | + }); |
872 | 1071 | }); |
0 commit comments