add unlinkTask route and refactor user.unlink (now user.unlinkChallengesTasks)

This commit is contained in:
Matteo Pagliazzi
2016-01-14 19:46:43 +01:00
parent 01458574f9
commit 5206469e90
5 changed files with 95 additions and 54 deletions

View File

@@ -37,6 +37,7 @@
"memberCannotRemoveYourself": "You cannot remove yourself!", "memberCannotRemoveYourself": "You cannot remove yourself!",
"groupMemberNotFound": "User not found among group's members", "groupMemberNotFound": "User not found among group's members",
"keepOrRemoveAll": "req.query.keep must be either \"keep-all\" or \"remove-all\"", "keepOrRemoveAll": "req.query.keep must be either \"keep-all\" or \"remove-all\"",
"keepOrRemove": "req.query.keep must be either \"keep\" or \"remove\"",
"canOnlyInviteEmailUuid": "Can only invite using uuids or emails.", "canOnlyInviteEmailUuid": "Can only invite using uuids or emails.",
"inviteMissingEmail": "Missing email address in invite.", "inviteMissingEmail": "Missing email address in invite.",
"onlyGroupLeaderChal": "Only the group leader can create challenges", "onlyGroupLeaderChal": "Only the group leader can create challenges",
@@ -59,5 +60,6 @@
"userWithIDNotFound": "User with id \"<%= userId %>\" not found.", "userWithIDNotFound": "User with id \"<%= userId %>\" not found.",
"uuidsMustBeAnArray": "UUIDs invites must be a an Array.", "uuidsMustBeAnArray": "UUIDs invites must be a an Array.",
"emailsMustBeAnArray": "Email invites must be a an Array.", "emailsMustBeAnArray": "Email invites must be a an Array.",
"canOnlyInviteMaxInvites": "You can only invite \"<%= maxInvites %>\" at a time" "canOnlyInviteMaxInvites": "You can only invite \"<%= maxInvites %>\" at a time",
"cantOnlyUnlinkChalTask": "Only challenges tasks can be unlinked."
} }

View File

@@ -540,7 +540,7 @@ api.moveTask = {
}).exec(); }).exec();
if (!task) throw new NotFound(res.t('taskNotFound')); if (!task) throw new NotFound(res.t('taskNotFound'));
if (task.type === 'todo' && task.completed) throw new NotFound(res.t('cantMoveCompletedTodo')); if (task.type === 'todo' && task.completed) throw new BadRequest(res.t('cantMoveCompletedTodo'));
let order = user.tasksOrder[`${task.type}s`]; let order = user.tasksOrder[`${task.type}s`];
let currentIndex = order.indexOf(task._id); let currentIndex = order.indexOf(task._id);
@@ -837,20 +837,62 @@ api.removeTagFromTask = {
}; };
// Remove a task from (user|challenge).tasksOrder // Remove a task from (user|challenge).tasksOrder
function _removeTaskTasksOrder (userOrChallenge, taskId) { function _removeTaskTasksOrder (userOrChallenge, taskId, taskType) {
// Loop through all lists and when the task is found, remove it and return let list = userOrChallenge.tasksOrder[taskType];
for (let i = 0; i < Tasks.tasksTypes.length; i++) {
let list = userOrChallenge.tasksOrder[`${Tasks.tasksTypes[i]}s`];
let index = list.indexOf(taskId); let index = list.indexOf(taskId);
if (index !== -1) { if (index !== -1) list.splice(index, 1);
list.splice(index, 1); }
break;
// TODO this method needs some limitation, like to check if the challenge is really broken?
/**
* @api {post} /tasks/unlink/:taskId Unlink a challenge task
* @apiVersion 3.0.0
* @apiName UnlinkTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
*
* @apiSuccess {object} empty An empty object
*/
api.unlinkTask = {
method: 'POST',
url: '/tasks/unlink/:taskId',
middlewares: [authWithHeaders(), cron],
async handler (req, res) {
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkQuery('keep', res.t('keepOrRemove')).notEmpty().isIn(['keep', 'remove']);
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let user = res.locals.user;
let keep = req.query.keep;
let taskId = req.params.taskId;
let task = await Tasks.Task.findOne({
_id: taskId,
userId: user._id,
}).exec();
if (!task) throw new NotFound(res.t('taskNotFound'));
if (!task.challenge.id) throw new BadRequest(res.t('cantOnlyUnlinkChalTask'));
if (keep === 'keep') {
task.challenge = {};
await task.save();
} else { // remove
if (task.type !== 'todo' || !task.completed) { // eslint-disable-line no-lonely-if
_removeTaskTasksOrder(user, taskId, task.type);
await Q.all([user.save(), task.remove()]);
} else {
await task.remove();
} }
} }
return; res.respond(200, {}); // TODO what to return
} },
};
/** /**
* @api {delete} /task/:taskId Delete a user task given its id * @api {delete} /task/:taskId Delete a user task given its id
@@ -875,9 +917,8 @@ api.deleteTask = {
let validationErrors = req.validationErrors(); let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors; if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({ let taskId = req.params.taskId;
_id: req.params.taskId, let task = await Tasks.Task.findById(taskId).exec();
}).exec();
if (!task) { if (!task) {
throw new NotFound(res.t('taskNotFound')); throw new NotFound(res.t('taskNotFound'));
@@ -891,8 +932,12 @@ api.deleteTask = {
throw new NotAuthorized(res.t('cantDeleteChallengeTasks')); throw new NotAuthorized(res.t('cantDeleteChallengeTasks'));
} }
_removeTaskTasksOrder(challenge || user, req.params.taskId); if (task.type !== 'todo' || !task.completed) {
await Q.all([user.save(), task.remove()]); _removeTaskTasksOrder(challenge || user, taskId, task.type);
await Q.all([(challenge || user).save(), task.remove()]);
} else {
await task.remove();
}
res.respond(200, {}); res.respond(200, {});
if (challenge) challenge.removeTask(task); if (challenge) challenge.removeTask(task);

View File

@@ -182,7 +182,7 @@ schema.methods.removeTask = async function challengeRemoveTask (task) {
'challenge.taskId': task._id, 'challenge.taskId': task._id,
}, { }, {
$set: {'challenge.broken': 'TASK_DELETED'}, // TODO what about updatedAt? $set: {'challenge.broken': 'TASK_DELETED'}, // TODO what about updatedAt?
}).lean().exec(); }, {multi: true}).exec();
}; };
export let model = mongoose.model('Challenge', schema); export let model = mongoose.model('Challenge', schema);

View File

@@ -439,7 +439,7 @@ schema.statics.bossQuest = function bossQuest (user, progress) {
// Remove user from this group // Remove user from this group
// TODO this is highly inefficient // TODO this is highly inefficient
schema.methods.leave = function leaveGroup (user, keep = 'keep-all') { schema.methods.leave = function leaveGroup (user, keep) {
let group = this; let group = this;
return Q.all([ return Q.all([
@@ -455,12 +455,12 @@ schema.methods.leave = function leaveGroup (user, keep = 'keep-all') {
{_id: {$in: _.pluck(challenges, '_id')}}, {_id: {$in: _.pluck(challenges, '_id')}},
{$pull: {members: user._id}}, {$pull: {members: user._id}},
{multi: true} {multi: true}
).then(() => challenges); // pass `challenges` above to next promise TODO ok to return a non-promise? ).then(() => challenges); // pass `challenges` above to next promise
}).then(challenges => { }).then(challenges => {
return Q.all(challenges.map(chal => { return Q.all(challenges.map(chal => {
let i = user.challenges.indexOf(chal._id); let i = user.challenges.indexOf(chal._id);
if (i !== -1) user.challenges.splice(i, 1); if (i !== -1) user.challenges.splice(i, 1);
return user.unlink({cid: chal._id, keep}); return user.unlinkChallengeTasks(chal._id, keep);
})); }));
}), }),

View File

@@ -643,40 +643,34 @@ schema.methods.isSubscribed = function isSubscribed () {
return !!this.purchased.plan.customerId; // eslint-disable-line no-implicit-coercion return !!this.purchased.plan.customerId; // eslint-disable-line no-implicit-coercion
}; };
schema.methods.unlink = function unlink (options, cb) { // Unlink challenges tasks from user
let cid = options.cid; schema.methods.unlinkChallengeTasks = async function unlinkChallengeTasks (challengeId, keep) {
let keep = options.keep; let user = this;
let tid = options.tid; let findQuery = {
userId: user._id,
'challenge.id': challengeId,
};
if (!cid) { if (keep === 'keep-all') {
return cb('Could not remove challenge tasks. Please delete them manually.'); await Tasks.Task.update(findQuery, {
$set: {challenge: {}}, // TODO what about updatedAt?
}, {multi: true}).exec();
} else { // keep = 'remove-all'
let tasks = Tasks.Task.find(findQuery).select('_id type completed').exec();
tasks = tasks.map(task => {
// Remove task from user.tasksOrder and delete them
if (task.type !== 'todo' || !task.completed) {
let list = user.tasksOrder[task.type];
let index = list.indexOf(task._id);
if (index !== -1) list.splice(index, 1);
} }
let self = this; return task.remove();
if (keep === 'keep') {
self.tasks[tid].challenge = {};
} else if (keep === 'remove') {
self.ops.deleteTask({params: {id: tid}}, () => {});
} else if (keep === 'keep-all') {
_.each(self.tasks, (t) => {
if (t.challenge && t.challenge.id === cid) {
t.challenge = {};
}
}); });
} else if (keep === 'remove-all') {
_.each(self.tasks, (t) => {
if (t.challenge && t.challenge.id === cid) {
this.ops.deleteTask({params: {id: tid}}, () => {});
}
});
}
self.markModified('habits'); tasks.push(user.save());
self.markModified('dailys'); await Q.all(tasks);
self.markModified('todos'); }
self.markModified('rewards');
self.save(cb);
}; };
export let model = mongoose.model('User', schema); export let model = mongoose.model('User', schema);