batch.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. const npm = {
  2. u: require('util'),
  3. os: require('os'),
  4. utils: require('../utils/static')
  5. };
  6. /**
  7. * @class errors.BatchError
  8. * @augments external:Error
  9. * @description
  10. * This type represents all errors rejected by method {@link batch}, except for {@link external:TypeError TypeError}
  11. * when the method receives invalid input parameters.
  12. *
  13. * @property {string} name
  14. * Standard {@link external:Error Error} property - error type name = `BatchError`.
  15. *
  16. * @property {string} message
  17. * Standard {@link external:Error Error} property - the error message.
  18. *
  19. * It represents the message of the first error encountered in the batch, and is a safe
  20. * version of using `first.message`.
  21. *
  22. * @property {string} stack
  23. * Standard {@link external:Error Error} property - the stack trace.
  24. *
  25. * @property {array} data
  26. * Array of objects `{success, result, [origin]}`:
  27. * - `success` = true/false, indicates whether the corresponding value in the input array was resolved.
  28. * - `result` = resolved data, if `success`=`true`, or else the rejection reason.
  29. * - `origin` - set only when failed as a result of an unsuccessful call into the notification callback
  30. * (parameter `cb` of method {@link batch})
  31. *
  32. * The array has the same size as the input one that was passed into method {@link batch}, providing direct mapping.
  33. *
  34. * @property {} stat
  35. * Resolution Statistics.
  36. *
  37. * @property {number} stat.total
  38. * Total number of elements in the batch.
  39. *
  40. * @property {number} stat.succeeded
  41. * Number of resolved values in the batch.
  42. *
  43. * @property {number} stat.failed
  44. * Number of rejected values in the batch.
  45. *
  46. * @property {number} stat.duration
  47. * Time in milliseconds it took to settle all values.
  48. *
  49. * @property {} first
  50. * The very first error within the batch, with support for nested batch results, it is also the same error
  51. * as $[promise.all] would provide.
  52. *
  53. * @see {@link batch}
  54. *
  55. */
  56. class BatchError extends Error {
  57. constructor(result, errors, duration) {
  58. function getErrors() {
  59. const err = new Array(errors.length);
  60. for (let i = 0; i < errors.length; i++) {
  61. err[i] = result[errors[i]].result;
  62. if (err[i] instanceof BatchError) {
  63. err[i] = err[i].getErrors();
  64. }
  65. }
  66. npm.utils.extend(err, '$isErrorList', true);
  67. return err;
  68. }
  69. const e = getErrors();
  70. let first = e[0];
  71. while (first && first.$isErrorList) {
  72. first = first[0];
  73. }
  74. let message;
  75. if (first instanceof Error) {
  76. message = first.message;
  77. } else {
  78. if (typeof first !== 'string') {
  79. first = npm.u.inspect(first);
  80. }
  81. message = first;
  82. }
  83. super(message);
  84. this.name = this.constructor.name;
  85. this.data = result;
  86. // we do not show it within the inspect, because when the error
  87. // happens for a nested result, the output becomes a mess.
  88. this.first = first;
  89. this.stat = {
  90. total: result.length,
  91. succeeded: result.length - e.length,
  92. failed: e.length,
  93. duration: duration
  94. };
  95. this.getErrors = getErrors;
  96. Error.captureStackTrace(this, this.constructor);
  97. }
  98. /**
  99. * @method errors.BatchError.getErrors
  100. * @description
  101. * Returns the complete list of errors only.
  102. *
  103. * It supports nested batch results, presented as a sub-array.
  104. *
  105. * @returns {array}
  106. */
  107. }
  108. /**
  109. * @method errors.BatchError.toString
  110. * @description
  111. * Creates a well-formatted multi-line string that represents the error.
  112. *
  113. * It is called automatically when writing the object into the console.
  114. *
  115. * The output is an abbreviated version of the error, because the complete error
  116. * is often too much for displaying or even logging, as a batch can be of any size.
  117. * Therefore, only errors are rendered from the `data` property, alongside their indexes,
  118. * and only up to the first 5, to avoid polluting the screen or the log file.
  119. *
  120. * @param {number} [level=0]
  121. * Nested output level, to provide visual offset.
  122. *
  123. * @returns {string}
  124. */
  125. BatchError.prototype.toString = function (level) {
  126. level = level > 0 ? parseInt(level) : 0;
  127. const gap0 = npm.utils.messageGap(level),
  128. gap1 = npm.utils.messageGap(level + 1),
  129. gap2 = npm.utils.messageGap(level + 2),
  130. lines = [
  131. 'BatchError {',
  132. gap1 + 'stat: { total: ' + this.stat.total + ', succeeded: ' + this.stat.succeeded +
  133. ', failed: ' + this.stat.failed + ', duration: ' + this.stat.duration + ' }',
  134. gap1 + 'errors: ['
  135. ];
  136. // In order to avoid polluting the error log or the console,
  137. // we limit the log output to the top 5 errors:
  138. const maxErrors = 5;
  139. let counter = 0;
  140. this.data.forEach((d, index) => {
  141. if (!d.success && counter < maxErrors) {
  142. lines.push(gap2 + index + ': ' + npm.utils.formatError(d.result, level + 2));
  143. counter++;
  144. }
  145. });
  146. lines.push(gap1 + ']');
  147. lines.push(gap0 + '}');
  148. return lines.join(npm.os.EOL);
  149. };
  150. npm.utils.addInspection(BatchError, function () {
  151. return this.toString();
  152. });
  153. module.exports = {BatchError};