diff --git a/CHANGELOG.md b/CHANGELOG.md index 66f43abee..9be5ce9c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -101,9 +101,9 @@ PRs are welcome! Please do open an issue to discuss first if it's a big feature, - [ ] !!! dashboard: Remove the Authorization required tooltip on the authentication screen https://github.com/transloadit/uppy/issues/1425 - [ ] @uppy/companion: investigate 423 and 500 issues with React Native + Url plugin when pause/resuming an upload - [ ] docs: add docs on locales — how to use from NPM and CDN +- [ ] @uppy/transloadit: finish Transloadit-Client header on https://github.com/transloadit/uppy/tree/feature/transloadit-client - [ ] companion: reports an error at first sign in. we did a hotfix in https://github.com/transloadit/uppy/pull/1478#issuecomment-485937942 but need a proper fix for that (@ife). Also: what about changing the location of that tooltip? So legit errors also don't block buttons? - ## 1.0 Goals What we need to do to release Uppy 1.0 diff --git a/packages/@uppy/transloadit/src/Client.js b/packages/@uppy/transloadit/src/Client.js index 031abfd0e..520ace9a5 100644 --- a/packages/@uppy/transloadit/src/Client.js +++ b/packages/@uppy/transloadit/src/Client.js @@ -4,6 +4,8 @@ module.exports = class Client { constructor (opts = {}) { this.opts = opts + + this._reportError = this._reportError.bind(this) } /** @@ -31,7 +33,8 @@ module.exports = class Client { }) data.append('num_expected_upload_files', expectedFiles) - return fetch(`${this.opts.service}/assemblies`, { + const url = `${this.opts.service}/assemblies` + return fetch(url, { method: 'post', body: data }).then((response) => response.json()).then((assembly) => { @@ -43,7 +46,7 @@ module.exports = class Client { } return assembly - }) + }).catch((err) => this._reportError(err, { url, type: 'API_ERROR' })) } /** @@ -54,8 +57,10 @@ module.exports = class Client { */ reserveFile (assembly, file) { const size = encodeURIComponent(file.size) - return fetch(`${assembly.assembly_ssl_url}/reserve_file?size=${size}`, { method: 'post' }) + const url = `${assembly.assembly_ssl_url}/reserve_file?size=${size}` + return fetch(url, { method: 'post' }) .then((response) => response.json()) + .catch((err) => this._reportError(err, { assembly, file, url, type: 'API_ERROR' })) } /** @@ -69,13 +74,15 @@ module.exports = class Client { return Promise.reject(new Error('File does not have an `uploadURL`.')) } const size = encodeURIComponent(file.size) - const url = encodeURIComponent(file.uploadURL) + const uploadUrl = encodeURIComponent(file.uploadURL) const filename = encodeURIComponent(file.name) const fieldname = 'file' - const qs = `size=${size}&filename=${filename}&fieldname=${fieldname}&s3Url=${url}` - return fetch(`${assembly.assembly_ssl_url}/add_file?${qs}`, { method: 'post' }) + const qs = `size=${size}&filename=${filename}&fieldname=${fieldname}&s3Url=${uploadUrl}` + const url = `${assembly.assembly_ssl_url}/add_file?${qs}` + return fetch(url, { method: 'post' }) .then((response) => response.json()) + .catch((err) => this._reportError(err, { assembly, file, url, type: 'API_ERROR' })) } /** @@ -84,8 +91,10 @@ module.exports = class Client { * @param {object} assembly */ cancelAssembly (assembly) { - return fetch(assembly.assembly_ssl_url, { method: 'delete' }) + const url = assembly.assembly_ssl_url + return fetch(url, { method: 'delete' }) .then((response) => response.json()) + .catch((err) => this._reportError(err, { url, type: 'API_ERROR' })) } /** @@ -96,5 +105,46 @@ module.exports = class Client { getAssemblyStatus (url) { return fetch(url) .then((response) => response.json()) + .catch((err) => this._reportError(err, { url, type: 'STATUS_ERROR' })) + } + + submitError (err, { endpoint, instance, assembly }) { + const message = err.details + ? `${err.message} (${err.details})` + : err.message + + return fetch('https://status.transloadit.com/client_error', { + method: 'post', + body: JSON.stringify({ + endpoint, + instance, + assembly_id: assembly, + agent: typeof navigator !== 'undefined' ? navigator.userAgent : '', + error: message + }) + }).then((response) => response.json()) + } + + _reportError (err, params) { + if (this.opts.errorReporting === false) { + throw err + } + + const opts = { + type: params.type + } + if (params.assembly) { + opts.assembly = params.assembly.assembly_id + opts.instance = params.assembly.instance + } + if (params.url) { + opts.endpoint = params.url + } + + this.submitError(err, opts).catch((_) => { + // not much we can do then is there + }) + + throw err } } diff --git a/packages/@uppy/transloadit/src/index.js b/packages/@uppy/transloadit/src/index.js index 2bcf27b80..4db8cb6c6 100644 --- a/packages/@uppy/transloadit/src/index.js +++ b/packages/@uppy/transloadit/src/index.js @@ -41,6 +41,7 @@ module.exports = class Transloadit extends Plugin { const defaultOptions = { service: 'https://api2.transloadit.com', + errorReporting: true, waitForEncoding: false, waitForMetadata: false, alwaysRunAssembly: false, @@ -64,6 +65,7 @@ module.exports = class Transloadit extends Plugin { this._prepareUpload = this._prepareUpload.bind(this) this._afterUpload = this._afterUpload.bind(this) this._onError = this._onError.bind(this) + this._onTusError = this._onTusError.bind(this) this._onCancelAll = this._onCancelAll.bind(this) this._onFileUploadURLAvailable = this._onFileUploadURLAvailable.bind(this) this._onRestored = this._onRestored.bind(this) @@ -79,7 +81,8 @@ module.exports = class Transloadit extends Plugin { } this.client = new Client({ - service: this.opts.service + service: this.opts.service, + errorReporting: this.opts.errorReporting }) // Contains Assembly instances for in-progress Assemblies. this.activeAssemblies = {} @@ -669,6 +672,17 @@ module.exports = class Transloadit extends Plugin { }) } + _onTusError (err) { + if (err && /^tus: /.test(err.message)) { + const url = err.originalRequest && err.originalRequest.responseURL + ? err.originalRequest.responseURL + : null + this.client.submitError(err, { url, type: 'TUS_ERROR' }).then((_) => { + // if we can't report the error that sucks + }) + } + } + install () { this.uppy.addPreProcessor(this._prepareUpload) this.uppy.addPostProcessor(this._afterUpload) @@ -679,6 +693,9 @@ module.exports = class Transloadit extends Plugin { // Handle cancellation. this.uppy.on('cancel-all', this._onCancelAll) + // For error reporting. + this.uppy.on('upload-error', this._onTusError) + if (this.opts.importFromUploadURLs) { // No uploader needed when importing; instead we take the upload URL from an existing uploader. this.uppy.on('upload-success', this._onFileUploadURLAvailable)