fix(subscriptions): Don't publish undefined

Fix #3088
This commit is contained in:
roschaefer 2020-02-18 15:20:48 +01:00
parent 44d503c53c
commit f32bfc7e36
2 changed files with 75 additions and 25 deletions

View File

@ -2,11 +2,21 @@ import extractMentionedUsers from './mentions/extractMentionedUsers'
import { validateNotifyUsers } from '../validation/validationMiddleware' import { validateNotifyUsers } from '../validation/validationMiddleware'
import { pubsub, NOTIFICATION_ADDED } from '../../server' import { pubsub, NOTIFICATION_ADDED } from '../../server'
const publishNotifications = async (...promises) => {
const notifications = await Promise.all(promises)
notifications
.flat()
.forEach(notificationAdded => pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }))
}
const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => { const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => {
const idsOfUsers = extractMentionedUsers(args.content) const idsOfUsers = extractMentionedUsers(args.content)
const post = await resolve(root, args, context, resolveInfo) const post = await resolve(root, args, context, resolveInfo)
if (post && idsOfUsers && idsOfUsers.length) if (post) {
await notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context) await publishNotifications(
notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context),
)
}
return post return post
} }
@ -16,10 +26,10 @@ const handleContentDataOfComment = async (resolve, root, args, context, resolveI
const comment = await resolve(root, args, context, resolveInfo) const comment = await resolve(root, args, context, resolveInfo)
const [postAuthor] = await postAuthorOfComment(comment.id, { context }) const [postAuthor] = await postAuthorOfComment(comment.id, { context })
idsOfUsers = idsOfUsers.filter(id => id !== postAuthor.id) idsOfUsers = idsOfUsers.filter(id => id !== postAuthor.id)
if (idsOfUsers && idsOfUsers.length) await publishNotifications(
await notifyUsersOfMention('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context) notifyUsersOfMention('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context),
if (context.user.id !== postAuthor.id) notifyUsersOfComment('Comment', comment.id, postAuthor.id, 'commented_on_post', context),
await notifyUsersOfComment('Comment', comment.id, postAuthor.id, 'commented_on_post', context) )
return comment return comment
} }
@ -43,6 +53,7 @@ const postAuthorOfComment = async (commentId, { context }) => {
} }
const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => { const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
if (!(idsOfUsers && idsOfUsers.length)) return []
await validateNotifyUsers(label, reason) await validateNotifyUsers(label, reason)
let mentionedCypher let mentionedCypher
switch (reason) { switch (reason) {
@ -91,8 +102,8 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
return notificationTransactionResponse.records.map(record => record.get('notification')) return notificationTransactionResponse.records.map(record => record.get('notification'))
}) })
try { try {
const [notification] = await writeTxResultPromise const notifications = await writeTxResultPromise
return pubsub.publish(NOTIFICATION_ADDED, { notificationAdded: notification }) return notifications
} catch (error) { } catch (error) {
throw new Error(error) throw new Error(error)
} finally { } finally {
@ -101,6 +112,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
} }
const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, context) => { const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, context) => {
if (!(context.user.id !== postAuthorId)) return []
await validateNotifyUsers(label, reason) await validateNotifyUsers(label, reason)
const session = context.driver.session() const session = context.driver.session()
const writeTxResultPromise = await session.writeTransaction(async transaction => { const writeTxResultPromise = await session.writeTransaction(async transaction => {
@ -121,8 +133,8 @@ const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, cont
return notificationTransactionResponse.records.map(record => record.get('notification')) return notificationTransactionResponse.records.map(record => record.get('notification'))
}) })
try { try {
const [notification] = await writeTxResultPromise const notifications = await writeTxResultPromise
return pubsub.publish(NOTIFICATION_ADDED, { notificationAdded: notification }) return notifications
} finally { } finally {
session.close() session.close()
} }

View File

@ -2,9 +2,10 @@ import { gql } from '../../helpers/jest'
import { cleanDatabase } from '../../db/factories' import { cleanDatabase } from '../../db/factories'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import { getNeode, getDriver } from '../../db/neo4j' import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server' import createServer, { pubsub } from '../../server'
let server, query, mutate, notifiedUser, authenticatedUser let server, query, mutate, notifiedUser, authenticatedUser
let publishSpy
const driver = getDriver() const driver = getDriver()
const neode = getNeode() const neode = getNeode()
const categoryIds = ['cat9'] const categoryIds = ['cat9']
@ -36,6 +37,7 @@ const createCommentMutation = gql`
beforeAll(async () => { beforeAll(async () => {
await cleanDatabase() await cleanDatabase()
publishSpy = jest.spyOn(pubsub, 'publish')
const createServerResult = createServer({ const createServerResult = createServer({
context: () => { context: () => {
return { return {
@ -52,6 +54,7 @@ beforeAll(async () => {
}) })
beforeEach(async () => { beforeEach(async () => {
publishSpy.mockClear()
notifiedUser = await neode.create( notifiedUser = await neode.create(
'User', 'User',
{ {
@ -259,7 +262,15 @@ describe('notifications', () => {
await createPostAction() await createPostAction()
const expectedContent = const expectedContent =
'Hey <a class="mention" data-mention-id="you" href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do?' 'Hey <a class="mention" data-mention-id="you" href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do?'
const expected = expect.objectContaining({ await expect(
query({
query: notificationQuery,
variables: {
read: false,
},
}),
).resolves.toMatchObject({
errors: undefined,
data: { data: {
notifications: [ notifications: [
{ {
@ -275,15 +286,22 @@ describe('notifications', () => {
], ],
}, },
}) })
})
await expect( it('publishes `NOTIFICATION_ADDED` to me', async () => {
query({ await createPostAction()
query: notificationQuery, expect(publishSpy).toHaveBeenCalledWith(
variables: { 'NOTIFICATION_ADDED',
read: false, expect.objectContaining({
}, notificationAdded: expect.objectContaining({
reason: 'mentioned_in_post',
to: expect.objectContaining({
id: 'you',
}),
}),
}), }),
).resolves.toEqual(expected) )
expect(publishSpy).toHaveBeenCalledTimes(1)
}) })
describe('updates the post and mentions me again', () => { describe('updates the post and mentions me again', () => {
@ -429,6 +447,11 @@ describe('notifications', () => {
}), }),
).resolves.toEqual(expected) ).resolves.toEqual(expected)
}) })
it('does not publish `NOTIFICATION_ADDED`', async () => {
await createPostAction()
expect(publishSpy).not.toHaveBeenCalled()
})
}) })
}) })
@ -554,10 +577,6 @@ describe('notifications', () => {
it('sends no notification', async () => { it('sends no notification', async () => {
await createCommentOnPostAction() await createCommentOnPostAction()
const expected = expect.objectContaining({
data: { notifications: [] },
})
await expect( await expect(
query({ query({
query: notificationQuery, query: notificationQuery,
@ -565,7 +584,26 @@ describe('notifications', () => {
read: false, read: false,
}, },
}), }),
).resolves.toEqual(expected) ).resolves.toMatchObject({
data: { notifications: [] },
errors: undefined,
})
})
it('does not publish `NOTIFICATION_ADDED` to authenticated user', async () => {
await createCommentOnPostAction()
expect(publishSpy).toHaveBeenCalledWith(
'NOTIFICATION_ADDED',
expect.objectContaining({
notificationAdded: expect.objectContaining({
reason: 'commented_on_post',
to: expect.objectContaining({
id: 'postAuthor', // that's expected, it's not me but the post author
}),
}),
}),
)
expect(publishSpy).toHaveBeenCalledTimes(1)
}) })
}) })
}) })