Skip to content

Commit

Permalink
Start SSE stream with a ping (#2958)
Browse files Browse the repository at this point in the history
  • Loading branch information
enisdenjo committed Aug 10, 2023
1 parent 266492e commit 5f18200
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 79 deletions.
5 changes: 5 additions & 0 deletions .changeset/polite-rivers-brake.md
@@ -0,0 +1,5 @@
---
'graphql-yoga': patch
---

Start SSE stream with a ping
24 changes: 14 additions & 10 deletions examples/generic-auth/__integration-tests__/generic-auth.spec.ts
Expand Up @@ -76,13 +76,15 @@ describe('graphql-auth example integration', () => {
},
);
await expect(response.text()).resolves.toMatchInlineSnapshot(`
"event: next
data: {"data":{"requiresAuth":"hi foo@foo.com"}}
":
event: complete
event: next
data: {"data":{"requiresAuth":"hi foo@foo.com"}}
"
`);
event: complete
"
`);
});

it('should not execute on auth required field with subscription', async () => {
Expand All @@ -95,12 +97,14 @@ describe('graphql-auth example integration', () => {
},
);
await expect(response.text()).resolves.toMatchInlineSnapshot(`
"event: next
data: {"data":null,"errors":[{"message":"Accessing 'Subscription.requiresAuth' requires authentication.","locations":[{"line":1,"column":14}]}]}
":
event: next
data: {"data":null,"errors":[{"message":"Accessing 'Subscription.requiresAuth' requires authentication.","locations":[{"line":1,"column":14}]}]}
event: complete
event: complete
"
`);
"
`);
});
});
28 changes: 15 additions & 13 deletions examples/hapi/__integration-tests__/hapi.spec.ts
Expand Up @@ -58,24 +58,26 @@ describe('hapi example integration', () => {
});

await expect(res.text()).resolves.toMatchInlineSnapshot(`
"event: next
data: {"data":{"greetings":"Hi"}}
":
event: next
data: {"data":{"greetings":"Bonjour"}}
event: next
data: {"data":{"greetings":"Hi"}}
event: next
data: {"data":{"greetings":"Hola"}}
event: next
data: {"data":{"greetings":"Bonjour"}}
event: next
data: {"data":{"greetings":"Ciao"}}
event: next
data: {"data":{"greetings":"Hola"}}
event: next
data: {"data":{"greetings":"Zdravo"}}
event: next
data: {"data":{"greetings":"Ciao"}}
event: complete
event: next
data: {"data":{"greetings":"Zdravo"}}
"
`);
event: complete
"
`);
});
});
12 changes: 7 additions & 5 deletions examples/node-ts/__integration-tests__/node-ts.spec.ts
Expand Up @@ -19,12 +19,14 @@ describe('node-ts example integration', () => {

expect(response.status).toBe(400);
expect(await response.text()).toMatchInlineSnapshot(`
"event: next
data: {"errors":[{"message":"Subscriptions have been disabled"}]}
":
event: complete
event: next
data: {"errors":[{"message":"Subscriptions have been disabled"}]}
"
`);
event: complete
"
`);
});
});
28 changes: 15 additions & 13 deletions examples/pothos/__integration-tests__/pothos.spec.ts
Expand Up @@ -17,24 +17,26 @@ describe('pothos example integration', () => {

expect(response.status).toBe(200);
expect(await response.text()).toMatchInlineSnapshot(`
"event: next
data: {"data":{"greetings":"Hi"}}
":
event: next
data: {"data":{"greetings":"Bonjour"}}
event: next
data: {"data":{"greetings":"Hi"}}
event: next
data: {"data":{"greetings":"Hola"}}
event: next
data: {"data":{"greetings":"Bonjour"}}
event: next
data: {"data":{"greetings":"Ciao"}}
event: next
data: {"data":{"greetings":"Hola"}}
event: next
data: {"data":{"greetings":"Zdravo"}}
event: next
data: {"data":{"greetings":"Ciao"}}
event: complete
event: next
data: {"data":{"greetings":"Zdravo"}}
"
`);
event: complete
"
`);
});
});
26 changes: 15 additions & 11 deletions packages/graphql-yoga/__tests__/graphql-sse.spec.ts
Expand Up @@ -52,16 +52,18 @@ describe('GraphQL over SSE', () => {
});
expect(res.ok).toBeTruthy();
await expect(res.text()).resolves.toMatchInlineSnapshot(`
":
":
:
:
:
:
event: complete
:
"
`);
event: complete
"
`);
});

it('should support single result operations', async () => {
Expand Down Expand Up @@ -170,13 +172,15 @@ describe('GraphQL over SSE', () => {
});
expect(res.ok).toBeTruthy();
await expect(res.text()).resolves.toMatchInlineSnapshot(`
"event: next
data: {"errors":[{"message":"Cannot query field \\"nope\\" on type \\"Query\\".","locations":[{"line":1,"column":2}]}]}
":
event: complete
event: next
data: {"errors":[{"message":"Cannot query field \\"nope\\" on type \\"Query\\".","locations":[{"line":1,"column":2}]}]}
"
`);
event: complete
"
`);
});

it('accept: application/graphql-response+json, application/json, multipart/mixed, text/event-stream', async () => {
Expand Down
32 changes: 18 additions & 14 deletions packages/graphql-yoga/__tests__/subscriptions.spec.ts
Expand Up @@ -297,16 +297,18 @@ describe('Subscription', () => {
const text = await response.text();

expect(text).toMatchInlineSnapshot(`
"event: next
data: {"data":{"hi":"hi"}}
":
event: next
data: {"errors":[{"message":"Unexpected error.","locations":[{"line":2,"column":11}]}]}
event: next
data: {"data":{"hi":"hi"}}
event: complete
event: next
data: {"errors":[{"message":"Unexpected error.","locations":[{"line":2,"column":11}]}]}
"
`);
event: complete
"
`);

expect(logging.error).toBeCalledTimes(1);
expect(logging.error.mock.calls[0]).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -363,16 +365,18 @@ describe('Subscription', () => {
const text = await response.text();

expect(text).toMatchInlineSnapshot(`
"event: next
data: {"data":{"hi":"hi"}}
":
event: next
data: {"errors":[{"message":"hi","locations":[{"line":2,"column":11}]}]}
event: next
data: {"data":{"hi":"hi"}}
event: complete
event: next
data: {"errors":[{"message":"hi","locations":[{"line":2,"column":11}]}]}
"
`);
event: complete
"
`);

expect(logging.error).toBeCalledTimes(0);
});
Expand Down
4 changes: 4 additions & 0 deletions packages/graphql-yoga/src/plugins/result-processor/sse.ts
Expand Up @@ -29,6 +29,10 @@ export function getSSEProcessor(): ResultProcessor {
const textEncoder = new fetchAPI.TextEncoder();
const readableStream = new fetchAPI.ReadableStream({
start(controller) {
// always start with a ping because some browsers dont accept a header flush
// causing the fetch to stall until something is streamed through the response
controller.enqueue(textEncoder.encode(':\n\n'));

// ping client every 12 seconds to keep the connection alive
pingInterval = setInterval(() => {
if (!controller.desiredSize) {
Expand Down
28 changes: 15 additions & 13 deletions packages/nestjs/__tests__/subscriptions.spec.ts
Expand Up @@ -32,23 +32,25 @@ it('should subscribe using sse', async () => {
});

await expect(sub.text()).resolves.toMatchInlineSnapshot(`
"event: next
data: {"data":{"greetings":"Hi"}}
":
event: next
data: {"data":{"greetings":"Bonjour"}}
event: next
data: {"data":{"greetings":"Hi"}}
event: next
data: {"data":{"greetings":"Hola"}}
event: next
data: {"data":{"greetings":"Bonjour"}}
event: next
data: {"data":{"greetings":"Ciao"}}
event: next
data: {"data":{"greetings":"Hola"}}
event: next
data: {"data":{"greetings":"Zdravo"}}
event: next
data: {"data":{"greetings":"Ciao"}}
event: complete
event: next
data: {"data":{"greetings":"Zdravo"}}
"
`);
event: complete
"
`);
});

0 comments on commit 5f18200

Please sign in to comment.