diff --git a/backend/src/schema/resolvers/moderation.js b/backend/src/schema/resolvers/moderation.js index 9d687dd42..730ff73c3 100644 --- a/backend/src/schema/resolvers/moderation.js +++ b/backend/src/schema/resolvers/moderation.js @@ -1,94 +1,45 @@ +const transformReturnType = record => { + return { + ...record.get('review').properties, + report: record.get('report').properties, + to: { + __typename: record.get('type'), + ...record.get('resource').properties, + }, + } +} + export default { Mutation: { review: async (_object, params, context, _resolveInfo) => { - const { resourceId } = params - let { disable, closed } = params - disable = disable === undefined ? null : disable - closed = closed === undefined ? null : closed const { user: moderator, driver } = context let createdRelationshipWithNestedAttributes = null // return value - const session = driver.session() try { const cypher = ` + MATCH (resource {id: $params.resourceId})<-[:BELONGS_TO]-(report:Report {closed: false}) MATCH (moderator:User {id: $moderatorId}) - MATCH (resource {id: $resourceId}) WHERE resource:User OR resource:Post OR resource:Comment - - // no open report, create one, update existing - MERGE (resource)<-[:BELONGS_TO]-(report:Report {closed: false}) - ON CREATE SET report.id = randomUUID(), report.createdAt = $dateTime, report.updatedAt = report.createdAt, report.rule = 'latestReviewUpdatedAtRules', report.disable = resource.disabled, report.closed = false - ON MATCH SET report.updatedAt = $dateTime - // report.disable and report.closed are set after setting them in review - - // Create review on report - WITH moderator, resource, report - MERGE (report)<-[review:REVIEWED]-(moderator) - ON CREATE SET review.createdAt = $dateTime, review.updatedAt = review.createdAt, - review.disable = CASE WHEN $disable IS NULL - THEN report.disable - ELSE $disable END, - review.closed = CASE WHEN $closed IS NULL - THEN report.closed - ELSE $closed END - ON MATCH SET - review.updatedAt = $dateTime, - review.disable = CASE WHEN $disable IS NULL - THEN review.disable - ELSE $disable END, - review.closed = CASE WHEN $closed IS NULL - THEN review.closed - ELSE $closed END - - SET report.disable = review.disable, report.closed = review.closed + MERGE (report)<-[review:REVIEWED { disable: $params.disable, closed: $params.closed }]-(moderator) + ON CREATE SET review.createdAt = $dateTime, review.updatedAt = review.createdAt + ON MATCH SET review.updatedAt = $dateTime + SET report.updatedAt = $dateTime, report.closed = review.closed SET resource.disabled = review.disable - RETURN moderator, review, report, resource, labels(resource)[0] AS type + RETURN review, report, resource, labels(resource)[0] AS type ` - const mutateDecisionWriteTxResultPromise = session.writeTransaction(async txc => { - const mutateDecisionTransactionResponse = await txc.run(cypher, { - resourceId, + const reviewWriteTxResultPromise = session.writeTransaction(async txc => { + const reviewTransactionResponse = await txc.run(cypher, { + params, moderatorId: moderator.id, dateTime: new Date().toISOString(), - disable, - closed, }) - return mutateDecisionTransactionResponse.records.map(record => ({ - moderator: record.get('moderator'), - review: record.get('review'), - report: record.get('report'), - resource: record.get('resource'), - type: record.get('type'), - })) + return reviewTransactionResponse.records.map(transformReturnType) }) - const txResult = await mutateDecisionWriteTxResultPromise + const txResult = await reviewWriteTxResultPromise if (!txResult[0]) return null - const { moderator: moderatorInResult, review, report, resource, type } = txResult[0] - createdRelationshipWithNestedAttributes = { - ...review.properties, - reportId: report.properties.id, - reportCreatedAt: report.properties.createdAt, - reportUpdatedAt: report.properties.updatedAt, - reportDisable: report.properties.disable, - reportClosed: report.properties.closed, - moderator: moderatorInResult.properties, - type, - post: null, - comment: null, - user: null, - } - switch (type) { - case 'Post': - createdRelationshipWithNestedAttributes.post = resource.properties - break - case 'Comment': - createdRelationshipWithNestedAttributes.comment = resource.properties - break - case 'User': - createdRelationshipWithNestedAttributes.user = resource.properties - break - } + createdRelationshipWithNestedAttributes = txResult[0] } finally { session.close() } diff --git a/backend/src/schema/resolvers/moderation.spec.js b/backend/src/schema/resolvers/moderation.spec.js index 1c056500e..6dd2b41f7 100644 --- a/backend/src/schema/resolvers/moderation.spec.js +++ b/backend/src/schema/resolvers/moderation.spec.js @@ -8,63 +8,50 @@ const factory = Factory() const neode = getNeode() const driver = getDriver() -let query, - mutate, +let mutate, authenticatedUser, disableVariables, enableVariables, - resourceVariables, moderator, - nonModerator + nonModerator, + closeReportVariables -const reportMutation = gql` - mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) { - fileReport( - resourceId: $resourceId - reasonCategory: $reasonCategory - reasonDescription: $reasonDescription - ) { - type - } - } -` const reviewMutation = gql` mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) { review(resourceId: $resourceId, disable: $disable, closed: $closed) { - post { - id + createdAt + updatedAt + to { + __typename + ... on User { + id + disabled + } + ... on Post { + id + disabled + } + ... on Comment { + id + disabled + } } - comment { - id - } - } - } -` -const commentQuery = gql` - query($id: ID!) { - Comment(id: $id) { - id - disabled - reviewedByModerator { - id - } - } - } -` -const postQuery = gql` - query($id: ID) { - Post(id: $id) { - id - disabled - reviewedByModerator { + report { id + createdAt + updatedAt + closed + reviewedByModerator { + id + } } } } ` describe('moderate resources', () => { - beforeAll(() => { + beforeAll(async () => { + await factory.cleanDatabase() authenticatedUser = undefined const { server } = createServer({ context: () => { @@ -76,13 +63,9 @@ describe('moderate resources', () => { }, }) mutate = createTestClient(server).mutate - query = createTestClient(server).query }) beforeEach(async () => { - resourceVariables = { - id: 'undefined-resource', - } disableVariables = { resourceId: 'undefined-resource', disable: true, @@ -101,12 +84,81 @@ describe('moderate resources', () => { password: '1234', role: 'moderator', }) + nonModerator = await factory.create('User', { + id: 'non-moderator', + name: 'Non Moderator', + email: 'non.moderator@example.org', + password: '1234', + }) }) afterEach(async () => { await factory.cleanDatabase() }) + describe('review to close report, leaving resource enabled', () => { + describe('unauthenticated', () => { + it('throws authorization error', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + errors: [{ message: 'Not Authorised!' }], + }) + }) + }) + + describe('authenticated', () => { + beforeEach(async () => { + authenticatedUser = await nonModerator.toJson() + }) + + it('non-moderator receives an authorization error', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + errors: [{ message: 'Not Authorised!' }], + }) + }) + }) + + describe('moderator', () => { + beforeEach(async () => { + authenticatedUser = await moderator.toJson() + const questionablePost = await factory.create('Post', { + id: 'should-i-be-disabled', + }) + const reportAgainstQuestionablePost = await factory.create('Report') + await Promise.all([ + reportAgainstQuestionablePost.relateTo(nonModerator, 'filed', { + resourceId: 'should-i-be-disabled', + reasonCategory: 'doxing', + reasonDescription: "This shouldn't be shown to anybody else! It's my private thing!", + }), + reportAgainstQuestionablePost.relateTo(questionablePost, 'belongsTo'), + ]) + closeReportVariables = { + resourceId: 'should-i-be-disabled', + disable: false, + closed: true, + } + }) + + it('report can be closed without disabling resource', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: closeReportVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'Post', id: 'should-i-be-disabled', disabled: false }, + report: { id: expect.any(String), closed: true }, + }, + }, + errors: undefined, + }) + }) + }) + }) + describe('review to disable', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { @@ -119,218 +171,256 @@ describe('moderate resources', () => { }) describe('authenticated', () => { - describe('non moderator', () => { + beforeEach(async () => { + authenticatedUser = await nonModerator.toJson() + }) + + it('non-moderator receives an authorization error', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + errors: [{ message: 'Not Authorised!' }], + }) + }) + }) + + describe('moderator', () => { + beforeEach(async () => { + authenticatedUser = await moderator.toJson() + }) + + describe('review of a resource that is not a (Comment|Post|User) ', () => { beforeEach(async () => { - nonModerator = await factory.create('User', { - id: 'non-moderator', - name: 'Non Moderator', - email: 'non.moderator@example.org', - password: '1234', - }) - authenticatedUser = await nonModerator.toJson() + disableVariables = { + ...disableVariables, + resourceId: 'tag-id', + } + await factory.create('Tag', { id: 'tag-id' }) }) - it('throws authorization error', async () => { + it('returns null', async () => { await expect( mutate({ mutation: reviewMutation, variables: disableVariables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + data: { review: null }, }) }) }) - describe('moderator', () => { + describe('moderate a comment', () => { beforeEach(async () => { - authenticatedUser = await moderator.toJson() + const trollingComment = await factory.create('Comment', { + id: 'comment-id', + }) + const reportAgainstTrollingComment = await factory.create('Report') + await Promise.all([ + reportAgainstTrollingComment.relateTo(nonModerator, 'filed', { + resourceId: 'comment-id', + reasonCategory: 'other', + reasonDescription: 'This comment is bigoted', + }), + reportAgainstTrollingComment.relateTo(trollingComment, 'belongsTo'), + ]) + disableVariables = { + ...disableVariables, + resourceId: 'comment-id', + } }) - describe('moderate a resource that is not a (Comment|Post|User) ', () => { - beforeEach(async () => { - disableVariables = { - ...disableVariables, - resourceId: 'sample-tag-id', - } - await factory.create('Tag', { id: 'sample-tag-id' }) - }) - - it('returns null', async () => { - await expect( - mutate({ mutation: reviewMutation, variables: disableVariables }), - ).resolves.toMatchObject({ - data: { review: null }, - }) + it('returns disabled resource id', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { review: { to: { __typename: 'Comment', id: 'comment-id' } } }, + errors: undefined, }) }) - describe('moderate a comment', () => { - beforeEach(async () => { - await factory.create('Comment', { - id: 'comment-id', - }) - await mutate({ - mutation: reportMutation, - variables: { - resourceId: 'comment-id', - reasonCategory: 'discrimination_etc', - reasonDescription: 'I am what I am !!!', + it('returns .reviewedByModerator', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'Comment', id: 'comment-id' }, + report: { id: expect.any(String), reviewedByModerator: { id: 'moderator-id' } }, }, - }) - }) - - it('returns disabled resource id', async () => { - disableVariables = { - ...disableVariables, - resourceId: 'comment-id', - } - await expect( - mutate({ mutation: reviewMutation, variables: disableVariables }), - ).resolves.toMatchObject({ - data: { review: { comment: { id: 'comment-id' } } }, - errors: undefined, - }) - }) - - it('changes .reviewedByModerator', async () => { - resourceVariables = { - id: 'comment-id', - } - disableVariables = { - ...disableVariables, - resourceId: 'comment-id', - } - const before = { data: { Comment: [{ id: 'comment-id', reviewedByModerator: null }] } } - const expected = { - data: { - Comment: [{ id: 'comment-id', reviewedByModerator: { id: 'moderator-id' } }], - }, - } - await expect( - query({ query: commentQuery, variables: resourceVariables }), - ).resolves.toMatchObject(before) - await expect( - mutate({ mutation: reviewMutation, variables: disableVariables }), - ).resolves.toMatchObject({ - data: { review: { comment: { id: 'comment-id' } } }, - }) - await expect( - query({ query: commentQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) - }) - - it('updates .disabled on comment', async () => { - resourceVariables = { - id: 'comment-id', - } - disableVariables = { - ...disableVariables, - resourceId: 'comment-id', - } - const before = { data: { Comment: [{ id: 'comment-id', disabled: false }] } } - const expected = { data: { Comment: [{ id: 'comment-id', disabled: true }] } } - - await expect( - query({ query: commentQuery, variables: resourceVariables }), - ).resolves.toMatchObject(before) - await expect( - mutate({ mutation: reviewMutation, variables: disableVariables }), - ).resolves.toMatchObject({ - data: { review: { comment: { id: 'comment-id' } } }, - }) - await expect( - query({ query: commentQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) + }, }) }) - describe('moderate a post', () => { - beforeEach(async () => { - await factory.create('Post', { - id: 'sample-post-id', - }) - await mutate({ - mutation: reportMutation, - variables: { - resourceId: 'sample-post-id', - reasonCategory: 'discrimination_etc', - reasonDescription: 'I am what I am !!!', + it('updates .disabled on comment', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { review: { to: { __typename: 'Comment', id: 'comment-id', disabled: true } } }, + }) + }) + + it('can be closed with one review', async () => { + closeReportVariables = { + ...disableVariables, + closed: true, + } + await expect( + mutate({ mutation: reviewMutation, variables: closeReportVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'Comment', id: 'comment-id' }, + report: { id: expect.any(String), closed: true }, }, - }) + }, }) + }) + }) - it('returns disabled resource id', async () => { - disableVariables = { - ...disableVariables, - resourceId: 'sample-post-id', - } - await expect( - mutate({ mutation: reviewMutation, variables: disableVariables }), - ).resolves.toMatchObject({ - data: { review: { post: { id: 'sample-post-id' } } }, - }) + describe('moderate a post', () => { + beforeEach(async () => { + const trollingPost = await factory.create('Post', { + id: 'post-id', }) + const reportAgainstTrollingPost = await factory.create('Report') + await Promise.all([ + reportAgainstTrollingPost.relateTo(nonModerator, 'filed', { + resourceId: 'post-id', + reasonCategory: 'doxing', + reasonDescription: "This shouldn't be shown to anybody else! It's my private thing!", + }), + reportAgainstTrollingPost.relateTo(trollingPost, 'belongsTo'), + ]) + disableVariables = { + ...disableVariables, + resourceId: 'post-id', + } + }) - it('changes .reviewedByModerator', async () => { - resourceVariables = { - id: 'sample-post-id', - } - disableVariables = { - ...disableVariables, - resourceId: 'sample-post-id', - } - const before = { data: { Post: [{ id: 'sample-post-id', reviewedByModerator: null }] } } - const expected = { - data: { - Post: [{ id: 'sample-post-id', reviewedByModerator: { id: 'moderator-id' } }], + it('returns disabled resource id', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'Post', id: 'post-id' }, }, - } - - await expect( - query({ query: postQuery, variables: resourceVariables }), - ).resolves.toMatchObject(before) - await expect( - mutate({ mutation: reviewMutation, variables: disableVariables }), - ).resolves.toMatchObject({ - data: { review: { post: { id: 'sample-post-id' } } }, - }) - await expect( - query({ query: postQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) + }, }) + }) - it('updates .disabled on post', async () => { - resourceVariables = { - id: 'sample-post-id', - } - disableVariables = { - ...disableVariables, - resourceId: 'sample-post-id', - } - const before = { data: { Post: [{ id: 'sample-post-id', disabled: false }] } } - const expected = { data: { Post: [{ id: 'sample-post-id', disabled: true }] } } + it('returns .reviewedByModerator', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'Post', id: 'post-id' }, + report: { id: expect.any(String), reviewedByModerator: { id: 'moderator-id' } }, + }, + }, + }) + }) - await expect( - query({ query: postQuery, variables: resourceVariables }), - ).resolves.toMatchObject(before) - await expect( - mutate({ mutation: reviewMutation, variables: disableVariables }), - ).resolves.toMatchObject({ - data: { review: { post: { id: 'sample-post-id' } } }, - }) - await expect( - query({ query: postQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) + it('updates .disabled on post', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { review: { to: { __typename: 'Post', id: 'post-id', disabled: true } } }, + }) + }) + + it('can be closed with one review', async () => { + closeReportVariables = { + ...disableVariables, + closed: true, + } + await expect( + mutate({ mutation: reviewMutation, variables: closeReportVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'Post', id: 'post-id' }, + report: { id: expect.any(String), closed: true }, + }, + }, + }) + }) + }) + + describe('moderate a user', () => { + beforeEach(async () => { + const troll = await factory.create('User', { + id: 'user-id', + }) + const reportAgainstTroll = await factory.create('Report') + await Promise.all([ + reportAgainstTroll.relateTo(nonModerator, 'filed', { + resourceId: 'user-id', + reasonCategory: 'discrimination_etc', + reasonDescription: 'This user is harassing me with bigoted remarks!', + }), + reportAgainstTroll.relateTo(troll, 'belongsTo'), + ]) + disableVariables = { + ...disableVariables, + resourceId: 'user-id', + } + }) + + it('returns disabled resource id', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { review: { to: { __typename: 'User', id: 'user-id' } } }, + }) + }) + + it('returns .reviewedByModerator', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'User', id: 'user-id' }, + report: { id: expect.any(String), reviewedByModerator: { id: 'moderator-id' } }, + }, + }, + }) + }) + + it('updates .disabled on user', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: disableVariables }), + ).resolves.toMatchObject({ + data: { review: { to: { __typename: 'User', id: 'user-id', disabled: true } } }, + }) + }) + + it('can be closed with one review', async () => { + closeReportVariables = { + ...disableVariables, + closed: true, + } + await expect( + mutate({ mutation: reviewMutation, variables: closeReportVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'User', id: 'user-id' }, + report: { id: expect.any(String), closed: true }, + }, + }, }) }) }) }) }) - describe('enable', () => { + describe('review to re-enable after disabled', () => { describe('unautenticated user', () => { it('throws authorization error', async () => { enableVariables = { ...enableVariables, - resourceId: 'sample-post-id', + resourceId: 'post-id', } await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), @@ -343,19 +433,13 @@ describe('moderate resources', () => { describe('authenticated user', () => { describe('non moderator', () => { beforeEach(async () => { - nonModerator = await factory.create('User', { - id: 'non-moderator', - name: 'Non Moderator', - email: 'non.moderator@example.org', - password: '1234', - }) authenticatedUser = await nonModerator.toJson() }) it('throws authorization error', async () => { enableVariables = { ...enableVariables, - resourceId: 'sample-post-id', + resourceId: 'post-id', } await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), @@ -372,13 +456,13 @@ describe('moderate resources', () => { describe('moderate a resource that is not a (Comment|Post|User) ', () => { beforeEach(async () => { - await Promise.all([factory.create('Tag', { id: 'sample-tag-id' })]) + await Promise.all([factory.create('Tag', { id: 'tag-id' })]) }) it('returns null', async () => { enableVariables = { ...enableVariables, - resourceId: 'sample-tag-id', + resourceId: 'tag-id', } await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), @@ -390,132 +474,175 @@ describe('moderate resources', () => { describe('moderate a comment', () => { beforeEach(async () => { - resourceVariables = { + const trollingComment = await factory.create('Comment', { id: 'comment-id', - } - disableVariables = { - ...disableVariables, - resourceId: 'comment-id', - } + }) + const reportAgainstTrollingComment = await factory.create('Report') + await Promise.all([ + reportAgainstTrollingComment.relateTo(nonModerator, 'filed', { + resourceId: 'comment-id', + reasonCategory: 'other', + reasonDescription: 'This comment is bigoted', + }), + reportAgainstTrollingComment.relateTo(trollingComment, 'belongsTo'), + ]) + await Promise.all([ + reportAgainstTrollingComment.relateTo(moderator, 'reviewed', { + ...disableVariables, + resourceId: 'comment-id', + }), + trollingComment.update({ disabled: true, updatedAt: new Date().toISOString() }), + ]) enableVariables = { ...enableVariables, resourceId: 'comment-id', } - await factory.create('Comment', { - id: 'comment-id', - }) - await mutate({ - mutation: reportMutation, - variables: { - resourceId: 'comment-id', - reasonCategory: 'discrimination_etc', - reasonDescription: 'I am what I am !!!', - }, - }) - await mutate({ mutation: reviewMutation, variables: disableVariables }) }) it('returns enabled resource id', async () => { await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - data: { review: { comment: { id: 'comment-id' } } }, + data: { review: { to: { __typename: 'Comment', id: 'comment-id' } } }, }) }) - it('changes .reviewedByModerator', async () => { - const expected = { - data: { - Comment: [{ id: 'comment-id', reviewedByModerator: { id: 'moderator-id' } }], - }, - } - + it('returns .reviewedByModerator', async () => { await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - data: { review: { comment: { id: 'comment-id' } } }, + data: { + review: { + to: { __typename: 'Comment', id: 'comment-id' }, + report: { id: expect.any(String), reviewedByModerator: { id: 'moderator-id' } }, + }, + }, }) - await expect( - query({ query: commentQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) }) it('updates .disabled on comment', async () => { - const expected = { - data: { Comment: [{ id: 'comment-id', disabled: false }] }, - } - await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - data: { review: { comment: { id: 'comment-id' } } }, + data: { + review: { to: { __typename: 'Comment', id: 'comment-id', disabled: false } }, + }, }) - await expect( - query({ query: commentQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) }) }) describe('moderate a post', () => { beforeEach(async () => { - resourceVariables = { id: 'post-id' } - disableVariables = { - ...disableVariables, - resourceId: 'post-id', - } + const trollingPost = await factory.create('Post', { + id: 'post-id', + }) + const reportAgainstTrollingPost = await factory.create('Report') + await Promise.all([ + reportAgainstTrollingPost.relateTo(nonModerator, 'filed', { + resourceId: 'post-id', + reasonCategory: 'doxing', + reasonDescription: + "This shouldn't be shown to anybody else! It's my private thing!", + }), + reportAgainstTrollingPost.relateTo(trollingPost, 'belongsTo'), + ]) + await Promise.all([ + reportAgainstTrollingPost.relateTo(moderator, 'reviewed', { + ...disableVariables, + resourceId: 'comment-id', + }), + trollingPost.update({ disabled: true, updatedAt: new Date().toISOString() }), + ]) enableVariables = { ...enableVariables, resourceId: 'post-id', } - await factory.create('Post', { - id: 'post-id', - }) - await mutate({ - mutation: reportMutation, - variables: { - resourceId: 'post-id', - reasonCategory: 'discrimination_etc', - reasonDescription: 'I am what I am !!!', - }, - }) - await mutate({ mutation: reviewMutation, variables: disableVariables }) }) it('returns enabled resource id', async () => { await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - data: { review: { post: { id: 'post-id' } } }, + data: { review: { to: { __typename: 'Post', id: 'post-id' } } }, }) }) - it('changes .reviewedByModerator', async () => { - const expected = { - data: { Post: [{ id: 'post-id', reviewedByModerator: { id: 'moderator-id' } }] }, - } + it('returns .reviewedByModerator', async () => { await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - data: { review: { post: { id: 'post-id' } } }, + data: { + review: { + to: { __typename: 'Post', id: 'post-id' }, + report: { id: expect.any(String), reviewedByModerator: { id: 'moderator-id' } }, + }, + }, }) - await expect( - query({ query: postQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) }) it('updates .disabled on post', async () => { - const expected = { - data: { Post: [{ id: 'post-id', disabled: false }] }, - } - await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - data: { review: { post: { id: 'post-id' } } }, + data: { review: { to: { __typename: 'Post', id: 'post-id', disabled: false } } }, }) + }) + }) + + describe('moderate a user', () => { + beforeEach(async () => { + const troll = await factory.create('User', { + id: 'user-id', + }) + const reportAgainstTroll = await factory.create('Report') + await Promise.all([ + reportAgainstTroll.relateTo(nonModerator, 'filed', { + resourceId: 'user-id', + reasonCategory: 'discrimination_etc', + reasonDescription: 'This user is harassing me with bigoted remarks!', + }), + reportAgainstTroll.relateTo(troll, 'belongsTo'), + ]) + await Promise.all([ + reportAgainstTroll.relateTo(moderator, 'reviewed', { + ...disableVariables, + resourceId: 'comment-id', + }), + troll.update({ disabled: true, updatedAt: new Date().toISOString() }), + ]) + enableVariables = { + ...enableVariables, + resourceId: 'user-id', + } + }) + + it('returns enabled resource id', async () => { await expect( - query({ query: postQuery, variables: resourceVariables }), - ).resolves.toMatchObject(expected) + mutate({ mutation: reviewMutation, variables: enableVariables }), + ).resolves.toMatchObject({ + data: { review: { to: { __typename: 'User', id: 'user-id' } } }, + }) + }) + + it('returns .reviewedByModerator', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: enableVariables }), + ).resolves.toMatchObject({ + data: { + review: { + to: { __typename: 'User', id: 'user-id' }, + report: { id: expect.any(String), reviewedByModerator: { id: 'moderator-id' } }, + }, + }, + }) + }) + + it('updates .disabled on user', async () => { + await expect( + mutate({ mutation: reviewMutation, variables: enableVariables }), + ).resolves.toMatchObject({ + data: { review: { to: { __typename: 'User', id: 'user-id', disabled: false } } }, + }) }) }) }) diff --git a/backend/src/schema/types/type/REVIEWED.gql b/backend/src/schema/types/type/REVIEWED.gql index c296483ea..5b4a392ae 100644 --- a/backend/src/schema/types/type/REVIEWED.gql +++ b/backend/src/schema/types/type/REVIEWED.gql @@ -1,23 +1,15 @@ type REVIEWED { createdAt: String! updatedAt: String! - # reasonCategory: ReasonCategory - # reasonDescription: String disable: Boolean! closed: Boolean! - - reportId: ID! - reportCreatedAt: String! - reportUpdatedAt: String! - reportDisable: Boolean! - reportClosed: Boolean! - + report: Report + # @cypher(statement: "MATCH (report:Report)<-[this:REVIEWED]-(:User) RETURN report") moderator: User type: String - user: User - post: Post - comment: Comment + to: ReviewedResource } +union ReviewedResource = User | Post | Comment type Mutation { review(resourceId: ID!, disable: Boolean, closed: Boolean): REVIEWED