Skip to content

Commit b5b574c

Browse files
committed
copilot manager updates
1 parent e14627c commit b5b574c

File tree

12 files changed

+74
-31
lines changed

12 files changed

+74
-31
lines changed

src/constants.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ export const USER_ROLE = {
3434

3535
export const ADMIN_ROLES = [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN];
3636

37-
export const MANAGER_ROLES = [...ADMIN_ROLES, USER_ROLE.MANAGER, PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER];
37+
export const MANAGER_ROLES = [
38+
...ADMIN_ROLES,
39+
USER_ROLE.MANAGER,
40+
USER_ROLE.TOPCODER_ACCOUNT_MANAGER,
41+
USER_ROLE.COPILOT_MANAGER,
42+
];
3843

3944
export const EVENT = {
4045
ROUTING_KEY: {
@@ -162,5 +167,8 @@ export const INVITE_STATUS = {
162167
PENDING: 'pending',
163168
ACCEPTED: 'accepted',
164169
REFUSED: 'refused',
170+
REQUESTED: 'requested',
171+
REQUEST_REJECTED: 'request_rejected',
172+
REQUEST_APPROVED: 'request_approved',
165173
CANCELED: 'canceled',
166174
};

src/events/busApi.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -697,15 +697,16 @@ module.exports = (app, logger) => {
697697
}
698698
});
699699

700-
app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, userId, email, role }) => {
700+
app.on(EVENT.ROUTING_KEY.PROJECT_MEMBER_INVITE_CREATED, ({ req, userId, email, status, role }) => {
701701
logger.debug('receive PROJECT_MEMBER_INVITE_CREATED event');
702702
const projectId = _.parseInt(req.params.projectId);
703703

704-
if (role === PROJECT_MEMBER_ROLE.COPILOT) {
704+
if (status === INVITE_STATUS.REQUESTED) {
705705
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REQUESTED, {
706706
projectId,
707707
userId,
708708
email,
709+
role,
709710
initiatorUserId: req.authUser.userId,
710711
}, logger);
711712
} else {
@@ -714,6 +715,7 @@ module.exports = (app, logger) => {
714715
projectId,
715716
userId,
716717
email,
718+
role,
717719
initiatorUserId: req.authUser.userId,
718720
}, logger);
719721
}
@@ -723,23 +725,25 @@ module.exports = (app, logger) => {
723725
logger.debug('receive PROJECT_MEMBER_INVITE_UPDATED event');
724726
const projectId = _.parseInt(req.params.projectId);
725727

726-
if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.ACCEPTED) {
728+
if (status === INVITE_STATUS.REQUEST_APPROVED) {
727729
// send event to bus api
728730
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_APPROVED, {
729731
projectId,
730732
userId,
731733
originator: createdBy,
732734
email,
735+
role,
733736
status,
734737
initiatorUserId: req.authUser.userId,
735738
}, logger);
736-
} else if (role === PROJECT_MEMBER_ROLE.COPILOT && status === INVITE_STATUS.REFUSED) {
739+
} else if (status === INVITE_STATUS.REQUEST_REJECTED) {
737740
// send event to bus api
738741
createEvent(BUS_API_EVENT.PROJECT_MEMBER_INVITE_REJECTED, {
739742
projectId,
740743
userId,
741744
originator: createdBy,
742745
email,
746+
role,
743747
status,
744748
initiatorUserId: req.authUser.userId,
745749
}, logger);
@@ -749,6 +753,7 @@ module.exports = (app, logger) => {
749753
projectId,
750754
userId,
751755
email,
756+
role,
752757
status,
753758
initiatorUserId: req.authUser.userId,
754759
}, logger);

src/models/projectMemberInvite.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ module.exports = function defineProjectMemberInvite(sequelize, DataTypes) {
5555
raw: true,
5656
});
5757
},
58+
getPendingAndReguestedInvitesForProject(projectId) {
59+
return this.findAll({
60+
where: {
61+
projectId,
62+
status: { $in: [INVITE_STATUS.PENDING, INVITE_STATUS.REQUESTED] },
63+
},
64+
raw: true,
65+
});
66+
},
5867
getPendingInviteByEmailOrUserId(projectId, email, userId) {
5968
const where = { projectId, status: INVITE_STATUS.PENDING };
6069

@@ -69,6 +78,16 @@ module.exports = function defineProjectMemberInvite(sequelize, DataTypes) {
6978
where,
7079
});
7180
},
81+
getRequestedInvite(projectId, userId) {
82+
const where = { projectId, status: INVITE_STATUS.REQUESTED };
83+
84+
if (userId) {
85+
_.assign(where, { userId });
86+
}
87+
return this.findOne({
88+
where,
89+
});
90+
},
7291
getProjectInvitesForUser(email, userId) {
7392
const where = { status: INVITE_STATUS.PENDING };
7493

src/permissions/project.edit.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import _ from 'lodash';
44
import util from '../util';
55
import models from '../models';
6-
import { USER_ROLE } from '../constants';
6+
import { MANAGER_ROLES } from '../constants';
77

88
/**
99
* Super admin, Topcoder Managers are allowed to edit any project
@@ -20,7 +20,7 @@ module.exports = freq => new Promise((resolve, reject) => {
2020
req.context.currentProjectMembers = members;
2121
// check if auth user has acecss to this project
2222
const hasAccess = util.hasAdminRole(req)
23-
|| util.hasRole(req, USER_ROLE.MANAGER)
23+
|| util.hasRoles(req, MANAGER_ROLES)
2424
|| !_.isUndefined(_.find(members, m => m.userId === req.authUser.userId));
2525

2626
if (!hasAccess) {

src/permissions/project.view.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import _ from 'lodash';
33
import util from '../util';
44
import models from '../models';
5-
import { USER_ROLE, PROJECT_STATUS, PROJECT_MEMBER_ROLE } from '../constants';
5+
import { USER_ROLE, PROJECT_STATUS, PROJECT_MEMBER_ROLE, MANAGER_ROLES } from '../constants';
66

77
/**
88
* Super admin, Topcoder Managers are allowed to view any projects
@@ -21,8 +21,7 @@ module.exports = freq => new Promise((resolve, reject) => {
2121
req.context.currentProjectMembers = members;
2222
// check if auth user has acecss to this project
2323
const hasAccess = util.hasAdminRole(req)
24-
|| util.hasRole(req, USER_ROLE.MANAGER)
25-
|| util.hasRole(req, USER_ROLE.TOPCODER_ACCOUNT_MANAGER)
24+
|| util.hasRoles(req, MANAGER_ROLES)
2625
|| !_.isUndefined(_.find(members, m => m.userId === currentUserId));
2726

2827
// if user is co-pilot and the project doesn't have any copilots then

src/routes/projectMemberInvites/create.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { middleware as tcMiddleware } from 'tc-core-library-js';
88
import models from '../../models';
99
import util from '../../util';
1010
import { PROJECT_MEMBER_ROLE, PROJECT_MEMBER_MANAGER_ROLES,
11-
MANAGER_ROLES, INVITE_STATUS, EVENT, BUS_API_EVENT } from '../../constants';
11+
MANAGER_ROLES, INVITE_STATUS, EVENT, BUS_API_EVENT, USER_ROLE } from '../../constants';
1212
import { createEvent } from '../../services/busApi';
1313

1414

@@ -224,7 +224,11 @@ module.exports = [
224224
const data = {
225225
projectId,
226226
role: invite.role,
227-
status: INVITE_STATUS.PENDING,
227+
// invite directly if user is admin or copilot manager
228+
status: (invite.role !== PROJECT_MEMBER_ROLE.COPILOT ||
229+
util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.COPILOT_MANAGER]))
230+
? INVITE_STATUS.PENDING
231+
: INVITE_STATUS.REQUESTED,
228232
createdBy: req.authUser.userId,
229233
updatedBy: req.authUser.userId,
230234
};
@@ -243,6 +247,7 @@ module.exports = [
243247
req,
244248
userId: v.userId,
245249
email: v.email,
250+
status: v.status,
246251
role: v.role,
247252
});
248253
req.app.services.pubsub.publish(
@@ -251,7 +256,7 @@ module.exports = [
251256
{ correlationId: req.id },
252257
);
253258
// send email invite (async)
254-
if (v.email && !v.userId && v.role !== PROJECT_MEMBER_ROLE.COPILOT) {
259+
if (v.email && !v.userId && v.status === INVITE_STATUS.PENDING) {
255260
sendInviteEmail(req, projectId, v);
256261
}
257262
});

src/routes/projectMemberInvites/update.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,17 @@ module.exports = [
4444
}
4545

4646
let invite;
47+
let requestedInvite;
4748
return models.ProjectMemberInvite.getPendingInviteByEmailOrUserId(
4849
projectId,
4950
putInvite.email,
5051
putInvite.userId,
5152
).then((_invite) => {
5253
invite = _invite;
53-
if (!invite) {
54+
}).then(() => models.ProjectMemberInvite.getRequestedInvite(projectId, putInvite.userId))
55+
.then((_requestedInvite) => {
56+
requestedInvite = _requestedInvite;
57+
if (!invite && !requestedInvite) {
5458
// check there is an existing invite for the user with status PENDING
5559
// handle 404
5660
const err = new Error(
@@ -60,18 +64,20 @@ module.exports = [
6064
return next(err);
6165
}
6266

67+
invite = invite || requestedInvite;
68+
6369
req.log.debug('Chekcing user permission for updating invite');
6470
let error = null;
65-
if (putInvite.status === INVITE_STATUS.CANCELED) {
71+
if (invite.status === INVITE_STATUS.REQUESTED &&
72+
!util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.COPILOT_MANAGER])) {
73+
error = 'Requested invites can only be updated by Copilot manager';
74+
} else if (putInvite.status === INVITE_STATUS.CANCELED) {
6675
if (!util.hasRoles(req, MANAGER_ROLES) && invite.role !== PROJECT_MEMBER_ROLE.CUSTOMER) {
6776
error = `Project members can cancel invites only for ${PROJECT_MEMBER_ROLE.CUSTOMER}`;
6877
}
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';
7278
} else if (((!!putInvite.userId && putInvite.userId !== req.authUser.userId) ||
7379
(!!putInvite.email && putInvite.email !== req.authUser.email)) &&
74-
(!!invite.role && invite.role !== PROJECT_MEMBER_ROLE.COPILOT)) {
80+
!util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.COPILOT_MANAGER])) {
7581
error = 'Project members can only update invites for themselves';
7682
}
7783

@@ -101,7 +107,8 @@ module.exports = [
101107

102108
req.log.debug('Adding user to project');
103109
// add user to project if accept invite
104-
if (updatedInvite.status === INVITE_STATUS.ACCEPTED) {
110+
if (updatedInvite.status === INVITE_STATUS.ACCEPTED ||
111+
updatedInvite.status === INVITE_STATUS.REQUEST_APPROVED) {
105112
return models.ProjectMember.getActiveProjectMembers(projectId)
106113
.then((members) => {
107114
req.context = req.context || {};

src/routes/projectMemberInvites/update.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('Project member invite update', () => {
6262
});
6363
models.ProjectMemberInvite.create({
6464
projectId: project1.id,
65-
userId: 40051332,
65+
userId: 40051334,
6666
email: null,
6767
role: PROJECT_MEMBER_ROLE.MANAGER,
6868
status: INVITE_STATUS.PENDING,
@@ -79,7 +79,7 @@ describe('Project member invite update', () => {
7979
userId: 40051332,
8080
email: null,
8181
role: PROJECT_MEMBER_ROLE.COPILOT,
82-
status: INVITE_STATUS.PENDING,
82+
status: INVITE_STATUS.REQUESTED,
8383
createdBy: 1,
8484
updatedBy: 1,
8585
createdAt: '2016-06-30 00:33:07+00',
@@ -298,7 +298,7 @@ describe('Project member invite update', () => {
298298
should.exist(resJson);
299299
res.body.result.status.should.equal(403);
300300
const errorMessage = _.get(resJson, 'message', '');
301-
sinon.assert.match(errorMessage, 'Only Connect copilot manager can add copilots');
301+
sinon.assert.match(errorMessage, 'Requested invites can only be updated by Copilot manager');
302302
done();
303303
}
304304
});

src/routes/projects/create.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import config from 'config';
77
import moment from 'moment';
88

99
import models from '../../models';
10-
import { PROJECT_MEMBER_ROLE, PROJECT_STATUS, PROJECT_PHASE_STATUS, USER_ROLE, EVENT, REGEX } from '../../constants';
10+
import { PROJECT_MEMBER_ROLE, MANAGER_ROLES, PROJECT_STATUS, PROJECT_PHASE_STATUS,
11+
EVENT, REGEX } from '../../constants';
1112
import fieldLookupValidation from '../../middlewares/fieldLookupValidation';
1213
import util from '../../util';
1314
import directProject from '../../services/directProject';
@@ -197,7 +198,7 @@ module.exports = [
197198
(req, res, next) => {
198199
const project = req.body.param;
199200
// by default connect admin and managers joins projects as manager
200-
const userRole = util.hasRoles(req, [USER_ROLE.CONNECT_ADMIN, USER_ROLE.MANAGER])
201+
const userRole = util.hasRoles(req, MANAGER_ROLES)
201202
? PROJECT_MEMBER_ROLE.MANAGER
202203
: PROJECT_MEMBER_ROLE.CUSTOMER;
203204
// set defaults

src/routes/projects/get.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ module.exports = [
6464
if (attachments) {
6565
project.attachments = attachments;
6666
}
67-
return models.ProjectMemberInvite.getPendingInvitesForProject(projectId);
67+
return models.ProjectMemberInvite.getPendingAndReguestedInvitesForProject(projectId);
6868
})
6969
.then((invites) => {
7070
project.invites = invites;

0 commit comments

Comments
 (0)