Refactor decide mutation to new CaseFolder structure

This commit is contained in:
Wolfgang Huß 2019-11-20 15:17:29 +01:00
parent 7c3ec289f7
commit cf02ca4506
15 changed files with 254 additions and 143 deletions

View File

@ -25,12 +25,12 @@ module.exports = {
target: 'User',
direction: 'in',
},
decidedByModerator: {
type: 'relationship',
relationship: 'DECIDED',
target: 'User',
direction: 'in',
},
// Wolle reviewedByModerator: {
// type: 'relationship',
// relationship: 'DECIDED',
// target: 'User',
// direction: 'in',
// },
notified: {
type: 'relationship',
relationship: 'NOTIFIED',

View File

@ -17,12 +17,12 @@ module.exports = {
image: { type: 'string', allow: [null] },
deleted: { type: 'boolean', default: false },
disabled: { type: 'boolean', default: false },
decidedByModerator: {
type: 'relationship',
relationship: 'DECIDED',
target: 'User',
direction: 'in',
},
// Wolle reviewedByModerator: {
// type: 'relationship',
// relationship: 'DECIDED',
// target: 'User',
// direction: 'in',
// },
notified: {
type: 'relationship',
relationship: 'NOTIFIED',

View File

@ -42,12 +42,12 @@ module.exports = {
},
},
friends: { type: 'relationship', relationship: 'FRIENDS', target: 'User', direction: 'both' },
decidedByModerator: {
type: 'relationship',
relationship: 'DECIDED',
target: 'User',
direction: 'in',
},
// Wolle reviewedByModerator: {
// type: 'relationship',
// relationship: 'DECIDED',
// target: 'User',
// direction: 'in',
// },
rewarded: {
type: 'relationship',
relationship: 'REWARDED',

View File

@ -24,7 +24,7 @@ export default applyScalars(
'SocialMedia',
'NOTIFIED',
'REPORTED',
'DECIDED',
'REVIEWED',
'Donations',
],
// add 'User' here as soon as possible
@ -46,7 +46,7 @@ export default applyScalars(
'EMOTED',
'NOTIFIED',
'REPORTED',
'DECIDED',
'REVIEWED',
'Donations',
],
// add 'User' here as soon as possible

View File

@ -69,8 +69,35 @@ export default {
hasOne: {
author: '<-[:WROTE]-(related:User)',
post: '-[:COMMENTS]->(related:Post)',
decidedByModerator: '<-[:DECIDED]-(related:User)',
// Wolle reviewedByModerator: '<-[:DECIDED]-(related:User)',
},
}),
// Wolle reviewedByModerator: async (parent, params, context, resolveInfo) => {
// console.log('reviewedByModerator !!!')
// console.log('reviewedByModerator !!!')
// console.log('reviewedByModerator !!!')
// console.log('reviewedByModerator !!!')
// console.log('reviewedByModerator !!!')
// if (typeof parent.reviewedByModerator !== 'undefined') return parent.reviewedByModerator
// // const { id } = parent
// // const statement = `
// // MATCH (p:Post {id: $id})-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post)
// // WHERE NOT post.deleted AND NOT post.disabled
// // RETURN DISTINCT post
// // LIMIT 10
// // `
// // let reviewedByModerator
// // const session = context.driver.session()
// // try {
// // const result = await session.run(statement, { id })
// // relatedContributions = result.records.map(r => r.get('post').properties)
// // } finally {
// // session.close()
// // }
// // return reviewedByModerator
// return {
// id: 'Hallo !!!'
// }
// },
},
}

View File

@ -1,33 +1,32 @@
import uuid from 'uuid/v4'
import { undefinedToNullResolver } from './helpers/Resolver'
const queryOpenDecisionWriteTransaction = (session, resourceId) => {
return session.writeTransaction(async txc => {
const queryOpenDecisionTransactionResponse = await txc.run(
`
//MATCH (moderator:User)-[decision:DECIDED {closed: false}]->(resource {id: $resourceId})
//WHERE resource:User OR resource:Comment OR resource:Post
//RETURN decision, moderator {.id} AS decisionModerator
// const queryOpenDecisionWriteTransaction = (session, resourceId) => {
// return session.writeTransaction(async txc => {
// const queryOpenDecisionTransactionResponse = await txc.run(
// `
// //MATCH (moderator:User)-[decision:DECIDED {closed: false}]->(resource {id: $resourceId})
// //WHERE resource:User OR resource:Comment OR resource:Post
// //RETURN decision, moderator {.id} AS decisionModerator
// Wolle only review on reported resources
// // Wolle only review on reported resources
MATCH (moderator:User)-[:REPORTED]->(caseFolder:CaseFolder {closed: false})-[:FLAGGED]->(resource {id: $resourceId})
WHERE resource:User OR resource:Post OR resource:Comment
RETURN caseFolder, moderator {.id} AS caseModerator
`,
{ resourceId },
)
return queryOpenDecisionTransactionResponse.records.map(record => ({
caseFolder: record.get('caseFolder'),
caseModerator: record.get('caseModerator'),
}))
})
}
// MATCH (caseModerator:User)-[:REPORTED]->(caseFolder:CaseFolder {closed: false})-[:FLAGGED]->(resource {id: $resourceId})
// WHERE resource:User OR resource:Post OR resource:Comment
// RETURN caseFolder, caseModerator {.id}
// `,
// { resourceId },
// )
// return queryOpenDecisionTransactionResponse.records.map(record => ({
// caseFolder: record.get('caseFolder'),
// caseModerator: record.get('caseModerator'),
// }))
// })
// }
export default {
Mutation: {
decide: async (_object, params, context, _resolveInfo) => {
let createdRelationshipWithNestedAttributes = null
// Wolle console.log('params: ', params)
const { resourceId } = params
// Wolle console.log('resourceId: ', resourceId)
@ -35,101 +34,161 @@ export default {
// Wolle console.log('disable: ', disable)
const { user: moderator, driver } = context
let createdRelationshipWithNestedAttributes = null // return value
const session = driver.session()
try {
const queryOpenDecisionWriteTxResultPromise = queryOpenDecisionWriteTransaction(
session,
resourceId,
)
const [openDecisionTxResult] = await queryOpenDecisionWriteTxResultPromise
// const queryOpenDecisionWriteTxResultPromise = queryOpenDecisionWriteTransaction(
// session,
// resourceId,
// )
// console.log('queryOpenDecisionWriteTxResultPromise: ', queryOpenDecisionWriteTxResultPromise)
// console.log('queryOpenDecisionWriteTxResultPromise: ', queryOpenDecisionWriteTxResultPromise)
// console.log('queryOpenDecisionWriteTxResultPromise: ', queryOpenDecisionWriteTxResultPromise)
// console.log('queryOpenDecisionWriteTxResultPromise: ', queryOpenDecisionWriteTxResultPromise)
// console.log('queryOpenDecisionWriteTxResultPromise: ', queryOpenDecisionWriteTxResultPromise)
// const [openDecisionTxResult] = await queryOpenDecisionWriteTxResultPromise
let cypherHeader = ''
// let cypherHeader = ''
if (!openDecisionTxResult) {
// no open caseFolder, then create one
if (disable === undefined) disable = false // default for creation
if (closed === undefined) closed = false // default for creation
cypherHeader = `
MATCH (resource {id: $resourceId})
WHERE resource: User OR resource: Comment OR resource: Post
OPTIONAL MATCH (:User)-[lastDecision:DECIDED {latest: true}]->(resource)
SET (CASE WHEN lastDecision IS NOT NULL THEN lastDecision END).latest = false
WITH resource
MATCH (moderator:User {id: $moderatorId})
CREATE (resource)<-[decision:DECIDED]-(moderator)
SET decision.latest = true
`
} else {
// an open caseFolder, then change it
if (disable === undefined) disable = openDecisionTxResult.caseFolder.properties.disable // default set to existing
if (closed === undefined) closed = openDecisionTxResult.caseFolder.properties.closed // default set to existing
// current moderator is not the same as old
if (moderator.id !== openDecisionTxResult.caseModerator.id) {
// from a different moderator, then create relation with properties to new moderator
cypherHeader = `
MATCH (moderator:User)-[oldDecision:DECIDED {closed: false}]->(resource {id: $resourceId})
WHERE resource:User OR resource:Comment OR resource:Post
DELETE oldDecision
MATCH (moderator:User {id: $moderatorId})
MATCH (resource {id: $resourceId})
WHERE resource:User OR resource:Comment OR resource:Post
CREATE (resource)<-[decision:DECIDED]-(moderator)
SET decision = oldDecision
`
} else {
// an open caseFolder from same moderator, then match this
cypherHeader = `
MATCH (moderator:User)-[decision:DECIDED {closed: false}]->(resource {id: $resourceId})
WHERE resource:User OR resource:Comment OR resource:Post
`
}
}
let decisionUUID = null
let cypherClosed = ''
if (closed) {
decisionUUID = uuid()
cypherClosed = `
WITH decision, resource, moderator
OPTIONAL MATCH (:User)-[report:REPORTED {closed: false}]->(resource)
SET (CASE WHEN report IS NOT NULL THEN report END).closed = true
SET (CASE WHEN report IS NOT NULL THEN report END).decisionUuid = $decisionUUID
SET decision.uuid = $decisionUUID
`
}
// // Wolle openDecisionTxResult should not be undefined !!!
// if (!openDecisionTxResult) {
// // no open caseFolder, then create one
// if (disable === undefined) disable = false // default for creation
// if (closed === undefined) closed = false // default for creation
// cypherHeader = `
// MATCH (resource {id: $resourceId})
// WHERE resource: User OR resource: Comment OR resource: Post
// OPTIONAL MATCH (:User)-[lastDecision:DECIDED {latest: true}]->(resource)
// SET (CASE WHEN lastDecision IS NOT NULL THEN lastDecision END).latest = false
// WITH resource
// MATCH (moderator:User {id: $moderatorId})
// CREATE (resource)<-[decision:DECIDED]-(moderator)
// SET decision.latest = true
// `
// } else {
// // an open caseFolder, then change it
// if (disable === undefined) disable = openDecisionTxResult.caseFolder.properties.disable // default set to existing
// if (closed === undefined) closed = openDecisionTxResult.caseFolder.properties.closed // default set to existing
// // current moderator is not the same as old
// if (moderator.id !== openDecisionTxResult.caseModerator.id) {
// // from a different moderator, then create relation with properties to new moderator
// cypherHeader = `
// MATCH (moderator:User)-[oldDecision:DECIDED {closed: false}]->(resource {id: $resourceId})
// WHERE resource:User OR resource:Comment OR resource:Post
// DELETE oldDecision
// MATCH (moderator:User {id: $moderatorId})
// MATCH (resource {id: $resourceId})
// WHERE resource:User OR resource:Comment OR resource:Post
// CREATE (resource)<-[decision:DECIDED]-(moderator)
// SET decision = oldDecision
// `
// } else {
// // an open caseFolder from same moderator, then match this
// cypherHeader = `
// MATCH (moderator:User)-[decision:DECIDED {closed: false}]->(resource {id: $resourceId})
// WHERE resource:User OR resource:Comment OR resource:Post
// `
// }
// }
// let decisionUUID = null
// let cypherClosed = ''
// if (closed) {
// decisionUUID = uuid()
// cypherClosed = `
// WITH decision, resource, moderator
// OPTIONAL MATCH (:User)-[report:REPORTED {closed: false}]->(resource)
// SET (CASE WHEN report IS NOT NULL THEN report END).closed = true
// SET (CASE WHEN report IS NOT NULL THEN report END).decisionUuid = $decisionUUID
// SET decision.uuid = $decisionUUID
// `
// }
// const cypher =
// cypherHeader +
// `SET decision.updatedAt = toString(datetime())
// SET (CASE WHEN decision.createdAt IS NULL THEN decision END).createdAt = decision.updatedAt
// SET decision.disable = $disable, decision.closed = $closed
// SET resource.disabled = $disable
// ` +
// cypherClosed +
// `RETURN decision, resource, moderator, labels(resource)[0] AS type
// `
const cypher =
cypherHeader +
`SET decision.updatedAt = toString(datetime())
SET (CASE WHEN decision.createdAt IS NULL THEN decision END).createdAt = decision.updatedAt
SET decision.disable = $disable, decision.closed = $closed
`
// Wolle only review on reported resources
MATCH (moderator:User {id: $moderatorId})
MATCH (resource {id: $resourceId})
WHERE resource:User OR resource:Post OR resource:Comment
// no open caseFolder, create one, update existing
MERGE (resource)<-[:FLAGGED]-(caseFolder:CaseFolder {closed: false})
ON CREATE SET caseFolder.id = randomUUID(), caseFolder.createdAt = $createdAt, caseFolder.updatedAt = caseFolder.createdAt, caseFolder.disable = $disable, caseFolder.closed = $closed
ON MATCH SET caseFolder.updatedAt = $createdAt, caseFolder.disable = $disable, caseFolder.closed = $closed
// Create review on caseFolder
WITH moderator, resource, caseFolder
CREATE (caseFolder)<-[review:REVIEWED {createdAt: $createdAt, updatedAt: $createdAt, disable: $disable, closed: $closed}]-(moderator)
SET resource.disabled = $disable
` +
cypherClosed +
`RETURN decision, resource, moderator, labels(resource)[0] AS type
RETURN moderator, review, caseFolder {.id}, resource, labels(resource)[0] AS type
//RETURN decision, resource, moderator, labels(resource)[0] AS type
`
// Wolle console.log('cypher: ', cypher)
// console.log('disable: ', disable)
// const mutateDecisionWriteTxResultPromise = session.writeTransaction(async txc => {
// const mutateDecisionTransactionResponse = await txc.run(
// cypher, {
// resourceId,
// moderatorId: moderator.id,
// disable,
// closed,
// decisionUUID,
// })
// return mutateDecisionTransactionResponse.records.map(record => ({
// decision: record.get('decision'),
// resource: record.get('resource'),
// moderator: record.get('moderator'),
// type: record.get('type'),
// }))
// })
const mutateDecisionWriteTxResultPromise = session.writeTransaction(async txc => {
const mutateDecisionTransactionResponse = await txc.run(cypher, {
const mutateDecisionTransactionResponse = await txc.run(
cypher, {
resourceId,
moderatorId: moderator.id,
createdAt: new Date().toISOString(),
disable,
closed,
decisionUUID,
})
return mutateDecisionTransactionResponse.records.map(record => ({
decision: record.get('decision'),
resource: record.get('resource'),
moderator: record.get('moderator'),
review: record.get('review'),
caseFolder: record.get('caseFolder'),
resource: record.get('resource'),
type: record.get('type'),
}))
})
const txResult = await mutateDecisionWriteTxResultPromise
if (!txResult[0]) return null
const { decision, resource, moderator: moderatorInResult, type } = txResult[0]
// const { decision, resource, moderator: moderatorInResult, type } = txResult[0]
// createdRelationshipWithNestedAttributes = {
// ...decision.properties,
// moderator: moderatorInResult.properties,
// type,
// post: null,
// comment: null,
// user: null,
// }
const { moderator: moderatorInResult, review, caseFolder, resource, type } = txResult[0]
createdRelationshipWithNestedAttributes = {
...decision.properties,
...review.properties,
moderator: moderatorInResult.properties,
caseFolderId: caseFolder.id,
type,
post: null,
comment: null,
@ -153,7 +212,7 @@ export default {
return createdRelationshipWithNestedAttributes
},
},
DECIDED: {
...undefinedToNullResolver(['uuid']),
REVIEWED: {
// Wolle ...undefinedToNullResolver(['uuid']),
},
}

View File

@ -34,7 +34,7 @@ const commentQuery = gql`
Comment(id: $id) {
id
disabled
decidedByModerator {
reviewedByModerator {
id
}
}
@ -45,7 +45,7 @@ const postQuery = gql`
Post(id: $id) {
id
disabled
decidedByModerator {
reviewedByModerator {
id
}
}
@ -172,7 +172,7 @@ describe('moderate resources', () => {
})
})
it('changes .decidedByModerator', async () => {
it('changes .reviewedByModerator', async () => {
resourceVariables = {
id: 'comment-id',
}
@ -180,9 +180,9 @@ describe('moderate resources', () => {
...disableVariables,
resourceId: 'comment-id',
}
const before = { data: { Comment: [{ id: 'comment-id', decidedByModerator: null }] } }
const before = { data: { Comment: [{ id: 'comment-id', reviewedByModerator: null }] } }
const expected = {
data: { Comment: [{ id: 'comment-id', decidedByModerator: { id: 'moderator-id' } }] },
data: { Comment: [{ id: 'comment-id', reviewedByModerator: { id: 'moderator-id' } }] },
}
await expect(
query({ query: commentQuery, variables: resourceVariables }),
@ -241,7 +241,7 @@ describe('moderate resources', () => {
})
})
it('changes .decidedByModerator', async () => {
it('changes .reviewedByModerator', async () => {
resourceVariables = {
id: 'sample-post-id',
}
@ -249,10 +249,10 @@ describe('moderate resources', () => {
...disableVariables,
resourceId: 'sample-post-id',
}
const before = { data: { Post: [{ id: 'sample-post-id', decidedByModerator: null }] } }
const before = { data: { Post: [{ id: 'sample-post-id', reviewedByModerator: null }] } }
const expected = {
data: {
Post: [{ id: 'sample-post-id', decidedByModerator: { id: 'moderator-id' } }],
Post: [{ id: 'sample-post-id', reviewedByModerator: { id: 'moderator-id' } }],
},
}
@ -387,9 +387,9 @@ describe('moderate resources', () => {
})
})
it('changes .decidedByModerator', async () => {
it('changes .reviewedByModerator', async () => {
const expected = {
data: { Comment: [{ id: 'comment-id', decidedByModerator: { id: 'moderator-id' } }] },
data: { Comment: [{ id: 'comment-id', reviewedByModerator: { id: 'moderator-id' } }] },
}
await expect(
@ -443,9 +443,9 @@ describe('moderate resources', () => {
})
})
it('changes .decidedByModerator', async () => {
it('changes .reviewedByModerator', async () => {
const expected = {
data: { Post: [{ id: 'post-id', decidedByModerator: { id: 'moderator-id' } }] },
data: { Post: [{ id: 'post-id', reviewedByModerator: { id: 'moderator-id' } }] },
}
await expect(
mutate({ mutation: decideMutation, variables: enableVariables }),

View File

@ -307,7 +307,7 @@ export default {
},
hasOne: {
author: '<-[:WROTE]-(related:User)',
decidedByModerator: '<-[:DECIDED]-(related:User)',
// Wolle reviewedByModerator: '<-[:DECIDED]-(related:User)',
pinnedBy: '<-[:PINNED]-(related:User)',
},
count: {

View File

@ -199,7 +199,7 @@ export default {
},
hasOne: {
invitedBy: '<-[:INVITED]-(related:User)',
decidedByModerator: '<-[:DECIDED]-(related:User)',
// Wolle reviewedByModerator: '<-[:DECIDED]-(related:User)',
location: '-[:IS_IN]->(related:Location)',
},
hasMany: {

View File

@ -9,7 +9,16 @@ type Comment {
updatedAt: String
deleted: Boolean
disabled: Boolean
decidedByModerator: User @relation(name: "DECIDED", direction: "IN")
# Wolle reviewedByModerator: User # Wolle @relation(name: "DECIDED", direction: "IN")
reviewedByModerator: User
@cypher(
statement: """
MATCH (this)<-[:FLAGGED]-(caseFolder:CaseFolder)<-[review:REVIEWED]-(moderator:User)
RETURN moderator
ORDER BY caseFolder.updatedAt ASC, review.updatedAt ASC
LIMIT 1
"""
)
}
type Mutation {

View File

@ -46,8 +46,16 @@ type Post {
visibility: Visibility
deleted: Boolean
disabled: Boolean
reviewedByModerator: User
@cypher(
statement: """
MATCH (this)<-[:FLAGGED]-(caseFolder:CaseFolder)<-[review:REVIEWED]-(moderator:User)
RETURN moderator
ORDER BY caseFolder.updatedAt ASC, review.updatedAt ASC
LIMIT 1
"""
)
pinned: Boolean
decidedByModerator: User @relation(name: "DECIDED", direction: "IN")
createdAt: String
updatedAt: String
language: String

View File

@ -1,25 +1,25 @@
type DECIDED {
type REVIEWED {
createdAt: String!
updatedAt: String!
# reasonCategory: ReasonCategory
# reasonDescription: String
disable: Boolean!
closed: Boolean!
uuid: ID
last: Boolean!
caseFolderId: ID!
# Wolle last: Boolean!
moderator: User
@cypher(statement: "MATCH (resource)<-[:DECIDED]-(moderator:User) RETURN moderator")
# @cypher(statement: "MATCH (resource)<-[:DECIDED]-(moderator:User) RETURN moderator")
# not yet supported
# resource: ReportResource
# @cypher(statement: "MATCH (resource)<-[:DECIDED]-(:User) RETURN resource")
type: String
@cypher(statement: "MATCH (resource)<-[:DECIDED]-(user:User) RETURN labels(resource)[0]")
# @cypher(statement: "MATCH (resource)<-[:DECIDED]-(user:User) RETURN labels(resource)[0]")
user: User
post: Post
comment: Comment
}
type Mutation {
decide(resourceId: ID!, disable: Boolean, closed: Boolean): DECIDED
decide(resourceId: ID!, disable: Boolean, closed: Boolean): REVIEWED
}

View File

@ -8,7 +8,15 @@ type User {
coverImg: String
deleted: Boolean
disabled: Boolean
decidedByModerator: User @relation(name: "DECIDED", direction: "IN")
reviewedByModerator: User
@cypher(
statement: """
MATCH (this)<-[:FLAGGED]-(caseFolder:CaseFolder)<-[review:REVIEWED]-(moderator:User)
RETURN moderator
ORDER BY caseFolder.updatedAt ASC, review.updatedAt ASC
LIMIT 1
"""
)
role: UserGroup!
publicKey: String
invitedBy: User @relation(name: "INVITED", direction: "IN")

View File

@ -29,7 +29,7 @@ export const reportListQuery = () => {
name
disabled
deleted
decidedByModerator {
reviewedByModerator {
id
slug
name
@ -60,7 +60,7 @@ export const reportListQuery = () => {
disabled
deleted
}
decidedByModerator {
reviewedByModerator {
id
slug
name
@ -84,7 +84,7 @@ export const reportListQuery = () => {
contributionsCount
commentedCount
}
decidedByModerator {
reviewedByModerator {
id
slug
name

View File

@ -101,8 +101,8 @@
>
{{ $t('moderation.reports.decideButton') }}
</ds-button>
<!-- decidedByModerator -->
<div v-if="content.resource.decidedByModerator">
<!-- reviewedByModerator -->
<div v-if="content.resource.reviewedByModerator">
<br />
<div v-if="content.caseFolderDisable">
<ds-icon name="eye-slash" class="ban" />
@ -113,7 +113,7 @@
{{ $t('moderation.reports.enabledBy') }}
</div>
<hc-user
:user="content.resource.decidedByModerator"
:user="content.resource.reviewedByModerator"
:showAvatar="false"
:trunc="30"
:date-time="content.caseFolderUpdatedAt"