const errors = require('../../errors'); const { CREATE_ACTION, DELETE_ACTION } = require('../../perms/constants'); const { IGNORE_FLAGS_AGAINST_STAFF } = require('../../config'); /** * getActionItem will return the item that is associated with the given action. * If it does not exist, it will throw an error. * * @param {Object} ctx the graphql context for the request * @param {Object} action the action being performed * @return {Promise} resolves to the referenced item */ const getActionItem = async (ctx, { item_id, item_type }) => { const { loaders: { Comments, Users } } = ctx; switch (item_type) { case 'COMMENTS': return Comments.get.load(item_id); case 'USERS': return Users.getByID.load(item_id); default: return null; } }; /** * Creates an action on a item. If the item is a user flag, sets the user's status to * pending. * * @param {Object} ctx the graphql context for the request * @param {Object} action the action being created * @return {Promise} resolves to the action created */ const createAction = async ( ctx, { item_id, item_type, action_type, group_id, metadata = {} } ) => { const { user = {}, pubsub, connectors: { services: { Actions } } } = ctx; // Gets the item referenced by the action. const item = await getActionItem(ctx, { item_id, item_type }); if (!item || item === null) { throw errors.ErrNotFound; } // If we are ignoring flags against staff, ensure that the target isn't a // staff member. if (IGNORE_FLAGS_AGAINST_STAFF) { if (action_type === 'FLAG') { // If the item is a user, and this is a flag. Check to see if they are // staff, if they are, don't permit the flag. if (item_type === 'USERS' && item.isStaff()) { return null; } } } if (action_type === 'FLAG' && item_type === 'USERS') { // The item is a user, and this is a flag. Check to see if they are staff, // if they are, don't permit the flag. if (item.isStaff()) { throw errors.ErrNotAuthorized; } } // Create the action itself. let action = await Actions.create({ item_id, item_type, user_id: user.id, group_id, action_type, metadata, }); if (action_type === 'FLAG') { switch (item_type) { case 'COMMENTS': // The item is a comment, and this is a flag. Push that the comment was // flagged, don't wait for it to finish. pubsub.publish('commentFlagged', item); break; case 'USERS': // The item is a user, and this is a flag. Push that the user was // flagged, don't wait for it to finish. pubsub.publish('usernameFlagged', item); break; default: } } return action; }; /** * Deletes an action based on the user id if the user owns that action. * * @param {Object} user the user performing the request * @param {String} id the id of the action to delete * @return {Promise} resolves to the deleted action, or null if not found. */ const deleteAction = (ctx, { id }) => { const { user, connectors: { services: { Actions } } } = ctx; return Actions.delete({ id, user_id: user.id }); }; module.exports = ctx => { let mutators = { Action: { create: () => Promise.reject(errors.ErrNotAuthorized), delete: () => Promise.reject(errors.ErrNotAuthorized), }, }; if (ctx.user && ctx.user.can(CREATE_ACTION)) { mutators.Action.create = action => createAction(ctx, action); } if (ctx.user && ctx.user.can(DELETE_ACTION)) { mutators.Action.delete = action => deleteAction(ctx, action); } return mutators; };