initial impl of cached action counts using ee

This commit is contained in:
Wyatt Johnson
2017-08-16 15:47:56 -06:00
parent 4c1ebde3cc
commit b126cc3cb3
6 changed files with 128 additions and 37 deletions
+8
View File
@@ -0,0 +1,8 @@
{
"eslint.autoFixOnSave": true,
"editor.tabSize": 2,
"editor.rulers": [
80
],
"files.trimTrailingWhitespace": true
}
+1 -5
View File
@@ -1,4 +1,3 @@
const ActionModel = require('../../models/action');
const ActionsService = require('../../services/actions');
const UsersService = require('../../services/users');
const errors = require('../../errors');
@@ -52,10 +51,7 @@ const createAction = async ({user = {}, pubsub, loaders: {Comments}}, {item_id,
* @return {Promise} resolves to the deleted action, or null if not found.
*/
const deleteAction = ({user}, {id}) => {
return ActionModel.findOneAndRemove({
id,
user_id: user.id
});
return ActionsService.delete({id, user_id: user.id});
};
module.exports = (context) => {
+9 -1
View File
@@ -66,15 +66,23 @@ const CommentSchema = new Schema({
enum: COMMENT_STATUS,
default: 'NONE'
},
// parent_id is the id of the parent comment (null if there is none).
parent_id: String,
// Counts to store related to actions taken on the given comment.
action_counts: {
default: {},
type: Object,
},
// Tags are added by the self or by administrators.
tags: [TagLinkSchema],
// Additional metadata stored on the field.
metadata: {
default: {},
type: Object
type: Object,
}
}, {
timestamps: {
+50 -31
View File
@@ -1,6 +1,30 @@
const ActionModel = require('../models/action');
const _ = require('lodash');
const errors = require('../errors');
const events = require('./events');
const findOneAndUpdate = async (query, update, options = {}) => new Promise((resolve, reject) => {
ActionModel.findOneAndUpdate(query, update, Object.assign({}, options, {
// Use raw result to get `updatedExisting`.
passRawResult: true,
// Ensure that if it's new, we return the new object created.
new: true,
// Perform an upsert in the event that this doesn't exist.
upsert: true,
// Set the default values if not provided based on the mongoose models.
setDefaultsOnInsert: true
}), (err, doc, raw) => {
if (err) { return reject(err); }
if (raw.lastErrorObject.updatedExisting) {
return reject(new errors.ErrAlreadyExists(raw.value));
}
return resolve(raw.value);
});
});
module.exports = class ActionsService {
@@ -19,46 +43,26 @@ module.exports = class ActionsService {
* @param {String} action the new action to the item
* @return {Promise}
*/
static insertUserAction(action) {
static async insertUserAction(action) {
// Actions are made unique by using a query that can be reproducable, i.e.,
// not containing user inputable values.
let query = {
let foundAction = await findOneAndUpdate({
action_type: action.action_type,
item_type: action.item_type,
item_id: action.item_id,
user_id: action.user_id,
group_id: action.group_id
};
group_id: action.group_id,
}, {
// Create/Update the action.
return new Promise((resolve, reject) => {
ActionModel.findOneAndUpdate(
query, {
// Only set when not existing.
$setOnInsert: action,
}, {
// Ensure that if it's new, we return the new object created.
new: true,
// Use raw result to get `updatedExisting`.
passRawResult: true,
// Perform an upsert in the event that this doesn't exist.
upsert: true,
// Set the default values if not provided based on the mongoose models.
setDefaultsOnInsert: true
}, (err, doc, raw) => {
if (err) { return reject(err); }
if (raw.lastErrorObject.updatedExisting) {
return reject(new errors.ErrAlreadyExists(raw.value));
}
return resolve(raw.value);
});
// Only set when not existing.
$setOnInsert: action,
});
// Emit that there was a new action created.
events.emit('actions.new', foundAction);
return foundAction;
}
/**
@@ -190,6 +194,21 @@ module.exports = class ActionsService {
});
}
static async delete({id, user_id}) {
let action = await ActionModel.findOneAndRemove({
id,
user_id,
});
if (!action) {
return;
}
// Emit that the action was deleted.
events.emit('actions.delete', action);
return action;
}
/**
* Finds all comments ids for a specific action.
* @param {String} action_type type of action
+43
View File
@@ -5,6 +5,49 @@ const ActionsService = require('./actions');
const SettingsService = require('./settings');
const errors = require('../errors');
const events = require('./events');
// When a new action is created, modify the comment.
events.on('actions.new', async (action) => {
if (!action || action.item_type !== 'COMMENTS') {
return;
}
const ACTION_TYPE = action.action_type.toLowerCase();
try {
await CommentModel.update({
id: action.item_id,
}, {
$inc: {
[`action_counts.${ACTION_TYPE}`]: 1,
}
});
} catch (err) {
console.error(`Can't increment the action_counts.${ACTION_TYPE}:`, err);
}
});
// When an action is deleted, modify the comment.
events.on('actions.delete', async (action) => {
if (!action || action.item_type !== 'COMMENTS') {
return;
}
const ACTION_TYPE = action.action_type.toLowerCase();
try {
await CommentModel.update({
id: action.item_id,
}, {
$inc: {
[`action_counts.${ACTION_TYPE}`]: -1,
}
});
} catch (err) {
console.error(`Can't decrement the action_counts.${ACTION_TYPE}:`, err);
}
});
module.exports = class CommentsService {
+17
View File
@@ -0,0 +1,17 @@
const {EventEmitter2} = require('eventemitter2');
const debug = require('debug')('talk:services:events');
const enabled = require('debug').enabled('talk:services:events');
const events = new EventEmitter2({
wildcard: true,
});
// If event debugging is enabled, bind the debugger to all events being emitted
// and log a debug message.
if (enabled) {
events.onAny(function(event) {
debug(`[${event}] ${arguments.length - 1} argument${arguments.length - 1 === 1 ? '' : 's'}`);
});
}
module.exports = events;