Split handleNotificationsMiddleware in notificationsMiddleware and hashtagsMiddleware

This commit is contained in:
Wolfgang Huß 2019-08-26 16:24:43 +02:00
parent 7498d88a44
commit af968461b6
9 changed files with 235 additions and 147 deletions

View File

@ -0,0 +1,49 @@
import extractHashtags from '../hashtags/extractHashtags'
const updateHashtagsOfPost = async (postId, hashtags, context) => {
if (!hashtags.length) return
const session = context.driver.session()
// We need two Cypher statements, because the 'MATCH' in the 'cypherDeletePreviousRelations' statement
// functions as an 'if'. In case there is no previous relation, the rest of the commands are omitted
// and no new Hashtags and relations will be created.
const cypherDeletePreviousRelations = `
MATCH (p: Post { id: $postId })-[previousRelations: TAGGED]->(t: Tag)
DELETE previousRelations
RETURN p, t
`
const cypherCreateNewTagsAndRelations = `
MATCH (p: Post { id: $postId})
UNWIND $hashtags AS tagName
MERGE (t: Tag { id: tagName, disabled: false, deleted: false })
MERGE (p)-[:TAGGED]->(t)
RETURN p, t
`
await session.run(cypherDeletePreviousRelations, {
postId,
})
await session.run(cypherCreateNewTagsAndRelations, {
postId,
hashtags,
})
session.close()
}
const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => {
const hashtags = extractHashtags(args.content)
const post = await resolve(root, args, context, resolveInfo)
if (post) {
await updateHashtagsOfPost(post.id, hashtags, context)
}
return post
}
export default {
Mutation: {
CreatePost: handleContentDataOfPost,
UpdatePost: handleContentDataOfPost,
},
}

View File

@ -0,0 +1,176 @@
import { gql } from '../../jest/helpers'
import Factory from '../../seed/factories'
import { createTestClient } from 'apollo-server-testing'
import { neode, getDriver } from '../../bootstrap/neo4j'
import createServer from '../../server'
let server
let query
let mutate
let hashtagingUser
let authenticatedUser
const factory = Factory()
const driver = getDriver()
const instance = neode()
const categoryIds = ['cat9']
const createPostMutation = gql`
mutation($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
CreatePost(id: $id, title: $title, content: $postContent, categoryIds: $categoryIds) {
id
title
content
}
}
`
const updatePostMutation = gql`
mutation($id: ID!, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
UpdatePost(id: $id, content: $postContent, title: $title, categoryIds: $categoryIds) {
title
content
}
}
`
beforeAll(() => {
const createServerResult = createServer({
context: () => {
return {
user: authenticatedUser,
neode: instance,
driver,
}
},
})
server = createServerResult.server
const createTestClientResult = createTestClient(server)
query = createTestClientResult.query
mutate = createTestClientResult.mutate
})
beforeEach(async () => {
hashtagingUser = await instance.create('User', {
id: 'you',
name: 'Al Capone',
slug: 'al-capone',
email: 'test@example.org',
password: '1234',
})
await instance.create('Category', {
id: 'cat9',
name: 'Democracy & Politics',
icon: 'university',
})
})
afterEach(async () => {
await factory.cleanDatabase()
})
describe('hashtags', () => {
const id = 'p135'
const title = 'Two Hashtags'
const postContent =
'<p>Hey Dude, <a class="hashtag" href="/search/hashtag/Democracy">#Democracy</a> should work equal for everybody!? That seems to be the only way to have equal <a class="hashtag" href="/search/hashtag/Liberty">#Liberty</a> for everyone.</p>'
const postWithHastagsQuery = gql`
query($id: ID) {
Post(id: $id) {
tags {
id
}
}
}
`
const postWithHastagsVariables = {
id,
}
describe('authenticated', () => {
beforeEach(async () => {
authenticatedUser = await hashtagingUser.toJson()
})
describe('create a Post with Hashtags', () => {
beforeEach(async () => {
await mutate({
mutation: createPostMutation,
variables: {
id,
title,
postContent,
categoryIds,
},
})
})
it('both hashtags are created with the "id" set to their "name"', async () => {
const expected = [
{
id: 'Democracy',
},
{
id: 'Liberty',
},
]
await expect(
query({
query: postWithHastagsQuery,
variables: postWithHastagsVariables,
}),
).resolves.toEqual(
expect.objectContaining({
data: {
Post: [
{
tags: expect.arrayContaining(expected),
},
],
},
}),
)
})
describe('afterwards update the Post by removing a Hashtag, leaving a Hashtag and add a Hashtag', () => {
// The already existing Hashtag has no class at this point.
const postContent =
'<p>Hey Dude, <a class="hashtag" href="/search/hashtag/Elections">#Elections</a> should work equal for everybody!? That seems to be the only way to have equal <a href="/search/hashtag/Liberty">#Liberty</a> for everyone.</p>'
it('only one previous Hashtag and the new Hashtag exists', async () => {
await mutate({
mutation: updatePostMutation,
variables: {
id,
title,
postContent,
categoryIds,
},
})
const expected = [
{
id: 'Elections',
},
{
id: 'Liberty',
},
]
await expect(
query({
query: postWithHastagsQuery,
variables: postWithHastagsVariables,
}),
).resolves.toEqual(
expect.objectContaining({
data: {
Post: [
{
tags: expect.arrayContaining(expected),
},
],
},
}),
)
})
})
})
})
})

View File

@ -12,7 +12,8 @@ import user from './userMiddleware'
import includedFields from './includedFieldsMiddleware'
import orderBy from './orderByMiddleware'
import validation from './validation/validationMiddleware'
import handleNotifications from './handleNotifications/handleNotificationsMiddleware'
import notifications from './notifications/notificationsMiddleware'
import hashtags from './hashtags/hashtagsMiddleware'
import email from './email/emailMiddleware'
import sentry from './sentryMiddleware'
@ -25,13 +26,16 @@ export default schema => {
validation,
sluggify,
excerpt,
handleNotifications,
notifications,
hashtags,
xss,
softDelete,
user,
includedFields,
orderBy,
email: email({ isEnabled: CONFIG.SMTP_HOST && CONFIG.SMTP_PORT }),
email: email({
isEnabled: CONFIG.SMTP_HOST && CONFIG.SMTP_PORT,
}),
}
let order = [
@ -43,7 +47,8 @@ export default schema => {
'sluggify',
'excerpt',
'email',
'handleNotifications',
'notifications',
'hashtags',
'xss',
'softDelete',
'user',

View File

@ -1,5 +1,4 @@
import extractMentionedUsers from './notifications/extractMentionedUsers'
import extractHashtags from './hashtags/extractHashtags'
import extractMentionedUsers from './mentions/extractMentionedUsers'
const notifyUsers = async (label, id, idsOfUsers, reason, context) => {
if (!idsOfUsers.length) return
@ -66,44 +65,13 @@ const notifyUsers = async (label, id, idsOfUsers, reason, context) => {
session.close()
}
const updateHashtagsOfPost = async (postId, hashtags, context) => {
if (!hashtags.length) return
const session = context.driver.session()
// We need two Cypher statements, because the 'MATCH' in the 'cypherDeletePreviousRelations' statement
// functions as an 'if'. In case there is no previous relation, the rest of the commands are omitted
// and no new Hashtags and relations will be created.
const cypherDeletePreviousRelations = `
MATCH (p: Post { id: $postId })-[previousRelations: TAGGED]->(t: Tag)
DELETE previousRelations
RETURN p, t
`
const cypherCreateNewTagsAndRelations = `
MATCH (p: Post { id: $postId})
UNWIND $hashtags AS tagName
MERGE (t: Tag { id: tagName, disabled: false, deleted: false })
MERGE (p)-[:TAGGED]->(t)
RETURN p, t
`
await session.run(cypherDeletePreviousRelations, {
postId,
})
await session.run(cypherCreateNewTagsAndRelations, {
postId,
hashtags,
})
session.close()
}
const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo) => {
const idsOfUsers = extractMentionedUsers(args.content)
const hashtags = extractHashtags(args.content)
const post = await resolve(root, args, context, resolveInfo)
if (post) {
await notifyUsers('Post', post.id, idsOfUsers, 'mentioned_in_post', context)
await updateHashtagsOfPost(post.id, hashtags, context)
}
return post
@ -121,7 +89,6 @@ const handleContentDataOfComment = async (resolve, root, args, context, resolveI
}
const handleCreateComment = async (resolve, root, args, context, resolveInfo) => {
// removes classes from the content
const comment = await handleContentDataOfComment(resolve, root, args, context, resolveInfo)
if (comment) {

View File

@ -461,112 +461,3 @@ describe('notifications', () => {
})
})
})
describe('Hashtags', () => {
const id = 'p135'
const title = 'Two Hashtags'
const postContent =
'<p>Hey Dude, <a class="hashtag" href="/search/hashtag/Democracy">#Democracy</a> should work equal for everybody!? That seems to be the only way to have equal <a class="hashtag" href="/search/hashtag/Liberty">#Liberty</a> for everyone.</p>'
const postWithHastagsQuery = gql`
query($id: ID) {
Post(id: $id) {
tags {
id
}
}
}
`
const postWithHastagsVariables = {
id,
}
describe('authenticated', () => {
beforeEach(async () => {
authenticatedUser = await notifiedUser.toJson()
})
describe('create a Post with Hashtags', () => {
beforeEach(async () => {
await mutate({
mutation: createPostMutation,
variables: {
id,
title,
postContent,
categoryIds,
},
})
})
it('both Hashtags are created with the "id" set to their "name"', async () => {
const expected = [
{
id: 'Democracy',
},
{
id: 'Liberty',
},
]
await expect(
query({
query: postWithHastagsQuery,
variables: postWithHastagsVariables,
}),
).resolves.toEqual(
expect.objectContaining({
data: {
Post: [
{
tags: expect.arrayContaining(expected),
},
],
},
}),
)
})
describe('afterwards update the Post by removing a Hashtag, leaving a Hashtag and add a Hashtag', () => {
// The already existing Hashtag has no class at this point.
const postContent =
'<p>Hey Dude, <a class="hashtag" href="/search/hashtag/Elections">#Elections</a> should work equal for everybody!? That seems to be the only way to have equal <a href="/search/hashtag/Liberty">#Liberty</a> for everyone.</p>'
it('only one previous Hashtag and the new Hashtag exists', async () => {
await mutate({
mutation: updatePostMutation,
variables: {
id,
title,
postContent,
categoryIds,
},
})
const expected = [
{
id: 'Elections',
},
{
id: 'Liberty',
},
]
await expect(
query({
query: postWithHastagsQuery,
variables: postWithHastagsVariables,
}),
).resolves.toEqual(
expect.objectContaining({
data: {
Post: [
{
tags: expect.arrayContaining(expected),
},
],
},
}),
)
})
})
})
})
})