diff --git a/packages/@uppy/aws-s3/src/index.js b/packages/@uppy/aws-s3/src/index.js index d0cb777d8..9f5f6884f 100644 --- a/packages/@uppy/aws-s3/src/index.js +++ b/packages/@uppy/aws-s3/src/index.js @@ -26,10 +26,8 @@ */ const { BasePlugin } = require('@uppy/core') -const Translator = require('@uppy/utils/lib/Translator') const { RateLimitedQueue, internalRateLimitedQueue } = require('@uppy/utils/lib/RateLimitedQueue') const settle = require('@uppy/utils/lib/settle') -const hasProperty = require('@uppy/utils/lib/hasProperty') const { RequestClient } = require('@uppy/companion-client') const MiniXHRUpload = require('./MiniXHRUpload') const isXml = require('./isXml') @@ -62,12 +60,49 @@ function assertServerError (res) { return res } +function validateParameters (file, params) { + const valid = params != null + && typeof params.url === 'string' + && (typeof params.fields === 'object' || params.fields == null) + + if (!valid) { + const err = new TypeError(`AwsS3: got incorrect result from 'getUploadParameters()' for file '${file.name}', expected an object '{ url, method, fields, headers }' but got '${JSON.stringify(params)}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`) + throw err + } + + const methodIsValid = params.method == null || /^p(u|os)t$/i.test(params.method) + + if (!methodIsValid) { + const err = new TypeError(`AwsS3: got incorrect method from 'getUploadParameters()' for file '${file.name}', expected 'put' or 'post' but got '${params.method}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`) + throw err + } +} + +// Get the error data from a failed XMLHttpRequest instance. +// `content` is the S3 response as a string. +// `xhr` is the XMLHttpRequest instance. +function defaultGetResponseError (content, xhr) { + // If no response, we don't have a specific error message, use the default. + if (!isXml(content, xhr)) { + return undefined + } + const error = getXmlValue(content, 'Message') + return new Error(error) +} + // warning deduplication flag: see `getResponseData()` XHRUpload option definition let warnedSuccessActionStatus = false module.exports = class AwsS3 extends BasePlugin { + // eslint-disable-next-line global-require static VERSION = require('../package.json').version + #client + + #requests + + #uploader + constructor (uppy, opts) { super(uppy, opts) this.type = 'uploader' @@ -89,9 +124,8 @@ module.exports = class AwsS3 extends BasePlugin { this.opts = { ...defaultOptions, ...opts } - this.client = new RequestClient(uppy, opts) - this.handleUpload = this.handleUpload.bind(this) - this.requests = new RateLimitedQueue(this.opts.limit) + this.#client = new RequestClient(uppy, opts) + this.#requests = new RateLimitedQueue(this.opts.limit) } getUploadParameters (file) { @@ -108,31 +142,11 @@ module.exports = class AwsS3 extends BasePlugin { ) const query = new URLSearchParams({ filename, type, ...metadata }) - return this.client.get(`s3/params?${query}`) + return this.#client.get(`s3/params?${query}`) .then(assertServerError) } - validateParameters (file, params) { - const valid = typeof params === 'object' && params - && typeof params.url === 'string' - && (typeof params.fields === 'object' || params.fields == null) - - if (!valid) { - const err = new TypeError(`AwsS3: got incorrect result from 'getUploadParameters()' for file '${file.name}', expected an object '{ url, method, fields, headers }' but got '${JSON.stringify(params)}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`) - console.error(err) - throw err - } - - const methodIsValid = params.method == null || /^(put|post)$/i.test(params.method) - - if (!methodIsValid) { - const err = new TypeError(`AwsS3: got incorrect method from 'getUploadParameters()' for file '${file.name}', expected 'put' or 'post' but got '${params.method}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`) - console.error(err) - throw err - } - } - - handleUpload (fileIDs) { + #handleUpload = (fileIDs) => { /** * keep track of `getUploadParameters()` responses * so we can cancel the calls individually using just a file ID @@ -143,9 +157,7 @@ module.exports = class AwsS3 extends BasePlugin { function onremove (file) { const { id } = file - if (hasProperty(paramsPromises, id)) { - paramsPromises[id].abort() - } + paramsPromises[id]?.abort() } this.uppy.on('file-removed', onremove) @@ -154,7 +166,7 @@ module.exports = class AwsS3 extends BasePlugin { this.uppy.emit('upload-started', file) }) - const getUploadParameters = this.requests.wrapPromiseFunction((file) => { + const getUploadParameters = this.#requests.wrapPromiseFunction((file) => { return this.opts.getUploadParameters(file) }) @@ -166,7 +178,7 @@ module.exports = class AwsS3 extends BasePlugin { delete paramsPromises[id] const file = this.uppy.getFile(id) - this.validateParameters(file, params) + validateParameters(file, params) const { method = 'post', @@ -190,23 +202,22 @@ module.exports = class AwsS3 extends BasePlugin { xhrUpload: xhrOpts, }) - return this._uploader.uploadFile(file.id, index, numberOfFiles) + return this.#uploader.uploadFile(file.id, index, numberOfFiles) }).catch((error) => { delete paramsPromises[id] const file = this.uppy.getFile(id) this.uppy.emit('upload-error', file, error) }) - })).then((settled) => { + })).finally(() => { // cleanup. this.uppy.off('file-removed', onremove) - return settled }) } install () { const { uppy } = this - this.uppy.addUploader(this.handleUpload) + uppy.addUploader(this.#handleUpload) // Get the response data from a successful XMLHttpRequest instance. // `content` is the S3 response as a string. @@ -247,24 +258,12 @@ module.exports = class AwsS3 extends BasePlugin { } } - // Get the error data from a failed XMLHttpRequest instance. - // `content` is the S3 response as a string. - // `xhr` is the XMLHttpRequest instance. - function defaultGetResponseError (content, xhr) { - // If no response, we don't have a specific error message, use the default. - if (!isXml(content, xhr)) { - return - } - const error = getXmlValue(content, 'Message') - return new Error(error) - } - const xhrOptions = { fieldName: 'file', responseUrlFieldName: 'location', timeout: this.opts.timeout, // Share the rate limiting queue with XHRUpload. - [internalRateLimitedQueue]: this.requests, + [internalRateLimitedQueue]: this.#requests, responseType: 'text', getResponseData: this.opts.getResponseData || defaultGetResponseData, getResponseError: defaultGetResponseError, @@ -273,12 +272,12 @@ module.exports = class AwsS3 extends BasePlugin { // Only for MiniXHRUpload, remove once we can depend on XHRUpload directly again xhrOptions.i18n = this.i18n - // Revert to `this.uppy.use(XHRUpload)` once the big comment block at the top of + // Revert to `uppy.use(XHRUpload)` once the big comment block at the top of // this file is solved - this._uploader = new MiniXHRUpload(this.uppy, xhrOptions) + this.#uploader = new MiniXHRUpload(uppy, xhrOptions) } uninstall () { - this.uppy.removeUploader(this.handleUpload) + this.uppy.removeUploader(this.#handleUpload) } }