Skip to content

Commit b1737e5

Browse files
authored
Copilot workflow changes (#254)
* copilot workflow changes * Lint-fix * update events naming
1 parent 42a788e commit b1737e5

File tree

5 files changed

+128
-24
lines changed

5 files changed

+128
-24
lines changed

src/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export const USER_ROLE = {
2929
TOPCODER_ACCOUNT_MANAGER: 'Connect Account Manager',
3030
COPILOT: 'Connect Copilot',
3131
CONNECT_ADMIN: 'Connect Admin',
32+
COPILOT_MANAGER: 'Connect Copilot Manager',
3233
};
3334

3435
export const ADMIN_ROLES = [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN];
@@ -132,7 +133,10 @@ export const BUS_API_EVENT = {
132133

133134
// Project Member Invites
134135
PROJECT_MEMBER_INVITE_CREATED: 'notifications.connect.project.member.invite.created',
136+
PROJECT_MEMBER_INVITE_REQUESTED: 'notifications.connect.project.member.invite.requested',
135137
PROJECT_MEMBER_INVITE_UPDATED: 'notifications.connect.project.member.invite.updated',
138+
PROJECT_MEMBER_INVITE_APPROVED: 'notifications.connect.project.member.invite.approved',
139+
PROJECT_MEMBER_INVITE_REJECTED: 'notifications.connect.project.member.invite.rejected',
136140
PROJECT_MEMBER_EMAIL_INVITE_CREATED: 'connect.action.email.project.member.invite.created',
137141
};
138142

src/events/busApi.js

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import _ from 'lodash';
22
import config from 'config';
3-
import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_PHASE_STATUS, PROJECT_MEMBER_ROLE, MILESTONE_STATUS }
3+
import { EVENT, BUS_API_EVENT, PROJECT_STATUS, PROJECT_PHASE_STATUS, PROJECT_MEMBER_ROLE, MILESTONE_STATUS,
4+
INVITE_STATUS }
45
from '../constants';
56
import { createEvent } from '../services/busApi';
67
import models from '../models';
@@ -696,30 +697,61 @@ module.exports = (app, logger) => {
696697
}
697698
});
698699

699-
app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, userId, email }) => {
700+
app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, userId, email, role }) => {
700701
logger.debug('receive PROJECT_MEMBER_INVITE_CREATED event');
701702
const projectId = _.parseInt(req.params.projectId);
702703

703-
// send event to bus api
704-
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, {
705-
projectId,
706-
userId,
707-
email,
708-
initiatorUserId: req.authUser.userId,
709-
}, logger);
704+
if (role === PROJECT_MEMBER_ROLE.COPILOT) {
705+
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REQUESTED, {
706+
projectId,
707+
userId,
708+
email,
709+
initiatorUserId: req.authUser.userId,
710+
}, logger);
711+
} else {
712+
// send event to bus api
713+
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_CREATED, {
714+
projectId,
715+
userId,
716+
email,
717+
initiatorUserId: req.authUser.userId,
718+
}, logger);
719+
}
710720
});
711721

712-
app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, ({ req, userId, email, status }) => {
722+
app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, ({ req, userId, email, status, role, createdBy }) => {
713723
logger.debug('receive PROJECT_MEMBER_INVITE_UPDATED event');
714724
const projectId = _.parseInt(req.params.projectId);
715725

716-
// send event to bus api
717-
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, {
718-
projectId,
719-
userId,
720-
email,
721-
status,
722-
initiatorUserId: req.authUser.userId,
723-
}, logger);
726+
if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.ACCEPTED) {
727+
// send event to bus api
728+
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_APPROVED, {
729+
projectId,
730+
userId,
731+
originator: createdBy,
732+
email,
733+
status,
734+
initiatorUserId: req.authUser.userId,
735+
}, logger);
736+
} else if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.REFUSED) {
737+
// send event to bus api
738+
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REJECTED, {
739+
projectId,
740+
userId,
741+
originator: createdBy,
742+
email,
743+
status,
744+
initiatorUserId: req.authUser.userId,
745+
}, logger);
746+
} else {
747+
// send event to bus api
748+
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_UPDATED, {
749+
projectId,
750+
userId,
751+
email,
752+
status,
753+
initiatorUserId: req.authUser.userId,
754+
}, logger);
755+
}
724756
});
725757
};

src/routes/projectMemberInvites/create.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ module.exports = [
250250
{ correlationId: req.id },
251251
);
252252
// send email invite (async)
253-
if (v.email && !v.userId) {
253+
if (v.email && !v.userId && v.role !== PROJECT_MEMBER_ROLE.COPILOT) {
254254
sendInviteEmail(req, projectId, v);
255255
}
256256
});

src/routes/projectMemberInvites/update.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Joi from 'joi';
44
import { middleware as tcMiddleware } from 'tc-core-library-js';
55
import models from '../../models';
66
import util from '../../util';
7-
import { PROJECT_MEMBER_ROLE, MANAGER_ROLES, INVITE_STATUS, EVENT } from '../../constants';
7+
import { PROJECT_MEMBER_ROLE, MANAGER_ROLES, INVITE_STATUS, EVENT, USER_ROLE } from '../../constants';
88

99
/**
1010
* API to update invite member to project.
@@ -66,8 +66,12 @@ module.exports = [
6666
if (!util.hasRoles(req, MANAGER_ROLES) && invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) {
6767
error = `Project members can cancel invites only for ${PROJECT_MEMBER_ROLE.CUSTOMER}`;
6868
}
69-
} else if ((!!putInvite.userId && putInvite.userId !== req.authUser.userId) ||
70-
(!!putInvite.email && putInvite.email !== req.authUser.email)) {
69+
} else if ((!!invite.role && invite.role === PROJECT_MEMBER_ROLE.COPILOT) &&
70+
!req.authUser.roles.includes(USER_ROLE.COPILOT_MANAGER)) {
71+
error = 'Only Connect copilot manager can add copilots';
72+
} else if (((!!putInvite.userId && putInvite.userId !== req.authUser.userId) ||
73+
(!!putInvite.email && putInvite.email !== req.authUser.email)) &&
74+
(!!invite.role && invite.role !== PROJECT_MEMBER_ROLE.COPILOT)) {
7175
error = 'Project members can only update invites for themselves';
7276
}
7377

@@ -88,6 +92,8 @@ module.exports = [
8892
userId: updatedInvite.userId,
8993
email: updatedInvite.email,
9094
status: updatedInvite.status,
95+
role: updatedInvite.role,
96+
createdBy: updatedInvite.createdBy,
9197
});
9298
req.app.services.pubsub.publish(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_UPDATED, updatedInvite, {
9399
correlationId: req.id,
@@ -103,7 +109,7 @@ module.exports = [
103109
const member = {
104110
projectId,
105111
role: updatedInvite.role,
106-
userId: req.authUser.userId,
112+
userId: updatedInvite.userId,
107113
createdBy: req.authUser.userId,
108114
updatedBy: req.authUser.userId,
109115
};

src/routes/projectMemberInvites/update.spec.js

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ describe('Project member invite update', () => {
1616
let project1;
1717
let invite1;
1818
let invite2;
19+
let invite3;
1920

2021
beforeEach((done) => {
2122
testUtil.clearDb()
@@ -73,7 +74,22 @@ describe('Project member invite update', () => {
7374
invite2 = in2.get({
7475
plain: true,
7576
});
76-
done();
77+
models.ProjectMemberInvite.create({
78+
projectId: project1.id,
79+
userId: 40051332,
80+
email: null,
81+
role: PROJECT_MEMBER_ROLE.COPILOT,
82+
status: INVITE_STATUS.PENDING,
83+
createdBy: 1,
84+
updatedBy: 1,
85+
createdAt: '2016-06-30 00:33:07+00',
86+
updatedAt: '2016-06-30 00:33:07+00',
87+
}).then((in3) => {
88+
invite3 = in3.get({
89+
plain: true,
90+
});
91+
done();
92+
});
7793
});
7894
});
7995
});
@@ -243,6 +259,52 @@ describe('Project member invite update', () => {
243259
});
244260
});
245261

262+
it('should return 403 if try to update COPILOT role invite with copilot', (done) => {
263+
const mockHttpClient = _.merge(testUtil.mockHttpClient, {
264+
get: () => Promise.resolve({
265+
status: 200,
266+
data: {
267+
id: 'requesterId',
268+
version: 'v3',
269+
result: {
270+
success: true,
271+
status: 200,
272+
content: [{
273+
roleName: USER_ROLE.COPILOT,
274+
}],
275+
},
276+
},
277+
}),
278+
});
279+
sandbox.stub(util, 'getHttpClient', () => mockHttpClient);
280+
request(server)
281+
.put(`/v4/projects/${project1.id}/members/invite`)
282+
.set({
283+
Authorization: `Bearer ${testUtil.jwts.copilot}`,
284+
})
285+
.send({
286+
param: {
287+
userId: invite3.userId,
288+
status: INVITE_STATUS.ACCEPTED,
289+
},
290+
})
291+
.expect('Content-Type', /json/)
292+
.expect(403)
293+
.end((err, res) => {
294+
if (err) {
295+
done(err);
296+
} else {
297+
const resJson = res.body.result.content;
298+
should.exist(resJson);
299+
res.body.result.status.should.equal(403);
300+
const errorMessage = _.get(resJson, 'message', '');
301+
sinon.assert.match(errorMessage, 'Only Connect copilot manager can add copilots');
302+
done();
303+
}
304+
});
305+
});
306+
307+
246308
describe('Bus api', () => {
247309
let createEventSpy;
248310

0 commit comments

Comments
 (0)