fix uploadRemoteFile undefined (#4814)

* fix uploadRemoteFile undefined

for some reason, only when testing locally using VITE_COMPANION_URL=https://api2.transloadit.com/companion only
we get this error...
i'm thinking for some reason, the remote file gets serialized into state and then de-serialized again
causing remote.requestClient to be gone
so I solve it by instead storing the request clients in Uppy

fixes #4791

* fix bugs

* remove unrelated space changes

* apply suggestions

* Update Uppy.js

* fix lint

---------

Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
Mikael Finstad 2023-12-13 03:51:06 +08:00 committed by GitHub
parent df36e12fb7
commit eb8a806908
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 47 additions and 13 deletions

View file

@ -27,6 +27,8 @@ export default class MyCustomProvider extends UIPlugin {
pluginId: this.id,
})
uppy.registerRequestClient(MyCustomProvider.name, this.provider)
this.defaultLocale = {
strings: {
pluginNameMyUnsplash: 'MyUnsplash',

View file

@ -878,7 +878,7 @@ export default class AwsS3Multipart extends BasePlugin {
}
this.uppy.on('file-removed', removedHandler)
const uploadPromise = file.remote.requestClient.uploadRemoteFile(
const uploadPromise = this.uppy.getRequestClientForFile(file).uploadRemoteFile(
file,
this.#getCompanionClientArgs(file),
{ signal: controller.signal, getQueue },

View file

@ -281,7 +281,7 @@ export default class AwsS3 extends BasePlugin {
}
this.uppy.on('file-removed', removedHandler)
const uploadPromise = file.remote.requestClient.uploadRemoteFile(
const uploadPromise = this.uppy.getRequestClientForFile(file).uploadRemoteFile(
file,
this.#getCompanionClientArgs(file),
{ signal: controller.signal, getQueue },

View file

@ -1386,6 +1386,30 @@ class Uppy {
}
}
// We need to store request clients by a unique ID, so we can share RequestClient instances across files
// this allows us to do rate limiting and synchronous operations like refreshing provider tokens
// example: refreshing tokens: if each file has their own requestclient,
// we don't have any way to synchronize all requests in order to
// - block all requests
// - refresh the token
// - unblock all requests and allow them to run with a the new access token
// back when we had a requestclient per file, once an access token expired,
// all 6 files would go ahead and refresh the token at the same time
// (calling /refresh-token up to 6 times), which will probably fail for some providers
#requestClientById = new Map()
registerRequestClient(id, client) {
this.#requestClientById.set(id, client)
}
/** @protected */
getRequestClientForFile (file) {
if (!file.remote) throw new Error(`Tried to get RequestClient for a non-remote file ${file.id}`)
const requestClient = this.#requestClientById.get(file.remote.requestClientId)
if (requestClient == null) throw new Error(`requestClientId "${file.remote.requestClientId}" not registered for file "${file.id}"`)
return requestClient
}
/**
* Restore an upload by its ID.
*/

View file

@ -75,6 +75,8 @@ export default class ProviderView extends View {
isSearchVisible: false,
currentSelection: [],
})
this.registerRequestClient()
}
// eslint-disable-next-line class-methods-use-this
@ -399,7 +401,7 @@ export default class ProviderView extends View {
// finished all async operations before we add any file
// see https://github.com/transloadit/uppy/pull/4384
this.plugin.uppy.log('Adding files from a remote provider')
this.plugin.uppy.addFiles(newFiles.map((file) => this.getTagFile(file)))
this.plugin.uppy.addFiles(newFiles.map((file) => this.getTagFile(file, this.requestClientId)))
this.plugin.setPluginState({ filterInput: '' })
messages.forEach(message => this.plugin.uppy.info(message))

View file

@ -54,6 +54,8 @@ export default class SearchProviderView extends View {
// Set default state for the plugin
this.plugin.setPluginState(this.defaultState)
this.registerRequestClient()
}
// eslint-disable-next-line class-methods-use-this

View file

@ -57,6 +57,11 @@ export default class View {
uppy.info({ message, details: error.toString() }, 'error', 5000)
}
registerRequestClient() {
this.requestClientId = this.provider.provider;
this.plugin.uppy.registerRequestClient(this.requestClientId, this.provider)
}
// todo document what is a "tagFile" or get rid of this concept
getTagFile (file) {
const tagFile = {
@ -78,15 +83,10 @@ export default class View {
},
providerName: this.provider.name,
provider: this.provider.provider,
requestClientId: this.requestClientId,
},
}
// all properties on this object get saved into the Uppy store.
// Some users might serialize their store (for example using JSON.stringify),
// or when using Golden Retriever it will serialize state into e.g. localStorage.
// However RequestClient is not serializable so we need to prevent it from being serialized.
Object.defineProperty(tagFile.remote, 'requestClient', { value: this.provider, enumerable: false })
const fileType = getFileType(tagFile)
// TODO Should we just always use the thumbnail URL if it exists?

View file

@ -489,7 +489,7 @@ export default class Tus extends BasePlugin {
}
this.uppy.on('file-removed', removedHandler)
const uploadPromise = file.remote.requestClient.uploadRemoteFile(
const uploadPromise = this.uppy.getRequestClientForFile(file).uploadRemoteFile(
file,
this.#getCompanionClientArgs(file),
{ signal: controller.signal, getQueue },

View file

@ -48,6 +48,7 @@ function getFileNameFromUrl (url) {
const { pathname } = new URL(url)
return pathname.substring(pathname.lastIndexOf('/') + 1)
}
/**
* Url
*
@ -55,6 +56,8 @@ function getFileNameFromUrl (url) {
export default class Url extends UIPlugin {
static VERSION = packageJson.version
static requestClientId = Url.name
constructor (uppy, opts) {
super(uppy, opts)
this.id = this.opts.id || 'Url'
@ -88,6 +91,8 @@ export default class Url extends UIPlugin {
companionHeaders: this.opts.companionHeaders,
companionCookiesRule: this.opts.companionCookiesRule,
})
this.uppy.registerRequestClient(Url.requestClientId, this.client)
}
getMeta (url) {
@ -132,11 +137,10 @@ export default class Url extends UIPlugin {
fileId: url,
url,
},
requestClientId: Url.requestClientId,
},
}
Object.defineProperty(tagFile.remote, 'requestClient', { value: this.client, enumerable: false })
this.uppy.log('[Url] Adding remote file')
try {
return this.uppy.addFile(tagFile)

View file

@ -473,7 +473,7 @@ export default class XHRUpload extends BasePlugin {
}
this.uppy.on('file-removed', removedHandler)
const uploadPromise = file.remote.requestClient.uploadRemoteFile(
const uploadPromise = this.uppy.getRequestClientForFile(file).uploadRemoteFile(
file,
this.#getCompanionClientArgs(file),
{ signal: controller.signal, getQueue },