1 /**
  2  * @fileOverview Error classes.
  3  */
  4 
  5 /**
  6  * @name errors
  7  */
  8 (function () {
  9   var util = require('util'),
 10     CloudError;
 11 
 12   /**
 13    * Base cloud error class.
 14    *
 15    * ## Note
 16    * A ``CloudError`` is generally thrown only on a failed cloud operation,
 17    * so that calling code can make intelligent retry / failure handling
 18    * decisions.
 19    *
 20    * Sunny throws straight ``Error``'s for programming / calling errors
 21    * (e.g., missing required parameters, invalid parameter input). Any
 22    * ``Error`` indicates a code and/or Sunny library error and should be
 23    * fixed.
 24    *
 25    * @param {String}        [message]     Exception message.
 26    * @param {Object}        [options]     Options object.
 27    * @config {Error}        [error]       Underlying error object.
 28    * @config {Object|Array|String}
 29    *                        [types]       List/object of error types.
 30    * @config {HttpResponse} [response]    Offending response object.
 31    * @exports CloudError as errors.CloudError
 32    * @constructor
 33    */
 34   CloudError = function (message, options) {
 35     options = options || {};
 36     message = message || (options.error ? options.error.message : '');
 37 
 38     var self = this,
 39       error = options.error || new Error(),
 40       types = options.types || {},
 41       typesMap = {},
 42       response = options.response || {};
 43 
 44     Error.apply(self, [message]);
 45 
 46     /**
 47      * Error message.
 48      * @name errors.CloudError#message
 49      * @type string
 50      */
 51     self.message = message;
 52 
 53     /**
 54      * HTTP status code (if any).
 55      * @name errors.CloudError#statusCode
 56      * @type number
 57      */
 58     self.statusCode = response.statusCode || null;
 59 
 60     // Patch in other error parts.
 61     self.stack = error.stack;
 62     self.arguments = error.arguments;
 63     self.type = error.type;
 64 
 65     // Set appropriate cloud-specific errors.
 66     self.TYPES = CloudError.TYPES;
 67     if (typeof types === 'string') {
 68       // Convert string.
 69       self._types = {};
 70       self._types[types] = true;
 71     } else if (Array.isArray(types)) {
 72       // Convert array.
 73       types.forEach(function (key) {
 74         typesMap[key] = true;
 75       });
 76       self._types = typesMap;
 77     } else {
 78       // Already an object.
 79       self._types = types;
 80     }
 81   };
 82 
 83   util.inherits(CloudError, Error);
 84 
 85   /** Prototype of all available errors. */
 86   CloudError.TYPES = (function (keys) {
 87     var types = {};
 88     keys.forEach(function (key) {
 89       // Bind key and value to string for object.
 90       types[key] = key;
 91     });
 92     return types;
 93   }([
 94     'NOT_FOUND',
 95     'NOT_EMPTY',
 96     'INVALID_NAME',
 97     'NOT_OWNER',
 98     'ALREADY_OWNED_BY_YOU'
 99   ]));
100 
101   /**
102    * Return true if error is of this type.
103    * @private
104    */
105   CloudError.prototype._is = function (errorType) {
106     if (!errorType || this.TYPES[errorType] !== errorType) {
107       throw new Error("Unknown error type: " + errorType);
108     }
109 
110     return this._types[errorType] === true;
111   };
112 
113  /**#@+
114   * @returns {boolean} True if given error type.
115   */
116   CloudError.prototype.isNotFound = function () {
117     return this._is(this.TYPES.NOT_FOUND);
118   };
119   CloudError.prototype.isNotEmpty = function () {
120     return this._is(this.TYPES.NOT_EMPTY);
121   };
122   CloudError.prototype.isInvalidName = function () {
123     return this._is(this.TYPES.INVALID_NAME);
124   };
125   CloudError.prototype.isNotOwner = function () {
126     return this._is(this.TYPES.NOT_OWNER);
127   };
128   CloudError.prototype.isAlreadyOwnedByYou = function () {
129     return this._is(this.TYPES.ALREADY_OWNED_BY_YOU);
130   };
131  /**#@-*/
132 
133   module.exports.CloudError = CloudError;
134 }());
135