@@ -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' ;
@@ -104,6 +105,37 @@ const query = new GraphQLObjectType({
104
105
yield await Promise . resolve ( { } ) ;
105
106
} ,
106
107
} ,
108
+ asyncIterableListDelayed : {
109
+ type : new GraphQLList ( friendType ) ,
110
+ async * resolve ( ) {
111
+ for ( const friend of friends ) {
112
+ // pause an additional ms before yielding to allow time
113
+ // for tests to return or throw before next value is processed.
114
+ // eslint-disable-next-line no-await-in-loop
115
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
116
+ yield friend ; /* c8 ignore start */
117
+ // Not reachable, early return
118
+ }
119
+ } /* c8 ignore stop */ ,
120
+ } ,
121
+ asyncIterableListNoReturn : {
122
+ type : new GraphQLList ( friendType ) ,
123
+ resolve ( ) {
124
+ let i = 0 ;
125
+ return {
126
+ [ Symbol . asyncIterator ] : ( ) => ( {
127
+ async next ( ) {
128
+ const friend = friends [ i ++ ] ;
129
+ if ( friend ) {
130
+ await new Promise ( ( r ) => setTimeout ( r , 1 ) ) ;
131
+ return { value : friend , done : false } ;
132
+ }
133
+ return { value : undefined , done : true } ;
134
+ } ,
135
+ } ) ,
136
+ } ;
137
+ } ,
138
+ } ,
107
139
asyncIterableListDelayedClose : {
108
140
type : new GraphQLList ( friendType ) ,
109
141
async * resolve ( ) {
@@ -973,4 +1005,175 @@ describe('Execute: stream directive', () => {
973
1005
} ,
974
1006
] ) ;
975
1007
} ) ;
1008
+ it ( 'Returns underlying async iterables when dispatcher is returned' , async ( ) => {
1009
+ const document = parse ( `
1010
+ query {
1011
+ asyncIterableListDelayed @stream(initialCount: 1) {
1012
+ name
1013
+ id
1014
+ }
1015
+ }
1016
+ ` ) ;
1017
+ const schema = new GraphQLSchema ( { query } ) ;
1018
+
1019
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1020
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1021
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1022
+
1023
+ const result1 = await iterator . next ( ) ;
1024
+ expectJSON ( result1 ) . toDeepEqual ( {
1025
+ done : false ,
1026
+ value : {
1027
+ data : {
1028
+ asyncIterableListDelayed : [
1029
+ {
1030
+ id : '1' ,
1031
+ name : 'Luke' ,
1032
+ } ,
1033
+ ] ,
1034
+ } ,
1035
+ hasNext : true ,
1036
+ } ,
1037
+ } ) ;
1038
+
1039
+ const returnPromise = iterator . return ( ) ;
1040
+
1041
+ // this result had started processing before return was called
1042
+ const result2 = await iterator . next ( ) ;
1043
+ expectJSON ( result2 ) . toDeepEqual ( {
1044
+ done : false ,
1045
+ value : {
1046
+ data : {
1047
+ id : '2' ,
1048
+ name : 'Han' ,
1049
+ } ,
1050
+ hasNext : true ,
1051
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1052
+ } ,
1053
+ } ) ;
1054
+
1055
+ // third result is not returned because async iterator has returned
1056
+ const result3 = await iterator . next ( ) ;
1057
+ expectJSON ( result3 ) . toDeepEqual ( {
1058
+ done : true ,
1059
+ value : undefined ,
1060
+ } ) ;
1061
+ await returnPromise ;
1062
+ } ) ;
1063
+ it ( 'Can return async iterable when underlying iterable does not have a return method' , async ( ) => {
1064
+ const document = parse ( `
1065
+ query {
1066
+ asyncIterableListNoReturn @stream(initialCount: 1) {
1067
+ name
1068
+ id
1069
+ }
1070
+ }
1071
+ ` ) ;
1072
+ const schema = new GraphQLSchema ( { query } ) ;
1073
+
1074
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1075
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1076
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1077
+
1078
+ const result1 = await iterator . next ( ) ;
1079
+ expectJSON ( result1 ) . toDeepEqual ( {
1080
+ done : false ,
1081
+ value : {
1082
+ data : {
1083
+ asyncIterableListNoReturn : [
1084
+ {
1085
+ id : '1' ,
1086
+ name : 'Luke' ,
1087
+ } ,
1088
+ ] ,
1089
+ } ,
1090
+ hasNext : true ,
1091
+ } ,
1092
+ } ) ;
1093
+
1094
+ const returnPromise = iterator . return ( ) ;
1095
+
1096
+ // this result had started processing before return was called
1097
+ const result2 = await iterator . next ( ) ;
1098
+ expectJSON ( result2 ) . toDeepEqual ( {
1099
+ done : false ,
1100
+ value : {
1101
+ data : {
1102
+ id : '2' ,
1103
+ name : 'Han' ,
1104
+ } ,
1105
+ hasNext : true ,
1106
+ path : [ 'asyncIterableListNoReturn' , 1 ] ,
1107
+ } ,
1108
+ } ) ;
1109
+
1110
+ // third result is not returned because async iterator has returned
1111
+ const result3 = await iterator . next ( ) ;
1112
+ expectJSON ( result3 ) . toDeepEqual ( {
1113
+ done : true ,
1114
+ value : undefined ,
1115
+ } ) ;
1116
+ await returnPromise ;
1117
+ } ) ;
1118
+ it ( 'Returns underlying async iterables when dispatcher is thrown' , async ( ) => {
1119
+ const document = parse ( `
1120
+ query {
1121
+ asyncIterableListDelayed @stream(initialCount: 1) {
1122
+ name
1123
+ id
1124
+ }
1125
+ }
1126
+ ` ) ;
1127
+ const schema = new GraphQLSchema ( { query } ) ;
1128
+
1129
+ const executeResult = await execute ( { schema, document, rootValue : { } } ) ;
1130
+ invariant ( isAsyncIterable ( executeResult ) ) ;
1131
+ const iterator = executeResult [ Symbol . asyncIterator ] ( ) ;
1132
+
1133
+ const result1 = await iterator . next ( ) ;
1134
+ expectJSON ( result1 ) . toDeepEqual ( {
1135
+ done : false ,
1136
+ value : {
1137
+ data : {
1138
+ asyncIterableListDelayed : [
1139
+ {
1140
+ id : '1' ,
1141
+ name : 'Luke' ,
1142
+ } ,
1143
+ ] ,
1144
+ } ,
1145
+ hasNext : true ,
1146
+ } ,
1147
+ } ) ;
1148
+
1149
+ const throwPromise = iterator . throw ( new Error ( 'bad' ) ) ;
1150
+
1151
+ // this result had started processing before return was called
1152
+ const result2 = await iterator . next ( ) ;
1153
+ expectJSON ( result2 ) . toDeepEqual ( {
1154
+ done : false ,
1155
+ value : {
1156
+ data : {
1157
+ id : '2' ,
1158
+ name : 'Han' ,
1159
+ } ,
1160
+ hasNext : true ,
1161
+ path : [ 'asyncIterableListDelayed' , 1 ] ,
1162
+ } ,
1163
+ } ) ;
1164
+
1165
+ // third result is not returned because async iterator has returned
1166
+ const result3 = await iterator . next ( ) ;
1167
+ expectJSON ( result3 ) . toDeepEqual ( {
1168
+ done : true ,
1169
+ value : undefined ,
1170
+ } ) ;
1171
+ try {
1172
+ await throwPromise ; /* c8 ignore start */
1173
+ // Not reachable, always throws
1174
+ /* c8 ignore stop */
1175
+ } catch ( e ) {
1176
+ // ignore error
1177
+ }
1178
+ } ) ;
976
1179
} ) ;
0 commit comments