From fb9bedc57b805e3ab14fb285a53445e47661cc9c Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 8 Feb 2017 17:41:11 -0700 Subject: [PATCH 1/6] Added new action rep, refactored mutations --- graph/loaders/actions.js | 10 + graph/mutators/action.js | 3 +- graph/resolvers/action.js | 8 + graph/resolvers/action_summary.js | 11 +- graph/resolvers/comment.js | 11 +- graph/resolvers/flag_action.js | 9 + graph/resolvers/flag_action_summary.js | 7 + graph/resolvers/generic_user_error.js | 3 + graph/resolvers/index.js | 18 +- graph/resolvers/like_action.js | 5 + graph/resolvers/root_mutation.js | 27 +- graph/resolvers/user.js | 10 +- graph/resolvers/user_error.js | 11 + graph/resolvers/validation_user_error.js | 3 + graph/schema.js | 7 + graph/typeDefs.graphql | 412 ++++++++++++++++++----- models/action.js | 6 + routes/api/comments/index.js | 17 - services/actions.js | 113 ++++--- test/routes/api/comments/index.js | 76 ----- 20 files changed, 527 insertions(+), 240 deletions(-) create mode 100644 graph/resolvers/flag_action.js create mode 100644 graph/resolvers/flag_action_summary.js create mode 100644 graph/resolvers/generic_user_error.js create mode 100644 graph/resolvers/like_action.js create mode 100644 graph/resolvers/user_error.js create mode 100644 graph/resolvers/validation_user_error.js diff --git a/graph/loaders/actions.js b/graph/loaders/actions.js index c41f172196..2dd6472a08 100644 --- a/graph/loaders/actions.js +++ b/graph/loaders/actions.js @@ -5,6 +5,15 @@ const util = require('./util'); const ActionsService = require('../../services/actions'); const ActionModel = require('../../models/action'); +/** + * Gets actions based on their item id's. + */ +const genActionsByItemID = (_, item_ids) => { + return ActionsService + .findByItemIdArray(item_ids) + .then(util.arrayJoinBy(item_ids, 'item_id')); +}; + /** * Looks up actions based on the requested id's all bounded by the user. * @param {Object} context the context of the request @@ -35,6 +44,7 @@ const getItemIdsByActionTypeAndItemType = (_, action_type, item_type) => { */ module.exports = (context) => ({ Actions: { + getByID: new DataLoader((ids) => genActionsByItemID(context, ids)), getSummariesByItemID: new DataLoader((ids) => genActionSummariessByItemID(context, ids)), getByTypes: ({action_type, item_type}) => getItemIdsByActionTypeAndItemType(context, action_type, item_type) } diff --git a/graph/mutators/action.js b/graph/mutators/action.js index b5264fd498..12decd8cbe 100644 --- a/graph/mutators/action.js +++ b/graph/mutators/action.js @@ -11,11 +11,12 @@ const UsersService = require('../../services/users'); * @param {String} action_type type of the action * @return {Promise} resolves to the action created */ -const createAction = ({user = {}}, {item_id, item_type, action_type, metadata = {}}) => { +const createAction = ({user = {}}, {item_id, item_type, action_type, group_id, metadata = {}}) => { return ActionsService.insertUserAction({ item_id, item_type, user_id: user.id, + group_id, action_type, metadata }).then((action) => { diff --git a/graph/resolvers/action.js b/graph/resolvers/action.js index f8aa602456..0962f59af0 100644 --- a/graph/resolvers/action.js +++ b/graph/resolvers/action.js @@ -1,4 +1,12 @@ const Action = { + __resolveType({action_type}) { + switch (action_type) { + case 'FLAG': + return 'FlagAction'; + case 'LIKE': + return 'LikeAction'; + } + }, // This will load the user for the specific action. We'll limit this to the // admin users only or the current logged in user. diff --git a/graph/resolvers/action_summary.js b/graph/resolvers/action_summary.js index 48eff50c78..93848d285e 100644 --- a/graph/resolvers/action_summary.js +++ b/graph/resolvers/action_summary.js @@ -1,3 +1,12 @@ -const ActionSummary = {}; +const ActionSummary = { + __resolveType({action_type}) { + switch (action_type) { + case 'FLAG': + return 'FlagActionSummary'; + case 'LIKE': + return 'LikeActionSummary'; + } + }, +}; module.exports = ActionSummary; diff --git a/graph/resolvers/comment.js b/graph/resolvers/comment.js index b3d79225e5..41c10750ba 100644 --- a/graph/resolvers/comment.js +++ b/graph/resolvers/comment.js @@ -13,7 +13,16 @@ const Comment = { replyCount({id}, _, {loaders: {Comments}}) { return Comments.countByParentID.load(id); }, - actions({id}, _, {loaders: {Actions}}) { + actions({id}, _, {user, loaders: {Actions}}) { + + // Only return the actions if the user is not an admin. + if (user && user.hasRoles('ADMIN')) { + return Actions.getByID.load(id); + } + + return null; + }, + action_summaries({id}, _, {loaders: {Actions}}) { return Actions.getSummariesByItemID.load(id); }, asset({asset_id}, _, {loaders: {Assets}}) { diff --git a/graph/resolvers/flag_action.js b/graph/resolvers/flag_action.js new file mode 100644 index 0000000000..44cf7a4101 --- /dev/null +++ b/graph/resolvers/flag_action.js @@ -0,0 +1,9 @@ +const FlagAction = { + + // Stored in the metadata, extract and return. + reason({metadata: {reason}}) { + return reason; + } +}; + +module.exports = FlagAction; diff --git a/graph/resolvers/flag_action_summary.js b/graph/resolvers/flag_action_summary.js new file mode 100644 index 0000000000..58dda689e0 --- /dev/null +++ b/graph/resolvers/flag_action_summary.js @@ -0,0 +1,7 @@ +const FlagActionSummary = { + reason({group_id}) { + return group_id; + } +}; + +module.exports = FlagActionSummary; diff --git a/graph/resolvers/generic_user_error.js b/graph/resolvers/generic_user_error.js new file mode 100644 index 0000000000..45b656f5b0 --- /dev/null +++ b/graph/resolvers/generic_user_error.js @@ -0,0 +1,3 @@ +const GenericUserError = {}; + +module.exports = GenericUserError; diff --git a/graph/resolvers/index.js b/graph/resolvers/index.js index f25664f348..84dd10fdcc 100644 --- a/graph/resolvers/index.js +++ b/graph/resolvers/index.js @@ -1,21 +1,33 @@ -const Action = require('./action'); const ActionSummary = require('./action_summary'); +const Action = require('./action'); const Asset = require('./asset'); const Comment = require('./comment'); const Date = require('./date'); +const FlagActionSummary = require('./flag_action_summary'); +const FlagAction = require('./flag_action'); +const GenericUserError = require('./generic_user_error'); +const LikeAction = require('./like_action'); const RootMutation = require('./root_mutation'); const RootQuery = require('./root_query'); const Settings = require('./settings'); +const UserError = require('./user_error'); const User = require('./user'); +const ValidationUserError = require('./validation_user_error'); module.exports = { - Action, ActionSummary, + Action, Asset, Comment, Date, + FlagActionSummary, + FlagAction, + GenericUserError, + LikeAction, RootMutation, RootQuery, Settings, - User + UserError, + User, + ValidationUserError, }; diff --git a/graph/resolvers/like_action.js b/graph/resolvers/like_action.js new file mode 100644 index 0000000000..12d10f81e9 --- /dev/null +++ b/graph/resolvers/like_action.js @@ -0,0 +1,5 @@ +const LikeAction = { + +}; + +module.exports = LikeAction; diff --git a/graph/resolvers/root_mutation.js b/graph/resolvers/root_mutation.js index ef46b839ab..8369b72817 100644 --- a/graph/resolvers/root_mutation.js +++ b/graph/resolvers/root_mutation.js @@ -1,12 +1,31 @@ +/** + * Wraps up a promise to return an object with the resolution of the promise + * keyed at `key` or an error caught at `errors`. + */ +const wrapResponse = (key) => (promise) => { + return promise.then((value) => { + let res = {}; + if (key) { + res[key] = value; + } + return res; + }).catch((err) => ({ + errors: [err] + })); +}; + const RootMutation = { createComment(_, {asset_id, parent_id, body}, {mutators: {Comment}}) { - return Comment.create({asset_id, parent_id, body}); + return wrapResponse('comment')(Comment.create({asset_id, parent_id, body})); + }, + createLike(_, {like: {item_id, item_type}}, {mutators: {Action}}) { + return wrapResponse('like')(Action.create({item_id, item_type, action_type: 'LIKE'})); }, - createAction(_, {action}, {mutators: {Action}}) { - return Action.create(action); + createFlag(_, {flag: {item_id, item_type, reason, message}}, {mutators: {Action}}) { + return wrapResponse('flag')(Action.create({item_id, item_type, action_type: 'FLAG', group_id: reason, metadata: {message}})); }, deleteAction(_, {id}, {mutators: {Action}}) { - return Action.delete({id}); + return wrapResponse(null)(Action.delete({id})); }, }; diff --git a/graph/resolvers/user.js b/graph/resolvers/user.js index c75cdbf54f..13efe29e94 100644 --- a/graph/resolvers/user.js +++ b/graph/resolvers/user.js @@ -1,7 +1,15 @@ const User = { - actions({id}, _, {loaders: {Actions}}) { + action_summaries({id}, _, {loaders: {Actions}}) { return Actions.getSummariesByItemID.load(id); }, + actions({id}, _, {user, loaders: {Actions}}) { + + // Only return the actions if the user is not an admin. + if (user && user.hasRoles('ADMIN')) { + return Actions.getByID.load(id); + } + + }, comments({id}, _, {loaders: {Comments}, user}) { // If the user is not an admin, only return comment list for the owner of diff --git a/graph/resolvers/user_error.js b/graph/resolvers/user_error.js new file mode 100644 index 0000000000..3716fa1268 --- /dev/null +++ b/graph/resolvers/user_error.js @@ -0,0 +1,11 @@ +const UserError = { + __resolveType({field_name}) { + if (field_name) { + return 'ValidationUserError'; + } + + return 'GenericUserError'; + } +}; + +module.exports = UserError; diff --git a/graph/resolvers/validation_user_error.js b/graph/resolvers/validation_user_error.js new file mode 100644 index 0000000000..211a0e505e --- /dev/null +++ b/graph/resolvers/validation_user_error.js @@ -0,0 +1,3 @@ +const ValidationUserError = {}; + +module.exports = ValidationUserError; diff --git a/graph/schema.js b/graph/schema.js index b4b42b809d..2fca1a87f6 100644 --- a/graph/schema.js +++ b/graph/schema.js @@ -1,8 +1,15 @@ const tools = require('graphql-tools'); +const maskErrors = require('graphql-errors').maskErrors; const resolvers = require('./resolvers'); const typeDefs = require('./typeDefs'); const schema = tools.makeExecutableSchema({typeDefs, resolvers}); +if (process.env.NODE_ENV === 'production') { + + // Mask errors that are thrown if we are in a production environment. + maskErrors(schema); +} + module.exports = schema; diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index b1f9b82f71..a865960828 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -1,40 +1,17 @@ -# Establishes the ordering of the content by their created_at time stamp. -enum SORT_ORDER { - # newest to oldest order. - REVERSE_CHRONOLOGICAL - - # oldest to newer order. - CHRONOLOGICAL -} +################################################################################ +## Custom Scalar Types +################################################################################ # Date represented as an ISO8601 string. scalar Date -input CommentsQuery { - # current status of a comment. - statuses: [COMMENT_STATUS] - - # asset that a comment is on. - asset_id: ID - - # the parent of the comment that we want to retrieve. - parent_id: ID - - # comments returned will only be ones which have at least one action of this - # type. - action_type: ACTION_TYPE - - # limit the number of results to be returned. - limit: Int = 10 - - # skip results from the last created_at timestamp. - cursor: Date - - # sort the results by created_at. - sort: SORT_ORDER = REVERSE_CHRONOLOGICAL -} +################################################################################ +## Users +################################################################################ +# Roles that a user can have, these can be combined. enum USER_ROLES { + # an administrator of the site ADMIN @@ -45,13 +22,18 @@ enum USER_ROLES { # Any person who can author comments, create actions, and view comments on a # stream. type User { + + # The ID of the User. id: ID! # display name of a user. displayName: String! - # actions against a specific user. - actions: [ActionSummary] + # Action summaries against the user. + action_summaries: [ActionSummary] + + # Actions completed on the parent. + actions: [Action] # the current roles of the user. roles: [USER_ROLES] @@ -63,80 +45,223 @@ type User { comments(query: CommentsQuery): [Comment] } +################################################################################ +## Comments +################################################################################ + +# The statuses that a comment may have. +enum COMMENT_STATUS { + + # The comment has been accepted by a moderator. + ACCEPTED + + # The comment has been rejected by a moderator. + REJECTED + + # The comment was created while the asset's premoderation option was on, and + # new comments that haven't been moderated yet are referred to as + # "premoderated" or "premod" comments. + PREMOD +} + +# The types of action there are as enum's. +enum ACTION_TYPE { + + # Represents a LikeAction. + LIKE + + # Represents a FlagAction. + FLAG +} + +# CommentsQuery allows the ability to query comments by a specific methods. +input CommentsQuery { + + # current status of a comment. + statuses: [COMMENT_STATUS] + + # asset that a comment is on. + asset_id: ID + + # the parent of the comment that we want to retrieve. + parent_id: ID + + # comments returned will only be ones which have at least one action of this + # type. + action_type: ACTION_TYPE + + # limit the number of results to be returned. + limit: Int = 10 + + # skip results from the last created_at timestamp. + cursor: Date + + # sort the results by created_at. + sort: SORT_ORDER = REVERSE_CHRONOLOGICAL +} + +# Comment is the base representation of user interaction in Talk. type Comment { + + # The ID of the comment. id: ID! - # the actual comment data. + # The actual comment data. body: String! - # the user who authored the comment. + # The user who authored the comment. user: User - # the replies that were made to the comment. + # The replies that were made to the comment. replies(sort: SORT_ORDER = CHRONOLOGICAL, limit: Int = 3): [Comment] - # the count of replies on a comment + # The count of replies on a comment. replyCount: Int - # the actions made against a comment. - actions: [ActionSummary] + # Actions completed on the parent. + actions: [Action] + + # Action summaries against a comment. + action_summaries: [ActionSummary] - # the asset that a comment was made on. + # The asset that a comment was made on. asset: Asset - # the current status of a comment. + # The current status of a comment. status: COMMENT_STATUS - # the time when the comment was created + # The time when the comment was created created_at: Date! } -enum ITEM_TYPE { - ASSETS - COMMENTS - USERS -} +################################################################################ +## Actions +################################################################################ -enum ACTION_TYPE { - LIKE - FLAG -} +# An action rendered against a parent enity item. +interface Action { -type Action { + # The ID of the action. id: ID! - action_type: ACTION_TYPE! - item_id: ID! - item_type: ITEM_TYPE! + # The author of the action. + user: User - user: User! + # The time when the Action was updated. updated_at: Date + + # The time when the Action was created. created_at: Date } -type ActionSummary { - action_type: ACTION_TYPE! - item_type: ITEM_TYPE! +# A summary of actions based on the specific grouping of the group_id. +interface ActionSummary { + + # The count of actions with this group. count: Int + + # The current user's action. current_user: Action } +# LikeAction is used by users who "like" a specific entity. +type LikeAction implements Action { + + # The ID of the action. + id: ID! + + # The author of the action. + user: User + + # The time when the Action was updated. + updated_at: Date + + # The time when the Action was created. + created_at: Date +} + +# LikeActionSummary is counts the amount of "likes" that a specific entity has. +type LikeActionSummary implements ActionSummary { + + # The count of likes against the parent entity. + count: Int! + + current_user: LikeAction +} + +# A FLAG action that contains flag metadata. +type FlagAction implements Action { + + # The ID of the Flag Action. + id: ID! + + # The reason for which the Flag Action was created. + reason: String + + # An optional message sent with the flagging action by the user. + message: String + + # The user who created the action. + user: User + + # The time when the Flag Action was updated. + updated_at: Date + + # The time when the Flag Action was created. + created_at: Date +} + +# Summary for Flag Action with a a unique reason. +type FlagActionSummary implements ActionSummary { + + # The total count of flags with this reason. + count: Int! + + # The reason for which the Flag Action was created. + reason: String + + # The flag by the current user against the parent entity with this reason. + current_user: FlagAction +} + +################################################################################ +## Settings +################################################################################ + +# The moderation mode of the site. enum MODERATION_MODE { + + # Comments posted while in `PRE` mode will be labeled with a `PREMOD` + # status and will require a moderator decision before being visible. PRE + + # Comments posted while in `POST` will be visible immediately. POST } +# Site wide global settings. type Settings { + + # Moderation mode for the site. moderation: MODERATION_MODE! + + # Enables a requirement for email confirmation before a user can login. + requireEmailConfirmation: Boolean + infoBoxEnable: Boolean infoBoxContent: String closeTimeout: Int closedMessage: String charCountEnable: Boolean charCount: Int - requireEmailConfirmation: Boolean + } +################################################################################ +## Assets +################################################################################ + +# Where comments are made on. type Asset { # The current ID of the asset. @@ -165,53 +290,178 @@ type Asset { created_at: Date } -enum COMMENT_STATUS { - ACCEPTED - REJECTED - PREMOD +################################################################################ +## Errors +################################################################################ + +# Any error rendered due to the user's input. +interface UserError { + + # Translation key relating to a translatable string containing details to be + # displayed to the end user. + translation_key: String! +} + +# A generic error not related to validation reasons. +type GenericUserError implements UserError { + + # Translation key relating to a translatable string containing details to be + # displayed to the end user. + translation_key: String! +} + +# A validation error that affects the input. +type ValidationUserError implements UserError { + + # Translation key relating to a translatable string containing details to be + # displayed to the end user. + translation_key: String! + + # The field in question that caused the error. + field_name: String! +} + +################################################################################ +## Queries +################################################################################ + +# Establishes the ordering of the content by their created_at time stamp. +enum SORT_ORDER { + + # newest to oldest order. + REVERSE_CHRONOLOGICAL + + # oldest to newer order. + CHRONOLOGICAL } +# All queries that can be executed. type RootQuery { - # retrieves site wide settings and defaults. + # Site wide settings and defaults. settings: Settings - # retrieves all assets. + # All assets. assets: [Asset] - # retrieves a specific asset. + # Find or create an asset by url, or just find with the ID. asset(id: ID, url: String): Asset - # retrieves comments based on the input query. + # Comments returned based on a query. comments(query: CommentsQuery!): [Comment] - # retrieves the current logged in user. + # The currently logged in user based on the request. me: User } -input CreateActionInput { - # the type of action. - action_type: ACTION_TYPE! +################################################################################ +## Mutations +################################################################################ + +# Response defines what can be expected from any response to a mutation action. +interface Response { + + # An array of errors relating to the mutation that occured. + errors: [UserError] +} + +# CreateCommentResponse is returned with the comment that was created and any +# errors that may have occured in the attempt to create it. +type CreateCommentResponse implements Response { + + # The comment that was created. + comment: Comment + + # An array of errors relating to the mutation that occured. + errors: [UserError] +} + +# Used to represent the item type for an action. +enum ACTION_ITEM_TYPE { + + # The action references a entity of type Asset. + ASSETS + + # The action references a entity of type Comment. + COMMENTS + + # The action references a entity of type User. + USERS +} + +input CreateLikeInput { + + # The item's id for which we are to create a like. + item_id: ID! + + # The type of the item for which we are to create the like. + item_type: ACTION_ITEM_TYPE! +} + +type CreateLikeResponse implements Response { + + # The like that was created. + like: LikeAction + + # An array of errors relating to the mutation that occured. + errors: [UserError] +} - # the type of the item. - item_type: ITEM_TYPE! +input CreateFlagInput { - # the id of the item that is related to the action. + # The item's id for which we are to create a flag. item_id: ID! + + # The type of the item for which we are to create the flag. + item_type: ACTION_ITEM_TYPE! + + # The reason for flagging the item. + reason: String! + + # An optional message sent with the flagging action by the user. + message: String +} + +# CreateFlagResponse is the response returned with possibly some errors +# relating to the creating the flag action attempt and possibly the flag that +# was created. +type CreateFlagResponse implements Response { + + # The like that was created. + flag: FlagAction + + # An array of errors relating to the mutation that occured. + errors: [UserError] +} + +# DeleteActionResponse is the response returned with possibly some errors +# relating to the delete action attempt. +type DeleteActionResponse implements Response { + + # An array of errors relating to the mutation that occured. + errors: [UserError] } +# All mutations for the application are defined on this object. type RootMutation { - # creates a comment on the asset. - createComment(asset_id: ID!, parent_id: ID, body: String!): Comment - # creates an action based on an input. - createAction(action: CreateActionInput!): Action + # Creates a comment on the asset. + createComment(asset_id: ID!, parent_id: ID, body: String!): CreateCommentResponse + + # Creates a like on an entity. + createLike(like: CreateLikeInput!): CreateLikeResponse - # delete an action based on the action id. - deleteAction(id: ID!): Boolean + # Creates a flag on an entity. + createFlag(flag: CreateFlagInput!): CreateFlagResponse + # Delete an action based on the action id. + deleteAction(id: ID!): DeleteActionResponse } +################################################################################ +## Schema +################################################################################ + schema { query: RootQuery mutation: RootMutation diff --git a/models/action.js b/models/action.js index ed871f56b9..36d83574e9 100644 --- a/models/action.js +++ b/models/action.js @@ -29,6 +29,12 @@ const ActionSchema = new Schema({ }, item_id: String, user_id: String, + + // The element that summaries will additionally group on in addtion to their action_type, item_type, and + // item_id. + group_id: String, + + // Additional metadata stored on the field. metadata: Schema.Types.Mixed }, { timestamps: { diff --git a/routes/api/comments/index.js b/routes/api/comments/index.js index 19346274d5..96f3911d41 100644 --- a/routes/api/comments/index.js +++ b/routes/api/comments/index.js @@ -127,21 +127,4 @@ router.put('/:comment_id/status', authorization.needed('ADMIN'), (req, res, next }); }); -router.post('/:comment_id/actions', (req, res, next) => { - - const { - action_type, - metadata - } = req.body; - - CommentsService - .addAction(req.params.comment_id, req.user.id, action_type, metadata) - .then((action) => { - res.status(201).json(action); - }) - .catch((err) => { - next(err); - }); -}); - module.exports = router; diff --git a/services/actions.js b/services/actions.js index c336dd974e..0bcb81af03 100644 --- a/services/actions.js +++ b/services/actions.js @@ -26,7 +26,8 @@ module.exports = class ActionsService { action_type: action.action_type, item_type: action.item_type, item_id: action.item_id, - user_id: action.user_id + user_id: action.user_id, + group_id: action.group_id }; // Create/Update the action. @@ -86,68 +87,70 @@ module.exports = class ActionsService { * @param {String} ids array of user identifiers (uuid) */ static getActionSummaries(item_ids, current_user_id = '') { - return ActionModel.aggregate([ - { - // only grab items that match the specified item id's - $match: { - item_id: { - $in: item_ids - } - } + // only grab items that match the specified item id's + let $match = { + item_id: { + $in: item_ids + } + }; + + let $group = { + + // group unique documents by these properties, we are leveraging the + // fact that each uuid is completly unique. + _id: { + item_id: '$item_id', + action_type: '$action_type', + group_id: '$group_id' + }, + + // and sum up all actions matching the above grouping criteria + count: { + $sum: 1 + }, + + // we are leveraging the fact that each uuid is completly unique and + // just grabbing the last instance of the item type here. + item_type: { + $first: '$item_type' }, - { - $group: { - - // group unique documents by these properties, we are leveraging the - // fact that each uuid is completly unique. - _id: { - item_id: '$item_id', - action_type: '$action_type' - }, - - // and sum up all actions matching the above grouping criteria - count: { - $sum: 1 - }, - - // we are leveraging the fact that each uuid is completly unique and - // just grabbing the last instance of the item type here. - item_type: { - $last: '$item_type' - }, - - current_user: { - $max: { - $cond: { - if: { - $eq: ['$user_id', current_user_id], - }, - then: '$$CURRENT', - else: null - } - } + + current_user: { + $max: { + $cond: { + if: { + $eq: ['$user_id', current_user_id], + }, + then: '$$CURRENT', + else: null } } - }, - { - $project: { + } + }; - // suppress the _id field - _id: false, + let $project = { - // map the fields from the _id grouping down a level - item_id: '$_id.item_id', - action_type: '$_id.action_type', + // suppress the _id field + _id: false, - // map the field directly - count: '$count', - item_type: '$item_type', + // map the fields from the _id grouping down a level + item_id: '$_id.item_id', + action_type: '$_id.action_type', + group_id: '$_id.group_id', - // set the current user to false here - current_user: '$current_user' - } - } + // map the field directly + count: '$count', + item_type: '$item_type', + + // set the current user to false here + current_user: '$current_user' + }; + + return ActionModel.aggregate([ + {$match}, + {$group}, + {$project} ]); } diff --git a/test/routes/api/comments/index.js b/test/routes/api/comments/index.js index c305f9f6b2..0ee19fc5e8 100644 --- a/test/routes/api/comments/index.js +++ b/test/routes/api/comments/index.js @@ -267,79 +267,3 @@ describe('/api/v1/comments/:comment_id', () => { }); }); }); - -describe('/api/v1/comments/:comment_id/actions', () => { - - const comments = [{ - id: 'abc', - body: 'comment 10', - asset_id: 'asset', - author_id: '123', - status_history: [] - }, { - id: 'def', - body: 'comment 20', - asset_id: 'asset', - author_id: '456', - status: 'REJECTED', - status_history: [{ - type: 'REJECTED' - }] - }, { - id: 'hij', - body: 'comment 30', - asset_id: '456', - status: 'ACCEPTED', - status_history: [{ - type: 'ACCEPTED' - }] - }]; - - const users = [{ - displayName: 'Ana', - email: 'ana@gmail.com', - password: '123456789' - }, { - displayName: 'Maria', - email: 'maria@gmail.com', - password: '123456789' - }]; - - const actions = [{ - action_type: 'FLAG', - item_type: 'COMMENTS', - item_id: 'abc' - }, { - action_type: 'LIKE', - item_type: 'COMMENTS', - item_id: 'hij' - }]; - - beforeEach(() => { - return SettingsService.init(settings).then(() => { - return Promise.all([ - CommentModel.create(comments), - UsersService.createLocalUsers(users), - ActionModel.create(actions) - ]); - }); - }); - - describe('#post', () => { - it('it should create an action', () => { - return chai.request(app) - .post('/api/v1/comments/abc/actions') - .set(passport.inject({id: '456', roles: ['ADMIN']})) - .send({'action_type': 'flag', 'metadata': {'reason': 'Comment is too awesome.'}}) - .then((res) => { - expect(res).to.have.status(201); - expect(res).to.have.body; - - expect(res.body).to.have.property('action_type', 'flag'); - expect(res.body).to.have.property('metadata'); - expect(res.body.metadata).to.deep.equal({'reason': 'Comment is too awesome.'}); - expect(res.body).to.have.property('item_id', 'abc'); - }); - }); - }); -}); From 25103630b584cdafaebc1abbc2ea73c0f560ec06 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 8 Feb 2017 17:41:28 -0700 Subject: [PATCH 2/6] Added graphql docs endpoint. --- app.js | 5 ++ client/coral-docs/src/index.js | 11 +++++ client/coral-docs/src/services/fetcher.js | 10 ++++ package.json | 2 + views/admin/docs.ejs | 34 ++++++++++++++ webpack.config.js | 3 +- yarn.lock | 57 ++++++++++++++++++++--- 7 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 client/coral-docs/src/index.js create mode 100644 client/coral-docs/src/services/fetcher.js create mode 100644 views/admin/docs.ejs diff --git a/app.js b/app.js index c04e0cb4a5..c2601ad5a2 100644 --- a/app.js +++ b/app.js @@ -94,6 +94,11 @@ if (app.get('env') !== 'production') { endpointURL: '/api/v1/graph/ql' })); + // GraphQL documention. + app.get('/admin/docs', (req, res) => { + res.render('admin/docs'); + }); + } //============================================================================== diff --git a/client/coral-docs/src/index.js b/client/coral-docs/src/index.js new file mode 100644 index 0000000000..6ca0fb8abb --- /dev/null +++ b/client/coral-docs/src/index.js @@ -0,0 +1,11 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import {GraphQLDocs} from 'graphql-docs'; + +import fetcher from './services/fetcher'; + +// Render the application into the DOM +ReactDOM.render(
+

Talk: GraphQL Docs

+ +
, document.querySelector('#root')); diff --git a/client/coral-docs/src/services/fetcher.js b/client/coral-docs/src/services/fetcher.js new file mode 100644 index 0000000000..aec4487629 --- /dev/null +++ b/client/coral-docs/src/services/fetcher.js @@ -0,0 +1,10 @@ +export default function fetcher(query) { + return fetch(`${window.location.origin}/api/v1/graph/ql`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({query}), + }).then((res) => res.json()); +} diff --git a/package.json b/package.json index 548119cfcc..a40e1ad976 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "express": "^4.14.0", "express-session": "^1.14.2", "graphql": "^0.8.2", + "graphql-errors": "^2.1.0", "graphql-server-express": "^0.5.0", "graphql-tag": "^1.2.3", "graphql-tools": "^0.9.0", @@ -120,6 +121,7 @@ "eslint-plugin-standard": "^2.0.1", "exports-loader": "^0.6.3", "fetch-mock": "^5.5.0", + "graphql-docs": "^0.2.0", "hammerjs": "^2.0.8", "ignore-styles": "^5.0.1", "immutable": "^3.8.1", diff --git a/views/admin/docs.ejs b/views/admin/docs.ejs new file mode 100644 index 0000000000..908537a377 --- /dev/null +++ b/views/admin/docs.ejs @@ -0,0 +1,34 @@ + + + + + Talk: GraphQL Docs + + + + +
+
+ + + diff --git a/webpack.config.js b/webpack.config.js index dde8017440..5a7bb297b7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -7,7 +7,8 @@ const webpack = require('webpack'); // Edit the build targets and embeds below. const buildTargets = [ - 'coral-admin' + 'coral-admin', + 'coral-docs' ]; const buildEmbeds = [ diff --git a/yarn.lock b/yarn.lock index e76614702e..f1c047be2d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1011,7 +1011,7 @@ babel-register@^6.22.0: mkdirp "^0.5.1" source-map-support "^0.4.2" -babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0: +babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.6.1: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611" dependencies: @@ -3307,6 +3307,21 @@ graphql-anywhere@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-2.1.0.tgz#888c0a1718db3ff866b313070747777380560f69" +graphql-docs@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/graphql-docs/-/graphql-docs-0.2.0.tgz#cf803f9c9d354fa03e89073d74e419261a5bfa74" + dependencies: + marked "^0.3.5" + request "^2.74.0" + yargs "^5.0.0" + +graphql-errors@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/graphql-errors/-/graphql-errors-2.1.0.tgz#831c8c491b354859ee7a0c07bff101af64731195" + dependencies: + babel-runtime "^6.6.1" + uuid "^2.0.2" + graphql-server-core@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/graphql-server-core/-/graphql-server-core-0.5.2.tgz#7e23fc516cb754e42c16f92928b595c354d6c8a7" @@ -4480,7 +4495,7 @@ lodash.assign@^3.0.0: lodash._createassigner "^3.0.0" lodash.keys "^3.0.0" -lodash.assign@^4.0.3, lodash.assign@^4.0.6: +lodash.assign@^4.0.3, lodash.assign@^4.0.6, lodash.assign@^4.1.0, lodash.assign@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -4696,6 +4711,10 @@ map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" +marked@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7" + material-design-lite@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/material-design-lite/-/material-design-lite-1.3.0.tgz#d004ce3fee99a1eeb74a78b8a325134a5f1171d3" @@ -6707,7 +6726,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@2.79.0, request@^2.55.0, request@^2.79.0: +request@2.79.0, request@^2.55.0, request@^2.74.0, request@^2.79.0: version "2.79.0" resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" dependencies: @@ -7286,7 +7305,7 @@ supports-color@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" -supports-color@3.1.2: +supports-color@3.1.2, supports-color@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5" dependencies: @@ -7300,7 +7319,7 @@ supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" -supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3: +supports-color@^3.1.2, supports-color@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" dependencies: @@ -7658,7 +7677,7 @@ utils-merge@1.0.0, utils-merge@1.x.x: version "1.0.0" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8" -uuid@^2.0.1, uuid@^2.0.3: +uuid@^2.0.1, uuid@^2.0.2, uuid@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a" @@ -7908,6 +7927,13 @@ yargs-parser@^2.4.1: camelcase "^3.0.0" lodash.assign "^4.0.6" +yargs-parser@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-3.2.0.tgz#5081355d19d9d0c8c5d81ada908cb4e6d186664f" + dependencies: + camelcase "^3.0.0" + lodash.assign "^4.1.0" + yargs-parser@^4.2.0: version "4.2.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" @@ -7933,6 +7959,25 @@ yargs@^4.0.0: y18n "^3.2.1" yargs-parser "^2.4.1" +yargs@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-5.0.0.tgz#3355144977d05757dbb86d6e38ec056123b3a66e" + dependencies: + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + lodash.assign "^4.2.0" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + window-size "^0.2.0" + y18n "^3.2.1" + yargs-parser "^3.2.0" + yargs@^6.0.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" From 1272dabb4bcff353a6c0129445da6ac0395ad459 Mon Sep 17 00:00:00 2001 From: David Jay Date: Thu, 9 Feb 2017 17:01:16 -0800 Subject: [PATCH 3/6] Updating frontend actions to new format. --- client/coral-embed-stream/src/Comment.js | 23 +++++++++------- client/coral-embed-stream/src/Embed.js | 10 ++++--- client/coral-embed-stream/src/Stream.js | 6 +++-- .../fragments/actionSummaryView.graphql | 8 ++++++ .../graphql/fragments/commentView.graphql | 11 +++----- .../graphql/mutations/deleteAction.graphql | 6 ++++- .../graphql/mutations/index.js | 20 +++++++++++--- .../graphql/mutations/postAction.graphql | 5 ---- .../graphql/mutations/postComment.graphql | 7 ++++- .../graphql/mutations/postFlag.graphql | 10 +++++++ .../graphql/mutations/postLike.graphql | 10 +++++++ .../coral-framework/graphql/queries/index.js | 2 +- client/coral-plugin-flags/FlagButton.js | 27 ++++++++++--------- client/coral-plugin-likes/LikeButton.js | 15 +++++------ 14 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 client/coral-framework/graphql/fragments/actionSummaryView.graphql delete mode 100644 client/coral-framework/graphql/mutations/postAction.graphql create mode 100644 client/coral-framework/graphql/mutations/postFlag.graphql create mode 100644 client/coral-framework/graphql/mutations/postLike.graphql diff --git a/client/coral-embed-stream/src/Comment.js b/client/coral-embed-stream/src/Comment.js index c0c0d57d1c..01e183a9b4 100644 --- a/client/coral-embed-stream/src/Comment.js +++ b/client/coral-embed-stream/src/Comment.js @@ -18,7 +18,8 @@ import LikeButton from 'coral-plugin-likes/LikeButton'; import styles from './Comment.css'; -const getAction = (type, comment) => comment.actions.filter((a) => a.type === type)[0]; +const getActionSummary = (type, comment) => comment.action_summaries + .filter((a) => a.__typename === type)[0]; class Comment extends React.Component { @@ -35,7 +36,8 @@ class Comment extends React.Component { setActiveReplyBox: PropTypes.func.isRequired, refetch: PropTypes.func.isRequired, showSignInDialog: PropTypes.func.isRequired, - postAction: PropTypes.func.isRequired, + postFlag: PropTypes.func.isRequired, + postLike: PropTypes.func.isRequired, deleteAction: PropTypes.func.isRequired, parentId: PropTypes.string, addNotification: PropTypes.func.isRequired, @@ -51,7 +53,7 @@ class Comment extends React.Component { }), comment: PropTypes.shape({ depth: PropTypes.number, - actions: PropTypes.array.isRequired, + action_summaries: PropTypes.array.isRequired, body: PropTypes.string.isRequired, id: PropTypes.string.isRequired, replies: PropTypes.arrayOf( @@ -78,14 +80,15 @@ class Comment extends React.Component { refetch, addNotification, showSignInDialog, - postAction, + postLike, + postFlag, setActiveReplyBox, activeReplyBox, deleteAction } = this.props; - const like = getAction('LIKE', comment); - const flag = getAction('FLAG', comment); + const like = getActionSummary('LikeActionSummary', comment); + const flag = getActionSummary('FlagActionSummary', comment); return (
@@ -117,7 +120,7 @@ class Comment extends React.Component { flag={flag} id={comment.id} author_id={comment.user.id} - postAction={postAction} + postFlag={postFlag} deleteAction={deleteAction} showSignInDialog={showSignInDialog} currentUser={currentUser} /> @@ -150,7 +153,8 @@ class Comment extends React.Component { depth={depth + 1} asset={asset} currentUser={currentUser} - postAction={postAction} + postLike={postLike} + postFlag={postFlag} deleteAction={deleteAction} showSignInDialog={showSignInDialog} reactKey={reply.id} @@ -158,7 +162,6 @@ class Comment extends React.Component { comment={reply} />; }) } -
); } diff --git a/client/coral-embed-stream/src/Embed.js b/client/coral-embed-stream/src/Embed.js index a853efead8..6cc783746a 100644 --- a/client/coral-embed-stream/src/Embed.js +++ b/client/coral-embed-stream/src/Embed.js @@ -10,7 +10,7 @@ const {addNotification, clearNotification} = notificationActions; const {fetchAssetSuccess} = assetActions; import {queryStream} from 'coral-framework/graphql/queries'; -import {postComment, postAction, deleteAction} from 'coral-framework/graphql/mutations'; +import {postComment, postFlag, postLike, deleteAction} from 'coral-framework/graphql/mutations'; import {editName} from 'coral-framework/actions/user'; import {Notification, notificationActions, authActions, assetActions, pym} from 'coral-framework'; @@ -91,7 +91,7 @@ class Embed extends Component { minHeight: document.body.scrollHeight + 200 } : {}; - if (loading) { + if (loading || !asset) { return ; } @@ -146,7 +146,8 @@ class Embed extends Component { postItem={this.props.postItem} asset={asset} currentUser={user} - postAction={this.props.postAction} + postLike={this.props.postLike} + postFlag={this.props.postFlag} deleteAction={this.props.deleteAction} showSignInDialog={this.props.showSignInDialog} comments={asset.comments} /> @@ -209,7 +210,8 @@ const mapDispatchToProps = dispatch => ({ export default compose( connect(mapStateToProps, mapDispatchToProps), postComment, - postAction, + postFlag, + postLike, deleteAction, queryStream )(Embed); diff --git a/client/coral-embed-stream/src/Stream.js b/client/coral-embed-stream/src/Stream.js index 65743e6c77..53b54d4ba3 100644 --- a/client/coral-embed-stream/src/Stream.js +++ b/client/coral-embed-stream/src/Stream.js @@ -37,7 +37,8 @@ class Stream extends React.Component { asset, postItem, addNotification, - postAction, + postFlag, + postLike, deleteAction, showSignInDialog, refetch @@ -56,7 +57,8 @@ class Stream extends React.Component { postItem={postItem} asset={asset} currentUser={currentUser} - postAction={postAction} + postLike={postLike} + postFlag={postFlag} deleteAction={deleteAction} showSignInDialog={showSignInDialog} key={comment.id} diff --git a/client/coral-framework/graphql/fragments/actionSummaryView.graphql b/client/coral-framework/graphql/fragments/actionSummaryView.graphql new file mode 100644 index 0000000000..4ac232bf67 --- /dev/null +++ b/client/coral-framework/graphql/fragments/actionSummaryView.graphql @@ -0,0 +1,8 @@ +fragment actionSummaryView on ActionSummary { + __typename + count + current_user { + id + created_at + } +} diff --git a/client/coral-framework/graphql/fragments/commentView.graphql b/client/coral-framework/graphql/fragments/commentView.graphql index 29f9b3bfe9..575434a319 100644 --- a/client/coral-framework/graphql/fragments/commentView.graphql +++ b/client/coral-framework/graphql/fragments/commentView.graphql @@ -1,3 +1,5 @@ +#import "../fragments/actionSummaryView.graphql" + fragment commentView on Comment { id body @@ -7,12 +9,7 @@ fragment commentView on Comment { id name: displayName } - actions { - type: action_type - count - current: current_user { - id - created_at - } + action_summaries { + ...actionSummaryView } } diff --git a/client/coral-framework/graphql/mutations/deleteAction.graphql b/client/coral-framework/graphql/mutations/deleteAction.graphql index bfce8cf6a2..f8adf371c4 100644 --- a/client/coral-framework/graphql/mutations/deleteAction.graphql +++ b/client/coral-framework/graphql/mutations/deleteAction.graphql @@ -1,3 +1,7 @@ mutation deleteAction ($id: ID!) { - deleteAction(id:$id) + deleteAction(id:$id) { + errors { + translation_key + } + } } diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index b97e5ff22b..40ca0a7a3e 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -1,6 +1,7 @@ import {graphql} from 'react-apollo'; import POST_COMMENT from './postComment.graphql'; -import POST_ACTION from './postAction.graphql'; +import POST_FLAG from './postFlag.graphql'; +import POST_LIKE from './postLike.graphql'; import DELETE_ACTION from './deleteAction.graphql'; import commentView from '../fragments/commentView.graphql'; @@ -21,12 +22,23 @@ export const postComment = graphql(POST_COMMENT, { }}), }); -export const postAction = graphql(POST_ACTION, { +export const postLike = graphql(POST_LIKE, { props: ({mutate}) => ({ - postAction: (action) => { + postLike: (like) => { return mutate({ variables: { - action + like + } + }); + }}), +}); + +export const postFlag = graphql(POST_FLAG, { + props: ({mutate}) => ({ + postFlag: (flag) => { + return mutate({ + variables: { + flag } }); }}), diff --git a/client/coral-framework/graphql/mutations/postAction.graphql b/client/coral-framework/graphql/mutations/postAction.graphql deleted file mode 100644 index fff737fa89..0000000000 --- a/client/coral-framework/graphql/mutations/postAction.graphql +++ /dev/null @@ -1,5 +0,0 @@ -mutation CreateAction ($action: CreateActionInput!) { - createAction(action:$action) { - id - } -} diff --git a/client/coral-framework/graphql/mutations/postComment.graphql b/client/coral-framework/graphql/mutations/postComment.graphql index 499871766c..6ce46eaa87 100644 --- a/client/coral-framework/graphql/mutations/postComment.graphql +++ b/client/coral-framework/graphql/mutations/postComment.graphql @@ -2,6 +2,11 @@ mutation CreateComment ($asset_id: ID!, $parent_id: ID, $body: String!) { createComment(asset_id:$asset_id, parent_id:$parent_id, body:$body) { - ...commentView + comment { + ...commentView + } + errors { + translation_key + } } } diff --git a/client/coral-framework/graphql/mutations/postFlag.graphql b/client/coral-framework/graphql/mutations/postFlag.graphql new file mode 100644 index 0000000000..cabc2feefc --- /dev/null +++ b/client/coral-framework/graphql/mutations/postFlag.graphql @@ -0,0 +1,10 @@ +mutation CreateFlag($flag: CreateFlagInput!) { + createFlag(flag:$flag) { + flag { + id + } + errors { + translation_key + } + } +} diff --git a/client/coral-framework/graphql/mutations/postLike.graphql b/client/coral-framework/graphql/mutations/postLike.graphql new file mode 100644 index 0000000000..3509043525 --- /dev/null +++ b/client/coral-framework/graphql/mutations/postLike.graphql @@ -0,0 +1,10 @@ +mutation CreateLike ($like: CreateLikeInput!) { + createLike(like:$like) { + like { + id + } + errors { + translation_key + } + } +} diff --git a/client/coral-framework/graphql/queries/index.js b/client/coral-framework/graphql/queries/index.js index 7ce065baa2..ffd7dfdc9e 100644 --- a/client/coral-framework/graphql/queries/index.js +++ b/client/coral-framework/graphql/queries/index.js @@ -13,7 +13,7 @@ function getQueryVariable(variable) { } // If no query is included, return a default string for development - return 'http://dev.default.stream'; + return 'http://localhost/default/stream'; } export const queryStream = graphql(STREAM_QUERY, { diff --git a/client/coral-plugin-flags/FlagButton.js b/client/coral-plugin-flags/FlagButton.js index ec62f53969..e94dfc5fca 100644 --- a/client/coral-plugin-flags/FlagButton.js +++ b/client/coral-plugin-flags/FlagButton.js @@ -12,7 +12,7 @@ class FlagButton extends Component { showMenu: false, itemType: '', reason: '', - note: '', + message: '', step: 0, posted: false, localPost: null, @@ -23,7 +23,7 @@ class FlagButton extends Component { onReportClick = () => { const {currentUser, flag, deleteAction} = this.props; const {localPost, localDelete} = this.state; - const flagged = (flag && flag.current && !localDelete) || localPost; + const flagged = (flag && flag.current_user && !localDelete) || localPost; if (!currentUser) { const offset = document.getElementById(`c_${this.props.id}`).getBoundingClientRect().top - 75; this.props.showSignInDialog(offset); @@ -31,15 +31,15 @@ class FlagButton extends Component { } if (flagged) { this.setState((prev) => prev.localPost ? {...prev, localPost: null, step: 0} : {...prev, localDelete: true}); - deleteAction(localPost || flag.current.id); + deleteAction(localPost || flag.current_user.id); } else { this.setState({showMenu: !this.state.showMenu}); } } onPopupContinue = () => { - const {postAction, id, author_id} = this.props; - const {itemType, reason, step, posted} = this.state; + const {postFlag, id, author_id} = this.props; + const {itemType, reason, step, posted, message} = this.state; // Proceed to the next step or close the menu if we've reached the end if (step + 1 >= this.props.getPopupMenu.length) { @@ -67,13 +67,14 @@ class FlagButton extends Component { if (itemType === 'COMMENTS') { this.setState({localPost: 'temp'}); } - postAction({ + postFlag({ item_id, item_type: itemType, - action_type: 'FLAG' + reason, + message }).then(({data}) => { if (itemType === 'COMMENTS') { - this.setState({localPost: data.createAction.id}); + this.setState({localPost: data.createFlag.flag.id}); } }); } @@ -99,7 +100,7 @@ class FlagButton extends Component { } onNoteTextChange = (e) => { - this.setState({note: e.target.value}); + this.setState({message: e.target.value}); } handleClickOutside () { @@ -109,7 +110,7 @@ class FlagButton extends Component { render () { const {flag, getPopupMenu} = this.props; const {localPost, localDelete} = this.state; - const flagged = (flag && flag.current && !localDelete) || localPost; + const flagged = (flag && flag.current_user && !localDelete) || localPost; const popupMenu = getPopupMenu[this.state.step](this.state.itemType); return
@@ -150,15 +151,15 @@ class FlagButton extends Component { } { this.state.reason &&
-