From 1e329c8967ffb0ecfb9dca00e20c1f68adc08d94 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 13 Feb 2018 17:45:26 -0700 Subject: [PATCH] code cleanup --- graph/loaders/actions.js | 205 +++++++++++++++++++++++---------------- 1 file changed, 124 insertions(+), 81 deletions(-) diff --git a/graph/loaders/actions.js b/graph/loaders/actions.js index 2a501b6c6..6e8c42355 100644 --- a/graph/loaders/actions.js +++ b/graph/loaders/actions.js @@ -1,7 +1,7 @@ const DataLoader = require('dataloader'); const util = require('./util'); const ActionsService = require('../../services/actions'); -const { first, get, remove, groupBy, reduce, defaultTo } = require('lodash'); +const { first, get, merge, remove, groupBy, reduce } = require('lodash'); /** * Gets actions based on their item id's. @@ -23,6 +23,128 @@ const genActionsAuthoredWithID = ({ user = {} }, itemIDs) => util.arrayJoinBy(itemIDs, 'item_id') ); +/** + * iterateActionCounts will create an iterable object that can be used to + * compute action summaries. + * + * @param {Object} action_counts the action count object + */ +const iterateActionCounts = action_counts => + Object.keys(action_counts).map(action_type => ({ + count: action_counts[action_type], + action_type: action_type.toUpperCase(), + })); + +/** + * getUserActions will get the actions made by the user for this specific + * item. + * + * @param {Object} ctx the graph context of the request + * @param {Object} item the item that we're getting the actions for + */ +async function getUserActions(ctx, { action_counts, id }) { + const { loaders: { Actions } } = ctx; + + // Get the total count for all action types. + const totalActionCount = reduce( + action_counts, + (total, count) => total + count, + 0 + ); + + // Check to see if there are any user actions to get. + const hasUserActions = ctx.user && totalActionCount > 0; + if (!hasUserActions) { + return {}; + } + + // Possibly get the list of user actions completed by the user. This will be + // used later to join together with the action summaries to provide context. + const userActions = await Actions.getAuthoredByID.load(id); + if (userActions.length === 0) { + return {}; + } + + // Group the user actions in the same way that the action counts are + // grouped. This will let us extract it easy. + return reduce( + groupBy(userActions, ({ action_type, group_id }) => + (group_id ? `${action_type}_${group_id}` : action_type).toUpperCase() + ), + (allUserActions, userActions, actionType) => + merge(allUserActions, { [actionType]: first(userActions) }), + {} + ); +} + +// This will match any action count that is specific for a group id. +const nonGroupIDTest = /^([A-Z]+)_([A-Z_]+)$/; + +/** + * resolveActionSummariesForItem will resolve the action summaries for an item. + * + * @param {Object} ctx the graph context of the request + * @param {Object} item the item that we are resolving an action summary for + */ +async function resolveActionSummariesForItem(ctx, { id, action_counts }) { + // Cache all those entries for which we got the group id of, because we + // don't want to include them twice. + const groupIDCache = {}; + + // Get the user actions for this specific item. + const groupedUserActions = await getUserActions(ctx, { id, action_counts }); + + // Generate the action summaries for the item. + return iterateActionCounts(action_counts).reduce( + (actionTypeList, { count, action_type }) => { + // Get the current user's actions (if they have any). + const current_user = get(groupedUserActions, action_type, null); + + // Check to see if this is a action without a corresponding group id. + if (nonGroupIDTest.test(action_type)) { + // This action type does have a group id associated with it. + const results = nonGroupIDTest.exec(action_type); + const groupActionType = results[1]; + const groupID = results[2]; + + // Purge out the summary if it already exists, and mark that this + // group id has been found so we don't include it in the future. + remove( + actionTypeList, + ({ action_type }) => action_type === groupActionType + ); + groupIDCache[groupActionType] = true; + + // Push the new entry in. + actionTypeList.push({ + action_type: groupActionType, + group_id: groupID, + count, + current_user, + }); + } else { + // This does not have a group id. Check to see if this group id + // already has an specific (group id) entry. + if (groupIDCache[action_type]) { + // It does. Don't add anything. + return actionTypeList; + } + + // It does not, add the entry. + actionTypeList.push({ + action_type, + group_id: null, + count, + current_user, + }); + } + + return actionTypeList; + }, + [] + ); +} + /** * Looks up the action summaries for a set of items. * @@ -30,91 +152,12 @@ const genActionsAuthoredWithID = ({ user = {} }, itemIDs) => * @param {Array} items the items that should have their items looked up for */ const genActionSummariesByItem = async (ctx, items) => { - const { loaders: { Actions } } = ctx; - // This is designed to match the action_counts value that is embedded on // documents which cache action counts. For users that are not logged in, we // don't need to hit the actions collection at all! - // This will match any action count that is specific for a group id. - const nonGroupIDTest = /^([A-Z]+)_([A-Z_]+)$/; - // We will literate over all the items that we're comparing. - return items.map(async ({ id, action_counts = {} }) => { - // Cache all those entries for which we got the group id of, because we - // don't want to include them twice. - const groupIDCache = {}; - - // Possibly get the list of user actions completed by the user. This will be - // used later to join together with the action summaries to provide context. - const userActions = - ctx.user && reduce(action_counts, (total, count) => total + count, 0) > 0 - ? await Actions.getAuthoredByID.load(id) - : []; - - // Group the user actions in the same way that the action counts are - // grouped. This will let us extract it easy. - const groupedUserActions = groupBy( - userActions, - ({ action_type, group_id }) => - (group_id ? `${action_type}_${group_id}` : action_type).toUpperCase() - ); - - // Generate the action summaries for the item. - return Object.keys(action_counts) - .map(action_type => ({ - count: action_counts[action_type], - action_type: action_type.toUpperCase(), - })) - .reduce((actionTypeList, { count, action_type }) => { - // Get the current user's actions (if they have any). - const current_user = defaultTo( - first(get(groupedUserActions, action_type, [])), - null - ); - - // Check to see if this is a action without a corresponding group id. - if (nonGroupIDTest.test(action_type)) { - // This action type does have a group id associated with it. - const results = nonGroupIDTest.exec(action_type); - const groupActionType = results[1]; - const groupID = results[2]; - - // Purge out the summary if it already exists, and mark that this - // group id has been found so we don't include it in the future. - remove( - actionTypeList, - ({ action_type }) => action_type === groupActionType - ); - groupIDCache[groupActionType] = true; - - // Push the new entry in. - actionTypeList.push({ - action_type: groupActionType, - group_id: groupID, - count, - current_user, - }); - } else { - // This does not have a group id. Check to see if this group id - // already has an specific (group id) entry. - if (groupIDCache[action_type]) { - // It does. Don't add anything. - return actionTypeList; - } - - // It does not, add the entry. - actionTypeList.push({ - action_type, - group_id: null, - count, - current_user, - }); - } - - return actionTypeList; - }, []); - }); + return items.map(resolveActionSummariesForItem); }; /**