Skip to content

Commit

Permalink
WIP: GraphQL SSE Single Connection mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Feb 17, 2023
1 parent 53d0f9b commit 3111e25
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 136 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ describe('GraphQL SSE Client compatibility', () => {
fetchFn: yoga.fetch,
abortControllerImpl: yoga.fetchAPI.AbortController,
retryAttempts: 0,
singleConnection: true,
})
let unsubscribe: () => void
afterAll(() => {
Expand Down
2 changes: 2 additions & 0 deletions packages/graphql-yoga/src/plugins/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ export interface OnResultProcessEventPayload {
resultProcessor: ResultProcessor,
acceptedMediaType: string,
): void
fetchAPI: FetchAPI
endResponse(response: Response): void
}

export type OnResponseHook<TServerContext> = (
Expand Down
8 changes: 8 additions & 0 deletions packages/graphql-yoga/src/process-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export async function processResult({
const acceptableMediaTypes: string[] = []
let acceptedMediaType = '*/*'

let earlyResponse: Response | undefined
for (const onResultProcessHook of onResultProcessHooks) {
await onResultProcessHook({
request,
Expand All @@ -37,7 +38,14 @@ export async function processResult({
resultProcessor = newResultProcessor
acceptedMediaType = newAcceptedMimeType
},
fetchAPI,
endResponse(response) {
earlyResponse = response
},
})
if (earlyResponse) {
return earlyResponse
}
}

// If no result processor found for this result, return an error
Expand Down
143 changes: 73 additions & 70 deletions packages/plugins/graphql-sse/__tests__/graphql-sse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,37 +33,46 @@ describe('graphql-sse', () => {
const yoga = createYoga({
schema,
plugins: [useGraphQLSSE()],
sse: {
graphqlSSEDistinctConnections: true,
},
maskedErrors: false,
})

let client: ReturnType<typeof createClient>

afterEach(() => {
client?.dispose()
})

it('should stream using distinct connection mode', async () => {
const client = createClient({
url: 'http://yoga/graphql/stream',
client = createClient({
url: 'http://yoga/graphql',
fetchFn: yoga.fetch,
abortControllerImpl: yoga.fetchAPI.AbortController,
singleConnection: false, // distinct connection mode
retryAttempts: 0,
})

await expect(
new Promise((resolve, reject) => {
const msgs: unknown[] = []
client.subscribe(
{
query: /* GraphQL */ `
subscription {
greetings
}
`,
},
{
next: (msg) => msgs.push(msg),
error: reject,
complete: () => resolve(msgs),
},
)
}),
).resolves.toMatchInlineSnapshot(`
const result = await new Promise((resolve, reject) => {
const msgs: unknown[] = []
client.subscribe(
{
query: /* GraphQL */ `
subscription {
greetings
}
`,
},
{
next: (msg) => msgs.push(msg),
error: reject,
complete: () => resolve(msgs),
},
)
})

expect(result).toMatchInlineSnapshot(`
[
{
"data": {
Expand Down Expand Up @@ -92,39 +101,37 @@ describe('graphql-sse', () => {
},
]
`)

client.dispose()
})

it('should stream using single connection and lazy mode', async () => {
const client = createClient({
url: 'http://yoga/graphql/stream',
client = createClient({
url: 'http://yoga/graphql',
fetchFn: yoga.fetch,
abortControllerImpl: yoga.fetchAPI.AbortController,
singleConnection: true, // single connection mode
lazy: true,
retryAttempts: 0,
})

await expect(
new Promise((resolve, reject) => {
const msgs: unknown[] = []
client.subscribe(
{
query: /* GraphQL */ `
subscription {
greetings
}
`,
},
{
next: (msg) => msgs.push(msg),
error: reject,
complete: () => resolve(msgs),
},
)
}),
).resolves.toMatchInlineSnapshot(`
const result = await new Promise((resolve, reject) => {
const msgs: unknown[] = []
client.subscribe(
{
query: /* GraphQL */ `
subscription {
greetings
}
`,
},
{
next: (msg) => msgs.push(msg),
error: reject,
complete: () => resolve(msgs),
},
)
})

expect(result).toMatchInlineSnapshot(`
[
{
"data": {
Expand Down Expand Up @@ -153,39 +160,37 @@ describe('graphql-sse', () => {
},
]
`)

client.dispose()
})

it('should stream using single connection and non-lazy mode', async () => {
const client = createClient({
url: 'http://yoga/graphql/stream',
client = createClient({
url: 'http://yoga/graphql',
fetchFn: yoga.fetch,
abortControllerImpl: yoga.fetchAPI.AbortController,
singleConnection: true, // single connection mode
lazy: false,
retryAttempts: 0,
})

await expect(
new Promise((resolve, reject) => {
const msgs: unknown[] = []
client.subscribe(
{
query: /* GraphQL */ `
subscription {
greetings
}
`,
},
{
next: (msg) => msgs.push(msg),
error: reject,
complete: () => resolve(msgs),
},
)
}),
).resolves.toMatchInlineSnapshot(`
const result = await new Promise((resolve, reject) => {
const msgs: unknown[] = []
client.subscribe(
{
query: /* GraphQL */ `
subscription {
greetings
}
`,
},
{
next: (msg) => msgs.push(msg),
error: reject,
complete: () => resolve(msgs),
},
)
})

expect(result).toMatchInlineSnapshot(`
[
{
"data": {
Expand Down Expand Up @@ -214,8 +219,6 @@ describe('graphql-sse', () => {
},
]
`)

client.dispose()
})

it('should use CORS settings from the server', async () => {
Expand All @@ -231,7 +234,7 @@ describe('graphql-sse', () => {
maskedErrors: false,
})

const res = await yoga.fetch('http://yoga/graphql/stream', {
const res = await yoga.fetch('http://yoga/graphql', {
method: 'OPTIONS',
headers: {
origin: 'http://yoga',
Expand Down
Loading

0 comments on commit 3111e25

Please sign in to comment.