* feat: Add alias property to task model
This commit is contained in:
Blade Barringer
2016-06-16 12:28:45 -05:00
committed by GitHub
parent c34c21192b
commit b7b61e6251
21 changed files with 536 additions and 97 deletions

View File

@@ -16,6 +16,22 @@ import logger from '../../libs/api-v3/logger';
let api = {};
async function _validateTaskAlias (tasks, res) {
let tasksWithAliases = tasks.filter(task => task.alias);
let aliases = tasksWithAliases.map(task => task.alias);
// Compares the short names in tasks against
// a Set, where values cannot repeat. If the
// lengths are different, some name was duplicated
if (aliases.length !== [...new Set(aliases)].length) {
throw new BadRequest(res.t('taskAliasAlreadyUsed'));
}
await Bluebird.map(tasksWithAliases, (task) => {
return task.validate();
});
}
// challenge must be passed only when a challenge task is being created
async function _createTasks (req, res, user, challenge) {
let toSave = Array.isArray(req.body) ? req.body : [req.body];
@@ -42,7 +58,12 @@ async function _createTasks (req, res, user, challenge) {
(challenge || user).tasksOrder[`${taskType}s`].unshift(newTask._id);
return newTask;
}).map(task => task.save({ // If all tasks are valid (this is why it's not in the previous .map()), save everything, withough running validation again
});
// tasks with aliases need to be validated asyncronously
await _validateTaskAlias(toSave, res);
toSave = toSave.map(task => task.save({ // If all tasks are valid (this is why it's not in the previous .map()), save everything, withough running validation again
validateBeforeSave: false,
}));
@@ -233,7 +254,7 @@ api.getChallengeTasks = {
* @apiName GetTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
*
* @apiSuccess {object} data The task object
*/
@@ -243,15 +264,8 @@ api.getTask = {
middlewares: [authWithHeaders()],
async handler (req, res) {
let user = res.locals.user;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id);
if (!task) {
throw new NotFound(res.t('taskNotFound'));
@@ -274,7 +288,7 @@ api.getTask = {
* @apiName UpdateTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
*
* @apiSuccess {object} data The updated task
*/
@@ -286,14 +300,13 @@ api.updateTask = {
let user = res.locals.user;
let challenge;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id);
if (!task) {
throw new NotFound(res.t('taskNotFound'));
@@ -308,7 +321,6 @@ api.updateTask = {
// we have to convert task to an object because otherwise things don't get merged correctly. Bad for performances?
let [updatedTaskObj] = common.ops.updateTask(task.toObject(), req);
// Sanitize differently user tasks linked to a challenge
let sanitizedObj;
@@ -362,7 +374,7 @@ function _generateWebhookTaskData (task, direction, delta, stats, user) {
* @apiName ScoreTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {string="up","down"} direction The direction for scoring the task
*
* @apiSuccess {object} data._tmp If an item was dropped it'll be returned in te _tmp object
@@ -374,19 +386,16 @@ api.scoreTask = {
url: '/tasks/:taskId/score/:direction',
middlewares: [authWithHeaders()],
async handler (req, res) {
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('direction', res.t('directionUpDown')).notEmpty().isIn(['up', 'down']);
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let user = res.locals.user;
let direction = req.params.direction;
let {taskId} = req.params;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
userId: user._id,
}).exec();
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, {userId: user._id});
let direction = req.params.direction;
if (!task) throw new NotFound(res.t('taskNotFound'));
@@ -448,7 +457,7 @@ api.scoreTask = {
* @apiName MoveTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {Number} position Query parameter - Where to move the task (-1 means push to bottom). First position is 0
*
* @apiSuccess {array} data The new tasks order (user.tasksOrder.{task.type}s)
@@ -458,19 +467,17 @@ api.moveTask = {
url: '/tasks/:taskId/move/to/:position',
middlewares: [authWithHeaders()],
async handler (req, res) {
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
req.checkParams('position', res.t('positionRequired')).notEmpty().isNumeric();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let user = res.locals.user;
let taskId = req.params.taskId;
let to = Number(req.params.position);
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
userId: user._id,
}).exec();
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id });
if (!task) throw new NotFound(res.t('taskNotFound'));
if (task.type === 'todo' && task.completed) throw new BadRequest(res.t('cantMoveCompletedTodo'));
@@ -503,7 +510,7 @@ api.moveTask = {
* @apiName AddChecklistItem
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
*
* @apiSuccess {object} data The updated task
*/
@@ -515,14 +522,13 @@ api.addChecklistItem = {
let user = res.locals.user;
let challenge;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id);
if (!task) {
throw new NotFound(res.t('taskNotFound'));
@@ -552,7 +558,7 @@ api.addChecklistItem = {
* @apiName ScoreChecklistItem
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {UUID} itemId The checklist item _id
*
* @apiSuccess {object} data The updated task
@@ -564,16 +570,14 @@ api.scoreCheckListItem = {
async handler (req, res) {
let user = res.locals.user;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
userId: user._id,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id });
if (!task) throw new NotFound(res.t('taskNotFound'));
if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo'));
@@ -594,7 +598,7 @@ api.scoreCheckListItem = {
* @apiName UpdateChecklistItem
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {UUID} itemId The checklist item _id
*
* @apiSuccess {object} data The updated task
@@ -607,15 +611,14 @@ api.updateChecklistItem = {
let user = res.locals.user;
let challenge;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id);
if (!task) {
throw new NotFound(res.t('taskNotFound'));
@@ -647,7 +650,7 @@ api.updateChecklistItem = {
* @apiName RemoveChecklistItem
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {UUID} itemId The checklist item _id
*
* @apiSuccess {object} data The updated task
@@ -660,15 +663,14 @@ api.removeChecklistItem = {
let user = res.locals.user;
let challenge;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id);
if (!task) {
throw new NotFound(res.t('taskNotFound'));
@@ -698,7 +700,7 @@ api.removeChecklistItem = {
* @apiName AddTagToTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {UUID} tagId The tag id
*
* @apiSuccess {object} data The updated task
@@ -710,17 +712,15 @@ api.addTagToTask = {
async handler (req, res) {
let user = res.locals.user;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
let userTags = user.tags.map(tag => tag.id);
req.checkParams('tagId', res.t('tagIdRequired')).notEmpty().isUUID().isIn(userTags);
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
userId: user._id,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id });
if (!task) throw new NotFound(res.t('taskNotFound'));
let tagId = req.params.tagId;
@@ -741,7 +741,7 @@ api.addTagToTask = {
* @apiName RemoveTagFromTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {UUID} tagId The tag id
*
* @apiSuccess {object} data The updated task
@@ -753,16 +753,14 @@ api.removeTagFromTask = {
async handler (req, res) {
let user = res.locals.user;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty();
req.checkParams('tagId', res.t('tagIdRequired')).notEmpty().isUUID();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let task = await Tasks.Task.findOne({
_id: req.params.taskId,
userId: user._id,
}).exec();
let taskId = req.params.taskId;
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id });
if (!task) throw new NotFound(res.t('taskNotFound'));
@@ -842,7 +840,7 @@ api.unlinkAllTasks = {
* @apiName UnlinkOneTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
* @apiParam {string} keep Query parameter - keep or remove
*
* @apiSuccess {object} data An empty object
@@ -862,10 +860,7 @@ api.unlinkOneTask = {
let keep = req.query.keep;
let taskId = req.params.taskId;
let task = await Tasks.Task.findOne({
_id: taskId,
userId: user._id,
}).exec();
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id, { userId: user._id });
if (!task) throw new NotFound(res.t('taskNotFound'));
if (!task.challenge.id) throw new BadRequest(res.t('cantOnlyUnlinkChalTask'));
@@ -924,7 +919,7 @@ api.clearCompletedTodos = {
* @apiName DeleteTask
* @apiGroup Task
*
* @apiParam {UUID} taskId The task _id
* @apiParam {UUID|string} taskId The task _id or alias
*
* @apiSuccess {object} data An empty object
*/
@@ -936,13 +931,8 @@ api.deleteTask = {
let user = res.locals.user;
let challenge;
req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let taskId = req.params.taskId;
let task = await Tasks.Task.findById(taskId).exec();
let task = await Tasks.Task.findByIdOrAlias(taskId, user._id);
if (!task) {
throw new NotFound(res.t('taskNotFound'));