mattwr18 ba3e9e1025 Rename blacklist/whitelist to mute/unmute
- Follow @roschaefer suggestion in issue
- This can be reverted if we decide otherwise
2020-01-08 18:04:26 +01:00

267 lines
9.4 KiB
JavaScript

import { neo4jgraphql } from 'neo4j-graphql-js'
import fileUpload from './fileUpload'
import { getNeode } from '../../bootstrap/neo4j'
import { UserInputError, ForbiddenError } from 'apollo-server'
import Resolver from './helpers/Resolver'
import log from './helpers/databaseLogger'
import createOrUpdateLocations from './users/location'
const neode = getNeode()
export const getMutedUsers = async context => {
const { neode } = context
const userModel = neode.model('User')
let mutedUsers = neode
.query()
.match('user', userModel)
.where('user.id', context.user.id)
.relationship(userModel.relationships().get('muted'))
.to('muted', userModel)
.return('muted')
mutedUsers = await mutedUsers.execute()
mutedUsers = mutedUsers.records.map(r => r.get('muted').properties)
return mutedUsers
}
export default {
Query: {
mutedUsers: async (object, args, context, resolveInfo) => {
try {
return getMutedUsers(context)
} catch (e) {
throw new UserInputError(e.message)
}
},
User: async (object, args, context, resolveInfo) => {
const { email } = args
if (email) {
let session
try {
session = context.driver.session()
const readTxResult = await session.readTransaction(txc => {
const result = txc.run(
`
MATCH (user:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $args.email})
RETURN user`,
{ args },
)
return result
})
return readTxResult.records.map(r => r.get('user').properties)
} finally {
session.close()
}
}
return neo4jgraphql(object, args, context, resolveInfo)
},
},
Mutation: {
muteUser: async (_parent, params, context, _resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === params.id) return null
await neode.cypher(
`
MATCH(u:User {id: $currentUser.id})-[previousRelationship:FOLLOWS]->(b:User {id: $params.id})
DELETE previousRelationship
`,
{ currentUser, params },
)
const [user, mutedUser] = await Promise.all([
neode.find('User', currentUser.id),
neode.find('User', params.id),
])
await user.relateTo(mutedUser, 'muted')
return mutedUser.toJson()
},
unmuteUser: async (_parent, params, context, _resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === params.id) return null
await neode.cypher(
`
MATCH(u:User {id: $currentUser.id})-[previousRelationship:MUTED]->(b:User {id: $params.id})
DELETE previousRelationship
`,
{ currentUser, params },
)
const unmutedUser = await neode.find('User', params.id)
return unmutedUser.toJson()
},
block: async (object, args, context, resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === args.id) return null
await neode.cypher(
`
MATCH(u:User {id: $currentUser.id})-[r:FOLLOWS]->(b:User {id: $args.id})
DELETE r
`,
{ currentUser, args },
)
const [user, blockedUser] = await Promise.all([
neode.find('User', currentUser.id),
neode.find('User', args.id),
])
await user.relateTo(blockedUser, 'blocked')
return blockedUser.toJson()
},
unblock: async (object, args, context, resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === args.id) return null
await neode.cypher(
`
MATCH(u:User {id: $currentUser.id})-[r:BLOCKED]->(b:User {id: $args.id})
DELETE r
`,
{ currentUser, args },
)
const blockedUser = await neode.find('User', args.id)
return blockedUser.toJson()
},
UpdateUser: async (_parent, params, context, _resolveInfo) => {
const { termsAndConditionsAgreedVersion } = params
if (termsAndConditionsAgreedVersion) {
const regEx = new RegExp(/^[0-9]+\.[0-9]+\.[0-9]+$/g)
if (!regEx.test(termsAndConditionsAgreedVersion)) {
throw new ForbiddenError('Invalid version format!')
}
params.termsAndConditionsAgreedAt = new Date().toISOString()
}
params = await fileUpload(params, { file: 'avatarUpload', url: 'avatar' })
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async transaction => {
const updateUserTransactionResponse = await transaction.run(
`
MATCH (user:User {id: $params.id})
SET user += $params
SET user.updatedAt = toString(datetime())
RETURN user
`,
{ params },
)
return updateUserTransactionResponse.records.map(record => record.get('user').properties)
})
try {
const [user] = await writeTxResultPromise
await createOrUpdateLocations(params.id, params.locationName, session)
return user
} catch (error) {
throw new UserInputError(error.message)
} finally {
session.close()
}
},
DeleteUser: async (object, params, context, resolveInfo) => {
const { resource } = params
const session = context.driver.session()
try {
if (resource && resource.length) {
await session.writeTransaction(transaction => {
resource.map(node => {
return transaction.run(
`
MATCH (resource:${node})<-[:WROTE]-(author:User {id: $userId})
OPTIONAL MATCH (resource)<-[:COMMENTS]-(comment:Comment)
SET resource.deleted = true
SET resource.content = 'UNAVAILABLE'
SET resource.contentExcerpt = 'UNAVAILABLE'
SET comment.deleted = true
RETURN author
`,
{
userId: context.user.id,
},
)
})
})
}
const deleteUserTxResultPromise = session.writeTransaction(async transaction => {
const deleteUserTransactionResponse = await transaction.run(
`
MATCH (user:User {id: $userId})
SET user.deleted = true
SET user.name = 'UNAVAILABLE'
SET user.about = 'UNAVAILABLE'
WITH user
OPTIONAL MATCH (user)<-[:BELONGS_TO]-(email:EmailAddress)
DETACH DELETE email
WITH user
OPTIONAL MATCH (user)<-[:OWNED_BY]-(socialMedia:SocialMedia)
DETACH DELETE socialMedia
RETURN user
`,
{ userId: context.user.id },
)
log(deleteUserTransactionResponse)
return deleteUserTransactionResponse.records.map(record => record.get('user').properties)
})
const [user] = await deleteUserTxResultPromise
return user
} finally {
session.close()
}
},
},
User: {
email: async (parent, params, context, resolveInfo) => {
if (typeof parent.email !== 'undefined') return parent.email
const { id } = parent
const statement = `MATCH(u:User {id: {id}})-[:PRIMARY_EMAIL]->(e:EmailAddress) RETURN e`
const result = await neode.cypher(statement, { id })
const [{ email }] = result.records.map(r => r.get('e').properties)
return email
},
...Resolver('User', {
undefinedToNull: [
'actorId',
'avatar',
'coverImg',
'deleted',
'disabled',
'locationName',
'about',
'termsAndConditionsAgreedVersion',
'termsAndConditionsAgreedAt',
'allowEmbedIframes',
'showShoutsPublicly',
'locale',
],
boolean: {
followedByCurrentUser:
'MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
isBlocked:
'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',
},
count: {
contributionsCount:
'-[:WROTE]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
friendsCount: '<-[:FRIENDS]->(related:User)',
followingCount: '-[:FOLLOWS]->(related:User)',
followedByCount: '<-[:FOLLOWS]-(related:User)',
commentedCount:
'-[:WROTE]->(c:Comment)-[:COMMENTS]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
shoutedCount:
'-[:SHOUTED]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
badgesCount: '<-[:REWARDED]-(related:Badge)',
},
hasOne: {
invitedBy: '<-[:INVITED]-(related:User)',
location: '-[:IS_IN]->(related:Location)',
},
hasMany: {
followedBy: '<-[:FOLLOWS]-(related:User)',
following: '-[:FOLLOWS]->(related:User)',
friends: '-[:FRIENDS]-(related:User)',
socialMedia: '<-[:OWNED_BY]-(related:SocialMedia)',
contributions: '-[:WROTE]->(related:Post)',
comments: '-[:WROTE]->(related:Comment)',
shouted: '-[:SHOUTED]->(related:Post)',
categories: '-[:CATEGORIZED]->(related:Category)',
badges: '<-[:REWARDED]-(related:Badge)',
},
}),
},
}