Start backend implementation UserAccount Deletion

This commit is contained in:
Matt Rider 2019-06-06 17:28:52 -03:00
parent ffa15bf728
commit bf352dca92
4 changed files with 196 additions and 9 deletions

View File

@ -80,6 +80,12 @@ const isAuthor = rule({
return authorId === user.id
})
const isDeletingOwnAccount = rule({
cache: 'no_cache',
})(async (parent, args, context, info) => {
return context.user.id === args.id
})
// Permissions
const permissions = shield({
Query: {
@ -115,6 +121,7 @@ const permissions = shield({
CreateComment: isAuthenticated,
DeleteComment: isAuthor,
// CreateUser: allow,
DeleteUser: isDeletingOwnAccount,
},
User: {
email: isMyOwn,

View File

@ -11,5 +11,33 @@ export default {
params = await fileUpload(params, { file: 'avatarUpload', url: 'avatar' })
return neo4jgraphql(object, params, context, resolveInfo, false)
},
DeleteUser: async (object, params, context, resolveInfo) => {
const { comments, posts } = params
const session = context.driver.session()
if (comments) {
await session.run(
`
MATCH (comments:Comment)<-[:WROTE]-(author:User {id: $userId})
DETACH DELETE comments
RETURN author`,
{
userId: context.user.id,
},
)
}
if (posts) {
await session.run(
`
MATCH (posts:Post)<-[:WROTE]-(author:User {id: $userId})
DETACH DELETE posts
RETURN author`,
{
userId: context.user.id,
},
)
}
session.close()
return neo4jgraphql(object, params, context, resolveInfo, false)
},
},
}

View File

@ -1,6 +1,7 @@
import { GraphQLClient } from 'graphql-request'
import { host } from '../../jest/helpers'
import { host, login } from '../../jest/helpers'
import Factory from '../../seed/factories'
import gql from 'graphql-tag'
const factory = Factory()
let client
@ -82,4 +83,153 @@ describe('users', () => {
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
})
})
describe('DeleteUser', () => {
let deleteUserVariables
let asAuthor
const deleteUserMutation = gql`
mutation($id: ID!, $comments: Boolean, $posts: Boolean) {
DeleteUser(id: $id, comments: $comments, posts: $posts) {
id
}
}
`
beforeEach(async () => {
asAuthor = await factory.create('User', {
email: 'test@example.org',
password: '1234',
id: 'u343',
})
await factory.create('User', {
email: 'friendsAccount@example.org',
password: '1234',
id: 'u565',
})
deleteUserVariables = { id: 'u343' }
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
client = new GraphQLClient(host)
await expect(client.request(deleteUserMutation, deleteUserVariables)).rejects.toThrow(
'Not Authorised',
)
})
})
describe('authenticated', () => {
let headers
beforeEach(async () => {
headers = await login({
email: 'test@example.org',
password: '1234',
})
client = new GraphQLClient(host, { headers })
})
describe("attempting to delete another user's account", () => {
it('throws an authorization error', async () => {
deleteUserVariables = { id: 'u565' }
await expect(client.request(deleteUserMutation, deleteUserVariables)).rejects.toThrow(
'Not Authorised',
)
})
})
describe('attempting to delete my own account', () => {
const commentQuery = gql`
query($content: String) {
Comment(content: $content) {
id
}
}
`
const postQuery = gql`
query($content: String) {
Post(content: $content) {
id
}
}
`
beforeEach(async () => {
await asAuthor.authenticateAs({
email: 'test@example.org',
password: '1234',
})
await asAuthor.create('Post', {
id: 'p139',
content: 'Post by user u343',
})
await asAuthor.create('Comment', {
id: 'c155',
postId: 'p139',
content: 'Comment by user u343',
})
deleteUserVariables = { id: 'u343' }
})
it('deletes my account', async () => {
const expected = {
DeleteUser: {
id: 'u343',
},
}
await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
expected,
)
})
describe("doesn't delete a user's", () => {
it('comments by default', async () => {
await client.request(deleteUserMutation, deleteUserVariables)
const commentQueryVariablesByContent = {
content: 'Comment by user u343',
}
const { Comment } = await client.request(commentQuery, commentQueryVariablesByContent)
expect(Comment).toEqual([
{
id: 'c155',
},
])
})
it('posts by default', async () => {
await client.request(deleteUserMutation, deleteUserVariables)
const postQueryVariablesByContent = {
content: 'Post by user u343',
}
const { Post } = await client.request(postQuery, postQueryVariablesByContent)
expect(Post).toEqual([
{
id: 'p139',
},
])
})
})
describe("deletes a user's", () => {
it('comments on request', async () => {
deleteUserVariables = { id: 'u343', comments: true }
await client.request(deleteUserMutation, deleteUserVariables)
const commentQueryVariablesByContent = {
content: 'Comment by user u343',
}
const { Comment } = await client.request(commentQuery, commentQueryVariablesByContent)
expect(Comment).toEqual([])
})
it('posts on request', async () => {
deleteUserVariables = { id: 'u343', posts: true }
await client.request(deleteUserMutation, deleteUserVariables)
const postQueryVariablesByContent = {
content: 'Post by user u343',
}
const { Post } = await client.request(postQuery, postQueryVariablesByContent)
expect(Post).toEqual([])
})
})
})
})
})
})

View File

@ -4,8 +4,9 @@ type Query {
currentUser: User
# Get the latest Network Statistics
statistics: Statistics!
findPosts(filter: String!, limit: Int = 10): [Post]! @cypher(
statement: """
findPosts(filter: String!, limit: Int = 10): [Post]!
@cypher(
statement: """
CALL db.index.fulltext.queryNodes('full_text_search', $filter)
YIELD node as post, score
MATCH (post)<-[:WROTE]-(user:User)
@ -14,8 +15,8 @@ type Query {
AND NOT post.deleted = true AND NOT post.disabled = true
RETURN post
LIMIT $limit
"""
)
"""
)
CommentByPost(postId: ID!): [Comment]!
}
@ -23,7 +24,7 @@ type Mutation {
# Get a JWT Token for the given Email and password
login(email: String!, password: String!): String!
signup(email: String!, password: String!): Boolean!
changePassword(oldPassword:String!, newPassword: String!): String!
changePassword(oldPassword: String!, newPassword: String!): String!
report(id: ID!, description: String): Report
disable(id: ID!): ID
enable(id: ID!): ID
@ -37,6 +38,7 @@ type Mutation {
follow(id: ID!, type: FollowTypeEnum): Boolean!
# Unfollow the given Type and ID
unfollow(id: ID!, type: FollowTypeEnum): Boolean!
DeleteUser(id: ID!, comments: Boolean, posts: Boolean): User
}
type Statistics {
@ -53,7 +55,7 @@ type Statistics {
type Notification {
id: ID!
read: Boolean,
read: Boolean
user: User @relation(name: "NOTIFIED", direction: "OUT")
post: Post @relation(name: "NOTIFIED", direction: "IN")
createdAt: String
@ -80,7 +82,8 @@ type Report {
id: ID!
submitter: User @relation(name: "REPORTED", direction: "IN")
description: String
type: String! @cypher(statement: "MATCH (resource)<-[:REPORTED]-(this) RETURN labels(resource)[0]")
type: String!
@cypher(statement: "MATCH (resource)<-[:REPORTED]-(this) RETURN labels(resource)[0]")
createdAt: String
comment: Comment @relation(name: "REPORTED", direction: "OUT")
post: Post @relation(name: "REPORTED", direction: "OUT")
@ -131,4 +134,3 @@ type SocialMedia {
url: String
ownedBy: [User]! @relation(name: "OWNED", direction: "IN")
}