UNPKG

openhim-core

Version:

The OpenHIM core application that provides logging and routing of http requests

525 lines (476 loc) 19.5 kB
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