@@ -2,6 +2,7 @@ import { describe, it } from 'mocha';
2
2
3
3
import { expectJSON } from '../../__testUtils__/expectJSON' ;
4
4
5
+ import { invariant } from '../../jsutils/invariant' ;
5
6
import { isAsyncIterable } from '../../jsutils/isAsyncIterable' ;
6
7
7
8
import type { DocumentNode } from '../../language/ast' ;
@@ -134,6 +135,37 @@ const query = new GraphQLObjectType({
134
135
yield await Promise . resolve ( { string : friends [ 1 ] . name } ) ;
135
136
} ,
136
137
} ,
138
+ asyncIterableListDelayed : {
139
+ type : new GraphQLList ( friendType ) ,
140
+ async * resolve ( ) {
141
+ for ( const friend of friends ) {
142
+ // pause an additional ms before yielding to allow time
143
+ // for tests to return or throw before next value is processed.
144
+ // eslint-disable-next-line no-await-in-loop
145
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
146
+ yield friend ; /* c8 ignore start */
147
+ // Not reachable, early return
148
+ }
149
+ } /* c8 ignore stop */ ,
150
+ } ,
151
+ asyncIterableListNoReturn : {
152
+ type : new GraphQLList ( friendType ) ,
153
+ resolve ( ) {
154
+ let i = 0 ;
155
+ return {
156
+ [ Symbol . asyncIterator ] : ( ) => ( {
157
+ async next ( ) {
158
+ const friend = friends [ i ++ ] ;
159
+ if ( friend ) {
160
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
161
+ return { value : friend , done : false } ;
162
+ }
163
+ return { value : undefined , done : true } ;
164
+ } ,
165
+ } ) ,
166
+ } ;
167
+ } ,
168
+ } ,
137
169
asyncIterableListDelayedClose : {
138
170
type : new GraphQLList ( friendType ) ,
139
171
async * resolve ( ) {
@@ -1080,4 +1112,175 @@ describe('Execute: stream directive', () => {
1080
1112
} ,
1081
1113
] ) ;
1082
1114
} ) ;
1115
+ it ( 'Returns underlying async iterables when dispatcher is returned' , async ( ) => {
1116
+ const document = parse ( `
1117
+ query {
1118
+ asyncIterableListDelayed @stream(initialCount: 1) {
1119
+ name
1120
+ id
1121
+ }
1122
+ }
1123
+ ` ) ;
1124
+ const schema = new GraphQLSchema ( { query } ) ;
1125
+
1126
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1127
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1128
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1129
+
1130
+ const result1 = await iterator . next ( ) ;
1131
+ expectJSON ( result1 ) . toDeepEqual ( {
1132
+ done : false ,
1133
+ value : {
1134
+ data : {
1135
+ asyncIterableListDelayed : [
1136
+ {
1137
+ id : '1' ,
1138
+ name : 'Luke' ,
1139
+ } ,
1140
+ ] ,
1141
+ } ,
1142
+ hasNext : true ,
1143
+ } ,
1144
+ } ) ;
1145
+
1146
+ const returnPromise = iterator . return ( ) ;
1147
+
1148
+ // this result had started processing before return was called
1149
+ const result2 = await iterator . next ( ) ;
1150
+ expectJSON ( result2 ) . toDeepEqual ( {
1151
+ done : false ,
1152
+ value : {
1153
+ data : {
1154
+ id : '2' ,
1155
+ name : 'Han' ,
1156
+ } ,
1157
+ hasNext : true ,
1158
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1159
+ } ,
1160
+ } ) ;
1161
+
1162
+ // third result is not returned because async iterator has returned
1163
+ const result3 = await iterator . next ( ) ;
1164
+ expectJSON ( result3 ) . toDeepEqual ( {
1165
+ done : true ,
1166
+ value : undefined ,
1167
+ } ) ;
1168
+ await returnPromise ;
1169
+ } ) ;
1170
+ it ( 'Can return async iterable when underlying iterable does not have a return method' , async ( ) => {
1171
+ const document = parse ( `
1172
+ query {
1173
+ asyncIterableListNoReturn @stream(initialCount: 1) {
1174
+ name
1175
+ id
1176
+ }
1177
+ }
1178
+ ` ) ;
1179
+ const schema = new GraphQLSchema ( { query } ) ;
1180
+
1181
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1182
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1183
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1184
+
1185
+ const result1 = await iterator . next ( ) ;
1186
+ expectJSON ( result1 ) . toDeepEqual ( {
1187
+ done : false ,
1188
+ value : {
1189
+ data : {
1190
+ asyncIterableListNoReturn : [
1191
+ {
1192
+ id : '1' ,
1193
+ name : 'Luke' ,
1194
+ } ,
1195
+ ] ,
1196
+ } ,
1197
+ hasNext : true ,
1198
+ } ,
1199
+ } ) ;
1200
+
1201
+ const returnPromise = iterator . return ( ) ;
1202
+
1203
+ // this result had started processing before return was called
1204
+ const result2 = await iterator . next ( ) ;
1205
+ expectJSON ( result2 ) . toDeepEqual ( {
1206
+ done : false ,
1207
+ value : {
1208
+ data : {
1209
+ id : '2' ,
1210
+ name : 'Han' ,
1211
+ } ,
1212
+ hasNext : true ,
1213
+ path : [ 'asyncIterableListNoReturn' , 1 ] ,
1214
+ } ,
1215
+ } ) ;
1216
+
1217
+ // third result is not returned because async iterator has returned
1218
+ const result3 = await iterator . next ( ) ;
1219
+ expectJSON ( result3 ) . toDeepEqual ( {
1220
+ done : true ,
1221
+ value : undefined ,
1222
+ } ) ;
1223
+ await returnPromise ;
1224
+ } ) ;
1225
+ it ( 'Returns underlying async iterables when dispatcher is thrown' , async ( ) => {
1226
+ const document = parse ( `
1227
+ query {
1228
+ asyncIterableListDelayed @stream(initialCount: 1) {
1229
+ name
1230
+ id
1231
+ }
1232
+ }
1233
+ ` ) ;
1234
+ const schema = new GraphQLSchema ( { query } ) ;
1235
+
1236
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1237
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1238
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1239
+
1240
+ const result1 = await iterator . next ( ) ;
1241
+ expectJSON ( result1 ) . toDeepEqual ( {
1242
+ done : false ,
1243
+ value : {
1244
+ data : {
1245
+ asyncIterableListDelayed : [
1246
+ {
1247
+ id : '1' ,
1248
+ name : 'Luke' ,
1249
+ } ,
1250
+ ] ,
1251
+ } ,
1252
+ hasNext : true ,
1253
+ } ,
1254
+ } ) ;
1255
+
1256
+ const throwPromise = iterator . throw ( new Error ( 'bad' ) ) ;
1257
+
1258
+ // this result had started processing before return was called
1259
+ const result2 = await iterator . next ( ) ;
1260
+ expectJSON ( result2 ) . toDeepEqual ( {
1261
+ done : false ,
1262
+ value : {
1263
+ data : {
1264
+ id : '2' ,
1265
+ name : 'Han' ,
1266
+ } ,
1267
+ hasNext : true ,
1268
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1269
+ } ,
1270
+ } ) ;
1271
+
1272
+ // third result is not returned because async iterator has returned
1273
+ const result3 = await iterator . next ( ) ;
1274
+ expectJSON ( result3 ) . toDeepEqual ( {
1275
+ done : true ,
1276
+ value : undefined ,
1277
+ } ) ;
1278
+ try {
1279
+ await throwPromise ; /* c8 ignore start */
1280
+ // Not reachable, always throws
1281
+ /* c8 ignore stop */
1282
+ } catch ( e ) {
1283
+ // ignore error
1284
+ }
1285
+ } ) ;
1083
1286
} ) ;
0 commit comments