Skip to content

Commit 3c402ce

Browse files
author
Hamid Tavakoli
authored
Merge pull request #52 from topcoder-platform/feature/PLAT-1484
upgrade node and coding style
2 parents ca8c497 + d003c0e commit 3c402ce

23 files changed

+2954
-2579
lines changed

.eslintrc

Lines changed: 0 additions & 3 deletions
This file was deleted.

.eslintrc.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = {
2+
"parserOptions": {
3+
"ecmaVersion": 12,
4+
"env": {
5+
"node": true,
6+
"es2021": true
7+
}
8+
}
9+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
node_modules
22
.env
3+
log.txt

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# and runs it against the specified Topcoder backend (development or
33
# production) when container is executed.
44

5-
FROM node:8.2.1
6-
LABEL app="tc email" version="1.1"
5+
FROM node:16.17
6+
LABEL app="tc email" version="2.0"
77

88
WORKDIR /opt/app
99
COPY . .

config/default.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**
22
* The configuration file.
33
*/
4+
require('dotenv').config()
45
module.exports = {
56
LOG_LEVEL: process.env.LOG_LEVEL,
67
PORT: process.env.PORT,
@@ -34,15 +35,15 @@ module.exports = {
3435
TEMPLATE_MAP: process.env.TEMPLATE_MAP,
3536
SENDGRID_API_KEY: process.env.SENDGRID_API_KEY || '',
3637
EMAIL_FROM: process.env.EMAIL_FROM || 'no-reply@topcoder.com',
37-
38+
3839
// temporary not in use feature
3940
EMAIL_MAX_ERRORS: process.env.EMAIL_MAX_ERRORS || 2,
4041
EMAIL_PAUSE_TIME: process.env.EMAIL_PAUSE_TIME || 30,
4142

4243
//in every 2 minutes will retry for failed status
4344
EMAIL_RETRY_SCHEDULE: process.env.EMAIL_RETRY_SCHEDULE || '0 */2 * * * *',
4445
//wont't retry failed emails older than this time (msec)
45-
EMAIL_RETRY_MAX_AGE: process.env.EMAIL_RETRY_MAX_AGE || 1000*60*60*24,
46+
EMAIL_RETRY_MAX_AGE: process.env.EMAIL_RETRY_MAX_AGE || 1000 * 60 * 60 * 24,
4647

4748
API_CONTEXT_PATH: process.env.API_CONTEXT_PATH || '/v5/email',
4849

connect/connectEmailServer.js

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
/**
22
* This is TopCoder connect email server.
33
*/
4-
'use strict';
5-
6-
global.Promise = require('bluebird');
7-
84
const _ = require('lodash');
95
const config = require('config');
106
const emailServer = require('../index');
@@ -13,27 +9,27 @@ const logger = require('../src/common/logger');
139

1410
// set configuration for the server, see ../config/default.js for available config parameters
1511
// setConfig should be called before initDatabase and start functions
16-
emailServer.setConfig({ LOG_LEVEL: 'debug' });
12+
emailServer.setConfig({ LOG_LEVEL: config.LOG_LEVEL });
1713

1814
// add topic handlers,
1915
// handler is used build a notification list for a message of a topic,
20-
// it is defined as: function(topic, message, callback),
16+
// it is defined as: function(topic, message),
2117
// the topic is topic name,
2218
// the message is JSON event message,
23-
// the callback is function(error, templateId), where templateId is the used SendGrid template id
24-
const handler = (topic, message, callback) => {
19+
const handler = async (topic, message) => {
2520
let templateId = config.TEMPLATE_MAP[topic];
2621
templateId = _.get(message, config.PAYLOAD_SENDGRID_TEMPLATE_KEY, templateId);
2722
if (!templateId) {
28-
return callback(null, { success: false, error: `Template not found for topic ${topic}` });
23+
return { success: false, error: `Template not found for topic ${topic}` };
2924
}
3025

31-
service.sendEmail(templateId, message).then(() => {
32-
callback(null, { success: true });
33-
}).catch((err) => {
34-
logger.error("Error occurred in sendgrid api calling:", err);
35-
callback(null, { success: false, error: err });
36-
});
26+
try {
27+
await service.sendEmail(templateId, message)
28+
return { success: true };
29+
} catch (err) {
30+
logger.error("Error occurred in sendgrid api calling:", err);
31+
return { success: false, error: err };
32+
}
3733

3834
};
3935

@@ -45,8 +41,14 @@ _.keys(config.TEMPLATE_MAP).forEach((eventType) => {
4541
// init database, it will clear and re-create all tables
4642
emailServer
4743
.initDatabase()
48-
.then(() => emailServer.start())
49-
.catch((e) => console.log(e)); // eslint-disable-line no-console
44+
.then(() => {
45+
logger.info('Database initialized successfully.')
46+
emailServer.start()
47+
})
48+
.catch((e) => {
49+
logger.error('Error occurred in starting email server:', e);
50+
process.exit(1);
51+
}); // eslint-disable-line no-console
5052

5153
// if no need to init database, then directly start the server:
52-
// emailServer.start();
54+
// emailServer.start()

connect/service.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
/**
22
* This is TopCoder connect email service.
33
*/
4-
'use strict';
54

65
const sgMail = require('@sendgrid/mail');
76
const config = require('config');
@@ -10,7 +9,7 @@ const logger = require('../src/common/logger');
109
// set api key for SendGrid email client
1110
sgMail.setApiKey(config.SENDGRID_API_KEY);
1211

13-
const sendEmail = (templateId, message) => { // send email
12+
const sendEmail = async (templateId, message) => { // send email
1413

1514
let msg = {}
1615
const from = message.from ? message.from : config.EMAIL_FROM;
@@ -52,7 +51,13 @@ const sendEmail = (templateId, message) => { // send email
5251
};
5352
}
5453
logger.info(`Sending email with templateId: ${templateId} and message: ${JSON.stringify(msg)}`);
55-
return sgMail.send(msg)
54+
try {
55+
const result = await sgMail.send(msg)
56+
return result
57+
} catch (err) {
58+
logger.error(`Error occurred in sendgrid api calling: ${err}`);
59+
throw err
60+
}
5661
}
5762
module.exports = {
5863
sendEmail,

docs/swagger_api.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ info:
44
description: "TOPCODER EMAIL SERIES - EMAIL SERVER"
55
version: "1.0.0"
66
host: "localhost:6100"
7-
basePath : "/v5/email"
7+
basePath: "/v5/email"
88
schemes:
9-
- "https"
9+
- "https"
1010
securityDefinitions:
1111
jwt:
1212
type: apiKey
@@ -17,8 +17,7 @@ securityDefinitions:
1717
paths:
1818
/health:
1919
get:
20-
description:
21-
health check endpoint
20+
description: health check endpoint
2221
produces:
2322
- application/json
2423
responses:

index.js

Lines changed: 110 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
/**
2-
* This is entry point of the TopCoder notification server module.
3-
*/
4-
'use strict';
5-
1+
require('dotenv').config()
62
const config = require('config');
3+
const jwtAuth = require('tc-core-library-js').middleware.jwtAuthenticator;
4+
const express = require('express');
75
const _ = require('lodash');
6+
const cors = require('cors');
7+
const bodyParser = require('body-parser');
88
const schedule = require('node-schedule');
9+
const helper = require('./src/common/helper');
910
const logger = require('./src/common/logger');
1011
const errors = require('./src/common/errors');
12+
const models = require('./src/models');
13+
const { initServer, retryEmail } = require('./src/init');
1114

1215
config.TEMPLATE_MAP = JSON.parse(config.TEMPLATE_MAP);
1316

@@ -34,21 +37,6 @@ function setConfig(cfg) {
3437
_.extend(config, cfg);
3538
}
3639

37-
/**
38-
* Add topic handler for topic, override existing one if any.
39-
* @param {String} topic the topic name
40-
* @param {Function} handler the handler
41-
*/
42-
function addTopicHandler(topic, handler) {
43-
if (!topic) {
44-
throw new errors.ValidationError('Missing topic.');
45-
}
46-
if (!handler) {
47-
throw new errors.ValidationError('Missing handler.');
48-
}
49-
handlers[topic] = handler;
50-
}
51-
5240
/**
5341
* Remove topic handler for topic.
5442
* @param {String} topic the topic name
@@ -69,28 +57,118 @@ function getAllHandlers() {
6957
}
7058

7159
/**
72-
* Start the notification server.
60+
* Add topic handler for topic, override existing one if any.
61+
* @param {String} topic the topic name
62+
* @param {Function} handler the handler
7363
*/
74-
function start() {
75-
if (_.isEmpty(handlers)) {
76-
throw new errors.ValidationError('Missing handler(s).');
64+
function addTopicHandler(topic, handler) {
65+
if (!topic) {
66+
throw new errors.ValidationError('Missing topic.');
67+
}
68+
if (!handler) {
69+
throw new errors.ValidationError('Missing handler.');
7770
}
78-
// load app only after config is set
79-
const app = require('./src/app');
80-
schedule.scheduleJob(config.EMAIL_RETRY_SCHEDULE, function() {
81-
app.retryEmail(handlers).catch((err) => logger.error(err));
71+
handlers[topic] = handler;
72+
}
73+
74+
const app = express();
75+
app.set('port', config.PORT);
76+
77+
app.use(cors());
78+
app.use(bodyParser.json());
79+
app.use(bodyParser.urlencoded({ extended: true }));
80+
81+
const apiRouter = express.Router();
82+
83+
// load all routes
84+
_.each(require('./src/routes'), (verbs, url) => {
85+
_.each(verbs, (def, verb) => {
86+
const actions = [];
87+
const method = require('./src/controllers/' + def.controller)[def.method];
88+
if (!method) {
89+
throw new Error(def.method + ' is undefined');
90+
}
91+
actions.push((req, res, next) => {
92+
req.signature = `${def.controller}#${def.method}`;
93+
next();
94+
});
95+
if (url !== '/health') {
96+
actions.push(jwtAuth());
97+
actions.push((req, res, next) => {
98+
if (!req.authUser) {
99+
return next(new errors.UnauthorizedError('Authorization failed.'));
100+
}
101+
req.user = req.authUser;
102+
return next();
103+
});
104+
}
105+
actions.push(method);
106+
apiRouter[verb](url, helper.autoWrapExpress(actions));
82107
});
83-
return app.start(handlers).catch((err) => logger.error(err));
108+
});
109+
110+
app.use(config.API_CONTEXT_PATH, apiRouter);
111+
112+
app.use((req, res) => {
113+
res.status(404).json({ error: 'route not found' });
114+
});
115+
116+
app.use((err, req, res) => { // eslint-disable-line
117+
logger.logFullError(err, req.signature);
118+
let status = err.httpStatus || 500;
119+
if (err.isJoi) {
120+
status = 400;
121+
}
122+
// from express-jwt
123+
if (err.name === 'UnauthorizedError') {
124+
status = 401;
125+
}
126+
res.status(status);
127+
if (err.isJoi) {
128+
res.json({
129+
error: 'Validation failed',
130+
details: err.details,
131+
});
132+
} else {
133+
res.json({
134+
error: err.message,
135+
});
136+
}
137+
});
138+
139+
140+
function start() {
141+
142+
initServer(handlers).then(() => {
143+
if (_.isEmpty(handlers)) {
144+
throw new errors.ValidationError('Missing handler(s).');
145+
}
146+
147+
schedule.scheduleJob(config.EMAIL_RETRY_SCHEDULE, async function () {
148+
try {
149+
await retryEmail(handlers)
150+
} catch (err) {
151+
console.log(err);
152+
logger.error(err);
153+
}
154+
});
155+
app.listen(app.get('port'), () => {
156+
logger.info(`Express server listening on port ${app.get('port')}`);
157+
});
158+
}).catch((err) => {
159+
logger.error(err);
160+
process.exit(1);
161+
})
84162
}
85163

86164
/**
87165
* Initialize database. All tables are cleared and re-created.
88166
* @returns {Promise} promise to init db
89167
*/
90-
function initDatabase() {
168+
async function initDatabase() {
91169
// load models only after config is set
92-
const models = require('./src/models');
93-
return models.init(true);
170+
logger.info('Initializing database...');
171+
await models.init(true);
94172
}
95173

96174
// Exports

0 commit comments

Comments
 (0)