Merge pull request #2291 from transloadit/more-network-errors

Transloadit: Add NetworkError handling
This commit is contained in:
Artur Paikin 2020-06-13 03:34:21 +02:00 committed by GitHub
commit dddb2a1f43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 44 deletions

View file

@ -1,7 +1,7 @@
'use strict'
const AuthError = require('./AuthError')
const NetworkError = require('@uppy/utils/lib/NetworkError')
const fetchWithNetworkError = require('@uppy/utils/lib/fetchWithNetworkError')
// Remove the trailing slash so we can always safely append /xyz.
function stripSlash (url) {
@ -134,18 +134,11 @@ module.exports = class RequestClient {
get (path, skipPostResponse) {
return new Promise((resolve, reject) => {
this.preflightAndHeaders(path).then((headers) => {
fetch(this._getUrl(path), {
fetchWithNetworkError(this._getUrl(path), {
method: 'get',
headers: headers,
credentials: 'same-origin'
})
.catch((err) => {
if (err.name === 'AbortError') {
throw err
} else {
throw new NetworkError(err)
}
})
.then(this._getPostResponseFunc(skipPostResponse))
.then((res) => this._json(res).then(resolve))
.catch((err) => {
@ -159,19 +152,12 @@ module.exports = class RequestClient {
post (path, data, skipPostResponse) {
return new Promise((resolve, reject) => {
this.preflightAndHeaders(path).then((headers) => {
fetch(this._getUrl(path), {
fetchWithNetworkError(this._getUrl(path), {
method: 'post',
headers: headers,
credentials: 'same-origin',
body: JSON.stringify(data)
})
.catch((err) => {
if (err.name === 'AbortError') {
throw err
} else {
throw new NetworkError(err)
}
})
.then(this._getPostResponseFunc(skipPostResponse))
.then((res) => this._json(res).then(resolve))
.catch((err) => {
@ -185,19 +171,12 @@ module.exports = class RequestClient {
delete (path, data, skipPostResponse) {
return new Promise((resolve, reject) => {
this.preflightAndHeaders(path).then((headers) => {
fetch(`${this.hostname}/${path}`, {
fetchWithNetworkError(`${this.hostname}/${path}`, {
method: 'delete',
headers: headers,
credentials: 'same-origin',
body: data ? JSON.stringify(data) : null
})
.catch((err) => {
if (err.name === 'AbortError') {
throw err
} else {
throw new NetworkError(err)
}
})
.then(this._getPostResponseFunc(skipPostResponse))
.then((res) => this._json(res).then(resolve))
.catch((err) => {

View file

@ -2,6 +2,8 @@ const io = requireSocketIo
const Emitter = require('component-emitter')
const has = require('@uppy/utils/lib/hasProperty')
const parseUrl = require('./parseUrl')
const NetworkError = require('@uppy/utils/lib/NetworkError')
const fetchWithNetworkError = require('@uppy/utils/lib/fetchWithNetworkError')
// Lazy load socket.io to avoid a console error
// in IE 10 when the Transloadit plugin is not used.
@ -78,6 +80,12 @@ class TransloaditAssembly extends Emitter {
this.emit('connect')
})
socket.on('connect_failed', () => {
this._onError(new NetworkError('Transloadit Socket.io connection error'))
this.socket = null
})
socket.on('error', () => {
socket.disconnect()
this.socket = null
@ -143,7 +151,7 @@ class TransloaditAssembly extends Emitter {
* 'status'.
*/
_fetchStatus ({ diff = true } = {}) {
return fetch(this.status.assembly_ssl_url)
return fetchWithNetworkError(this.status.assembly_ssl_url)
.then((response) => response.json())
.then((status) => {
// Avoid updating if we closed during this request's lifetime.
@ -156,6 +164,7 @@ class TransloaditAssembly extends Emitter {
this.status = status
}
})
.catch((err) => this._onError(err))
}
update () {

View file

@ -1,3 +1,5 @@
const fetchWithNetworkError = require('@uppy/utils/lib/fetchWithNetworkError')
/**
* A Barebones HTTP API client for Transloadit.
*/
@ -38,23 +40,25 @@ module.exports = class Client {
data.append('num_expected_upload_files', expectedFiles)
const url = `${this.opts.service}/assemblies`
return fetch(url, {
return fetchWithNetworkError(url, {
method: 'post',
headers: this._headers,
body: data
}).then((response) => response.json()).then((assembly) => {
if (assembly.error) {
const error = new Error(assembly.error)
error.details = assembly.message
error.assembly = assembly
if (assembly.assembly_id) {
error.details += ' ' + `Assembly ID: ${assembly.assembly_id}`
})
.then((response) => response.json()).then((assembly) => {
if (assembly.error) {
const error = new Error(assembly.error)
error.details = assembly.message
error.assembly = assembly
if (assembly.assembly_id) {
error.details += ' ' + `Assembly ID: ${assembly.assembly_id}`
}
throw error
}
throw error
}
return assembly
}).catch((err) => this._reportError(err, { url, type: 'API_ERROR' }))
return assembly
})
.catch((err) => this._reportError(err, { url, type: 'API_ERROR' }))
}
/**
@ -66,7 +70,7 @@ module.exports = class Client {
reserveFile (assembly, file) {
const size = encodeURIComponent(file.size)
const url = `${assembly.assembly_ssl_url}/reserve_file?size=${size}`
return fetch(url, { method: 'post', headers: this._headers })
return fetchWithNetworkError(url, { method: 'post', headers: this._headers })
.then((response) => response.json())
.catch((err) => this._reportError(err, { assembly, file, url, type: 'API_ERROR' }))
}
@ -88,7 +92,7 @@ module.exports = class Client {
const qs = `size=${size}&filename=${filename}&fieldname=${fieldname}&s3Url=${uploadUrl}`
const url = `${assembly.assembly_ssl_url}/add_file?${qs}`
return fetch(url, { method: 'post', headers: this._headers })
return fetchWithNetworkError(url, { method: 'post', headers: this._headers })
.then((response) => response.json())
.catch((err) => this._reportError(err, { assembly, file, url, type: 'API_ERROR' }))
}
@ -100,7 +104,7 @@ module.exports = class Client {
*/
cancelAssembly (assembly) {
const url = assembly.assembly_ssl_url
return fetch(url, { method: 'delete', headers: this._headers })
return fetchWithNetworkError(url, { method: 'delete', headers: this._headers })
.then((response) => response.json())
.catch((err) => this._reportError(err, { url, type: 'API_ERROR' }))
}
@ -111,7 +115,7 @@ module.exports = class Client {
* @param {string} url The status endpoint of the assembly.
*/
getAssemblyStatus (url) {
return fetch(url, { headers: this._headers })
return fetchWithNetworkError(url, { headers: this._headers })
.then((response) => response.json())
.catch((err) => this._reportError(err, { url, type: 'STATUS_ERROR' }))
}
@ -121,7 +125,7 @@ module.exports = class Client {
? `${err.message} (${err.details})`
: err.message
return fetch('https://status.transloadit.com/client_error', {
return fetchWithNetworkError('https://status.transloadit.com/client_error', {
method: 'post',
body: JSON.stringify({
endpoint,
@ -131,7 +135,8 @@ module.exports = class Client {
client: this.opts.client,
error: message
})
}).then((response) => response.json())
})
.then((response) => response.json())
}
_reportError (err, params) {

View file

@ -0,0 +1,15 @@
const NetworkError = require('@uppy/utils/lib/NetworkError')
/**
* Wrapper around window.fetch that throws a NetworkError when appropriate
*/
module.exports = function fetchWithNetworkError (...options) {
return fetch(...options)
.catch((err) => {
if (err.name === 'AbortError') {
throw err
} else {
throw new NetworkError(err)
}
})
}

View file

@ -753,6 +753,18 @@ uppy.on('upload-error', (file, error, response) => {
})
```
If the error is related to network conditions — endpoint unreachable due to firewall or ISP blockage, for instance — the error will have `error.isNetworkError` property set to `true`. Heres how you can check for network errors:
``` javascript
uppy.on('upload-error', (file, error, response) => {
if (error.isNetworkError) {
// Let your users know that file upload could have failed
// due to firewall or ISP issues
alertUserAboutPossibleFirewallOrISPIssues(error)
}
})
```
### `upload-retry`
Fired when an upload has been retried (after an error, for example):