mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
SoftDeleteMiddleware obfuscates deleted resources
This commit is contained in:
parent
580c048cfa
commit
c4ba2c4aeb
@ -13,8 +13,8 @@ const setDefaultFilters = (resolve, root, args, context, info) => {
|
||||
return resolve(root, args, context, info)
|
||||
}
|
||||
|
||||
const obfuscateDisabled = async (resolve, root, args, context, info) => {
|
||||
if (!isModerator(context) && root.disabled) {
|
||||
const obfuscate = async (resolve, root, args, context, info) => {
|
||||
if (root.deleted || (!isModerator(context) && root.disabled)) {
|
||||
root.content = 'UNAVAILABLE'
|
||||
root.contentExcerpt = 'UNAVAILABLE'
|
||||
root.title = 'UNAVAILABLE'
|
||||
@ -40,7 +40,7 @@ export default {
|
||||
}
|
||||
return resolve(root, args, context, info)
|
||||
},
|
||||
Post: obfuscateDisabled,
|
||||
User: obfuscateDisabled,
|
||||
Comment: obfuscateDisabled,
|
||||
Post: obfuscate,
|
||||
User: obfuscate,
|
||||
Comment: obfuscate,
|
||||
}
|
||||
|
||||
@ -362,8 +362,8 @@ describe('softDeleteMiddleware', () => {
|
||||
authenticatedUser = await moderator.toJson()
|
||||
})
|
||||
|
||||
it('shows deleted posts', async () => {
|
||||
const expected = { data: { Post: [{ title: 'Deleted post' }] } }
|
||||
it('does not show deleted posts', async () => {
|
||||
const expected = { data: { Post: [{ title: 'UNAVAILABLE' }] } }
|
||||
await expect(action()).resolves.toMatchObject(expected)
|
||||
})
|
||||
})
|
||||
|
||||
@ -53,8 +53,8 @@ export default {
|
||||
`
|
||||
MATCH (comment:Comment {id: $commentId})
|
||||
SET comment.deleted = TRUE
|
||||
SET comment.content = 'DELETED'
|
||||
SET comment.contentExcerpt = 'DELETED'
|
||||
SET comment.content = 'UNAVAILABLE'
|
||||
SET comment.contentExcerpt = 'UNAVAILABLE'
|
||||
RETURN comment
|
||||
`,
|
||||
{ commentId: args.id },
|
||||
|
||||
@ -271,8 +271,8 @@ describe('DeleteComment', () => {
|
||||
DeleteComment: {
|
||||
id: 'c456',
|
||||
deleted: true,
|
||||
content: 'DELETED',
|
||||
contentExcerpt: 'DELETED',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
},
|
||||
}
|
||||
expect(data).toMatchObject(expected)
|
||||
|
||||
@ -148,9 +148,9 @@ export default {
|
||||
MATCH (post:Post {id: $postId})
|
||||
OPTIONAL MATCH (post)<-[:COMMENTS]-(comment:Comment)
|
||||
SET post.deleted = TRUE
|
||||
SET post.image = 'DELETED'
|
||||
SET post.content = 'DELETED'
|
||||
SET post.contentExcerpt = 'DELETED'
|
||||
SET post.image = 'UNAVAILABLE'
|
||||
SET post.content = 'UNAVAILABLE'
|
||||
SET post.contentExcerpt = 'UNAVAILABLE'
|
||||
SET comment.deleted = TRUE
|
||||
RETURN post
|
||||
`,
|
||||
|
||||
@ -468,9 +468,9 @@ describe('DeletePost', () => {
|
||||
DeletePost: {
|
||||
id: 'p4711',
|
||||
deleted: true,
|
||||
content: 'DELETED',
|
||||
contentExcerpt: 'DELETED',
|
||||
image: 'DELETED',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
image: 'UNAVAILABLE',
|
||||
comments: [],
|
||||
},
|
||||
},
|
||||
@ -495,15 +495,15 @@ describe('DeletePost', () => {
|
||||
DeletePost: {
|
||||
id: 'p4711',
|
||||
deleted: true,
|
||||
content: 'DELETED',
|
||||
contentExcerpt: 'DELETED',
|
||||
image: 'DELETED',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
image: 'UNAVAILABLE',
|
||||
comments: [
|
||||
{
|
||||
deleted: true,
|
||||
// Should we black out the comment content in the database, too?
|
||||
content: 'to be deleted comment content',
|
||||
contentExcerpt: 'to be deleted comment content',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@ -102,23 +102,41 @@ export default {
|
||||
const { resource } = params
|
||||
const session = context.driver.session()
|
||||
|
||||
if (resource && resource.length) {
|
||||
await Promise.all(
|
||||
resource.map(async node => {
|
||||
await session.run(
|
||||
`
|
||||
let user
|
||||
try {
|
||||
if (resource && resource.length) {
|
||||
await Promise.all(
|
||||
resource.map(async node => {
|
||||
await session.run(
|
||||
`
|
||||
MATCH (resource:${node})<-[:WROTE]-(author:User {id: $userId})
|
||||
OPTIONAL MATCH (resource)<-[:COMMENTS]-(comment:Comment)
|
||||
SET resource.deleted = true
|
||||
SET resource.content = 'UNAVAILABLE'
|
||||
SET resource.contentExcerpt = 'UNAVAILABLE'
|
||||
SET comment.deleted = true
|
||||
RETURN author`,
|
||||
{
|
||||
userId: context.user.id,
|
||||
},
|
||||
)
|
||||
}),
|
||||
{
|
||||
userId: context.user.id,
|
||||
},
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
const transactionResult = await session.run(
|
||||
`
|
||||
MATCH (user:User {id: $userId})
|
||||
SET user.deleted = true
|
||||
SET user.name = 'UNAVAILABLE'
|
||||
SET user.about = 'UNAVAILABLE'
|
||||
RETURN user`,
|
||||
{ userId: context.user.id },
|
||||
)
|
||||
user = transactionResult.records.map(r => r.get('user').properties)[0]
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
return neo4jgraphql(object, params, context, resolveInfo, false)
|
||||
return user
|
||||
},
|
||||
},
|
||||
User: {
|
||||
|
||||
@ -1,203 +1,242 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import Factory from '../../seed/factories'
|
||||
import { host, login, gql } from '../../jest/helpers'
|
||||
import { neode } from '../../bootstrap/neo4j'
|
||||
import { gql } from '../../jest/helpers'
|
||||
import { neode as getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
let client
|
||||
const factory = Factory()
|
||||
const instance = neode()
|
||||
const categoryIds = ['cat9']
|
||||
let user
|
||||
|
||||
let query
|
||||
let mutate
|
||||
let authenticatedUser
|
||||
let variables
|
||||
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
beforeAll(() => {
|
||||
const { server } = createServer({
|
||||
context: () => {
|
||||
return {
|
||||
driver,
|
||||
neode,
|
||||
user: authenticatedUser,
|
||||
}
|
||||
},
|
||||
})
|
||||
query = createTestClient(server).query
|
||||
mutate = createTestClient(server).mutate
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await factory.cleanDatabase()
|
||||
})
|
||||
|
||||
describe('users', () => {
|
||||
describe('User', () => {
|
||||
describe('query by email address', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', { name: 'Johnny', email: 'any-email-address@example.org' })
|
||||
})
|
||||
|
||||
const query = `query($email: String) { User(email: $email) { name } }`
|
||||
const variables = { email: 'any-email-address@example.org' }
|
||||
beforeEach(() => {
|
||||
client = new GraphQLClient(host)
|
||||
})
|
||||
|
||||
it('is forbidden', async () => {
|
||||
await expect(client.request(query, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
|
||||
describe('as admin', () => {
|
||||
beforeEach(async () => {
|
||||
const userParams = {
|
||||
role: 'admin',
|
||||
email: 'admin@example.org',
|
||||
password: '1234',
|
||||
}
|
||||
const factory = Factory()
|
||||
await factory.create('User', userParams)
|
||||
const headers = await login(userParams)
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('is permitted', async () => {
|
||||
await expect(client.request(query, variables)).resolves.toEqual({
|
||||
User: [{ name: 'Johnny' }],
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('User', () => {
|
||||
describe('query by email address', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', { name: 'Johnny', email: 'any-email-address@example.org' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('UpdateUser', () => {
|
||||
const userParams = {
|
||||
email: 'user@example.org',
|
||||
password: '1234',
|
||||
id: 'u47',
|
||||
name: 'John Doe',
|
||||
}
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: 'John Doughnut',
|
||||
}
|
||||
|
||||
const mutation = `
|
||||
mutation($id: ID!, $name: String) {
|
||||
UpdateUser(id: $id, name: $name) {
|
||||
id
|
||||
const userQuery = gql`
|
||||
query($email: String) {
|
||||
User(email: $email) {
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
const variables = { email: 'any-email-address@example.org' }
|
||||
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', userParams)
|
||||
it('is forbidden', async () => {
|
||||
const { errors } = await query({ query: userQuery, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
})
|
||||
|
||||
describe('as another user', () => {
|
||||
describe('as admin', () => {
|
||||
beforeEach(async () => {
|
||||
const someoneElseParams = {
|
||||
email: 'someone-else@example.org',
|
||||
const userParams = {
|
||||
role: 'admin',
|
||||
email: 'admin@example.org',
|
||||
password: '1234',
|
||||
name: 'James Doe',
|
||||
}
|
||||
|
||||
await factory.create('User', someoneElseParams)
|
||||
const headers = await login(someoneElseParams)
|
||||
client = new GraphQLClient(host, { headers })
|
||||
const admin = await factory.create('User', userParams)
|
||||
authenticatedUser = await admin.toJson()
|
||||
})
|
||||
|
||||
it('is not allowed to change other user accounts', async () => {
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
it('is permitted', async () => {
|
||||
await expect(query({ query: userQuery, variables })).resolves.toMatchObject({
|
||||
data: { User: [{ name: 'Johnny' }] },
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('as the same user', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login(userParams)
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
describe('UpdateUser', () => {
|
||||
const userParams = {
|
||||
email: 'user@example.org',
|
||||
password: '1234',
|
||||
id: 'u47',
|
||||
name: 'John Doe',
|
||||
}
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: 'John Doughnut',
|
||||
}
|
||||
|
||||
it('name within specifications', async () => {
|
||||
const expected = {
|
||||
const updateUserMutation = gql`
|
||||
mutation($id: ID!, $name: String) {
|
||||
UpdateUser(id: $id, name: $name) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await factory.create('User', userParams)
|
||||
})
|
||||
|
||||
describe('as another user', () => {
|
||||
beforeEach(async () => {
|
||||
const someoneElseParams = {
|
||||
email: 'someone-else@example.org',
|
||||
password: '1234',
|
||||
name: 'James Doe',
|
||||
}
|
||||
|
||||
const someoneElse = await factory.create('User', someoneElseParams)
|
||||
authenticatedUser = await someoneElse.toJson()
|
||||
})
|
||||
|
||||
it('is not allowed to change other user accounts', async () => {
|
||||
const { errors } = await mutate({ mutation: updateUserMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('as the same user', () => {
|
||||
beforeEach(async () => {
|
||||
authenticatedUser = await user.toJson()
|
||||
})
|
||||
|
||||
it('name within specifications', async () => {
|
||||
const expected = {
|
||||
data: {
|
||||
UpdateUser: {
|
||||
id: 'u47',
|
||||
name: 'John Doughnut',
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
},
|
||||
}
|
||||
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject(
|
||||
expected,
|
||||
)
|
||||
})
|
||||
|
||||
it('with `null` as name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: null,
|
||||
}
|
||||
const expected = '"name" must be a string'
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||
})
|
||||
it('with `null` as name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: null,
|
||||
}
|
||||
const { errors } = await mutate({ mutation: updateUserMutation, variables })
|
||||
expect(errors[0]).toHaveProperty(
|
||||
'message',
|
||||
'child "name" fails because ["name" contains an invalid value, "name" must be a string]',
|
||||
)
|
||||
})
|
||||
|
||||
it('with too short name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: ' ',
|
||||
it('with too short name', async () => {
|
||||
const variables = {
|
||||
id: 'u47',
|
||||
name: ' ',
|
||||
}
|
||||
const { errors } = await mutate({ mutation: updateUserMutation, variables })
|
||||
expect(errors[0]).toHaveProperty(
|
||||
'message',
|
||||
'child "name" fails because ["name" length must be at least 3 characters long]',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('DeleteUser', () => {
|
||||
const deleteUserMutation = gql`
|
||||
mutation($id: ID!, $resource: [Deletable]) {
|
||||
DeleteUser(id: $id, resource: $resource) {
|
||||
id
|
||||
name
|
||||
about
|
||||
deleted
|
||||
contributions {
|
||||
id
|
||||
content
|
||||
contentExcerpt
|
||||
deleted
|
||||
comments {
|
||||
id
|
||||
content
|
||||
contentExcerpt
|
||||
deleted
|
||||
}
|
||||
}
|
||||
const expected = '"name" length must be at least 3 characters long'
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow(expected)
|
||||
})
|
||||
comments {
|
||||
id
|
||||
content
|
||||
contentExcerpt
|
||||
deleted
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
beforeEach(async () => {
|
||||
variables = { id: ' u343', resource: [] }
|
||||
|
||||
user = await factory.create('User', {
|
||||
name: 'My name should be deleted',
|
||||
about: 'along with my about',
|
||||
id: 'u343',
|
||||
})
|
||||
await factory.create('User', {
|
||||
email: 'friends-account@example.org',
|
||||
password: '1234',
|
||||
id: 'u565',
|
||||
})
|
||||
})
|
||||
|
||||
describe('DeleteUser', () => {
|
||||
let deleteUserVariables
|
||||
const deleteUserMutation = gql`
|
||||
mutation($id: ID!, $resource: [Deletable]) {
|
||||
DeleteUser(id: $id, resource: $resource) {
|
||||
id
|
||||
contributions {
|
||||
id
|
||||
deleted
|
||||
}
|
||||
comments {
|
||||
id
|
||||
deleted
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: deleteUserMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
user = await factory.create('User', {
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
id: 'u343',
|
||||
})
|
||||
await factory.create('User', {
|
||||
email: 'friends-account@example.org',
|
||||
password: '1234',
|
||||
id: 'u565',
|
||||
})
|
||||
deleteUserVariables = { id: 'u343', resource: [] }
|
||||
authenticatedUser = await user.toJson()
|
||||
})
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
client = new GraphQLClient(host)
|
||||
await expect(client.request(deleteUserMutation, deleteUserVariables)).rejects.toThrow(
|
||||
'Not Authorised',
|
||||
)
|
||||
describe("attempting to delete another user's account", () => {
|
||||
beforeEach(() => {
|
||||
variables = { ...variables, id: 'u565' }
|
||||
})
|
||||
|
||||
it('throws an authorization error', async () => {
|
||||
const { errors } = await mutate({ mutation: deleteUserMutation, variables })
|
||||
expect(errors[0]).toHaveProperty('message', '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 my own account', () => {
|
||||
beforeEach(() => {
|
||||
variables = { ...variables, id: 'u343' }
|
||||
})
|
||||
|
||||
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', () => {
|
||||
let expectedResponse
|
||||
describe('given posts and comments', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.authenticateAs({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
await instance.create('Category', {
|
||||
await factory.create('Category', {
|
||||
id: 'cat9',
|
||||
name: 'Democracy & Politics',
|
||||
icon: 'university',
|
||||
@ -211,64 +250,192 @@ describe('users', () => {
|
||||
await factory.create('Comment', {
|
||||
author: user,
|
||||
id: 'c155',
|
||||
postId: 'p139',
|
||||
content: 'Comment by user u343',
|
||||
})
|
||||
expectedResponse = {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
contributions: [{ id: 'p139', deleted: false }],
|
||||
comments: [{ id: 'c155', deleted: false }],
|
||||
await factory.create('Comment', {
|
||||
postId: 'p139',
|
||||
id: 'c156',
|
||||
content: "A comment by someone else on user u343's post",
|
||||
})
|
||||
})
|
||||
|
||||
it("deletes my account, but doesn't delete posts or comments by default", async () => {
|
||||
const expectedResponse = {
|
||||
data: {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
name: 'UNAVAILABLE',
|
||||
about: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
contributions: [
|
||||
{
|
||||
id: 'p139',
|
||||
content: 'Post by user u343',
|
||||
contentExcerpt: 'Post by user u343',
|
||||
deleted: false,
|
||||
comments: [
|
||||
{
|
||||
id: 'c156',
|
||||
content: "A comment by someone else on user u343's post",
|
||||
contentExcerpt: "A comment by someone else on user u343's post",
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
id: 'c155',
|
||||
content: 'Comment by user u343',
|
||||
contentExcerpt: 'Comment by user u343',
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
it("deletes my account, but doesn't delete posts or comments by default", async () => {
|
||||
await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
|
||||
await expect(mutate({ mutation: deleteUserMutation, variables })).resolves.toMatchObject(
|
||||
expectedResponse,
|
||||
)
|
||||
})
|
||||
|
||||
describe("deletes a user's", () => {
|
||||
it('posts on request', async () => {
|
||||
deleteUserVariables = { id: 'u343', resource: ['Post'] }
|
||||
expectedResponse = {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
contributions: [{ id: 'p139', deleted: true }],
|
||||
comments: [{ id: 'c155', deleted: false }],
|
||||
},
|
||||
}
|
||||
await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
|
||||
expectedResponse,
|
||||
)
|
||||
describe('deletion of all post requested', () => {
|
||||
beforeEach(() => {
|
||||
variables = { ...variables, resource: ['Post'] }
|
||||
})
|
||||
|
||||
it('comments on request', async () => {
|
||||
deleteUserVariables = { id: 'u343', resource: ['Comment'] }
|
||||
expectedResponse = {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
contributions: [{ id: 'p139', deleted: false }],
|
||||
comments: [{ id: 'c155', deleted: true }],
|
||||
},
|
||||
}
|
||||
await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
|
||||
expectedResponse,
|
||||
)
|
||||
describe("marks user's posts as deleted", () => {
|
||||
it('posts on request', async () => {
|
||||
const expectedResponse = {
|
||||
data: {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
name: 'UNAVAILABLE',
|
||||
about: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
contributions: [
|
||||
{
|
||||
id: 'p139',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
comments: [
|
||||
{
|
||||
id: 'c156',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
id: 'c155',
|
||||
content: 'Comment by user u343',
|
||||
contentExcerpt: 'Comment by user u343',
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
await expect(
|
||||
mutate({ mutation: deleteUserMutation, variables }),
|
||||
).resolves.toMatchObject(expectedResponse)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('deletion of all comments requested', () => {
|
||||
beforeEach(() => {
|
||||
variables = { ...variables, resource: ['Comment'] }
|
||||
})
|
||||
|
||||
it('posts and comments on request', async () => {
|
||||
deleteUserVariables = { id: 'u343', resource: ['Post', 'Comment'] }
|
||||
expectedResponse = {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
contributions: [{ id: 'p139', deleted: true }],
|
||||
comments: [{ id: 'c155', deleted: true }],
|
||||
it('marks comments as deleted', async () => {
|
||||
const expectedResponse = {
|
||||
data: {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
name: 'UNAVAILABLE',
|
||||
about: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
contributions: [
|
||||
{
|
||||
id: 'p139',
|
||||
content: 'Post by user u343',
|
||||
contentExcerpt: 'Post by user u343',
|
||||
deleted: false,
|
||||
comments: [
|
||||
{
|
||||
id: 'c156',
|
||||
content: "A comment by someone else on user u343's post",
|
||||
contentExcerpt: "A comment by someone else on user u343's post",
|
||||
deleted: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
id: 'c155',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
await expect(client.request(deleteUserMutation, deleteUserVariables)).resolves.toEqual(
|
||||
expectedResponse,
|
||||
)
|
||||
await expect(
|
||||
mutate({ mutation: deleteUserMutation, variables }),
|
||||
).resolves.toMatchObject(expectedResponse)
|
||||
})
|
||||
})
|
||||
|
||||
describe('deletion of all post and comments requested', () => {
|
||||
beforeEach(() => {
|
||||
variables = { ...variables, resource: ['Post', 'Comment'] }
|
||||
})
|
||||
|
||||
it('marks posts and comments as deleted', async () => {
|
||||
const expectedResponse = {
|
||||
data: {
|
||||
DeleteUser: {
|
||||
id: 'u343',
|
||||
name: 'UNAVAILABLE',
|
||||
about: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
contributions: [
|
||||
{
|
||||
id: 'p139',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
comments: [
|
||||
{
|
||||
id: 'c156',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
comments: [
|
||||
{
|
||||
id: 'c155',
|
||||
content: 'UNAVAILABLE',
|
||||
contentExcerpt: 'UNAVAILABLE',
|
||||
deleted: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
await expect(
|
||||
mutate({ mutation: deleteUserMutation, variables }),
|
||||
).resolves.toMatchObject(expectedResponse)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,7 +3,7 @@ import uuid from 'uuid/v4'
|
||||
|
||||
export default function create() {
|
||||
return {
|
||||
factory: async ({ args, neodeInstance }) => {
|
||||
factory: async ({ args, neodeInstance, factoryInstance }) => {
|
||||
const defaults = {
|
||||
id: uuid(),
|
||||
content: [faker.lorem.sentence(), faker.lorem.sentence()].join('. '),
|
||||
@ -12,11 +12,22 @@ export default function create() {
|
||||
...defaults,
|
||||
...args,
|
||||
}
|
||||
const { postId } = args
|
||||
if (!postId) throw new Error('PostId is missing!')
|
||||
const post = await neodeInstance.find('Post', postId)
|
||||
args.contentExcerpt = args.contentExcerpt || args.content
|
||||
|
||||
let { post, postId } = args
|
||||
delete args.post
|
||||
delete args.postId
|
||||
const author = args.author || (await neodeInstance.create('User', args))
|
||||
if (post && post) throw new Error('You provided both post and postId')
|
||||
if (postId) post = await neodeInstance.find('Post', postId)
|
||||
post = post || (await factoryInstance.create('Post'))
|
||||
|
||||
let { author, authorId } = args
|
||||
delete args.author
|
||||
delete args.authorId
|
||||
if (author && authorId) throw new Error('You provided both author and authorId')
|
||||
if (authorId) author = await neodeInstance.find('User', authorId)
|
||||
author = author || (await factoryInstance.create('User'))
|
||||
|
||||
delete args.author
|
||||
const comment = await neodeInstance.create('Comment', args)
|
||||
await comment.relateTo(post, 'post')
|
||||
|
||||
@ -27,13 +27,13 @@ export default function create() {
|
||||
args.slug = args.slug || slugify(args.title, { lower: true })
|
||||
args.contentExcerpt = args.contentExcerpt || args.content
|
||||
|
||||
const { categoryIds } = args
|
||||
if (!categoryIds.length) throw new Error('CategoryIds are empty!')
|
||||
const categories = await Promise.all(
|
||||
categoryIds.map(c => {
|
||||
return neodeInstance.find('Category', c)
|
||||
}),
|
||||
)
|
||||
let { categories, categoryIds } = args
|
||||
delete args.categories
|
||||
delete args.categoryIds
|
||||
if (categories && categoryIds) throw new Error('You provided both category and categoryIds')
|
||||
if (categoryIds)
|
||||
categories = await Promise.all(categoryIds.map(id => neodeInstance.find('Category', id)))
|
||||
categories = categories || (await Promise.all([factoryInstance.create('Category')]))
|
||||
|
||||
const { tagIds = [] } = args
|
||||
delete args.tags
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user