openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
384 lines (349 loc) • 12.7 kB
JavaScript
var Alert, Channel, ContactGroup, Event, Q, User, afterSendAlert, alertingTask, authorisation, calcDateFromForUser, config, contact, countTotalTransactionsForChannel, findGroup, findOneAlert, findTransactions, findTransactionsMatchingStatus, getAllChannels, getTransactionsForAlert, htmlTemplate, logger, moment, plainTemplate, sendAlert, sendAlerts, setupAgenda, smsTemplate, trxURL, userAlreadyReceivedAlert, utils;
config = require("./config/config");
config.alerts = config.get('alerts');
logger = require("winston");
contact = require('./contact');
moment = require('moment');
Q = require('q');
Channel = require('./model/channels').Channel;
Event = require('./model/events').Event;
ContactGroup = require('./model/contactGroups').ContactGroup;
Alert = require('./model/alerts').Alert;
User = require('./model/users').User;
authorisation = require('./middleware/authorisation');
utils = require('./utils');
trxURL = function(trx) {
return config.alerts.consoleURL + "/#/transactions/" + trx._id;
};
plainTemplate = function(transactions, channelName, status) {
return "OpenHIM Transactions Alert\n\nThe following transaction(s) have completed with status " + status + " on the OpenHIM instance running on " + config.alerts.himInstance + ":\nChannel - " + channelName + "\n" + ((transactions.map(function(trx) {
return trxURL(trx);
})).join('\n')) + "\n";
};
htmlTemplate = function(transactions, channelName, status) {
var alert;
alert = "<html>\n <head></head>\n <body>\n <h1>OpenHIM Transactions Alert</h1>\n <div>\n <p>The following transaction(s) have completed with status <b>" + status + "</b> on the OpenHIM instance running on <b>" + config.alerts.himInstance + "</b>:</p>\n <table>\n <tr><td>Channel - <b>" + channelName + "</b></td></td>\n";
alert += (transactions.map(function(trx) {
return " <tr><td><a href='" + (trxURL(trx)) + "'>" + (trxURL(trx)) + "</a></td></tr>";
})).join('\n');
alert += '\n';
return alert += " </table>\n </div>\n </body>\n</html>";
};
smsTemplate = function(transactions, channelName, status) {
var alert;
alert = "Alert - ";
if (transactions.length > 1) {
alert += transactions.length + " transactions have";
} else if (transactions.length === 1) {
alert += "1 transaction has";
} else {
alert += "no transactions have";
}
return alert += " completed with status " + status + " on the OpenHIM running on " + config.alerts.himInstance + " (" + channelName + ")";
};
getAllChannels = function(callback) {
return Channel.find({}, callback);
};
findGroup = function(groupID, callback) {
return ContactGroup.findOne({
_id: groupID
}, callback);
};
findTransactions = function(channelID, dateFrom, status, callback) {
return Event.find({
created: {
$gte: dateFrom
},
channelID: channelID,
event: 'end',
status: status
}, {
'transactionID': 'transactionID'
}).hint({
created: 1
}).exec(callback);
};
countTotalTransactionsForChannel = function(channelID, dateFrom, callback) {
return Event.count({
created: {
$gte: dateFrom
},
channelID: channelID,
event: 'end'
}, callback);
};
findOneAlert = function(channelID, status, dateFrom, user, alertStatus, callback) {
var criteria;
criteria = {
timestamp: {
"$gte": dateFrom
},
channelID: channelID,
status: status,
alertStatus: alertStatus
};
if (user) {
criteria.user = user;
}
return Alert.findOne(criteria).exec(callback);
};
findTransactionsMatchingStatus = function(channelID, status, dateFrom, failureRate, callback) {
var dateToCheck, pat, statusMatch;
pat = /\dxx/.exec(status);
if (pat) {
statusMatch = {
"$gte": status[0] * 100,
"$lt": status[0] * 100 + 100
};
} else {
statusMatch = status;
}
dateToCheck = dateFrom;
if (failureRate != null) {
dateToCheck = moment().subtract(1, 'hours').toDate();
}
return findTransactions(channelID, dateToCheck, statusMatch, function(err, results) {
var _countStart;
if (!err && (results != null) && (failureRate != null)) {
_countStart = new Date();
return countTotalTransactionsForChannel(channelID, dateToCheck, function(err, count) {
var failureRatio;
logger.debug(".countTotalTransactionsForChannel: " + (new Date() - _countStart) + " ms");
if (err) {
return callback(err, null);
}
failureRatio = results.length / count * 100.0;
if (failureRatio >= failureRate) {
return findOneAlert(channelID, status, dateToCheck, null, 'Completed', function(err, alert) {
if (err) {
return callback(err, null);
}
if (alert != null) {
return callback(err, []);
} else {
return callback(err, utils.uniqArray(results));
}
});
} else {
return callback(err, []);
}
});
} else {
return callback(err, results);
}
});
};
calcDateFromForUser = function(user) {
var dateFrom;
if (user.maxAlerts === '1 per hour') {
return dateFrom = moment().subtract(1, 'hours').toDate();
} else if (user.maxAlerts === '1 per day') {
return dateFrom = moment().startOf('day').toDate();
} else {
return null;
}
};
userAlreadyReceivedAlert = function(channelID, status, user, callback) {
var dateFrom;
if (!user.maxAlerts || user.maxAlerts === 'no max') {
return callback(null, false);
} else {
dateFrom = calcDateFromForUser(user);
if (!dateFrom) {
return callback("Unsupported option 'maxAlerts=" + user.maxAlerts + "'");
}
return findOneAlert(channelID, status, dateFrom, user.user, 'Completed', function(err, alert) {
return callback(err != null ? err : null, alert ? true : false);
});
}
};
getTransactionsForAlert = function(channelID, status, user, transactions, callback) {
var dateFrom;
if (!user.maxAlerts || user.maxAlerts === 'no max') {
return callback(null, transactions);
} else {
dateFrom = calcDateFromForUser(user);
if (!dateFrom) {
return callback("Unsupported option 'maxAlerts=" + user.maxAlerts + "'");
}
return findTransactionsMatchingStatus(channelID, status, dateFrom, null, callback);
}
};
sendAlert = function(channel, status, user, transactions, contactHandler, done) {
return User.findOne({
email: user.user
}, function(err, dbUser) {
if (err) {
return done(err);
}
if (!dbUser) {
return done("Cannot send alert: Unknown user '" + user.user + "'");
}
return userAlreadyReceivedAlert(channel._id, status, user, function(err, received) {
if (err) {
return done(err, true);
}
if (received) {
return done(null, true);
}
logger.info("Sending alert for user '" + user.user + "' using method '" + user.method + "'");
return getTransactionsForAlert(channel._id, status, user, transactions, function(err, transactionsForAlert) {
var htmlMsg, plainMsg, smsMsg;
if (user.method === 'email') {
plainMsg = plainTemplate(transactionsForAlert, channel.name, status);
htmlMsg = htmlTemplate(transactionsForAlert, channel.name, status);
return contactHandler('email', user.user, 'OpenHIM Alert', plainMsg, htmlMsg, done);
} else if (user.method === 'sms') {
if (!dbUser.msisdn) {
return done("Cannot send alert: MSISDN not specified for user '" + user.user + "'");
}
smsMsg = smsTemplate(transactionsForAlert, channel.name, status);
return contactHandler('sms', dbUser.msisdn, 'OpenHIM Alert', smsMsg, null, done);
} else {
return done("Unknown method '" + user.method + "' specified for user '" + user.user + "'");
}
});
});
});
};
afterSendAlert = function(err, channelID, alert, user, transactions, skipSave, done) {
if (err) {
logger.error(err);
}
if (!skipSave) {
alert = new Alert({
user: user.user,
method: user.method,
channelID: channelID,
status: alert.status,
alertStatus: err ? 'Failed' : 'Completed'
});
return alert.save(function(err) {
if (err) {
logger.error(err);
}
return done();
});
} else {
return done();
}
};
sendAlerts = function(channel, alert, transactions, contactHandler, done) {
var _alertStart, fn, group, groupDefer, i, j, len, len1, promises, ref, ref1, user;
promises = [];
_alertStart = new Date();
if (alert.groups) {
ref = alert.groups;
for (i = 0, len = ref.length; i < len; i++) {
group = ref[i];
groupDefer = Q.defer();
findGroup(group, function(err, result) {
var fn, groupUserPromises, j, len1, ref1, user;
if (err) {
logger.error(err);
return groupDefer.resolve();
} else {
groupUserPromises = [];
ref1 = result.users;
fn = function(user) {
var groupUserDefer;
groupUserDefer = Q.defer();
sendAlert(channel, alert.status, user, transactions, contactHandler, function(err, skipSave) {
return afterSendAlert(err, channel._id, alert, user, transactions, skipSave, function() {
return groupUserDefer.resolve();
});
});
return groupUserPromises.push(groupUserDefer.promise);
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
user = ref1[j];
fn(user);
}
return (Q.all(groupUserPromises)).then(function() {
return groupDefer.resolve();
});
}
});
promises.push(groupDefer.promise);
}
}
if (alert.users) {
ref1 = alert.users;
fn = function(user) {
var userDefer;
userDefer = Q.defer();
sendAlert(channel, alert.status, user, transactions, contactHandler, function(err, skipSave) {
return afterSendAlert(err, channel._id, alert, user, transactions, skipSave, function() {
return userDefer.resolve();
});
});
return promises.push(userDefer.promise);
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
user = ref1[j];
fn(user);
}
}
return (Q.all(promises)).then(function() {
logger.debug(".sendAlerts: " + (new Date() - _alertStart) + " ms");
return done();
});
};
alertingTask = function(job, contactHandler, done) {
var _taskStart, lastAlertDate, ref;
if (!job.attrs.data) {
job.attrs.data = {};
}
lastAlertDate = (ref = job.attrs.data.lastAlertDate) != null ? ref : new Date();
_taskStart = new Date();
return getAllChannels(function(err, results) {
var alert, channel, fn, i, j, len, len1, promises, ref1;
promises = [];
for (i = 0, len = results.length; i < len; i++) {
channel = results[i];
if (authorisation.isChannelEnabled(channel)) {
ref1 = channel.alerts;
fn = function(channel, alert) {
var _findStart, deferred;
deferred = Q.defer();
_findStart = new Date();
findTransactionsMatchingStatus(channel._id, alert.status, lastAlertDate, alert.failureRate, function(err, results) {
logger.debug(".findTransactionsMatchingStatus: " + (new Date() - _findStart) + " ms");
if (err) {
logger.error(err);
return deferred.resolve();
} else if ((results != null) && results.length > 0) {
return sendAlerts(channel, alert, results, contactHandler, function() {
return deferred.resolve();
});
} else {
return deferred.resolve();
}
});
return promises.push(deferred.promise);
};
for (j = 0, len1 = ref1.length; j < len1; j++) {
alert = ref1[j];
fn(channel, alert);
}
}
}
return (Q.all(promises)).then(function() {
job.attrs.data.lastAlertDate = new Date();
logger.debug("Alerting task total time: " + (new Date() - _taskStart) + " ms");
return done();
});
});
};
setupAgenda = function(agenda) {
agenda.define('generate transaction alerts', function(job, done) {
return alertingTask(job, contact.contactUser, done);
});
return agenda.every(config.alerts.pollPeriodMinutes + " minutes", 'generate transaction alerts');
};
exports.setupAgenda = setupAgenda;
if (process.env.NODE_ENV === "test") {
exports.findTransactionsMatchingStatus = findTransactionsMatchingStatus;
exports.alertingTask = alertingTask;
}
//# sourceMappingURL=alerts.js.map