diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index b1a08a14d..ea4ba3dd2 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -16,11 +16,15 @@ const isAdmin = rule()(async (parent, args, { user }, info) => { return user && user.role === 'admin' }) -const isMyOwn = rule({ cache: 'no_cache' })(async (parent, args, context, info) => { +const isMyOwn = rule({ + cache: 'no_cache', +})(async (parent, args, context, info) => { return context.user.id === parent.id }) -const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => { +const belongsToMe = rule({ + cache: 'no_cache', +})(async (_, args, context) => { const { driver, user: { id: userId }, @@ -32,7 +36,10 @@ const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => { MATCH (u:User {id: $userId})<-[:NOTIFIED]-(n:Notification {id: $notificationId}) RETURN n `, - { userId, notificationId }, + { + userId, + notificationId, + }, ) const [notification] = result.records.map(record => { return record.get('n') @@ -41,12 +48,16 @@ const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => { return Boolean(notification) }) -const onlyEnabledContent = rule({ cache: 'strict' })(async (parent, args, ctx, info) => { +const onlyEnabledContent = rule({ + cache: 'strict', +})(async (parent, args, ctx, info) => { const { disabled, deleted } = args return !(disabled || deleted) }) -const isAuthor = rule({ cache: 'no_cache' })(async (parent, args, { user, driver }) => { +const isAuthor = rule({ + cache: 'no_cache', +})(async (parent, args, { user, driver }) => { if (!user) return false const session = driver.session() const { id: postId } = args @@ -55,7 +66,9 @@ const isAuthor = rule({ cache: 'no_cache' })(async (parent, args, { user, driver MATCH (post:Post {id: $postId})<-[:WROTE]-(author) RETURN author `, - { postId }, + { + postId, + }, ) const [author] = result.records.map(record => { return record.get('author') @@ -100,6 +113,7 @@ const permissions = shield({ enable: isModerator, disable: isModerator, CreateComment: isAuthenticated, + DeleteComment: isAuthenticated, // CreateUser: allow, }, User: { diff --git a/backend/src/resolvers/comments.js b/backend/src/resolvers/comments.js index 949b77041..a4b3c21b1 100644 --- a/backend/src/resolvers/comments.js +++ b/backend/src/resolvers/comments.js @@ -53,6 +53,11 @@ export default { ) session.close() + return comment + }, + DeleteComment: async (object, params, context, resolveInfo) => { + const socialMedia = await neo4jgraphql(object, params, context, resolveInfo, false) + return comment }, }, diff --git a/backend/src/resolvers/comments.spec.js b/backend/src/resolvers/comments.spec.js index 451c559f1..4d86d2d58 100644 --- a/backend/src/resolvers/comments.spec.js +++ b/backend/src/resolvers/comments.spec.js @@ -1,3 +1,4 @@ +import gql from 'graphql-tag' import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' import { host, login } from '../jest/helpers' @@ -5,6 +6,7 @@ import { host, login } from '../jest/helpers' const factory = Factory() let client let createCommentVariables +let deleteCommentVariables let createPostVariables let createCommentVariablesSansPostId let createCommentVariablesWithNonExistentPost @@ -21,22 +23,22 @@ afterEach(async () => { }) describe('CreateComment', () => { - const createCommentMutation = ` - mutation($postId: ID, $content: String!) { - CreateComment(postId: $postId, content: $content) { - id - content + const createCommentMutation = gql` + mutation($postId: ID, $content: String!) { + CreateComment(postId: $postId, content: $content) { + id + content + } } - } ` - const createPostMutation = ` - mutation($id: ID!, $title: String!, $content: String!) { - CreatePost(id: $id, title: $title, content: $content) { - id + const createPostMutation = gql` + mutation($id: ID!, $title: String!, $content: String!) { + CreatePost(id: $id, title: $title, content: $content) { + id + } } - } ` - const commentQueryForPostId = ` + const commentQueryForPostId = gql` query($content: String) { Comment(content: $content) { postId @@ -59,8 +61,13 @@ describe('CreateComment', () => { describe('authenticated', () => { let headers beforeEach(async () => { - headers = await login({ email: 'test@example.org', password: '1234' }) - client = new GraphQLClient(host, { headers }) + headers = await login({ + email: 'test@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) createCommentVariables = { postId: 'p1', content: "I'm authorised to comment", @@ -96,7 +103,15 @@ describe('CreateComment', () => { } }`) - expect(User).toEqual([{ comments: [{ content: "I'm authorised to comment" }] }]) + expect(User).toEqual([ + { + comments: [ + { + content: "I'm authorised to comment", + }, + ], + }, + ]) }) it('throw an error if an empty string is sent from the editor as content', async () => { @@ -186,7 +201,204 @@ describe('CreateComment', () => { commentQueryForPostId, commentQueryVariablesByContent, ) - expect(Comment).toEqual([{ postId: null }]) + expect(Comment).toEqual([ + { + postId: null, + }, + ]) }) }) }) + +// describe('DeleteComment', () => { +// const createCommentMutation = gql ` +// mutation($postId: ID, $content: String!) { +// CreateComment(postId: $postId, content: $content) { +// id +// content +// } +// } +// ` +// const deleteCommentMutation = gql ` +// mutation($id: ID!) { +// DeleteComment(id: $id) { +// id +// content +// } +// } +// ` +// const createPostMutation = gql ` +// mutation($id: ID!, $title: String!, $content: String!) { +// CreatePost(id: $id, title: $title, content: $content) { +// id +// } +// } +// ` +// const commentQueryForPostId = gql ` +// query($content: String) { +// Comment(content: $content) { +// postId +// } +// } +// ` +// describe('unauthenticated', () => { +// it('throws authorization error', async () => { +// deleteCommentVariables = { +// id: 'c1', +// } +// client = new GraphQLClient(host) +// await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow( +// 'Not Authorised', +// ) +// }) +// }) + +// // describe('authenticated', () => { +// // let headers +// // beforeEach(async () => { +// // headers = await login({ +// // email: 'test@example.org', +// // password: '1234' +// // }) +// // client = new GraphQLClient(host, { +// // headers +// // }) +// // createCommentVariables = { +// // postId: 'p1', +// // content: "I'm authorised to comment", +// // } +// // createPostVariables = { +// // id: 'p1', +// // title: 'post to comment on', +// // content: 'please comment on me', +// // } +// // await client.request(createPostMutation, createPostVariables) +// // }) + +// // it('creates a comment', async () => { +// // const expected = { +// // CreateComment: { +// // content: "I'm authorised to comment", +// // }, +// // } + +// // await expect( +// // client.request(createCommentMutation, createCommentVariables), +// // ).resolves.toMatchObject(expected) +// // }) + +// // it('assigns the authenticated user as author', async () => { +// // await client.request(createCommentMutation, createCommentVariables) + +// // const { +// // User +// // } = await client.request(`{ +// // User(email: "test@example.org") { +// // comments { +// // content +// // } +// // } +// // }`) + +// // expect(User).toEqual([{ +// // comments: [{ +// // content: "I'm authorised to comment" +// // }] +// // }]) +// // }) + +// // it('throw an error if an empty string is sent from the editor as content', async () => { +// // createCommentVariables = { +// // postId: 'p1', +// // content: '

', +// // } + +// // await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( +// // 'Comment must be at least 1 character long!', +// // ) +// // }) + +// // it('throws an error if a comment sent from the editor does not contain a single character', async () => { +// // createCommentVariables = { +// // postId: 'p1', +// // content: '

', +// // } + +// // await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( +// // 'Comment must be at least 1 character long!', +// // ) +// // }) + +// // it('throws an error if postId is sent as an empty string', async () => { +// // createCommentVariables = { +// // postId: 'p1', +// // content: '', +// // } + +// // await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( +// // 'Comment must be at least 1 character long!', +// // ) +// // }) + +// // it('throws an error if content is sent as an string of empty characters', async () => { +// // createCommentVariables = { +// // postId: 'p1', +// // content: ' ', +// // } + +// // await expect(client.request(createCommentMutation, createCommentVariables)).rejects.toThrow( +// // 'Comment must be at least 1 character long!', +// // ) +// // }) + +// // it('throws an error if postId is sent as an empty string', async () => { +// // createCommentVariablesSansPostId = { +// // postId: '', +// // content: 'this comment should not be created', +// // } + +// // await expect( +// // client.request(createCommentMutation, createCommentVariablesSansPostId), +// // ).rejects.toThrow('Comment cannot be created without a post!') +// // }) + +// // it('throws an error if postId is sent as an string of empty characters', async () => { +// // createCommentVariablesSansPostId = { +// // postId: ' ', +// // content: 'this comment should not be created', +// // } + +// // await expect( +// // client.request(createCommentMutation, createCommentVariablesSansPostId), +// // ).rejects.toThrow('Comment cannot be created without a post!') +// // }) + +// // it('throws an error if the post does not exist in the database', async () => { +// // createCommentVariablesWithNonExistentPost = { +// // postId: 'p2', +// // content: "comment should not be created cause the post doesn't exist", +// // } + +// // await expect( +// // client.request(createCommentMutation, createCommentVariablesWithNonExistentPost), +// // ).rejects.toThrow('Comment cannot be created without a post!') +// // }) + +// // it('does not create the comment with the postId as an attribute', async () => { +// // const commentQueryVariablesByContent = { +// // content: "I'm authorised to comment", +// // } + +// // await client.request(createCommentMutation, createCommentVariables) +// // const { +// // Comment +// // } = await client.request( +// // commentQueryForPostId, +// // commentQueryVariablesByContent, +// // ) +// // expect(Comment).toEqual([{ +// // postId: null +// // }]) +// // }) +// // }) +// }) diff --git a/webapp/components/Comment.spec.js b/webapp/components/Comment.spec.js index fa22679c5..e899a05e1 100644 --- a/webapp/components/Comment.spec.js +++ b/webapp/components/Comment.spec.js @@ -1,8 +1,4 @@ -import { - config, - shallowMount, - createLocalVue -} from '@vue/test-utils' +import { config, shallowMount, createLocalVue } from '@vue/test-utils' import Comment from './Comment.vue' import Vuex from 'vuex' import Styleguide from '@human-connection/styleguide' @@ -25,6 +21,13 @@ describe('Comment.vue', () => { propsData = {} mocks = { $t: jest.fn(), + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + $apollo: { + mutate: jest.fn().mockResolvedValue(), + }, } getters = { 'auth/user': () => { @@ -76,12 +79,9 @@ describe('Comment.vue', () => { }) it('translates a placeholder', () => { - /* const wrapper = */ - Wrapper() + wrapper = Wrapper() const calls = mocks.$t.mock.calls - const expected = [ - ['comment.content.unavailable-placeholder'] - ] + const expected = [['comment.content.unavailable-placeholder']] expect(calls).toEqual(expect.arrayContaining(expected)) }) @@ -121,10 +121,6 @@ describe('Comment.vue', () => { expect(wrapper.emitted().deleteComment.length).toBe(1) }) - // it('does not go to index (main) page', () => { - // expect(mocks.$router.history.push).not.toHaveBeenCalled() - // }) - it('does call mutation', () => { expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) }) diff --git a/webapp/components/ContentMenu.vue b/webapp/components/ContentMenu.vue index ffa42fec0..60cc4b658 100644 --- a/webapp/components/ContentMenu.vue +++ b/webapp/components/ContentMenu.vue @@ -1,18 +1,32 @@