'use strict';
const _ = require('lodash')
// const eyes = require('eyes'), p = eyes.inspect.bind(eyes);
/**
* Base class for [`ClientError`]{@link module:classeur-api-client.ClientError} and [`ServerError`]{@link module:classeur-api-client.ServerError} objects.
* @deprecated You should never need to construct one of these directly; they will be provided to callbacks for REST API operations as the first argument if they occur.
* @extends [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
* @memberof module:classeur-api-client
*/
class ErrorBase extends Error {
/** @lends module:classeur-api-client.ErrorBase */
constructor(error, data, response) {
if ( _.isString(error) ) {
error = new Error(error)
}
if ( ! _.isError(error) ) {
throw new Error(`Expected string or Error object, got ${error} instead`)
}
super(error)
let message = _.replace(this.message, /^\s*Error:\s*/, '')
_.merge(this, data)
_.merge(this, error)
this.message = message
if (response) {
/**
* JSON decoding failures will raise a {@link module:classeur-api-client.ClientError} instead.
* This field is only present if its value could be derived from the error object returned from the REST client.
* @summary The JSON-decoded response sent back from the server with the error.
* @deprecated This is usually not useful; the properties `reason` and `status` are usually of more interest than `response`, and they are directly readable from instances of this class.
* @type {Object?}
* @instance
* @readonly
*/
this.response = response;
}
if ( data ) {
/**
* JSON decoding failures will raise a {@link module:classeur-api-client.ClientError} instead.
* This field is only present if its value could be derived from the error object returned from the REST client.
* @summary The JSON-decoded response-data sent back from the server with the error.
* @deprecated This is usually not useful; the properties `reason` and `status` are usually all that is contained in `data`, and they are directly readable from instances of this class.
* @type {Object?}
* @instance
* @readonly
*/
this.data = data
}
if ( _.property('body.reason')(error) ) {
/**
* This is usually the best source for detailed failure/debugging information.
* This field is only present if its value could be derived from the error object returned from the REST client.
* @summary Information sent back by the server indicating why a request failed.
* @type {String?}
* @instance
* @readonly
*/
this.reason = error.body.reason
}
if ( _.property('body.error')(error) ) {
/**
* This field is only present if its value could be derived from the error object returned from the REST client.
* Usually a short word or phrase explaining what a given error code means in HTTP convention.
* @summary English version of [ServerError#status]{@link module:classeur-api-client.ServerError#status}.
* @type {String?}
* @instance
* @readonly
*/
this.shortReason = error.body.error
}
}
}
// Bizarre link formatting for the @extends argument is due to jsdoc parser limitations:
// it doesn't support spaces in @extends, even if those spaces are in a @link location field.
/**
* Object representation of an error that occurred on the server while performing a REST API operation.
* This is a thin wrapper around a [flashheart](https://github.com/bbc/flashheart) error object, and exists as a separate class only to deliniate server-caused errors from client-caused errors.
* @deprecated You should never need to construct one of these directly; they will be provided to callbacks for REST API operations as the first argument if they occur.
* @extends [ErrorBase](./module-classeur-api-client.ErrorBase.html)
* @memberof module:classeur-api-client
* @see The [flashheart documentation](https://github.com/bbc/flashheart#errors) for more info on possible Error contents.
* @example
* const ServerError = require('classeur-api-client').ServerError;
* myClient.getFile('nonexistent', (error) => {
* if ( error instanceof ServerError ) {
* console.log(error.message); // e.g. 'Server error: Received HTTP code 403 for GET https:// ...'
* console.log(error.reason); // e.g. 'file_is_not_readable'
* console.log(error.status); // e.g. 403
* throw error;
* }
* });
*/
class ServerError extends ErrorBase {
/** @lends module:classeur-api-client.ServerError */
constructor(error, body, response) {
if ( ! _.property('statusCode')(error) ) {
throw new Error(`Expected error object to have 'statusCode' property: ${error}`)
}
if ( ! _.isNumber(error.statusCode) ) {
throw new Error(`Expected error.statusCode to be numeric, got ${error.statusCode} instead`)
}
super(error, body, response)
this.message = 'Server error: ' + this.message
/**
* If headers were returned from the server with the error, this object will contain their contents. It is supplied directly by the flashheart REST client library.
* @memberOf module:classeur-api-client.ServerError
* @member {Object} headers
* @instance
* @readonly
* @see The [flashheart documentation]{@link https://github.com/bbc/flashheart#errors} for more information about this field's contents.
*/
/**
* HTTP status code of the error.
* @type {Number}
* @instance
* @readonly
*/
this.status = error.statusCode
}
};
/**
* Object representation of an error that occurred on the client during or after the processing REST API operation.
* Example causes if a client error (and valid values for the `message` property) include:
* - Request aborted
* - Request timed out
* - Corrupt/non-JSON-decodable content received
*
* ClientError instances are usually thin wrappers around [flashheart](https://github.com/bbc/flashheart) error objects, and exist only to deliniate server-caused errors from client-caused errors. ClientErrors may not contain all/most error fields supplied by flashheart, though, since ClientErrors may occur before error metadata is returned from the server.
* @deprecated You should never need to construct one of these directly; they will be provided to callbacks for REST API operations as the first argument if they occur.
* @extends [ErrorBase](./module-classeur-api-client.ErrorBase.html)
* @memberof module:classeur-api-client
* @see The [flashheart documentation](https://github.com/bbc/flashheart#errors) for more info on possible Error contents.
* @example
* const ClientError = require('classeur-api-client').ClientError;
* myClient.getFile('nonexistent', (error) => {
* if ( error instanceof ClientError ) {
* console.log(error.message); // e.g. 'JSON decoding failed!'
* throw error;
* }
* });
*/
class ClientError extends ErrorBase {
/** @lends module:classeur-api-client.ClientError*/
constructor(error, body, response, clntimeout) {
super(error, body, response)
this.message = 'Client error: ' + this.message
if ( this.message.includes('ETIMEDOUT') ) {
/**
* If a timeout occurred, this property will be set to the timeout threshold that was exceeded, in milliseconds. This property will only be set if a ClientError occurred due to a timeout.
* @type {Number?}
* @instance
* @readonly
*/
this.timeout = clntimeout
}
}
};
module.exports.ServerError = ServerError;
module.exports.ClientError = ClientError;