@@ -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 ( ) => {
@@ -1260,6 +1371,108 @@ function runTests(server: Server) {
1260
1371
} ) ;
1261
1372
} ) ;
1262
1373
1374
+ it ( 'allows for custom error formatting in initial payload of async iterator' , async ( ) => {
1375
+ const app = server ( ) ;
1376
+
1377
+ app . post (
1378
+ urlString ( ) ,
1379
+ graphqlHTTP ( {
1380
+ schema : TestSchema ,
1381
+ customFormatErrorFn ( error ) {
1382
+ return { message : 'Custom error format: ' + error . message } ;
1383
+ } ,
1384
+ } ) ,
1385
+ ) ;
1386
+
1387
+ const req = app
1388
+ . request ( )
1389
+ . post ( urlString ( ) )
1390
+ . send ( {
1391
+ query :
1392
+ '{ thrower, ...frag @defer } fragment frag on QueryRoot { test(who: "World") }' ,
1393
+ } )
1394
+ . parse ( ( res , cb ) => {
1395
+ res . on ( 'data' , ( data ) => {
1396
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
1397
+ } ) ;
1398
+ res . on ( 'end' , ( err ) => {
1399
+ cb ( err , null ) ;
1400
+ } ) ;
1401
+ } ) ;
1402
+
1403
+ const response = await req ;
1404
+ expect ( response . text ) . to . equal (
1405
+ [
1406
+ '' ,
1407
+ '---' ,
1408
+ 'Content-Type: application/json; charset=utf-8' ,
1409
+ 'Content-Length: 94' ,
1410
+ '' ,
1411
+ '{"errors":[{"message":"Custom error format: Throws!"}],"data":{"thrower":null},"hasNext":true}' ,
1412
+ '' ,
1413
+ '---' ,
1414
+ 'Content-Type: application/json; charset=utf-8' ,
1415
+ 'Content-Length: 57' ,
1416
+ '' ,
1417
+ '{"data":{"test":"Hello World"},"path":[],"hasNext":false}' ,
1418
+ '' ,
1419
+ '-----' ,
1420
+ '' ,
1421
+ ] . join ( '\r\n' ) ,
1422
+ ) ;
1423
+ } ) ;
1424
+
1425
+ it ( 'allows for custom error formatting in subsequent payloads of async iterator' , async ( ) => {
1426
+ const app = server ( ) ;
1427
+
1428
+ app . post (
1429
+ urlString ( ) ,
1430
+ graphqlHTTP ( {
1431
+ schema : TestSchema ,
1432
+ customFormatErrorFn ( error ) {
1433
+ return { message : 'Custom error format: ' + error . message } ;
1434
+ } ,
1435
+ } ) ,
1436
+ ) ;
1437
+
1438
+ const req = app
1439
+ . request ( )
1440
+ . post ( urlString ( ) )
1441
+ . send ( {
1442
+ query :
1443
+ '{ test(who: "World"), ...frag @defer } fragment frag on QueryRoot { thrower }' ,
1444
+ } )
1445
+ . parse ( ( res , cb ) => {
1446
+ res . on ( 'data' , ( data ) => {
1447
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
1448
+ } ) ;
1449
+ res . on ( 'end' , ( err ) => {
1450
+ cb ( err , null ) ;
1451
+ } ) ;
1452
+ } ) ;
1453
+
1454
+ const response = await req ;
1455
+ expect ( response . text ) . to . equal (
1456
+ [
1457
+ '' ,
1458
+ '---' ,
1459
+ 'Content-Type: application/json; charset=utf-8' ,
1460
+ 'Content-Length: 46' ,
1461
+ '' ,
1462
+ '{"data":{"test":"Hello World"},"hasNext":true}' ,
1463
+ '' ,
1464
+ '---' ,
1465
+ 'Content-Type: application/json; charset=utf-8' ,
1466
+ 'Content-Length: 105' ,
1467
+ '' ,
1468
+ '{"data":{"thrower":null},"path":[],"errors":[{"message":"Custom error format: Throws!"}],"hasNext":false}' ,
1469
+ '' ,
1470
+ '-----' ,
1471
+ '' ,
1472
+ ] . join ( '\r\n' ) ,
1473
+ ) ;
1474
+ } ) ;
1475
+
1263
1476
it ( 'allows for custom error formatting to elaborate' , async ( ) => {
1264
1477
const app = server ( ) ;
1265
1478
@@ -2100,6 +2313,10 @@ function runTests(server: Server) {
2100
2313
async customExecuteFn ( args ) {
2101
2314
seenExecuteArgs = args ;
2102
2315
const result = await Promise . resolve ( execute ( args ) ) ;
2316
+ // istanbul ignore if this test query will never return an async iterable
2317
+ if ( isAsyncIterable ( result ) ) {
2318
+ return result ;
2319
+ }
2103
2320
return {
2104
2321
...result ,
2105
2322
data : {
@@ -2253,6 +2470,57 @@ function runTests(server: Server) {
2253
2470
} ) ;
2254
2471
} ) ;
2255
2472
2473
+ it ( 'allows for custom extensions in initial and subsequent payloads of async iterator' , async ( ) => {
2474
+ const app = server ( ) ;
2475
+
2476
+ app . post (
2477
+ urlString ( ) ,
2478
+ graphqlHTTP ( {
2479
+ schema : TestSchema ,
2480
+ extensions ( { result } ) {
2481
+ return { preservedResult : { ...result } } ;
2482
+ } ,
2483
+ } ) ,
2484
+ ) ;
2485
+
2486
+ const req = app
2487
+ . request ( )
2488
+ . post ( urlString ( ) )
2489
+ . send ( {
2490
+ query :
2491
+ '{ hello: test(who: "Rob"), ...frag @defer } fragment frag on QueryRoot { test(who: "World") }' ,
2492
+ } )
2493
+ . parse ( ( res , cb ) => {
2494
+ res . on ( 'data' , ( data ) => {
2495
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
2496
+ } ) ;
2497
+ res . on ( 'end' , ( err ) => {
2498
+ cb ( err , null ) ;
2499
+ } ) ;
2500
+ } ) ;
2501
+
2502
+ const response = await req ;
2503
+ expect ( response . text ) . to . equal (
2504
+ [
2505
+ '' ,
2506
+ '---' ,
2507
+ 'Content-Type: application/json; charset=utf-8' ,
2508
+ 'Content-Length: 124' ,
2509
+ '' ,
2510
+ '{"data":{"hello":"Hello Rob"},"hasNext":true,"extensions":{"preservedResult":{"data":{"hello":"Hello Rob"},"hasNext":true}}}' ,
2511
+ '' ,
2512
+ '---' ,
2513
+ 'Content-Type: application/json; charset=utf-8' ,
2514
+ 'Content-Length: 148' ,
2515
+ '' ,
2516
+ '{"data":{"test":"Hello World"},"path":[],"hasNext":false,"extensions":{"preservedResult":{"data":{"test":"Hello World"},"path":[],"hasNext":false}}}' ,
2517
+ '' ,
2518
+ '-----' ,
2519
+ '' ,
2520
+ ] . join ( '\r\n' ) ,
2521
+ ) ;
2522
+ } ) ;
2523
+
2256
2524
it ( 'extension function may be async' , async ( ) => {
2257
2525
const app = server ( ) ;
2258
2526
@@ -2293,12 +2561,44 @@ function runTests(server: Server) {
2293
2561
2294
2562
const response = await app
2295
2563
. request ( )
2296
- . get ( urlString ( { query : '{test}' , raw : '' } ) )
2297
- . set ( 'Accept' , 'text/html' ) ;
2564
+ . get (
2565
+ urlString ( {
2566
+ query :
2567
+ '{ hello: test(who: "Rob"), ...frag @defer } fragment frag on QueryRoot { test(who: "World") }' ,
2568
+ raw : '' ,
2569
+ } ) ,
2570
+ )
2571
+ . set ( 'Accept' , 'text/html' )
2572
+ . parse ( ( res , cb ) => {
2573
+ res . on ( 'data' , ( data ) => {
2574
+ res . text = `${ res . text || '' } ${ data . toString ( 'utf8' ) as string } ` ;
2575
+ } ) ;
2576
+ res . on ( 'end' , ( err ) => {
2577
+ cb ( err , null ) ;
2578
+ } ) ;
2579
+ } ) ;
2298
2580
2299
2581
expect ( response . status ) . to . equal ( 200 ) ;
2300
- expect ( response . type ) . to . equal ( 'application/json' ) ;
2301
- expect ( response . text ) . to . equal ( '{"data":{"test":"Hello World"}}' ) ;
2582
+ expect ( response . type ) . to . equal ( 'multipart/mixed' ) ;
2583
+ expect ( response . text ) . to . equal (
2584
+ [
2585
+ '' ,
2586
+ '---' ,
2587
+ 'Content-Type: application/json; charset=utf-8' ,
2588
+ 'Content-Length: 45' ,
2589
+ '' ,
2590
+ '{"data":{"hello":"Hello Rob"},"hasNext":true}' ,
2591
+ '' ,
2592
+ '---' ,
2593
+ 'Content-Type: application/json; charset=utf-8' ,
2594
+ 'Content-Length: 57' ,
2595
+ '' ,
2596
+ '{"data":{"test":"Hello World"},"path":[],"hasNext":false}' ,
2597
+ '' ,
2598
+ '-----' ,
2599
+ '' ,
2600
+ ] . join ( '\r\n' ) ,
2601
+ ) ;
2302
2602
} ) ;
2303
2603
} ) ;
2304
2604
}
0 commit comments