diff --git a/backend/package.json b/backend/package.json
index 1822a52ca..ba09981b0 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -70,7 +70,7 @@
"lodash": "~4.17.14",
"merge-graphql-schemas": "^1.7.6",
"metascraper": "^5.11.0",
- "metascraper-audio": "^5.10.7",
+ "metascraper-audio": "^5.11.1",
"metascraper-author": "^5.10.7",
"metascraper-clearbit-logo": "^5.3.0",
"metascraper-date": "^5.10.7",
@@ -112,7 +112,7 @@
"@babel/plugin-proposal-throw-expressions": "^7.8.3",
"@babel/preset-env": "~7.8.4",
"@babel/register": "^7.8.3",
- "apollo-server-testing": "~2.10.0",
+ "apollo-server-testing": "~2.10.1",
"babel-core": "~7.0.0-0",
"babel-eslint": "~10.0.3",
"babel-jest": "~25.1.0",
diff --git a/backend/src/middleware/notifications/notificationsMiddleware.js b/backend/src/middleware/notifications/notificationsMiddleware.js
index 4636b8e9f..64eca97c8 100644
--- a/backend/src/middleware/notifications/notificationsMiddleware.js
+++ b/backend/src/middleware/notifications/notificationsMiddleware.js
@@ -2,11 +2,21 @@ import extractMentionedUsers from './mentions/extractMentionedUsers'
import { validateNotifyUsers } from '../validation/validationMiddleware'
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 idsOfUsers = extractMentionedUsers(args.content)
const post = await resolve(root, args, context, resolveInfo)
- if (post && idsOfUsers && idsOfUsers.length)
- await notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context)
+ if (post) {
+ await publishNotifications(
+ notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context),
+ )
+ }
return post
}
@@ -16,10 +26,10 @@ const handleContentDataOfComment = async (resolve, root, args, context, resolveI
const comment = await resolve(root, args, context, resolveInfo)
const [postAuthor] = await postAuthorOfComment(comment.id, { context })
idsOfUsers = idsOfUsers.filter(id => id !== postAuthor.id)
- if (idsOfUsers && idsOfUsers.length)
- await notifyUsersOfMention('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context)
- if (context.user.id !== postAuthor.id)
- await notifyUsersOfComment('Comment', comment.id, postAuthor.id, 'commented_on_post', context)
+ await publishNotifications(
+ notifyUsersOfMention('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context),
+ notifyUsersOfComment('Comment', comment.id, postAuthor.id, 'commented_on_post', context),
+ )
return comment
}
@@ -29,7 +39,7 @@ const postAuthorOfComment = async (commentId, { context }) => {
try {
postAuthorId = await session.readTransaction(transaction => {
return transaction.run(
- `
+ `
MATCH (author:User)-[:WROTE]->(:Post)<-[:COMMENTS]-(:Comment { id: $commentId })
RETURN author { .id } as authorId
`,
@@ -43,6 +53,7 @@ const postAuthorOfComment = async (commentId, { context }) => {
}
const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
+ if (!(idsOfUsers && idsOfUsers.length)) return []
await validateNotifyUsers(label, reason)
let mentionedCypher
switch (reason) {
@@ -91,8 +102,8 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
return notificationTransactionResponse.records.map(record => record.get('notification'))
})
try {
- const [notification] = await writeTxResultPromise
- return pubsub.publish(NOTIFICATION_ADDED, { notificationAdded: notification })
+ const notifications = await writeTxResultPromise
+ return notifications
} catch (error) {
throw new Error(error)
} finally {
@@ -101,6 +112,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
}
const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, context) => {
+ if (context.user.id === postAuthorId) return []
await validateNotifyUsers(label, reason)
const session = context.driver.session()
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'))
})
try {
- const [notification] = await writeTxResultPromise
- return pubsub.publish(NOTIFICATION_ADDED, { notificationAdded: notification })
+ const notifications = await writeTxResultPromise
+ return notifications
} finally {
session.close()
}
diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.js b/backend/src/middleware/notifications/notificationsMiddleware.spec.js
index 95c0037b8..af4ed9693 100644
--- a/backend/src/middleware/notifications/notificationsMiddleware.spec.js
+++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.js
@@ -2,9 +2,10 @@ import { gql } from '../../helpers/jest'
import { cleanDatabase } from '../../db/factories'
import { createTestClient } from 'apollo-server-testing'
import { getNeode, getDriver } from '../../db/neo4j'
-import createServer from '../../server'
+import createServer, { pubsub } from '../../server'
let server, query, mutate, notifiedUser, authenticatedUser
+let publishSpy
const driver = getDriver()
const neode = getNeode()
const categoryIds = ['cat9']
@@ -36,6 +37,7 @@ const createCommentMutation = gql`
beforeAll(async () => {
await cleanDatabase()
+ publishSpy = jest.spyOn(pubsub, 'publish')
const createServerResult = createServer({
context: () => {
return {
@@ -52,6 +54,7 @@ beforeAll(async () => {
})
beforeEach(async () => {
+ publishSpy.mockClear()
notifiedUser = await neode.create(
'User',
{
@@ -259,7 +262,15 @@ describe('notifications', () => {
await createPostAction()
const expectedContent =
'Hey @al-capone how do you do?'
- const expected = expect.objectContaining({
+ await expect(
+ query({
+ query: notificationQuery,
+ variables: {
+ read: false,
+ },
+ }),
+ ).resolves.toMatchObject({
+ errors: undefined,
data: {
notifications: [
{
@@ -275,15 +286,22 @@ describe('notifications', () => {
],
},
})
+ })
- await expect(
- query({
- query: notificationQuery,
- variables: {
- read: false,
- },
+ it('publishes `NOTIFICATION_ADDED` to me', async () => {
+ await createPostAction()
+ expect(publishSpy).toHaveBeenCalledWith(
+ 'NOTIFICATION_ADDED',
+ 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', () => {
@@ -429,6 +447,11 @@ describe('notifications', () => {
}),
).resolves.toEqual(expected)
})
+
+ it('does not publish `NOTIFICATION_ADDED`', async () => {
+ await createPostAction()
+ expect(publishSpy).not.toHaveBeenCalled()
+ })
})
})
@@ -505,7 +528,7 @@ describe('notifications', () => {
})
it('sends only one notification with reason commented_on_post, no notification with reason mentioned_in_comment', async () => {
await createCommentOnPostAction()
- const expected = expect.objectContaining({
+ const expected = {
data: {
notifications: [
{
@@ -520,7 +543,7 @@ describe('notifications', () => {
},
],
},
- })
+ }
await expect(
query({
@@ -529,7 +552,7 @@ describe('notifications', () => {
read: false,
},
}),
- ).resolves.toEqual(expected)
+ ).resolves.toMatchObject(expected, { errors: undefined })
})
})
@@ -554,10 +577,6 @@ describe('notifications', () => {
it('sends no notification', async () => {
await createCommentOnPostAction()
- const expected = expect.objectContaining({
- data: { notifications: [] },
- })
-
await expect(
query({
query: notificationQuery,
@@ -565,7 +584,26 @@ describe('notifications', () => {
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)
})
})
})
diff --git a/backend/yarn.lock b/backend/yarn.lock
index 8bc2e7c00..b4d497ac5 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -1860,10 +1860,10 @@ apollo-engine-reporting-protobuf@^0.4.4:
dependencies:
"@apollo/protobufjs" "^1.0.3"
-apollo-engine-reporting@^1.5.0:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.5.0.tgz#6e3746de14fc87e14c289c0776a2d350e6f50918"
- integrity sha512-Pe2DelijZ2QHqkqv8E97iOb32l+FIMT2nxpQsuH+nWi+96cCFJJJHjm3RLAPEUuvGOgW9dFYQP3J91EyC5O0tQ==
+apollo-engine-reporting@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.6.0.tgz#a5073a2e350ea4c8ce6adb5a5b536028ed165390"
+ integrity sha512-prA17Tp/WYBJdCd4ey1CnGX8d4Xis1n9PsFmT7x8PV/oNpxG21/x3yNw5kPBZuKAoKz8yEggYtHhkYie1ZBjPQ==
dependencies:
apollo-engine-reporting-protobuf "^0.4.4"
apollo-graphql "^0.4.0"
@@ -1943,10 +1943,10 @@ apollo-server-caching@^0.5.1:
dependencies:
lru-cache "^5.0.0"
-apollo-server-core@^2.10.0:
- version "2.10.0"
- resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.10.0.tgz#b8d51bdffe6529f0e3ca670ee8f1238765cfade4"
- integrity sha512-x/UK6XvU307W8D/pzTclU04JIjRarcbg5mFPe0nVmO4OTc26uQgKi1WlZkcewXsAUnn+nDwKVn2c2G3dHEgXzQ==
+apollo-server-core@^2.10.0, apollo-server-core@^2.10.1:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.10.1.tgz#5fa4ce7992d0bf1cce616dedf1a22a41c7589c7c"
+ integrity sha512-BVITSJRMnj+CWFkjt7FMcaoqg/Ni9gfyVE9iu8bUc1IebBfFDcQj652Iolr7dTqyUziN2jbf0wfcybKYJLQHQQ==
dependencies:
"@apollographql/apollo-tools" "^0.4.3"
"@apollographql/graphql-playground-html" "1.6.24"
@@ -1954,7 +1954,7 @@ apollo-server-core@^2.10.0:
"@types/ws" "^6.0.0"
apollo-cache-control "^0.8.11"
apollo-datasource "^0.7.0"
- apollo-engine-reporting "^1.5.0"
+ apollo-engine-reporting "^1.6.0"
apollo-server-caching "^0.5.1"
apollo-server-env "^2.4.3"
apollo-server-errors "^2.3.4"
@@ -2012,12 +2012,12 @@ apollo-server-plugin-base@^0.6.10:
dependencies:
apollo-server-types "^0.2.10"
-apollo-server-testing@~2.10.0:
- version "2.10.0"
- resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.10.0.tgz#c8d7fc2d4e6eaf84232aaa7c125d9fae691fbcf4"
- integrity sha512-wBJ/CT3ZN5nmSySMqgpAFwX/I3yzsQhRGR8MCK/16MjhEZH6svNaJWzoif6gaocj0NyVBJvOIijuMTecG9+6vg==
+apollo-server-testing@~2.10.1:
+ version "2.10.1"
+ resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.10.1.tgz#c493c41f51c122b3d87c0e5ffba4f0590b924593"
+ integrity sha512-KsvLzDb/mIf5h93QUxGXymywZq8urnXUPqckBxyNaF08puAO8VO0c4EE0VvuVZnelKZvlKlU0tYQQNQsc9iHfg==
dependencies:
- apollo-server-core "^2.10.0"
+ apollo-server-core "^2.10.1"
apollo-server-types@^0.2.10:
version "0.2.10"
@@ -6197,12 +6197,12 @@ merge2@^1.3.0:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81"
integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw==
-metascraper-audio@^5.10.7:
- version "5.10.7"
- resolved "https://registry.yarnpkg.com/metascraper-audio/-/metascraper-audio-5.10.7.tgz#ba9f8333a7b71d388a0bf88dff64fc4f06595566"
- integrity sha512-VHZlT21bh/TWnHOQMGret3UcMdJOsyWvagK7MG8rLczYmrPEtvxnJjwPhyrEj1oJC+fz2P//bfQ6gyrD4HrmEQ==
+metascraper-audio@^5.11.1:
+ version "5.11.1"
+ resolved "https://registry.yarnpkg.com/metascraper-audio/-/metascraper-audio-5.11.1.tgz#46a45fc8d9c4ccc1c24340d46a8c25dc3685d7b9"
+ integrity sha512-L5eGfw5cOww4/f3ppMa/k+bix3LdICKcKJ2WVTLgz1QkKTWt5IQrgdW+kRfwUdaUTH6w0Tco+nOO7yUCaWytAQ==
dependencies:
- "@metascraper/helpers" "^5.10.7"
+ "@metascraper/helpers" "^5.11.1"
metascraper-author@^5.10.7:
version "5.10.7"
diff --git a/cypress/integration/common/steps.js b/cypress/integration/common/steps.js
index 3f8724bfb..2a87e3d83 100644
--- a/cypress/integration/common/steps.js
+++ b/cypress/integration/common/steps.js
@@ -342,7 +342,6 @@ Then(
Given("I am logged in with these credentials:", table => {
loginCredentials = table.hashes()[0];
- cy.debug();
cy.factory().build("user", {
...termsAndConditionsAgreedVersion,
name: loginCredentials.email,
@@ -420,7 +419,6 @@ When("mention {string} in the text", mention => {
cy.get(".suggestion-list__item")
.contains(mention)
.click();
- cy.debug();
});
Then("the notification gets marked as read", () => {
diff --git a/webapp/components/CommentList/CommentList.vue b/webapp/components/CommentList/CommentList.vue
index 2e6647c2d..1cff75138 100644
--- a/webapp/components/CommentList/CommentList.vue
+++ b/webapp/components/CommentList/CommentList.vue
@@ -1,12 +1,12 @@
-
+
{{ $t('common.comment', null, 0) }}
-