openhim-core
Version:
The OpenHIM core application that provides logging and routing of http requests
525 lines (476 loc) • 19.5 kB
JavaScript
var Channel, Client, Q, apiConf, application, authorisation, autoRetryUtils, config, domain, events, generateEvents, getChannelIDsArray, getProjectionObject, hasError, logger, os, sdc, statsd_client, statsd_server, timer, transactions, truncateTransactionDetails, updateTransactionMetrics, utils,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
transactions = require('../model/transactions');
events = require('../middleware/events');
Channel = require('../model/channels').Channel;
Client = require('../model/clients').Client;
autoRetryUtils = require('../autoRetry');
Q = require('q');
logger = require('winston');
authorisation = require('./authorisation');
utils = require("../utils");
config = require('../config/config');
statsd_client = require("statsd-client");
statsd_server = config.get('statsd');
sdc = new statsd_client(statsd_server);
application = config.get('application');
apiConf = config.get('api');
os = require("os");
timer = new Date();
domain = os.hostname() + '.' + application.name;
hasError = function(updates) {
var error, ref, ref1;
if (updates.error != null) {
return true;
}
if (updates.routes != null) {
error = false;
updates.routes.forEach(function(route) {
if (route.error) {
return error = true;
}
});
}
if (error) {
return true;
}
if (((ref = updates.$push) != null ? (ref1 = ref.routes) != null ? ref1.error : void 0 : void 0) != null) {
return true;
}
return false;
};
getChannelIDsArray = function(channels) {
var channel, channelIDs, i, len;
channelIDs = [];
for (i = 0, len = channels.length; i < len; i++) {
channel = channels[i];
channelIDs.push(channel._id.toString());
}
return channelIDs;
};
getProjectionObject = function(filterRepresentation) {
switch (filterRepresentation) {
case "simpledetails":
return {
"request.body": 0,
"response.body": 0,
"routes.request.body": 0,
"routes.response.body": 0,
"orchestrations.request.body": 0,
"orchestrations.response.body": 0
};
case "full":
return {};
case "fulltruncate":
return {};
case "bulkrerun":
return {
"_id": 1,
"childIDs": 1,
"canRerun": 1,
"channelID": 1
};
default:
return {
"request.body": 0,
"request.headers": 0,
"response.body": 0,
"response.headers": 0,
orchestrations: 0,
routes: 0
};
}
};
truncateTransactionDetails = function(trx) {
var i, j, len, len1, o, r, ref, ref1, ref2, ref3, results, trunc, truncateAppend, truncateSize;
truncateSize = (ref = apiConf.truncateSize) != null ? ref : 15000;
truncateAppend = (ref1 = apiConf.truncateAppend) != null ? ref1 : "\n[truncated ...]";
trunc = function(t) {
var ref2, ref3;
if ((((ref2 = t.request) != null ? ref2.body : void 0) != null) && t.request.body.length > truncateSize) {
t.request.body = t.request.body.slice(0, truncateSize) + truncateAppend;
}
if ((((ref3 = t.response) != null ? ref3.body : void 0) != null) && t.response.body.length > truncateSize) {
return t.response.body = t.response.body.slice(0, truncateSize) + truncateAppend;
}
};
trunc(trx);
if (trx.routes != null) {
ref2 = trx.routes;
for (i = 0, len = ref2.length; i < len; i++) {
r = ref2[i];
trunc(r);
}
}
if (trx.orchestrations != null) {
ref3 = trx.orchestrations;
results = [];
for (j = 0, len1 = ref3.length; j < len1; j++) {
o = ref3[j];
results.push(trunc(o));
}
return results;
}
};
/*
* Retrieves the list of transactions
*/
exports.getTransactions = function*() {
var channels, e, error1, filterLimit, filterPage, filterRepresentation, filterSkip, filters, filtersObject, i, key, len, projectionFiltersObject, ref, ref1, results, trx;
try {
filtersObject = this.request.query;
filterLimit = filtersObject.filterLimit;
filterPage = filtersObject.filterPage;
filterRepresentation = filtersObject.filterRepresentation;
delete filtersObject.filterLimit;
delete filtersObject.filterPage;
delete filtersObject.filterRepresentation;
filterSkip = filterPage * filterLimit;
filters = filtersObject.filters != null ? JSON.parse(filtersObject.filters) : {};
if (!authorisation.inGroup('admin', this.authenticated)) {
channels = (yield authorisation.getUserViewableChannels(this.authenticated));
if (!filtersObject.channelID) {
filters.channelID = {
$in: getChannelIDsArray(channels)
};
} else if (ref = filtersObject.channelID, indexOf.call(getChannelIDsArray(channels), ref) < 0) {
return utils.logAndSetResponse(this, 403, "Forbidden: Unauthorized channel " + filtersObject.channelID, 'info');
}
filterRepresentation = '';
}
projectionFiltersObject = getProjectionObject(filterRepresentation);
if (filtersObject.channelID) {
filters.channelID = filtersObject.channelID;
}
if (filters['request.timestamp']) {
filters['request.timestamp'] = JSON.parse(filters['request.timestamp']);
}
/* Transaction Filters */
if (filters['request.path']) {
filters['request.path'] = new RegExp(filters['request.path'], "i");
}
if (filters['request.querystring']) {
filters['request.querystring'] = new RegExp(filters['request.querystring'], "i");
}
if (filters['response.status'] && utils.statusCodePatternMatch(filters['response.status'])) {
filters['response.status'] = {
"$gte": filters['response.status'][0] * 100,
"$lt": filters['response.status'][0] * 100 + 100
};
}
if (filters['properties']) {
key = Object.keys(filters['properties'])[0];
filters['properties.' + key] = filters['properties'][key];
if (filters['properties'][key] === null) {
filters['properties.' + key] = {
'$exists': true
};
}
delete filters['properties'];
}
if (filters['childIDs.0']) {
filters['childIDs.0'] = JSON.parse(filters['childIDs.0']);
}
/* Route Filters */
if (filters['routes.request.path']) {
filters['routes.request.path'] = new RegExp(filters['routes.request.path'], "i");
}
if (filters['routes.request.querystring']) {
filters['routes.request.querystring'] = new RegExp(filters['routes.request.querystring'], "i");
}
if (filters['routes.response.status'] && utils.statusCodePatternMatch(filters['routes.response.status'])) {
filters['routes.response.status'] = {
"$gte": filters['routes.response.status'][0] * 100,
"$lt": filters['routes.response.status'][0] * 100 + 100
};
}
/* orchestration Filters */
if (filters['orchestrations.request.path']) {
filters['orchestrations.request.path'] = new RegExp(filters['orchestrations.request.path'], "i");
}
if (filters['orchestrations.request.querystring']) {
filters['orchestrations.request.querystring'] = new RegExp(filters['orchestrations.request.querystring'], "i");
}
if (filters['orchestrations.response.status'] && utils.statusCodePatternMatch(filters['orchestrations.response.status'])) {
filters['orchestrations.response.status'] = {
"$gte": filters['orchestrations.response.status'][0] * 100,
"$lt": filters['orchestrations.response.status'][0] * 100 + 100
};
}
this.body = (yield transactions.Transaction.find(filters, projectionFiltersObject).skip(filterSkip).limit(parseInt(filterLimit)).sort({
'request.timestamp': -1
}).exec());
if (filterRepresentation === 'fulltruncate') {
ref1 = this.body;
results = [];
for (i = 0, len = ref1.length; i < len; i++) {
trx = ref1[i];
results.push(truncateTransactionDetails(trx));
}
return results;
}
} catch (error1) {
e = error1;
return utils.logAndSetResponse(this, 500, "Could not retrieve transactions via the API: " + e, 'error');
}
};
/*
* Adds an transaction
*/
exports.addTransaction = function*() {
var e, error1, transactionData, tx;
if (!authorisation.inGroup('admin', this.authenticated)) {
utils.logAndSetResponse(this, 403, "User " + this.authenticated.email + " is not an admin, API access to addTransaction denied.", 'info');
return;
}
transactionData = this.request.body;
tx = new transactions.Transaction(transactionData);
try {
(yield Q.ninvoke(tx, "save"));
this.status = 201;
logger.info("User " + this.authenticated.email + " created transaction with id " + tx.id);
return generateEvents(tx, tx.channelID);
} catch (error1) {
e = error1;
return utils.logAndSetResponse(this, 500, "Could not add a transaction via the API: " + e, 'error');
}
};
/*
* Retrieves the details for a specific transaction
*/
exports.getTransactionById = function*(transactionId) {
var channel, channels, e, error1, filterRepresentation, filtersObject, group, i, len, projectionFiltersObject, ref, result, txChannelID;
transactionId = unescape(transactionId);
try {
filtersObject = this.request.query;
filterRepresentation = filtersObject.filterRepresentation;
delete filtersObject.filterRepresentation;
if (!filterRepresentation) {
filterRepresentation = 'full';
}
if (!authorisation.inGroup('admin', this.authenticated)) {
txChannelID = (yield transactions.Transaction.findById(transactionId, {
channelID: 1,
_id: 0
}).exec());
if ((txChannelID != null ? txChannelID.length : void 0) === 0) {
this.body = "Could not find transaction with ID: " + transactionId;
this.status = 404;
return;
} else {
filterRepresentation = 'simpledetails';
channel = (yield Channel.findById(txChannelID.channelID, {
txViewFullAcl: 1,
_id: 0
}).exec());
ref = this.authenticated.groups;
for (i = 0, len = ref.length; i < len; i++) {
group = ref[i];
if (channel.txViewFullAcl.indexOf(group) >= 0) {
filterRepresentation = 'full';
break;
}
}
}
}
projectionFiltersObject = getProjectionObject(filterRepresentation);
result = (yield transactions.Transaction.findById(transactionId, projectionFiltersObject).exec());
if (result && filterRepresentation === 'fulltruncate') {
truncateTransactionDetails(result);
}
if (!result) {
this.body = "Could not find transaction with ID: " + transactionId;
return this.status = 404;
} else if (!authorisation.inGroup('admin', this.authenticated)) {
channels = (yield authorisation.getUserViewableChannels(this.authenticated));
if (getChannelIDsArray(channels).indexOf(result.channelID.toString()) >= 0) {
return this.body = result;
} else {
return utils.logAndSetResponse(this, 403, "User " + this.authenticated.email + " is not authenticated to retrieve transaction " + transactionId, 'info');
}
} else {
return this.body = result;
}
} catch (error1) {
e = error1;
return utils.logAndSetResponse(this, 500, "Could not get transaction by ID via the API: " + e, 'error');
}
};
/*
* Retrieves all transactions specified by clientId
*/
exports.findTransactionByClientId = function*(clientId) {
var channels, e, error1, filterRepresentation, filtersObject, projectionFiltersObject;
clientId = unescape(clientId);
try {
filtersObject = this.request.query;
filterRepresentation = filtersObject.filterRepresentation;
projectionFiltersObject = getProjectionObject(filterRepresentation);
filtersObject = {};
filtersObject.clientID = clientId;
if (!authorisation.inGroup('admin', this.authenticated)) {
channels = (yield authorisation.getUserViewableChannels(this.authenticated));
filtersObject.channelID = {
$in: getChannelIDsArray(channels)
};
filterRepresentation = '';
}
return this.body = (yield transactions.Transaction.find(filtersObject, projectionFiltersObject).sort({
'request.timestamp': -1
}).exec());
} catch (error1) {
e = error1;
return utils.logAndSetResponse(this, 500, "Could not get transaction by clientID via the API: " + e, 'error');
}
};
generateEvents = function(transaction, channelID) {
return Channel.findById(channelID, function(err, channel) {
var done, trxEvents;
logger.debug("Storing events for transaction: " + transaction._id);
trxEvents = [];
done = function(err) {
if (err) {
return logger.error(err);
}
};
events.createTransactionEvents(trxEvents, transaction, channel);
if (trxEvents.length > 0) {
return events.saveEvents(trxEvents, done);
}
});
};
updateTransactionMetrics = function(updates, doc) {
var k, ref, ref1, results, route;
if (((ref = updates['$push']) != null ? ref.routes : void 0) != null) {
ref1 = updates['$push'];
results = [];
for (k in ref1) {
route = ref1[k];
results.push((function(route) {
var i, j, len, len1, metric, orchestration, ref2, ref3, results1;
if (route.metrics != null) {
ref2 = route.metrics;
for (i = 0, len = ref2.length; i < len; i++) {
metric = ref2[i];
if (metric.type === 'counter') {
logger.debug("incrementing mediator counter " + metric.name);
sdc.increment(domain + ".channels." + doc.channelID + "." + route.name + ".mediator_metrics." + metric.name);
}
if (metric.type === 'timer') {
logger.debug("incrementing mediator timer " + metric.name);
sdc.timing(domain + ".channels." + doc.channelID + "." + route.name + ".mediator_metrics." + metric.name, metric.value);
}
if (metric.type === 'gauge') {
logger.debug("incrementing mediator gauge " + metric.name);
sdc.gauge(domain + ".channels." + doc.channelID + "." + route.name + ".mediator_metrics." + metric.name, metric.value);
}
}
}
ref3 = route.orchestrations;
results1 = [];
for (j = 0, len1 = ref3.length; j < len1; j++) {
orchestration = ref3[j];
results1.push((function(orchestration) {
var l, len2, orchestrationDuration, orchestrationName, orchestrationStatus, ref4, results2;
orchestrationDuration = orchestration.response.timestamp - orchestration.request.timestamp;
orchestrationStatus = orchestration.response.status;
orchestrationName = orchestration.name;
if (orchestration.group) {
orchestrationName = orchestration.group + "." + orchestration.name;
}
/*
* Update timers
*/
logger.debug('updating async route timers');
sdc.timing(domain + ".channels." + doc.channelID + "." + route.name + ".orchestrations." + orchestrationName, orchestrationDuration);
sdc.timing(domain + ".channels." + doc.channelID + "." + route.name + ".orchestrations." + orchestrationName + ".statusCodes." + orchestrationStatus, orchestrationDuration);
/*
* Update counters
*/
logger.debug('updating async route counters');
sdc.increment(domain + ".channels." + doc.channelID + "." + route.name + ".orchestrations." + orchestrationName);
sdc.increment(domain + ".channels." + doc.channelID + "." + route.name + ".orchestrations." + orchestrationName + ".statusCodes." + orchestrationStatus);
if (orchestration.metrics != null) {
ref4 = orchestration.metrics;
results2 = [];
for (l = 0, len2 = ref4.length; l < len2; l++) {
metric = ref4[l];
if (metric.type === 'counter') {
logger.debug("incrementing " + route.name + " orchestration counter " + metric.name);
sdc.increment(domain + ".channels." + doc.channelID + "." + route.name + ".orchestrations." + orchestrationName + "." + metric.name, metric.value);
}
if (metric.type === 'timer') {
logger.debug("incrementing " + route.name + " orchestration timer " + metric.name);
sdc.timing(domain + ".channels." + doc.channelID + "." + route.name + ".orchestrations." + orchestrationName + "." + metric.name, metric.value);
}
if (metric.type === 'gauge') {
logger.debug("incrementing " + route.name + " orchestration gauge " + metric.name);
results2.push(sdc.gauge(domain + ".channels." + doc.channelID + "." + route.name + ".orchestrations." + orchestrationName + "." + metric.name, metric.value));
} else {
results2.push(void 0);
}
}
return results2;
}
})(orchestration));
}
return results1;
})(route));
}
return results;
}
};
/*
* Updates a transaction record specified by transactionId
*/
exports.updateTransaction = function*(transactionId) {
var channel, e, error1, tx, updates;
if (!authorisation.inGroup('admin', this.authenticated)) {
utils.logAndSetResponse(this, 403, "User " + this.authenticated.email + " is not an admin, API access to updateTransaction denied.", 'info');
return;
}
transactionId = unescape(transactionId);
updates = this.request.body;
try {
if (hasError(updates)) {
tx = (yield transactions.Transaction.findById(transactionId).exec());
channel = (yield Channel.findById(tx.channelID).exec());
if (!autoRetryUtils.reachedMaxAttempts(tx, channel)) {
updates.autoRetry = true;
autoRetryUtils.queueForRetry(tx);
}
}
tx = (yield transactions.Transaction.findByIdAndUpdate(transactionId, updates, {
"new": true
}).exec());
this.body = "Transaction with ID: " + transactionId + " successfully updated";
this.status = 200;
logger.info("User " + this.authenticated.email + " updated transaction with id " + transactionId);
generateEvents(updates, tx.channelID);
return updateTransactionMetrics(updates, tx);
} catch (error1) {
e = error1;
return utils.logAndSetResponse(this, 500, "Could not update transaction via the API: " + e, 'error');
}
};
/*
* Removes a transaction
*/
exports.removeTransaction = function*(transactionId) {
var e, error1;
if (!authorisation.inGroup('admin', this.authenticated)) {
utils.logAndSetResponse(this, 403, "User " + this.authenticated.email + " is not an admin, API access to removeTransaction denied.", 'info');
return;
}
transactionId = unescape(transactionId);
try {
(yield transactions.Transaction.findByIdAndRemove(transactionId).exec());
this.body = 'Transaction successfully deleted';
this.status = 200;
return logger.info("User " + this.authenticated.email + " removed transaction with id " + transactionId);
} catch (error1) {
e = error1;
return utils.logAndSetResponse(this, 500, "Could not remove transaction via the API: " + e, 'error');
}
};
//# sourceMappingURL=transactions.js.map