Refactor GQL and tests, first approach

This commit is contained in:
Wolfgang Huß 2020-02-17 12:10:16 +01:00
parent ba3cfa8e04
commit f380915b2c
6 changed files with 242 additions and 96 deletions

View File

@ -9,14 +9,6 @@ const driver = getDriver()
let query, authenticatedUser, owner, anotherRegularUser, administrator, variables, moderator
const userQuery = gql`
query($name: String) {
User(name: $name) {
email
}
}
`
describe('authorization', () => {
beforeAll(async () => {
await cleanDatabase()
@ -30,7 +22,11 @@ describe('authorization', () => {
query = createTestClient(server).query
})
describe('given two existing users', () => {
afterEach(async () => {
await cleanDatabase()
})
describe('given an owner, an other user, an admin, a moderator', () => {
beforeEach(async () => {
;[owner, anotherRegularUser, administrator, moderator] = await Promise.all([
Factory.build(
@ -79,15 +75,20 @@ describe('authorization', () => {
variables = {}
})
afterEach(async () => {
await cleanDatabase()
})
describe('access email address', () => {
const userQuery = gql`
query($name: String) {
User(name: $name) {
email
}
}
`
describe('unauthenticated', () => {
beforeEach(() => {
authenticatedUser = null
})
it("throws an error and does not expose the owner's email address", async () => {
await expect(
query({ query: userQuery, variables: { name: 'Owner' } }),
@ -143,7 +144,7 @@ describe('authorization', () => {
})
})
describe('administrator', () => {
describe('as an administrator', () => {
beforeEach(async () => {
authenticatedUser = await administrator.toJson()
})
@ -158,5 +159,97 @@ describe('authorization', () => {
})
})
})
// Wolle describe('access reports protected propertied', () => {
// const reportsQuery = gql `
// query {
// reports {
// id
// createdAt
// updatedAt
// rule
// disable
// closed
// filed
// reviewed
// resource
// }
// }
// `
// describe('unauthenticated', () => {
// beforeEach(() => {
// authenticatedUser = null
// })
// it("throws an error and does not expose the owner's email address", async () => {
// await expect(
// query({ query: reportsQuery, variables: { name: 'Owner' } }),
// ).resolves.toMatchObject({
// errors: [{ message: 'Not Authorised!' }],
// data: { User: [null] },
// })
// })
// })
// describe('authenticated', () => {
// describe('as the owner', () => {
// beforeEach(async () => {
// authenticatedUser = await owner.toJson()
// })
// it("exposes the owner's email address", async () => {
// variables = { name: 'Owner' }
// await expect(query({ query: reportsQuery, variables })).resolves.toMatchObject({
// data: { User: [{ email: 'owner@example.org' }] },
// errors: undefined,
// })
// })
// })
// describe('as another regular user', () => {
// beforeEach(async () => {
// authenticatedUser = await anotherRegularUser.toJson()
// })
// it("throws an error and does not expose the owner's email address", async () => {
// await expect(
// query({ query: reportsQuery, variables: { name: 'Owner' } }),
// ).resolves.toMatchObject({
// errors: [{ message: 'Not Authorised!' }],
// data: { User: [null] },
// })
// })
// })
// describe('as a moderator', () => {
// beforeEach(async () => {
// authenticatedUser = await moderator.toJson()
// })
// it("throws an error and does not expose the owner's email address", async () => {
// await expect(
// query({ query: reportsQuery, variables: { name: 'Owner' } }),
// ).resolves.toMatchObject({
// errors: [{ message: 'Not Authorised!' }],
// data: { User: [null] },
// })
// })
// })
// describe('as an administrator', () => {
// beforeEach(async () => {
// authenticatedUser = await administrator.toJson()
// })
// it("exposes the owner's email address", async () => {
// variables = { name: 'Owner' }
// await expect(query({ query: reportsQuery, variables })).resolves.toMatchObject({
// data: { User: [{ email: 'owner@example.org' }] },
// errors: undefined,
// })
// })
// })
// })
// })
})
})

View File

@ -1,14 +1,14 @@
import log from './helpers/databaseLogger'
const transformReturnType = record => {
return {
...record.get('report').properties,
resource: {
__typename: record.get('type'),
...record.get('resource').properties,
},
}
}
// Wolle const transformReturnType = record => {
// return {
// ...record.get('report').properties,
// resource: {
// __typename: record.get('type'),
// ...record.get('resource').properties,
// },
// }
// }
export default {
Mutation: {
@ -27,7 +27,8 @@ export default {
WITH submitter, resource, report
CREATE (report)<-[filed:FILED {createdAt: $createdAt, reasonCategory: $reasonCategory, reasonDescription: $reasonDescription}]-(submitter)
RETURN report, resource, labels(resource)[0] AS type
WITH report, resource {.*, __typename: labels(resource)[0]} AS finalResource
RETURN {reportId: report.id, resource: properties(finalResource)} AS filedReport
`,
{
resourceId,
@ -38,13 +39,18 @@ export default {
},
)
log(reportTransactionResponse)
return reportTransactionResponse.records.map(transformReturnType)
// Wolle return reportTransactionResponse.records.map(transformReturnType)
return reportTransactionResponse.records.map(record =>
record.get('filedReport'),
)
})
try {
const [createdRelationshipWithNestedAttributes] = await reportWriteTxResultPromise
console.log('createdRelationshipWithNestedAttributes: ', createdRelationshipWithNestedAttributes)
if (!createdRelationshipWithNestedAttributes) return null
return createdRelationshipWithNestedAttributes
} finally {
console.log('session.close !!!')
session.close()
}
},

View File

@ -10,22 +10,54 @@ const driver = getDriver()
describe('file a report on a resource', () => {
let authenticatedUser, currentUser, mutate, query, moderator, abusiveUser, otherReportingUser
const categoryIds = ['cat9']
const reportMutation = gql`
// Wolle const reportMutation = gql`
// mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
// fileReport(
// resourceId: $resourceId
// reasonCategory: $reasonCategory
// reasonDescription: $reasonDescription
// ) {
// id
// createdAt
// updatedAt
// closed
// rule
// resource {
// __typename
// ... on User {
// name
// }
// ... on Post {
// title
// }
// ... on Comment {
// content
// }
// }
// filed {
// submitter {
// id
// }
// createdAt
// reasonCategory
// reasonDescription
// }
// }
// }
// `
const fileReportMutation = gql`
mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
fileReport(
resourceId: $resourceId
reasonCategory: $reasonCategory
reasonDescription: $reasonDescription
) {
id
createdAt
updatedAt
closed
rule
reportId
resource {
__typename
... on User {
name
# Wolle filedUnclosedReportByCurrentUser
}
... on Post {
title
@ -34,6 +66,35 @@ describe('file a report on a resource', () => {
content
}
}
}
}
`
const variables = {
resourceId: 'invalid',
reasonCategory: 'other',
reasonDescription: 'Violates code of conduct !!!',
}
const reportsQuery = gql`
query {
reports(orderBy: createdAt_desc) {
id
createdAt
updatedAt
closed
resource {
__typename
... on User {
id
# Wolle FOLLOWSfiledUnclosedReportByCurrentUser
followedByCurrentUser
}
... on Post {
id
}
... on Comment {
id
}
}
filed {
submitter {
id
@ -45,11 +106,6 @@ describe('file a report on a resource', () => {
}
}
`
const variables = {
resourceId: 'whatever',
reasonCategory: 'other',
reasonDescription: 'Violates code of conduct !!!',
}
beforeAll(async () => {
await cleanDatabase()
@ -74,7 +130,7 @@ describe('file a report on a resource', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(mutate({ mutation: reportMutation, variables })).resolves.toMatchObject({
await expect(mutate({ mutation: fileReportMutation, variables })).resolves.toMatchObject({
data: { fileReport: null },
errors: [{ message: 'Not Authorised!' }],
})
@ -127,7 +183,7 @@ describe('file a report on a resource', () => {
describe('invalid resource id', () => {
it('returns null', async () => {
await expect(mutate({ mutation: reportMutation, variables })).resolves.toMatchObject({
await expect(mutate({ mutation: fileReportMutation, variables })).resolves.toMatchObject({
data: { fileReport: null },
errors: undefined,
})
@ -136,16 +192,20 @@ describe('file a report on a resource', () => {
describe('valid resource', () => {
describe('creates report', () => {
it('which belongs to resource', async () => {
it.only('which belongs to resource now reported by current user', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
}),
).resolves.toMatchObject({
data: {
fileReport: {
id: expect.any(String),
reportId: expect.any(String),
resource: {
name: 'abusive-user',
// Wolle filedUnclosedReportByCurrentUser: true,
},
},
},
errors: undefined,
@ -154,12 +214,12 @@ describe('file a report on a resource', () => {
it('creates only one report for multiple reports on the same resource', async () => {
const firstReport = await mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
})
authenticatedUser = await otherReportingUser.toJson()
const secondReport = await mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
})
expect(firstReport.data.fileReport.id).toEqual(secondReport.data.fileReport.id)
@ -168,7 +228,7 @@ describe('file a report on a resource', () => {
it('returns the rule for how the report was decided', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
}),
).resolves.toMatchObject({
@ -187,7 +247,7 @@ describe('file a report on a resource', () => {
it('returns __typename "User"', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
}),
).resolves.toMatchObject({
@ -205,7 +265,7 @@ describe('file a report on a resource', () => {
it('returns user attribute info', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
}),
).resolves.toMatchObject({
@ -224,7 +284,7 @@ describe('file a report on a resource', () => {
it('returns the submitter', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
}),
).resolves.toMatchObject({
@ -246,7 +306,7 @@ describe('file a report on a resource', () => {
it('returns a date', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: { ...variables, resourceId: 'abusive-user-id' },
}),
).resolves.toMatchObject({
@ -262,7 +322,7 @@ describe('file a report on a resource', () => {
it('returns the reason category', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'abusive-user-id',
@ -286,7 +346,7 @@ describe('file a report on a resource', () => {
it('gives an error if the reason category is not in enum "ReasonCategory"', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'abusive-user-id',
@ -307,7 +367,7 @@ describe('file a report on a resource', () => {
it('returns the reason description', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'abusive-user-id',
@ -331,7 +391,7 @@ describe('file a report on a resource', () => {
it('sanitizes the reason description', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'abusive-user-id',
@ -371,7 +431,7 @@ describe('file a report on a resource', () => {
it('returns type "Post"', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'post-to-report-id',
@ -392,7 +452,7 @@ describe('file a report on a resource', () => {
it('returns resource in post attribute', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'post-to-report-id',
@ -442,7 +502,7 @@ describe('file a report on a resource', () => {
it('returns type "Comment"', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'comment-to-report-id',
@ -463,7 +523,7 @@ describe('file a report on a resource', () => {
it('returns resource in comment attribute', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'comment-to-report-id',
@ -493,7 +553,7 @@ describe('file a report on a resource', () => {
it('returns null', async () => {
await expect(
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
...variables,
resourceId: 'tag-to-report-id',
@ -510,37 +570,6 @@ describe('file a report on a resource', () => {
})
describe('query for reported resource', () => {
const reportsQuery = gql`
query {
reports(orderBy: createdAt_desc) {
id
createdAt
updatedAt
closed
resource {
__typename
... on User {
id
}
... on Post {
id
}
... on Comment {
id
}
}
filed {
submitter {
id
}
createdAt
reasonCategory
reasonDescription
}
}
}
`
beforeEach(async () => {
authenticatedUser = null
moderator = await Factory.build(
@ -632,7 +661,7 @@ describe('file a report on a resource', () => {
authenticatedUser = await currentUser.toJson()
await Promise.all([
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
resourceId: 'abusive-post-1',
reasonCategory: 'other',
@ -640,7 +669,7 @@ describe('file a report on a resource', () => {
},
}),
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
resourceId: 'abusive-comment-1',
reasonCategory: 'discrimination_etc',
@ -648,7 +677,7 @@ describe('file a report on a resource', () => {
},
}),
mutate({
mutation: reportMutation,
mutation: fileReportMutation,
variables: {
resourceId: 'abusive-user-1',
reasonCategory: 'doxing',
@ -678,7 +707,7 @@ describe('file a report on a resource', () => {
})
})
it('role "moderator" gets reports', async () => {
it.only('role "moderator" gets reports', async () => {
const expected = {
reports: expect.arrayContaining([
expect.objectContaining({
@ -689,6 +718,8 @@ describe('file a report on a resource', () => {
resource: {
__typename: 'User',
id: 'abusive-user-1',
// Wolle filedUnclosedReportByCurrentUser: false,
followedByCurrentUser: false,
},
filed: expect.arrayContaining([
expect.objectContaining({

View File

@ -251,12 +251,14 @@ export default {
boolean: {
followedByCurrentUser:
'MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
filedUnclosedReportByCurrentUser:
'MATCH (this)<-[:BELONGS_TO]-(:Report {closed: false})<-[:FILED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isBlocked:
'MATCH (this)<-[:BLOCKED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
blocked:
'MATCH (this)-[:BLOCKED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isMuted:
'MATCH (this)<-[:MUTED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isBlocked:
'MATCH (this)<-[:BLOCKED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
},
count: {
contributionsCount:

View File

@ -10,6 +10,11 @@ type Report {
resource: ReportedResource
}
type FiledReport {
reportId: ID!
resource: ReportedResource
}
union ReportedResource = User | Post | Comment
enum ReportRule {
@ -17,7 +22,7 @@ enum ReportRule {
}
type Mutation {
fileReport(resourceId: ID!, reasonCategory: ReasonCategory!, reasonDescription: String!): Report
fileReport(resourceId: ID!, reasonCategory: ReasonCategory!, reasonDescription: String!): FiledReport
}
type Query {

View File

@ -64,10 +64,19 @@ type User {
# Is the currently logged in user following that user?
followedByCurrentUser: Boolean! @cypher(
statement: """
MATCH (this)<-[: FOLLOWS]-(u: User { id: $cypherParams.currentUserId})
MATCH (this)<-[:FOLLOWS]-(u:User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
"""
)
# Has the currently logged in user reported that user?
filedUnclosedReportByCurrentUser: Boolean! @cypher(
statement: """
MATCH (this)<-[:BELONGS_TO]-(:Report {closed: false})<-[:FILED]-(u:User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
"""
)
isBlocked: Boolean! @cypher(
statement: """
MATCH (this)<-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})