2015-11-28 16:47:32 +00:00
import { authWithHeaders } from '../../middlewares/api-v3/auth' ;
2015-12-14 18:23:13 +00:00
import { sendTaskWebhook } from '../../libs/api-v3/webhook' ;
2016-01-21 14:05:51 +00:00
import { removeFromArray } from '../../libs/api-v3/collectionManipulators' ;
2015-11-28 17:05:07 +00:00
import * as Tasks from '../../models/task' ;
2016-01-04 18:44:39 +00:00
import { model as Challenge } from '../../models/challenge' ;
2016-02-02 10:42:05 +00:00
import { model as Group } from '../../models/group' ;
2015-11-29 17:29:35 +00:00
import {
NotFound ,
NotAuthorized ,
2015-11-30 16:25:40 +00:00
BadRequest ,
2015-11-29 17:29:35 +00:00
} from '../../libs/api-v3/errors' ;
2016-03-05 17:58:40 +00:00
import common from '../../../../common' ;
2016-05-11 12:34:01 +00:00
import Bluebird from 'bluebird' ;
2015-11-29 18:05:24 +00:00
import _ from 'lodash' ;
2016-04-13 20:55:39 +00:00
import logger from '../../libs/api-v3/logger' ;
2015-11-28 16:47:32 +00:00
let api = { } ;
2016-01-06 17:40:11 +00:00
// 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 ] ;
toSave = toSave . map ( taskData => {
// Validate that task.type is valid
if ( ! taskData || Tasks . tasksTypes . indexOf ( taskData . type ) === - 1 ) throw new BadRequest ( res . t ( 'invalidTaskType' ) ) ;
let taskType = taskData . type ;
2016-04-12 17:30:39 +00:00
let newTask = new Tasks [ taskType ] ( Tasks . Task . sanitize ( taskData ) ) ;
2016-01-06 17:40:11 +00:00
if ( challenge ) {
newTask . challenge . id = challenge . id ;
} else {
newTask . userId = user . _id ;
}
// Validate that the task is valid and throw if it isn't
// otherwise since we're saving user/challenge and task in parallel it could save the user/challenge with a tasksOrder that doens't match reality
let validationErrors = newTask . validateSync ( ) ;
if ( validationErrors ) throw validationErrors ;
// Otherwise update the 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
validateBeforeSave : false ,
} ) ) ;
toSave . unshift ( ( challenge || user ) . save ( ) ) ;
2016-05-11 12:34:01 +00:00
let tasks = await Bluebird . all ( toSave ) ;
2016-01-06 17:40:11 +00:00
tasks . splice ( 0 , 1 ) ; // Remove user or challenge
return tasks ;
}
2015-11-28 16:47:32 +00:00
/ * *
2016-04-16 23:17:22 +00:00
* @ api { post } / api / v3 / tasks / user Create a new task the user .
* @ apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks .
2015-11-28 16:47:32 +00:00
* @ apiVersion 3.0 . 0
2016-01-06 17:40:11 +00:00
* @ apiName CreateUserTasks
2015-11-28 16:47:32 +00:00
* @ apiGroup Task
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess data An object if a single task was created , otherwise an array of tasks
2016-01-06 17:40:11 +00:00
* /
api . createUserTasks = {
method : 'POST' ,
url : '/tasks/user' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2016-01-06 17:40:11 +00:00
async handler ( req , res ) {
let tasks = await _createTasks ( req , res , res . locals . user ) ;
res . respond ( 201 , tasks . length === 1 ? tasks [ 0 ] : tasks ) ;
} ,
} ;
/ * *
2016-04-16 23:17:22 +00:00
* @ api { post } / api / v3 / tasks / challenge / : challengeId Create a new task belonging to a challenge .
* @ apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks .
2016-01-06 17:40:11 +00:00
* @ apiVersion 3.0 . 0
* @ apiName CreateChallengeTasks
* @ apiGroup Task
*
* @ apiParam { UUID } challengeId The id of the challenge the new task ( s ) will belong to .
2016-01-04 18:44:39 +00:00
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess data An object if a single task was created , otherwise an array of tasks
2015-11-28 16:47:32 +00:00
* /
2016-01-06 17:40:11 +00:00
api . createChallengeTasks = {
2015-11-28 16:47:32 +00:00
method : 'POST' ,
2016-05-06 18:24:53 +00:00
url : '/tasks/challenge/:challengeId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2016-01-17 17:31:03 +00:00
req . checkParams ( 'challengeId' , res . t ( 'challengeIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2016-01-04 21:03:21 +00:00
2016-01-05 19:19:55 +00:00
let reqValidationErrors = req . validationErrors ( ) ;
if ( reqValidationErrors ) throw reqValidationErrors ;
2016-01-04 21:03:21 +00:00
2016-01-15 17:54:28 +00:00
let user = res . locals . user ;
2016-01-06 17:40:11 +00:00
let challengeId = req . params . challengeId ;
2016-01-04 18:44:39 +00:00
2016-01-06 17:40:11 +00:00
let challenge = await Challenge . findOne ( { _id : challengeId } ) . exec ( ) ;
2016-01-04 18:44:39 +00:00
2016-01-06 17:40:11 +00:00
// If the challenge does not exist, or if it exists but user is not the leader -> throw error
if ( ! challenge || user . challenges . indexOf ( challengeId ) === - 1 ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
if ( challenge . leader !== user . _id ) throw new NotAuthorized ( res . t ( 'onlyChalLeaderEditTasks' ) ) ;
2015-11-28 16:47:32 +00:00
2016-01-06 17:40:11 +00:00
let tasks = await _createTasks ( req , res , user , challenge ) ;
Added intitial group leave tests
Fixed test readability, updated party test, and updated challenge update code when leaving group
Updated library, added group existance check, and reset full party
Updated syntax, added new userUnlinkChallenges, and added some initial testing for challenges
Added challenge tasks tests
Added try/catch to group remove, add more party tests, fixed broken challenge test, removed useless return value
Added public guild tests, added more tests to party, and abstracted remove invitations logic
Closes #6506
2016-01-13 00:00:37 +00:00
2016-01-06 17:40:11 +00:00
res . respond ( 201 , tasks . length === 1 ? tasks [ 0 ] : tasks ) ;
2016-01-02 14:43:33 +00:00
2016-01-06 17:40:11 +00:00
// If adding tasks to a challenge -> sync users
2016-04-13 16:18:00 +00:00
if ( challenge ) challenge . addTasks ( tasks ) ;
2016-05-12 16:06:26 +00:00
return null ;
2016-01-06 17:40:11 +00:00
} ,
} ;
2016-01-04 18:44:39 +00:00
2016-01-06 17:40:11 +00:00
// challenge must be passed only when a challenge task is being created
async function _getTasks ( req , res , user , challenge ) {
let query = challenge ? { 'challenge.id' : challenge . id , userId : { $exists : false } } : { userId : user . _id } ;
let type = req . query . type ;
if ( type ) {
2016-02-02 10:42:05 +00:00
if ( type === 'todos' ) {
query . completed = false ; // Exclude completed todos
} else if ( type === 'completedTodos' ) {
query = Tasks . Task . find ( {
userId : user . _id ,
type : 'todo' ,
completed : true ,
} ) . limit ( 30 ) . sort ( { // TODO add ability to pick more than 30 completed todos
dateCompleted : 1 ,
} ) ;
} else {
query . type = type . slice ( 0 , - 1 ) ; // removing the final "s"
}
2016-01-06 17:40:11 +00:00
} else {
query . $or = [ // Exclude completed todos
{ type : 'todo' , completed : false } ,
{ type : { $in : [ 'habit' , 'daily' , 'reward' ] } } ,
] ;
}
2016-02-02 10:42:05 +00:00
let tasks = await Tasks . Task . find ( query ) . exec ( ) ;
2016-02-02 11:23:56 +00:00
// Order tasks based on tasksOrder
if ( type && type !== 'completedTodos' ) {
let order = ( challenge || user ) . tasksOrder [ type ] ;
let orderedTasks = new Array ( tasks . length ) ;
let unorderedTasks = [ ] ; // what we want to add later
tasks . forEach ( ( task , index ) => {
let taskId = task . _id ;
let i = order [ index ] === taskId ? index : order . indexOf ( taskId ) ;
if ( i === - 1 ) {
unorderedTasks . push ( task ) ;
} else {
orderedTasks [ i ] = task ;
}
} ) ;
// Remove empty values from the array and add any unordered task
orderedTasks = _ . compact ( orderedTasks ) . concat ( unorderedTasks ) ;
res . respond ( 200 , orderedTasks ) ;
} else {
res . respond ( 200 , tasks ) ;
}
2016-01-06 17:40:11 +00:00
}
2015-11-28 16:47:32 +00:00
2016-01-06 17:40:11 +00:00
/ * *
2016-05-14 23:10:08 +00:00
* @ api { get } / api / v3 / tasks / user Get a user ' s tasks
2016-01-06 17:40:11 +00:00
* @ apiVersion 3.0 . 0
* @ apiName GetUserTasks
* @ apiGroup Task
*
2016-02-02 11:23:56 +00:00
* @ apiParam { string = "habits" , "dailys" , "todos" , "rewards" , "completedTodos" } type Optional query parameter to return just a type of tasks . By default all types will be returned except completed todos that requested separately .
2016-01-06 17:40:11 +00:00
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { Array } data An array of tasks
2016-01-06 17:40:11 +00:00
* /
api . getUserTasks = {
method : 'GET' ,
url : '/tasks/user' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2016-01-06 17:40:11 +00:00
async handler ( req , res ) {
2016-02-02 10:42:05 +00:00
let types = Tasks . tasksTypes . map ( type => ` ${ type } s ` ) ;
types . push ( 'completedTodos' ) ;
req . checkQuery ( 'type' , res . t ( 'invalidTaskType' ) ) . optional ( ) . isIn ( types ) ;
2015-11-28 17:18:28 +00:00
2016-01-06 17:40:11 +00:00
let validationErrors = req . validationErrors ( ) ;
if ( validationErrors ) throw validationErrors ;
2015-12-31 10:46:21 +00:00
2016-05-12 16:06:26 +00:00
return await _getTasks ( req , res , res . locals . user ) ;
2016-01-06 17:40:11 +00:00
} ,
} ;
2016-01-04 21:03:21 +00:00
2016-01-06 17:40:11 +00:00
/ * *
2016-04-14 14:02:28 +00:00
* @ api { get } / api / v3 / tasks / challenge / : challengeId Get a challenge ' s tasks
2016-01-06 17:40:11 +00:00
* @ apiVersion 3.0 . 0
* @ apiName GetChallengeTasks
* @ apiGroup Task
*
* @ apiParam { UUID } challengeId The id of the challenge from which to retrieve the tasks .
2016-02-02 10:42:05 +00:00
* @ apiParam { string = "habits" , "dailys" , "todos" , "rewards" } type Optional query parameter to return just a type of tasks
2016-01-06 17:40:11 +00:00
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { Array } data An array of tasks
2016-01-06 17:40:11 +00:00
* /
api . getChallengeTasks = {
method : 'GET' ,
url : '/tasks/challenge/:challengeId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2016-01-06 17:40:11 +00:00
async handler ( req , res ) {
2016-01-17 17:31:03 +00:00
req . checkParams ( 'challengeId' , res . t ( 'challengeIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2016-02-02 10:42:05 +00:00
let types = Tasks . tasksTypes . map ( type => ` ${ type } s ` ) ;
req . checkQuery ( 'type' , res . t ( 'invalidTaskType' ) ) . optional ( ) . isIn ( types ) ;
2016-01-06 17:40:11 +00:00
let validationErrors = req . validationErrors ( ) ;
if ( validationErrors ) throw validationErrors ;
2016-01-15 17:54:28 +00:00
let user = res . locals . user ;
2016-01-06 17:40:11 +00:00
let challengeId = req . params . challengeId ;
2016-02-02 20:47:12 +00:00
let challenge = await Challenge . findOne ( { _id : challengeId } ) . select ( 'group leader tasksOrder' ) . exec ( ) ;
2016-02-02 10:42:05 +00:00
if ( ! challenge ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
2016-02-02 20:47:12 +00:00
let group = await Group . getGroup ( { user , groupId : challenge . group , fields : '_id type privacy' , optionalMembership : true } ) ;
2016-02-02 10:42:05 +00:00
if ( ! group || ! challenge . canView ( user , group ) ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
2016-01-04 18:44:39 +00:00
2016-05-12 16:06:26 +00:00
return await _getTasks ( req , res , res . locals . user , challenge ) ;
2015-11-28 16:47:32 +00:00
} ,
} ;
/ * *
2016-04-16 23:17:22 +00:00
* @ api { get } / api / v3 / task / : taskId Get a task
2015-11-28 16:47:32 +00:00
* @ apiVersion 3.0 . 0
* @ apiName GetTask
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The task object
2015-11-28 16:47:32 +00:00
* /
api . getTask = {
method : 'GET' ,
url : '/tasks/:taskId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-28 16:47:32 +00:00
let user = res . locals . user ;
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-11-28 16:47:32 +00:00
_id : req . params . taskId ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! task ) {
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
} else if ( ! task . userId ) { // If the task belongs to a challenge make sure the user has rights
2016-03-08 18:42:05 +00:00
let challenge = await Challenge . find ( { _id : task . challenge . id } ) . select ( 'leader' ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! challenge || ( user . challenges . indexOf ( task . challenge . id ) === - 1 && challenge . leader !== user . _id && ! user . contributor . admin ) ) { // eslint-disable-line no-extra-parens
2016-01-04 18:57:20 +00:00
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
}
2016-05-14 23:10:08 +00:00
} else if ( task . userId !== user . _id ) { // If the task is owned by a user make it's the current one
2016-01-04 18:57:20 +00:00
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
}
2015-12-31 10:46:21 +00:00
res . respond ( 200 , task ) ;
2015-11-28 16:47:32 +00:00
} ,
} ;
2015-11-29 16:47:10 +00:00
2015-11-29 18:05:24 +00:00
/ * *
2016-04-14 14:02:28 +00:00
* @ api { put } / api / v3 / task / : taskId Update a task
2015-11-29 18:05:24 +00:00
* @ apiVersion 3.0 . 0
2015-11-30 16:25:40 +00:00
* @ apiName UpdateTask
2015-11-29 18:05:24 +00:00
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The updated task
2015-11-29 18:05:24 +00:00
* /
api . updateTask = {
2015-11-30 16:25:40 +00:00
method : 'PUT' ,
2015-11-29 18:05:24 +00:00
url : '/tasks/:taskId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-29 18:05:24 +00:00
let user = res . locals . user ;
2016-01-04 19:48:48 +00:00
let challenge ;
2015-11-30 16:25:40 +00:00
2015-11-29 18:05:24 +00:00
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-11-29 18:05:24 +00:00
_id : req . params . taskId ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
2015-12-06 16:29:14 +00:00
2016-01-04 19:48:48 +00:00
if ( ! task ) {
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
} else if ( ! task . userId ) { // If the task belongs to a challenge make sure the user has rights
2016-03-10 21:30:22 +00:00
challenge = await Challenge . findOne ( { _id : task . challenge . id } ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! challenge ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
if ( challenge . leader !== user . _id ) throw new NotAuthorized ( res . t ( 'onlyChalLeaderEditTasks' ) ) ;
2016-05-14 23:10:08 +00:00
} else if ( task . userId !== user . _id ) { // If the task is owned by a user make it's the current one
2016-01-04 19:48:48 +00:00
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
}
2015-12-06 16:29:14 +00:00
2016-04-16 23:17:22 +00:00
// we have to convert task to an object because otherwise things don't get merged correctly. Bad for performances?
2016-04-20 22:52:03 +00:00
let [ updatedTaskObj ] = common . ops . updateTask ( task . toObject ( ) , req ) ;
_ . assign ( task , Tasks . Task . sanitize ( updatedTaskObj ) ) ;
2016-04-16 23:17:22 +00:00
// console.log(task.modifiedPaths(), task.toObject().repeat === tep)
2015-12-31 10:46:21 +00:00
// repeat is always among modifiedPaths because mongoose changes the other of the keys when using .toObject()
// see https://github.com/Automattic/mongoose/issues/2749
let savedTask = await task . save ( ) ;
res . respond ( 200 , savedTask ) ;
2016-04-13 16:18:00 +00:00
if ( challenge ) challenge . updateTask ( savedTask ) ;
2016-05-12 16:06:26 +00:00
return null ;
2015-11-29 18:05:24 +00:00
} ,
} ;
2015-12-06 17:03:51 +00:00
function _generateWebhookTaskData ( task , direction , delta , stats , user ) {
let extendedStats = _ . extend ( stats , {
2016-03-05 17:58:40 +00:00
toNextLevel : common . tnl ( user . stats . lvl ) ,
maxHealth : common . maxHealth ,
2015-12-06 17:03:51 +00:00
maxMP : user . _statsComputed . maxMP , // TODO refactor as method not getter
} ) ;
let userData = {
_id : user . _id ,
_tmp : user . _tmp ,
stats : extendedStats ,
} ;
let taskData = {
details : task ,
direction ,
delta ,
} ;
return {
task : taskData ,
user : userData ,
} ;
}
2015-12-02 10:22:53 +00:00
/ * *
2016-04-14 14:02:28 +00:00
* @ api { put } / api / v3 / tasks / : taskId / score / : direction Score a task
2015-12-02 10:22:53 +00:00
* @ apiVersion 3.0 . 0
* @ apiName ScoreTask
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
* @ apiParam { string = "up" , "down" } direction The direction for scoring the task
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data . _tmp If an item was dropped it ' ll be returned in te _tmp object
* @ apiSuccess { number } data . delta
* @ apiSuccess { object } data The user stats
2015-12-02 10:22:53 +00:00
* /
api . scoreTask = {
method : 'POST' ,
2015-12-11 10:41:46 +00:00
url : '/tasks/:taskId/score/:direction' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-12-02 10:22:53 +00:00
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2016-05-06 18:24:53 +00:00
req . checkParams ( 'direction' , res . t ( 'directionUpDown' ) ) . notEmpty ( ) . isIn ( [ 'up' , 'down' ] ) ;
2015-12-02 10:22:53 +00:00
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-12-02 10:22:53 +00:00
let user = res . locals . user ;
2015-12-06 17:03:51 +00:00
let direction = req . params . direction ;
2015-12-02 10:22:53 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-12-02 10:22:53 +00:00
_id : req . params . taskId ,
userId : user . _id ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
2015-12-06 17:03:51 +00:00
2015-12-31 10:46:21 +00:00
if ( ! task ) throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
2015-12-06 17:03:51 +00:00
2015-12-31 10:46:21 +00:00
let wasCompleted = task . completed ;
2016-04-20 22:52:03 +00:00
let [ delta ] = common . ops . scoreTask ( { task , user , direction } , req ) ;
2015-12-31 10:46:21 +00:00
// Drop system (don't run on the client, as it would only be discarded since ops are sent to the API, not the results)
if ( direction === 'up' ) user . fns . randomDrop ( { task , delta } , req ) ;
// If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list
2016-04-04 15:16:36 +00:00
// TODO move to common code?
2015-12-31 10:46:21 +00:00
if ( task . type === 'todo' ) {
if ( ! wasCompleted && task . completed ) {
2016-01-21 14:05:51 +00:00
removeFromArray ( user . tasksOrder . todos , task . _id ) ;
2015-12-31 10:46:21 +00:00
} else if ( wasCompleted && ! task . completed ) {
2016-01-21 14:05:51 +00:00
let hasTask = removeFromArray ( user . tasksOrder . todos , task . _id ) ;
if ( ! hasTask ) {
2016-05-06 18:24:53 +00:00
user . tasksOrder . todos . push ( task . _id ) ;
2016-04-16 23:17:22 +00:00
} // If for some reason it hadn't been removed previously don't do anything
2015-12-16 11:57:19 +00:00
}
2015-12-31 10:46:21 +00:00
}
2015-12-16 11:57:19 +00:00
2016-05-11 12:34:01 +00:00
let results = await Bluebird . all ( [
2015-12-31 10:46:21 +00:00
user . save ( ) ,
task . save ( ) ,
] ) ;
let savedUser = results [ 0 ] ;
let userStats = savedUser . stats . toJSON ( ) ;
let resJsonData = _ . extend ( { delta , _tmp : user . _tmp } , userStats ) ;
res . respond ( 200 , resJsonData ) ;
sendTaskWebhook ( user . preferences . webhooks , _generateWebhookTaskData ( task , direction , delta , userStats , user ) ) ;
if ( task . challenge . id && task . challenge . taskId && ! task . challenge . broken && task . type !== 'reward' ) {
// Wrapping everything in a try/catch block because if an error occurs using `await` it MUST NOT bubble up because the request has already been handled
try {
let chalTask = await Tasks . Task . findOne ( {
_id : task . challenge . taskId ,
} ) . exec ( ) ;
2016-03-18 19:00:14 +00:00
await chalTask . scoreChallengeTask ( delta ) ;
2015-12-31 10:46:21 +00:00
} catch ( e ) {
2016-04-13 20:55:39 +00:00
logger . error ( e ) ;
2015-12-31 10:46:21 +00:00
}
}
2016-05-12 16:06:26 +00:00
return null ;
2015-12-02 10:22:53 +00:00
} ,
} ;
2015-11-30 19:14:53 +00:00
// completed todos cannot be moved, they'll be returned ordered by date of completion
2016-01-04 19:48:48 +00:00
// TODO support challenges?
2015-11-30 19:14:53 +00:00
/ * *
2016-04-14 14:02:28 +00:00
* @ api { post } / api / v3 / tasks / : taskId / move / to / : position Move a task to a new position
2015-11-30 19:14:53 +00:00
* @ apiVersion 3.0 . 0
* @ apiName MoveTask
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
2016-04-16 23:17:22 +00:00
* @ apiParam { Number } position Query parameter - Where to move the task ( - 1 means push to bottom ) . First position is 0
2015-11-30 19:14:53 +00:00
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { array } data The new tasks order ( user . tasksOrder . { task . type } s )
2015-11-30 19:14:53 +00:00
* /
api . moveTask = {
method : 'POST' ,
2016-02-02 10:14:55 +00:00
url : '/tasks/:taskId/move/to/:position' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-30 19:14:53 +00:00
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
req . checkParams ( 'position' , res . t ( 'positionRequired' ) ) . notEmpty ( ) . isNumeric ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-11-30 19:14:53 +00:00
let user = res . locals . user ;
let to = Number ( req . params . position ) ;
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-11-30 19:14:53 +00:00
_id : req . params . taskId ,
userId : user . _id ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
if ( ! task ) throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
2016-01-14 18:46:43 +00:00
if ( task . type === 'todo' && task . completed ) throw new BadRequest ( res . t ( 'cantMoveCompletedTodo' ) ) ;
2015-12-31 10:46:21 +00:00
let order = user . tasksOrder [ ` ${ task . type } s ` ] ;
let currentIndex = order . indexOf ( task . _id ) ;
2016-02-02 10:14:55 +00:00
// If for some reason the task isn't ordered (should never happen), push it in the new position
// if the task is moved to a non existing position
// or if the task is moved to position -1 (push to bottom)
2015-12-31 10:46:21 +00:00
// -> push task at end of list
2016-02-02 10:14:55 +00:00
if ( ! order [ to ] && to !== - 1 ) {
2015-12-31 10:46:21 +00:00
order . push ( task . _id ) ;
} else {
2016-02-02 10:14:55 +00:00
if ( currentIndex !== - 1 ) order . splice ( currentIndex , 1 ) ;
if ( to === - 1 ) {
order . push ( task . _id ) ;
} else {
order . splice ( to , 0 , task . _id ) ;
}
2015-12-31 10:46:21 +00:00
}
2015-11-30 19:14:53 +00:00
2015-12-31 10:46:21 +00:00
await user . save ( ) ;
2016-02-02 10:14:55 +00:00
res . respond ( 200 , order ) ;
2015-11-30 19:14:53 +00:00
} ,
} ;
2015-11-30 16:25:40 +00:00
/ * *
2016-04-16 23:17:22 +00:00
* @ api { post } / api / v3 / tasks / : taskId / checklist Add an item to the task ' s checklist
2015-11-30 16:25:40 +00:00
* @ apiVersion 3.0 . 0
* @ apiName AddChecklistItem
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The updated task
2015-11-30 16:25:40 +00:00
* /
api . addChecklistItem = {
method : 'POST' ,
2015-12-03 18:19:53 +00:00
url : '/tasks/:taskId/checklist' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-30 16:25:40 +00:00
let user = res . locals . user ;
2016-01-04 19:48:48 +00:00
let challenge ;
2015-11-30 16:25:40 +00:00
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-11-30 16:25:40 +00:00
_id : req . params . taskId ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! task ) {
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
} else if ( ! task . userId ) { // If the task belongs to a challenge make sure the user has rights
2016-03-11 20:19:46 +00:00
challenge = await Challenge . findOne ( { _id : task . challenge . id } ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! challenge ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
if ( challenge . leader !== user . _id ) throw new NotAuthorized ( res . t ( 'onlyChalLeaderEditTasks' ) ) ;
2016-05-14 23:10:08 +00:00
} else if ( task . userId !== user . _id ) { // If the task is owned by a user make it's the current one
2016-01-04 19:48:48 +00:00
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
}
2015-12-31 10:46:21 +00:00
if ( task . type !== 'daily' && task . type !== 'todo' ) throw new BadRequest ( res . t ( 'checklistOnlyDailyTodo' ) ) ;
2016-04-30 16:34:16 +00:00
task . checklist . push ( Tasks . Task . sanitizeChecklist ( req . body ) ) ;
2015-12-31 10:46:21 +00:00
let savedTask = await task . save ( ) ;
2016-04-16 23:17:22 +00:00
res . respond ( 200 , savedTask ) ;
2016-01-04 19:48:48 +00:00
if ( challenge ) challenge . updateTask ( savedTask ) ;
2016-05-12 16:06:26 +00:00
return null ;
2015-11-30 16:25:40 +00:00
} ,
} ;
/ * *
2016-04-14 14:02:28 +00:00
* @ api { post } / api / v3 / tasks / : taskId / checklist / : itemId / score Score a checklist item
2015-11-30 16:25:40 +00:00
* @ apiVersion 3.0 . 0
* @ apiName ScoreChecklistItem
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
* @ apiParam { UUID } itemId The checklist item _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The updated task
2015-11-30 16:25:40 +00:00
* /
api . scoreCheckListItem = {
method : 'POST' ,
url : '/tasks/:taskId/checklist/:itemId/score' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-30 16:25:40 +00:00
let user = res . locals . user ;
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
req . checkParams ( 'itemId' , res . t ( 'itemIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-11-30 16:25:40 +00:00
_id : req . params . taskId ,
userId : user . _id ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
if ( ! task ) throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
if ( task . type !== 'daily' && task . type !== 'todo' ) throw new BadRequest ( res . t ( 'checklistOnlyDailyTodo' ) ) ;
2016-04-30 16:34:16 +00:00
let item = _ . find ( task . checklist , { id : req . params . itemId } ) ;
2015-12-31 10:46:21 +00:00
if ( ! item ) throw new NotFound ( res . t ( 'checklistItemNotFound' ) ) ;
item . completed = ! item . completed ;
let savedTask = await task . save ( ) ;
2016-04-16 23:17:22 +00:00
res . respond ( 200 , savedTask ) ;
2015-11-30 16:25:40 +00:00
} ,
} ;
/ * *
2016-04-14 14:02:28 +00:00
* @ api { put } / api / v3 / tasks / : taskId / checklist / : itemId Update a checklist item
2015-11-30 16:25:40 +00:00
* @ apiVersion 3.0 . 0
* @ apiName UpdateChecklistItem
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
* @ apiParam { UUID } itemId The checklist item _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The updated task
2015-11-30 16:25:40 +00:00
* /
api . updateChecklistItem = {
method : 'PUT' ,
url : '/tasks/:taskId/checklist/:itemId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-30 16:25:40 +00:00
let user = res . locals . user ;
2016-01-04 19:48:48 +00:00
let challenge ;
2015-11-30 16:25:40 +00:00
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
req . checkParams ( 'itemId' , res . t ( 'itemIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-11-30 16:25:40 +00:00
_id : req . params . taskId ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! task ) {
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
} else if ( ! task . userId ) { // If the task belongs to a challenge make sure the user has rights
2016-03-12 16:38:40 +00:00
challenge = await Challenge . findOne ( { _id : task . challenge . id } ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! challenge ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
if ( challenge . leader !== user . _id ) throw new NotAuthorized ( res . t ( 'onlyChalLeaderEditTasks' ) ) ;
2016-05-14 23:10:08 +00:00
} else if ( task . userId !== user . _id ) { // If the task is owned by a user make it's the current one
2016-01-04 19:48:48 +00:00
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
}
2015-12-31 10:46:21 +00:00
if ( task . type !== 'daily' && task . type !== 'todo' ) throw new BadRequest ( res . t ( 'checklistOnlyDailyTodo' ) ) ;
2016-04-30 16:34:16 +00:00
let item = _ . find ( task . checklist , { id : req . params . itemId } ) ;
2015-12-31 10:46:21 +00:00
if ( ! item ) throw new NotFound ( res . t ( 'checklistItemNotFound' ) ) ;
_ . merge ( item , Tasks . Task . sanitizeChecklist ( req . body ) ) ;
let savedTask = await task . save ( ) ;
2016-04-16 23:17:22 +00:00
res . respond ( 200 , savedTask ) ;
2016-01-04 19:48:48 +00:00
if ( challenge ) challenge . updateTask ( savedTask ) ;
2016-05-12 16:06:26 +00:00
return null ;
2015-11-30 16:25:40 +00:00
} ,
} ;
/ * *
2016-04-14 14:02:28 +00:00
* @ api { delete } / api / v3 / tasks / : taskId / checklist / : itemId Remove a checklist item
2015-11-30 16:25:40 +00:00
* @ apiVersion 3.0 . 0
* @ apiName RemoveChecklistItem
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
* @ apiParam { UUID } itemId The checklist item _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The updated task
2015-11-30 16:25:40 +00:00
* /
api . removeChecklistItem = {
method : 'DELETE' ,
url : '/tasks/:taskId/checklist/:itemId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-30 16:25:40 +00:00
let user = res . locals . user ;
2016-01-04 19:48:48 +00:00
let challenge ;
2015-11-30 16:25:40 +00:00
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
req . checkParams ( 'itemId' , res . t ( 'itemIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 16:48:32 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-11-30 16:25:40 +00:00
_id : req . params . taskId ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! task ) {
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
} else if ( ! task . userId ) { // If the task belongs to a challenge make sure the user has rights
2016-03-12 16:55:12 +00:00
challenge = await Challenge . findOne ( { _id : task . challenge . id } ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! challenge ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
if ( challenge . leader !== user . _id ) throw new NotAuthorized ( res . t ( 'onlyChalLeaderEditTasks' ) ) ;
2016-05-14 23:10:08 +00:00
} else if ( task . userId !== user . _id ) { // If the task is owned by a user make it's the current one
2016-01-04 19:48:48 +00:00
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
}
2015-12-31 10:46:21 +00:00
if ( task . type !== 'daily' && task . type !== 'todo' ) throw new BadRequest ( res . t ( 'checklistOnlyDailyTodo' ) ) ;
2016-04-30 16:34:16 +00:00
let hasItem = removeFromArray ( task . checklist , { id : req . params . itemId } ) ;
2016-01-21 14:05:51 +00:00
if ( ! hasItem ) throw new NotFound ( res . t ( 'checklistItemNotFound' ) ) ;
2015-12-31 10:46:21 +00:00
2016-01-04 19:48:48 +00:00
let savedTask = await task . save ( ) ;
2016-04-16 23:17:22 +00:00
res . respond ( 200 , savedTask ) ;
2016-01-04 19:48:48 +00:00
if ( challenge ) challenge . updateTask ( savedTask ) ;
2016-05-12 16:06:26 +00:00
return null ;
2015-11-30 16:25:40 +00:00
} ,
} ;
2015-12-03 18:19:53 +00:00
/ * *
2016-04-14 14:02:28 +00:00
* @ api { post } / api / v3 / tasks / : taskId / tags / : tagId Add a tag to a task
2015-12-03 18:19:53 +00:00
* @ apiVersion 3.0 . 0
* @ apiName AddTagToTask
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
* @ apiParam { UUID } tagId The tag id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The updated task
2015-12-03 18:19:53 +00:00
* /
api . addTagToTask = {
method : 'POST' ,
2015-12-14 21:44:50 +00:00
url : '/tasks/:taskId/tags/:tagId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-12-03 18:19:53 +00:00
let user = res . locals . user ;
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2016-04-30 16:34:16 +00:00
let userTags = user . tags . map ( tag => tag . id ) ;
2015-12-03 18:19:53 +00:00
req . checkParams ( 'tagId' , res . t ( 'tagIdRequired' ) ) . notEmpty ( ) . isUUID ( ) . isIn ( userTags ) ;
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 18:19:53 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-12-03 18:19:53 +00:00
_id : req . params . taskId ,
userId : user . _id ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
if ( ! task ) throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
let tagId = req . params . tagId ;
let alreadyTagged = task . tags . indexOf ( tagId ) !== - 1 ;
if ( alreadyTagged ) throw new BadRequest ( res . t ( 'alreadyTagged' ) ) ;
task . tags . push ( tagId ) ;
let savedTask = await task . save ( ) ;
2016-04-16 23:17:22 +00:00
res . respond ( 200 , savedTask ) ;
2015-12-03 18:19:53 +00:00
} ,
} ;
/ * *
2016-04-16 23:17:22 +00:00
* @ api { delete } / api / v3 / tasks / : taskId / tags / : tagId Remove a tag from atask
2015-12-03 18:19:53 +00:00
* @ apiVersion 3.0 . 0
* @ apiName RemoveTagFromTask
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
* @ apiParam { UUID } tagId The tag id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data The updated task
2015-12-03 18:19:53 +00:00
* /
api . removeTagFromTask = {
method : 'DELETE' ,
url : '/tasks/:taskId/tags/:tagId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-12-03 18:19:53 +00:00
let user = res . locals . user ;
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
req . checkParams ( 'tagId' , res . t ( 'tagIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 18:19:53 +00:00
2015-12-31 10:46:21 +00:00
let task = await Tasks . Task . findOne ( {
2015-12-03 18:19:53 +00:00
_id : req . params . taskId ,
userId : user . _id ,
2015-12-31 10:46:21 +00:00
} ) . exec ( ) ;
if ( ! task ) throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
2016-01-21 14:05:51 +00:00
let hasTag = removeFromArray ( task . tags , req . params . tagId ) ;
if ( ! hasTag ) throw new NotFound ( res . t ( 'tagNotFound' ) ) ;
2015-12-31 10:46:21 +00:00
2016-04-16 23:17:22 +00:00
let savedTask = await task . save ( ) ;
res . respond ( 200 , savedTask ) ;
2015-12-03 18:19:53 +00:00
} ,
} ;
2016-01-14 18:46:43 +00:00
// TODO this method needs some limitation, like to check if the challenge is really broken?
/ * *
2016-04-14 14:02:28 +00:00
* @ api { post } / api / v3 / tasks / unlink / : taskId Unlink a challenge task
2016-01-14 18:46:43 +00:00
* @ apiVersion 3.0 . 0
* @ apiName UnlinkTask
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data An empty object
2016-01-14 18:46:43 +00:00
* /
api . unlinkTask = {
method : 'POST' ,
url : '/tasks/unlink/:taskId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2016-01-14 18:46:43 +00:00
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
2016-01-21 14:05:51 +00:00
removeFromArray ( user . tasksOrder [ ` ${ task . type } s ` ] , taskId ) ;
2016-05-11 12:34:01 +00:00
await Bluebird . all ( [ user . save ( ) , task . remove ( ) ] ) ;
2016-01-14 18:46:43 +00:00
} else {
await task . remove ( ) ;
}
}
2016-04-16 23:17:22 +00:00
res . respond ( 200 , { } ) ;
2016-01-14 18:46:43 +00:00
} ,
} ;
2015-11-29 16:47:10 +00:00
/ * *
2016-04-14 14:02:28 +00:00
* @ api { post } / api / v3 / tasks / clearCompletedTodos Delete user ' s completed todos
2016-01-25 16:23:01 +00:00
* @ apiVersion 3.0 . 0
* @ apiName ClearCompletedTodos
* @ apiGroup Task
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data An empty object
2016-01-25 16:23:01 +00:00
* /
api . clearCompletedTodos = {
method : 'POST' ,
url : '/tasks/clearCompletedTodos' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2016-01-25 16:23:01 +00:00
async handler ( req , res ) {
let user = res . locals . user ;
// Clear completed todos
2016-04-03 19:50:32 +00:00
// Do not delete challenges completed todos unless the task is broken
2016-01-25 16:23:01 +00:00
await Tasks . Task . remove ( {
userId : user . _id ,
type : 'todo' ,
completed : true ,
2016-04-03 19:50:32 +00:00
$or : [
{ 'challenge.id' : { $exists : false } } ,
{ 'challenge.broken' : { $exists : true } } ,
] ,
2016-01-25 16:23:01 +00:00
} ) . exec ( ) ;
res . respond ( 200 , { } ) ;
} ,
} ;
/ * *
2016-04-14 14:02:28 +00:00
* @ api { delete } / api / v3 / tasks / : taskId Delete a task given its id
2015-11-29 16:47:10 +00:00
* @ apiVersion 3.0 . 0
* @ apiName DeleteTask
* @ apiGroup Task
*
* @ apiParam { UUID } taskId The task _id
*
2016-04-16 23:17:22 +00:00
* @ apiSuccess { object } data An empty object
2015-11-29 16:47:10 +00:00
* /
api . deleteTask = {
2015-12-06 15:51:18 +00:00
method : 'DELETE' ,
2015-11-29 16:47:10 +00:00
url : '/tasks/:taskId' ,
2016-04-14 11:30:58 +00:00
middlewares : [ authWithHeaders ( ) ] ,
2015-12-31 10:46:21 +00:00
async handler ( req , res ) {
2015-11-29 16:47:10 +00:00
let user = res . locals . user ;
2016-01-04 19:48:48 +00:00
let challenge ;
2015-11-29 16:47:10 +00:00
req . checkParams ( 'taskId' , res . t ( 'taskIdRequired' ) ) . notEmpty ( ) . isUUID ( ) ;
2015-12-03 16:48:32 +00:00
let validationErrors = req . validationErrors ( ) ;
2015-12-31 10:46:21 +00:00
if ( validationErrors ) throw validationErrors ;
2015-12-03 17:15:22 +00:00
2016-01-14 18:46:43 +00:00
let taskId = req . params . taskId ;
let task = await Tasks . Task . findById ( taskId ) . exec ( ) ;
2015-12-31 10:46:21 +00:00
2016-01-04 19:48:48 +00:00
if ( ! task ) {
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
} else if ( ! task . userId ) { // If the task belongs to a challenge make sure the user has rights
2016-03-13 03:00:43 +00:00
challenge = await Challenge . findOne ( { _id : task . challenge . id } ) . exec ( ) ;
2016-01-04 19:48:48 +00:00
if ( ! challenge ) throw new NotFound ( res . t ( 'challengeNotFound' ) ) ;
if ( challenge . leader !== user . _id ) throw new NotAuthorized ( res . t ( 'onlyChalLeaderEditTasks' ) ) ;
2016-05-14 23:10:08 +00:00
} else if ( task . userId !== user . _id ) { // If the task is owned by a user make it's the current one
2016-01-04 19:48:48 +00:00
throw new NotFound ( res . t ( 'taskNotFound' ) ) ;
2016-03-15 15:03:48 +00:00
} else if ( task . userId && task . challenge . id && ! task . challenge . broken ) {
2016-01-04 19:48:48 +00:00
throw new NotAuthorized ( res . t ( 'cantDeleteChallengeTasks' ) ) ;
}
2015-12-31 10:46:21 +00:00
2016-01-14 18:46:43 +00:00
if ( task . type !== 'todo' || ! task . completed ) {
2016-01-21 14:05:51 +00:00
removeFromArray ( ( challenge || user ) . tasksOrder [ ` ${ task . type } s ` ] , taskId ) ;
2016-05-11 12:34:01 +00:00
await Bluebird . all ( [ ( challenge || user ) . save ( ) , task . remove ( ) ] ) ;
2016-01-14 18:46:43 +00:00
} else {
await task . remove ( ) ;
}
2015-12-31 10:46:21 +00:00
res . respond ( 200 , { } ) ;
2016-01-04 19:48:48 +00:00
if ( challenge ) challenge . removeTask ( task ) ;
2016-05-12 16:06:26 +00:00
return null ;
2015-11-29 16:47:10 +00:00
} ,
} ;
2015-11-28 16:47:32 +00:00
2016-03-04 19:29:51 +00:00
module . exports = api ;