diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index f69938e1..cd758993 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -45,6 +45,15 @@ services: # - SMTP_PASSWORD= # - SMTP_FROM="Demo Demo" + # Optional fields: accessToken, events, excludedEvents + # - | + # WEBHOOKS=[{ + # "url": "http://localhost:3001", + # "accessToken": "notaccesstoken", + # "events": ["cardCreate", "cardUpdate", "cardDelete"], + # "excludedEvents": ["notificationCreate", "notificationUpdate"] + # }] + # - SLACK_BOT_TOKEN= # - SLACK_CHANNEL_ID= diff --git a/docker-compose.yml b/docker-compose.yml index 85a5dab2..0601a057 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,6 +53,15 @@ services: # - SMTP_PASSWORD= # - SMTP_FROM="Demo Demo" + # Optional fields: accessToken, events, excludedEvents + # - | + # WEBHOOKS=[{ + # "url": "http://localhost:3001", + # "accessToken": "notaccesstoken", + # "events": ["cardCreate", "cardUpdate", "cardDelete"], + # "excludedEvents": ["notificationCreate", "notificationUpdate"] + # }] + # - SLACK_BOT_TOKEN= # - SLACK_CHANNEL_ID= depends_on: diff --git a/server/.env.sample b/server/.env.sample index b4fb6605..f005b294 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -43,12 +43,17 @@ SECRET_KEY=notsecretkey # SMTP_PASSWORD= # SMTP_FROM="Demo Demo" +# Optional fields: accessToken, events, excludedEvents +# WEBHOOKS='[{ +# "url": "http://localhost:3001", +# "accessToken": "notaccesstoken", +# "events": ["cardCreate", "cardUpdate", "cardDelete"], +# "excludedEvents": ["notificationCreate", "notificationUpdate"] +# }]' + # SLACK_BOT_TOKEN= # SLACK_CHANNEL_ID= -# WEBHOOK_URL= -# WEBHOOK_BEARER= - ## Do not edit this TZ=UTC diff --git a/server/api/controllers/attachments/create.js b/server/api/controllers/attachments/create.js index 1ffe9627..03e0c8cf 100644 --- a/server/api/controllers/attachments/create.js +++ b/server/api/controllers/attachments/create.js @@ -44,12 +44,12 @@ module.exports = { async fn(inputs, exits) { const { currentUser } = this.req; - const { card, board } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: card.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -83,12 +83,14 @@ module.exports = { const fileData = await sails.helpers.attachments.processUploadedFile(file); const attachment = await sails.helpers.attachments.createOne.with({ + project, + board, + list, values: { ...fileData, card, creatorUser: currentUser, }, - board, requestId: inputs.requestId, request: this.req, }); diff --git a/server/api/controllers/attachments/delete.js b/server/api/controllers/attachments/delete.js index 697f749d..c0b0d666 100755 --- a/server/api/controllers/attachments/delete.js +++ b/server/api/controllers/attachments/delete.js @@ -33,7 +33,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND); let { attachment } = path; - const { card, board } = path; + const { card, list, board, project } = path; const boardMembership = await BoardMembership.findOne({ boardId: board.id, @@ -49,9 +49,12 @@ module.exports = { } attachment = await sails.helpers.attachments.deleteOne.with({ + project, board, + list, card, record: attachment, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/attachments/update.js b/server/api/controllers/attachments/update.js index 90e51bab..752d86db 100755 --- a/server/api/controllers/attachments/update.js +++ b/server/api/controllers/attachments/update.js @@ -37,7 +37,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND); let { attachment } = path; - const { board } = path; + const { card, list, board, project } = path; const boardMembership = await BoardMembership.findOne({ boardId: board.id, @@ -56,8 +56,12 @@ module.exports = { attachment = await sails.helpers.attachments.updateOne.with({ values, + project, board, + list, + card, record: attachment, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/board-memberships/create.js b/server/api/controllers/board-memberships/create.js index 7c70fa5b..f5337a66 100755 --- a/server/api/controllers/board-memberships/create.js +++ b/server/api/controllers/board-memberships/create.js @@ -48,14 +48,11 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { board } = await sails.helpers.boards + const { board, project } = await sails.helpers.boards .getProjectPath(inputs.boardId) .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); - const isProjectManager = await sails.helpers.users.isProjectManager( - currentUser.id, - board.projectId, - ); + const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); if (!isProjectManager) { throw Errors.BOARD_NOT_FOUND; // Forbidden @@ -71,11 +68,13 @@ module.exports = { const boardMembership = await sails.helpers.boardMemberships.createOne .with({ + project, values: { ...values, board, user, }, + actorUser: currentUser, request: this.req, }) .intercept('userAlreadyBoardMember', () => Errors.USER_ALREADY_BOARD_MEMBER); diff --git a/server/api/controllers/board-memberships/delete.js b/server/api/controllers/board-memberships/delete.js index b9d6021c..48c073fd 100755 --- a/server/api/controllers/board-memberships/delete.js +++ b/server/api/controllers/board-memberships/delete.js @@ -27,7 +27,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND); let { boardMembership } = path; - const { project } = path; + const { board, project } = path; if (boardMembership.userId !== currentUser.id) { const isProjectManager = await sails.helpers.users.isProjectManager( @@ -42,7 +42,9 @@ module.exports = { boardMembership = await sails.helpers.boardMemberships.deleteOne.with({ project, + board, record: boardMembership, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/board-memberships/update.js b/server/api/controllers/board-memberships/update.js index 695b2b8a..d1ac0539 100644 --- a/server/api/controllers/board-memberships/update.js +++ b/server/api/controllers/board-memberships/update.js @@ -35,7 +35,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.BOARD_MEMBERSHIP_NOT_FOUND); let { boardMembership } = path; - const { project } = path; + const { board, project } = path; const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); @@ -47,7 +47,10 @@ module.exports = { boardMembership = await sails.helpers.boardMemberships.updateOne.with({ values, + project, + board, record: boardMembership, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/boards/create.js b/server/api/controllers/boards/create.js index 207180bc..990cb85a 100755 --- a/server/api/controllers/boards/create.js +++ b/server/api/controllers/boards/create.js @@ -103,7 +103,7 @@ module.exports = { project, }, import: boardImport, - user: currentUser, + actorUser: currentUser, requestId: inputs.requestId, request: this.req, }); diff --git a/server/api/controllers/boards/delete.js b/server/api/controllers/boards/delete.js index 9d0fc5cd..f834576d 100755 --- a/server/api/controllers/boards/delete.js +++ b/server/api/controllers/boards/delete.js @@ -22,25 +22,27 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - let { board } = await sails.helpers.boards + const path = await sails.helpers.boards .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); + let { board } = path; + const { project } = path; + if (!board) { throw Errors.BOARD_NOT_FOUND; } - const isProjectManager = await sails.helpers.users.isProjectManager( - currentUser.id, - board.projectId, - ); + const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); if (!isProjectManager) { throw Errors.BOARD_NOT_FOUND; // Forbidden } board = await sails.helpers.boards.deleteOne.with({ + project, record: board, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/boards/update.js b/server/api/controllers/boards/update.js index 85c2d01c..2ffb5741 100755 --- a/server/api/controllers/boards/update.js +++ b/server/api/controllers/boards/update.js @@ -29,18 +29,18 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - let { board } = await sails.helpers.boards + const path = await sails.helpers.boards .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); + let { board } = path; + const { project } = path; + if (!board) { throw Errors.BOARD_NOT_FOUND; } - const isProjectManager = await sails.helpers.users.isProjectManager( - currentUser.id, - board.projectId, - ); + const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); if (!isProjectManager) { throw Errors.BOARD_NOT_FOUND; // Forbidden @@ -50,7 +50,9 @@ module.exports = { board = await sails.helpers.boards.updateOne.with({ values, + project, record: board, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/card-labels/create.js b/server/api/controllers/card-labels/create.js index 207619af..68341372 100755 --- a/server/api/controllers/card-labels/create.js +++ b/server/api/controllers/card-labels/create.js @@ -45,12 +45,12 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { card } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: card.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -64,7 +64,7 @@ module.exports = { const label = await Label.findOne({ id: inputs.labelId, - boardId: card.boardId, + boardId: board.id, }); if (!label) { @@ -73,10 +73,14 @@ module.exports = { const cardLabel = await sails.helpers.cardLabels.createOne .with({ + project, + board, + list, values: { card, label, }, + actorUser: currentUser, request: this.req, }) .intercept('labelAlreadyInCard', () => Errors.LABEL_ALREADY_IN_CARD); diff --git a/server/api/controllers/card-labels/delete.js b/server/api/controllers/card-labels/delete.js index 2911bce4..23b4c9c9 100755 --- a/server/api/controllers/card-labels/delete.js +++ b/server/api/controllers/card-labels/delete.js @@ -39,7 +39,7 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { board } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); @@ -66,8 +66,12 @@ module.exports = { } cardLabel = await sails.helpers.cardLabels.deleteOne.with({ + project, board, + list, + card, record: cardLabel, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/card-memberships/create.js b/server/api/controllers/card-memberships/create.js index 9143cc91..14f544ef 100755 --- a/server/api/controllers/card-memberships/create.js +++ b/server/api/controllers/card-memberships/create.js @@ -45,12 +45,12 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { card, board } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: card.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -62,7 +62,7 @@ module.exports = { throw Errors.NOT_ENOUGH_RIGHTS; } - const isBoardMember = await sails.helpers.users.isBoardMember(inputs.userId, card.boardId); + const isBoardMember = await sails.helpers.users.isBoardMember(inputs.userId, board.id); if (!isBoardMember) { throw Errors.USER_NOT_FOUND; @@ -70,11 +70,14 @@ module.exports = { const cardMembership = await sails.helpers.cardMemberships.createOne .with({ + project, + board, + list, values: { card, userId: inputs.userId, }, - board, + actorUser: currentUser, request: this.req, }) .intercept('userAlreadyCardMember', () => Errors.USER_ALREADY_CARD_MEMBER); diff --git a/server/api/controllers/card-memberships/delete.js b/server/api/controllers/card-memberships/delete.js index 4b9d7547..297c93b4 100755 --- a/server/api/controllers/card-memberships/delete.js +++ b/server/api/controllers/card-memberships/delete.js @@ -39,7 +39,7 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { board, card } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); @@ -66,9 +66,12 @@ module.exports = { } cardMembership = await sails.helpers.cardMemberships.deleteOne.with({ + project, board, + list, card, record: cardMembership, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/cards/create.js b/server/api/controllers/cards/create.js index b847c3e0..33bc6b2d 100755 --- a/server/api/controllers/cards/create.js +++ b/server/api/controllers/cards/create.js @@ -78,7 +78,7 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { board, list } = await sails.helpers.lists + const { list, board, project } = await sails.helpers.lists .getProjectPath(inputs.listId) .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); @@ -99,6 +99,7 @@ module.exports = { const card = await sails.helpers.cards.createOne .with({ + project, board, values: { ...values, diff --git a/server/api/controllers/cards/delete.js b/server/api/controllers/cards/delete.js index dad82de1..12bb9a5b 100755 --- a/server/api/controllers/cards/delete.js +++ b/server/api/controllers/cards/delete.js @@ -28,12 +28,15 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - let { card } = await sails.helpers.cards + const path = await sails.helpers.cards .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); + let { card } = path; + const { list, board, project } = path; + const boardMembership = await BoardMembership.findOne({ - boardId: card.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -46,8 +49,11 @@ module.exports = { } card = await sails.helpers.cards.deleteOne.with({ + project, + board, + list, record: card, - user: currentUser, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/cards/duplicate.js b/server/api/controllers/cards/duplicate.js index 918a8ec1..25cfaad5 100755 --- a/server/api/controllers/cards/duplicate.js +++ b/server/api/controllers/cards/duplicate.js @@ -35,12 +35,12 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { card, list, board } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: card.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -60,6 +60,7 @@ module.exports = { cardLabels, tasks, } = await sails.helpers.cards.duplicateOne.with({ + project, board, list, record: card, diff --git a/server/api/controllers/cards/update.js b/server/api/controllers/cards/update.js index 8ee37523..d3deaf48 100755 --- a/server/api/controllers/cards/update.js +++ b/server/api/controllers/cards/update.js @@ -118,7 +118,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); let { card } = path; - const { list, board } = path; + const { list, board, project } = path; let boardMembership = await BoardMembership.findOne({ boardId: board.id, @@ -133,9 +133,11 @@ module.exports = { throw Errors.NOT_ENOUGH_RIGHTS; } + let nextProject; let nextBoard; + if (!_.isUndefined(inputs.boardId)) { - ({ board: nextBoard } = await sails.helpers.boards + ({ board: nextBoard, project: nextProject } = await sails.helpers.boards .getProjectPath(inputs.boardId) .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND)); @@ -177,15 +179,17 @@ module.exports = { card = await sails.helpers.cards.updateOne .with({ + project, board, list, record: card, values: { ...values, + project: nextProject, board: nextBoard, list: nextList, }, - user: currentUser, + actorUser: currentUser, request: this.req, }) .intercept('positionMustBeInValues', () => Errors.POSITION_MUST_BE_PRESENT) diff --git a/server/api/controllers/comment-actions/create.js b/server/api/controllers/comment-actions/create.js index 7712a3a5..41cc694d 100755 --- a/server/api/controllers/comment-actions/create.js +++ b/server/api/controllers/comment-actions/create.js @@ -32,7 +32,7 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { board, card } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); @@ -55,7 +55,9 @@ module.exports = { }; const action = await sails.helpers.actions.createOne.with({ + project, board, + list, values: { ...values, card, diff --git a/server/api/controllers/comment-actions/delete.js b/server/api/controllers/comment-actions/delete.js index 97c98790..25082358 100755 --- a/server/api/controllers/comment-actions/delete.js +++ b/server/api/controllers/comment-actions/delete.js @@ -36,7 +36,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND); let { action } = path; - const { board, project, card } = path; + const { card, list, board, project } = path; const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); @@ -60,9 +60,12 @@ module.exports = { } action = await sails.helpers.actions.deleteOne.with({ + project, board, + list, card, record: action, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/comment-actions/update.js b/server/api/controllers/comment-actions/update.js index a385ae8c..aa62fd5c 100755 --- a/server/api/controllers/comment-actions/update.js +++ b/server/api/controllers/comment-actions/update.js @@ -40,7 +40,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.COMMENT_ACTION_NOT_FOUND); let { action } = path; - const { board, project, card } = path; + const { card, list, board, project } = path; const isProjectManager = await sails.helpers.users.isProjectManager(currentUser.id, project.id); @@ -69,9 +69,12 @@ module.exports = { action = await sails.helpers.actions.updateOne.with({ values, - card, + project, board, + list, + card, record: action, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/labels/create.js b/server/api/controllers/labels/create.js index d3b8743c..a1e84991 100755 --- a/server/api/controllers/labels/create.js +++ b/server/api/controllers/labels/create.js @@ -42,7 +42,7 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { board } = await sails.helpers.boards + const { board, project } = await sails.helpers.boards .getProjectPath(inputs.boardId) .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); @@ -62,10 +62,12 @@ module.exports = { const values = _.pick(inputs, ['position', 'name', 'color']); const label = await sails.helpers.labels.createOne.with({ + project, values: { ...values, board, }, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/labels/delete.js b/server/api/controllers/labels/delete.js index e2f0a098..f14bf451 100755 --- a/server/api/controllers/labels/delete.js +++ b/server/api/controllers/labels/delete.js @@ -28,12 +28,15 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - let { label } = await sails.helpers.labels + const path = await sails.helpers.labels .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND); + let { label } = path; + const { board, project } = path; + const boardMembership = await BoardMembership.findOne({ - boardId: label.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -46,7 +49,10 @@ module.exports = { } label = await sails.helpers.labels.deleteOne.with({ + project, + board, record: label, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/labels/update.js b/server/api/controllers/labels/update.js index e289bdf9..7e76b74b 100755 --- a/server/api/controllers/labels/update.js +++ b/server/api/controllers/labels/update.js @@ -40,12 +40,15 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - let { label } = await sails.helpers.labels + const path = await sails.helpers.labels .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.LABEL_NOT_FOUND); + let { label } = path; + const { board, project } = path; + const boardMembership = await BoardMembership.findOne({ - boardId: label.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -61,7 +64,10 @@ module.exports = { label = await sails.helpers.labels.updateOne.with({ values, + project, + board, record: label, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/lists/create.js b/server/api/controllers/lists/create.js index 9383f5be..869c00bb 100755 --- a/server/api/controllers/lists/create.js +++ b/server/api/controllers/lists/create.js @@ -36,7 +36,7 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { board } = await sails.helpers.boards + const { board, project } = await sails.helpers.boards .getProjectPath(inputs.boardId) .intercept('pathNotFound', () => Errors.BOARD_NOT_FOUND); @@ -56,10 +56,12 @@ module.exports = { const values = _.pick(inputs, ['position', 'name']); const list = await sails.helpers.lists.createOne.with({ + project, values: { ...values, board, }, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/lists/delete.js b/server/api/controllers/lists/delete.js index 982b0f73..2368161f 100755 --- a/server/api/controllers/lists/delete.js +++ b/server/api/controllers/lists/delete.js @@ -28,13 +28,15 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - // eslint-disable-next-line prefer-const - let { list, board } = await sails.helpers.lists + const path = await sails.helpers.lists .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); + let { list } = path; + const { board, project } = path; + const boardMembership = await BoardMembership.findOne({ - boardId: list.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -47,8 +49,10 @@ module.exports = { } list = await sails.helpers.lists.deleteOne.with({ - record: list, + project, board, + record: list, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/lists/sort.js b/server/api/controllers/lists/sort.js index ec5863f6..0451ca69 100644 --- a/server/api/controllers/lists/sort.js +++ b/server/api/controllers/lists/sort.js @@ -32,12 +32,12 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { list } = await sails.helpers.lists + const { list, board, project } = await sails.helpers.lists .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: list.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -50,8 +50,11 @@ module.exports = { } const cards = await sails.helpers.lists.sortOne.with({ + project, + board, record: list, type: inputs.type, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/lists/update.js b/server/api/controllers/lists/update.js index 15f5f209..e1988b93 100755 --- a/server/api/controllers/lists/update.js +++ b/server/api/controllers/lists/update.js @@ -35,13 +35,15 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - // eslint-disable-next-line prefer-const - let { list, board } = await sails.helpers.lists + const path = await sails.helpers.lists .getProjectPath(inputs.id) .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); + let { list } = path; + const { board, project } = path; + const boardMembership = await BoardMembership.findOne({ - boardId: list.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -57,8 +59,10 @@ module.exports = { list = await sails.helpers.lists.updateOne.with({ values, + project, board, record: list, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/notifications/update.js b/server/api/controllers/notifications/update.js index f8a97a35..09d5b4c2 100755 --- a/server/api/controllers/notifications/update.js +++ b/server/api/controllers/notifications/update.js @@ -18,7 +18,7 @@ module.exports = { const notifications = await sails.helpers.notifications.updateMany.with({ values, recordsOrIds: inputs.ids.split(','), - user: currentUser, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/project-managers/create.js b/server/api/controllers/project-managers/create.js index a5751fdb..e3796958 100755 --- a/server/api/controllers/project-managers/create.js +++ b/server/api/controllers/project-managers/create.js @@ -63,6 +63,7 @@ module.exports = { project, user, }, + actorUser: currentUser, request: this.req, }) .intercept('userAlreadyProjectManager', () => Errors.USER_ALREADY_PROJECT_MANAGER); diff --git a/server/api/controllers/project-managers/delete.js b/server/api/controllers/project-managers/delete.js index 5bd3f9af..46946e61 100755 --- a/server/api/controllers/project-managers/delete.js +++ b/server/api/controllers/project-managers/delete.js @@ -40,6 +40,7 @@ module.exports = { // TODO: check if the last one projectManager = await sails.helpers.projectManagers.deleteOne.with({ record: projectManager, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/projects/create.js b/server/api/controllers/projects/create.js index 1ce57ae8..5a8702ec 100755 --- a/server/api/controllers/projects/create.js +++ b/server/api/controllers/projects/create.js @@ -13,7 +13,7 @@ module.exports = { const { project, projectManager } = await sails.helpers.projects.createOne.with({ values, - user: currentUser, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/projects/delete.js b/server/api/controllers/projects/delete.js index dedb9070..90ae180d 100755 --- a/server/api/controllers/projects/delete.js +++ b/server/api/controllers/projects/delete.js @@ -36,6 +36,7 @@ module.exports = { project = await sails.helpers.projects.deleteOne.with({ record: project, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/projects/update-background-image.js b/server/api/controllers/projects/update-background-image.js index 85825e08..2045ee1e 100755 --- a/server/api/controllers/projects/update-background-image.js +++ b/server/api/controllers/projects/update-background-image.js @@ -90,6 +90,7 @@ module.exports = { values: { backgroundImage: fileData, }, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/projects/update.js b/server/api/controllers/projects/update.js index 6d6e16e9..230be644 100755 --- a/server/api/controllers/projects/update.js +++ b/server/api/controllers/projects/update.js @@ -81,6 +81,7 @@ module.exports = { project = await sails.helpers.projects.updateOne.with({ values, record: project, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/tasks/create.js b/server/api/controllers/tasks/create.js index ff4b0523..126e39a6 100755 --- a/server/api/controllers/tasks/create.js +++ b/server/api/controllers/tasks/create.js @@ -39,12 +39,12 @@ module.exports = { async fn(inputs) { const { currentUser } = this.req; - const { card, board } = await sails.helpers.cards + const { card, list, board, project } = await sails.helpers.cards .getProjectPath(inputs.cardId) .intercept('pathNotFound', () => Errors.CARD_NOT_FOUND); const boardMembership = await BoardMembership.findOne({ - boardId: card.boardId, + boardId: board.id, userId: currentUser.id, }); @@ -59,11 +59,14 @@ module.exports = { const values = _.pick(inputs, ['position', 'name', 'isCompleted']); const task = await sails.helpers.tasks.createOne.with({ + project, + board, + list, values: { ...values, card, }, - board, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/tasks/delete.js b/server/api/controllers/tasks/delete.js index c4ba0ed3..a83cc9dc 100755 --- a/server/api/controllers/tasks/delete.js +++ b/server/api/controllers/tasks/delete.js @@ -33,7 +33,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.TASK_NOT_FOUND); let { task } = path; - const { board } = path; + const { card, list, board, project } = path; const boardMembership = await BoardMembership.findOne({ boardId: board.id, @@ -49,8 +49,12 @@ module.exports = { } task = await sails.helpers.tasks.deleteOne.with({ + project, board, + list, + card, record: task, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/tasks/update.js b/server/api/controllers/tasks/update.js index 9e08f76f..ff43fcb8 100755 --- a/server/api/controllers/tasks/update.js +++ b/server/api/controllers/tasks/update.js @@ -43,7 +43,7 @@ module.exports = { .intercept('pathNotFound', () => Errors.TASK_NOT_FOUND); let { task } = path; - const { board } = path; + const { card, list, board, project } = path; const boardMembership = await BoardMembership.findOne({ boardId: board.id, @@ -62,8 +62,12 @@ module.exports = { task = await sails.helpers.tasks.updateOne.with({ values, + project, board, + list, + card, record: task, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/users/create.js b/server/api/controllers/users/create.js index 7d40c975..d1dc4a68 100755 --- a/server/api/controllers/users/create.js +++ b/server/api/controllers/users/create.js @@ -71,6 +71,8 @@ module.exports = { }, async fn(inputs) { + const { currentUser } = this.req; + if (sails.config.custom.oidcEnforced) { throw Errors.NOT_ENOUGH_RIGHTS; } @@ -89,6 +91,7 @@ module.exports = { const user = await sails.helpers.users.createOne .with({ values, + actorUser: currentUser, request: this.req, }) .intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE) diff --git a/server/api/controllers/users/delete.js b/server/api/controllers/users/delete.js index 56a42f8b..b1afab29 100755 --- a/server/api/controllers/users/delete.js +++ b/server/api/controllers/users/delete.js @@ -26,6 +26,8 @@ module.exports = { }, async fn(inputs) { + const { currentUser } = this.req; + let user = await sails.helpers.users.getOne(inputs.id); if (!user) { @@ -38,6 +40,7 @@ module.exports = { user = await sails.helpers.users.deleteOne.with({ record: user, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/users/update-avatar.js b/server/api/controllers/users/update-avatar.js index 37b01f11..fc628564 100755 --- a/server/api/controllers/users/update-avatar.js +++ b/server/api/controllers/users/update-avatar.js @@ -91,7 +91,7 @@ module.exports = { values: { avatar: fileData, }, - user: currentUser, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/users/update-email.js b/server/api/controllers/users/update-email.js index f067e004..17575577 100644 --- a/server/api/controllers/users/update-email.js +++ b/server/api/controllers/users/update-email.js @@ -82,7 +82,7 @@ module.exports = { .with({ values, record: user, - user: currentUser, + actorUser: currentUser, request: this.req, }) .intercept('emailAlreadyInUse', () => Errors.EMAIL_ALREADY_IN_USE); diff --git a/server/api/controllers/users/update-password.js b/server/api/controllers/users/update-password.js index 107c013e..01466811 100644 --- a/server/api/controllers/users/update-password.js +++ b/server/api/controllers/users/update-password.js @@ -80,7 +80,7 @@ module.exports = { user = await sails.helpers.users.updateOne.with({ values, record: user, - user: currentUser, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/controllers/users/update-username.js b/server/api/controllers/users/update-username.js index b55529b4..a59c587d 100644 --- a/server/api/controllers/users/update-username.js +++ b/server/api/controllers/users/update-username.js @@ -83,7 +83,7 @@ module.exports = { .with({ values, record: user, - user: currentUser, + actorUser: currentUser, request: this.req, }) .intercept('usernameAlreadyInUse', () => Errors.USERNAME_ALREADY_IN_USE); diff --git a/server/api/controllers/users/update.js b/server/api/controllers/users/update.js index 161ae78c..a8e6215e 100755 --- a/server/api/controllers/users/update.js +++ b/server/api/controllers/users/update.js @@ -95,7 +95,7 @@ module.exports = { user = await sails.helpers.users.updateOne.with({ values, record: user, - user: currentUser, + actorUser: currentUser, request: this.req, }); diff --git a/server/api/helpers/actions/create-one.js b/server/api/helpers/actions/create-one.js index e0310c82..fb0d1856 100644 --- a/server/api/helpers/actions/create-one.js +++ b/server/api/helpers/actions/create-one.js @@ -14,21 +14,21 @@ const valuesValidator = (value) => { return true; }; -const buildAndSendSlackMessage = async (user, card, action) => { +const buildAndSendSlackMessage = async (card, action, actorUser) => { const cardLink = `<${sails.config.custom.baseUrl}/cards/${card.id}|${card.name}>`; let markdown; switch (action.type) { case Action.Types.CREATE_CARD: - markdown = `${cardLink} was created by ${user.name} in *${action.data.list.name}*`; + markdown = `${cardLink} was created by ${actorUser.name} in *${action.data.list.name}*`; break; case Action.Types.MOVE_CARD: - markdown = `${cardLink} was moved by ${user.name} to *${action.data.toList.name}*`; + markdown = `${cardLink} was moved by ${actorUser.name} to *${action.data.toList.name}*`; break; case Action.Types.COMMENT_CARD: - markdown = `*${user.name}* commented on ${cardLink}:\n>${action.data.text}`; + markdown = `*${actorUser.name}* commented on ${cardLink}:\n>${action.data.text}`; break; default: @@ -45,10 +45,18 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -72,6 +80,24 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'actionCreate', + data: { + item: action, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [values.card], + }, + }, + user: values.user, + }); + + if (sails.config.custom.slackBotToken) { + buildAndSendSlackMessage(values.card, action, values.user); + } + const subscriptionUserIds = await sails.helpers.cards.getSubscriptionUserIds( action.cardId, action.userId, @@ -84,26 +110,15 @@ module.exports = { userId, action, }, - user: values.user, + project: inputs.project, board: inputs.board, + list: inputs.list, card: values.card, + actorUser: values.user, }), ), ); - if (sails.config.custom.slackBotToken) { - buildAndSendSlackMessage(values.user, values.card, action); - } - - await sails.helpers.utils.sendWebhook.with({ - event: 'ACTION_CREATE', - data: action, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: values.card, - board: inputs.board, - }); - return action; }, }; diff --git a/server/api/helpers/actions/delete-one.js b/server/api/helpers/actions/delete-one.js index ba862f85..3ccaff19 100644 --- a/server/api/helpers/actions/delete-one.js +++ b/server/api/helpers/actions/delete-one.js @@ -4,11 +4,23 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + list: { + type: 'ref', + required: true, + }, card: { type: 'ref', required: true, }, - board: { + actorUser: { type: 'ref', required: true, }, @@ -30,13 +42,18 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'ACTION_DELETE', - data: action, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: inputs.card, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'actionDelete', + data: { + item: action, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/actions/update-one.js b/server/api/helpers/actions/update-one.js index cdca79c0..7835fa74 100644 --- a/server/api/helpers/actions/update-one.js +++ b/server/api/helpers/actions/update-one.js @@ -8,7 +8,7 @@ module.exports = { type: 'json', required: true, }, - card: { + project: { type: 'ref', required: true, }, @@ -16,6 +16,18 @@ module.exports = { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, + card: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -36,13 +48,18 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'ACTION_UPDATE', - data: action, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: inputs.card, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'actionUpdate', + data: { + item: action, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/attachments/create-one.js b/server/api/helpers/attachments/create-one.js index 0a5923b3..8db35ab6 100644 --- a/server/api/helpers/attachments/create-one.js +++ b/server/api/helpers/attachments/create-one.js @@ -21,10 +21,18 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, requestId: { type: 'string', isNotEmptyString: true, @@ -35,7 +43,7 @@ module.exports = { }, async fn(inputs) { - const { values, board } = inputs; + const { values } = inputs; const attachment = await Attachment.create({ ...values, @@ -53,26 +61,33 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'attachmentCreate', + data: { + item: attachment, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [values.card], + }, + }, + user: values.creatorUser, + }); + if (!values.card.coverAttachmentId && attachment.image) { await sails.helpers.cards.updateOne.with({ record: values.card, values: { coverAttachmentId: attachment.id, }, - board, - request: inputs.request, + project: inputs.project, + board: inputs.board, + list: inputs.list, + actorUser: values.creatorUser, }); } - await sails.helpers.utils.sendWebhook.with({ - event: 'ATTACHMENT_CREATE', - data: attachment, - projectId: board.projectId, - user: inputs.request.currentUser, - card: values.card, - board, - }); - return attachment; }, }; diff --git a/server/api/helpers/attachments/delete-one.js b/server/api/helpers/attachments/delete-one.js index 469e1461..9d08cb4f 100644 --- a/server/api/helpers/attachments/delete-one.js +++ b/server/api/helpers/attachments/delete-one.js @@ -7,14 +7,26 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, card: { type: 'ref', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -27,6 +39,10 @@ module.exports = { values: { coverAttachmentId: null, }, + project: inputs.project, + board: inputs.board, + list: inputs.list, + actorUser: inputs.actorUser, request: inputs.request, }); } @@ -49,13 +65,18 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'ATTACHMENT_DELETE', - data: attachment, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: inputs.card, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'attachmentDelete', + data: { + item: attachment, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/attachments/update-one.js b/server/api/helpers/attachments/update-one.js index 9de735e1..49660206 100644 --- a/server/api/helpers/attachments/update-one.js +++ b/server/api/helpers/attachments/update-one.js @@ -8,10 +8,26 @@ module.exports = { type: 'json', required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, + card: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -32,12 +48,18 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'ATTACHMENT_UPDATE', - data: attachment, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'attachmentUpdate', + data: { + item: attachment, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/board-memberships/create-one.js b/server/api/helpers/board-memberships/create-one.js index 5adf9eb2..f6088474 100644 --- a/server/api/helpers/board-memberships/create-one.js +++ b/server/api/helpers/board-memberships/create-one.js @@ -21,6 +21,14 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -67,6 +75,19 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'boardMembershipCreate', + data: { + item: boardMembership, + included: { + users: [values.user], + projects: [inputs.project], + boards: [values.board], + }, + }, + user: inputs.actorUser, + }); + return boardMembership; }, }; diff --git a/server/api/helpers/board-memberships/delete-one.js b/server/api/helpers/board-memberships/delete-one.js index 236734ab..e51daf4d 100644 --- a/server/api/helpers/board-memberships/delete-one.js +++ b/server/api/helpers/board-memberships/delete-one.js @@ -10,6 +10,14 @@ module.exports = { type: 'ref', required: true, }, + board: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -73,6 +81,18 @@ module.exports = { ); }); } + + sails.helpers.utils.sendWebhooks.with({ + event: 'boardMembershipDelete', + data: { + item: boardMembership, + included: { + projects: [inputs.project], + boards: [inputs.board], + }, + }, + user: inputs.actorUser, + }); } return boardMembership; diff --git a/server/api/helpers/board-memberships/update-one.js b/server/api/helpers/board-memberships/update-one.js index cb43adc8..63948907 100644 --- a/server/api/helpers/board-memberships/update-one.js +++ b/server/api/helpers/board-memberships/update-one.js @@ -8,6 +8,18 @@ module.exports = { type: 'json', required: true, }, + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -40,6 +52,18 @@ module.exports = { }, inputs.request, ); + + sails.helpers.utils.sendWebhooks.with({ + event: 'boardMembershipUpdate', + data: { + item: boardMembership, + included: { + projects: [inputs.project], + boards: [inputs.board], + }, + }, + user: inputs.actorUser, + }); } return boardMembership; diff --git a/server/api/helpers/boards/create-one.js b/server/api/helpers/boards/create-one.js index 98618722..368dda92 100644 --- a/server/api/helpers/boards/create-one.js +++ b/server/api/helpers/boards/create-one.js @@ -37,7 +37,7 @@ module.exports = { type: 'json', custom: importValidator, }, - user: { + actorUser: { type: 'ref', required: true, }, @@ -80,6 +80,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); }); @@ -90,12 +92,12 @@ module.exports = { }).fetch(); if (inputs.import && inputs.import.type === Board.ImportTypes.TRELLO) { - await sails.helpers.boards.importFromTrello(inputs.user, board, inputs.import.board); + await sails.helpers.boards.importFromTrello(board, inputs.import.board, inputs.actorUser); } const boardMembership = await BoardMembership.create({ boardId: board.id, - userId: inputs.user.id, + userId: inputs.actorUser.id, role: BoardMembership.Roles.EDITOR, }).fetch(); @@ -111,12 +113,15 @@ module.exports = { ); }); - await sails.helpers.utils.sendWebhook.with({ - event: 'BOARD_CREATE', - data: board, - projectId: board.projectId, - user: inputs.request.currentUser, - board, + sails.helpers.utils.sendWebhooks.with({ + event: 'boardCreate', + data: { + item: board, + included: { + projects: [values.project], + }, + }, + user: inputs.actorUser, }); return { diff --git a/server/api/helpers/boards/delete-one.js b/server/api/helpers/boards/delete-one.js index ddc51d7e..8633cd55 100644 --- a/server/api/helpers/boards/delete-one.js +++ b/server/api/helpers/boards/delete-one.js @@ -4,6 +4,14 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -33,15 +41,18 @@ module.exports = { inputs.request, ); }); - } - await sails.helpers.utils.sendWebhook.with({ - event: 'BOARD_DELETE', - data: board, - projectId: board.projectId, - user: inputs.request.currentUser, - board, - }); + sails.helpers.utils.sendWebhooks.with({ + event: 'boardDelete', + data: { + item: board, + included: { + projects: [inputs.project], + }, + }, + user: inputs.actorUser, + }); + } return board; }, diff --git a/server/api/helpers/boards/import-from-trello.js b/server/api/helpers/boards/import-from-trello.js index 2ab6e0af..a7b54238 100644 --- a/server/api/helpers/boards/import-from-trello.js +++ b/server/api/helpers/boards/import-from-trello.js @@ -2,10 +2,6 @@ const POSITION_GAP = 65535; // TODO: move to config module.exports = { inputs: { - user: { - type: 'ref', - required: true, - }, board: { type: 'ref', required: true, @@ -14,6 +10,10 @@ module.exports = { type: 'json', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, }, async fn(inputs) { @@ -87,7 +87,7 @@ module.exports = { trelloComments.map(async (trelloComment) => { return Action.create({ cardId: plankaCard.id, - userId: inputs.user.id, + userId: inputs.actorUser.id, type: 'commentCard', data: { text: @@ -105,7 +105,7 @@ module.exports = { const plankaCard = await Card.create({ boardId: inputs.board.id, listId: plankaList.id, - creatorUserId: inputs.user.id, + creatorUserId: inputs.actorUser.id, position: trelloCard.pos, name: trelloCard.name, description: trelloCard.desc || null, diff --git a/server/api/helpers/boards/update-one.js b/server/api/helpers/boards/update-one.js index 82fdcce0..e20324dd 100644 --- a/server/api/helpers/boards/update-one.js +++ b/server/api/helpers/boards/update-one.js @@ -21,6 +21,14 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -64,6 +72,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); }); } @@ -81,15 +91,18 @@ module.exports = { inputs.request, ); }); - } - await sails.helpers.utils.sendWebhook.with({ - event: 'BOARD_UPDATE', - data: board, - projectId: board.projectId, - user: inputs.request.currentUser, - board, - }); + sails.helpers.utils.sendWebhooks.with({ + event: 'boardUpdate', + data: { + item: board, + included: { + projects: [inputs.project], + }, + }, + user: inputs.actorUser, + }); + } return board; }, diff --git a/server/api/helpers/card-labels/create-one.js b/server/api/helpers/card-labels/create-one.js index f0cde82c..7e168ca9 100644 --- a/server/api/helpers/card-labels/create-one.js +++ b/server/api/helpers/card-labels/create-one.js @@ -21,6 +21,22 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + list: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -50,6 +66,21 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'cardLabelCreate', + data: { + item: cardLabel, + included: { + projects: [inputs.project], + boards: [inputs.board], + labels: [values.label], + lists: [inputs.list], + cards: [values.card], + }, + }, + user: inputs.actorUser, + }); + return cardLabel; }, }; diff --git a/server/api/helpers/card-labels/delete-one.js b/server/api/helpers/card-labels/delete-one.js index 3dc30ddc..e557d383 100644 --- a/server/api/helpers/card-labels/delete-one.js +++ b/server/api/helpers/card-labels/delete-one.js @@ -4,10 +4,26 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, + card: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -25,6 +41,20 @@ module.exports = { }, inputs.request, ); + + sails.helpers.utils.sendWebhooks.with({ + event: 'cardLabelDelete', + data: { + item: cardLabel, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, + }); } return cardLabel; diff --git a/server/api/helpers/card-memberships/create-one.js b/server/api/helpers/card-memberships/create-one.js index 52f17882..c32ff428 100644 --- a/server/api/helpers/card-memberships/create-one.js +++ b/server/api/helpers/card-memberships/create-one.js @@ -21,10 +21,22 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -57,6 +69,20 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'cardMembershipCreate', + data: { + item: cardMembership, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [values.card], + }, + }, + user: inputs.actorUser, + }); + const cardSubscription = await CardSubscription.create({ cardId: cardMembership.cardId, userId: cardMembership.userId, @@ -77,16 +103,9 @@ module.exports = { }, inputs.request, ); - } - await sails.helpers.utils.sendWebhook.with({ - event: 'CARD_MEMBERSHIP_CREATE', - data: cardMembership, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: values.card, - board: inputs.board, - }); + // TODO: send webhooks + } return cardMembership; }, diff --git a/server/api/helpers/card-memberships/delete-one.js b/server/api/helpers/card-memberships/delete-one.js index a4ed107f..cfa2fc7e 100644 --- a/server/api/helpers/card-memberships/delete-one.js +++ b/server/api/helpers/card-memberships/delete-one.js @@ -4,11 +4,23 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + list: { + type: 'ref', + required: true, + }, card: { type: 'ref', required: true, }, - board: { + actorUser: { type: 'ref', required: true, }, @@ -30,6 +42,20 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'cardMembershipDelete', + data: { + item: cardMembership, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, + }); + const cardSubscription = await CardSubscription.destroyOne({ cardId: cardMembership.cardId, userId: cardMembership.userId, @@ -43,16 +69,9 @@ module.exports = { isSubscribed: false, }, }); - } - await sails.helpers.utils.sendWebhook.with({ - event: 'CARD_MEMBERSHIP_DELETE', - data: cardMembership, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: inputs.card, - board: inputs.board, - }); + // TODO: send webhook + } } return cardMembership; diff --git a/server/api/helpers/cards/create-one.js b/server/api/helpers/cards/create-one.js index 0514bf5f..36cdb81a 100644 --- a/server/api/helpers/cards/create-one.js +++ b/server/api/helpers/cards/create-one.js @@ -25,6 +25,10 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, @@ -66,6 +70,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); const card = await Card.create({ @@ -85,6 +91,19 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'cardCreate', + data: { + item: card, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [values.list], + }, + }, + user: values.creatorUser, + }); + if (values.creatorUser.subscribeToOwnCards) { await CardSubscription.create({ cardId: card.id, @@ -97,6 +116,8 @@ module.exports = { isSubscribed: true, }, }); + + // TODO: send webhooks } await sails.helpers.actions.createOne.with({ @@ -108,18 +129,10 @@ module.exports = { }, user: values.creatorUser, }, - board: inputs.board, - request: inputs.request, - }); - - await sails.helpers.utils.sendWebhook.with({ - event: 'CARD_CREATE', - data: card, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card, + project: inputs.project, board: inputs.board, list: values.list, + request: inputs.request, }); return card; diff --git a/server/api/helpers/cards/delete-one.js b/server/api/helpers/cards/delete-one.js index e95596e8..1400bfcb 100644 --- a/server/api/helpers/cards/delete-one.js +++ b/server/api/helpers/cards/delete-one.js @@ -1,5 +1,5 @@ -const buildAndSendSlackMessage = async (user, card) => { - await sails.helpers.utils.sendSlackMessage(`*${card.name}* was deleted by ${user.name}`); +const buildAndSendSlackMessage = async (card, actorUser) => { + await sails.helpers.utils.sendSlackMessage(`*${card.name}* was deleted by ${actorUser.name}`); }; module.exports = { @@ -8,7 +8,19 @@ module.exports = { type: 'ref', required: true, }, - user: { + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + list: { + type: 'ref', + required: true, + }, + actorUser: { type: 'ref', required: true, }, @@ -21,10 +33,6 @@ module.exports = { const card = await Card.archiveOne(inputs.record.id); if (card) { - const { board } = await sails.helpers.lists - .getProjectPath(card.listId) - .intercept('pathNotFound', () => Errors.LIST_NOT_FOUND); - sails.sockets.broadcast( `board:${card.boardId}`, 'cardDelete', @@ -34,18 +42,22 @@ module.exports = { inputs.request, ); - if (sails.config.custom.slackBotToken) { - buildAndSendSlackMessage(inputs.user, card); - } - - await sails.helpers.utils.sendWebhook.with({ - event: 'CARD_DELETE', - data: card, - projectId: board.projectId, - user: inputs.request.currentUser, - card, - board, + sails.helpers.utils.sendWebhooks.with({ + event: 'cardDelete', + data: { + item: card, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + }, + }, + user: inputs.actorUser, }); + + if (sails.config.custom.slackBotToken) { + buildAndSendSlackMessage(card, inputs.actorUser); + } } return card; diff --git a/server/api/helpers/cards/duplicate-one.js b/server/api/helpers/cards/duplicate-one.js index 71645533..94a0d755 100644 --- a/server/api/helpers/cards/duplicate-one.js +++ b/server/api/helpers/cards/duplicate-one.js @@ -25,6 +25,10 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, @@ -62,6 +66,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); const card = await Card.create({ @@ -108,6 +114,19 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'cardCreate', + data: { + item: card, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + }, + }, + user: values.creatorUser, + }); + if (values.creatorUser.subscribeToOwnCards) { await CardSubscription.create({ cardId: card.id, @@ -120,6 +139,8 @@ module.exports = { isSubscribed: true, }, }); + + // TODO: send webhooks } await sails.helpers.actions.createOne.with({ @@ -131,18 +152,10 @@ module.exports = { }, user: values.creatorUser, }, - board: inputs.board, - request: inputs.request, - }); - - await sails.helpers.utils.sendWebhook.with({ - event: 'CARD_CREATE', - data: card, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card, + project: inputs.project, board: inputs.board, list: inputs.list, + request: inputs.request, }); return { diff --git a/server/api/helpers/cards/update-one.js b/server/api/helpers/cards/update-one.js index cc054039..1c1dddae 100644 --- a/server/api/helpers/cards/update-one.js +++ b/server/api/helpers/cards/update-one.js @@ -7,8 +7,14 @@ const valuesValidator = (value) => { return false; } - if (!_.isUndefined(value.board) && !_.isPlainObject(value.board)) { - return false; + if (!_.isUndefined(value.board)) { + if (!_.isPlainObject(value.project)) { + return false; + } + + if (!_.isPlainObject(value.board)) { + return false; + } } if (!_.isUndefined(value.list) && !_.isPlainObject(value.list)) { @@ -29,14 +35,21 @@ module.exports = { custom: valuesValidator, required: true, }, - user: { + project: { type: 'ref', + required: true, }, board: { type: 'ref', + required: true, }, list: { type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, }, request: { type: 'ref', @@ -45,65 +58,56 @@ module.exports = { exits: { positionMustBeInValues: {}, + boardInValuesMustBelongToProject: {}, listMustBeInValues: {}, listInValuesMustBelongToBoard: {}, - userMustBePresent: {}, - boardMustBePresent: {}, - listMustBePresent: {}, }, async fn(inputs) { const { isSubscribed, ...values } = inputs.values; - if (values.board || values.list || !_.isUndefined(values.position)) { - if (!inputs.board) { - throw 'boardMustBePresent'; + if (values.project && values.project.id === inputs.project.id) { + delete values.project; + } + + const project = values.project || inputs.project; + + if (values.board) { + if (values.board.projectId !== project.id) { + throw 'boardInValuesMustBelongToProject'; } - if (values.board) { - if (values.board.id === inputs.board.id) { - delete values.board; - } else { - values.boardId = values.board.id; - } - } - - const board = values.board || inputs.board; - - if (values.list) { - if (!inputs.list) { - throw 'listMustBePresent'; - } - - if (values.list.boardId !== board.id) { - throw 'listInValuesMustBelongToBoard'; - } - - if (values.list.id === inputs.list.id) { - delete values.list; - } else { - values.listId = values.list.id; - } - } - - if (values.list) { - if (_.isUndefined(values.position)) { - throw 'positionMustBeInValues'; - } - } else if (values.board) { - throw 'listMustBeInValues'; + if (values.board.id === inputs.board.id) { + delete values.board; + } else { + values.boardId = values.board.id; } } - if ((!_.isUndefined(isSubscribed) || values.board || values.list) && !inputs.user) { - throw 'userMustBePresent'; + const board = values.board || inputs.board; + + if (values.list) { + if (values.list.boardId !== board.id) { + throw 'listInValuesMustBelongToBoard'; + } + + if (values.list.id === inputs.list.id) { + delete values.list; + } else { + values.listId = values.list.id; + } + } else if (values.board) { + throw 'listMustBeInValues'; + } + + const list = values.list || inputs.list; + + if (values.list && _.isUndefined(values.position)) { + throw 'positionMustBeInValues'; } if (!_.isUndefined(values.position)) { - const boardId = values.boardId || inputs.record.boardId; - const listId = values.listId || inputs.record.listId; - - const cards = await sails.helpers.lists.getCards(listId, inputs.record.id); + const cards = await sails.helpers.lists.getCards(list.id, inputs.record.id); const { position, repositions } = sails.helpers.utils.insertToPositionables( values.position, @@ -115,17 +119,19 @@ module.exports = { repositions.forEach(async ({ id, position: nextPosition }) => { await Card.update({ id, - listId, + listId: list.id, }).set({ position: nextPosition, }); - sails.sockets.broadcast(`board:${boardId}`, 'cardUpdate', { + sails.sockets.broadcast(`board:${board.id}`, 'cardUpdate', { item: { id, position: nextPosition, }, }); + + // TODO: send webhooks }); } @@ -175,10 +181,12 @@ module.exports = { } const { id } = await sails.helpers.labels.createOne.with({ + project, values: { ..._.omit(label, ['id', 'boardId']), board: values.board, }, + actorUser: inputs.actorUser, }); return id; @@ -209,6 +217,8 @@ module.exports = { isSubscribed: true, }, }); + + // TODO: send webhooks }); } else { sails.sockets.broadcast( @@ -221,18 +231,33 @@ module.exports = { ); } + sails.helpers.utils.sendWebhooks.with({ + event: 'cardUpdate', + data: { + item: card, + included: { + projects: [project], + boards: [board], + lists: [list], + }, + }, + user: inputs.actorUser, + }); + if (!values.board && values.list) { await sails.helpers.actions.createOne.with({ + project, + board, + list, values: { card, - user: inputs.user, + user: inputs.actorUser, type: Action.Types.MOVE_CARD, data: { fromList: _.pick(inputs.list, ['id', 'name']), toList: _.pick(values.list, ['id', 'name']), }, }, - board: inputs.board, request: inputs.request, }); } @@ -241,23 +266,26 @@ module.exports = { } if (!_.isUndefined(isSubscribed)) { - const prevIsSubscribed = await sails.helpers.users.isCardSubscriber(inputs.user.id, card.id); + const prevIsSubscribed = await sails.helpers.users.isCardSubscriber( + inputs.actorUser.id, + card.id, + ); if (isSubscribed !== prevIsSubscribed) { if (isSubscribed) { await CardSubscription.create({ cardId: card.id, - userId: inputs.user.id, + userId: inputs.actorUser.id, }).tolerate('E_UNIQUE'); } else { await CardSubscription.destroyOne({ cardId: card.id, - userId: inputs.user.id, + userId: inputs.actorUser.id, }); } sails.sockets.broadcast( - `user:${inputs.user.id}`, + `user:${inputs.actorUser.id}`, 'cardUpdate', { item: { @@ -267,18 +295,11 @@ module.exports = { }, inputs.request, ); + + // TODO: send webhooks } } - await sails.helpers.utils.sendWebhook.with({ - event: 'CARD_UPDATE', - data: card, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card, - board: inputs.board, - }); - return card; }, }; diff --git a/server/api/helpers/labels/create-one.js b/server/api/helpers/labels/create-one.js index 04f58c36..f9d66d64 100644 --- a/server/api/helpers/labels/create-one.js +++ b/server/api/helpers/labels/create-one.js @@ -21,6 +21,14 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -50,6 +58,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); const label = await Label.create({ @@ -67,6 +77,18 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'labelCreate', + data: { + item: label, + included: { + projects: [inputs.project], + boards: [values.board], + }, + }, + user: inputs.actorUser, + }); + return label; }, }; diff --git a/server/api/helpers/labels/delete-one.js b/server/api/helpers/labels/delete-one.js index 654d97c7..da45eb9c 100644 --- a/server/api/helpers/labels/delete-one.js +++ b/server/api/helpers/labels/delete-one.js @@ -4,6 +4,18 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -25,6 +37,18 @@ module.exports = { }, inputs.request, ); + + sails.helpers.utils.sendWebhooks.with({ + event: 'labelDelete', + data: { + item: label, + included: { + projects: [inputs.project], + boards: [inputs.board], + }, + }, + user: inputs.actorUser, + }); } return label; diff --git a/server/api/helpers/labels/update-one.js b/server/api/helpers/labels/update-one.js index 3237ce8c..01efcdbd 100644 --- a/server/api/helpers/labels/update-one.js +++ b/server/api/helpers/labels/update-one.js @@ -21,6 +21,18 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -53,6 +65,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); } @@ -67,6 +81,18 @@ module.exports = { }, inputs.request, ); + + sails.helpers.utils.sendWebhooks.with({ + event: 'labelUpdate', + data: { + item: label, + included: { + projects: [inputs.project], + boards: [inputs.board], + }, + }, + user: inputs.actorUser, + }); } return label; diff --git a/server/api/helpers/lists/create-one.js b/server/api/helpers/lists/create-one.js index 1c65fcce..833ff6d2 100644 --- a/server/api/helpers/lists/create-one.js +++ b/server/api/helpers/lists/create-one.js @@ -21,6 +21,14 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -50,6 +58,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); const list = await List.create({ @@ -67,12 +77,16 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'LIST_CREATE', - data: list, - projectId: values.board.projectId, - user: inputs.request.currentUser, - board: values.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'listCreate', + data: { + item: list, + included: { + projects: [inputs.project], + boards: [values.board], + }, + }, + user: inputs.actorUser, }); return list; diff --git a/server/api/helpers/lists/delete-one.js b/server/api/helpers/lists/delete-one.js index d4f7c49b..78041730 100644 --- a/server/api/helpers/lists/delete-one.js +++ b/server/api/helpers/lists/delete-one.js @@ -4,10 +4,18 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -26,12 +34,16 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'LIST_DELETE', - data: list, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'listDelete', + data: { + item: list, + included: { + projects: [inputs.project], + boards: [inputs.board], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/lists/sort-one.js b/server/api/helpers/lists/sort-one.js index c5e4fbd2..58384ef6 100644 --- a/server/api/helpers/lists/sort-one.js +++ b/server/api/helpers/lists/sort-one.js @@ -11,6 +11,18 @@ module.exports = { isIn: Object.values(List.SortTypes), defaultsTo: List.SortTypes.NAME_ASC, }, + project: { + type: 'ref', + required: true, + }, + board: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -65,6 +77,19 @@ module.exports = { inputs.request, ); + sails.helpers.utils.sendWebhooks.with({ + event: 'listSort', + data: { + item: inputs.record, + included: { + cards, + projects: [inputs.project], + boards: [inputs.board], + }, + }, + user: inputs.actorUser, + }); + return cards; }, }; diff --git a/server/api/helpers/lists/update-one.js b/server/api/helpers/lists/update-one.js index ba186b82..0335b9ee 100644 --- a/server/api/helpers/lists/update-one.js +++ b/server/api/helpers/lists/update-one.js @@ -21,10 +21,18 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -57,6 +65,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); } @@ -72,12 +82,16 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'LIST_UPDATE', - data: list, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'listUpdate', + data: { + item: list, + included: { + projects: [inputs.project], + boards: [inputs.board], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/notifications/create-one.js b/server/api/helpers/notifications/create-one.js index f24e4bfa..95d88382 100644 --- a/server/api/helpers/notifications/create-one.js +++ b/server/api/helpers/notifications/create-one.js @@ -15,14 +15,14 @@ const valuesValidator = (value) => { }; // TODO: use templates (views) to build html -const buildAndSendEmail = async (user, board, card, action, notifiableUser) => { +const buildAndSendEmail = async (board, card, action, actorUser, notifiableUser) => { let emailData; switch (action.type) { case Action.Types.MOVE_CARD: emailData = { - subject: `${user.name} moved ${card.name} from ${action.data.fromList.name} to ${action.data.toList.name} on ${board.name}`, + subject: `${actorUser.name} moved ${card.name} from ${action.data.fromList.name} to ${action.data.toList.name} on ${board.name}`, html: - `

${user.name} moved ` + + `

${actorUser.name} moved ` + `${card.name} ` + `from ${action.data.fromList.name} to ${action.data.toList.name} ` + `on ${board.name}

`, @@ -31,9 +31,9 @@ const buildAndSendEmail = async (user, board, card, action, notifiableUser) => { break; case Action.Types.COMMENT_CARD: emailData = { - subject: `${user.name} left a new comment to ${card.name} on ${board.name}`, + subject: `${actorUser.name} left a new comment to ${card.name} on ${board.name}`, html: - `

${user.name} left a new comment to ` + + `

${actorUser.name} left a new comment to ` + `${card.name} ` + `on ${board.name}

` + `

${action.data.text}

`, @@ -57,7 +57,7 @@ module.exports = { custom: valuesValidator, required: true, }, - user: { + project: { type: 'ref', required: true, }, @@ -65,10 +65,18 @@ module.exports = { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, card: { type: 'ref', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, }, async fn(inputs) { @@ -96,9 +104,24 @@ module.exports = { notifiableUser = await sails.helpers.users.getOne(notification.userId); } - buildAndSendEmail(inputs.user, inputs.board, inputs.card, values.action, notifiableUser); + buildAndSendEmail(inputs.board, inputs.card, values.action, inputs.actorUser, notifiableUser); } + sails.helpers.utils.sendWebhooks.with({ + event: 'notificationCreate', + data: { + item: notification, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + actions: [values.action], + }, + }, + user: inputs.actorUser, + }); + return notification; }, }; diff --git a/server/api/helpers/notifications/update-many.js b/server/api/helpers/notifications/update-many.js index fee2c024..7775919d 100644 --- a/server/api/helpers/notifications/update-many.js +++ b/server/api/helpers/notifications/update-many.js @@ -12,8 +12,9 @@ module.exports = { type: 'json', required: true, }, - user: { + actorUser: { type: 'ref', + required: true, }, request: { type: 'ref', @@ -23,7 +24,9 @@ module.exports = { async fn(inputs) { const { values } = inputs; - const criteria = {}; + const criteria = { + userId: inputs.actorUser.id, + }; if (_.every(inputs.recordsOrIds, _.isPlainObject)) { criteria.id = sails.helpers.utils.mapRecords(inputs.recordsOrIds); @@ -31,10 +34,6 @@ module.exports = { criteria.id = inputs.recordsOrIds; } - if (inputs.user) { - criteria.userId = inputs.user.id; - } - const notifications = await Notification.update(criteria) .set({ ...values }) .fetch(); @@ -48,6 +47,14 @@ module.exports = { }, inputs.request, ); + + sails.helpers.utils.sendWebhooks.with({ + event: 'notificationUpdate', + data: { + item: notification, + }, + user: inputs.actorUser, + }); }); return notifications; diff --git a/server/api/helpers/project-managers/create-one.js b/server/api/helpers/project-managers/create-one.js index 56929d18..80a9a432 100644 --- a/server/api/helpers/project-managers/create-one.js +++ b/server/api/helpers/project-managers/create-one.js @@ -21,6 +21,10 @@ module.exports = { custom: valuesValidator, required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -55,6 +59,18 @@ module.exports = { ); }); + sails.helpers.utils.sendWebhooks.with({ + event: 'projectManagerCreate', + data: { + item: projectManager, + included: { + users: [values.user], + projects: [values.project], + }, + }, + user: inputs.actorUser, + }); + return projectManager; }, }; diff --git a/server/api/helpers/project-managers/delete-one.js b/server/api/helpers/project-managers/delete-one.js index 3677836d..aa9b7487 100644 --- a/server/api/helpers/project-managers/delete-one.js +++ b/server/api/helpers/project-managers/delete-one.js @@ -4,6 +4,10 @@ module.exports = { type: 'ref', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -27,6 +31,14 @@ module.exports = { inputs.request, ); }); + + sails.helpers.utils.sendWebhooks.with({ + event: 'projectManagerDelete', + data: { + item: projectManager, + }, + user: inputs.actorUser, + }); } return projectManager; diff --git a/server/api/helpers/projects/create-one.js b/server/api/helpers/projects/create-one.js index 7deb9c4a..a22dd698 100644 --- a/server/api/helpers/projects/create-one.js +++ b/server/api/helpers/projects/create-one.js @@ -4,7 +4,7 @@ module.exports = { type: 'json', required: true, }, - user: { + actorUser: { type: 'ref', required: true, }, @@ -20,7 +20,7 @@ module.exports = { const projectManager = await ProjectManager.create({ projectId: project.id, - userId: inputs.user.id, + userId: inputs.actorUser.id, }).fetch(); sails.sockets.broadcast( @@ -32,11 +32,12 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'PROJECT_CREATE', - data: project, - projectId: project.id, - user: inputs.request.currentUser, + sails.helpers.utils.sendWebhooks.with({ + event: 'projectCreate', + data: { + item: project, + }, + user: inputs.actorUser, }); return { diff --git a/server/api/helpers/projects/delete-one.js b/server/api/helpers/projects/delete-one.js index 1021d272..c5b3c9dc 100644 --- a/server/api/helpers/projects/delete-one.js +++ b/server/api/helpers/projects/delete-one.js @@ -4,6 +4,10 @@ module.exports = { type: 'ref', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -38,11 +42,12 @@ module.exports = { ); }); - await sails.helpers.utils.sendWebhook.with({ - event: 'PROJECT_DELETE', - data: project, - projectId: project.id, - user: inputs.request.currentUser, + sails.helpers.utils.sendWebhooks.with({ + event: 'projectDelete', + data: { + item: project, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/projects/update-one.js b/server/api/helpers/projects/update-one.js index 6b57a71c..f9e11eec 100644 --- a/server/api/helpers/projects/update-one.js +++ b/server/api/helpers/projects/update-one.js @@ -28,6 +28,10 @@ module.exports = { custom: valuesValidator, required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -109,11 +113,12 @@ module.exports = { ); }); - await sails.helpers.utils.sendWebhook.with({ - event: 'PROJECT_UPDATE', - data: project, - projectId: project.id, - user: inputs.request.currentUser, + sails.helpers.utils.sendWebhooks.with({ + event: 'projectUpdate', + data: { + item: project, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/tasks/create-one.js b/server/api/helpers/tasks/create-one.js index 7bb33182..5b156235 100644 --- a/server/api/helpers/tasks/create-one.js +++ b/server/api/helpers/tasks/create-one.js @@ -21,10 +21,22 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -54,6 +66,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); const task = await Task.create({ @@ -71,13 +85,18 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'TASK_CREATE', - data: task, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: values.card, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'taskCreate', + data: { + item: task, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [values.card], + }, + }, + user: inputs.actorUser, }); return task; diff --git a/server/api/helpers/tasks/delete-one.js b/server/api/helpers/tasks/delete-one.js index 3b876aea..c413e39d 100644 --- a/server/api/helpers/tasks/delete-one.js +++ b/server/api/helpers/tasks/delete-one.js @@ -4,10 +4,26 @@ module.exports = { type: 'ref', required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, + card: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -26,12 +42,18 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'TASK_DELETE', - data: task, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'taskDelete', + data: { + item: task, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/tasks/update-one.js b/server/api/helpers/tasks/update-one.js index 6d6b81ba..94c1d4b7 100644 --- a/server/api/helpers/tasks/update-one.js +++ b/server/api/helpers/tasks/update-one.js @@ -21,10 +21,26 @@ module.exports = { custom: valuesValidator, required: true, }, + project: { + type: 'ref', + required: true, + }, board: { type: 'ref', required: true, }, + list: { + type: 'ref', + required: true, + }, + card: { + type: 'ref', + required: true, + }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -57,6 +73,8 @@ module.exports = { position: nextPosition, }, }); + + // TODO: send webhooks }); } @@ -72,13 +90,18 @@ module.exports = { inputs.request, ); - await sails.helpers.utils.sendWebhook.with({ - event: 'TASK_UPDATE', - data: task, - projectId: inputs.board.projectId, - user: inputs.request.currentUser, - card: values.card, - board: inputs.board, + sails.helpers.utils.sendWebhooks.with({ + event: 'taskUpdate', + data: { + item: task, + included: { + projects: [inputs.project], + boards: [inputs.board], + lists: [inputs.list], + cards: [inputs.card], + }, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/users/create-one.js b/server/api/helpers/users/create-one.js index 87f36863..358ef679 100644 --- a/server/api/helpers/users/create-one.js +++ b/server/api/helpers/users/create-one.js @@ -27,6 +27,10 @@ module.exports = { custom: valuesValidator, required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -84,21 +88,12 @@ module.exports = { ); }); - /* The user could be created manually by an user or via OIDC. We hijack the id field, so one can differentiate between the two on the webhook side. */ - let initiator; - if (inputs.request && inputs.request.currentUser) { - initiator = inputs.request.currentUser; - } else { - initiator = { - id: 'oidc', - }; - } - - await sails.helpers.utils.sendWebhook.with({ - event: 'USER_CREATE', - data: { ...user, password: undefined }, - projectId: '', - user: initiator, + sails.helpers.utils.sendWebhooks.with({ + event: 'userCreate', + data: { + item: user, + }, + user: inputs.actorUser, }); return user; diff --git a/server/api/helpers/users/delete-one.js b/server/api/helpers/users/delete-one.js index 524db4b5..5c9c9f59 100644 --- a/server/api/helpers/users/delete-one.js +++ b/server/api/helpers/users/delete-one.js @@ -4,6 +4,10 @@ module.exports = { type: 'ref', required: true, }, + actorUser: { + type: 'ref', + required: true, + }, request: { type: 'ref', }, @@ -60,11 +64,12 @@ module.exports = { ); }); - await sails.helpers.utils.sendWebhook.with({ - event: 'USER_DELETE', - data: { ...user, password: undefined }, - projectId: '', - user: inputs.request.currentUser, + sails.helpers.utils.sendWebhooks.with({ + event: 'userDelete', + data: { + item: user, + }, + user: inputs.actorUser, }); } diff --git a/server/api/helpers/users/get-or-create-one-using-oidc.js b/server/api/helpers/users/get-or-create-one-using-oidc.js index 2186c099..c54b2095 100644 --- a/server/api/helpers/users/get-or-create-one-using-oidc.js +++ b/server/api/helpers/users/get-or-create-one-using-oidc.js @@ -88,8 +88,11 @@ module.exports = { // Otherwise, create a new user. if (!user) { - user = await sails.helpers.users - .createOne(values) + user = await sails.helpers.users.createOne + .with({ + values, + actorUser: User.OIDC, + }) .intercept('usernameAlreadyInUse', 'usernameAlreadyInUse'); } @@ -115,8 +118,12 @@ module.exports = { } if (Object.keys(updateValues).length > 0) { - user = await sails.helpers.users - .updateOne(user, updateValues, {}) // FIXME: hack for last parameter + user = await sails.helpers.users.updateOne + .with({ + record: user, + values: updateValues, + actorUser: User.OIDC, + }) .intercept('emailAlreadyInUse', 'emailAlreadyInUse') .intercept('usernameAlreadyInUse', 'usernameAlreadyInUse'); } diff --git a/server/api/helpers/users/update-one.js b/server/api/helpers/users/update-one.js index a829b43e..4d2b3910 100644 --- a/server/api/helpers/users/update-one.js +++ b/server/api/helpers/users/update-one.js @@ -38,7 +38,7 @@ module.exports = { custom: valuesValidator, required: true, }, - user: { + actorUser: { type: 'ref', required: true, }, @@ -62,14 +62,14 @@ module.exports = { let isOnlyPasswordChange = false; if (!_.isUndefined(values.password)) { + if (Object.keys(values).length === 1) { + isOnlyPasswordChange = true; + } + Object.assign(values, { password: bcrypt.hashSync(values.password, 10), passwordChangedAt: new Date().toISOString(), }); - - if (Object.keys(values).length === 1) { - isOnlyPasswordChange = true; - } } if (values.username) { @@ -118,7 +118,7 @@ module.exports = { inputs.request, ); - if (user.id === inputs.user.id && inputs.request && inputs.request.isSocket) { + if (user.id === inputs.actorUser.id && inputs.request && inputs.request.isSocket) { const tempRoom = uuid(); sails.sockets.addRoomMembersToRooms(`@user:${user.id}`, tempRoom, () => { @@ -153,14 +153,15 @@ module.exports = { inputs.request, ); }); - } - await sails.helpers.utils.sendWebhook.with({ - event: 'USER_UPDATE', - data: { ...user, password: undefined }, - projectId: '', - user: inputs.request.currentUser, - }); + sails.helpers.utils.sendWebhooks.with({ + event: 'userUpdate', + data: { + item: user, + }, + user: inputs.actorUser, + }); + } } return user; diff --git a/server/api/helpers/utils/jsonify-record.js b/server/api/helpers/utils/jsonify-record.js new file mode 100644 index 00000000..9927e09a --- /dev/null +++ b/server/api/helpers/utils/jsonify-record.js @@ -0,0 +1,14 @@ +module.exports = { + sync: true, + + inputs: { + record: { + type: 'ref', + required: true, + }, + }, + + fn(inputs) { + return inputs.record.toJSON ? inputs.record.toJSON() : inputs.record; + }, +}; diff --git a/server/api/helpers/utils/send-email.js b/server/api/helpers/utils/send-email.js index 02593f88..cb6a1e13 100644 --- a/server/api/helpers/utils/send-email.js +++ b/server/api/helpers/utils/send-email.js @@ -1,4 +1,6 @@ module.exports = { + // TODO: make sync? + inputs: { to: { type: 'string', diff --git a/server/api/helpers/utils/send-slack-message.js b/server/api/helpers/utils/send-slack-message.js index 510ae1b2..d17985e2 100644 --- a/server/api/helpers/utils/send-slack-message.js +++ b/server/api/helpers/utils/send-slack-message.js @@ -1,6 +1,8 @@ const POST_MESSAGE_API_URL = 'https://slack.com/api/chat.postMessage'; module.exports = { + // TODO: make sync? + inputs: { markdown: { type: 'string', diff --git a/server/api/helpers/utils/send-webhook.js b/server/api/helpers/utils/send-webhook.js deleted file mode 100644 index a33f7a89..00000000 --- a/server/api/helpers/utils/send-webhook.js +++ /dev/null @@ -1,115 +0,0 @@ -const EVENT_TYPES = { - ACTION_CREATE: 'action_create', - ACTION_UPDATE: 'action_update', - ACTION_DELETE: 'action_delete', - - CARD_CREATE: 'card_create', - CARD_UPDATE: 'card_update', - CARD_DELETE: 'card_delete', - - CARD_MEMBERSHIP_CREATE: 'card_membership_create', - CARD_MEMBERSHIP_DELETE: 'card_membership_delete', - - LIST_CREATE: 'list_create', - LIST_UPDATE: 'list_update', - LIST_DELETE: 'list_delete', - - BOARD_CREATE: 'board_create', - BOARD_UPDATE: 'board_update', - BOARD_DELETE: 'board_delete', - - ATTACHMENT_CREATE: 'attachment_create', - ATTACHMENT_UPDATE: 'attachment_update', - ATTACHMENT_DELETE: 'attachment_delete', - - PROJECT_CREATE: 'project_create', - PROJECT_UPDATE: 'project_update', - PROJECT_DELETE: 'project_delete', - - TASK_CREATE: 'task_create', - TASK_UPDATE: 'task_update', - TASK_DELETE: 'task_delete', - - USER_CREATE: 'user_create', - USER_UPDATE: 'user_update', - USER_DELETE: 'user_delete', -}; - -/** - * Sends a webhook notification to a configured URL. - * - * @param {Object} inputs - Data to include in the webhook payload. - * @param {string} inputs.event - The event type (see {@link EVENT_TYPES}). - * @param {*} inputs.data - The actual data related to the event. - * @param {string} inputs.projectId - The project ID associated with the event. - * @param {ref} [inputs.user] - Optional user object associated with the event. - * @param {ref} [inputs.card] - Optional card object associated with the event. - * @param {ref} [inputs.board] - Optional board object associated with the event. - * @param {ref} [inputs.list] - Optional list object associated with the event. - * @returns {Promise} - */ -async function sendWebhook(inputs) { - const url = sails.config.custom.webhookUrl; - const headers = { - 'Content-Type': 'application/json', - }; - if (sails.config.custom.webhookBearer) { - headers.Authorization = `Bearer ${sails.config.custom.webhookBearer}`; - } - - const body = JSON.stringify({ - ...inputs, - user: { - ...inputs.user, - password: undefined, - }, - }); - - const req = await fetch(url, { - method: 'POST', - headers, - body, - }); - if (req.status !== 200) { - sails.log.error(`Webhook failed with status ${req.status} and message: ${await req.text()}`); - } -} - -module.exports = { - eventTypes: EVENT_TYPES, - inputs: { - event: { - type: 'string', - isIn: Object.keys(EVENT_TYPES), - required: true, - }, - data: { - type: 'ref', - required: true, - }, - projectId: { - type: 'string', - default: '', - }, - user: { - type: 'ref', - }, - card: { - type: 'ref', - }, - board: { - type: 'ref', - }, - list: { - type: 'ref', - }, - }, - async fn(inputs) { - if (!sails.config.custom.webhookUrl) return; - try { - await sendWebhook(inputs); - } catch (err) { - sails.log.error(err); - } - }, -}; diff --git a/server/api/helpers/utils/send-webhooks.js b/server/api/helpers/utils/send-webhooks.js new file mode 100644 index 00000000..57da1bf0 --- /dev/null +++ b/server/api/helpers/utils/send-webhooks.js @@ -0,0 +1,103 @@ +const jsonifyData = (data) => { + const nextData = {}; + + if (data.item) { + nextData.item = sails.helpers.utils.jsonifyRecord(data.item); + } + + if (data.items) { + nextData.items = data.items.map((item) => sails.helpers.utils.jsonifyRecord(item)); + } + + if (data.included) { + nextData.included = Object.entries(data.included).reduce( + (result, [key, items]) => ({ + ...result, + [key]: items.map((item) => sails.helpers.utils.jsonifyRecord(item)), + }), + {}, + ); + } + + return nextData; +}; + +/** + * Sends a webhook notification to a configured URL. + * + * @param {*} webhook - Webhook configuration. + * @param {string} event - The event (see {@link Events}). + * @param {*} data - The actual data related to the event. + * @param {ref} user - User object associated with the event. + * @returns {Promise} + */ +async function sendWebhook(webhook, event, data, user) { + const headers = { + 'Content-Type': 'application/json', + }; + + if (webhook.accessToken) { + headers.Authorization = `Bearer ${webhook.accessToken}`; + } + + const body = JSON.stringify({ + event, + data: jsonifyData(data), + user: sails.helpers.utils.jsonifyRecord(user), + }); + + const response = await fetch(webhook.url, { + headers, + body, + method: 'POST', + }); + + if (!response.ok) { + const message = await response.text(); + + sails.log.error( + `Webhook ${webhook.url} failed with status ${response.status} and message: ${message}`, + ); + } +} + +module.exports = { + sync: true, + + inputs: { + event: { + type: 'string', + required: true, + }, + data: { + type: 'ref', + required: true, + }, + user: { + type: 'ref', + required: true, + }, + }, + + fn(inputs) { + if (!sails.config.custom.webhooks) { + return; + } + + sails.config.custom.webhooks.forEach((webhook) => { + if (!webhook.url) { + return; + } + + if (webhook.excludedEvents && webhook.excludedEvents.includes(inputs.event)) { + return; + } + + if (webhook.events && !webhook.events.includes(inputs.event)) { + return; + } + + sendWebhook(webhook, inputs.event, inputs.data, inputs.user); + }); + }, +}; diff --git a/server/api/models/User.js b/server/api/models/User.js index 1d875318..09034437 100755 --- a/server/api/models/User.js +++ b/server/api/models/User.js @@ -5,7 +5,13 @@ * @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models */ +const OIDC = { + id: '_oidc', +}; + module.exports = { + OIDC, + attributes: { // ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗ // ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗ diff --git a/server/config/custom.js b/server/config/custom.js index 8e335c50..45609fd5 100644 --- a/server/config/custom.js +++ b/server/config/custom.js @@ -60,9 +60,8 @@ module.exports.custom = { smtpPassword: process.env.SMTP_PASSWORD, smtpFrom: process.env.SMTP_FROM, + webhooks: JSON.parse(process.env.WEBHOOKS), // TODO: validate structure + slackBotToken: process.env.SLACK_BOT_TOKEN, slackChannelId: process.env.SLACK_CHANNEL_ID, - - webhookUrl: process.env.WEBHOOK_URL, - webhookBearer: process.env.WEBHOOK_BEARER, };