From c05065f684680af66561a485afd7b10e54d338bd Mon Sep 17 00:00:00 2001 From: mattwr18 Date: Mon, 2 Dec 2019 13:38:35 +0100 Subject: [PATCH] Refactor backend - update script - use readTxResult for validateReview - favor more verbose variables - do not set review.closed as we close the report and the rule at the moment is set to 'latestReviewUpdatedAtRules' rule, so it's clear the last review must have been the one that closed the report --- .../validation/validationMiddleware.js | 65 ++++++++++--------- backend/src/schema/resolvers/moderation.js | 4 +- backend/src/schema/resolvers/reports.js | 4 +- ...ge_disabled_relationship_to_report_node.sh | 55 ++++++++++++++++ 4 files changed, 95 insertions(+), 33 deletions(-) create mode 100755 neo4j/change_disabled_relationship_to_report_node.sh diff --git a/backend/src/middleware/validation/validationMiddleware.js b/backend/src/middleware/validation/validationMiddleware.js index b8c539a0f..4dcfb6ed4 100644 --- a/backend/src/middleware/validation/validationMiddleware.js +++ b/backend/src/middleware/validation/validationMiddleware.js @@ -66,43 +66,50 @@ const validateReport = async (resolve, root, args, context, info) => { const validateReview = async (resolve, root, args, context, info) => { const { resourceId } = args + let existingReportedResource const { user, driver } = context if (resourceId === user.id) throw new Error('You cannot review yourself!') const session = driver.session() - const reportQueryRes = await session.run( - ` - MATCH (resource {id: $resourceId}) - WHERE resource:User OR resource:Post OR resource:Comment - OPTIONAL MATCH (:User)-[filed:FILED]->(:Report {closed: false})-[:BELONGS_TO]->(resource) - OPTIONAL MATCH (resource)<-[:WROTE]-(author:User) - RETURN labels(resource)[0] AS label, author, filed - `, - { - resourceId, - submitterId: user.id, - }, - ) - session.close() - const [existingReportedResource] = reportQueryRes.records.map(record => { - return { + const reportReadTxPromise = session.writeTransaction(async txc => { + const validateReviewTransactionResponse = await txc.run( + ` + MATCH (resource {id: $resourceId}) + WHERE resource:User OR resource:Post OR resource:Comment + OPTIONAL MATCH (:User)-[filed:FILED]->(:Report {closed: false})-[:BELONGS_TO]->(resource) + OPTIONAL MATCH (resource)<-[:WROTE]-(author:User) + RETURN labels(resource)[0] AS label, author, filed + `, + { + resourceId, + submitterId: user.id, + }, + ) + return validateReviewTransactionResponse.records.map(record => ({ label: record.get('label'), author: record.get('author'), filed: record.get('filed'), - } + })) }) + try { + const txResult = await reportReadTxPromise + existingReportedResource = txResult + if (!existingReportedResource || !existingReportedResource.length) + throw new Error(`Resource not found or is not a Post|Comment|User!`) + existingReportedResource = existingReportedResource[0] + if (!existingReportedResource.filed) + throw new Error( + `Before starting the review process, please report the ${existingReportedResource.label}!`, + ) + const authorId = + existingReportedResource.label !== 'User' && existingReportedResource.author + ? existingReportedResource.author.properties.id + : null + if (authorId && authorId === user.id) + throw new Error(`You cannot review your own ${existingReportedResource.label}!`) + } finally { + session.close() + } - if (!existingReportedResource) - throw new Error(`Resource not found or is not a Post|Comment|User!`) - if (!existingReportedResource.filed) - throw new Error( - `Before starting the review process, please report the ${existingReportedResource.label}!`, - ) - const authorId = - existingReportedResource.label !== 'User' && existingReportedResource.author - ? existingReportedResource.author.properties.id - : null - if (authorId && authorId === user.id) - throw new Error(`You cannot review your own ${existingReportedResource.label}!`) return resolve(root, args, context, info) } diff --git a/backend/src/schema/resolvers/moderation.js b/backend/src/schema/resolvers/moderation.js index b5aed1e14..4bdf82d50 100644 --- a/backend/src/schema/resolvers/moderation.js +++ b/backend/src/schema/resolvers/moderation.js @@ -24,8 +24,8 @@ export default { MERGE (report)<-[review:REVIEWED]-(moderator) ON CREATE SET review.createdAt = $dateTime, review.updatedAt = review.createdAt ON MATCH SET review.updatedAt = $dateTime - SET review.disable = $params.disable, review.closed = $params.closed - SET report.updatedAt = $dateTime, report.closed = review.closed + SET review.disable = $params.disable + SET report.updatedAt = $dateTime, report.closed = $params.closed SET resource.disabled = review.disable RETURN review, report, resource, labels(resource)[0] AS type diff --git a/backend/src/schema/resolvers/reports.js b/backend/src/schema/resolvers/reports.js index c3ac3caac..fc93229ae 100644 --- a/backend/src/schema/resolvers/reports.js +++ b/backend/src/schema/resolvers/reports.js @@ -63,7 +63,7 @@ export default { default: orderByClause = '' } - const readTxPromise = session.readTransaction(async tx => { + const reportReadTxPromise = session.readTransaction(async tx => { const allReportsTransactionResponse = await tx.run( ` MATCH (submitter:User)-[filed:FILED]->(report:Report)-[:BELONGS_TO]->(resource) @@ -76,7 +76,7 @@ export default { return allReportsTransactionResponse.records.map(transformReturnType) }) try { - const txResult = await readTxPromise + const txResult = await reportReadTxPromise if (!txResult[0]) return null reports = txResult } finally { diff --git a/neo4j/change_disabled_relationship_to_report_node.sh b/neo4j/change_disabled_relationship_to_report_node.sh new file mode 100755 index 000000000..55dd1333c --- /dev/null +++ b/neo4j/change_disabled_relationship_to_report_node.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +ENV_FILE=$(dirname "$0")/.env +[[ -f "$ENV_FILE" ]] && source "$ENV_FILE" + +if [ -z "$NEO4J_USERNAME" ] || [ -z "$NEO4J_PASSWORD" ]; then + echo "Please set NEO4J_USERNAME and NEO4J_PASSWORD environment variables." + echo "Database manipulation is not possible without connecting to the database." + echo "E.g. you could \`cp .env.template .env\` unless you run the script in a docker container" +fi + +until echo 'RETURN "Connection successful" as info;' | cypher-shell +do + echo "Connecting to neo4j failed, trying again..." + sleep 1 +done + +echo " +// convert old DISABLED to new REVIEWED-Report-BELONGS_TO structure +MATCH (moderator:User)-[disabled:DISABLED]->(disabledResource) +WHERE disabledResource:User OR disabledResource:Comment OR disabledResource:Post +DELETE disabled +CREATE (moderator)-[review:REVIEWED]->(report:Report)-[:BELONGS_TO]->(disabledResource) +SET review.createdAt = toString(datetime()), review.updatedAt = review.createdAt, review.disable = true +SET report.id = randomUUID(), report.createdAt = toString(datetime()), report.updatedAt = report.createdAt, report.rule = 'latestReviewUpdatedAtRules', report.closed = false + +// if disabledResource has no filed report, then create a moderators default filed report +WITH moderator, disabledResource, report +OPTIONAL MATCH (disabledResourceReporter:User)-[existingFiledReport:FILED]->(disabledResource) +FOREACH(disabledResource IN CASE WHEN existingFiledReport IS NULL THEN [1] ELSE [] END | + CREATE (moderator)-[addModeratorReport:FILED]->(report) + SET addModeratorReport.createdAt = toString(datetime()), addModeratorReport.reasonCategory = 'other', addModeratorReport.reasonDescription = 'Old DISABLED relation had no now mandatory report !!! Created automatically to ensure database consistency! Creation date is when the database manipulation happened.' +) +FOREACH(disabledResource IN CASE WHEN existingFiledReport IS NOT NULL THEN [1] ELSE [] END | + CREATE (disabledResourceReporter)-[moveModeratorReport:FILED]->(report) + SET moveModeratorReport = existingFiledReport + DELETE existingFiledReport +) + +RETURN disabledResource {.id}; +" | cypher-shell + +echo " +// for FILED resources without DISABLED relation which are handled above, create new FILED-Report-BELONGS_TO structure +MATCH (reporter:User)-[oldReport:REPORTED]->(notDisabledResource) +WHERE notDisabledResource:User OR notDisabledResource:Comment OR notDisabledResource:Post +MERGE (report:Report)-[:BELONGS_TO]->(notDisabledResource) +ON CREATE SET report.id = randomUUID(), report.createdAt = toString(datetime()), report.updatedAt = report.createdAt, report.rule = 'latestReviewUpdatedAtRules', report.closed = false +CREATE (reporter)-[filed:FILED]->(report) +SET report = oldReport +DELETE oldReport + +RETURN notDisabledResource {.id}; +" | cypher-shell +