Source: util/errors.ts

  1. /**
  2. * @fileoverview Errors Helper
  3. */
  4. import * as qs from 'querystring';
  5. // ------------------------------------------------------------------------------
  6. // Requirements
  7. // ------------------------------------------------------------------------------
  8. const httpStatusCodes = require('http-status');
  9. const TRACE_ID_HEADER_NAME = 'box-request-id';
  10. // ------------------------------------------------------------------------------
  11. // Typedefs and Callbacks
  12. // ------------------------------------------------------------------------------
  13. /**
  14. * An generic error propagated when the response has caused an error.
  15. * @typedef {Error} Errors~ResponseError
  16. * @property {APIRequest~ResponseObject} response The response object that generated the error
  17. * @property {int} statusCode A shortcut to the status code of the response
  18. */
  19. /**
  20. * Error propagated whenever the SDK is unable to successfully complete an action
  21. * due to an expired access token (and refresh token, if one was provided).
  22. * @typedef {Errors~ResponseError} Errors~AuthError
  23. * @property {boolean} authExpired - always true
  24. */
  25. /**
  26. * Request structure for error objects
  27. * @param {Object} req The request object
  28. * @constructor
  29. * @private
  30. */
  31. class Request {
  32. method: any /* FIXME */;
  33. url: any /* FIXME */;
  34. httpVersion: any /* FIXME */;
  35. headers: any /* FIXME */;
  36. body: any /* FIXME */;
  37. constructor(req: any /* FIXME */) {
  38. this.method = req.method;
  39. if (req.uri) {
  40. this.url = {
  41. protocol: req.uri.protocol,
  42. host: req.uri.host,
  43. path: req.uri.pathname,
  44. query: qs.parse(req.uri.query),
  45. fragment: req.uri.hash,
  46. };
  47. } else {
  48. this.url = null;
  49. }
  50. this.httpVersion = req.response ? req.response.httpVersion : null;
  51. this.headers = req.headers;
  52. this.body = req.body;
  53. }
  54. }
  55. // ------------------------------------------------------------------------------
  56. // Public
  57. // ------------------------------------------------------------------------------
  58. /**
  59. * A Helper for building errors across the SDK. Makes sure that easily-forgotten
  60. * fields aren't missed, and that everything is formatted properly to return to the
  61. * consumer.
  62. *
  63. * @name Errors
  64. * @module box-node-sdk/lib/util/errors
  65. */
  66. export = {
  67. /**
  68. * Build a response error with the given message, and attaching meta data from the
  69. * response data.
  70. *
  71. * @param {?APIRequest~ResponseObject} response - The response returned by an APIRequestManager request
  72. * @param {string} message - the response error message
  73. * @returns {Errors~ResponseError} an error describing the response error
  74. */
  75. buildResponseError(response: any /* FIXME */, message?: string) {
  76. response = response || {};
  77. message = message || 'API Response Error';
  78. var statusCode = response.statusCode;
  79. var statusMessage = httpStatusCodes[statusCode];
  80. var debugID = ''; // Of the form <requestID>.<traceID>, both parts optional
  81. var errorCode;
  82. var errorDescription;
  83. if (response.headers && response.headers[TRACE_ID_HEADER_NAME]) {
  84. // Append trace ID with dot separator — if not present, the dot should be omitted
  85. debugID += `.${response.headers[TRACE_ID_HEADER_NAME]}`;
  86. }
  87. if (response.body) {
  88. if (response.body.request_id) {
  89. // Prepend request ID
  90. debugID = response.body.request_id + debugID;
  91. }
  92. errorCode = response.body.code || response.body.error;
  93. errorDescription =
  94. response.body.message || response.body.error_description;
  95. }
  96. var errorMessage;
  97. if (debugID) {
  98. errorMessage = `${message} [${statusCode} ${statusMessage} | ${debugID}]`;
  99. } else {
  100. errorMessage = `${message} [${statusCode} ${statusMessage}]`;
  101. }
  102. if (errorCode) {
  103. errorMessage += ` ${errorCode}`;
  104. }
  105. if (errorDescription) {
  106. errorMessage += ` - ${errorDescription}`;
  107. }
  108. var responseError: any /* FIXME */ = new Error(errorMessage);
  109. responseError.statusCode = response.statusCode;
  110. responseError.response = response;
  111. responseError.request = response.request
  112. ? new Request(response.request)
  113. : {};
  114. return responseError;
  115. },
  116. /**
  117. * Build an authentication error. {@see Errors~AuthError}
  118. *
  119. * @param {?APIRequest~ResponseObject} response - The response returned by an APIRequestManager request
  120. * @param {string} [message] - Optional message for the error
  121. * @returns {Errors~AuthError} A properly formatted authentication error
  122. */
  123. buildAuthError(response: any /* FIXME */, message?: string) {
  124. message = message || 'Expired Auth: Auth code or refresh token has expired';
  125. var responseError = this.buildResponseError(response, message);
  126. responseError.authExpired = true;
  127. return responseError;
  128. },
  129. /**
  130. * Build the error for an "Unexpected Response" from the API. This is a shortcut for
  131. * responseError built specifically for the 401 UNEXPECTED response case. It
  132. * should be called and the error should be propogated to the consumer
  133. * whenever an unexpected response was recieved from the API.
  134. *
  135. * @param {?APIRequest~ResponseObject} response - The response returned by an APIRequestManager request
  136. * @returns {Errors~ResponseError} an error describing the response error
  137. */
  138. buildUnexpectedResponseError(response: any /* FIXME */) {
  139. return this.buildResponseError(response, 'Unexpected API Response');
  140. },
  141. /**
  142. * Unwrap a Bluebird error and throw it, or just re-throw if the error
  143. * is not a Bluebird error. This is necessary to preserve errors when
  144. * a function is promisified.
  145. * @param {Error} error The error to unwrap
  146. * @returns {void}
  147. * @throws {Error} The unwrapped error
  148. */
  149. unwrapAndThrow(error: any /* FIXME */) {
  150. if (error.cause) {
  151. throw error.cause;
  152. }
  153. throw error;
  154. },
  155. };