restify
Version:
REST framework
134 lines (105 loc) • 3.94 kB
JavaScript
;
var util = require('util');
var _ = require('lodash');
var assert = require('assert-plus');
var helpers = require('./helpers');
var HttpError = require('./baseClasses/HttpError');
var RestError = require('./baseClasses/RestError');
var httpErrors = require('./httpErrors');
var restErrors = require('./restErrors');
/**
* create an error object from an http status code.
* first arg is status code, all subsequent args
* passed on to the constructor. only works for regular
* HttpErrors, not RestErrors.
* @public
* @function makeErrFromCode
* @param {Number} statusCode the http status code
* @returns {Error} an error instance
*/
function makeErrFromCode(statusCode) {
// assert!
assert.number(statusCode, 'statusCode');
assert.equal(statusCode >= 400, true);
// drop the first arg
var args = _.drop(_.toArray(arguments));
var name = helpers.errNameFromCode(statusCode);
var ErrCtor = httpErrors[name];
// assert constructor was found
assert.func(ErrCtor);
// pass every other arg down to constructor
return makeInstance(ErrCtor, makeErrFromCode, args);
}
/**
* create RestError subclasses for users. takes a string, creates a
* constructor for them. magicks, again.
* @public
* @function makeConstructor
* @param {String} name the name of the error class to create
* @param {Number} defaults optional status code
* @returns {Function} a constructor function
*/
function makeConstructor(name, defaults) {
assert.string(name, 'name');
assert.optionalObject(defaults, 'defaults');
// code property doesn't have 'Error' in it. remove it.
var defaultCode = name.replace(new RegExp('[Ee]rror$'), '');
var d = _.assign({}, {
name: name,
code: (defaults && defaults.code) || defaultCode,
restCode: _.get(defaults, 'restCode', defaultCode)
}, defaults);
// assert that this constructor doesn't already exist.
assert.equal(typeof module.exports[name], 'undefined',
'Constructor already exists!');
// dynamically create a constructor.
// must be anonymous fn.
var ErrCtor = function() {
// call super
RestError.apply(this, arguments);
this.name = name;
};
util.inherits(ErrCtor, RestError);
// copy over all options to prototype
_.assign(ErrCtor.prototype, d);
// assign display name
ErrCtor.displayName = name;
// store constructor on main exports
module.exports[name] = ErrCtor;
}
/**
* helper function to dynamically apply args
* to a dynamic constructor. magicks.
* @private
* @function makeInstance
* @param {Function} constructor the constructor function
* @param {Function} constructorOpt where to start the error stack trace
* @param {Array} args array of arguments to apply to ctor
* @returns {Object} instance of the ctor
*/
function makeInstance(constructor, constructorOpt, args) {
// pass args to the constructor
function F() {
return constructor.apply(this, args);
}
F.prototype = constructor.prototype;
// new up an instance, and capture stack trace from the
// passed in constructorOpt
var errInstance = new F();
Error.captureStackTrace(errInstance, constructorOpt);
// return the error instance
return errInstance;
}
module.exports = _.assign({}, httpErrors, restErrors, {
// export base classes
HttpError: HttpError,
RestError: RestError,
// export convenience functions
makeConstructor: makeConstructor,
makeErrFromCode: makeErrFromCode,
// deprecated method names, how long do we keep these for?
// restify has already been updated, but what about external consumers?
codeToHttpError: makeErrFromCode,
// built in bunyan serializer
bunyanSerializer: require('./serializer')
});