diff --git a/common/locales/en/api-v3.json b/common/locales/en/api-v3.json index 0b810c76d1..935fdf2a1d 100644 --- a/common/locales/en/api-v3.json +++ b/common/locales/en/api-v3.json @@ -27,5 +27,5 @@ "positionRequired": "\"position\" is required and must be a number.", "cantMoveCompletedTodo": "Can't move a completed todo.", "directionUpDown": "\"direction\" is required and must be 'up' or 'down'", - "alreadyTagged": "The task is already tagged with give tag." + "alreadyTagged": "The task is already tagged with given tag." } 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 new file mode 100644 index 0000000000..6fa2c94d68 --- /dev/null +++ b/test/api/v3/integration/tasks/tags/DELETE-tasks_taskId_tags_tagId.test.js @@ -0,0 +1,53 @@ +import { + generateUser, + requester, + translate as t, +} from '../../../../../helpers/api-integration.helper'; +import { v4 as generateUUID } from 'uuid'; + +describe('DELETE /tasks/:taskId/tags/:tagId', () => { + let user, api; + + before(() => { + return generateUser().then((generatedUser) => { + user = generatedUser; + api = requester(user); + }); + }); + + it('removes a tag from a task', () => { + let tag; + let task; + + return api.post('/tasks', { + type: 'habit', + text: 'Task with tag', + }).then(createdTask => { + task = createdTask; + return api.post('/tags', {name: 'Tag 1'}); + }).then(createdTag => { + tag = createdTag; + return api.post(`/tasks/${task._id}/tags/${tag._id}`); + }).then(savedTask => { + return api.del(`/tasks/${task._id}/tags/${tag._id}`); + }).then(() => api.get(`/tasks/${task._id}`)) + .then(updatedTask => { + expect(updatedTask.tags.length).to.equal(0); + }); + }); + + it('only deletes existing tags', () => { + let task; + + return expect(api.post('/tasks', { + type: 'habit', + text: 'Task with tag', + }).then(createdTask => { + return api.del(`/tasks/${createdTask._id}/tags/${generateUUID()}`); + })).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('tagNotFound'), + }); + }); +}); 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 new file mode 100644 index 0000000000..b5e8f4e485 --- /dev/null +++ b/test/api/v3/integration/tasks/tags/POST-tasks_taskId_tags_tagId.test.js @@ -0,0 +1,70 @@ +import { + generateUser, + requester, + translate as t, +} from '../../../../../helpers/api-integration.helper'; +import { v4 as generateUUID } from 'uuid'; + +describe('POST /tasks/:taskId/tags/:tagId', () => { + let user, api; + + before(() => { + return generateUser().then((generatedUser) => { + user = generatedUser; + api = requester(user); + }); + }); + + it('adds a tag to a task', () => { + let tag; + let task; + + return api.post('/tasks', { + type: 'habit', + text: 'Task with tag', + }).then(createdTask => { + task = createdTask; + return api.post('/tags', {name: 'Tag 1'}); + }).then(createdTag => { + tag = createdTag; + return api.post(`/tasks/${task._id}/tags/${tag._id}`); + }).then(savedTask => { + expect(savedTask.tags[0]).to.equal(tag._id); + }); + }); + + it('does not add a tag to a task twice', () => { + let tag; + let task; + + return expect(api.post('/tasks', { + type: 'habit', + text: 'Task with tag', + }).then(createdTask => { + task = createdTask; + return api.post('/tags', {name: 'Tag 1'}); + }).then(createdTag => { + tag = createdTag; + return api.post(`/tasks/${task._id}/tags/${tag._id}`); + }).then(() => { + return api.post(`/tasks/${task._id}/tags/${tag._id}`); + })).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('alreadyTagged'), + }); + }); + + it('does not add a non existing tag to a task', () => { + return expect(api.post('/tasks', { + type: 'habit', + text: 'Task with tag', + }).then((task) => { + return api.post(`/tasks/${task._id}/tags/${generateUUID()}`); + })).to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('invalidReqParams'), + }); + }); +}); diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js index eec1e28ddb..818362efc9 100644 --- a/website/src/controllers/api-v3/tasks.js +++ b/website/src/controllers/api-v3/tasks.js @@ -518,7 +518,7 @@ api.removeChecklistItem = { */ api.addTagToTask = { method: 'POST', - url: '/tasks/:taskId/tags', + url: '/tasks/:taskId/tags/:tagId', middlewares: [authWithHeaders()], handler (req, res, next) { let user = res.locals.user; @@ -538,7 +538,7 @@ api.addTagToTask = { if (!task) throw new NotFound(res.t('taskNotFound')); let tagId = req.params.tagId; - let alreadyTagged = task.tags.indexOf(tagId) === -1; + let alreadyTagged = task.tags.indexOf(tagId) !== -1; if (alreadyTagged) throw new BadRequest(res.t('alreadyTagged')); task.tags.push(tagId); @@ -580,7 +580,7 @@ api.removeTagFromTask = { .then((task) => { if (!task) throw new NotFound(res.t('taskNotFound')); - let tagI = _.findIndex(task.tags, {_id: req.params.tagId}); + let tagI = task.tags.indexOf(req.params.tagId); if (tagI === -1) throw new NotFound(res.t('tagNotFound')); task.tags.splice(tagI, 1);