diff --git a/test/api/v3/integration/tasks/DELETE-tasks_id.test.js b/test/api/v3/integration/tasks/DELETE-tasks_id.test.js index 503e484e66..053176a9ee 100644 --- a/test/api/v3/integration/tasks/DELETE-tasks_id.test.js +++ b/test/api/v3/integration/tasks/DELETE-tasks_id.test.js @@ -16,7 +16,7 @@ describe('DELETE /tasks/:id', () => { let task; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test habit', type: 'habit', }).then((createdTask) => { @@ -48,7 +48,7 @@ describe('DELETE /tasks/:id', () => { it('cannot delete a task owned by someone else', () => { return generateUser() .then((anotherUser) => { - return anotherUser.post('/tasks?tasksOwner=user', { + return anotherUser.post('/tasks/user', { text: 'test habit', type: 'habit', }); diff --git a/test/api/v3/integration/tasks/GET-tasks.test.js b/test/api/v3/integration/tasks/GET-tasks.test.js index bf77442ea4..bf652777e4 100644 --- a/test/api/v3/integration/tasks/GET-tasks.test.js +++ b/test/api/v3/integration/tasks/GET-tasks.test.js @@ -2,7 +2,7 @@ import { generateUser, } from '../../../../helpers/api-integration.helper'; -describe('GET /tasks', () => { +describe('GET /tasks/user', () => { let user; beforeEach(async () => { @@ -16,14 +16,14 @@ describe('GET /tasks', () => { }); it('returns all user\'s tasks', async () => { - let createdTasks = await user.post('/tasks?tasksOwner=user', [{text: 'test habit', type: 'habit'}, {text: 'test todo', type: 'todo'}]); - let tasks = await user.get('/tasks?tasksOwner=user'); + let createdTasks = await user.post('/tasks/user', [{text: 'test habit', type: 'habit'}, {text: 'test todo', type: 'todo'}]); + let tasks = await user.get('/tasks/user'); expect(tasks.length).to.equal(createdTasks.length + 1); // + 1 because 1 is a default task }); it('returns only a type of user\'s tasks if req.query.type is specified', async () => { - let createdTasks = await user.post('/tasks?tasksOwner=user', [{text: 'test habit', type: 'habit'}, {text: 'test todo', type: 'todo'}]); - let tasks = await user.get('/tasks?tasksOwner=user&type=habit'); + let createdTasks = await user.post('/tasks/user', [{text: 'test habit', type: 'habit'}, {text: 'test todo', type: 'todo'}]); + let tasks = await user.get('/tasks/user?type=habit'); expect(tasks.length).to.equal(1); expect(tasks[0]._id).to.equal(createdTasks[0]._id); }); diff --git a/test/api/v3/integration/tasks/GET-tasks_id.test.js b/test/api/v3/integration/tasks/GET-tasks_id.test.js index b88d8a95a6..d3fe449b8d 100644 --- a/test/api/v3/integration/tasks/GET-tasks_id.test.js +++ b/test/api/v3/integration/tasks/GET-tasks_id.test.js @@ -17,7 +17,7 @@ describe('GET /tasks/:id', () => { let task; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test habit', type: 'habit', }).then((createdTask) => { @@ -54,7 +54,7 @@ describe('GET /tasks/:id', () => { .then((user2) => { anotherUser = user2; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test habit', type: 'habit', }); diff --git a/test/api/v3/integration/tasks/POST-tasks.test.js b/test/api/v3/integration/tasks/POST-tasks.test.js index a002119634..3fd81aa7cd 100644 --- a/test/api/v3/integration/tasks/POST-tasks.test.js +++ b/test/api/v3/integration/tasks/POST-tasks.test.js @@ -14,7 +14,7 @@ describe('POST /tasks', () => { context('validates params', () => { it('returns an error if req.body.type is absent', async () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { notType: 'habit', })).to.eventually.be.rejected.and.eql({ code: 400, @@ -24,7 +24,7 @@ describe('POST /tasks', () => { }); it('returns an error if req.body.type is not valid', async () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habitF', })).to.eventually.be.rejected.and.eql({ code: 400, @@ -34,7 +34,7 @@ describe('POST /tasks', () => { }); it('returns an error if one object inside an array is invalid', async () => { - return expect(user.post('/tasks?tasksOwner=user', [ + return expect(user.post('/tasks/user', [ {type: 'habitF'}, {type: 'habit'}, ])).to.eventually.be.rejected.and.eql({ @@ -45,7 +45,7 @@ describe('POST /tasks', () => { }); it('returns an error if req.body.text is absent', async () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', })).to.eventually.be.rejected.and.eql({ code: 400, @@ -56,7 +56,7 @@ describe('POST /tasks', () => { it('does not update user.tasksOrder.{taskType} when the task is not saved because invalid', async () => { let originalHabitsOrder = (await user.get('/user')).tasksOrder.habits; - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', })).to.eventually.be.rejected.and.eql({ // this block is necessary code: 400, @@ -71,7 +71,7 @@ describe('POST /tasks', () => { it('does not update user.tasksOrder.{taskType} when a task inside an array is not saved because invalid', async () => { let originalHabitsOrder = (await user.get('/user')).tasksOrder.habits; - return expect(user.post('/tasks?tasksOwner=user', [ + return expect(user.post('/tasks/user', [ {type: 'habit'}, // Missing text {type: 'habit', text: 'valid'}, // Valid ])).to.eventually.be.rejected.and.eql({ // this block is necessary @@ -86,8 +86,8 @@ describe('POST /tasks', () => { }); it('does not save any task sent in an array when 1 is invalid', async () => { - let originalTasks = await user.get('/tasks?tasksOwner=user'); - return expect(user.post('/tasks?tasksOwner=user', [ + let originalTasks = await user.get('/tasks/user'); + return expect(user.post('/tasks/user', [ {type: 'habit'}, // Missing text {type: 'habit', text: 'valid'}, // Valid ])).to.eventually.be.rejected.and.eql({ // this block is necessary @@ -95,14 +95,14 @@ describe('POST /tasks', () => { error: 'BadRequest', message: 'habit validation failed', }).then(async () => { - let updatedTasks = await user.get('/tasks?tasksOwner=user'); + let updatedTasks = await user.get('/tasks/user'); expect(updatedTasks).to.eql(originalTasks); }); }); it('automatically sets "task.userId" to user\'s uuid', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test habit', type: 'habit', }); @@ -113,7 +113,7 @@ describe('POST /tasks', () => { it(`ignores setting userId, history, createdAt, updatedAt, challenge, completed, streak, dateCompleted fields`, async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test daily', type: 'daily', userId: 123, @@ -137,7 +137,7 @@ describe('POST /tasks', () => { }); it('ignores invalid fields', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test daily', type: 'daily', notValid: true, @@ -149,7 +149,7 @@ describe('POST /tasks', () => { context('habits', () => { it('creates a habit', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test habit', type: 'habit', up: false, @@ -167,7 +167,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.habits when a new habit is created', async () => { let originalHabitsOrderLen = (await user.get('/user')).tasksOrder.habits.length; - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { type: 'habit', text: 'an habit', }); @@ -179,7 +179,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.habits when multiple habits are created', async () => { let originalHabitsOrderLen = (await user.get('/user')).tasksOrder.habits.length; - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ type: 'habit', text: 'an habit', }, { @@ -194,7 +194,7 @@ describe('POST /tasks', () => { }); it('creates multiple habits', async () => { - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ text: 'test habit', type: 'habit', up: false, @@ -224,7 +224,7 @@ describe('POST /tasks', () => { }); it('defaults to setting up and down to true', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test habit', type: 'habit', notes: 1976, @@ -235,7 +235,7 @@ describe('POST /tasks', () => { }); it('cannot create checklists', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test habit', type: 'habit', checklist: [ @@ -249,7 +249,7 @@ describe('POST /tasks', () => { context('todos', () => { it('creates a todo', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test todo', type: 'todo', notes: 1976, @@ -262,7 +262,7 @@ describe('POST /tasks', () => { }); it('creates multiple todos', async () => { - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ text: 'test todo', type: 'todo', notes: 1976, @@ -285,7 +285,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.todos when a new todo is created', async () => { let originalTodosOrderLen = (await user.get('/user')).tasksOrder.todos.length; - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { type: 'todo', text: 'a todo', }); @@ -297,7 +297,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.todos when multiple todos are created', async () => { let originalTodosOrderLen = (await user.get('/user')).tasksOrder.todos.length; - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ type: 'todo', text: 'a todo', }, { @@ -312,7 +312,7 @@ describe('POST /tasks', () => { }); it('can create checklists', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test todo', type: 'todo', checklist: [ @@ -333,7 +333,7 @@ describe('POST /tasks', () => { it('creates a daily', async () => { let now = new Date(); - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test daily', type: 'daily', notes: 1976, @@ -352,7 +352,7 @@ describe('POST /tasks', () => { }); it('creates multiple dailys', async () => { - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ text: 'test daily', type: 'daily', notes: 1976, @@ -375,7 +375,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.dailys when a new daily is created', async () => { let originalDailysOrderLen = (await user.get('/user')).tasksOrder.dailys.length; - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { type: 'daily', text: 'a daily', }); @@ -387,7 +387,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.dailys when multiple dailys are created', async () => { let originalDailysOrderLen = (await user.get('/user')).tasksOrder.dailys.length; - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ type: 'daily', text: 'a daily', }, { @@ -402,7 +402,7 @@ describe('POST /tasks', () => { }); it('defaults to a weekly frequency, with every day set', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test daily', type: 'daily', }); @@ -421,7 +421,7 @@ describe('POST /tasks', () => { }); it('allows repeat field to be configured', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test daily', type: 'daily', repeat: { @@ -445,7 +445,7 @@ describe('POST /tasks', () => { it('defaults startDate to today', async () => { let today = (new Date()).getDay(); - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test daily', type: 'daily', }); @@ -454,7 +454,7 @@ describe('POST /tasks', () => { }); it('can create checklists', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test daily', type: 'daily', checklist: [ @@ -473,7 +473,7 @@ describe('POST /tasks', () => { context('rewards', () => { it('creates a reward', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test reward', type: 'reward', notes: 1976, @@ -488,7 +488,7 @@ describe('POST /tasks', () => { }); it('creates multiple rewards', async () => { - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ text: 'test reward', type: 'reward', notes: 1976, @@ -515,7 +515,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.rewards when a new reward is created', async () => { let originalRewardsOrderLen = (await user.get('/user')).tasksOrder.rewards.length; - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { type: 'reward', text: 'a reward', }); @@ -527,7 +527,7 @@ describe('POST /tasks', () => { it('updates user.tasksOrder.dreward when multiple rewards are created', async () => { let originalRewardsOrderLen = (await user.get('/user')).tasksOrder.rewards.length; - let [task, task2] = await user.post('/tasks?tasksOwner=user', [{ + let [task, task2] = await user.post('/tasks/user', [{ type: 'reward', text: 'a reward', }, { @@ -542,7 +542,7 @@ describe('POST /tasks', () => { }); it('defaults to a 0 value', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test reward', type: 'reward', }); @@ -551,7 +551,7 @@ describe('POST /tasks', () => { }); it('requires value to be coerced into a number', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test reward', type: 'reward', value: '10', @@ -561,7 +561,7 @@ describe('POST /tasks', () => { }); it('cannot create checklists', async () => { - let task = await user.post('/tasks?tasksOwner=user', { + let task = await user.post('/tasks/user', { text: 'test reward', type: 'reward', checklist: [ diff --git a/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js b/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js index 1c552613b4..139db4310f 100644 --- a/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_id_score_direction.test.js @@ -37,7 +37,7 @@ describe('POST /tasks/:id/score/:direction', () => { let todo; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test todo', type: 'todo', }).then((task) => { @@ -149,7 +149,7 @@ describe('POST /tasks/:id/score/:direction', () => { let daily; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test daily', type: 'daily', }).then((task) => { @@ -226,26 +226,26 @@ describe('POST /tasks/:id/score/:direction', () => { let habit, minusHabit, plusHabit, neitherHabit; // eslint-disable-line no-unused-vars beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test habit', type: 'habit', }).then((task) => { habit = task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test min habit', type: 'habit', up: false, }); }).then((task) => { minusHabit = task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test plus habit', type: 'habit', down: false, }); }).then((task) => { plusHabit = task; - user.post('/tasks?tasksOwner=user', { + user.post('/tasks/user', { text: 'test neither habit', type: 'habit', up: false, @@ -297,7 +297,7 @@ describe('POST /tasks/:id/score/:direction', () => { let reward; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test reward', type: 'reward', value: 5, diff --git a/test/api/v3/integration/tasks/PUT-tasks_id.test.js b/test/api/v3/integration/tasks/PUT-tasks_id.test.js index 408ef92e99..453fb03cea 100644 --- a/test/api/v3/integration/tasks/PUT-tasks_id.test.js +++ b/test/api/v3/integration/tasks/PUT-tasks_id.test.js @@ -16,7 +16,7 @@ describe('PUT /tasks/:id', () => { let task; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test habit', type: 'habit', }).then((createdTask) => { @@ -65,7 +65,7 @@ describe('PUT /tasks/:id', () => { let habit; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test habit', type: 'habit', notes: 1976, @@ -93,7 +93,7 @@ describe('PUT /tasks/:id', () => { let todo; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test todo', type: 'todo', notes: 1976, @@ -150,7 +150,7 @@ describe('PUT /tasks/:id', () => { let daily; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test daily', type: 'daily', notes: 1976, @@ -254,7 +254,7 @@ describe('PUT /tasks/:id', () => { let reward; beforeEach(() => { - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { text: 'test reward', type: 'reward', notes: 1976, diff --git a/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js b/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js index 738255996a..2c6de8e570 100644 --- a/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js +++ b/test/api/v3/integration/tasks/checklists/DELETE-tasks_taskId_checklist_itemId.test.js @@ -16,7 +16,7 @@ describe('DELETE /tasks/:taskId/checklist/:itemId', () => { it('deletes a checklist item', () => { let task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { type: 'daily', text: 'Daily with checklist', }).then(createdTask => { @@ -33,7 +33,7 @@ describe('DELETE /tasks/:taskId/checklist/:itemId', () => { it('does not work with habits', () => { let habit; - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', text: 'habit with checklist', }).then(createdTask => { @@ -47,7 +47,7 @@ describe('DELETE /tasks/:taskId/checklist/:itemId', () => { }); it('does not work with rewards', async () => { - let reward = await user.post('/tasks?tasksOwner=user', { + let reward = await user.post('/tasks/user', { type: 'reward', text: 'reward with checklist', }); @@ -68,7 +68,7 @@ describe('DELETE /tasks/:taskId/checklist/:itemId', () => { }); it('fails on checklist item not found', () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'daily', text: 'daily with checklist', }).then(createdTask => { diff --git a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js index 1ad4f7f58c..fd79b89ae9 100644 --- a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js +++ b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist.test.js @@ -16,7 +16,7 @@ describe('POST /tasks/:taskId/checklist/', () => { it('adds a checklist item to a task', () => { let task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { type: 'daily', text: 'Daily with checklist', }).then(createdTask => { @@ -36,7 +36,7 @@ describe('POST /tasks/:taskId/checklist/', () => { it('does not add a checklist to habits', () => { let habit; - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', text: 'habit with checklist', }).then(createdTask => { @@ -51,7 +51,7 @@ describe('POST /tasks/:taskId/checklist/', () => { it('does not add a checklist to rewards', () => { let reward; - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'reward', text: 'reward with checklist', }).then(createdTask => { diff --git a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js index 862b67b638..cc386341b7 100644 --- a/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js +++ b/test/api/v3/integration/tasks/checklists/POST-tasks_taskId_checklist_itemId_score.test.js @@ -16,7 +16,7 @@ describe('POST /tasks/:taskId/checklist/:itemId/score', () => { it('scores a checklist item', () => { let task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { type: 'daily', text: 'Daily with checklist', }).then(createdTask => { @@ -32,7 +32,7 @@ describe('POST /tasks/:taskId/checklist/:itemId/score', () => { it('fails on habits', () => { let habit; - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', text: 'habit with checklist', }).then(createdTask => { @@ -46,7 +46,7 @@ describe('POST /tasks/:taskId/checklist/:itemId/score', () => { }); it('fails on rewards', async () => { - let reward = await user.post('/tasks?tasksOwner=user', { + let reward = await user.post('/tasks/user', { type: 'reward', text: 'reward with checklist', }); @@ -67,7 +67,7 @@ describe('POST /tasks/:taskId/checklist/:itemId/score', () => { }); it('fails on checklist item not found', () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'daily', text: 'daily with checklist', }).then(createdTask => { diff --git a/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js b/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js index ae6babf778..2df3213f2b 100644 --- a/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js +++ b/test/api/v3/integration/tasks/checklists/PUT-tasks_taskId_checklist_itemId.test.js @@ -16,7 +16,7 @@ describe('PUT /tasks/:taskId/checklist/:itemId', () => { it('updates a checklist item', () => { let task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { type: 'daily', text: 'Daily with checklist', }).then(createdTask => { @@ -33,7 +33,7 @@ describe('PUT /tasks/:taskId/checklist/:itemId', () => { }); it('fails on habits', async () => { - let habit = await user.post('/tasks?tasksOwner=user', { + let habit = await user.post('/tasks/user', { type: 'habit', text: 'habit with checklist', }); @@ -46,7 +46,7 @@ describe('PUT /tasks/:taskId/checklist/:itemId', () => { }); it('fails on rewards', async () => { - let reward = await user.post('/tasks?tasksOwner=user', { + let reward = await user.post('/tasks/user', { type: 'reward', text: 'reward with checklist', }); @@ -67,7 +67,7 @@ describe('PUT /tasks/:taskId/checklist/:itemId', () => { }); it('fails on checklist item not found', () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'daily', text: 'daily with checklist', }).then(createdTask => { diff --git a/test/api/v3/integration/tasks/tags/DELETE-tasks_taskId_tags_tagId.test.js b/test/api/v3/integration/tasks/tags/DELETE-tasks_taskId_tags_tagId.test.js index 27d57bb2f0..8a9617c803 100644 --- a/test/api/v3/integration/tasks/tags/DELETE-tasks_taskId_tags_tagId.test.js +++ b/test/api/v3/integration/tasks/tags/DELETE-tasks_taskId_tags_tagId.test.js @@ -17,7 +17,7 @@ describe('DELETE /tasks/:taskId/tags/:tagId', () => { let tag; let task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { type: 'habit', text: 'Task with tag', }).then(createdTask => { @@ -35,7 +35,7 @@ describe('DELETE /tasks/:taskId/tags/:tagId', () => { }); it('only deletes existing tags', () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', text: 'Task with tag', }).then(createdTask => { diff --git a/test/api/v3/integration/tasks/tags/POST-tasks_taskId_tags_tagId.test.js b/test/api/v3/integration/tasks/tags/POST-tasks_taskId_tags_tagId.test.js index 9887a5bedb..ddc0c0fe6b 100644 --- a/test/api/v3/integration/tasks/tags/POST-tasks_taskId_tags_tagId.test.js +++ b/test/api/v3/integration/tasks/tags/POST-tasks_taskId_tags_tagId.test.js @@ -17,7 +17,7 @@ describe('POST /tasks/:taskId/tags/:tagId', () => { let tag; let task; - return user.post('/tasks?tasksOwner=user', { + return user.post('/tasks/user', { type: 'habit', text: 'Task with tag', }).then(createdTask => { @@ -35,7 +35,7 @@ describe('POST /tasks/:taskId/tags/:tagId', () => { let tag; let task; - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', text: 'Task with tag', }).then(createdTask => { @@ -54,7 +54,7 @@ describe('POST /tasks/:taskId/tags/:tagId', () => { }); it('does not add a non existing tag to a task', () => { - return expect(user.post('/tasks?tasksOwner=user', { + return expect(user.post('/tasks/user', { type: 'habit', text: 'Task with tag', }).then((task) => { diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js index 0f5ecc314c..a061ac28e5 100644 --- a/website/src/controllers/api-v3/tasks.js +++ b/website/src/controllers/api-v3/tasks.js @@ -16,88 +16,197 @@ import { preenHistory } from '../../../../common/script/api-v3/preenHistory'; let api = {}; +// 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; + let newTask = new Tasks[taskType](Tasks.Task.sanitizeCreate(taskData)); + + 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()); + + let tasks = await Q.all(toSave); + tasks.splice(0, 1); // Remove user or challenge + return tasks; +} + /** - * @api {post} /tasks Create a new task. Can be passed an object to create a single task or an array of objects to create multiple tasks. + * @api {post} /tasks/user Create a new task belonging to the autheticated user. Can be passed an object to create a single task or an array of objects to create multiple tasks. * @apiVersion 3.0.0 - * @apiName CreateTask + * @apiName CreateUserTasks * @apiGroup Task * - * @apiParam {string="user","challenge"} tasksOwner Query parameter to define if tasks will belong to the auhenticated user or to a challenge (specifying the "challengeId" parameter). - * @apiParam {UUID} challengeId Optional. Query parameter. If "tasksOwner" is "challenge" then specify the challenge id. - * * @apiSuccess {Object|Array} task The newly created task(s) */ -api.createTask = { +api.createUserTasks = { method: 'POST', - url: '/tasks', + url: '/tasks/user', middlewares: [authWithHeaders(), cron], async handler (req, res) { - req.checkQuery('tasksOwner', res.t('invalidTasksOwner')).isIn(['user', 'challenge']); - req.checkQuery('challengeId', res.t('challengeIdRequired')).optional().isUUID(); + let tasks = await _createTasks(req, res, res.locals.user); + res.respond(201, tasks.length === 1 ? tasks[0] : tasks); + }, +}; + +/** + * @api {post} /tasks/challenge/:challengeId Create a new task belonging to the challenge. Can be passed an object to create a single task or an array of objects to create multiple tasks. + * @apiVersion 3.0.0 + * @apiName CreateChallengeTasks + * @apiGroup Task + * + * @apiParam {UUID} challengeId The id of the challenge the new task(s) will belong to. + * + * @apiSuccess {Object|Array} task The newly created task(s) + */ +api.createChallengeTasks = { + method: 'POST', + url: '/tasks/challenge/:challengeId', + middlewares: [authWithHeaders(), cron], + async handler (req, res) { + req.checkQuery('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); let reqValidationErrors = req.validationErrors(); if (reqValidationErrors) throw reqValidationErrors; - let tasksData = Array.isArray(req.body) ? req.body : [req.body]; - let user = res.locals.user; - let tasksOwner = req.query.tasksOwner; - let challengeId = req.query.challengeId; - let challenge; + let user = res.local.user; + let challengeId = req.params.challengeId; - if (tasksOwner === 'user' && challengeId) throw new BadRequest(res.t('userTasksNoChallengeId')); - if (tasksOwner === 'challenge') { - if (!challengeId) throw new BadRequest(res.t('challengeIdRequired')); - challenge = await Challenge.findOne({_id: challengeId}).exec(); + let challenge = await Challenge.findOne({_id: challengeId}).exec(); - // If the challenge does not exist, or if it exists but user is not the leader -> throw error - if (!challenge) throw new NotFound(res.t('challengeNotFound')); - if (challenge.leader !== user._id) throw new NotAuthorized(res.t('onlyChalLeaderEditTasks')); - } + // 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')); - let toSave = tasksData.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; - let newTask = new Tasks[taskType](Tasks.Task.sanitizeCreate(taskData)); - - if (challenge) { - newTask.challenge.id = challengeId; - } 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; - }); - - // If all tasks are valid, save everything, withough running validation again - toSave = toSave.map(task => task.save({ - validateBeforeSave: false, - })); - toSave.unshift((challenge || user).save()); - - let tasks = await Q.all(toSave); - - if (tasks.length === 2) { - res.respond(201, tasks[1]); - } else { - tasks.splice(0, 1); // remove the user/challenge - res.respond(201, tasks); - } + let tasks = await _createTasks(req, res, user, challenge); + res.respond(201, tasks.length === 1 ? tasks[0] : tasks); // If adding tasks to a challenge -> sync users if (challenge) challenge.addTasks(tasks); // TODO catch/log }, }; +// 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) { + query.type = type; + if (type === 'todo') query.completed = false; // Exclude completed todos + } else { + query.$or = [ // Exclude completed todos + {type: 'todo', completed: false}, + {type: {$in: ['habit', 'daily', 'reward']}}, + ]; + } + + if (req.query.includeCompletedTodos === 'true' && (!type || type === 'todo')) { + if (challenge) throw new BadRequest(res.t('noCompletedTodosChallenge')); // no completed todos for challenges + + let queryCompleted = Tasks.Task.find({ + type: 'todo', + completed: true, + }).limit(30).sort({ // TODO add ability to pick more than 30 completed todos + dateCompleted: 1, + }); + + let results = await Q.all([ + queryCompleted.exec(), + Tasks.Task.find(query).exec(), + ]); + + res.respond(200, results[1].concat(results[0])); + } else { + let tasks = await Tasks.Task.find(query).exec(); + res.respond(200, tasks); + } +} + +/** + * @api {get} /tasks/user Get an user's tasks + * @apiVersion 3.0.0 + * @apiName GetUserTasks + * @apiGroup Task + * + * @apiParam {string="habit","daily","todo","reward"} type Optional query parameter to return just a type of tasks + * @apiParam {boolean} includeCompletedTodos Optional query parameter to include completed todos when "type" is "todo". Only valid whe "tasksOwner" is "user". + * + * @apiSuccess {Array} tasks An array of task objects + */ +api.getUserTasks = { + method: 'GET', + url: '/tasks/user', + middlewares: [authWithHeaders(), cron], + async handler (req, res) { + req.checkQuery('type', res.t('invalidTaskType')).optional().isIn(Tasks.tasksTypes); + + let validationErrors = req.validationErrors(); + if (validationErrors) throw validationErrors; + + await _getTasks(req, res, res.locals.user); + }, +}; + +/** + * @api {get} /tasks/challenge/:challengeId Get a challenge's tasks + * @apiVersion 3.0.0 + * @apiName GetChallengeTasks + * @apiGroup Task + * + * @apiParam {UUID} challengeId The id of the challenge from which to retrieve the tasks. + * + * @apiParam {string="habit","daily","todo","reward"} type Optional query parameter to return just a type of tasks + * + * @apiSuccess {Array} tasks An array of task objects + */ +api.getChallengeTasks = { + method: 'GET', + url: '/tasks/challenge/:challengeId', + middlewares: [authWithHeaders(), cron], + async handler (req, res) { + req.checkQuery('challengeId', res.t('challengeIdRequired')).notEmpty().isUUID(); + req.checkQuery('type', res.t('invalidTaskType')).optional().isIn(Tasks.tasksTypes); + + let validationErrors = req.validationErrors(); + if (validationErrors) throw validationErrors; + + let user = res.local.user; + let challengeId = req.params.challengeId; + + let challenge = await Challenge.findOne({_id: challengeId}).select('leader').exec(); + + // If the challenge does not exist, or if it exists but user is not a member, not the leader and not an admin -> throw error + if (!challenge || (user.challenges.indexOf(challengeId) === -1 && challenge.leader !== user._id && !user.contributor.admin)) { // eslint-disable-line no-extra-parens + throw new NotFound(res.t('challengeNotFound')); + } + + await _getTasks(req, res, res.locals.user, challenge); + }, +}; + /** * @api {get} /tasks/:tasksOwner/:challengeId Get an user's tasks * @apiVersion 3.0.0 @@ -116,29 +225,10 @@ api.getTasks = { url: '/tasks', middlewares: [authWithHeaders(), cron], async handler (req, res) { - req.checkQuery('tasksOwner', res.t('invalidTasksOwner')).isIn(['user', 'challenge']); - req.checkQuery('challengeId', res.t('challengeIdRequired')).optional().isUUID(); - req.checkQuery('type', res.t('invalidTaskType')).optional().isIn(Tasks.tasksTypes); - - let validationErrors = req.validationErrors(); - if (validationErrors) throw validationErrors; - let user = res.locals.user; - let tasksOwner = req.query.tasksOwner; let challengeId = req.query.challengeId; let challenge; - if (tasksOwner === 'user' && challengeId) throw new BadRequest(res.t('userTasksNoChallengeId')); - if (tasksOwner === 'challenge') { - if (!challengeId) throw new BadRequest(res.t('challengeIdRequired')); - challenge = await Challenge.findOne({_id: challengeId}).select('leader').exec(); - - // If the challenge does not exist, or if it exists but user is not a member, not the leader and not an admin -> throw error - if (!challenge || (user.challenges.indexOf(challengeId) === -1 && challenge.leader !== user._id && !user.contributor.admin)) { // eslint-disable-line no-extra-parens - throw new NotFound(res.t('challengeNotFound')); - } - } - let query = challenge ? {'challenge.id': challengeId, userId: {$exists: false}} : {userId: user._id}; let type = req.query.type;