Skip to content

Commit e2ad1f7

Browse files
author
vikasrohit
authored
Merge pull request #387 from topcoder-platform/feature/new-roles
New user roles
2 parents 49de528 + f4c756c commit e2ad1f7

File tree

11 files changed

+142
-24
lines changed

11 files changed

+142
-24
lines changed

src/constants.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,20 @@ export const PROJECT_MEMBER_ROLE = {
2727
CUSTOMER: 'customer',
2828
COPILOT: 'copilot',
2929
ACCOUNT_MANAGER: 'account_manager',
30+
PROGRAM_MANAGER: 'program_manager',
31+
ACCOUNT_EXECUTIVE: 'account_executive',
32+
SOLUTION_ARCHITECT: 'solution_architect',
33+
PROJECT_MANAGER: 'project_manager',
3034
};
3135

3236
export const PROJECT_MEMBER_MANAGER_ROLES = [
3337
PROJECT_MEMBER_ROLE.MANAGER,
3438
PROJECT_MEMBER_ROLE.OBSERVER,
3539
PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER,
40+
PROJECT_MEMBER_ROLE.ACCOUNT_EXECUTIVE,
41+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
42+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
43+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
3644
];
3745

3846
export const USER_ROLE = {
@@ -42,6 +50,12 @@ export const USER_ROLE = {
4250
COPILOT: 'Connect Copilot',
4351
CONNECT_ADMIN: 'Connect Admin',
4452
COPILOT_MANAGER: 'Connect Copilot Manager',
53+
BUSINESS_DEVELOPMENT_REPRESENTATIVE: 'Business Development Representative',
54+
PRESALES: 'Presales',
55+
ACCOUNT_EXECUTIVE: 'Account Executive',
56+
PROGRAM_MANAGER: 'Program Manager',
57+
SOLUTION_ARCHITECT: 'Solution Architect',
58+
PROJECT_MANAGER: 'Project Manager',
4559
};
4660

4761
export const ADMIN_ROLES = [USER_ROLE.CONNECT_ADMIN, USER_ROLE.TOPCODER_ADMIN];
@@ -51,6 +65,13 @@ export const MANAGER_ROLES = [
5165
USER_ROLE.MANAGER,
5266
USER_ROLE.TOPCODER_ACCOUNT_MANAGER,
5367
USER_ROLE.COPILOT_MANAGER,
68+
USER_ROLE.BUSINESS_DEVELOPMENT_REPRESENTATIVE,
69+
USER_ROLE.PRESALES,
70+
USER_ROLE.ACCOUNT_EXECUTIVE,
71+
72+
USER_ROLE.PROGRAM_MANAGER,
73+
USER_ROLE.SOLUTION_ARCHITECT,
74+
USER_ROLE.PROJECT_MANAGER,
5475
];
5576

5677
export const EVENT = {

src/events/busApi.js

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,17 @@ module.exports = (app, logger) => {
122122
logger.debug('receive PROJECT_MEMBER_ADDED event');
123123

124124
let eventType;
125-
switch (member.role) {
126-
case PROJECT_MEMBER_ROLE.MANAGER:
127-
eventType = BUS_API_EVENT.MEMBER_JOINED_MANAGER;
128-
break;
129-
case PROJECT_MEMBER_ROLE.COPILOT:
130-
eventType = BUS_API_EVENT.MEMBER_JOINED_COPILOT;
131-
break;
132-
default:
133-
eventType = BUS_API_EVENT.MEMBER_JOINED;
134-
break;
125+
if ([
126+
PROJECT_MEMBER_ROLE.MANAGER,
127+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
128+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
129+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
130+
].includes(member.role)) {
131+
eventType = BUS_API_EVENT.MEMBER_JOINED_MANAGER;
132+
} else if (member.role === PROJECT_MEMBER_ROLE.COPILOT) {
133+
eventType = BUS_API_EVENT.MEMBER_JOINED_COPILOT;
134+
} else {
135+
eventType = BUS_API_EVENT.MEMBER_JOINED;
135136
}
136137
const projectId = _.parseInt(req.params.projectId);
137138

src/events/projectMembers/index.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,13 @@ const projectMemberAddedHandler = Promise.coroutine(function* a(logger, msg, cha
4444
const newMember = JSON.parse(msg.content.toString());
4545
const projectId = newMember.projectId;
4646
const directUpdatePromise = Promise.coroutine(function* () { // eslint-disable-line func-names
47-
if (_.indexOf([PROJECT_MEMBER_ROLE.COPILOT, PROJECT_MEMBER_ROLE.MANAGER], newMember.role) > -1) {
47+
if (_.indexOf([
48+
PROJECT_MEMBER_ROLE.COPILOT,
49+
PROJECT_MEMBER_ROLE.MANAGER,
50+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
51+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
52+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
53+
], newMember.role) > -1) {
4854
// add copilot/update manager permissions operation promise
4955
const directProjectId = yield models.Project.getDirectProjectId(projectId);
5056
if (directProjectId) {
@@ -116,7 +122,13 @@ const projectMemberRemovedHandler = Promise.coroutine(function* (logger, msg, ch
116122
const projectId = member.projectId;
117123
// remove copilot/manager operation promise
118124
const updateDirectProjectPromise = Promise.coroutine(function* () { // eslint-disable-line func-names
119-
if (_.indexOf([PROJECT_MEMBER_ROLE.COPILOT, PROJECT_MEMBER_ROLE.MANAGER], member.role) > -1) {
125+
if (_.indexOf([
126+
PROJECT_MEMBER_ROLE.COPILOT,
127+
PROJECT_MEMBER_ROLE.MANAGER,
128+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
129+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
130+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
131+
], member.role) > -1) {
120132
const directProjectId = yield models.Project.getDirectProjectId(projectId);
121133
if (directProjectId) {
122134
const token = yield util.getM2MToken();

src/permissions/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ export const PERMISSION = { // eslint-disable-line import/prefer-default-export
2525
ROLES_COPILOT_AND_ABOVE: {
2626
topcoderRoles: ADMIN_ROLES,
2727
projectRoles: [
28+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
29+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
30+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
2831
PROJECT_MEMBER_ROLE.MANAGER,
2932
PROJECT_MEMBER_ROLE.COPILOT,
3033
],

src/permissions/project.delete.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,13 @@ module.exports = freq => new Promise((resolve, reject) => {
2323
const hasAccess = util.hasAdminRole(req) ||
2424
!_.isUndefined(_.find(members, m => m.userId === req.authUser.userId &&
2525
((m.role === PROJECT_MEMBER_ROLE.CUSTOMER && m.isPrimary) ||
26-
m.role === PROJECT_MEMBER_ROLE.MANAGER)));
26+
[
27+
PROJECT_MEMBER_ROLE.MANAGER,
28+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
29+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
30+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
31+
].includes(m.role)
32+
)));
2733

2834
if (!hasAccess) {
2935
// user is not an admin nor is a registered project member

src/permissions/projectMember.delete.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ module.exports = freq => new Promise((resolve, reject) => {
2525
const memberToBeRemoved = _.find(members, m => m.id === prjMemberId);
2626
// check if auth user has acecss to this project
2727
const hasAccess = util.hasAdminRole(req)
28-
|| (authMember && memberToBeRemoved && (authMember.role === PROJECT_MEMBER_ROLE.MANAGER ||
28+
|| (authMember && memberToBeRemoved && ([
29+
PROJECT_MEMBER_ROLE.MANAGER,
30+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
31+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
32+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
33+
].includes(authMember.role) ||
2934
(authMember.role === PROJECT_MEMBER_ROLE.CUSTOMER && authMember.isPrimary &&
3035
memberToBeRemoved.role === PROJECT_MEMBER_ROLE.CUSTOMER) ||
3136
memberToBeRemoved.userId === req.authUser.userId));

src/routes/projectMemberInvites/create.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ module.exports = [
289289
}
290290
return Promise.all(promises).then((rolesList) => {
291291
if (!!invite.userIds && _.includes(PROJECT_MEMBER_MANAGER_ROLES, invite.role)) {
292-
req.log.debug('Chekcing if userId is allowed as manager');
292+
req.log.debug('Checking if userId is allowed as manager');
293293
const forbidUserList = [];
294294
_.zip(invite.userIds, rolesList).forEach((data) => {
295295
const [userId, roles] = data;

src/routes/projectMembers/create.js

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@ const createProjectMemberValidations = {
1818
param: Joi.object()
1919
.keys({
2020
role: Joi.any()
21-
.valid(PROJECT_MEMBER_ROLE.MANAGER, PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER, PROJECT_MEMBER_ROLE.COPILOT),
21+
.valid(
22+
PROJECT_MEMBER_ROLE.MANAGER,
23+
PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER,
24+
PROJECT_MEMBER_ROLE.COPILOT,
25+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
26+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
27+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
28+
PROJECT_MEMBER_ROLE.ACCOUNT_EXECUTIVE,
29+
),
2230
}),
2331
},
2432
};
@@ -39,9 +47,49 @@ module.exports = [
3947
return next(err);
4048
}
4149

50+
if (PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT === targetRole &&
51+
!util.hasRoles(req, [USER_ROLE.SOLUTION_ARCHITECT])) {
52+
const err = new Error(`Only solution architect is able to join as ${targetRole}`);
53+
err.status = 401;
54+
return next(err);
55+
}
56+
57+
if (PROJECT_MEMBER_ROLE.PROJECT_MANAGER === targetRole &&
58+
!util.hasRoles(req, [USER_ROLE.PROJECT_MANAGER])) {
59+
const err = new Error(`Only project manager is able to join as ${targetRole}`);
60+
err.status = 401;
61+
return next(err);
62+
}
63+
64+
if (PROJECT_MEMBER_ROLE.PROGRAM_MANAGER === targetRole &&
65+
!util.hasRoles(req, [USER_ROLE.PROGRAM_MANAGER])) {
66+
const err = new Error(`Only program manager is able to join as ${targetRole}`);
67+
err.status = 401;
68+
return next(err);
69+
}
70+
71+
if (PROJECT_MEMBER_ROLE.ACCOUNT_EXECUTIVE === targetRole &&
72+
!util.hasRoles(req, [USER_ROLE.ACCOUNT_EXECUTIVE])) {
73+
const err = new Error(`Only account executive is able to join as ${targetRole}`);
74+
err.status = 401;
75+
return next(err);
76+
}
77+
4278
if (PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER === targetRole &&
43-
!util.hasRoles(req, [USER_ROLE.MANAGER, USER_ROLE.TOPCODER_ACCOUNT_MANAGER])) {
44-
const err = new Error(`Only manager or account manager is able to join as ${targetRole}`);
79+
!util.hasRoles(req, [
80+
USER_ROLE.MANAGER,
81+
USER_ROLE.TOPCODER_ACCOUNT_MANAGER,
82+
USER_ROLE.BUSINESS_DEVELOPMENT_REPRESENTATIVE,
83+
USER_ROLE.PRESALES,
84+
USER_ROLE.ACCOUNT_EXECUTIVE,
85+
USER_ROLE.PROGRAM_MANAGER,
86+
USER_ROLE.SOLUTION_ARCHITECT,
87+
USER_ROLE.PROJECT_MANAGER,
88+
])) {
89+
const err = new Error(
90+
// eslint-disable-next-line max-len
91+
`Only manager, account manager, business development representative, account executive, program manager, project manager, solution architect, or presales are able to join as ${targetRole}`,
92+
);
4593
err.status = 401;
4694
return next(err);
4795
}
@@ -53,10 +101,22 @@ module.exports = [
53101
}
54102
} else if (util.hasRoles(req, [USER_ROLE.MANAGER, USER_ROLE.CONNECT_ADMIN])) {
55103
targetRole = PROJECT_MEMBER_ROLE.MANAGER;
56-
} else if (util.hasRoles(req, [USER_ROLE.TOPCODER_ACCOUNT_MANAGER])) {
104+
} else if (util.hasRoles(req, [
105+
USER_ROLE.TOPCODER_ACCOUNT_MANAGER,
106+
USER_ROLE.BUSINESS_DEVELOPMENT_REPRESENTATIVE,
107+
USER_ROLE.PRESALES,
108+
])) {
57109
targetRole = PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER;
58110
} else if (util.hasRoles(req, [USER_ROLE.COPILOT, USER_ROLE.CONNECT_ADMIN])) {
59111
targetRole = PROJECT_MEMBER_ROLE.COPILOT;
112+
} else if (util.hasRoles(req, [USER_ROLE.ACCOUNT_EXECUTIVE])) {
113+
targetRole = PROJECT_MEMBER_ROLE.ACCOUNT_EXECUTIVE;
114+
} else if (util.hasRoles(req, [USER_ROLE.PROGRAM_MANAGER])) {
115+
targetRole = PROJECT_MEMBER_ROLE.PROGRAM_MANAGER;
116+
} else if (util.hasRoles(req, [USER_ROLE.SOLUTION_ARCHITECT])) {
117+
targetRole = PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT;
118+
} else if (util.hasRoles(req, [USER_ROLE.PROJECT_MANAGER])) {
119+
targetRole = PROJECT_MEMBER_ROLE.PROJECT_MANAGER;
60120
} else {
61121
const err = new Error('Only copilot or manager is able to call this endpoint');
62122
err.status = 401;

src/routes/projectMembers/update.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,17 @@ const updateProjectMemberValdiations = {
1616
body: {
1717
param: Joi.object().keys({
1818
isPrimary: Joi.boolean(),
19-
role: Joi.any().valid(PROJECT_MEMBER_ROLE.CUSTOMER, PROJECT_MEMBER_ROLE.MANAGER,
20-
PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER, PROJECT_MEMBER_ROLE.COPILOT, PROJECT_MEMBER_ROLE.OBSERVER).required(),
19+
role: Joi.any().valid(
20+
PROJECT_MEMBER_ROLE.CUSTOMER,
21+
PROJECT_MEMBER_ROLE.MANAGER,
22+
PROJECT_MEMBER_ROLE.ACCOUNT_MANAGER,
23+
PROJECT_MEMBER_ROLE.COPILOT,
24+
PROJECT_MEMBER_ROLE.OBSERVER,
25+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
26+
PROJECT_MEMBER_ROLE.ACCOUNT_EXECUTIVE,
27+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
28+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
29+
).required(),
2130
}),
2231
},
2332
};

src/routes/projects/update.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ module.exports = [
225225
const members = req.context.currentProjectMembers;
226226
const validRoles = [
227227
PROJECT_MEMBER_ROLE.MANAGER,
228-
PROJECT_MEMBER_ROLE.MANAGER,
228+
PROJECT_MEMBER_ROLE.PROGRAM_MANAGER,
229+
PROJECT_MEMBER_ROLE.PROJECT_MANAGER,
230+
PROJECT_MEMBER_ROLE.SOLUTION_ARCHITECT,
229231
].map(x => x.toLowerCase());
230232
const matchRole = role => _.indexOf(validRoles, role.toLowerCase()) >= 0;
231233
if (updatedProps.status === PROJECT_STATUS.ACTIVE &&

src/util.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ import config from 'config';
1616
import urlencode from 'urlencode';
1717
import elasticsearch from 'elasticsearch';
1818
import Promise from 'bluebird';
19+
import models from './models';
1920
// import AWS from 'aws-sdk';
2021

2122
import { ADMIN_ROLES, TOKEN_SCOPES, EVENT, PROJECT_MEMBER_ROLE, VALUE_TYPE, ESTIMATION_TYPE } from './constants';
2223

2324
const exec = require('child_process').exec;
24-
const models = require('./models').default;
2525
const tcCoreLibAuth = require('tc-core-library-js').auth;
2626

2727
const m2m = tcCoreLibAuth.m2m(config);
@@ -474,7 +474,6 @@ _.assignIn(util, {
474474
req.log.debug('creating member', member);
475475
let newMember = null;
476476
// register member
477-
478477
return models.ProjectMember.create(member)
479478
.then((_newMember) => {
480479
newMember = _newMember.get({ plain: true });

0 commit comments

Comments
 (0)