@@ -25,6 +25,7 @@ import {
2525} from 'graphql' ;
2626
2727import { graphqlHTTP } from '../index' ;
28+ import { isAsyncIterable } from '../isAsyncIterable' ;
2829
2930type Middleware = ( req : any , res : any , next : ( ) => void ) => unknown ;
3031type Server = ( ) => {
@@ -1027,6 +1028,60 @@ function runTests(server: Server) {
10271028 errors : [ { message : 'Must provide query string.' } ] ,
10281029 } ) ;
10291030 } ) ;
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+ } ) ;
10301085 } ) ;
10311086
10321087 describe ( 'Pretty printing' , ( ) => {
@@ -1109,6 +1164,62 @@ function runTests(server: Server) {
11091164
11101165 expect ( unprettyResponse . text ) . to . equal ( '{"data":{"test":"Hello World"}}' ) ;
11111166 } ) ;
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+ } ) ;
11121223 } ) ;
11131224
11141225 it ( 'will send request and response when using thunk' , async ( ) => {
@@ -1229,6 +1340,108 @@ function runTests(server: Server) {
12291340 } ) ;
12301341 } ) ;
12311342
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+
12321445 it ( 'allows for custom error formatting to elaborate' , async ( ) => {
12331446 const app = server ( ) ;
12341447
@@ -2069,6 +2282,10 @@ function runTests(server: Server) {
20692282 async customExecuteFn ( args ) {
20702283 seenExecuteArgs = args ;
20712284 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+ }
20722289 return {
20732290 ...result ,
20742291 data : {
@@ -2222,6 +2439,57 @@ function runTests(server: Server) {
22222439 } ) ;
22232440 } ) ;
22242441
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+
22252493 it ( 'extension function may be async' , async ( ) => {
22262494 const app = server ( ) ;
22272495
@@ -2262,12 +2530,44 @@ function runTests(server: Server) {
22622530
22632531 const response = await app
22642532 . 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+ } ) ;
22672549
22682550 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+ ) ;
22712571 } ) ;
22722572 } ) ;
22732573}
0 commit comments