UNPKG

openhim-core

Version:

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

175 lines (142 loc) 5.9 kB
transactions = require "../model/transactions" logger = require "winston" Q = require "q" config = require '../config/config' statsdServer = config.get 'statsd' application = config.get 'application' SDC = require 'statsd-client' os = require 'os' domain = "#{os.hostname()}.#{application.name}.appMetrics" sdc = new SDC statsdServer exports.transactionStatus = transactionStatus = PROCESSING: 'Processing' SUCCESSFUL: 'Successful' COMPLETED: 'Completed' COMPLETED_W_ERR: 'Completed with error(s)' FAILED: 'Failed' copyMapWithEscapedReservedCharacters = (map) -> escapedMap = {} for k, v of map if k.indexOf('.')>-1 or k.indexOf('$')>-1 k = k.replace('.', '\uff0e').replace('$', '\uff04') escapedMap[k] = v return escapedMap exports.storeTransaction = (ctx, done) -> logger.info 'Storing request metadata for inbound transaction' ctx.requestTimestamp = new Date() headers = copyMapWithEscapedReservedCharacters ctx.header tx = new transactions.Transaction status: transactionStatus.PROCESSING clientID: ctx.authenticated._id channelID: ctx.authorisedChannel._id clientIP: ctx.authenticated.ip request: host: ctx.host?.split(':')[0] port: ctx.host?.split(':')[1] path: ctx.path headers: headers querystring: ctx.querystring body: ctx.body method: ctx.method timestamp: ctx.requestTimestamp if ctx.parentID && ctx.taskID tx.parentID = ctx.parentID tx.taskID = ctx.taskID # check if channel request body is false and remove - or if request body is empty if ctx.authorisedChannel.requestBody == false || tx.request.body == '' # reset request body tx.request.body = '' # check if method is POST|PUT|PATCH - rerun not possible without request body if ctx.method == 'POST' or ctx.method == 'PUT' or ctx.method == 'PATCH' tx.canRerun = false tx.save (err, tx) -> if err logger.error 'Could not save transaction metadata: ' + err return done err else ctx.transactionId = tx._id ctx.header['X-OpenHIM-TransactionID'] = tx._id.toString() return done null, tx exports.storeResponse = (ctx, done) -> headers = copyMapWithEscapedReservedCharacters ctx.response.header res = status: ctx.response.status headers: headers body: if not ctx.response.body then "" else ctx.response.body.toString() timestamp: ctx.response.timestamp # check if channel response body is false and remove if ctx.authorisedChannel.responseBody == false # reset request body - primary route res.body = '' update = { response: res } # Set status from mediator if ctx.mediatorResponse?.status? update.status = ctx.mediatorResponse.status if ctx.mediatorResponse update.orchestrations = ctx.mediatorResponse.orchestrations if ctx.mediatorResponse.orchestrations update.properties = ctx.mediatorResponse.properties if ctx.mediatorResponse.properties transactions.Transaction.findOneAndUpdate { _id: ctx.transactionId }, update , { runValidators: true }, (err, tx) -> logger.info "stored primary response for #{tx._id}" if err logger.error 'Could not save response metadata for transaction: ' + ctx.transactionId + '. ' + err return done err if tx is undefined or tx is null logger.error 'Could not find transaction: ' + ctx.transactionId return done err return done() exports.storeNonPrimaryResponse = (ctx, routeObject, done) -> # check if channel response body is false and remove if ctx.authorisedChannel.responseBody == false routeObject.response.body = '' if ctx.transactionId? transactions.Transaction.findByIdAndUpdate ctx.transactionId, {$push: { "routes": routeObject } } , (err,tx) -> if err logger.error err done tx else logger.error "the request has no transactionId" exports.setFinalStatus = setFinalStatus = (ctx, callback) -> transactionId = '' if ctx.request?.header?["X-OpenHIM-TransactionID"] transactionId = ctx.request.header["X-OpenHIM-TransactionID"] else transactionId = ctx.transactionId.toString() transactions.Transaction.findById transactionId, (err, tx) -> if ctx.mediatorResponse?.status? logger.info "The transaction status has been set to #{ctx.mediatorResponse.status} by the mediator" callback tx else routeFailures = false routeSuccess = true if ctx.routes for route in ctx.routes if 500 <= route.response.status <= 599 routeFailures = true if not (200 <= route.response.status <= 299) routeSuccess = false tx.status = transactionStatus.COMPLETED if (500 <= ctx.response.status <= 599) tx.status = transactionStatus.FAILED else if routeFailures tx.status = transactionStatus.COMPLETED_W_ERR if (200 <= ctx.response.status <= 299) && routeSuccess tx.status = transactionStatus.SUCCESSFUL if (400 <= ctx.response.status <= 499) && routeSuccess tx.status = transactionStatus.COMPLETED # In all other cases mark as completed if not ctx.status tx.status = transactionStatus.COMPLETED logger.info "Final status for transaction #{tx._id} : #{tx.status}" transactions.Transaction.findByIdAndUpdate transactionId, {status: tx.status}, { }, (err,tx) -> tx.save callback tx exports.koaMiddleware = (next) -> startTime = new Date() if statsdServer.enabled saveTransaction = Q.denodeify exports.storeTransaction yield saveTransaction this sdc.timing "#{domain}.messageStoreMiddleware.storeTransaction", startTime if statsdServer.enabled yield next startTime = new Date() if statsdServer.enabled exports.storeResponse this, -> sdc.timing "#{domain}.messageStoreMiddleware.storeResponse", startTime if statsdServer.enabled