mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Update to use enum in tests, seed data, etc, refactor resolver
- Extract validations to the validations middleware to clean it up - Remove resourceId since it throws an error in the mutation if the user asks for it back, and the resourceId is returned in post/comment/user.id - use writeTxResultPromise to benefit from automatic retries - more descriptive variable naming - extract cypher query to make db manipulation into script so that it can be run from the command line, at least locally.
This commit is contained in:
parent
cae897808b
commit
faf0a15aee
@ -57,11 +57,40 @@ const validateUpdatePost = async (resolve, root, args, context, info) => {
|
|||||||
return validatePost(resolve, root, args, context, info)
|
return validatePost(resolve, root, args, context, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const validateReport = async (resolve, root, args, context, info) => {
|
||||||
|
const { resourceId } = args
|
||||||
|
const { user, driver } = context
|
||||||
|
if (resourceId === user.id) throw new Error('You cannot report yourself!')
|
||||||
|
const session = driver.session()
|
||||||
|
const reportQueryRes = await session.run(
|
||||||
|
`
|
||||||
|
MATCH (:User {id:$submitterId})-[:REPORTED]->(resource {id:$resourceId})
|
||||||
|
RETURN labels(resource)[0] as label
|
||||||
|
`,
|
||||||
|
{
|
||||||
|
resourceId,
|
||||||
|
submitterId: user.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
const [existingReportedResource] = reportQueryRes.records.map(record => {
|
||||||
|
return {
|
||||||
|
label: record.get('label'),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (existingReportedResource)
|
||||||
|
throw new Error(
|
||||||
|
`You have already reported the ${existingReportedResource.label}, please only report the same ${existingReportedResource.label} once`,
|
||||||
|
)
|
||||||
|
return resolve(root, args, context, info)
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
CreateComment: validateCommentCreation,
|
CreateComment: validateCommentCreation,
|
||||||
UpdateComment: validateUpdateComment,
|
UpdateComment: validateUpdateComment,
|
||||||
CreatePost: validatePost,
|
CreatePost: validatePost,
|
||||||
UpdatePost: validateUpdatePost,
|
UpdatePost: validateUpdatePost,
|
||||||
|
report: validateReport,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,87 +1,60 @@
|
|||||||
export default {
|
export default {
|
||||||
Mutation: {
|
Mutation: {
|
||||||
report: async (_parent, params, { driver, user }, _resolveInfo) => {
|
report: async (_parent, params, { driver, user }, _resolveInfo) => {
|
||||||
|
let createdRelationshipWithNestedAttributes
|
||||||
const { resourceId, reasonCategory, reasonDescription } = params
|
const { resourceId, reasonCategory, reasonDescription } = params
|
||||||
|
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const reportProperties = {
|
const writeTxResultPromise = session.writeTransaction(async txc => {
|
||||||
createdAt: new Date().toISOString(),
|
const reportRelationshipTransactionResponse = await txc.run(
|
||||||
reasonCategory,
|
`
|
||||||
reasonDescription,
|
MATCH (submitter:User {id: $submitterId})
|
||||||
}
|
MATCH (resource {id: $resourceId})
|
||||||
|
WHERE resource:User OR resource:Comment OR resource:Post
|
||||||
const reportQueryRes = await session.run(
|
CREATE (resource)<-[report:REPORTED {createdAt: $createdAt, reasonCategory: $reasonCategory, reasonDescription: $reasonDescription}]-(submitter)
|
||||||
`
|
RETURN report, submitter, resource, labels(resource)[0] as type
|
||||||
MATCH (:User {id:$submitterId})-[:REPORTED]->(resource {id:$resourceId})
|
`,
|
||||||
RETURN labels(resource)[0] as label
|
{
|
||||||
`,
|
resourceId,
|
||||||
{
|
submitterId: user.id,
|
||||||
resourceId,
|
createdAt: new Date().toISOString(),
|
||||||
submitterId: user.id,
|
reasonCategory,
|
||||||
},
|
reasonDescription,
|
||||||
)
|
},
|
||||||
const [rep] = reportQueryRes.records.map(record => {
|
)
|
||||||
return {
|
return reportRelationshipTransactionResponse.records.map(record => ({
|
||||||
label: record.get('label'),
|
report: record.get('report'),
|
||||||
}
|
submitter: record.get('submitter'),
|
||||||
|
resource: record.get('resource').properties,
|
||||||
|
type: record.get('type'),
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
|
try {
|
||||||
if (rep) {
|
const txResult = await writeTxResultPromise
|
||||||
throw new Error(rep.label)
|
if (!txResult[0]) return null
|
||||||
}
|
const { report, submitter, resource, type } = txResult[0]
|
||||||
|
createdRelationshipWithNestedAttributes = {
|
||||||
const res = await session.run(
|
...report.properties,
|
||||||
`
|
post: null,
|
||||||
MATCH (submitter:User {id: $submitterId})
|
comment: null,
|
||||||
MATCH (resource {id: $resourceId})
|
user: null,
|
||||||
WHERE resource:User OR resource:Comment OR resource:Post
|
submitter: submitter.properties,
|
||||||
CREATE (resource)<-[report:REPORTED {createdAt: $createdAt, reasonCategory: $reasonCategory, reasonDescription: $reasonDescription}]-(submitter)
|
type,
|
||||||
RETURN report, submitter, resource, labels(resource)[0] as type
|
|
||||||
`,
|
|
||||||
{
|
|
||||||
resourceId,
|
|
||||||
submitterId: user.id,
|
|
||||||
createdAt: reportProperties.createdAt,
|
|
||||||
reasonCategory: reportProperties.reasonCategory,
|
|
||||||
reasonDescription: reportProperties.reasonDescription,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
session.close()
|
|
||||||
|
|
||||||
const [dbResponse] = res.records.map(r => {
|
|
||||||
return {
|
|
||||||
report: r.get('report'),
|
|
||||||
submitter: r.get('submitter'),
|
|
||||||
resource: r.get('resource'),
|
|
||||||
type: r.get('type'),
|
|
||||||
}
|
}
|
||||||
})
|
switch (type) {
|
||||||
if (!dbResponse) return null
|
case 'Post':
|
||||||
|
createdRelationshipWithNestedAttributes.post = resource
|
||||||
const { report, submitter, resource, type } = dbResponse
|
break
|
||||||
|
case 'Comment':
|
||||||
const response = {
|
createdRelationshipWithNestedAttributes.comment = resource
|
||||||
...report.properties,
|
break
|
||||||
post: null,
|
case 'User':
|
||||||
comment: null,
|
createdRelationshipWithNestedAttributes.user = resource
|
||||||
user: null,
|
break
|
||||||
submitter: submitter.properties,
|
}
|
||||||
type,
|
} finally {
|
||||||
|
session.close()
|
||||||
}
|
}
|
||||||
switch (type) {
|
return createdRelationshipWithNestedAttributes
|
||||||
case 'Post':
|
|
||||||
response.post = resource.properties
|
|
||||||
break
|
|
||||||
case 'Comment':
|
|
||||||
response.comment = resource.properties
|
|
||||||
break
|
|
||||||
case 'User':
|
|
||||||
response.user = resource.properties
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Query: {
|
Query: {
|
||||||
@ -113,13 +86,13 @@ export default {
|
|||||||
|
|
||||||
const responseEle = {
|
const responseEle = {
|
||||||
...report.properties,
|
...report.properties,
|
||||||
resourceId: resource.properties.id,
|
|
||||||
post: null,
|
post: null,
|
||||||
comment: null,
|
comment: null,
|
||||||
user: null,
|
user: null,
|
||||||
submitter: submitter.properties,
|
submitter: submitter.properties,
|
||||||
type,
|
type,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'Post':
|
case 'Post':
|
||||||
responseEle.post = resource.properties
|
responseEle.post = resource.properties
|
||||||
|
|||||||
@ -20,7 +20,7 @@ describe('report mutation', () => {
|
|||||||
|
|
||||||
const action = () => {
|
const action = () => {
|
||||||
reportMutation = gql`
|
reportMutation = gql`
|
||||||
mutation($resourceId: ID!, $reasonCategory: String!, $reasonDescription: String!) {
|
mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
||||||
report(
|
report(
|
||||||
resourceId: $resourceId
|
resourceId: $resourceId
|
||||||
reasonCategory: $reasonCategory
|
reasonCategory: $reasonCategory
|
||||||
@ -166,7 +166,7 @@ describe('report mutation', () => {
|
|||||||
reasonCategory: 'my_category',
|
reasonCategory: 'my_category',
|
||||||
}
|
}
|
||||||
await expect(action()).rejects.toThrow(
|
await expect(action()).rejects.toThrow(
|
||||||
'Expected a value of type "ReasonCategory" but received: "my_category"',
|
'got invalid value "my_category"; Expected type ReasonCategory',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -307,16 +307,11 @@ describe('report mutation', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('reports query', () => {
|
describe('reports query', () => {
|
||||||
let query
|
let query, mutate, authenticatedUser, moderator, user, author
|
||||||
let mutate
|
|
||||||
let authenticatedUser = null
|
|
||||||
let moderator
|
|
||||||
let user
|
|
||||||
let author
|
|
||||||
const categoryIds = ['cat9']
|
const categoryIds = ['cat9']
|
||||||
|
|
||||||
const reportMutation = gql`
|
const reportMutation = gql`
|
||||||
mutation($resourceId: ID!, $reasonCategory: String!, $reasonDescription: String!) {
|
mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
||||||
report(
|
report(
|
||||||
resourceId: $resourceId
|
resourceId: $resourceId
|
||||||
reasonCategory: $reasonCategory
|
reasonCategory: $reasonCategory
|
||||||
@ -335,7 +330,6 @@ describe('reports query', () => {
|
|||||||
submitter {
|
submitter {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
resourceId
|
|
||||||
type
|
type
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
@ -350,7 +344,8 @@ describe('reports query', () => {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(async () => {
|
||||||
|
await factory.cleanDatabase()
|
||||||
const { server } = createServer({
|
const { server } = createServer({
|
||||||
context: () => {
|
context: () => {
|
||||||
return {
|
return {
|
||||||
@ -480,7 +475,6 @@ describe('reports query', () => {
|
|||||||
submitter: expect.objectContaining({
|
submitter: expect.objectContaining({
|
||||||
id: 'user1',
|
id: 'user1',
|
||||||
}),
|
}),
|
||||||
resourceId: 'auth1',
|
|
||||||
type: 'User',
|
type: 'User',
|
||||||
user: expect.objectContaining({
|
user: expect.objectContaining({
|
||||||
id: 'auth1',
|
id: 'auth1',
|
||||||
@ -495,7 +489,6 @@ describe('reports query', () => {
|
|||||||
submitter: expect.objectContaining({
|
submitter: expect.objectContaining({
|
||||||
id: 'user1',
|
id: 'user1',
|
||||||
}),
|
}),
|
||||||
resourceId: 'p1',
|
|
||||||
type: 'Post',
|
type: 'Post',
|
||||||
user: null,
|
user: null,
|
||||||
post: expect.objectContaining({
|
post: expect.objectContaining({
|
||||||
@ -510,7 +503,6 @@ describe('reports query', () => {
|
|||||||
submitter: expect.objectContaining({
|
submitter: expect.objectContaining({
|
||||||
id: 'user1',
|
id: 'user1',
|
||||||
}),
|
}),
|
||||||
resourceId: 'c1',
|
|
||||||
type: 'Comment',
|
type: 'Comment',
|
||||||
user: null,
|
user: null,
|
||||||
post: null,
|
post: null,
|
||||||
|
|||||||
@ -5,10 +5,8 @@ type REPORTED {
|
|||||||
submitter: User
|
submitter: User
|
||||||
@cypher(statement: "MATCH (resource)<-[:REPORTED]-(user:User) RETURN user")
|
@cypher(statement: "MATCH (resource)<-[:REPORTED]-(user:User) RETURN user")
|
||||||
# not yet supported
|
# not yet supported
|
||||||
# resource: ReportReource
|
# resource: ReportResource
|
||||||
# @cypher(statement: "MATCH (resource)<-[:REPORTED]-(user:User) RETURN resource")
|
# @cypher(statement: "MATCH (resource)<-[:REPORTED]-(user:User) RETURN resource")
|
||||||
resourceId: ID
|
|
||||||
@cypher(statement: "MATCH (resource)<-[:REPORTED]-(user:User) RETURN resource {.id}")
|
|
||||||
type: String
|
type: String
|
||||||
@cypher(statement: "MATCH (resource)<-[:REPORTED]-(user:User) RETURN labels(resource)[0]")
|
@cypher(statement: "MATCH (resource)<-[:REPORTED]-(user:User) RETURN labels(resource)[0]")
|
||||||
user: User
|
user: User
|
||||||
@ -29,7 +27,7 @@ enum ReasonCategory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# not yet supported
|
# not yet supported
|
||||||
# union ReportReource = User | Post | Comment
|
# union ReportResource = User | Post | Comment
|
||||||
|
|
||||||
enum ReportOrdering {
|
enum ReportOrdering {
|
||||||
createdAt_desc
|
createdAt_desc
|
||||||
@ -40,5 +38,5 @@ type Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
report(resourceId: ID!, reasonCategory: String!, reasonDescription: String!): REPORTED
|
report(resourceId: ID!, reasonCategory: ReasonCategory!, reasonDescription: String!): REPORTED
|
||||||
}
|
}
|
||||||
|
|||||||
@ -649,7 +649,7 @@ import { gql } from '../jest/helpers'
|
|||||||
|
|
||||||
// There is no error logged or the 'try' fails if this mutation is wrong. Why?
|
// There is no error logged or the 'try' fails if this mutation is wrong. Why?
|
||||||
const reportMutation = gql`
|
const reportMutation = gql`
|
||||||
mutation($resourceId: ID!, $reasonCategory: String!, $reasonDescription: String!) {
|
mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
||||||
report(
|
report(
|
||||||
resourceId: $resourceId
|
resourceId: $resourceId
|
||||||
reasonCategory: $reasonCategory
|
reasonCategory: $reasonCategory
|
||||||
|
|||||||
@ -1,7 +0,0 @@
|
|||||||
MATCH (submitter:User)-[:REPORTED]->(report:Report)-[:REPORTED]->(resource)
|
|
||||||
DETACH DELETE report
|
|
||||||
CREATE (submitter)-[reported:REPORTED]->(resource)
|
|
||||||
SET reported.createdAt = toString(datetime())
|
|
||||||
SET reported.reasonCategory = 'other'
|
|
||||||
SET reported.reasonDescription = '!!! Created automatically to ensure database consistency! Date-time is this creation date and time.'
|
|
||||||
RETURN reported
|
|
||||||
26
neo4j/change_report_node_to_relationship.sh
Executable file
26
neo4j/change_report_node_to_relationship.sh
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
#!/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 "
|
||||||
|
MATCH (submitter:User)-[:REPORTED]->(report:Report)-[:REPORTED]->(resource)
|
||||||
|
DETACH DELETE report
|
||||||
|
CREATE (submitter)-[reported:REPORTED]->(resource)
|
||||||
|
SET reported.createdAt = toString(datetime())
|
||||||
|
SET reported.reasonCategory = 'other'
|
||||||
|
SET reported.reasonDescription = '!!! Created automatically to ensure database consistency! createdAt is when the database manipulation happened.'
|
||||||
|
RETURN reported;
|
||||||
|
" | cypher-shell
|
||||||
Loading…
x
Reference in New Issue
Block a user