Fix backend tests

This commit is contained in:
Wolfgang Huß 2020-03-13 11:58:51 +01:00
parent ee60bf7e15
commit 32d5482597
5 changed files with 279 additions and 232 deletions

View File

@ -1,3 +1,4 @@
import log from '../../schema/resolvers/helpers/databaseLogger'
import extractMentionedUsers from './mentions/extractMentionedUsers'
import { validateNotifyUsers } from '../validation/validationMiddleware'
import { pubsub, NOTIFICATION_ADDED } from '../../server'
@ -80,6 +81,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
AND NOT (user)-[:BLOCKED]-(author)
AND NOT (user)-[:BLOCKED]-(postAuthor)
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
WITH comment AS resource, notification, user
`
break
}
@ -91,19 +93,19 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
WITH resource, user, notification, authors, posts,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
SET notification.read = FALSE
// Wolle SET ( CASE WHEN notification.createdAt IS NULL THEN notification END ).createdAt = toString(datetime())
SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime()))
SET notification.updatedAt = toString(datetime())
RETURN notification {.*, from: finalResource, to: properties(user)}
`
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async transaction => {
const notificationTransactionResponse = await transaction.run(mentionedCypher, {
const notificationsTransactionResponse = await transaction.run(mentionedCypher, {
id,
idsOfUsers,
reason,
})
return notificationTransactionResponse.records.map(record => record.get('notification'))
log(notificationsTransactionResponse)
return notificationsTransactionResponse.records.map(record => record.get('notification'))
})
try {
const notifications = await writeTxResultPromise
@ -120,7 +122,7 @@ const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, cont
await validateNotifyUsers(label, reason)
const session = context.driver.session()
const writeTxResultPromise = await session.writeTransaction(async transaction => {
const notificationTransactionResponse = await transaction.run(
const notificationsTransactionResponse = await transaction.run(
`
MATCH (postAuthor:User {id: $postAuthorId})-[:WROTE]->(post:Post)<-[:COMMENTS]-(comment:Comment { id: $commentId })<-[:WROTE]-(commenter:User)
WHERE NOT (postAuthor)-[:BLOCKED]-(commenter)
@ -134,7 +136,8 @@ const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, cont
`,
{ commentId, postAuthorId, reason },
)
return notificationTransactionResponse.records.map(record => record.get('notification'))
log(notificationsTransactionResponse)
return notificationsTransactionResponse.records.map(record => record.get('notification'))
})
try {
const notifications = await writeTxResultPromise
@ -146,41 +149,53 @@ const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, cont
}
}
const notifyReportFiler = async (resolve, root, args, context, resolveInfo) => {
const report = await resolve(root, args, context, resolveInfo)
const handleFileReport = async (resolve, root, args, context, resolveInfo) => {
const filedReport = await resolve(root, args, context, resolveInfo)
if (report) {
if (filedReport) {
const { reportId } = filedReport
const { resourceId } = args
const { driver, user } = context
const { id: reportId } = report
const session = driver.session()
try {
await session.writeTransaction(async transaction => {
await transaction.run(
`
MATCH (resource {id: $resourceId})<-[:BELONGS_TO]-(report:Report {id: $reportId})<-[:FILED]-(submitter:User {id: $submitterId})
WHERE resource: User OR resource: Post OR resource: Comment
MERGE (report)-[notification:NOTIFIED {reason: $reason}]->(submitter)
ON CREATE SET notification.createdAt = toString(datetime()), notification.updatedAt = notification.createdAt
ON MATCH SET notification.updatedAt = toString(datetime())
SET notification.read = FALSE
`,
{
reportId,
resourceId,
submitterId: user.id,
reason: 'filed_report_on_resource',
},
)
})
} catch (error) {
debug(error)
} finally {
session.close()
}
await publishNotifications(notifyReportFiler(reportId, resourceId, context))
}
return report
return filedReport
}
const notifyReportFiler = async (reportId, resourceId, context) => {
const { driver, user } = context
const session = driver.session()
const writeTxResultPromise = await session.writeTransaction(async transaction => {
const notificationsTransactionResponse = await transaction.run(
`
MATCH (resource {id: $resourceId})<-[:BELONGS_TO]-(report:Report {id: $reportId})<-[filed:FILED]-(submitter:User {id: $submitterId})
WHERE resource: User OR resource: Post OR resource: Comment
MERGE (report)-[notification:NOTIFIED {reason: $reason}]->(submitter)
ON CREATE SET notification.createdAt = toString(datetime()), notification.updatedAt = notification.createdAt
ON MATCH SET notification.updatedAt = toString(datetime())
SET notification.read = FALSE
WITH notification, submitter,
{__typename: "FiledReport", reportId: report.id, createdAt: filed.createdAt, reasonCategory: filed.reasonCategory, reasonDescription: filed.reasonDescription, resource: apoc.map.merge(properties(resource), {__typename: labels(resource)[0]})} AS finalResource
RETURN notification {.*, from: finalResource, to: properties(submitter)}
`,
{
reportId,
resourceId,
submitterId: user.id,
reason: 'filed_report_on_resource',
},
)
log(notificationsTransactionResponse)
return notificationsTransactionResponse.records.map(record => record.get('notification'))
})
try {
const [notification] = await writeTxResultPromise
if (notification) return [notification]
else throw new Error(`Notification for filing a report could not be send!`)
} catch (error) {
debug(error)
} finally {
session.close()
}
}
export default {
@ -189,6 +204,6 @@ export default {
UpdatePost: handleContentDataOfPost,
CreateComment: handleContentDataOfComment,
UpdateComment: handleContentDataOfComment,
fileReport: notifyReportFiler,
fileReport: handleFileReport,
},
}

View File

@ -1,5 +1,5 @@
import { gql } from '../../helpers/jest'
import Factory, { cleanDatabase } from '../../factories'
import Factory, { cleanDatabase } from '../../db/factories'
import { createTestClient } from 'apollo-server-testing'
import { getDriver } from '../../db/neo4j'
import createServer, { pubsub } from '../../server'
@ -40,7 +40,7 @@ const fileReportMutation = gql`
reasonCategory: $reasonCategory
reasonDescription: $reasonDescription
) {
id
reportId
}
}
`
@ -64,6 +64,12 @@ beforeAll(async () => {
beforeEach(async () => {
publishSpy.mockClear()
await Factory.build('category', {
id: 'cat9',
name: 'Democracy & Politics',
slug: 'democracy-politics',
icon: 'university',
})
notifiedUser = await Factory.build(
'user',
{
@ -76,12 +82,7 @@ beforeEach(async () => {
password: '1234',
},
)
await Factory.build('category', {
id: 'cat9',
name: 'Democracy & Politics',
slug: 'democracy-politics',
icon: 'university',
})
authenticatedUser = await notifiedUser.toJson()
})
afterEach(async () => {
@ -89,7 +90,7 @@ afterEach(async () => {
})
describe('notifications', () => {
const notificationQuery = gql`
const notificationsQuery = gql`
query($read: Boolean) {
notifications(read: $read, orderBy: updatedAt_desc) {
createdAt
@ -105,25 +106,23 @@ describe('notifications', () => {
id
content
}
... on Report {
id
filed {
reasonCategory
reasonDescription
reportedResource {
__typename
... on User {
id
name
}
... on Post {
id
content
}
... on Comment {
id
content
}
... on FiledReport {
reportId
reasonCategory
reasonDescription
resource {
__typename
... on User {
id
name
}
... on Post {
id
content
}
... on Comment {
id
content
}
}
}
@ -215,7 +214,7 @@ describe('notifications', () => {
})
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -232,7 +231,7 @@ describe('notifications', () => {
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -256,7 +255,7 @@ describe('notifications', () => {
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -275,7 +274,7 @@ describe('notifications', () => {
slug: 'mrs-post',
},
{
email: 'post-author@example.org',
email: 'mrs-post-author@example.org',
password: '1234',
},
)
@ -295,7 +294,7 @@ describe('notifications', () => {
'Hey <a class="mention" data-mention-id="you" href="/profile/you/al-capone" target="_blank">@al-capone</a> how do you do?'
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -387,7 +386,7 @@ describe('notifications', () => {
})
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -416,7 +415,7 @@ describe('notifications', () => {
notifications: [{ read: readBefore }],
},
} = await query({
query: notificationQuery,
query: notificationsQuery,
})
await updatePostAction()
const {
@ -424,7 +423,7 @@ describe('notifications', () => {
notifications: [{ read: readAfter }],
},
} = await query({
query: notificationQuery,
query: notificationsQuery,
})
expect(readBefore).toEqual(true)
expect(readAfter).toEqual(false)
@ -438,7 +437,7 @@ describe('notifications', () => {
notifications: [{ createdAt: createdAtBefore }],
},
} = await query({
query: notificationQuery,
query: notificationsQuery,
})
await updatePostAction()
const {
@ -446,7 +445,7 @@ describe('notifications', () => {
notifications: [{ createdAt: createdAtAfter }],
},
} = await query({
query: notificationQuery,
query: notificationsQuery,
})
expect(createdAtBefore).toBeTruthy()
expect(Date.parse(createdAtBefore)).toEqual(expect.any(Number))
@ -471,7 +470,7 @@ describe('notifications', () => {
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -519,7 +518,7 @@ describe('notifications', () => {
slug: 'mr-author',
},
{
email: 'post-author@example.org',
email: 'mr-post-author@example.org',
password: '1234',
},
)
@ -544,7 +543,7 @@ describe('notifications', () => {
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -557,6 +556,7 @@ describe('notifications', () => {
postContent = 'Content of post where I get mentioned in a comment.'
postAuthor = notifiedUser
})
it('sends only one notification with reason commented_on_post, no notification with reason mentioned_in_comment', async () => {
await createCommentOnPostAction()
const expected = {
@ -578,7 +578,7 @@ describe('notifications', () => {
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -610,7 +610,7 @@ describe('notifications', () => {
await createCommentOnPostAction()
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -641,11 +641,9 @@ describe('notifications', () => {
})
describe('given I file a report on a', () => {
let resourceId
let resourceId, reportFiler, reportedUserOrAuthorData, expectedMeAsNotifiedForFilingReport
const reasonCategory = 'discrimination_etc'
const reasonDescription = 'I am free to be gay !!!'
let reportFiler
let reportedUserOrAuthorData
const fileReportAction = async () => {
authenticatedUser = await reportFiler.toJson()
await mutate({
@ -658,7 +656,7 @@ describe('notifications', () => {
})
authenticatedUser = await notifiedUser.toJson()
}
const setExpectedNotificationOfReportedResource = reportedResource => {
const setExpectedNotificationOfReportedResource = resource => {
return expect.objectContaining({
data: {
notifications: [
@ -667,15 +665,11 @@ describe('notifications', () => {
read: false,
reason: 'filed_report_on_resource',
from: {
__typename: 'Report',
id: expect.any(String),
filed: [
{
reasonCategory: 'discrimination_etc',
reasonDescription: 'I am free to be gay !!!',
reportedResource,
},
],
__typename: 'FiledReport',
reportId: expect.any(String),
reasonCategory: 'discrimination_etc',
reasonDescription: 'I am free to be gay !!!',
resource,
},
},
],
@ -686,20 +680,38 @@ describe('notifications', () => {
beforeEach(async () => {
reportFiler = notifiedUser
reportedUserOrAuthorData = {
id: 'reportedUser',
name: 'Mrs Badman',
slug: 'mrs-badman',
email: 'reported-user@example.org',
password: '1234',
userProperties: {
id: 'reportedUser',
name: 'Mrs Badman',
slug: 'mrs-badman',
},
emailProperties: {
email: 'reported-user@example.org',
password: '1234',
},
}
expectedMeAsNotifiedForFilingReport = expect.objectContaining({
notificationAdded: expect.objectContaining({
reason: 'filed_report_on_resource',
to: expect.objectContaining({
id: 'you',
}),
}),
})
})
describe('user', () => {
it('sends me a notification for filing a report on a user', async () => {
await Factory.create('User', reportedUserOrAuthorData)
resourceId = 'reportedUser'
beforeEach(async () => {
await Factory.build(
'user',
reportedUserOrAuthorData.userProperties,
reportedUserOrAuthorData.emailProperties,
)
resourceId = reportedUserOrAuthorData.userProperties.id
await fileReportAction()
})
it('sends me a notification for filing a report on a user', async () => {
const expected = setExpectedNotificationOfReportedResource({
__typename: 'User',
id: 'reportedUser',
@ -708,28 +720,41 @@ describe('notifications', () => {
const { query } = createTestClient(server)
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
}),
).resolves.toEqual(expected)
})
it('does publish `NOTIFICATION_ADDED` to authenticated user', async () => {
expect(publishSpy).toHaveBeenCalledWith(
'NOTIFICATION_ADDED',
expectedMeAsNotifiedForFilingReport,
)
})
})
describe('post or comment', () => {
beforeEach(async () => {
title = 'My post'
postContent = 'My post content.'
postAuthor = await Factory.create('User', reportedUserOrAuthorData)
postAuthor = await Factory.build(
'user',
reportedUserOrAuthorData.userProperties,
reportedUserOrAuthorData.emailProperties,
)
})
describe('post', () => {
it('sends me a notification for filing a report on a post', async () => {
beforeEach(async () => {
await createPostAction()
resourceId = 'p47'
await fileReportAction()
})
it('sends me a notification for filing a report on a post', async () => {
const expected = setExpectedNotificationOfReportedResource({
__typename: 'Post',
id: 'p47',
@ -738,32 +763,43 @@ describe('notifications', () => {
const { query } = createTestClient(server)
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
}),
).resolves.toEqual(expected)
})
it('does publish `NOTIFICATION_ADDED` to authenticated user', async () => {
expect(publishSpy).toHaveBeenCalledWith(
'NOTIFICATION_ADDED',
expectedMeAsNotifiedForFilingReport,
)
})
})
describe('comment', () => {
beforeEach(async () => {
commentContent = "Commenter's comment."
commentAuthor = await Factory.create('User', {
id: 'commentAuthor',
name: 'Mrs Comment',
slug: 'mrs-comment',
email: 'commentauthor@example.org',
password: '1234',
})
})
it('sends me a notification for filing a report on a comment', async () => {
commentAuthor = await Factory.build(
'user',
{
id: 'commentAuthor',
name: 'Mrs Comment',
slug: 'mrs-comment',
},
{
email: 'commentauthor@example.org',
password: '1234',
},
)
await createCommentOnPostAction()
resourceId = 'c47'
await fileReportAction()
})
it('sends me a notification for filing a report on a comment', async () => {
const expected = setExpectedNotificationOfReportedResource({
__typename: 'Comment',
id: 'c47',
@ -772,7 +808,7 @@ describe('notifications', () => {
const { query } = createTestClient(server)
await expect(
query({
query: notificationQuery,
query: notificationsQuery,
variables: {
read: false,
},
@ -780,20 +816,11 @@ describe('notifications', () => {
).resolves.toEqual(expected)
})
it('does not publish `NOTIFICATION_ADDED` to authenticated user', async () => {
await createCommentOnPostAction()
it('does publish `NOTIFICATION_ADDED` to authenticated user', async () => {
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
}),
}),
}),
expectedMeAsNotifiedForFilingReport,
)
expect(publishSpy).toHaveBeenCalledTimes(1)
})
})
})

View File

@ -2,6 +2,27 @@ import log from './helpers/databaseLogger'
import { withFilter } from 'graphql-subscriptions'
import { pubsub, NOTIFICATION_ADDED } from '../../server'
const cypherReturnNotificationsWithCollectedResourceData = `
CALL apoc.case(
[
labels(resource)[0] = "Post", '
MATCH (resource)<-[:WROTE]-(author:User)
RETURN resource {.*, __typename: labels(resource)[0], author: author} AS finalResource',
labels(resource)[0] = "Comment", '
MATCH (author:User)-[:WROTE]->(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(postAuthor:User)
RETURN resource {.*, __typename: labels(resource)[0], author: author, post: apoc.map.merge(properties(post), {__typename: labels(post)[0], author: properties(postAuthor)})} AS finalResource',
labels(resource)[0] = "Report", '
MATCH (reportedResource)<-[:BELONGS_TO]-(resource)<-[filed:FILED]-(user)
RETURN {__typename: "FiledReport", reportId: resource.id, createdAt: filed.createdAt, reasonCategory: filed.reasonCategory, reasonDescription: filed.reasonDescription, resource: apoc.map.merge(properties(reportedResource), {__typename: labels(reportedResource)[0]})} AS finalResource'
],
'',
{
resource: resource,
user: user
}) YIELD value
RETURN notification {.*, from: value.finalResource, to: properties(user)}
`
export default {
Subscription: {
notificationAdded: {
@ -42,97 +63,87 @@ export default {
const offset = args.offset && typeof args.offset === 'number' ? `SKIP ${args.offset}` : ''
const limit = args.first && typeof args.first === 'number' ? `LIMIT ${args.first}` : ''
const cypher = `
// Wolle MATCH (resource)-[notification:NOTIFIED]->(user:User {id:$id})
// WHERE
// ((labels(resource)[0] in ["Post", "Comment"] AND NOT resource.deleted AND NOT resource.disabled)
// OR labels(resource)[0] in ["Report"])
// $ {whereClause}
// WITH user, notification, resource,
// [(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
// [(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post {.*, author: properties(author)} ] AS posts,
// [(reportedResource)<-[:BELONGS_TO]-(resource)<-[file:FILED]-(user) | file {.*, reportedResource: apoc.map.merge(properties(reportedResource), {__typename: labels(reportedResource)[0]})} ] AS files
// WITH resource, user, notification, authors, posts, files,
// resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], filed: files, resource: files[0].reportedResource} AS finalResource
// RETURN notification {.*, from: finalResource, to: properties(user)}
// $ {orderByClause}
// $ {offset} $ {limit}
MATCH (resource)-[notification:NOTIFIED]->(user:User {id:$id})
WHERE
((labels(resource)[0] in ["Post", "Comment"] AND NOT resource.deleted AND NOT resource.disabled)
OR labels(resource)[0] in ["Report"])
${whereClause}
${cypherReturnNotificationsWithCollectedResourceData}
// Wolle WITH resource, notification, user
// MATCH (author:User)-[:WROTE]->(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(postAuthor:User)
// WITH resource, notification, user, author, post, postAuthor
// resource {.*, __typename: labels(resource)[0], author: author, post: apoc.map.merge(properties(post), {__typename: labels(post)[0], author: properties(postAuthor)})} AS finalResource',
// RETURN notification {.*, from: value.finalResource, to: properties(user)}
${orderByClause}
${offset} ${limit}
// Wolle
// the UNION ALL with ORDER BY and SKIP, LIMIT is possible since Neo4j 4.0. See https://neo4j.com/docs/cypher-manual/4.0/clauses/call-subquery/#subquery-post-union
// refactor the following to the new CALL {} subquery
// MATCH (author:User)-[:WROTE]->(post:Post)-[notification:NOTIFIED]->(user:User {id: $id})
// WHERE NOT post.deleted AND NOT post.disabled
// $ {whereClause}
// WITH user, notification, post {.*, __typename: labels(post)[0], author: properties(author)}
// RETURN notification {.*, from: post, to: properties(user)}
// UNION ALL
// MATCH (author:User)-[:WROTE]->(comment:Comment)-[:COMMENTS]->(post:Post)<-[:WROTE]-(postAuthor:User),
// (comment)-[notification:NOTIFIED]->(user:User {id: $id})
// WHERE NOT comment.deleted AND NOT comment.disabled
// $ {whereClause}
// WITH user, notification, comment {.*, __typename: labels(comment)[0], author: properties(author), post: apoc.map.merge(properties(post), {__typename: labels(post)[0], author: properties(postAuthor)})}
// RETURN notification {.*, from: comment, to: properties(user)}
// UNION ALL
// MATCH (reportedResource)<-[:BELONGS_TO]-(report)<-[file:FILED]-(user:User {id:$id}),
// (report:Report)-[notification:NOTIFIED]->(user)
// WHERE (reportedResource:User) OR (reportedResource:Post) OR (reportedResource:Comment)
// $ {whereClause}
// // Wolle - Here the three different case are not distinguished and therefore Post is not added to Comment and the authors are not added etc.
// WITH
// user,
// notification,
// {
// __typename: "FiledReport",
// createdAt: file.createdAt,
// reasonCategory: file.reasonCategory,
// reasonDescription: file.reasonDescription,
// reportId: report.id,
// resource: apoc.map.merge(properties(reportedResource), {
// __typename: labels(reportedResource)[0]
// })
// } AS filedReport
// RETURN notification {.*, from: filedReport, to: properties(user)}
// $ {orderByClause}
// $ {offset} $ {limit}
`
const readTxResultPromise = session.readTransaction(async transaction => {
const notificationsTransactionResponse = await transaction.run(
`
// Wolle MATCH (resource)-[notification:NOTIFIED]->(user:User {id:$id})
// WHERE
// ((labels(resource)[0] in ["Post", "Comment"] AND NOT resource.deleted AND NOT resource.disabled)
// OR labels(resource)[0] in ["Report"])
// ${whereClause}
// WITH user, notification, resource,
// [(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
// [(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post {.*, author: properties(author)} ] AS posts,
// [(reportedResource)<-[:BELONGS_TO]-(resource)<-[file:FILED]-(user) | file {.*, reportedResource: apoc.map.merge(properties(reportedResource), {__typename: labels(reportedResource)[0]})} ] AS files
// WITH resource, user, notification, authors, posts, files,
// resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0], filed: files, resource: files[0].reportedResource} AS finalResource
// RETURN notification {.*, from: finalResource, to: properties(user)}
// ${orderByClause}
// ${offset} ${limit}
MATCH (resource)-[notification:NOTIFIED]->(user:User {id:$id})
WHERE
((labels(resource)[0] in ["Post", "Comment"] AND NOT resource.deleted AND NOT resource.disabled)
OR labels(resource)[0] in ["Report"])
${whereClause}
CALL apoc.case(
[
labels(resource)[0] = "Post", '
MATCH (resource)<-[:WROTE]-(author:User)
RETURN resource {.*, __typename: labels(resource)[0], author: author} AS finalResource',
labels(resource)[0] = "Comment", '
MATCH (author:User)-[:WROTE]->(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(postAuthor:User)
RETURN resource {.*, __typename: labels(resource)[0], author: author, post: apoc.map.merge(properties(post), {__typename: labels(post)[0], author: properties(postAuthor)})} AS finalResource',
labels(resource)[0] = "Report", '
MATCH (reportedResource)<-[:BELONGS_TO]-(resource)<-[filed:FILED]-(user)
RETURN {__typename: "FiledReport", reportId: resource.id, createdAt: filed.createdAt, reasonCategory: filed.reasonCategory, reasonDescription: filed.reasonDescription, resource: apoc.map.merge(properties(reportedResource), {__typename: labels(reportedResource)[0]})} AS finalResource'
],
'',
{
resource: resource,
user: user
}) YIELD value
RETURN notification {.*, from: value.finalResource, to: properties(user)}
${orderByClause}
${offset} ${limit}
// Wolle
// the UNION ALL with ORDER BY and SKIP, LIMIT is possible since Neo4j 4.0. See https://neo4j.com/docs/cypher-manual/4.0/clauses/call-subquery/#subquery-post-union
// refactor the following to the new CALL {} subquery
// MATCH (author:User)-[:WROTE]->(post:Post)-[notification:NOTIFIED]->(user:User {id: $id})
// WHERE NOT post.deleted AND NOT post.disabled
// ${whereClause}
// WITH user, notification, post {.*, __typename: labels(post)[0], author: properties(author)}
// RETURN notification {.*, from: post, to: properties(user)}
// UNION ALL
// MATCH (author:User)-[:WROTE]->(comment:Comment)-[:COMMENTS]->(post:Post)<-[:WROTE]-(postAuthor:User),
// (comment)-[notification:NOTIFIED]->(user:User {id: $id})
// WHERE NOT comment.deleted AND NOT comment.disabled
// ${whereClause}
// WITH user, notification, comment {.*, __typename: labels(comment)[0], author: properties(author), post: apoc.map.merge(properties(post), {__typename: labels(post)[0], author: properties(postAuthor)})}
// RETURN notification {.*, from: comment, to: properties(user)}
// UNION ALL
// MATCH (reportedResource)<-[:BELONGS_TO]-(report)<-[file:FILED]-(user:User {id:$id}),
// (report:Report)-[notification:NOTIFIED]->(user)
// WHERE (reportedResource:User) OR (reportedResource:Post) OR (reportedResource:Comment)
// ${whereClause}
// // Wolle - Here the three different case are not distinguished and therefore Post is not added to Comment and the authors are not added etc.
// WITH
// user,
// notification,
// {
// __typename: "FiledReport",
// createdAt: file.createdAt,
// reasonCategory: file.reasonCategory,
// reasonDescription: file.reasonDescription,
// reportId: report.id,
// resource: apoc.map.merge(properties(reportedResource), {
// __typename: labels(reportedResource)[0]
// })
// } AS filedReport
// RETURN notification {.*, from: filedReport, to: properties(user)}
// ${orderByClause}
// ${offset} ${limit}
`,
{ id: currentUser.id },
)
const notificationsTransactionResponse = await transaction.run(cypher, {
id: currentUser.id,
})
log(notificationsTransactionResponse)
// Wolle return notificationsTransactionResponse.records.map(record => record.get('notification'))
const notification = notificationsTransactionResponse.records.map(record => record.get('notification'))
// Wolle console.log('notification: ', notification)
return notification
const notifications = notificationsTransactionResponse.records.map(record =>
record.get('notification'),
)
return notifications
})
try {
const notifications = await readTxResultPromise
@ -144,7 +155,7 @@ export default {
},
Mutation: {
markAsRead: async (parent, args, context, resolveInfo) => {
markAsRead: async (_parent, args, context, _resolveInfo) => {
const { user: currentUser } = context
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async transaction => {
@ -152,12 +163,8 @@ export default {
`
MATCH (resource {id: $resourceId})-[notification:NOTIFIED {read: FALSE}]->(user:User {id: $id})
SET notification.read = TRUE
WITH user, notification, resource,
[(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors,
[(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts
WITH resource, user, notification, authors, posts,
resource {.*, __typename: labels(resource)[0], author: authors[0], post: posts[0]} AS finalResource
RETURN notification {.*, from: finalResource, to: properties(user)}
WITH resource, notification, user
${cypherReturnNotificationsWithCollectedResourceData}
`,
{
resourceId: args.id,
@ -170,8 +177,8 @@ export default {
)
})
try {
const [notifications] = await writeTxResultPromise
return notifications
const [notification] = await writeTxResultPromise
return notification
} finally {
session.close()
}

View File

@ -344,8 +344,7 @@ describe('given some notifications', () => {
resource: {
__typename: 'Comment',
id: 'c4',
content:
'I am harassing content in a harassing comment to a bad post !!!',
content: 'I am harassing content in a harassing comment to a bad post !!!',
},
},
},
@ -431,8 +430,7 @@ describe('given some notifications', () => {
resource: {
__typename: 'Comment',
id: 'c4',
content:
'I am harassing content in a harassing comment to a bad post !!!',
content: 'I am harassing content in a harassing comment to a bad post !!!',
},
},
},
@ -533,8 +531,8 @@ describe('given some notifications', () => {
... on Comment {
content
}
... on Report {
id
... on FiledReport {
reportId
}
}
}

View File

@ -161,7 +161,7 @@ export const markAsReadMutation = i18n => {
}
}
}
... on Report {
... on FiledReport {
id
}
}