Skip to content

Commit 1e6be72

Browse files
author
vikasrohit
authored
Merge branch 'dev' into hotfix/handling_unhandled_error_in_getting_mentioned_user_details
2 parents febce84 + 8e5c9d9 commit 1e6be72

17 files changed

+342
-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;
@@ -252,6 +253,83 @@ const getNotificationsForTopicStarter = (eventConfig, topicId) => {
252253
});
253254
};
254255

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