@@ -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' ;
@@ -111,6 +112,37 @@ const query = new GraphQLObjectType({
111
112
yield await Promise . resolve ( { } ) ;
112
113
} ,
113
114
} ,
115
+ asyncIterableListDelayed : {
116
+ type : new GraphQLList ( friendType ) ,
117
+ async * resolve ( ) {
118
+ for ( const friend of friends ) {
119
+ // pause an additional ms before yielding to allow time
120
+ // for tests to return or throw before next value is processed.
121
+ // eslint-disable-next-line no-await-in-loop
122
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
123
+ yield friend ; /* c8 ignore start */
124
+ // Not reachable, early return
125
+ }
126
+ } /* c8 ignore stop */ ,
127
+ } ,
128
+ asyncIterableListNoReturn : {
129
+ type : new GraphQLList ( friendType ) ,
130
+ resolve ( ) {
131
+ let i = 0 ;
132
+ return {
133
+ [ Symbol . asyncIterator ] : ( ) => ( {
134
+ async next ( ) {
135
+ const friend = friends [ i ++ ] ;
136
+ if ( friend ) {
137
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
138
+ return { value : friend , done : false } ;
139
+ }
140
+ return { value : undefined , done : true } ;
141
+ } ,
142
+ } ) ,
143
+ } ;
144
+ } ,
145
+ } ,
114
146
asyncIterableListDelayedClose : {
115
147
type : new GraphQLList ( friendType ) ,
116
148
async * resolve ( ) {
@@ -1011,4 +1043,175 @@ describe('Execute: stream directive', () => {
1011
1043
} ,
1012
1044
] ) ;
1013
1045
} ) ;
1046
+ it ( 'Returns underlying async iterables when dispatcher is returned' , async ( ) => {
1047
+ const document = parse ( `
1048
+ query {
1049
+ asyncIterableListDelayed @stream(initialCount: 1) {
1050
+ name
1051
+ id
1052
+ }
1053
+ }
1054
+ ` ) ;
1055
+ const schema = new GraphQLSchema ( { query } ) ;
1056
+
1057
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1058
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1059
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1060
+
1061
+ const result1 = await iterator . next ( ) ;
1062
+ expectJSON ( result1 ) . toDeepEqual ( {
1063
+ done : false ,
1064
+ value : {
1065
+ data : {
1066
+ asyncIterableListDelayed : [
1067
+ {
1068
+ id : '1' ,
1069
+ name : 'Luke' ,
1070
+ } ,
1071
+ ] ,
1072
+ } ,
1073
+ hasNext : true ,
1074
+ } ,
1075
+ } ) ;
1076
+
1077
+ const returnPromise = iterator . return ( ) ;
1078
+
1079
+ // this result had started processing before return was called
1080
+ const result2 = await iterator . next ( ) ;
1081
+ expectJSON ( result2 ) . toDeepEqual ( {
1082
+ done : false ,
1083
+ value : {
1084
+ data : {
1085
+ id : '2' ,
1086
+ name : 'Han' ,
1087
+ } ,
1088
+ hasNext : true ,
1089
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1090
+ } ,
1091
+ } ) ;
1092
+
1093
+ // third result is not returned because async iterator has returned
1094
+ const result3 = await iterator . next ( ) ;
1095
+ expectJSON ( result3 ) . toDeepEqual ( {
1096
+ done : true ,
1097
+ value : undefined ,
1098
+ } ) ;
1099
+ await returnPromise ;
1100
+ } ) ;
1101
+ it ( 'Can return async iterable when underlying iterable does not have a return method' , async ( ) => {
1102
+ const document = parse ( `
1103
+ query {
1104
+ asyncIterableListNoReturn @stream(initialCount: 1) {
1105
+ name
1106
+ id
1107
+ }
1108
+ }
1109
+ ` ) ;
1110
+ const schema = new GraphQLSchema ( { query } ) ;
1111
+
1112
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1113
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1114
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1115
+
1116
+ const result1 = await iterator . next ( ) ;
1117
+ expectJSON ( result1 ) . toDeepEqual ( {
1118
+ done : false ,
1119
+ value : {
1120
+ data : {
1121
+ asyncIterableListNoReturn : [
1122
+ {
1123
+ id : '1' ,
1124
+ name : 'Luke' ,
1125
+ } ,
1126
+ ] ,
1127
+ } ,
1128
+ hasNext : true ,
1129
+ } ,
1130
+ } ) ;
1131
+
1132
+ const returnPromise = iterator . return ( ) ;
1133
+
1134
+ // this result had started processing before return was called
1135
+ const result2 = await iterator . next ( ) ;
1136
+ expectJSON ( result2 ) . toDeepEqual ( {
1137
+ done : false ,
1138
+ value : {
1139
+ data : {
1140
+ id : '2' ,
1141
+ name : 'Han' ,
1142
+ } ,
1143
+ hasNext : true ,
1144
+ path : [ 'asyncIterableListNoReturn' , 1 ] ,
1145
+ } ,
1146
+ } ) ;
1147
+
1148
+ // third result is not returned because async iterator has returned
1149
+ const result3 = await iterator . next ( ) ;
1150
+ expectJSON ( result3 ) . toDeepEqual ( {
1151
+ done : true ,
1152
+ value : undefined ,
1153
+ } ) ;
1154
+ await returnPromise ;
1155
+ } ) ;
1156
+ it ( 'Returns underlying async iterables when dispatcher is thrown' , async ( ) => {
1157
+ const document = parse ( `
1158
+ query {
1159
+ asyncIterableListDelayed @stream(initialCount: 1) {
1160
+ name
1161
+ id
1162
+ }
1163
+ }
1164
+ ` ) ;
1165
+ const schema = new GraphQLSchema ( { query } ) ;
1166
+
1167
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1168
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1169
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1170
+
1171
+ const result1 = await iterator . next ( ) ;
1172
+ expectJSON ( result1 ) . toDeepEqual ( {
1173
+ done : false ,
1174
+ value : {
1175
+ data : {
1176
+ asyncIterableListDelayed : [
1177
+ {
1178
+ id : '1' ,
1179
+ name : 'Luke' ,
1180
+ } ,
1181
+ ] ,
1182
+ } ,
1183
+ hasNext : true ,
1184
+ } ,
1185
+ } ) ;
1186
+
1187
+ const throwPromise = iterator . throw ( new Error ( 'bad' ) ) ;
1188
+
1189
+ // this result had started processing before return was called
1190
+ const result2 = await iterator . next ( ) ;
1191
+ expectJSON ( result2 ) . toDeepEqual ( {
1192
+ done : false ,
1193
+ value : {
1194
+ data : {
1195
+ id : '2' ,
1196
+ name : 'Han' ,
1197
+ } ,
1198
+ hasNext : true ,
1199
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1200
+ } ,
1201
+ } ) ;
1202
+
1203
+ // third result is not returned because async iterator has returned
1204
+ const result3 = await iterator . next ( ) ;
1205
+ expectJSON ( result3 ) . toDeepEqual ( {
1206
+ done : true ,
1207
+ value : undefined ,
1208
+ } ) ;
1209
+ try {
1210
+ await throwPromise ; /* c8 ignore start */
1211
+ // Not reachable, always throws
1212
+ /* c8 ignore stop */
1213
+ } catch ( e ) {
1214
+ // ignore error
1215
+ }
1216
+ } ) ;
1014
1217
} ) ;
0 commit comments