From 5d156c0db9007e94633ee67913557be60ebf39cc Mon Sep 17 00:00:00 2001 From: Samir Date: Tue, 19 Feb 2019 01:18:06 +0100 Subject: [PATCH 1/3] copilot workflow changes --- src/constants.js | 4 ++ src/events/busApi.js | 38 ++++++++++- src/routes/projectMemberInvites/create.js | 2 +- src/routes/projectMemberInvites/update.js | 14 ++-- .../projectMemberInvites/update.spec.js | 64 ++++++++++++++++++- 5 files changed, 113 insertions(+), 9 deletions(-) diff --git a/src/constants.js b/src/constants.js index 032e1e8d..9b9f8141 100644 --- a/src/constants.js +++ b/src/constants.js @@ -27,6 +27,7 @@ export const USER_ROLE = { MANAGER: 'Connect Manager', COPILOT: 'Connect Copilot', CONNECT_ADMIN: 'Connect Admin', + COPILOT_MANAGER: 'Connect Copilot Manager', }; export const ADMIN_ROLES = [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN]; @@ -130,7 +131,10 @@ export const BUS_API_EVENT = { // Project Member Invites PROJECT_MEMBER_INVITE_CREATED: 'notifications.connect.project.member.invite.created', + PROJECT_MEMBER_INVITE_REQUESTED: 'notifications.connect.project.member.invite.requested', PROJECT_MEMBER_INVITE_UPDATED: 'notifications.connect.project.member.invite.updated', + PROJECT_MEMBER_COPILOT_ADDED: 'notifications.connect.project.member.copilot.added', + PROJECT_MEMBER_COPILOT_REFUSED: 'notifications.connect.project.member.copilot.refused', PROJECT_MEMBER_EMAIL_INVITE_CREATED: 'connect.action.email.project.member.invite.created', }; diff --git a/src/events/busApi.js b/src/events/busApi.js index 141ee3e4..ab848f8c 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -1,6 +1,7 @@ import _ from 'lodash'; import config from 'config'; -import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_PHASE_STATUS, PROJECT_MEMBER_ROLE, MILESTONE_STATUS } +import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_PHASE_STATUS, PROJECT_MEMBER_ROLE, MILESTONE_STATUS, + INVITE_STATUS } from '../constants'; import { createEvent } from '../services/busApi'; import models from '../models'; @@ -696,7 +697,7 @@ module.exports = (app, logger) => { } }); - app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, userId, email }) => { + app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, userId, email, role }) => { logger.debug('receive PROJECT_MEMBER_INVITE_CREATED event'); const projectId = _.parseInt(req.params.projectId); @@ -707,9 +708,18 @@ module.exports = (app, logger) => { email, initiatorUserId: req.authUser.userId, }, logger); + + if (role === PROJECT_MEMBER_ROLE.COPILOT) { + createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REQUESTED, { + projectId, + userId, + email, + initiatorUserId: req.authUser.userId, + }, logger); + } }); - app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, ({ req, userId, email, status }) => { + app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, ({ req, userId, email, status, role, createdBy }) => { logger.debug('receive PROJECT_MEMBER_INVITE_UPDATED event'); const projectId = _.parseInt(req.params.projectId); @@ -721,5 +731,27 @@ module.exports = (app, logger) => { status, initiatorUserId: req.authUser.userId, }, logger); + + if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.ACCEPTED) { + // send event to bus api + createEvent(BUS_API_EVENT.PROJECT_MEMBER_COPILOT_ADDED, { + projectId, + userId, + createdBy, + email, + status, + initiatorUserId: req.authUser.userId, + }, logger); + } else if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.REFUSED) { + // send event to bus api + createEvent(BUS_API_EVENT.PROJECT_MEMBER_COPILOT_REFUSED, { + projectId, + userId, + createdBy, + email, + status, + initiatorUserId: req.authUser.userId, + }, logger); + } }); }; diff --git a/src/routes/projectMemberInvites/create.js b/src/routes/projectMemberInvites/create.js index 3214c493..77566a4a 100644 --- a/src/routes/projectMemberInvites/create.js +++ b/src/routes/projectMemberInvites/create.js @@ -250,7 +250,7 @@ module.exports = [ { correlationId: req.id }, ); // send email invite (async) - if (v.email && !v.userId) { + if (v.email && !v.userId & v.role !== PROJECT_MEMBER_ROLE.COPILOT) { sendInviteEmail(req, projectId, v); } }); diff --git a/src/routes/projectMemberInvites/update.js b/src/routes/projectMemberInvites/update.js index 300ba0ad..a73f2cbf 100644 --- a/src/routes/projectMemberInvites/update.js +++ b/src/routes/projectMemberInvites/update.js @@ -4,7 +4,7 @@ import Joi from 'joi'; import { middleware as tcMiddleware } from 'tc-core-library-js'; import models from '../../models'; import util from '../../util'; -import { PROJECT_MEMBER_ROLE, MANAGER_ROLES, INVITE_STATUS, EVENT } from '../../constants'; +import { PROJECT_MEMBER_ROLE, MANAGER_ROLES, INVITE_STATUS, EVENT, USER_ROLE } from '../../constants'; /** * API to update invite member to project. @@ -66,8 +66,12 @@ module.exports = [ if (!util.hasRoles(req, MANAGER_ROLES) && invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) { error = `Project members can cancel invites only for ${PROJECT_MEMBER_ROLE.CUSTOMER}`; } - } else if ((!!putInvite.userId && putInvite.userId !== req.authUser.userId) || - (!!putInvite.email && putInvite.email !== req.authUser.email)) { + } else if ((!!invite.role && invite.role === PROJECT_MEMBER_ROLE.COPILOT) && + !req.authUser.roles.includes(USER_ROLE.COPILOT_MANAGER)) { + error = 'Only Connect copilot manager can add copilots'; + } else if (((!!putInvite.userId && putInvite.userId !== req.authUser.userId) || + (!!putInvite.email && putInvite.email !== req.authUser.email)) && + (!!invite.role && invite.role !== PROJECT_MEMBER_ROLE.COPILOT)) { error = 'Project members can only update invites for themselves'; } @@ -88,6 +92,8 @@ module.exports = [ userId: updatedInvite.userId, email: updatedInvite.email, status: updatedInvite.status, + role: updatedInvite.role, + createdBy: updatedInvite.createdBy, }); req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, updatedInvite, { correlationId: req.id, @@ -103,7 +109,7 @@ module.exports = [ const member = { projectId, role: updatedInvite.role, - userId: req.authUser.userId, + userId: updatedInvite.userId, createdBy: req.authUser.userId, updatedBy: req.authUser.userId, }; diff --git a/src/routes/projectMemberInvites/update.spec.js b/src/routes/projectMemberInvites/update.spec.js index a137e57e..c2f61df0 100644 --- a/src/routes/projectMemberInvites/update.spec.js +++ b/src/routes/projectMemberInvites/update.spec.js @@ -16,6 +16,7 @@ describe('Project member invite update', () => { let project1; let invite1; let invite2; + let invite3; beforeEach((done) => { testUtil.clearDb() @@ -73,7 +74,22 @@ describe('Project member invite update', () => { invite2 = in2.get({ plain: true, }); - done(); + models.ProjectMemberInvite.create({ + projectId: project1.id, + userId: 40051332, + email: null, + role: PROJECT_MEMBER_ROLE.COPILOT, + status: INVITE_STATUS.PENDING, + createdBy: 1, + updatedBy: 1, + createdAt: '2016-06-30 00:33:07+00', + updatedAt: '2016-06-30 00:33:07+00', + }).then((in3) => { + invite3 = in3.get({ + plain: true, + }); + done(); + }); }); }); }); @@ -243,6 +259,52 @@ describe('Project member invite update', () => { }); }); + it('should return 403 if try to update COPILOT role invite with copilot', (done) => { + const mockHttpClient = _.merge(testUtil.mockHttpClient, { + get: () => Promise.resolve({ + status: 200, + data: { + id: 'requesterId', + version: 'v3', + result: { + success: true, + status: 200, + content: [{ + roleName: USER_ROLE.COPILOT, + }], + }, + }, + }), + }); + sandbox.stub(util, 'getHttpClient', () => mockHttpClient); + request(server) + .put(`/v4/projects/${project1.id}/members/invite`) + .set({ + Authorization: `Bearer ${testUtil.jwts.copilot}`, + }) + .send({ + param: { + userId: invite3.userId, + status: INVITE_STATUS.ACCEPTED, + }, + }) + .expect('Content-Type', /json/) + .expect(403) + .end((err, res) => { + if (err) { + done(err); + } else { + const resJson = res.body.result.content; + should.exist(resJson); + res.body.result.status.should.equal(403); + const errorMessage = _.get(resJson, 'message', ''); + sinon.assert.match(errorMessage, 'Only Connect copilot manager can add copilots'); + done(); + } + }); + }); + + describe('Bus api', () => { let createEventSpy; From 79bd7f1d131d2590d659e0b91f496b6320e77b81 Mon Sep 17 00:00:00 2001 From: RishiRaj Date: Tue, 19 Feb 2019 11:21:29 +0530 Subject: [PATCH 2/3] Lint-fix --- src/routes/projectMemberInvites/create.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/projectMemberInvites/create.js b/src/routes/projectMemberInvites/create.js index 77566a4a..9143fcf2 100644 --- a/src/routes/projectMemberInvites/create.js +++ b/src/routes/projectMemberInvites/create.js @@ -250,7 +250,7 @@ module.exports = [ { correlationId: req.id }, ); // send email invite (async) - if (v.email && !v.userId & v.role !== PROJECT_MEMBER_ROLE.COPILOT) { + if (v.email && !v.userId && v.role !== PROJECT_MEMBER_ROLE.COPILOT) { sendInviteEmail(req, projectId, v); } }); From 396987e4361ee0d8c7edd7e9c02ec4c0afb277f4 Mon Sep 17 00:00:00 2001 From: Samir Date: Wed, 20 Feb 2019 11:15:30 +0100 Subject: [PATCH 3/3] update events naming --- src/constants.js | 4 ++-- src/events/busApi.js | 42 +++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/constants.js b/src/constants.js index 9b9f8141..76861abf 100644 --- a/src/constants.js +++ b/src/constants.js @@ -133,8 +133,8 @@ export const BUS_API_EVENT = { PROJECT_MEMBER_INVITE_CREATED: 'notifications.connect.project.member.invite.created', PROJECT_MEMBER_INVITE_REQUESTED: 'notifications.connect.project.member.invite.requested', PROJECT_MEMBER_INVITE_UPDATED: 'notifications.connect.project.member.invite.updated', - PROJECT_MEMBER_COPILOT_ADDED: 'notifications.connect.project.member.copilot.added', - PROJECT_MEMBER_COPILOT_REFUSED: 'notifications.connect.project.member.copilot.refused', + PROJECT_MEMBER_INVITE_APPROVED: 'notifications.connect.project.member.invite.approved', + PROJECT_MEMBER_INVITE_REJECTED: 'notifications.connect.project.member.invite.rejected', PROJECT_MEMBER_EMAIL_INVITE_CREATED: 'connect.action.email.project.member.invite.created', }; diff --git a/src/events/busApi.js b/src/events/busApi.js index ab848f8c..51bc6d45 100644 --- a/src/events/busApi.js +++ b/src/events/busApi.js @@ -701,14 +701,6 @@ module.exports = (app, logger) => { logger.debug('receive PROJECT_MEMBER_INVITE_CREATED event'); const projectId = _.parseInt(req.params.projectId); - // send event to bus api - createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, { - projectId, - userId, - email, - initiatorUserId: req.authUser.userId, - }, logger); - if (role === PROJECT_MEMBER_ROLE.COPILOT) { createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REQUESTED, { projectId, @@ -716,6 +708,14 @@ module.exports = (app, logger) => { email, initiatorUserId: req.authUser.userId, }, logger); + } else { + // send event to bus api + createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, { + projectId, + userId, + email, + initiatorUserId: req.authUser.userId, + }, logger); } }); @@ -723,31 +723,31 @@ module.exports = (app, logger) => { logger.debug('receive PROJECT_MEMBER_INVITE_UPDATED event'); const projectId = _.parseInt(req.params.projectId); - // send event to bus api - createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, { - projectId, - userId, - email, - status, - initiatorUserId: req.authUser.userId, - }, logger); - if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.ACCEPTED) { // send event to bus api - createEvent(BUS_API_EVENT.PROJECT_MEMBER_COPILOT_ADDED, { + createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_APPROVED, { projectId, userId, - createdBy, + originator: createdBy, email, status, initiatorUserId: req.authUser.userId, }, logger); } else if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.REFUSED) { // send event to bus api - createEvent(BUS_API_EVENT.PROJECT_MEMBER_COPILOT_REFUSED, { + createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REJECTED, { + projectId, + userId, + originator: createdBy, + email, + status, + initiatorUserId: req.authUser.userId, + }, logger); + } else { + // send event to bus api + createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, { projectId, userId, - createdBy, email, status, initiatorUserId: req.authUser.userId,