/** * Support utilities for using Promises, particularls Kris Kowal's Q: * @see {@link https://github.com/kriskowal/q} * * In particular, these utilities help with sending error responses through * a promise chain. The first time an error is received, the error handler * should call `return returnChainedError()`. Later handlers should then check * if there is a previous error (`hasChainedError()`), and if so just send it * on (`return resendChainedError()`). The final error handler can then return * the chained error. */ 'use strict'; var Q = require('q'); var httpStatus = require('http-status-codes'); var _ = require('lodash'); var debug = require('debug')('webconsole-api:utils:promises'); const ERR_KEY = 'cmcrdErrResponse'; module.exports = { ERR_KEY: ERR_KEY, ErrorResponse: ErrorResponse, sendErrorResponse: sendErrorResponse, returnChainedError: returnChainedError, resendChainedError: resendChainedError, hasChainedError: hasChainedError, getChainedError: getChainedError }; /** * Constructs a new ErrorResponse that is used for passing errors through a * promise chain. * * @class * @param {integer} httpcode - the http status code to respond with * @param {integer} code - the Bridge error code * @param {String} info - the further information string */ function ErrorResponse(httpcode, code, info) { // // Assign the values // this.httpcode = httpcode; this.errorInfo = { code: code, info: info }; } /** * Returns an error in an appropriate way for chaining through a promise * chain. * * @param {Object} err - the existing error for manipulating * @param {integer} httpcode - the http status code to respond with * @param {integer} code - the Bridge error code * @param {String} info - the further information string * * return {Promise} - rejected promise with the error info added */ function returnChainedError(err, httpcode, code, info) { var response = new ErrorResponse(httpcode, code, info); // // If err isn't already an object, turn it into one // if (!_.isObject(err)) { var original = err; err = { originalErr: err }; } err[ERR_KEY] = response; return resendChainedError(err); } /** * Sends on an error again as a rejected promise * * @param {Object} err - the existing error for manipulating * * return {Promise} - rejected promise with the error info added */ function resendChainedError(err) { var deferred = Q.defer(); deferred.reject(err); return deferred.promise; } /** * Checks if there is already a chained error in this error object * * @param {Object} err - the err reason object */ function hasChainedError(err) { return err.hasOwnProperty(ERR_KEY); } /** * Gets the chained error information * * @param {Object} err - the error object * * @return {?ErrorResponse} - the error response info (if any) */ function getChainedError(err) { if (!hasChainedError(err)) { return null; } else { return err[ERR_KEY]; } } /** * Returns an error back to the client. This is either the error that has * been passed through the promise chain, or a default unknown error is * returned. * * @param {Object} res - Express response object * @param {Object} err - the error object */ function sendErrorResponse(res, err) { var response = getChainedError(err); if (!response) { response = new ErrorResponse( httpStatus.INTERNAL_SERVER_ERROR, -1, 'Unknown error' ); } debug(' - send error: [%s] - {%d, %s}', response.httpcode, response.errorInfo.code, response.errorInfo.info ); res.status(response.httpcode).json(response.errorInfo); }