Skip to content

Commit fff545f

Browse files
author
Nkumar
committed
initial commit
1 parent 69fe5d0 commit fff545f

File tree

10,018 files changed

+1187687
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

10,018 files changed

+1187687
-0
lines changed

Procfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: node index.js

common/auth.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Authentication and authorization middleware.
3+
*/
4+
const _ = require('lodash')
5+
const createError = require('http-errors')
6+
const config = require('config')
7+
8+
const helper = require('./helper')
9+
10+
/**
11+
* Check if the request is authenticated and authorized.
12+
*
13+
* @param {Object} req the request
14+
* @param {Object} res the response
15+
* @param {Function} next the next middleware
16+
*/
17+
function auth (req, res, next) {
18+
// Parse the token from request header
19+
let token
20+
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
21+
token = req.headers.authorization.split(' ')[1]
22+
}
23+
24+
if (!token) {
25+
throw createError.Unauthorized('Action is not allowed for anonymous')
26+
}
27+
28+
const payload = helper.verifyJwtToken(token)
29+
30+
// Check if the service name is in the allowed list
31+
if (!_.includes(config.ALLOWED_SERVICES, payload.name)) {
32+
throw createError.Forbidden(`Action is not allowed for ${payload.name} service`)
33+
}
34+
35+
// Set source service name to the request
36+
req.sourceServiceName = payload.name
37+
next()
38+
}
39+
40+
module.exports = auth

common/helper.js

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
'use strict';
2+
3+
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
4+
5+
/**
6+
* This file defines helper methods.
7+
*/
8+
var util = require('util');
9+
var _ = require('lodash');
10+
var Joi = require('joi');
11+
var getParams = require('get-parameter-names');
12+
var config = require('config');
13+
var jwt = require('jsonwebtoken');
14+
var createError = require('http-errors');
15+
16+
var logger = require('./logger');
17+
18+
/**
19+
* Convert array with arguments to object.
20+
*
21+
* @param {Array} params the name of parameters
22+
* @param {Array} arr the array with values
23+
* @returns {Object} the combined object
24+
* @private
25+
*/
26+
function combineObject(params, arr) {
27+
var ret = {};
28+
_.forEach(arr, function (arg, i) {
29+
ret[params[i]] = arg;
30+
});
31+
return ret;
32+
}
33+
34+
/**
35+
* Decorate all functions of a service and log debug information if DEBUG is enabled.
36+
*
37+
* @param {Object} service the service
38+
* @private
39+
*/
40+
function decorateWithLogging(service) {
41+
if (config.LOG_LEVEL !== 'debug') {
42+
return;
43+
}
44+
_.forEach(service, function (method, name) {
45+
var params = method.params || getParams(method);
46+
service[name] = function () {
47+
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
48+
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
49+
args[_key] = arguments[_key];
50+
}
51+
52+
var result;
53+
return regeneratorRuntime.wrap(function _callee$(_context) {
54+
while (1) {
55+
switch (_context.prev = _context.next) {
56+
case 0:
57+
logger.debug('ENTER ' + name);
58+
logger.debug('input arguments');
59+
logger.debug(util.inspect(combineObject(params, args)));
60+
_context.next = 5;
61+
return method.apply(this, args);
62+
63+
case 5:
64+
result = _context.sent;
65+
66+
logger.debug('EXIT ' + name);
67+
logger.debug('output arguments');
68+
logger.debug(util.inspect(result));
69+
return _context.abrupt('return', result);
70+
71+
case 10:
72+
case 'end':
73+
return _context.stop();
74+
}
75+
}
76+
}, _callee, this);
77+
}));
78+
79+
function serviceMethodWithLogging() {
80+
return _ref.apply(this, arguments);
81+
}
82+
83+
return serviceMethodWithLogging;
84+
}();
85+
});
86+
}
87+
88+
/**
89+
* Decorate all functions of a service and validate input values
90+
* and replace input arguments with sanitized result = require('Joi.
91+
* Service method must have a `schema` property with Joi schema.
92+
*
93+
* @param {Object} service the service
94+
* @private
95+
*/
96+
function decorateWithValidator(service) {
97+
_.forEach(service, function (method, name) {
98+
if (!method.schema) {
99+
return;
100+
}
101+
var params = getParams(method);
102+
service[name] = function () {
103+
var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2() {
104+
for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
105+
args[_key2] = arguments[_key2];
106+
}
107+
108+
var value, normalized, newArgs;
109+
return regeneratorRuntime.wrap(function _callee2$(_context2) {
110+
while (1) {
111+
switch (_context2.prev = _context2.next) {
112+
case 0:
113+
value = combineObject(params, args);
114+
normalized = Joi.attempt(value, method.schema, { abortEarly: false });
115+
// Joi will normalize values
116+
// for example string number '1' to 1
117+
// if schema type is number
118+
119+
newArgs = _.map(params, function (param) {
120+
return normalized[param];
121+
});
122+
return _context2.abrupt('return', method.apply(this, newArgs));
123+
124+
case 4:
125+
case 'end':
126+
return _context2.stop();
127+
}
128+
}
129+
}, _callee2, this);
130+
}));
131+
132+
function serviceMethodWithValidation() {
133+
return _ref2.apply(this, arguments);
134+
}
135+
136+
return serviceMethodWithValidation;
137+
}();
138+
service[name].params = params;
139+
});
140+
}
141+
142+
/**
143+
* Apply logger and validation decorators to the service.
144+
*
145+
* @export helper/buildService
146+
* @param {any} service the service to wrap
147+
*/
148+
function buildService(service) {
149+
decorateWithValidator(service);
150+
decorateWithLogging(service);
151+
}
152+
153+
/**
154+
* Verify the JWT token and get the payload.
155+
*
156+
* @param {String} token the JWT token to verify
157+
* @returns {Object} the payload decoded from the token
158+
*/
159+
function verifyJwtToken(token) {
160+
var payload = void 0;
161+
162+
try {
163+
payload = jwt.verify(token, config.JWT_TOKEN_SECRET);
164+
} catch (err) {
165+
if (err.message === 'jwt expired') {
166+
throw createError.Unauthorized('Token has been expired');
167+
}
168+
169+
throw createError.Unauthorized('Failed to verify token');
170+
}
171+
172+
if (!payload) {
173+
throw createError.Unauthorized('Failed to decode token');
174+
}
175+
176+
return payload;
177+
}
178+
179+
/**
180+
* Sign the payload and get the JWT token.
181+
*
182+
* @param {Object} payload the payload to be sign
183+
* @returns {String} the token
184+
*/
185+
function signJwtToken(payload) {
186+
return jwt.sign(payload, config.JWT_TOKEN_SECRET, { expiresIn: config.JWT_TOKEN_EXPIRES_IN });
187+
}
188+
189+
/**
190+
* Validate the event based on the source service, type, and message.
191+
*
192+
* @param {String} sourceServiceName the source service name
193+
* @param {Object} event the event
194+
*/
195+
function validateEvent(sourceServiceName, event) {
196+
// The message should be a JSON-formatted string
197+
try {
198+
JSON.parse(event.message);
199+
} catch (err) {
200+
logger.error(err);
201+
throw createError.BadRequest('"message" is not a valid JSON-formatted string: ' + err.message);
202+
}
203+
204+
// The message should match with the source service and type
205+
// no-op for now
206+
}
207+
208+
module.exports = {
209+
buildService: buildService,
210+
verifyJwtToken: verifyJwtToken,
211+
signJwtToken: signJwtToken,
212+
validateEvent: validateEvent
213+
};

common/logger.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* Configure the logger.
3+
*/
4+
const config = require('config')
5+
const winston = require('winston')
6+
7+
const logger = new winston.Logger({
8+
level: config.LOG_LEVEL,
9+
transports: [
10+
new winston.transports.Console({
11+
timestamp: () => new Date().toISOString()
12+
})
13+
]
14+
})
15+
16+
module.exports = logger

config/default.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* The default configuration.
3+
*/
4+
module.exports = {
5+
KAFKA_CLIENT_CERT: process.env.KAFKA_CLIENT_CERT ? process.env.KAFKA_CLIENT_CERT.replace('\\n', '\n') : null,
6+
KAFKA_CLIENT_CERT_KEY: process.env.KAFKA_CLIENT_CERT_KEY ? process.env.KAFKA_CLIENT_CERT_KEY.replace('\\n', '\n') : null,
7+
ALLOWED_SERVICES: ['project-service','message-service'],
8+
LOG_LEVEL: process.env.LOG_LEVEL || 'debug',
9+
CONTEXT_PATH: process.env.API_VERSION || '/eventbus/',
10+
PORT: process.env.PORT || '3000',
11+
JWT_TOKEN_SECRET: process.env.JWT_TOKEN_SECRET || 'JWT_TOKEN_SECRET',
12+
JWT_TOKEN_EXPIRES_IN: process.env.JWT_TOKEN_EXPIRES_IN || '100 days',
13+
KAFKA_TOPIC_PREFIX: process.env.KAFKA_TOPIC_PREFIX || 'joan-26673.notifications.'
14+
}

controllers/EventController.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
/**
4+
* Create a new event.
5+
*
6+
* @param {Object} req the request
7+
* @param {Object} res the response
8+
* @param {Function} next the next middleware
9+
*/
10+
var create = function () {
11+
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(req, res, next) {
12+
return regeneratorRuntime.wrap(function _callee$(_context) {
13+
while (1) {
14+
switch (_context.prev = _context.next) {
15+
case 0:
16+
_context.next = 2;
17+
return MessageBusService.postEvent(req.sourceServiceName, req.body);
18+
19+
case 2:
20+
res.status(204).end();
21+
next();
22+
23+
case 4:
24+
case 'end':
25+
return _context.stop();
26+
}
27+
}
28+
}, _callee, this);
29+
}));
30+
31+
return function create(_x, _x2, _x3) {
32+
return _ref.apply(this, arguments);
33+
};
34+
}();
35+
36+
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
37+
38+
/**
39+
* The Event controller.
40+
*/
41+
var MessageBusService = require('../services/MessageBusService');
42+
43+
module.exports = {
44+
create: create
45+
};

controllers/HealthController.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* Contains endpoints related to service health
3+
*/
4+
'use strict';
5+
6+
/**
7+
* Health Check.
8+
* @param req the request
9+
* @param res the response
10+
*/
11+
12+
var health = function () {
13+
var _ref = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee(req, res, next) {
14+
return regeneratorRuntime.wrap(function _callee$(_context) {
15+
while (1) {
16+
switch (_context.prev = _context.next) {
17+
case 0:
18+
res.json({ health: 'ok' });
19+
next();
20+
21+
case 2:
22+
case 'end':
23+
return _context.stop();
24+
}
25+
}
26+
}, _callee, this);
27+
}));
28+
29+
return function health(_x, _x2, _x3) {
30+
return _ref.apply(this, arguments);
31+
};
32+
}();
33+
34+
// Exports
35+
36+
37+
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
38+
39+
module.exports = {
40+
health: health
41+
};

0 commit comments

Comments
 (0)