Kaynağa Gözat

Propagate `isNetworkError` through error wrappers (#3620)

The `ErrorWithCause` class uses the same signature as the builtin Error
does, but it supports the `{ cause }` option in older environments, and
it propagates the `isNetworkError` property from the underlying error.
Renée Kooi 3 yıl önce
ebeveyn
işleme
b22a812db3

+ 3 - 5
packages/@uppy/aws-s3/src/MiniXHRUpload.js

@@ -4,6 +4,7 @@ const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress')
 const getSocketHost = require('@uppy/utils/lib/getSocketHost')
 const EventTracker = require('@uppy/utils/lib/EventTracker')
 const ProgressTimeout = require('@uppy/utils/lib/ProgressTimeout')
+const ErrorWithCause = require('@uppy/utils/lib/ErrorWithCause')
 const NetworkError = require('@uppy/utils/lib/NetworkError')
 const isNetworkError = require('@uppy/utils/lib/isNetworkError')
 const { internalRateLimitedQueue } = require('@uppy/utils/lib/RateLimitedQueue')
@@ -12,10 +13,7 @@ const { internalRateLimitedQueue } = require('@uppy/utils/lib/RateLimitedQueue')
 function buildResponseError (xhr, error) {
   if (isNetworkError(xhr)) return new NetworkError(error, xhr)
 
-  // TODO: when we drop support for browsers that do not support this syntax, use:
-  // return new Error('Upload error', { cause: error, request: xhr })
-  const err = new Error('Upload error')
-  err.cause = error
+  const err = new ErrorWithCause('Upload error', { cause: error })
   err.request = xhr
   return err
 }
@@ -327,7 +325,7 @@ module.exports = class MiniXHRUpload {
         const resp = errData.response
         const error = resp
           ? opts.getResponseError(resp.responseText, resp)
-          : Object.assign(new Error(errData.error.message), { cause: errData.error })
+          : new ErrorWithCause(errData.error.message, { cause: errData.error })
         this.uppy.emit('upload-error', file, error)
         queuedRequest.done()
         if (this.uploaderEvents[file.id]) {

+ 3 - 3
packages/@uppy/companion-client/src/RequestClient.js

@@ -1,6 +1,7 @@
 'use strict'
 
 const fetchWithNetworkError = require('@uppy/utils/lib/fetchWithNetworkError')
+const ErrorWithCause = require('@uppy/utils/lib/ErrorWithCause')
 const AuthError = require('./AuthError')
 
 // Remove the trailing slash so we can always safely append /xyz.
@@ -87,9 +88,8 @@ module.exports = class RequestClient {
   #errorHandler (method, path) {
     return (err) => {
       if (!err?.isAuthError) {
-        const error = new Error(`Could not ${method} ${this.#getUrl(path)}`)
-        error.cause = err
-        err = error // eslint-disable-line no-param-reassign
+        // eslint-disable-next-line no-param-reassign
+        err = new ErrorWithCause(`Could not ${method} ${this.#getUrl(path)}`, { cause: err })
       }
       return Promise.reject(err)
     }

+ 3 - 3
packages/@uppy/transloadit/src/AssemblyOptions.js

@@ -1,3 +1,5 @@
+const ErrorWithCause = require('@uppy/utils/lib/ErrorWithCause')
+
 /**
  * Check that Assembly parameters are present and include all required fields.
  */
@@ -12,9 +14,7 @@ function validateParams (params) {
       params = JSON.parse(params)
     } catch (err) {
       // Tell the user that this is not an Uppy bug!
-      const error = new Error('Transloadit: The `params` option is a malformed JSON string.')
-      err.cause = err
-      throw error
+      throw new ErrorWithCause('Transloadit: The `params` option is a malformed JSON string.', { cause: err })
     }
   }
 

+ 3 - 6
packages/@uppy/transloadit/src/index.js

@@ -1,4 +1,5 @@
 const hasProperty = require('@uppy/utils/lib/hasProperty')
+const ErrorWithCause = require('@uppy/utils/lib/ErrorWithCause')
 const BasePlugin = require('@uppy/core/lib/BasePlugin')
 const Tus = require('@uppy/tus')
 const Assembly = require('./Assembly')
@@ -17,8 +18,7 @@ function defaultGetAssemblyOptions (file, options) {
 }
 
 const sendErrorToConsole = originalErr => err => {
-  const error = new Error('Failed to send error to the client')
-  error.cause = err
+  const error = new ErrorWithCause('Failed to send error to the client', { cause: err })
   // eslint-ignore-next-line no-console
   console.error(error, originalErr)
 }
@@ -236,10 +236,7 @@ module.exports = class Transloadit extends BasePlugin {
       this.uppy.log(`[Transloadit] Created Assembly ${assemblyID}`)
       return assembly
     }).catch((err) => {
-      const error = new Error(`${this.i18n('creatingAssemblyFailed')}: ${err.message}`)
-      error.cause = err
-      // Reject the promise.
-      throw error
+      throw new ErrorWithCause(`${this.i18n('creatingAssemblyFailed')}: ${err.message}`, { cause: err })
     })
   }
 

+ 13 - 0
packages/@uppy/utils/src/ErrorWithCause.js

@@ -0,0 +1,13 @@
+const hasProperty = require('./hasProperty')
+
+class ErrorWithCause extends Error {
+  constructor (message, options = {}) {
+    super(message)
+    this.cause = options.cause
+    if (this.cause && hasProperty(this.cause, 'isNetworkError')) {
+      this.isNetworkError = this.cause.isNetworkError
+    }
+  }
+}
+
+module.exports = ErrorWithCause

+ 20 - 0
packages/@uppy/utils/src/ErrorWithCause.test.js

@@ -0,0 +1,20 @@
+const ErrorWithCause = require('./ErrorWithCause')
+const NetworkError = require('./NetworkError')
+const isNetworkError = require('./isNetworkError')
+
+describe('ErrorWithCause', () => {
+  it('should support a `{ cause }` option', () => {
+    const cause = new Error('cause')
+    expect(new ErrorWithCause('message').cause).toEqual(undefined)
+    expect(new ErrorWithCause('message', {}).cause).toEqual(undefined)
+    expect(new ErrorWithCause('message', { cause }).cause).toEqual(cause)
+  })
+  it('should propagate isNetworkError', () => {
+    const regularError = new Error('cause')
+    const networkError = new NetworkError('cause')
+    expect(isNetworkError(new ErrorWithCause('message', { cause: networkError }).isNetworkError)).toEqual(true)
+    expect(isNetworkError(new ErrorWithCause('message', { cause: regularError }).isNetworkError)).toEqual(false)
+    expect(isNetworkError(new ErrorWithCause('message', {}).isNetworkError)).toEqual(false)
+    expect(isNetworkError(new ErrorWithCause('message').isNetworkError)).toEqual(false)
+  })
+})