diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js index 04b431db81..e1a31c2079 100644 --- a/website/src/controllers/api-v3/tasks.js +++ b/website/src/controllers/api-v3/tasks.js @@ -23,12 +23,12 @@ api.createTask = { url: '/tasks', middlewares: [authWithHeaders()], handler (req, res, next) { - req.checkBody('type', res.t('invalidTaskType')).notEmpty().isIn(['habit', 'daily', 'todo', 'reward']); + req.checkBody('type', res.t('invalidTaskType')).notEmpty().isIn(Tasks.tasksTypes); let user = res.locals.user; let taskType = req.body.type; - let newTask = new Tasks[`${taskType.charAt(0).toUpperCase() + taskType.slice(1)}Model`](Tasks.TaskModel.sanitize(req.body)); + let newTask = new Tasks[taskType](Tasks.Task.sanitize(req.body)); newTask.userId = user._id; user.tasksOrder[taskType].unshift(newTask._id); @@ -57,14 +57,14 @@ api.getTasks = { url: '/tasks', middlewares: [authWithHeaders()], handler (req, res, next) { - req.checkQuery('type', res.t('invalidTaskType')).isIn(['habit', 'daily', 'todo', 'reward']); + req.checkQuery('type', res.t('invalidTaskType')).isIn(Tasks.tasksTypes); let user = res.locals.user; let query = {userId: user._id}; let type = req.query.type; - if (type) query.type = type.charAt(0).toUpperCase() + type.slice(1); // task.ype is stored with firt uppercase letter + if (type) query.type = type; - Tasks.TaskModel.find(query).exec() + Tasks.Task.find(query).exec() .then((tasks) => res.respond(200, tasks)) .catch(next); }, @@ -89,7 +89,7 @@ api.getTask = { req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); - Tasks.TaskModel.findOne({ + Tasks.Task.findOne({ _id: req.params.taskId, userId: user._id, }).exec() @@ -121,7 +121,7 @@ api.updateTask = { req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); // TODO check that req.body isn't empty - Tasks.TaskModel.findOne({ + Tasks.Task.findOne({ _id: req.params.taskId, userId: user._id, }).exec() @@ -135,7 +135,7 @@ api.updateTask = { } // TODO merge goes deep into objects, it's ok? // TODO also check that array fields are updated correctly without marking modified - _.merge(task, Tasks.TaskModel.sanitizeUpdate(req.body)); + _.merge(task, Tasks.Task.sanitizeUpdate(req.body)); return task.save(); }) .then((savedTask) => res.respond(200, savedTask)) @@ -163,13 +163,13 @@ api.addChecklistItem = { req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); // TODO check that req.body isn't empty and is an array - Tasks.TaskModel.findOne({ + Tasks.Task.findOne({ _id: req.params.taskId, userId: user._id, }).exec() .then((task) => { if (!task) throw new NotFound(res.t('taskNotFound')); - if (task.type !== 'Daily' && task.type !== 'Todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); + if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); task.checklist.push(req.body); return task.save(); @@ -200,13 +200,13 @@ api.scoreCheckListItem = { req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID(); - Tasks.TaskModel.findOne({ + Tasks.Task.findOne({ _id: req.params.taskId, userId: user._id, }).exec() .then((task) => { if (!task) throw new NotFound(res.t('taskNotFound')); - if (task.type !== 'Daily' && task.type !== 'Todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); + if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); let item = _.find(task.checklist, {_id: req.params.itemId}); @@ -240,13 +240,13 @@ api.updateChecklistItem = { req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID(); - Tasks.TaskModel.findOne({ + Tasks.Task.findOne({ _id: req.params.taskId, userId: user._id, }).exec() .then((task) => { if (!task) throw new NotFound(res.t('taskNotFound')); - if (task.type !== 'Daily' && task.type !== 'Todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); + if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); let item = _.find(task.checklist, {_id: req.params.itemId}); if (!item) throw new NotFound(res.t('checklistItemNotFound')); @@ -281,13 +281,13 @@ api.removeChecklistItem = { req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); req.checkParams('itemId', res.t('itemIdRequired')).notEmpty().isUUID(); - Tasks.TaskModel.findOne({ + Tasks.Task.findOne({ _id: req.params.taskId, userId: user._id, }).exec() .then((task) => { if (!task) throw new NotFound(res.t('taskNotFound')); - if (task.type !== 'Daily' && task.type !== 'Todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); + if (task.type !== 'daily' && task.type !== 'todo') throw new BadRequest(res.t('checklistOnlyDailyTodo')); let itemI = _.findIndex(task.checklist, {_id: req.params.itemId}); if (itemI === -1) throw new NotFound(res.t('checklistItemNotFound')); @@ -302,11 +302,9 @@ api.removeChecklistItem = { // Remove a task from user.tasksOrder function _removeTaskTasksOrder (user, taskId) { - let types = ['habits', 'dailys', 'todos', 'rewards']; - // Loop through all lists and when the task is found, remove it and return - for (let i = 0; i < types.length; i++) { - let list = user.tasksOrder[types[i]]; + for (let i = 0; i < Tasks.tasksTypes.length; i++) { + let list = user.tasksOrder[Tasks.tasksTypes[i]]; let index = list.indexOf(taskId); if (index !== -1) { @@ -337,7 +335,7 @@ api.deleteTask = { req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); - Tasks.TaskModel.findOne({ + Tasks.Task.findOne({ _id: req.params.taskId, userId: user._id, }).exec() diff --git a/website/src/models/task.js b/website/src/models/task.js index 0fecf5f0ed..9228ad4788 100644 --- a/website/src/models/task.js +++ b/website/src/models/task.js @@ -11,12 +11,10 @@ let discriminatorOptions = { }; let subDiscriminatorOptions = _.defaults(_.cloneDeep(discriminatorOptions), {_id: false}); -// TODO make sure a task can only update the fields belonging to its type -// We could use discriminators but it looks like when loading from the parent -// Task model the subclasses are not applied - check twice +export let tasksTypes = ['habit', 'daily', 'todo', 'reward']; export let TaskSchema = new Schema({ - type: {type: String, enum: ['Habit', 'Todo', 'Daily', 'Reward'], required: true, default: 'Habit'}, + type: {type: String, enum: tasksTypes, required: true, default: tasksTypes[0]}, text: {type: String, required: true}, notes: {type: String, default: ''}, tags: {type: Schema.Types.Mixed, default: {}}, // TODO dictionary? { "4ddf03d9-54bd-41a3-b011-ca1f1d2e9371" : true }, validate @@ -45,10 +43,10 @@ TaskSchema.plugin(baseModel, { // A list of additional fields that cannot be updated (but can be set on creation) let noUpdate = ['_id', 'type']; TaskSchema.statics.sanitizeUpdate = function sanitizeUpdate (updateObj) { - return TaskModel.sanitize(updateObj, noUpdate); // eslint-disable-line no-use-before-define + return Task.sanitize(updateObj, noUpdate); // eslint-disable-line no-use-before-define }; -export let TaskModel = mongoose.model('Task', TaskSchema); +export let Task = mongoose.model('Task', TaskSchema); // habits and dailies shared fields let habitDailySchema = () => { @@ -73,7 +71,7 @@ export let HabitSchema = new Schema(_.defaults({ up: {type: Boolean, default: true}, down: {type: Boolean, default: true}, }, habitDailySchema()), subDiscriminatorOptions); -export let HabitModel = TaskModel.discriminator('Habit', HabitSchema); +export let Habit = Task.discriminator('habit', HabitSchema); export let DailySchema = new Schema(_.defaults({ frequency: {type: String, default: 'weekly', enum: ['daily', 'weekly']}, @@ -95,7 +93,7 @@ export let DailySchema = new Schema(_.defaults({ }, streak: {type: Number, default: 0}, }, habitDailySchema(), dailyTodoSchema()), subDiscriminatorOptions); -export let DailyModel = TaskModel.discriminator('Daily', DailySchema); +export let Daily = Task.discriminator('daily', DailySchema); export let TodoSchema = new Schema(_.defaults({ dateCompleted: Date, @@ -103,7 +101,7 @@ export let TodoSchema = new Schema(_.defaults({ // TODO change field name date: String, // due date for todos }, dailyTodoSchema()), subDiscriminatorOptions); -export let TodoModel = TaskModel.discriminator('Todo', TodoSchema); +export let Todo = Task.discriminator('todo', TodoSchema); export let RewardSchema = new Schema({}, subDiscriminatorOptions); -export let RewardModel = TaskModel.discriminator('Reward', RewardSchema); +export let Reward = Task.discriminator('reward', RewardSchema); diff --git a/website/src/models/user.js b/website/src/models/user.js index 45f21b6e4d..7e14b29c86 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -511,7 +511,7 @@ function _populateDefaultTasks (user, taskTypes) { taskTypes = tagsI !== -1 ? _.clone(taskTypes).slice(tagsI, 1) : taskTypes; _.each(taskTypes, (taskType) => { let tasksOfType = _.map(shared.content.userDefaults[taskType], (taskDefaults) => { - let newTask = new Tasks[taskType.charAt(0).toUpperCase() + taskType.slice(1)](taskDefaults); + let newTask = new Tasks[taskType](taskDefaults); newTask.userId = user._id; newTask.text = newTask.text(user.preferences.language); @@ -532,7 +532,7 @@ function _populateDefaultTasks (user, taskTypes) { return Q.all(tasksToCreate) .then((tasksCreated) => { _.each(tasksCreated, (task) => { - user.tasksOrder[`${task.type.toLowerCase()}s`].push(task._id); + user.tasksOrder[`${task.type}s`].push(task._id); }); }); }