Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix payload parsing for subscriptions #1148

Merged
merged 5 commits into from
Sep 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions src/subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,17 +84,13 @@ export class SubscriptionManager {
throw new Error(`Subscription '${name}' is not available`);
}

const { document, operationName, variables } = this.operations.get(name)!;

logger.info(`[Subscription] Start ${id}`, event);

const result = await this.execute({
id,
name,
url: event.url,
document,
operationName,
variables,
variables: event.variables,
contextValue,
});

Expand Down Expand Up @@ -151,22 +147,19 @@ export class SubscriptionManager {

private async execute({
id,
document,
name,
url,
operationName,
variables,
contextValue,
}: {
id: ID;
name: SubscriptionFieldName;
url: string;
document: DocumentNode;
operationName: string;
variables: Record<string, any>;
contextValue: ContextValue;
}) {
const variableNodes = this.operations.get(name)!.variables;
const { document, operationName, variables: variableNodes } = this.operations.get(name)!;

const variableValues = variableNodes.reduce((values, variable) => {
const value = parseVariable({
value: variables[variable.variable.name.value],
Expand All @@ -180,7 +173,7 @@ export class SubscriptionManager {

return {
...values,
[name]: value,
[variable.variable.name.value]: value,
};
}, {});

Expand Down
57 changes: 53 additions & 4 deletions tests/subscriptions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jest.mock('cross-undici-fetch', () => ({

import { fetch } from 'cross-undici-fetch';
import { makeExecutableSchema } from '@graphql-tools/schema';
import { PubSub } from 'graphql-subscriptions';
import { PubSub, withFilter } from 'graphql-subscriptions';
import supertest from 'supertest';
import express from 'express';
import bodyParser from 'body-parser';
Expand All @@ -21,19 +21,23 @@ const delay = (ms: number) => {
const testBook1 = {
id: 'book-id-1',
title: 'Test Book 1',
author: 'Test Author 1',
};
const testBook2 = {
id: 'book-id-2',
title: 'Test Book 2',
author: 'Test Author 2',
};
const BOOK_ADDED = 'BOOK_ADDED';
const typeDefs = /* GraphQL */ `
type Book {
id: ID!
title: String!
author: String!
}
type Subscription {
onBook: Book!
onBook: Book!,
onBookBy(author: String!): Book!
}
type Query {
books: [Book!]
Expand Down Expand Up @@ -75,7 +79,7 @@ test('should start subscriptions', async () => {
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: { onBook: { id: 'book-id-1', title: 'Test Book 1' } },
data: { onBook: { id: 'book-id-1', title: 'Test Book 1', author: 'Test Author 1' } },
}),
});
pubsub.publish(BOOK_ADDED, { onBook: testBook2 });
Expand All @@ -88,7 +92,7 @@ test('should start subscriptions', async () => {
},
body: JSON.stringify({
data: {
onBook: { id: 'book-id-2', title: 'Test Book 2' }
onBook: { id: 'book-id-2', title: 'Test Book 2', author: 'Test Author 2' }
}
}),
});
Expand Down Expand Up @@ -127,3 +131,48 @@ test('should stop subscriptions', async () => {
await delay(1000);
expect(fetch).toBeCalledTimes(1);
});

test('should start subscriptions with parameters', async () => {
(fetch as jest.Mock).mockClear();
const pubsub = new PubSub();
const sofa = useSofa({
basePath: '/api',
schema: makeExecutableSchema({
typeDefs,
resolvers: {
Subscription: {
onBookBy: {
subscribe: withFilter(() => pubsub.asyncIterator(BOOK_ADDED), ( { onBookBy: { author: publishedBy } }, { author: targetAuthor }) => {
return publishedBy === targetAuthor;
}),
},
},
},
}),
});

const app = express();
app.use(bodyParser.json());
app.use('/api', sofa);

const res = await supertest(app)
.post('/api/webhook')
.send({ subscription: 'onBookBy', url: '/bookBy', variables: { author: 'Test Author 1' } })
.expect(200);
expect(res.body).toEqual({ id: expect.any(String) });
pubsub.publish(BOOK_ADDED, { onBookBy: testBook1 });
await delay(1000);
expect(fetch).toBeCalledTimes(1);
expect(fetch).toBeCalledWith('/bookBy', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
data: { onBookBy: { id: 'book-id-1', title: 'Test Book 1', author: 'Test Author 1' } },
}),
});
pubsub.publish(BOOK_ADDED, { onBookBy: testBook2 });
await delay(1000);
expect(fetch).toBeCalledTimes(1);
});