Merge branch 'master' into 212-change-password

This commit is contained in:
Lala Sabathil 2019-03-11 02:19:02 +01:00 committed by GitHub
commit 2b6f7270b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 324 additions and 182 deletions

View File

@ -34,7 +34,7 @@ describe('authorization', () => {
return graphQLClient.request('{User(name: "Owner") { email } }')
}
describe('not logged in', async () => {
describe('not logged in', () => {
it('rejects', async () => {
await expect(action()).rejects.toThrow('Not Authorised!')
})

View File

@ -22,10 +22,9 @@ beforeEach(async () => {
await moderatorFactory.authenticateAs({ email: 'moderator@example.org', password: '1234' })
const disableMutation = `
mutation {
disable(resource: {
disable(
id: "p2"
type: contribution
})
)
}
`
await moderatorFactory.mutate(disableMutation)

View File

@ -1,30 +1,41 @@
export default {
Mutation: {
disable: async (object, params, { user, driver }) => {
const { resource: { id } } = params
const { id } = params
const { id: userId } = user
const cypher = `
MATCH (u:User {id: $userId})
MATCH (r {id: $id})
SET r.disabled = true
MERGE (r)<-[:DISABLED]-(u)
MATCH (resource {id: $id})
WHERE resource:User OR resource:Comment OR resource:Post
SET resource.disabled = true
MERGE (resource)<-[:DISABLED]-(u)
RETURN resource {.id}
`
const session = driver.session()
const res = await session.run(cypher, { id, userId })
session.close()
return Boolean(res)
const [resource] = res.records.map((record) => {
return record.get('resource')
})
if (!resource) return null
return resource.id
},
enable: async (object, params, { user, driver }) => {
const { resource: { id } } = params
const { id } = params
const cypher = `
MATCH (r {id: $id})<-[d:DISABLED]-()
SET r.disabled = false
MATCH (resource {id: $id})<-[d:DISABLED]-()
SET resource.disabled = false
DELETE d
RETURN resource {.id}
`
const session = driver.session()
const res = await session.run(cypher, { id })
session.close()
return Boolean(res)
const [resource] = res.records.map((record) => {
return record.get('resource')
})
if (!resource) return null
return resource.id
}
}
}

View File

@ -14,30 +14,28 @@ const setupAuthenticateClient = (params) => {
return authenticateClient
}
let setup
const runSetup = async () => {
await setup.createResource()
await setup.authenticateClient()
}
let createResource
let authenticateClient
beforeEach(() => {
setup = {
createResource: () => {
},
authenticateClient: () => {
client = new GraphQLClient(host)
}
createResource = () => {}
authenticateClient = () => {
client = new GraphQLClient(host)
}
})
const setup = async () => {
await createResource()
await authenticateClient()
}
afterEach(async () => {
await factory.cleanDatabase()
})
describe('disable', () => {
const mutation = `
mutation($id: ID!, $type: ResourceEnum!) {
disable(resource: { id: $id, type: $type })
mutation($id: ID!) {
disable(id: $id)
}
`
let variables
@ -45,8 +43,7 @@ describe('disable', () => {
beforeEach(() => {
// our defaul set of variables
variables = {
id: 'blabla',
type: 'contribution'
id: 'blabla'
}
})
@ -55,26 +52,26 @@ describe('disable', () => {
}
it('throws authorization error', async () => {
await runSetup()
await setup()
await expect(action()).rejects.toThrow('Not Authorised')
})
describe('authenticated', () => {
beforeEach(() => {
setup.authenticateClient = setupAuthenticateClient({
authenticateClient = setupAuthenticateClient({
email: 'user@example.org',
password: '1234'
})
})
it('throws authorization error', async () => {
await runSetup()
await setup()
await expect(action()).rejects.toThrow('Not Authorised')
})
describe('as moderator', () => {
beforeEach(() => {
setup.authenticateClient = setupAuthenticateClient({
authenticateClient = setupAuthenticateClient({
id: 'u7',
email: 'moderator@example.org',
password: '1234',
@ -82,14 +79,32 @@ describe('disable', () => {
})
})
describe('on something that is not a (Comment|Post|User) ', () => {
beforeEach(async () => {
variables = {
id: 't23'
}
createResource = () => {
return Promise.all([
factory.create('Tag', { id: 't23' })
])
}
})
it('returns null', async () => {
const expected = { disable: null }
await setup()
await expect(action()).resolves.toEqual(expected)
})
})
describe('on a comment', () => {
beforeEach(async () => {
variables = {
id: 'c47',
type: 'comment'
id: 'c47'
}
setup.createResource = async () => {
createResource = async () => {
await factory.create('User', { id: 'u45', email: 'commenter@example.org', password: '1234' })
await factory.authenticateAs({ email: 'commenter@example.org', password: '1234' })
await Promise.all([
@ -103,9 +118,9 @@ describe('disable', () => {
}
})
it('returns true', async () => {
const expected = { disable: true }
await runSetup()
it('returns disabled resource id', async () => {
const expected = { disable: 'c47' }
await setup()
await expect(action()).resolves.toEqual(expected)
})
@ -113,7 +128,7 @@ describe('disable', () => {
const before = { Comment: [{ id: 'c47', disabledBy: null }] }
const expected = { Comment: [{ id: 'c47', disabledBy: { id: 'u7' } }] }
await runSetup()
await setup()
await expect(client.request(
'{ Comment { id, disabledBy { id } } }'
)).resolves.toEqual(before)
@ -127,7 +142,7 @@ describe('disable', () => {
const before = { Comment: [ { id: 'c47', disabled: false } ] }
const expected = { Comment: [ { id: 'c47', disabled: true } ] }
await runSetup()
await setup()
await expect(client.request(
'{ Comment { id disabled } }'
)).resolves.toEqual(before)
@ -141,11 +156,10 @@ describe('disable', () => {
describe('on a post', () => {
beforeEach(async () => {
variables = {
id: 'p9',
type: 'contribution'
id: 'p9'
}
setup.createResource = async () => {
createResource = async () => {
await factory.create('User', { email: 'author@example.org', password: '1234' })
await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
await factory.create('Post', {
@ -154,9 +168,9 @@ describe('disable', () => {
}
})
it('returns true', async () => {
const expected = { disable: true }
await runSetup()
it('returns disabled resource id', async () => {
const expected = { disable: 'p9' }
await setup()
await expect(action()).resolves.toEqual(expected)
})
@ -164,7 +178,7 @@ describe('disable', () => {
const before = { Post: [{ id: 'p9', disabledBy: null }] }
const expected = { Post: [{ id: 'p9', disabledBy: { id: 'u7' } }] }
await runSetup()
await setup()
await expect(client.request(
'{ Post { id, disabledBy { id } } }'
)).resolves.toEqual(before)
@ -178,7 +192,7 @@ describe('disable', () => {
const before = { Post: [ { id: 'p9', disabled: false } ] }
const expected = { Post: [ { id: 'p9', disabled: true } ] }
await runSetup()
await setup()
await expect(client.request(
'{ Post { id disabled } }'
)).resolves.toEqual(before)
@ -194,8 +208,8 @@ describe('disable', () => {
describe('enable', () => {
const mutation = `
mutation($id: ID!, $type: ResourceEnum!) {
enable(resource: { id: $id, type: $type })
mutation($id: ID!) {
enable(id: $id)
}
`
let variables
@ -207,46 +221,64 @@ describe('enable', () => {
beforeEach(() => {
// our defaul set of variables
variables = {
id: 'blabla',
type: 'contribution'
id: 'blabla'
}
})
it('throws authorization error', async () => {
await runSetup()
await setup()
await expect(action()).rejects.toThrow('Not Authorised')
})
describe('authenticated', () => {
beforeEach(() => {
setup.authenticateClient = setupAuthenticateClient({
authenticateClient = setupAuthenticateClient({
email: 'user@example.org',
password: '1234'
})
})
it('throws authorization error', async () => {
await runSetup()
await setup()
await expect(action()).rejects.toThrow('Not Authorised')
})
describe('as moderator', () => {
beforeEach(async () => {
setup.authenticateClient = setupAuthenticateClient({
authenticateClient = setupAuthenticateClient({
role: 'moderator',
email: 'someUser@example.org',
password: '1234'
})
})
describe('on something that is not a (Comment|Post|User) ', () => {
beforeEach(async () => {
variables = {
id: 't23'
}
createResource = () => {
// we cannot create a :DISABLED relationship here
return Promise.all([
factory.create('Tag', { id: 't23' })
])
}
})
it('returns null', async () => {
const expected = { enable: null }
await setup()
await expect(action()).resolves.toEqual(expected)
})
})
describe('on a comment', () => {
beforeEach(async () => {
variables = {
id: 'c456',
type: 'comment'
id: 'c456'
}
setup.createResource = async () => {
createResource = async () => {
await factory.create('User', { id: 'u123', email: 'author@example.org', password: '1234' })
await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
await Promise.all([
@ -260,19 +292,16 @@ describe('enable', () => {
const disableMutation = `
mutation {
disable(resource: {
id: "c456"
type: comment
})
disable(id: "c456")
}
`
await factory.mutate(disableMutation) // that's we want to delete
}
})
it('returns true', async () => {
const expected = { enable: true }
await runSetup()
it('returns disabled resource id', async () => {
const expected = { enable: 'c456' }
await setup()
await expect(action()).resolves.toEqual(expected)
})
@ -280,7 +309,7 @@ describe('enable', () => {
const before = { Comment: [{ id: 'c456', disabledBy: { id: 'u123' } }] }
const expected = { Comment: [{ id: 'c456', disabledBy: null }] }
await runSetup()
await setup()
await expect(client.request(
'{ Comment(disabled: true) { id, disabledBy { id } } }'
)).resolves.toEqual(before)
@ -294,7 +323,7 @@ describe('enable', () => {
const before = { Comment: [ { id: 'c456', disabled: true } ] }
const expected = { Comment: [ { id: 'c456', disabled: false } ] }
await runSetup()
await setup()
await expect(client.request(
'{ Comment(disabled: true) { id disabled } }'
)).resolves.toEqual(before)
@ -308,11 +337,10 @@ describe('enable', () => {
describe('on a post', () => {
beforeEach(async () => {
variables = {
id: 'p9',
type: 'contribution'
id: 'p9'
}
setup.createResource = async () => {
createResource = async () => {
await factory.create('User', { id: 'u123', email: 'author@example.org', password: '1234' })
await factory.authenticateAs({ email: 'author@example.org', password: '1234' })
await factory.create('Post', {
@ -321,19 +349,16 @@ describe('enable', () => {
const disableMutation = `
mutation {
disable(resource: {
id: "p9"
type: contribution
})
disable(id: "p9")
}
`
await factory.mutate(disableMutation) // that's we want to delete
}
})
it('returns true', async () => {
const expected = { enable: true }
await runSetup()
it('returns disabled resource id', async () => {
const expected = { enable: 'p9' }
await setup()
await expect(action()).resolves.toEqual(expected)
})
@ -341,7 +366,7 @@ describe('enable', () => {
const before = { Post: [{ id: 'p9', disabledBy: { id: 'u123' } }] }
const expected = { Post: [{ id: 'p9', disabledBy: null }] }
await runSetup()
await setup()
await expect(client.request(
'{ Post(disabled: true) { id, disabledBy { id } } }'
)).resolves.toEqual(before)
@ -355,7 +380,7 @@ describe('enable', () => {
const before = { Post: [ { id: 'p9', disabled: true } ] }
const expected = { Post: [ { id: 'p9', disabled: false } ] }
await runSetup()
await setup()
await expect(client.request(
'{ Post(disabled: true) { id disabled } }'
)).resolves.toEqual(before)

View File

@ -2,50 +2,62 @@ import uuid from 'uuid/v4'
export default {
Mutation: {
report: async (parent, { resource, description }, { driver, req, user }, resolveInfo) => {
const contextId = uuid()
report: async (parent, { id, description }, { driver, req, user }, resolveInfo) => {
const reportId = uuid()
const session = driver.session()
const data = {
id: contextId,
type: resource.type,
const reportData = {
id: reportId,
createdAt: (new Date()).toISOString(),
description: resource.description
}
await session.run(
'CREATE (r:Report $report) ' +
'RETURN r.id, r.type, r.description', {
report: data
}
)
let contentType
switch (resource.type) {
case 'post':
case 'contribution':
contentType = 'Post'
break
case 'comment':
contentType = 'Comment'
break
case 'user':
contentType = 'User'
break
description: description
}
await session.run(
`MATCH (author:User {id: $userId}), (context:${contentType} {id: $resourceId}), (report:Report {id: $contextId}) ` +
'MERGE (report)<-[:REPORTED]-(author) ' +
'MERGE (context)<-[:REPORTED]-(report) ' +
'RETURN context', {
resourceId: resource.id,
userId: user.id,
contextId: contextId
}
const res = await session.run(`
MATCH (submitter:User {id: $userId})
MATCH (resource {id: $resourceId})
WHERE resource:User OR resource:Comment OR resource:Post
CREATE (report:Report $reportData)
MERGE (resource)<-[:REPORTED]-(report)
MERGE (report)<-[:REPORTED]-(submitter)
RETURN report, submitter, resource, labels(resource)[0] as type
`, {
resourceId: id,
userId: user.id,
reportData
}
)
session.close()
// TODO: output Report compatible object
return data
const [dbResponse] = res.records.map(r => {
return {
report: r.get('report'),
submitter: r.get('submitter'),
resource: r.get('resource'),
type: r.get('type')
}
})
if (!dbResponse) return null
const { report, submitter, resource, type } = dbResponse
let response = {
...report.properties,
post: null,
comment: null,
user: null,
submitter: submitter.properties,
type
}
switch (type) {
case 'Post':
response.post = resource.properties
break
case 'Comment':
response.comment = resource.properties
break
case 'User':
response.user = resource.properties
break
}
return response
}
}
}

View File

@ -5,8 +5,17 @@ import { host, login } from '../jest/helpers'
const factory = Factory()
describe('report', () => {
let mutation
let headers
let returnedObject
let variables
beforeEach(async () => {
returnedObject = '{ description }'
variables = { id: 'whatever' }
headers = {}
await factory.create('User', {
id: 'u1',
email: 'test@example.org',
password: '1234'
})
@ -22,45 +31,132 @@ describe('report', () => {
await factory.cleanDatabase()
})
describe('unauthenticated', () => {
let client
it('throws authorization error', async () => {
client = new GraphQLClient(host)
await expect(
client.request(`mutation {
let client
const action = () => {
mutation = `
mutation($id: ID!) {
report(
description: "I don't like this user",
resource: {
id: "u2",
type: user
}
) { id, createdAt }
}`)
).rejects.toThrow('Not Authorised')
id: $id,
description: "Violates code of conduct"
) ${returnedObject}
}
`
client = new GraphQLClient(host, { headers })
return client.request(mutation, variables)
}
describe('unauthenticated', () => {
it('throws authorization error', async () => {
await expect(action()).rejects.toThrow('Not Authorised')
})
describe('authenticated', () => {
let headers
let response
beforeEach(async () => {
headers = await login({ email: 'test@example.org', password: '1234' })
client = new GraphQLClient(host, { headers })
response = await client.request(`mutation {
report(
description: "I don't like this user",
resource: {
id: "u2",
type: user
}
) { id, createdAt }
}`,
{ headers }
)
})
it('creates a report', () => {
let { id, createdAt } = response.report
expect(response).toEqual({
report: { id, createdAt }
describe('invalid resource id', () => {
it('returns null', async () => {
await expect(action()).resolves.toEqual({
report: null
})
})
})
describe('valid resource id', () => {
beforeEach(async () => {
variables = { id: 'u2' }
})
it('creates a report', async () => {
await expect(action()).resolves.toEqual({
report: { description: 'Violates code of conduct' }
})
})
it('returns the submitter', async () => {
returnedObject = '{ submitter { email } }'
await expect(action()).resolves.toEqual({
report: { submitter: { email: 'test@example.org' } }
})
})
describe('reported resource is a user', () => {
it('returns type "User"', async () => {
returnedObject = '{ type }'
await expect(action()).resolves.toEqual({
report: { type: 'User' }
})
})
it('returns resource in user attribute', async () => {
returnedObject = '{ user { name } }'
await expect(action()).resolves.toEqual({
report: { user: { name: 'abusive-user' } }
})
})
})
describe('reported resource is a post', () => {
beforeEach(async () => {
await factory.authenticateAs({ email: 'test@example.org', password: '1234' })
await factory.create('Post', { id: 'p23', title: 'Matt and Robert having a pair-programming' })
variables = { id: 'p23' }
})
it('returns type "Post"', async () => {
returnedObject = '{ type }'
await expect(action()).resolves.toEqual({
report: { type: 'Post' }
})
})
it('returns resource in post attribute', async () => {
returnedObject = '{ post { title } }'
await expect(action()).resolves.toEqual({
report: { post: { title: 'Matt and Robert having a pair-programming' } }
})
})
it('returns null in user attribute', async () => {
returnedObject = '{ user { name } }'
await expect(action()).resolves.toEqual({
report: { user: null }
})
})
})
describe('reported resource is a comment', () => {
beforeEach(async () => {
await factory.authenticateAs({ email: 'test@example.org', password: '1234' })
await factory.create('Comment', { id: 'c34', content: 'Robert getting tired.' })
variables = { id: 'c34' }
})
it('returns type "Comment"', async () => {
returnedObject = '{ type }'
await expect(action()).resolves.toEqual({
report: { type: 'Comment' }
})
})
it('returns resource in comment attribute', async () => {
returnedObject = '{ comment { content } }'
await expect(action()).resolves.toEqual({
report: { comment: { content: 'Robert getting tired.' } }
})
})
})
describe('reported resource is a tag', () => {
beforeEach(async () => {
await factory.create('Tag', { id: 't23' })
variables = { id: 't23' }
})
it('returns null', async () => {
await expect(action()).resolves.toEqual({ report: null })
})
})
})
})

View File

@ -11,6 +11,8 @@ type Mutation {
signup(email: String!, password: String!): Boolean!
changePassword(oldPassword:String!, newPassword: String!): String!
report(resource: Resource!, description: String): Report
disable(id: ID!): ID
enable(id: ID!): ID
"Shout the given Type and ID"
shout(id: ID!, type: ShoutTypeEnum): Boolean! @cypher(statement: """
MATCH (n {id: $id})<-[:WROTE]-(wu:User), (u:User {id: $cypherParams.currentUserId})
@ -40,8 +42,6 @@ type Mutation {
DELETE r
RETURN COUNT(r) > 0
""")
disable(resource: Resource!): Boolean!
enable(resource: Resource!): Boolean!
}
type Statistics {
@ -60,17 +60,6 @@ scalar Date
scalar Time
scalar DateTime
input Resource {
id: ID!,
type: ResourceEnum!
}
enum ResourceEnum {
contribution
comment
user
}
enum VisibilityEnum {
public
friends
@ -216,12 +205,12 @@ type Comment {
type Report {
id: ID!
reporter: User @relation(name: "REPORTED", direction: "IN")
submitter: User @relation(name: "REPORTED", direction: "IN")
description: String
type: ResourceEnum!
type: String! @cypher(statement: "MATCH (resource)<-[:REPORTED]-(this) RETURN labels(resource)[0]")
createdAt: String
comment: Comment @relation(name: "REPORTED", direction: "OUT")
contribution: Post @relation(name: "REPORTED", direction: "OUT")
post: Post @relation(name: "REPORTED", direction: "OUT")
user: User @relation(name: "REPORTED", direction: "OUT")
}

View File

@ -3,17 +3,14 @@ import faker from 'faker'
export default function create (params) {
const {
description = faker.lorem.sentence(),
resource: { id: resourceId, type }
id
} = params
return `
mutation {
report(
description: "${description}",
resource: {
id: "${resourceId}",
type: ${type}
}
id: "${id}",
) {
id,
createdAt

View File

@ -107,14 +107,7 @@ import Factory from './factories'
asTick.create('Post', { id: 'p15' })
])
const disableMutation = `
mutation {
disable(resource: {
id: "p11"
type: contribution
})
}
`
const disableMutation = 'mutation { disable( id: "p11") }'
await asModerator.mutate(disableMutation)
await Promise.all([
@ -153,6 +146,26 @@ import Factory from './factories'
f.relate('Post', 'Tags', { from: 'p15', to: 't3' })
])
await Promise.all([
asAdmin
.shout({ id: 'p2', type: 'Post' }),
asAdmin
.shout({ id: 'p6', type: 'Post' }),
asModerator
.shout({ id: 'p0', type: 'Post' }),
asModerator
.shout({ id: 'p6', type: 'Post' }),
asUser
.shout({ id: 'p6', type: 'Post' }),
asUser
.shout({ id: 'p7', type: 'Post' }),
asTick
.shout({ id: 'p8', type: 'Post' }),
asTick
.shout({ id: 'p9', type: 'Post' }),
asTrack
.shout({ id: 'p10', type: 'Post' })
])
await Promise.all([
asAdmin
.shout({ id: 'p2', type: 'Post' }),
@ -202,9 +215,9 @@ import Factory from './factories'
])
await Promise.all([
asTick.create('Report', { description: 'I don\'t like this comment', resource: { id: 'c1', type: 'comment' } }),
asTrick.create('Report', { description: 'I don\'t like this post', resource: { id: 'p1', type: 'contribution' } }),
asTrack.create('Report', { description: 'I don\'t like this user', resource: { id: 'u1', type: 'user' } })
asTick.create('Report', { description: 'I don\'t like this comment', id: 'c1' }),
asTrick.create('Report', { description: 'I don\'t like this post', id: 'p1' }),
asTrack.create('Report', { description: 'I don\'t like this user', id: 'u1' })
])
await Promise.all([