mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #1572 from Human-Connection/set_created_at_in_cypher
Fix bug where Post.createdAt is sometimes null
This commit is contained in:
commit
de0837bbd3
@ -1,18 +0,0 @@
|
|||||||
const setCreatedAt = (resolve, root, args, context, info) => {
|
|
||||||
args.createdAt = new Date().toISOString()
|
|
||||||
return resolve(root, args, context, info)
|
|
||||||
}
|
|
||||||
const setUpdatedAt = (resolve, root, args, context, info) => {
|
|
||||||
args.updatedAt = new Date().toISOString()
|
|
||||||
return resolve(root, args, context, info)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
Mutation: {
|
|
||||||
CreatePost: setCreatedAt,
|
|
||||||
CreateComment: setCreatedAt,
|
|
||||||
UpdateUser: setUpdatedAt,
|
|
||||||
UpdatePost: setUpdatedAt,
|
|
||||||
UpdateComment: setUpdatedAt,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -5,7 +5,6 @@ import activityPub from './activityPubMiddleware'
|
|||||||
import softDelete from './softDelete/softDeleteMiddleware'
|
import softDelete from './softDelete/softDeleteMiddleware'
|
||||||
import sluggify from './sluggifyMiddleware'
|
import sluggify from './sluggifyMiddleware'
|
||||||
import excerpt from './excerptMiddleware'
|
import excerpt from './excerptMiddleware'
|
||||||
import dateTime from './dateTimeMiddleware'
|
|
||||||
import xss from './xssMiddleware'
|
import xss from './xssMiddleware'
|
||||||
import permissions from './permissionsMiddleware'
|
import permissions from './permissionsMiddleware'
|
||||||
import user from './userMiddleware'
|
import user from './userMiddleware'
|
||||||
@ -22,7 +21,6 @@ export default schema => {
|
|||||||
permissions,
|
permissions,
|
||||||
sentry,
|
sentry,
|
||||||
activityPub,
|
activityPub,
|
||||||
dateTime,
|
|
||||||
validation,
|
validation,
|
||||||
sluggify,
|
sluggify,
|
||||||
excerpt,
|
excerpt,
|
||||||
@ -40,7 +38,6 @@ export default schema => {
|
|||||||
'sentry',
|
'sentry',
|
||||||
'permissions',
|
'permissions',
|
||||||
// 'activityPub', disabled temporarily
|
// 'activityPub', disabled temporarily
|
||||||
'dateTime',
|
|
||||||
'validation',
|
'validation',
|
||||||
'sluggify',
|
'sluggify',
|
||||||
'excerpt',
|
'excerpt',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import cheerio from 'cheerio'
|
import cheerio from 'cheerio'
|
||||||
|
|
||||||
export default function(content) {
|
export default content => {
|
||||||
if (!content) return []
|
if (!content) return []
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
const userIds = $('a.mention[data-mention-id]')
|
const userIds = $('a.mention[data-mention-id]')
|
||||||
|
|||||||
@ -16,7 +16,6 @@ const notifyUsers = async (label, id, idsOfUsers, reason, context) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const createdAt = new Date().toISOString()
|
|
||||||
let cypher
|
let cypher
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case 'mentioned_in_post': {
|
case 'mentioned_in_post': {
|
||||||
@ -27,7 +26,11 @@ const notifyUsers = async (label, id, idsOfUsers, reason, context) => {
|
|||||||
AND NOT (user)<-[:BLOCKED]-(author)
|
AND NOT (user)<-[:BLOCKED]-(author)
|
||||||
MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user)
|
MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||||
SET notification.read = FALSE
|
SET notification.read = FALSE
|
||||||
SET notification.createdAt = $createdAt
|
SET (
|
||||||
|
CASE
|
||||||
|
WHEN notification.createdAt IS NULL
|
||||||
|
THEN notification END ).createdAt = toString(datetime())
|
||||||
|
SET notification.updatedAt = toString(datetime())
|
||||||
`
|
`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -40,7 +43,11 @@ const notifyUsers = async (label, id, idsOfUsers, reason, context) => {
|
|||||||
AND NOT (user)<-[:BLOCKED]-(postAuthor)
|
AND NOT (user)<-[:BLOCKED]-(postAuthor)
|
||||||
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
|
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||||
SET notification.read = FALSE
|
SET notification.read = FALSE
|
||||||
SET notification.createdAt = $createdAt
|
SET (
|
||||||
|
CASE
|
||||||
|
WHEN notification.createdAt IS NULL
|
||||||
|
THEN notification END ).createdAt = toString(datetime())
|
||||||
|
SET notification.updatedAt = toString(datetime())
|
||||||
`
|
`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -53,17 +60,19 @@ const notifyUsers = async (label, id, idsOfUsers, reason, context) => {
|
|||||||
AND NOT (author)<-[:BLOCKED]-(user)
|
AND NOT (author)<-[:BLOCKED]-(user)
|
||||||
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
|
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||||
SET notification.read = FALSE
|
SET notification.read = FALSE
|
||||||
SET notification.createdAt = $createdAt
|
SET (
|
||||||
|
CASE
|
||||||
|
WHEN notification.createdAt IS NULL
|
||||||
|
THEN notification END ).createdAt = toString(datetime())
|
||||||
|
SET notification.updatedAt = toString(datetime())
|
||||||
`
|
`
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await session.run(cypher, {
|
await session.run(cypher, {
|
||||||
label,
|
|
||||||
id,
|
id,
|
||||||
idsOfUsers,
|
idsOfUsers,
|
||||||
reason,
|
reason,
|
||||||
createdAt,
|
|
||||||
})
|
})
|
||||||
session.close()
|
session.close()
|
||||||
}
|
}
|
||||||
@ -82,6 +91,7 @@ const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo
|
|||||||
|
|
||||||
const handleContentDataOfComment = async (resolve, root, args, context, resolveInfo) => {
|
const handleContentDataOfComment = async (resolve, root, args, context, resolveInfo) => {
|
||||||
const idsOfUsers = extractMentionedUsers(args.content)
|
const idsOfUsers = extractMentionedUsers(args.content)
|
||||||
|
|
||||||
const comment = await resolve(root, args, context, resolveInfo)
|
const comment = await resolve(root, args, context, resolveInfo)
|
||||||
|
|
||||||
if (comment) {
|
if (comment) {
|
||||||
|
|||||||
@ -77,7 +77,7 @@ afterEach(async () => {
|
|||||||
describe('notifications', () => {
|
describe('notifications', () => {
|
||||||
const notificationQuery = gql`
|
const notificationQuery = gql`
|
||||||
query($read: Boolean) {
|
query($read: Boolean) {
|
||||||
notifications(read: $read, orderBy: createdAt_desc) {
|
notifications(read: $read, orderBy: updatedAt_desc) {
|
||||||
read
|
read
|
||||||
reason
|
reason
|
||||||
createdAt
|
createdAt
|
||||||
@ -391,7 +391,7 @@ describe('notifications', () => {
|
|||||||
expect(Date.parse(createdAtBefore)).toEqual(expect.any(Number))
|
expect(Date.parse(createdAtBefore)).toEqual(expect.any(Number))
|
||||||
expect(createdAtAfter).toBeTruthy()
|
expect(createdAtAfter).toBeTruthy()
|
||||||
expect(Date.parse(createdAtAfter)).toEqual(expect.any(Number))
|
expect(Date.parse(createdAtAfter)).toEqual(expect.any(Number))
|
||||||
expect(createdAtBefore).not.toEqual(createdAtAfter)
|
expect(createdAtBefore).toEqual(createdAtAfter)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -78,7 +78,7 @@ const invitationLimitReached = rule({
|
|||||||
|
|
||||||
const isAuthor = rule({
|
const isAuthor = rule({
|
||||||
cache: 'no_cache',
|
cache: 'no_cache',
|
||||||
})(async (parent, args, { user, driver }) => {
|
})(async (_parent, args, { user, driver }) => {
|
||||||
if (!user) return false
|
if (!user) return false
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const { id: resourceId } = args
|
const { id: resourceId } = args
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
import uuid from 'uuid/v4'
|
||||||
import Resolver from './helpers/Resolver'
|
import Resolver from './helpers/Resolver'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -10,44 +10,44 @@ export default {
|
|||||||
// because we use relationships for this. So, we are deleting it from params
|
// because we use relationships for this. So, we are deleting it from params
|
||||||
// before comment creation.
|
// before comment creation.
|
||||||
delete params.postId
|
delete params.postId
|
||||||
|
params.id = params.id || uuid()
|
||||||
|
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const commentWithoutRelationships = await neo4jgraphql(
|
const createCommentCypher = `
|
||||||
object,
|
MATCH (post:Post {id: $postId})
|
||||||
params,
|
MATCH (author:User {id: $userId})
|
||||||
context,
|
WITH post, author
|
||||||
resolveInfo,
|
CREATE (comment:Comment {params})
|
||||||
false,
|
SET comment.createdAt = toString(datetime())
|
||||||
)
|
SET comment.updatedAt = toString(datetime())
|
||||||
|
|
||||||
const transactionRes = await session.run(
|
|
||||||
`
|
|
||||||
MATCH (post:Post {id: $postId}), (comment:Comment {id: $commentId}), (author:User {id: $userId})
|
|
||||||
MERGE (post)<-[:COMMENTS]-(comment)<-[:WROTE]-(author)
|
MERGE (post)<-[:COMMENTS]-(comment)<-[:WROTE]-(author)
|
||||||
RETURN comment, author`,
|
RETURN comment
|
||||||
{
|
`
|
||||||
userId: context.user.id,
|
const transactionRes = await session.run(createCommentCypher, {
|
||||||
postId,
|
userId: context.user.id,
|
||||||
commentId: commentWithoutRelationships.id,
|
postId,
|
||||||
},
|
params,
|
||||||
)
|
|
||||||
|
|
||||||
const [commentWithAuthor] = transactionRes.records.map(record => {
|
|
||||||
return {
|
|
||||||
comment: record.get('comment'),
|
|
||||||
author: record.get('author'),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const { comment, author } = commentWithAuthor
|
|
||||||
|
|
||||||
const commentReturnedWithAuthor = {
|
|
||||||
...comment.properties,
|
|
||||||
author: author.properties,
|
|
||||||
}
|
|
||||||
session.close()
|
session.close()
|
||||||
return commentReturnedWithAuthor
|
|
||||||
|
const [comment] = transactionRes.records.map(record => record.get('comment').properties)
|
||||||
|
|
||||||
|
return comment
|
||||||
},
|
},
|
||||||
DeleteComment: async (object, args, context, resolveInfo) => {
|
UpdateComment: async (_parent, params, context, _resolveInfo) => {
|
||||||
|
const session = context.driver.session()
|
||||||
|
const updateCommentCypher = `
|
||||||
|
MATCH (comment:Comment {id: $params.id})
|
||||||
|
SET comment += $params
|
||||||
|
SET comment.updatedAt = toString(datetime())
|
||||||
|
RETURN comment
|
||||||
|
`
|
||||||
|
const transactionRes = await session.run(updateCommentCypher, { params })
|
||||||
|
session.close()
|
||||||
|
const [comment] = transactionRes.records.map(record => record.get('comment').properties)
|
||||||
|
return comment
|
||||||
|
},
|
||||||
|
DeleteComment: async (_parent, args, context, _resolveInfo) => {
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const transactionRes = await session.run(
|
const transactionRes = await session.run(
|
||||||
`
|
`
|
||||||
|
|||||||
@ -8,10 +8,7 @@ const driver = getDriver()
|
|||||||
const neode = getNeode()
|
const neode = getNeode()
|
||||||
const factory = Factory()
|
const factory = Factory()
|
||||||
|
|
||||||
let variables
|
let variables, mutate, authenticatedUser, commentAuthor, newlyCreatedComment
|
||||||
let mutate
|
|
||||||
let authenticatedUser
|
|
||||||
let commentAuthor
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
const { server } = createServer({
|
const { server } = createServer({
|
||||||
@ -57,7 +54,7 @@ const setupPostAndComment = async () => {
|
|||||||
content: 'Post to be commented',
|
content: 'Post to be commented',
|
||||||
categoryIds: ['cat9'],
|
categoryIds: ['cat9'],
|
||||||
})
|
})
|
||||||
await factory.create('Comment', {
|
newlyCreatedComment = await factory.create('Comment', {
|
||||||
id: 'c456',
|
id: 'c456',
|
||||||
postId: 'p1',
|
postId: 'p1',
|
||||||
author: commentAuthor,
|
author: commentAuthor,
|
||||||
@ -160,6 +157,8 @@ describe('UpdateComment', () => {
|
|||||||
UpdateComment(content: $content, id: $id) {
|
UpdateComment(content: $content, id: $id) {
|
||||||
id
|
id
|
||||||
content
|
content
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -200,6 +199,33 @@ describe('UpdateComment', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('updates a comment, but maintains non-updated attributes', async () => {
|
||||||
|
const expected = {
|
||||||
|
data: {
|
||||||
|
UpdateComment: {
|
||||||
|
id: 'c456',
|
||||||
|
content: 'The comment is updated',
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await expect(mutate({ mutation: updateCommentMutation, variables })).resolves.toMatchObject(
|
||||||
|
expected,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates the updatedAt attribute', async () => {
|
||||||
|
newlyCreatedComment = await newlyCreatedComment.toJson()
|
||||||
|
const {
|
||||||
|
data: { UpdateComment },
|
||||||
|
} = await mutate({ mutation: updateCommentMutation, variables })
|
||||||
|
expect(newlyCreatedComment.updatedAt).toBeTruthy()
|
||||||
|
expect(Date.parse(newlyCreatedComment.updatedAt)).toEqual(expect.any(Number))
|
||||||
|
expect(UpdateComment.updatedAt).toBeTruthy()
|
||||||
|
expect(Date.parse(UpdateComment.updatedAt)).toEqual(expect.any(Number))
|
||||||
|
expect(newlyCreatedComment.updatedAt).not.toEqual(UpdateComment.updatedAt)
|
||||||
|
})
|
||||||
|
|
||||||
describe('if `content` empty', () => {
|
describe('if `content` empty', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
variables = { ...variables, content: ' <p> </p>' }
|
variables = { ...variables, content: ' <p> </p>' }
|
||||||
|
|||||||
@ -15,7 +15,7 @@ const transformReturnType = record => {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
notifications: async (parent, args, context, resolveInfo) => {
|
notifications: async (_parent, args, context, _resolveInfo) => {
|
||||||
const { user: currentUser } = context
|
const { user: currentUser } = context
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
let notifications
|
let notifications
|
||||||
@ -32,11 +32,11 @@ export default {
|
|||||||
whereClause = ''
|
whereClause = ''
|
||||||
}
|
}
|
||||||
switch (args.orderBy) {
|
switch (args.orderBy) {
|
||||||
case 'createdAt_asc':
|
case 'updatedAt_asc':
|
||||||
orderByClause = 'ORDER BY notification.createdAt ASC'
|
orderByClause = 'ORDER BY notification.updatedAt ASC'
|
||||||
break
|
break
|
||||||
case 'createdAt_desc':
|
case 'updatedAt_desc':
|
||||||
orderByClause = 'ORDER BY notification.createdAt DESC'
|
orderByClause = 'ORDER BY notification.updatedAt DESC'
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
orderByClause = ''
|
orderByClause = ''
|
||||||
|
|||||||
@ -145,47 +145,46 @@ describe('given some notifications', () => {
|
|||||||
|
|
||||||
describe('no filters', () => {
|
describe('no filters', () => {
|
||||||
it('returns all notifications of current user', async () => {
|
it('returns all notifications of current user', async () => {
|
||||||
const expected = {
|
const expected = [
|
||||||
data: {
|
{
|
||||||
notifications: [
|
from: {
|
||||||
{
|
__typename: 'Comment',
|
||||||
from: {
|
content: 'You have seen this comment mentioning already',
|
||||||
__typename: 'Comment',
|
},
|
||||||
content: 'You have seen this comment mentioning already',
|
read: true,
|
||||||
},
|
createdAt: '2019-08-30T15:33:48.651Z',
|
||||||
read: true,
|
|
||||||
createdAt: '2019-08-30T15:33:48.651Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
from: {
|
|
||||||
__typename: 'Post',
|
|
||||||
content: 'Already seen post mention',
|
|
||||||
},
|
|
||||||
read: true,
|
|
||||||
createdAt: '2019-08-30T17:33:48.651Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
from: {
|
|
||||||
__typename: 'Comment',
|
|
||||||
content: 'You have been mentioned in a comment',
|
|
||||||
},
|
|
||||||
read: false,
|
|
||||||
createdAt: '2019-08-30T19:33:48.651Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
from: {
|
|
||||||
__typename: 'Post',
|
|
||||||
content: 'You have been mentioned in a post',
|
|
||||||
},
|
|
||||||
read: false,
|
|
||||||
createdAt: '2019-08-31T17:33:48.651Z',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
await expect(query({ query: notificationQuery, variables })).resolves.toMatchObject(
|
from: {
|
||||||
expected,
|
__typename: 'Post',
|
||||||
)
|
content: 'Already seen post mention',
|
||||||
|
},
|
||||||
|
read: true,
|
||||||
|
createdAt: '2019-08-30T17:33:48.651Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: {
|
||||||
|
__typename: 'Comment',
|
||||||
|
content: 'You have been mentioned in a comment',
|
||||||
|
},
|
||||||
|
read: false,
|
||||||
|
createdAt: '2019-08-30T19:33:48.651Z',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: {
|
||||||
|
__typename: 'Post',
|
||||||
|
content: 'You have been mentioned in a post',
|
||||||
|
},
|
||||||
|
read: false,
|
||||||
|
createdAt: '2019-08-31T17:33:48.651Z',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
await expect(query({ query: notificationQuery, variables })).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
notifications: expect.arrayContaining(expected),
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -74,7 +74,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
CreatePost: async (object, params, context, resolveInfo) => {
|
CreatePost: async (_parent, params, context, _resolveInfo) => {
|
||||||
const { categoryIds } = params
|
const { categoryIds } = params
|
||||||
delete params.categoryIds
|
delete params.categoryIds
|
||||||
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
|
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
|
||||||
@ -82,6 +82,8 @@ export default {
|
|||||||
let post
|
let post
|
||||||
|
|
||||||
const createPostCypher = `CREATE (post:Post {params})
|
const createPostCypher = `CREATE (post:Post {params})
|
||||||
|
SET post.createdAt = toString(datetime())
|
||||||
|
SET post.updatedAt = toString(datetime())
|
||||||
WITH post
|
WITH post
|
||||||
MATCH (author:User {id: $userId})
|
MATCH (author:User {id: $userId})
|
||||||
MERGE (post)<-[:WROTE]-(author)
|
MERGE (post)<-[:WROTE]-(author)
|
||||||
@ -96,9 +98,7 @@ export default {
|
|||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
try {
|
try {
|
||||||
const transactionRes = await session.run(createPostCypher, createPostVariables)
|
const transactionRes = await session.run(createPostCypher, createPostVariables)
|
||||||
const posts = transactionRes.records.map(record => {
|
const posts = transactionRes.records.map(record => record.get('post').properties)
|
||||||
return record.get('post').properties
|
|
||||||
})
|
|
||||||
post = posts[0]
|
post = posts[0]
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
|
if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
|
||||||
@ -110,14 +110,15 @@ export default {
|
|||||||
|
|
||||||
return post
|
return post
|
||||||
},
|
},
|
||||||
UpdatePost: async (object, params, context, resolveInfo) => {
|
UpdatePost: async (_parent, params, context, _resolveInfo) => {
|
||||||
const { categoryIds } = params
|
const { categoryIds } = params
|
||||||
delete params.categoryIds
|
delete params.categoryIds
|
||||||
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
|
params = await fileUpload(params, { file: 'imageUpload', url: 'image' })
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
|
|
||||||
let updatePostCypher = `MATCH (post:Post {id: $params.id})
|
let updatePostCypher = `MATCH (post:Post {id: $params.id})
|
||||||
SET post = $params
|
SET post += $params
|
||||||
|
SET post.updatedAt = toString(datetime())
|
||||||
`
|
`
|
||||||
|
|
||||||
if (categoryIds && categoryIds.length) {
|
if (categoryIds && categoryIds.length) {
|
||||||
@ -129,10 +130,11 @@ export default {
|
|||||||
|
|
||||||
await session.run(cypherDeletePreviousRelations, { params })
|
await session.run(cypherDeletePreviousRelations, { params })
|
||||||
|
|
||||||
updatePostCypher += `WITH post
|
updatePostCypher += `
|
||||||
UNWIND $categoryIds AS categoryId
|
WITH post
|
||||||
MATCH (category:Category {id: categoryId})
|
UNWIND $categoryIds AS categoryId
|
||||||
MERGE (post)-[:CATEGORIZED]->(category)
|
MATCH (category:Category {id: categoryId})
|
||||||
|
MERGE (post)-[:CATEGORIZED]->(category)
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,12 +143,12 @@ export default {
|
|||||||
|
|
||||||
const transactionRes = await session.run(updatePostCypher, updatePostVariables)
|
const transactionRes = await session.run(updatePostCypher, updatePostVariables)
|
||||||
const [post] = transactionRes.records.map(record => {
|
const [post] = transactionRes.records.map(record => {
|
||||||
return record.get('post')
|
return record.get('post').properties
|
||||||
})
|
})
|
||||||
|
|
||||||
session.close()
|
session.close()
|
||||||
|
|
||||||
return post.properties
|
return post
|
||||||
},
|
},
|
||||||
|
|
||||||
DeletePost: async (object, args, context, resolveInfo) => {
|
DeletePost: async (object, args, context, resolveInfo) => {
|
||||||
|
|||||||
@ -361,7 +361,7 @@ describe('CreatePost', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('UpdatePost', () => {
|
describe('UpdatePost', () => {
|
||||||
let author
|
let author, newlyCreatedPost
|
||||||
const updatePostMutation = gql`
|
const updatePostMutation = gql`
|
||||||
mutation($id: ID!, $title: String!, $content: String!, $categoryIds: [ID]) {
|
mutation($id: ID!, $title: String!, $content: String!, $categoryIds: [ID]) {
|
||||||
UpdatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) {
|
UpdatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) {
|
||||||
@ -370,12 +370,14 @@ describe('UpdatePost', () => {
|
|||||||
categories {
|
categories {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
author = await factory.create('User', { slug: 'the-author' })
|
author = await factory.create('User', { slug: 'the-author' })
|
||||||
await factory.create('Post', {
|
newlyCreatedPost = await factory.create('Post', {
|
||||||
author,
|
author,
|
||||||
id: 'p9876',
|
id: 'p9876',
|
||||||
title: 'Old title',
|
title: 'Old title',
|
||||||
@ -421,6 +423,29 @@ describe('UpdatePost', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('updates a post, but maintains non-updated attributes', async () => {
|
||||||
|
const expected = {
|
||||||
|
data: {
|
||||||
|
UpdatePost: { id: 'p9876', content: 'New content', createdAt: expect.any(String) },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
|
||||||
|
expected,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('updates the updatedAt attribute', async () => {
|
||||||
|
newlyCreatedPost = await newlyCreatedPost.toJson()
|
||||||
|
const {
|
||||||
|
data: { UpdatePost },
|
||||||
|
} = await mutate({ mutation: updatePostMutation, variables })
|
||||||
|
expect(newlyCreatedPost.updatedAt).toBeTruthy()
|
||||||
|
expect(Date.parse(newlyCreatedPost.updatedAt)).toEqual(expect.any(Number))
|
||||||
|
expect(UpdatePost.updatedAt).toBeTruthy()
|
||||||
|
expect(Date.parse(UpdatePost.updatedAt)).toEqual(expect.any(Number))
|
||||||
|
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.updatedAt)
|
||||||
|
})
|
||||||
|
|
||||||
describe('no new category ids provided for update', () => {
|
describe('no new category ids provided for update', () => {
|
||||||
it('resolves and keeps current categories', async () => {
|
it('resolves and keeps current categories', async () => {
|
||||||
const expected = {
|
const expected = {
|
||||||
|
|||||||
@ -100,7 +100,7 @@ export default {
|
|||||||
try {
|
try {
|
||||||
const user = await instance.find('User', args.id)
|
const user = await instance.find('User', args.id)
|
||||||
if (!user) return null
|
if (!user) return null
|
||||||
await user.update(args)
|
await user.update({ ...args, updatedAt: new Date().toISOString() })
|
||||||
return user.toJson()
|
return user.toJson()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw new UserInputError(e.message)
|
throw new UserInputError(e.message)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ type NOTIFIED {
|
|||||||
from: NotificationSource
|
from: NotificationSource
|
||||||
to: User
|
to: User
|
||||||
createdAt: String
|
createdAt: String
|
||||||
|
updatedAt: String
|
||||||
read: Boolean
|
read: Boolean
|
||||||
reason: NotificationReason
|
reason: NotificationReason
|
||||||
}
|
}
|
||||||
@ -11,6 +12,8 @@ union NotificationSource = Post | Comment
|
|||||||
enum NotificationOrdering {
|
enum NotificationOrdering {
|
||||||
createdAt_asc
|
createdAt_asc
|
||||||
createdAt_desc
|
createdAt_desc
|
||||||
|
updatedAt_asc
|
||||||
|
updatedAt_desc
|
||||||
}
|
}
|
||||||
|
|
||||||
enum NotificationReason {
|
enum NotificationReason {
|
||||||
|
|||||||
@ -97,7 +97,7 @@ export const notificationQuery = i18n => {
|
|||||||
${postFragment(lang)}
|
${postFragment(lang)}
|
||||||
|
|
||||||
query {
|
query {
|
||||||
notifications(read: false, orderBy: createdAt_desc) {
|
notifications(read: false, orderBy: updatedAt_desc) {
|
||||||
read
|
read
|
||||||
reason
|
reason
|
||||||
createdAt
|
createdAt
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user