Skip to content

Commit 6122446

Browse files
authored
Merge pull request #840 from topcoder-platform/pm-1510
fix(PM-1510): added existing membership to the copilot application
2 parents 4a2a671 + c2df9ee commit 6122446

File tree

3 files changed

+170
-24
lines changed

3 files changed

+170
-24
lines changed

src/routes/copilotOpportunity/assign.js

Lines changed: 106 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import _ from 'lodash';
22
import validate from 'express-validation';
33
import Joi from 'joi';
4+
import config from 'config';
45

56
import models from '../../models';
67
import util from '../../util';
78
import { PERMISSION } from '../../permissions/constants';
8-
import { COPILOT_APPLICATION_STATUS, COPILOT_OPPORTUNITY_STATUS, COPILOT_REQUEST_STATUS, EVENT, INVITE_STATUS, PROJECT_MEMBER_ROLE, RESOURCES } from '../../constants';
9+
import { CONNECT_NOTIFICATION_EVENT, COPILOT_APPLICATION_STATUS, COPILOT_OPPORTUNITY_STATUS, COPILOT_REQUEST_STATUS, EVENT, INVITE_STATUS, PROJECT_MEMBER_ROLE, RESOURCES, TEMPLATE_IDS } from '../../constants';
10+
import { getCopilotTypeLabel } from '../../utils/copilot';
11+
import { createEvent } from '../../services/busApi';
12+
import moment from 'moment';
913

1014
const assignCopilotOpportunityValidations = {
1115
body: Joi.object().keys({
@@ -45,11 +49,17 @@ module.exports = [
4549
throw err;
4650
}
4751

52+
const copilotRequest = await models.CopilotRequest.findOne({
53+
where: { id: opportunity.copilotRequestId },
54+
transaction: t,
55+
});
56+
4857
const application = await models.CopilotApplication.findOne({
4958
where: { id: applicationId, opportunityId: copilotOpportunityId },
5059
transaction: t,
5160
});
5261

62+
5363
if (!application) {
5464
const err = new Error('No such application available');
5565
err.status = 400;
@@ -65,12 +75,101 @@ module.exports = [
6575
const projectId = opportunity.projectId;
6676
const userId = application.userId;
6777
const activeMembers = await models.ProjectMember.getActiveProjectMembers(projectId, t);
68-
69-
const existingUser = activeMembers.find(item => item.userId === userId);
70-
if (existingUser && existingUser.role === 'copilot') {
71-
const err = new Error(`User is already a copilot of this project`);
72-
err.status = 400;
73-
throw err;
78+
const updateCopilotOpportunity = async () => {
79+
const transaction = await models.sequelize.transaction();
80+
const memberDetails = await util.getMemberDetailsByUserIds([application.userId], req.log, req.id);
81+
const member = memberDetails[0];
82+
req.log.debug(`Updating opportunity: ${JSON.stringify(opportunity)}`);
83+
await opportunity.update({
84+
status: COPILOT_OPPORTUNITY_STATUS.COMPLETED,
85+
}, {
86+
transaction,
87+
});
88+
req.log.debug(`Updating application: ${JSON.stringify(application)}`);
89+
await application.update({
90+
status: COPILOT_APPLICATION_STATUS.ACCEPTED,
91+
}, {
92+
transaction,
93+
});
94+
95+
req.log.debug(`Updating request: ${JSON.stringify(copilotRequest)}`);
96+
await copilotRequest.update({
97+
status: COPILOT_REQUEST_STATUS.FULFILLED,
98+
}, {
99+
transaction,
100+
});
101+
102+
req.log.debug(`Updating other applications: ${JSON.stringify(copilotRequest)}`);
103+
await models.CopilotApplication.update({
104+
status: COPILOT_APPLICATION_STATUS.CANCELED,
105+
}, {
106+
where: {
107+
opportunityId: opportunity.id,
108+
id: {
109+
$ne: application.id,
110+
},
111+
}
112+
});
113+
114+
req.log.debug(`All updations done`);
115+
transaction.commit();
116+
117+
req.log.debug(`Sending email notification`);
118+
const emailEventType = CONNECT_NOTIFICATION_EVENT.EXTERNAL_ACTION_EMAIL;
119+
const copilotPortalUrl = config.get('copilotPortalUrl');
120+
const requestData = copilotRequest.data;
121+
createEvent(emailEventType, {
122+
data: {
123+
opportunity_details_url: `${copilotPortalUrl}/opportunity/${opportunity.id}`,
124+
work_manager_url: config.get('workManagerUrl'),
125+
opportunity_type: getCopilotTypeLabel(requestData.projectType),
126+
opportunity_title: requestData.opportunityTitle,
127+
start_date: moment.utc(requestData.startDate).format('DD-MM-YYYY'),
128+
user_name: member ? member.handle : "",
129+
},
130+
sendgrid_template_id: TEMPLATE_IDS.COPILOT_ALREADY_PART_OF_PROJECT,
131+
recipients: [member.email],
132+
version: 'v3',
133+
}, req.log);
134+
135+
req.log.debug(`Email sent`);
136+
};
137+
138+
const existingMember = activeMembers.find(item => item.userId === userId);
139+
if (existingMember) {
140+
req.log.debug(`User already part of project: ${JSON.stringify(existingMember)}`);
141+
if (['copilot', 'manager'].includes(existingMember.role)) {
142+
req.log.debug(`User is a copilot or manager`);
143+
await updateCopilotOpportunity();
144+
} else {
145+
req.log.debug(`User has read/write role`);
146+
await models.ProjectMember.update({
147+
role: 'copilot',
148+
}, {
149+
where: {
150+
id: existingMember.id,
151+
},
152+
});
153+
154+
const projectMember = await models.ProjectMember.findOne({
155+
where: {
156+
id: existingMember.id,
157+
},
158+
});
159+
160+
req.log.debug(`Updated project member: ${JSON.stringify(projectMember.get({plain: true}))}`);
161+
162+
util.sendResourceToKafkaBus(
163+
req,
164+
EVENT.ROUTING_KEY.PROJECT_MEMBER_UPDATED,
165+
RESOURCES.PROJECT_MEMBER,
166+
projectMember.get({ plain: true }),
167+
existingMember);
168+
req.log.debug(`Member updated in kafka`);
169+
await updateCopilotOpportunity();
170+
}
171+
res.status(200).send({ id: applicationId });
172+
return;
74173
}
75174

76175
const existingInvite = await models.ProjectMemberInvite.findAll({

src/routes/copilotOpportunityApply/list.js

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,68 @@ module.exports = [
3131
canAccessAllApplications ? {} : { createdBy: userId },
3232
);
3333

34-
return models.CopilotApplication.findAll({
35-
where: whereCondition,
36-
include: [
37-
{
38-
model: models.CopilotOpportunity,
39-
as: 'copilotOpportunity',
40-
},
41-
],
42-
order: [[sortParams[0], sortParams[1]]],
34+
return models.CopilotOpportunity.findOne({
35+
where: {
36+
id: opportunityId,
37+
}
38+
}).then((opportunity) => {
39+
if (!opportunity) {
40+
const err = new Error('No opportunity found');
41+
err.status = 404;
42+
throw err;
43+
}
44+
return models.CopilotApplication.findAll({
45+
where: whereCondition,
46+
include: [
47+
{
48+
model: models.CopilotOpportunity,
49+
as: 'copilotOpportunity',
50+
},
51+
],
52+
order: [[sortParams[0], sortParams[1]]],
53+
})
54+
.then(copilotApplications => {
55+
req.log.debug(`CopilotApplications ${JSON.stringify(copilotApplications)}`);
56+
return models.ProjectMember.getActiveProjectMembers(opportunity.projectId).then((members) => {
57+
req.log.debug(`Fetched existing active members ${JSON.stringify(members)}`);
58+
req.log.debug(`Applications ${JSON.stringify(copilotApplications)}`);
59+
const enrichedApplications = copilotApplications.map(application => {
60+
const m = members.find(m => m.userId === application.userId);
61+
62+
// Using spread operator fails in lint check
63+
// While Object.assign fails silently during run time
64+
// So using this method
65+
const enriched = {
66+
id: application.id,
67+
opportunityId: application.opportunityId,
68+
notes: application.notes,
69+
status: application.status,
70+
userId: application.userId,
71+
deletedAt: application.deletedAt,
72+
createdAt: application.createdAt,
73+
updatedAt: application.updatedAt,
74+
deletedBy: application.deletedBy,
75+
createdBy: application.createdBy,
76+
updatedBy: application.updatedBy,
77+
copilotOpportunity: application.copilotOpportunity,
78+
};
79+
80+
if (m) {
81+
enriched.existingMembership = m;
82+
}
83+
84+
req.log.debug(`Existing member to application ${JSON.stringify(enriched)}`);
85+
86+
return enriched;
87+
});
88+
89+
req.log.debug(`Enriched Applications ${JSON.stringify(enrichedApplications)}`);
90+
res.status(200).send(enrichedApplications);
91+
});
92+
})
4393
})
44-
.then(copilotApplications => res.json(copilotApplications))
45-
.catch((err) => {
46-
util.handleError('Error fetching copilot applications', err, req, next);
47-
});
94+
.catch((err) => {
95+
util.handleError('Error fetching copilot applications', err, req, next);
96+
});
4897
},
4998
];

src/routes/projectMembers/update.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,6 @@ const completeAllCopilotRequests = async (req, projectId, _transaction, _member)
163163

164164
req.log.debug(`Sent email to ${member.email}`);
165165
});
166-
167-
await _transaction.commit();
168166
};
169167

170168
module.exports = [
@@ -263,8 +261,8 @@ module.exports = [
263261
projectMember = projectMember.get({ plain: true });
264262
projectMember = _.omit(projectMember, ['deletedAt']);
265263

266-
if (['observer', 'customer'].includes(updatedProps.role)) {
267-
await completeAllCopilotRequests(req, projectId, _transaction, _member);
264+
if (['observer', 'customer'].includes(previousValue.role) && ['copilot', 'manager'].includes(updatedProps.role)) {
265+
await completeAllCopilotRequests(req, projectId, _transaction, projectMember);
268266
}
269267
})
270268
.then(() => (

0 commit comments

Comments
 (0)