Skip to content

Commit b99f699

Browse files
author
vikasrohit
authored
Merge branch 'dev' into hotfix/handling_unhandled_error_in_getting_mentioned_user_details_updated
2 parents b82b726 + 4cba87a commit b99f699

17 files changed

+345
-177
lines changed

config/default.js

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,56 +45,57 @@ module.exports = {
4545
AUTH0_PROXY_SERVER_URL: process.env.AUTH0_PROXY_SERVER_URL,
4646

4747
KAFKA_CONSUMER_RULESETS: {
48-
// key is Kafka topic name, value is array of ruleset which have key as handler function name defined in src/processors/index.js
48+
// key is Kafka topic name, value is array of ruleset which have key as
49+
// handler function name defined in src/processors/index.js
4950
'challenge.notification.events': [
5051
{
5152
handleChallenge: /** topic handler name */
5253
{
5354
type: 'UPDATE_DRAFT_CHALLENGE',
54-
roles: ["Submitter" /** Competitor */, "Copilot", "Reviewer"],
55+
roles: ['Submitter' /** Competitor */, 'Copilot', 'Reviewer'],
5556
notification:
5657
{
5758
id: 0, /** challengeid or projectid */
5859
name: '', /** challenge name */
5960
group: 'Challenge',
60-
title: 'Challenge specification is modified.'
61-
}
62-
}
63-
}
61+
title: 'Challenge specification is modified.',
62+
},
63+
},
64+
},
6465
],
6566
'notifications.autopilot.events': [
6667
{
6768
handleAutoPilot:
6869
{
6970
phaseTypeName: 'Checkpoint Screening',
7071
state: 'START',
71-
roles: ["Copilot", "Reviewer"],
72+
roles: ['Copilot', 'Reviewer'],
7273
notification:
7374
{
7475
id: 0, /** challengeid or projectid */
7576
name: '', /** challenge name */
7677
group: 'Challenge',
77-
title: 'Challenge checkpoint review.'
78-
}
79-
}
80-
}
78+
title: 'Challenge checkpoint review.',
79+
},
80+
},
81+
},
8182
],
8283
'submission.notification.create': [
8384
{
8485
handleSubmission:
8586
{
8687
resource: 'submission',
87-
roles: ["Copilot", "Reviewer"],
88+
roles: ['Copilot', 'Reviewer'],
8889
selfOnly: true /** Submitter only */,
8990
notification:
9091
{
9192
id: 0, /** challengeid or projectid */
9293
name: '', /** challenge name */
9394
group: 'Submission',
94-
title: 'A new submission is uploaded.'
95-
}
96-
}
97-
}
95+
title: 'A new submission is uploaded.',
96+
},
97+
},
98+
},
9899
],
99100
//'notifications.community.challenge.created': ['handleChallengeCreated'],
100101
//'notifications.community.challenge.phasewarning': ['handleChallengePhaseWarning'],

connect/connectNotificationServer.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const config = require('./config');
99
const notificationServer = require('../index');
1010
const _ = require('lodash');
1111
const service = require('./service');
12+
const helpers = require('./helpers');
1213
const { BUS_API_EVENT } = require('./constants');
1314
const EVENTS = require('./events-config').EVENTS;
1415
const PROJECT_ROLE_RULES = require('./events-config').PROJECT_ROLE_RULES;
@@ -254,6 +255,83 @@ const getNotificationsForTopicStarter = (eventConfig, topicId) => {
254255
});
255256
};
256257

258+
/**
259+
* Filter members by project roles
260+
*
261+
* @params {Array} List of project roles
262+
* @params {Array} List of project members
263+
*
264+
* @returns {Array} List of objects with user ids
265+
*/
266+
const filterMembersByRoles = (roles, members) => {
267+
let result = [];
268+
269+
roles.forEach(projectRole => {
270+
result = result.concat(
271+
_.filter(members, PROJECT_ROLE_RULES[projectRole])
272+
.map(projectMember => ({
273+
userId: projectMember.userId.toString(),
274+
}))
275+
);
276+
});
277+
278+
return result;
279+
};
280+
281+
/**
282+
* Exclude private posts notification
283+
*
284+
* @param {Object} eventConfig event configuration
285+
* @param {Object} project project details
286+
* @param {Array} tags list of message tags
287+
*
288+
* @return {Promise} resolves to a list of notifications
289+
*/
290+
const getExcludedPrivatePostNotifications = (eventConfig, project, tags) => {
291+
// skip if message is not private or exclusion rule is not configured
292+
if (!_.includes(tags, 'MESSAGES') || !eventConfig.privatePostsForProjectRoles) {
293+
return Promise.resolve([]);
294+
}
295+
296+
const members = _.get(project, 'members', []);
297+
const notifications = filterMembersByRoles(eventConfig.privatePostsForProjectRoles, members);
298+
299+
return Promise.resolve(notifications);
300+
};
301+
302+
/**
303+
* Exclude notifications about posts inside draft phases
304+
*
305+
* @param {Object} eventConfig event configuration
306+
* @param {Object} project project details
307+
* @param {Array} tags list of message tags
308+
*
309+
* @return {Promise} resolves to a list of notifications
310+
*/
311+
const getExcludeDraftPhasesNotifications = (eventConfig, project, tags) => {
312+
// skip is no exclusion rule is configured
313+
if (!eventConfig.draftPhasesForProjectRoles) {
314+
return Promise.resolve([]);
315+
}
316+
317+
const phaseId = helpers.extractPhaseId(tags);
318+
// skip if it is not phase notification
319+
if (!phaseId) {
320+
return Promise.resolve([]);
321+
}
322+
323+
// exclude all user with configured roles if phase is in draft state
324+
return service.getPhase(project.id, phaseId)
325+
.then((phase) => {
326+
if (phase.status === 'draft') {
327+
const members = _.get(project, 'members', []);
328+
const notifications = filterMembersByRoles(eventConfig.draftPhasesForProjectRoles, members);
329+
330+
return Promise.resolve(notifications);
331+
}
332+
});
333+
};
334+
257335
/**
258336
* Exclude notifications using exclude rules of the event config
259337
*
@@ -281,12 +359,17 @@ const excludeNotifications = (logger, notifications, eventConfig, message, data)
281359
// and after filter out such notifications from the notifications list
282360
// TODO move this promise all together with `_.uniqBy` to one function
283361
// and reuse it here and in `handler` function
362+
const tags = _.get(message, 'tags', []);
363+
284364
return Promise.all([
285365
getNotificationsForTopicStarter(excludeEventConfig, message.topicId),
286366
getNotificationsForUserId(excludeEventConfig, message.userId),
287367
getNotificationsForMentionedUser(logger, excludeEventConfig, message.postContent),
288368
getProjectMembersNotifications(excludeEventConfig, project),
289369
getTopCoderMembersNotifications(excludeEventConfig),
370+
// these are special exclude rules which are only working for excluding notifications but not including
371+
getExcludedPrivatePostNotifications(excludeEventConfig, project, tags),
372+
getExcludeDraftPhasesNotifications(excludeEventConfig, project, tags),
290373
]).then((notificationsPerSource) => (
291374
_.uniqBy(_.flatten(notificationsPerSource), 'userId')
292375
)).then((excludedNotifications) => {

connect/events-config.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { BUS_API_EVENT } = require('./constants');
55

66
// project member role names
77
const PROJECT_ROLE_OWNER = 'owner';
8+
const PROJECT_ROLE_CUSTOMER = 'customer';
89
const PROJECT_ROLE_COPILOT = 'copilot';
910
const PROJECT_ROLE_MANAGER = 'manager';
1011
const PROJECT_ROLE_MEMBER = 'member';
@@ -13,13 +14,15 @@ const PROJECT_ROLE_ACCOUNT_MANAGER = 'account_manager';
1314
// project member role rules
1415
const PROJECT_ROLE_RULES = {
1516
[PROJECT_ROLE_OWNER]: { role: 'customer', isPrimary: true },
17+
[PROJECT_ROLE_CUSTOMER]: { role: 'customer' },
1618
[PROJECT_ROLE_COPILOT]: { role: 'copilot' },
1719
[PROJECT_ROLE_MANAGER]: { role: 'manager' },
1820
[PROJECT_ROLE_ACCOUNT_MANAGER]: { role: 'account_manager' },
1921
[PROJECT_ROLE_MEMBER]: {},
2022
};
2123

2224
// TopCoder roles
25+
// eslint-disable-next-line no-unused-vars
2326
const ROLE_CONNECT_COPILOT = 'Connect Copilot';
2427
const ROLE_CONNECT_MANAGER = 'Connect Manager';
2528
const ROLE_CONNECT_COPILOT_MANAGER = 'Connect Copilot Manager';
@@ -123,31 +126,53 @@ const EVENTS = [
123126
version: 2,
124127
projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER],
125128
toMentionedUsers: true,
129+
exclude: {
130+
privatePostsForProjectRoles: [PROJECT_ROLE_CUSTOMER],
131+
},
126132
}, {
127133
type: BUS_API_EVENT.CONNECT.POST.CREATED,
128134
version: 2,
129135
projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER],
130136
toTopicStarter: true,
131137
toMentionedUsers: true,
138+
exclude: {
139+
draftPhasesForProjectRoles: [PROJECT_ROLE_CUSTOMER],
140+
privatePostsForProjectRoles: [PROJECT_ROLE_CUSTOMER],
141+
},
132142
}, {
133143
type: BUS_API_EVENT.CONNECT.POST.UPDATED,
134144
version: 2,
135145
projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER],
136146
toTopicStarter: true,
137147
toMentionedUsers: true,
148+
exclude: {
149+
draftPhasesForProjectRoles: [PROJECT_ROLE_CUSTOMER],
150+
privatePostsForProjectRoles: [PROJECT_ROLE_CUSTOMER],
151+
},
138152
}, {
139153
type: BUS_API_EVENT.CONNECT.POST.MENTION,
154+
exclude: {
155+
draftPhasesForProjectRoles: [PROJECT_ROLE_CUSTOMER],
156+
privatePostsForProjectRoles: [PROJECT_ROLE_CUSTOMER],
157+
},
140158
},
141159
{
142160
type: BUS_API_EVENT.CONNECT.TOPIC.DELETED,
143161
version: 2,
144162
projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER],
145163
toTopicStarter: false,
164+
exclude: {
165+
privatePostsForProjectRoles: [PROJECT_ROLE_CUSTOMER],
166+
},
146167
},
147168
{
148169
type: BUS_API_EVENT.CONNECT.POST.DELETED,
149170
version: 2,
150171
projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER],
172+
exclude: {
173+
draftPhasesForProjectRoles: [PROJECT_ROLE_CUSTOMER],
174+
privatePostsForProjectRoles: [PROJECT_ROLE_CUSTOMER],
175+
},
151176
},
152177
{
153178
type: BUS_API_EVENT.CONNECT.PROJECT.LINK_CREATED,

connect/helpers.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
* Helper functions
33
*/
44
const Remarkable = require('remarkable');
5+
const _ = require('lodash');
6+
7+
const PHASE_ID_REGEXP = /phase#(\d+)/;
58

69
/**
710
* Convert markdown into raw draftjs state
@@ -42,7 +45,20 @@ const sanitizeEmail = (email) => {
4245
return '';
4346
};
4447

48+
/**
49+
* Helper method to extract phaseId from tag
50+
*
51+
* @param {Array} tags list of message tags
52+
*
53+
* @returns {String} phase id
54+
*/
55+
const extractPhaseId = (tags) => {
56+
const phaseIds = tags.map((tag) => _.get(tag.match(PHASE_ID_REGEXP), '1', null));
57+
return _.find(phaseIds, (phaseId) => phaseId !== null);
58+
};
59+
4560
module.exports = {
4661
markdownToHTML,
4762
sanitizeEmail,
63+
extractPhaseId,
4864
};

0 commit comments

Comments
 (0)