diff --git a/routes/api/graph/index.js b/routes/api/graph/index.js index 1e737fd53..55f177aa9 100644 --- a/routes/api/graph/index.js +++ b/routes/api/graph/index.js @@ -10,9 +10,15 @@ const router = express.Router(); // GraphQL endpoint. router.use('/ql', apollo.graphqlExpress((req) => { - let context = {req}; + let context = {}; + // Load the current logged in user to `user`, otherwise this'll be null. + context.user = req.user; + + // Create the loaders. context.loaders = loaders(context); + + // Create the mutators. context.mutators = mutators(context); return { diff --git a/routes/api/graph/loaders.js b/routes/api/graph/loaders.js index d6534450c..f5f0046a0 100644 --- a/routes/api/graph/loaders.js +++ b/routes/api/graph/loaders.js @@ -122,7 +122,7 @@ const createLoaders = (context) => ({ getByAssetID: new DataLoader((ids) => genCommentsByAssetID(ids)), }, Actions: { - getByID: new DataLoader((ids) => genActionsByID(ids, context.req.user)), + getByID: new DataLoader((ids) => genActionsByID(ids, context.user)), }, Users: { getByID: new DataLoader((ids) => User.findByIdArray(ids)) diff --git a/routes/api/graph/mutators.js b/routes/api/graph/mutators.js index 2a9f9bc64..e1b65a09e 100644 --- a/routes/api/graph/mutators.js +++ b/routes/api/graph/mutators.js @@ -1,20 +1,22 @@ const errors = require('../../../errors'); +const Action = require('../../../models/action'); const Asset = require('../../../models/asset'); const Comment = require('../../../models/comment'); +const User = require('../../../models/user'); const Wordlist = require('../../../services/wordlist'); /** * Creates a new comment. - * @param {Object} context a GraphQL context + * @param {Object} user the user performing the request * @param {String} body body of the comment * @param {String} asset_id asset for the comment * @param {String} parent_id optional parent of the comment * @param {Object} [wordlist={}] results for the wordlist analysis * @return {Promise} resolves to the created comment */ -const createComment = (context, {body, asset_id, parent_id = null}, wordlist = {}) => { +const createComment = ({user}, {body, asset_id, parent_id = null}, wordlist = {}) => { // Decide the status based on whether or not the current asset/settings // has pre-mod enabled or not. If the comment was rejected based on the @@ -58,7 +60,7 @@ const createComment = (context, {body, asset_id, parent_id = null}, wordlist = { asset_id, parent_id, status, - author_id: context.req.user.id + author_id: user.id })) .then((comment) => { if (wordlist.suspect) { @@ -86,8 +88,78 @@ const filterNewComment = (context, {body}) => { return wl.load().then(() => wl.scan('body', body)); }; -module.exports = (context) => ({ - createComment: (comment) => filterNewComment(context, comment).then((wordlist) => { - return createComment(context, comment, wordlist); - }) -}); +/** + * Creates an action on a item. + * @param {Object} user the user performing the request + * @param {String} item_id id of the item to add the action to + * @param {String} item_type type of the item + * @param {String} action_type type of the action + * @return {Promise} resolves to the action created + */ +const createAction = ({user}, {item_id, item_type, action_type}) => { + return Action.insertUserAction({ + item_id, + item_type, + user_id: user.id, + action_type + }); +}; + +/** + * Deletes an action based on the user id if the user owns that action. + * @param {Object} user the user performing the request + * @param {[type]} id [description] + * @return {[type]} [description] + */ +const deleteAction = ({user}, {id}) => { + return Action.remove({ + id, + user_id: user.id + }); +}; + +/** + * Updates a users settings. + * @param {[type]} user [description] + * @param {[type]} bio [description] + * @return {[type]} [description] + */ +const updateUserSettings = ({user}, {bio}) => { + return User.updateSettings(user.id, {bio}); +}; + +module.exports = (context) => { + + // TODO: refactor to something that'll return an error in the event an attempt + // is made to mutate state while not logged in. There's got to be a better way + // to do this. + if (context.user) { + return { + Comment: { + create: (comment) => filterNewComment(context, comment).then((wordlist) => { + return createComment(context, comment, wordlist); + }) + }, + Action: { + create: (action) => createAction(context, action), + delete: (action) => deleteAction(context, action) + }, + User: { + updateSettings: (settings) => updateUserSettings(context, settings) + } + }; + } + + return { + Comment: { + create: () => {} + }, + Action: { + create: () => {}, + delete: () => {} + }, + User: { + updateSettings: () => {} + } + }; +}; diff --git a/routes/api/graph/resolvers.js b/routes/api/graph/resolvers.js index 58107e5b2..4c5f553eb 100644 --- a/routes/api/graph/resolvers.js +++ b/routes/api/graph/resolvers.js @@ -9,13 +9,22 @@ module.exports = { settings(_, args, {loaders}) { return loaders.Settings.load(); }, - me(_, args, {req}) { - return req.user; + me(_, args, {user}) { + return user; } }, Mutation: { createComment(_, {asset_id, parent_id, body}, {mutators}) { - return mutators.createComment({asset_id, parent_id, body}); + return mutators.Comment.create({asset_id, parent_id, body}); + }, + createAction(_, {action}, {mutators}) { + return mutators.Action.create(action); + }, + deleteAction(_, {id}, {mutators}) { + return mutators.Action.delete({id}); + }, + updateUserSettings(_, {settings}, {mutators}) { + return mutators.User.updateSettings(settings); } }, Asset: { @@ -36,6 +45,37 @@ module.exports = { }); } }, + Action: { + action_type({action_type}) { + + // TODO: remove once we cast the data model to have uppercase action + // types. + return action_type.toUpperCase(); + }, + item_type({item_type}) { + + // TODO: remove once we cast the data model to have uppercase item + // types. + return item_type.toUpperCase(); + }, + user({user_id}, _, {loaders}) { + return loaders.Users.getByID.load(user_id); + } + }, + ActionSummary: { + action_type({action_type}) { + + // TODO: remove once we cast the data model to have uppercase action + // types. + return action_type.toUpperCase(); + }, + item_type({item_type}) { + + // TODO: remove once we cast the data model to have uppercase item + // types. + return item_type.toUpperCase(); + } + }, User: { actions({id}, _, {loaders}) { return loaders.Actions.getByID.load(id); diff --git a/routes/api/graph/typeDefs.js b/routes/api/graph/typeDefs.js index d2a9e7758..e0cff85ee 100644 --- a/routes/api/graph/typeDefs.js +++ b/routes/api/graph/typeDefs.js @@ -6,7 +6,7 @@ type UserSettings { type User { id: ID! displayName: String! - actions: [Action] + actions: [ActionSummary] settings: UserSettings } @@ -15,19 +15,42 @@ type Comment { body: String! user: User replies(limit: Int = 3): [Comment] - actions: [Action] + actions: [ActionSummary] } -type Action { +enum ITEM_TYPE { + ASSETS + COMMENTS + USERS +} + +enum ACTION_TYPE { + LIKE + FLAG +} + +interface ActionInterface { + action_type: ACTION_TYPE! + item_type: ITEM_TYPE! +} + +type Action implements ActionInterface { id: ID! item_id: ID! - action_type: String! - count: Int - current_user: Action + action_type: ACTION_TYPE! + item_type: ITEM_TYPE! + user: User! updated_at: String created_at: String } +type ActionSummary implements ActionInterface { + action_type: ACTION_TYPE! + item_type: ITEM_TYPE! + count: Int + current_user: Action +} + type Settings { moderation: String infoBoxEnable: Boolean @@ -55,8 +78,23 @@ type Query { me: User } +input CreateActionInput { + action_type: ACTION_TYPE! + item_type: ITEM_TYPE! + item_id: ID! +} + +input UpdateUserSettingsInput { + bio: String! +} + type Mutation { createComment(asset_id: ID!, parent_id: ID, body: String!): Comment + createAction(action: CreateActionInput!): Action + deleteAction(id: ID!): Boolean + + updateUserSettings(settings: UpdateUserSettingsInput!): Boolean + } schema {