@@ -25,6 +25,7 @@ import {
25
25
} from 'graphql' ;
26
26
27
27
import { graphqlHTTP } from '../index' ;
28
+ import { isAsyncIterable } from '../isAsyncIterable' ;
28
29
29
30
type Middleware = ( req : any , res : any , next : ( ) => void ) => unknown ;
30
31
type Server = ( ) => {
@@ -1027,6 +1028,60 @@ function runTests(server: Server) {
1027
1028
errors : [ { message : 'Must provide query string.' } ] ,
1028
1029
} ) ;
1029
1030
} ) ;
1031
+
1032
+ it ( 'allows for streaming results with @defer' , async ( ) => {
1033
+ const app = server ( ) ;
1034
+ const fakeFlush = sinon . fake ( ) ;
1035
+
1036
+ app . use ( ( _ , res , next ) => {
1037
+ res . flush = fakeFlush ;
1038
+ next ( ) ;
1039
+ } ) ;
1040
+ app . post (
1041
+ urlString ( ) ,
1042
+ graphqlHTTP ( {
1043
+ schema : TestSchema ,
1044
+ } ) ,
1045
+ ) ;
1046
+
1047
+ const req = app
1048
+ . request ( )
1049
+ . post ( urlString ( ) )
1050
+ . send ( {
1051
+ query :
1052
+ '{ ...frag @defer(label: "deferLabel") } fragment frag on QueryRoot { test(who: "World") }' ,
1053
+ } )
1054
+ . parse ( ( res , cb ) => {
1055
+ res . on ( 'data' , ( data ) => {
1056
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
1057
+ } ) ;
1058
+ res . on ( 'end' , ( err ) => {
1059
+ cb ( err , null ) ;
1060
+ } ) ;
1061
+ } ) ;
1062
+
1063
+ const response = await req ;
1064
+ expect ( fakeFlush . callCount ) . to . equal ( 2 ) ;
1065
+ expect ( response . text ) . to . equal (
1066
+ [
1067
+ '' ,
1068
+ '---' ,
1069
+ 'Content-Type: application/json; charset=utf-8' ,
1070
+ 'Content-Length: 26' ,
1071
+ '' ,
1072
+ '{"data":{},"hasNext":true}' ,
1073
+ '' ,
1074
+ '---' ,
1075
+ 'Content-Type: application/json; charset=utf-8' ,
1076
+ 'Content-Length: 78' ,
1077
+ '' ,
1078
+ '{"data":{"test":"Hello World"},"path":[],"label":"deferLabel","hasNext":false}' ,
1079
+ '' ,
1080
+ '-----' ,
1081
+ '' ,
1082
+ ] . join ( '\r\n' ) ,
1083
+ ) ;
1084
+ } ) ;
1030
1085
} ) ;
1031
1086
1032
1087
describe ( 'Pretty printing' , ( ) => {
@@ -1109,6 +1164,62 @@ function runTests(server: Server) {
1109
1164
1110
1165
expect ( unprettyResponse . text ) . to . equal ( '{"data":{"test":"Hello World"}}' ) ;
1111
1166
} ) ;
1167
+ it ( 'supports pretty printing async iterable requests' , async ( ) => {
1168
+ const app = server ( ) ;
1169
+
1170
+ app . post (
1171
+ urlString ( ) ,
1172
+ graphqlHTTP ( {
1173
+ schema : TestSchema ,
1174
+ pretty : true ,
1175
+ } ) ,
1176
+ ) ;
1177
+
1178
+ const req = app
1179
+ . request ( )
1180
+ . post ( urlString ( ) )
1181
+ . send ( {
1182
+ query :
1183
+ '{ ...frag @defer } fragment frag on QueryRoot { test(who: "World") }' ,
1184
+ } )
1185
+ . parse ( ( res , cb ) => {
1186
+ res . on ( 'data' , ( data ) => {
1187
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
1188
+ } ) ;
1189
+ res . on ( 'end' , ( err ) => {
1190
+ cb ( err , null ) ;
1191
+ } ) ;
1192
+ } ) ;
1193
+
1194
+ const response = await req ;
1195
+ expect ( response . text ) . to . equal (
1196
+ [
1197
+ '' ,
1198
+ '---' ,
1199
+ 'Content-Type: application/json; charset=utf-8' ,
1200
+ 'Content-Length: 35' ,
1201
+ '' ,
1202
+ [ '{' , ' "data": {},' , ' "hasNext": true' , '}' ] . join ( '\n' ) ,
1203
+ '' ,
1204
+ '---' ,
1205
+ 'Content-Type: application/json; charset=utf-8' ,
1206
+ 'Content-Length: 79' ,
1207
+ '' ,
1208
+ [
1209
+ '{' ,
1210
+ ' "data": {' ,
1211
+ ' "test": "Hello World"' ,
1212
+ ' },' ,
1213
+ ' "path": [],' ,
1214
+ ' "hasNext": false' ,
1215
+ '}' ,
1216
+ ] . join ( '\n' ) ,
1217
+ '' ,
1218
+ '-----' ,
1219
+ '' ,
1220
+ ] . join ( '\r\n' ) ,
1221
+ ) ;
1222
+ } ) ;
1112
1223
} ) ;
1113
1224
1114
1225
it ( 'will send request and response when using thunk' , async ( ) => {
@@ -1229,6 +1340,108 @@ function runTests(server: Server) {
1229
1340
} ) ;
1230
1341
} ) ;
1231
1342
1343
+ it ( 'allows for custom error formatting in initial payload of async iterator' , async ( ) => {
1344
+ const app = server ( ) ;
1345
+
1346
+ app . post (
1347
+ urlString ( ) ,
1348
+ graphqlHTTP ( {
1349
+ schema : TestSchema ,
1350
+ customFormatErrorFn ( error ) {
1351
+ return { message : 'Custom error format: ' + error . message } ;
1352
+ } ,
1353
+ } ) ,
1354
+ ) ;
1355
+
1356
+ const req = app
1357
+ . request ( )
1358
+ . post ( urlString ( ) )
1359
+ . send ( {
1360
+ query :
1361
+ '{ thrower, ...frag @defer } fragment frag on QueryRoot { test(who: "World") }' ,
1362
+ } )
1363
+ . parse ( ( res , cb ) => {
1364
+ res . on ( 'data' , ( data ) => {
1365
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
1366
+ } ) ;
1367
+ res . on ( 'end' , ( err ) => {
1368
+ cb ( err , null ) ;
1369
+ } ) ;
1370
+ } ) ;
1371
+
1372
+ const response = await req ;
1373
+ expect ( response . text ) . to . equal (
1374
+ [
1375
+ '' ,
1376
+ '---' ,
1377
+ 'Content-Type: application/json; charset=utf-8' ,
1378
+ 'Content-Length: 94' ,
1379
+ '' ,
1380
+ '{"errors":[{"message":"Custom error format: Throws!"}],"data":{"thrower":null},"hasNext":true}' ,
1381
+ '' ,
1382
+ '---' ,
1383
+ 'Content-Type: application/json; charset=utf-8' ,
1384
+ 'Content-Length: 57' ,
1385
+ '' ,
1386
+ '{"data":{"test":"Hello World"},"path":[],"hasNext":false}' ,
1387
+ '' ,
1388
+ '-----' ,
1389
+ '' ,
1390
+ ] . join ( '\r\n' ) ,
1391
+ ) ;
1392
+ } ) ;
1393
+
1394
+ it ( 'allows for custom error formatting in subsequent payloads of async iterator' , async ( ) => {
1395
+ const app = server ( ) ;
1396
+
1397
+ app . post (
1398
+ urlString ( ) ,
1399
+ graphqlHTTP ( {
1400
+ schema : TestSchema ,
1401
+ customFormatErrorFn ( error ) {
1402
+ return { message : 'Custom error format: ' + error . message } ;
1403
+ } ,
1404
+ } ) ,
1405
+ ) ;
1406
+
1407
+ const req = app
1408
+ . request ( )
1409
+ . post ( urlString ( ) )
1410
+ . send ( {
1411
+ query :
1412
+ '{ test(who: "World"), ...frag @defer } fragment frag on QueryRoot { thrower }' ,
1413
+ } )
1414
+ . parse ( ( res , cb ) => {
1415
+ res . on ( 'data' , ( data ) => {
1416
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
1417
+ } ) ;
1418
+ res . on ( 'end' , ( err ) => {
1419
+ cb ( err , null ) ;
1420
+ } ) ;
1421
+ } ) ;
1422
+
1423
+ const response = await req ;
1424
+ expect ( response . text ) . to . equal (
1425
+ [
1426
+ '' ,
1427
+ '---' ,
1428
+ 'Content-Type: application/json; charset=utf-8' ,
1429
+ 'Content-Length: 46' ,
1430
+ '' ,
1431
+ '{"data":{"test":"Hello World"},"hasNext":true}' ,
1432
+ '' ,
1433
+ '---' ,
1434
+ 'Content-Type: application/json; charset=utf-8' ,
1435
+ 'Content-Length: 105' ,
1436
+ '' ,
1437
+ '{"data":{"thrower":null},"path":[],"errors":[{"message":"Custom error format: Throws!"}],"hasNext":false}' ,
1438
+ '' ,
1439
+ '-----' ,
1440
+ '' ,
1441
+ ] . join ( '\r\n' ) ,
1442
+ ) ;
1443
+ } ) ;
1444
+
1232
1445
it ( 'allows for custom error formatting to elaborate' , async ( ) => {
1233
1446
const app = server ( ) ;
1234
1447
@@ -2069,6 +2282,10 @@ function runTests(server: Server) {
2069
2282
async customExecuteFn ( args ) {
2070
2283
seenExecuteArgs = args ;
2071
2284
const result = await Promise . resolve ( execute ( args ) ) ;
2285
+ // istanbul ignore if this test query will never return an async iterable
2286
+ if ( isAsyncIterable ( result ) ) {
2287
+ return result ;
2288
+ }
2072
2289
return {
2073
2290
...result ,
2074
2291
data : {
@@ -2222,6 +2439,57 @@ function runTests(server: Server) {
2222
2439
} ) ;
2223
2440
} ) ;
2224
2441
2442
+ it ( 'allows for custom extensions in initial and subsequent payloads of async iterator' , async ( ) => {
2443
+ const app = server ( ) ;
2444
+
2445
+ app . post (
2446
+ urlString ( ) ,
2447
+ graphqlHTTP ( {
2448
+ schema : TestSchema ,
2449
+ extensions ( { result } ) {
2450
+ return { preservedResult : { ...result } } ;
2451
+ } ,
2452
+ } ) ,
2453
+ ) ;
2454
+
2455
+ const req = app
2456
+ . request ( )
2457
+ . post ( urlString ( ) )
2458
+ . send ( {
2459
+ query :
2460
+ '{ hello: test(who: "Rob"), ...frag @defer } fragment frag on QueryRoot { test(who: "World") }' ,
2461
+ } )
2462
+ . parse ( ( res , cb ) => {
2463
+ res . on ( 'data' , ( data ) => {
2464
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
2465
+ } ) ;
2466
+ res . on ( 'end' , ( err ) => {
2467
+ cb ( err , null ) ;
2468
+ } ) ;
2469
+ } ) ;
2470
+
2471
+ const response = await req ;
2472
+ expect ( response . text ) . to . equal (
2473
+ [
2474
+ '' ,
2475
+ '---' ,
2476
+ 'Content-Type: application/json; charset=utf-8' ,
2477
+ 'Content-Length: 124' ,
2478
+ '' ,
2479
+ '{"data":{"hello":"Hello Rob"},"hasNext":true,"extensions":{"preservedResult":{"data":{"hello":"Hello Rob"},"hasNext":true}}}' ,
2480
+ '' ,
2481
+ '---' ,
2482
+ 'Content-Type: application/json; charset=utf-8' ,
2483
+ 'Content-Length: 148' ,
2484
+ '' ,
2485
+ '{"data":{"test":"Hello World"},"path":[],"hasNext":false,"extensions":{"preservedResult":{"data":{"test":"Hello World"},"path":[],"hasNext":false}}}' ,
2486
+ '' ,
2487
+ '-----' ,
2488
+ '' ,
2489
+ ] . join ( '\r\n' ) ,
2490
+ ) ;
2491
+ } ) ;
2492
+
2225
2493
it ( 'extension function may be async' , async ( ) => {
2226
2494
const app = server ( ) ;
2227
2495
@@ -2262,12 +2530,44 @@ function runTests(server: Server) {
2262
2530
2263
2531
const response = await app
2264
2532
. request ( )
2265
- . get ( urlString ( { query : '{test}' , raw : '' } ) )
2266
- . set ( 'Accept' , 'text/html' ) ;
2533
+ . get (
2534
+ urlString ( {
2535
+ query :
2536
+ '{ hello: test(who: "Rob"), ...frag @defer } fragment frag on QueryRoot { test(who: "World") }' ,
2537
+ raw : '' ,
2538
+ } ) ,
2539
+ )
2540
+ . set ( 'Accept' , 'text/html' )
2541
+ . parse ( ( res , cb ) => {
2542
+ res . on ( 'data' , ( data ) => {
2543
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
2544
+ } ) ;
2545
+ res . on ( 'end' , ( err ) => {
2546
+ cb ( err , null ) ;
2547
+ } ) ;
2548
+ } ) ;
2267
2549
2268
2550
expect ( response . status ) . to . equal ( 200 ) ;
2269
- expect ( response . type ) . to . equal ( 'application/json' ) ;
2270
- expect ( response . text ) . to . equal ( '{"data":{"test":"Hello World"}}' ) ;
2551
+ expect ( response . type ) . to . equal ( 'multipart/mixed' ) ;
2552
+ expect ( response . text ) . to . equal (
2553
+ [
2554
+ '' ,
2555
+ '---' ,
2556
+ 'Content-Type: application/json; charset=utf-8' ,
2557
+ 'Content-Length: 45' ,
2558
+ '' ,
2559
+ '{"data":{"hello":"Hello Rob"},"hasNext":true}' ,
2560
+ '' ,
2561
+ '---' ,
2562
+ 'Content-Type: application/json; charset=utf-8' ,
2563
+ 'Content-Length: 57' ,
2564
+ '' ,
2565
+ '{"data":{"test":"Hello World"},"path":[],"hasNext":false}' ,
2566
+ '' ,
2567
+ '-----' ,
2568
+ '' ,
2569
+ ] . join ( '\r\n' ) ,
2570
+ ) ;
2271
2571
} ) ;
2272
2572
} ) ;
2273
2573
}
0 commit comments