Skip to content

Commit 44a54b5

Browse files
committed
part of the winning submission from challenge 30090377 - Topcoder Notifications Service - Skip unnecessary notifications
this part contains fixes for lint errors in the existent code
1 parent cd5b535 commit 44a54b5

File tree

12 files changed

+180
-175
lines changed

12 files changed

+180
-175
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/notificationServices/email.js

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
const _ = require('lodash');
55
const jwt = require('jsonwebtoken');
66
const co = require('co');
7-
const fs = require('fs');
8-
const path = require('path');
97
const { logger, busService, eventScheduler, notificationService } = require('../../index');
108
const { createEventScheduler, SCHEDULED_EVENT_STATUS } = eventScheduler;
119

@@ -16,29 +14,43 @@ const {
1614
SETTINGS_EMAIL_SERVICE_ID,
1715
ACTIVE_USER_STATUSES,
1816
} = require('../constants');
19-
const { EVENTS, EVENT_BUNDLES } = require('../events-config');
17+
const { EVENT_BUNDLES } = require('../events-config');
2018
const helpers = require('../helpers');
2119
const service = require('../service');
2220

2321

2422
function replacePlaceholders(term, data) {
25-
let placeholders = term.match(/<[a-zA-Z]+>/g);
23+
const placeholders = term.match(/<[a-zA-Z]+>/g);
2624
let ret = term;
2725
if (placeholders && placeholders.length) {
2826
_(placeholders).each(p => {
29-
let values = _.map(data, p.slice(1, -1));
30-
const total = values.length;
31-
let replacement = values.length < 3 ?
32-
values.join(', ') :
33-
values.slice(0, 2).join(', ') + ' and ' + (total - 3) + 'others';
27+
const values = _.map(data, p.slice(1, -1));
28+
// TODO remove this code if possible.
29+
// This code appears to be not in use causing lint errors.
30+
// For now I'm commenting it, in case it contains some valuable logic.
31+
// But after confirmation that it's redundant it has to be removed.
32+
//
33+
// const total = values.length;
34+
// const replacement = values.length < 3 ?
35+
// values.join(', ') :
36+
// values.slice(0, 2).join(', ') + ' and ' + (total - 3) + 'others';
3437
ret = ret.replace(p, values.join(', '));
3538
});
3639
}
3740
return ret;
3841
}
3942

43+
function getEventGroupKey(value) {
44+
const key = _.chain(EVENT_BUNDLES)
45+
.keys()
46+
.find(k => _.includes(_.get(EVENT_BUNDLES, `${k}.types`), _.get(value, 'data.data.type')))
47+
.value();
48+
if (!key) return 'DEFAULT';
49+
return key;
50+
}
51+
4052
function getSections(projectUserEvents) {
41-
let sections = [];
53+
const sections = [];
4254
_.chain(projectUserEvents)
4355
.groupBy(value => getEventGroupKey(value))
4456
.forIn((value, key) => {
@@ -49,7 +61,7 @@ function getSections(projectUserEvents) {
4961
notifications: _(value).map(v => v.data.data).value(),
5062
});
5163
} else {
52-
_.chain(value).groupBy(n => n.data.data[EVENT_BUNDLES[key].groupBy]).forIn((groupValue, groupKey) => {
64+
_.chain(value).groupBy(n => n.data.data[EVENT_BUNDLES[key].groupBy]).forIn((groupValue) => {
5365
let title = EVENT_BUNDLES[key].title;
5466
title = replacePlaceholders(title, _(groupValue).map(g => g.data.data).value());
5567
sections.push({
@@ -140,15 +152,6 @@ const scheduler = createEventScheduler(
140152
handleScheduledEvents
141153
);
142154

143-
function getEventGroupKey(value) {
144-
let key = _.chain(EVENT_BUNDLES)
145-
.keys()
146-
.find(key => _.includes(_.get(EVENT_BUNDLES, `${key}.types`), _.get(value, 'data.data.type')))
147-
.value();
148-
if (!key) return 'DEFAULT';
149-
return key;
150-
}
151-
152155
/**
153156
* Prepares data to be provided to the template to render a single notification.
154157
*
@@ -245,30 +248,31 @@ function handler(topicName, messageJSON, notification) {
245248
};
246249
eventMessage.data[eventMessage.data.type] = true;
247250
_.assign(eventMessage.data, notification.contents);
248-
251+
249252
// message service may return tags
250253
// to understand if post notification is regarding phases or no, we will try to get phaseId from the tags
251-
const tags = _.get(notification.contents, 'tags', [])
252-
const PHASE_ID_REGEXP = /phase#(\d+)/
253-
const phaseIds = tags.map((tag) => _.get(tag.match(PHASE_ID_REGEXP), '1', null))
254-
const phaseId = _.find(phaseIds, (phaseId) => phaseId !== null)
254+
const tags = _.get(notification.contents, 'tags', []);
255+
const phaseId = helpers.extractPhaseId(tags);
255256
if (phaseId) {
256257
eventMessage.data.phaseId = phaseId;
257258
}
258259

259-
// if the notification is regarding topic: dashboard topic, dashboard post or phase post
260+
// if the notification is regarding topic: dashboard topic, dashboard post or phase post
260261
// we build a link to the post
261262
if (eventMessage.data.topicId) {
262263
// phase post
263264
if (eventMessage.data.phaseId) {
265+
// eslint-disable-next-line max-len
264266
eventMessage.data.postURL = `${config.CONNECT_URL}/projects/${eventMessage.data.projectId}/plan#phase-${eventMessage.data.phaseId}-posts-${eventMessage.data.postId}`;
265267

266268
// dashboard post
267269
} else if (eventMessage.data.postId) {
270+
// eslint-disable-next-line max-len
268271
eventMessage.data.postURL = `${config.CONNECT_URL}/projects/${eventMessage.data.projectId}#comment-${eventMessage.data.postId}`;
269272

270273
// dashboard topic
271274
} else {
275+
// eslint-disable-next-line max-len
272276
eventMessage.data.postURL = `${config.CONNECT_URL}/projects/${eventMessage.data.projectId}#feed-${eventMessage.data.topicId}`;
273277
}
274278
}
@@ -319,19 +323,21 @@ function handler(topicName, messageJSON, notification) {
319323
}
320324

321325
// if notifications has to be bundled
322-
let bundlePeriod = _.get(settings, `notifications['${notificationType}'].${SETTINGS_EMAIL_SERVICE_ID}.bundlePeriod`);
326+
let bundlePeriod = _.get(settings,
327+
`notifications['${notificationType}'].${SETTINGS_EMAIL_SERVICE_ID}.bundlePeriod`);
323328
bundlePeriod = bundlePeriod && bundlePeriod.trim().length > 0 ? bundlePeriod : null;
324329
// if bundling is not explicitly set and the event is not a messaging event, assume bundling enabled
325330
if (!bundlePeriod && !messagingEvent) {
326331
// finds the event category for the notification type
327-
let eventBundleCategory = _.findKey(EVENT_BUNDLES, b => b.types && b.types.indexOf(notificationType) !== -1);
332+
const eventBundleCategory = _.findKey(EVENT_BUNDLES, b => b.types && b.types.indexOf(notificationType) !== -1);
328333
if (eventBundleCategory) {
329334
const eventBundle = EVENT_BUNDLES[eventBundleCategory];
330335
// if we find the event category for the notification, use the bundle settings from the first event
331336
if (eventBundle && eventBundle.types && eventBundle.types.length) {
332337
const firstEvtInBundle = eventBundle.types[0];
333-
const firstEvtBundleSettingPath = `notifications['${firstEvtInBundle}'].${SETTINGS_EMAIL_SERVICE_ID}.bundlePeriod`;
334-
let firstEvtBundlePeriod = _.get(settings, firstEvtBundleSettingPath);
338+
const firstEvtBundleSettingPath =
339+
`notifications['${firstEvtInBundle}'].${SETTINGS_EMAIL_SERVICE_ID}.bundlePeriod`;
340+
const firstEvtBundlePeriod = _.get(settings, firstEvtBundleSettingPath);
335341
bundlePeriod = firstEvtBundlePeriod;
336342
logger.debug('Assuming bundle period of first event in the event category=>', bundlePeriod);
337343
}
@@ -341,7 +347,7 @@ function handler(topicName, messageJSON, notification) {
341347
}
342348
logger.debug('bundlePeriod=>', bundlePeriod);
343349

344-
if (bundlePeriod && 'immediately' !== bundlePeriod && !requiresImmediateAttention) {
350+
if (bundlePeriod && bundlePeriod !== 'immediately' && !requiresImmediateAttention) {
345351
if (!SCHEDULED_EVENT_PERIOD[bundlePeriod]) {
346352
throw new Error(`User's '${notification.userId}' setting for service`
347353
+ ` '${SETTINGS_EMAIL_SERVICE_ID}' option 'bundlePeriod' has unsupported value '${bundlePeriod}'.`);
@@ -359,7 +365,7 @@ function handler(topicName, messageJSON, notification) {
359365
} else {
360366
// send single field "notificationsHTML" with the rendered template
361367
eventMessage.data = wrapIndividualNotification({ data: eventMessage });
362-
//console.log(eventMessage.data.contents);
368+
// console.log(eventMessage.data.contents);
363369

364370
// send event to bus api
365371
return busService.postEvent({
@@ -374,7 +380,7 @@ function handler(topicName, messageJSON, notification) {
374380
.catch((err) => {
375381
logger.error(`Failed to send ${eventType} event`
376382
+ `; error: ${err.message}`
377-
+ `; with body ${JSON.stringify(eventMessage)} to bus api`);
383+
+ `; with body ${JSON.stringify(eventMessage)} to bus api`);
378384
});
379385
}
380386
});

consumer.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const _ = require('lodash');
88
const Kafka = require('no-kafka');
99
const co = require('co');
1010
global.Promise = require('bluebird');
11-
const healthcheck = require('topcoder-healthcheck-dropin')
11+
const healthcheck = require('topcoder-healthcheck-dropin');
1212

1313
const logger = require('./src/common/logger');
1414
const models = require('./src/models');
@@ -63,13 +63,13 @@ function startKafkaConsumer() {
6363
return co(function* () {
6464
// run each handler
6565
for (let i = 0; i < ruleSets.length; i += 1) {
66-
const rule = ruleSets[i]
67-
const handlerFuncArr = _.keys(rule)
68-
const handlerFuncName = _.get(handlerFuncArr, "0")
66+
const rule = ruleSets[i];
67+
const handlerFuncArr = _.keys(rule);
68+
const handlerFuncName = _.get(handlerFuncArr, '0');
6969

7070
try {
71-
const handler = processors[handlerFuncName]
72-
const handlerRuleSets = rule[handlerFuncName]
71+
const handler = processors[handlerFuncName];
72+
const handlerRuleSets = rule[handlerFuncName];
7373
if (!handler) {
7474
logger.error(`Handler ${handlerFuncName} is not defined`);
7575
continue;
@@ -79,17 +79,17 @@ function startKafkaConsumer() {
7979
const notifications = yield handler(messageJSON, handlerRuleSets);
8080
if (notifications && notifications.length > 0) {
8181
// save notifications in bulk to improve performance
82-
logger.info(`Going to insert ${notifications.length} notifications in database.`)
82+
logger.info(`Going to insert ${notifications.length} notifications in database.`);
8383
yield models.Notification.bulkCreate(_.map(notifications, (n) => ({
8484
userId: n.userId,
8585
type: n.type || topic,
8686
contents: n.contents || n.notification || messageJSON.payload || {},
8787
read: false,
8888
seen: false,
8989
version: n.version || null,
90-
})))
90+
})));
9191
// logging
92-
logger.info(`Saved ${notifications.length} notifications`)
92+
logger.info(`Saved ${notifications.length} notifications`);
9393
/* logger.info(` for users: ${
9494
_.map(notifications, (n) => n.userId).join(', ')
9595
}`); */
@@ -112,15 +112,15 @@ function startKafkaConsumer() {
112112

113113
const check = function () {
114114
if (!consumer.client.initialBrokers && !consumer.client.initialBrokers.length) {
115-
return false
115+
return false;
116116
}
117-
let connected = true
117+
let connected = true;
118118
consumer.client.initialBrokers.forEach(conn => {
119-
logger.debug(`url ${conn.server()} - connected=${conn.connected}`)
120-
connected = conn.connected & connected
121-
})
122-
return connected
123-
}
119+
logger.debug(`url ${conn.server()} - connected=${conn.connected}`);
120+
connected = conn.connected & connected;
121+
});
122+
return connected;
123+
};
124124

125125
// Start kafka consumer
126126
logger.info('Starting kafka consumer');
@@ -132,7 +132,7 @@ function startKafkaConsumer() {
132132
}])
133133
.then(() => {
134134
logger.info('Kafka consumer initialized successfully');
135-
healthcheck.init([check])
135+
healthcheck.init([check]);
136136
})
137137
.catch((err) => {
138138
logger.error('Kafka consumer failed');

src/app.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const logger = require('./common/logger');
1515
const errors = require('./common/errors');
1616
const models = require('./models');
1717
const Kafka = require('no-kafka');
18-
const healthcheck = require('topcoder-healthcheck-dropin')
18+
const healthcheck = require('topcoder-healthcheck-dropin');
1919

2020
/**
2121
* Start Kafka consumer for event bus events.
@@ -77,22 +77,22 @@ function startKafkaConsumer(handlers, notificationServiceHandlers) {
7777

7878
const check = function () {
7979
if (!consumer.client.initialBrokers && !consumer.client.initialBrokers.length) {
80-
return false
80+
return false;
8181
}
82-
let connected = true
82+
let connected = true;
8383
consumer.client.initialBrokers.forEach(conn => {
84-
logger.debug(`url ${conn.server()} - connected=${conn.connected}`)
85-
connected = conn.connected & connected
86-
})
87-
return connected
88-
}
84+
logger.debug(`url ${conn.server()} - connected=${conn.connected}`);
85+
connected = conn.connected & connected;
86+
});
87+
return connected;
88+
};
8989

9090
consumer
9191
.init()
9292
.then(() => {
9393
_.each(_.keys(handlers),
94-
(topicName) => consumer.subscribe(topicName, dataHandler))
95-
healthcheck.init([check])
94+
(topicName) => consumer.subscribe(topicName, dataHandler));
95+
healthcheck.init([check]);
9696
})
9797
.catch((err) => {
9898
logger.error('Kafka Consumer failed');

0 commit comments

Comments
 (0)