Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Companion+client stability fixes, error handling and retry #4734

Merged
merged 30 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4f52551
respond with 500 if unhandled upload error
mifi Oct 12, 2023
c8f49a6
add todo
mifi Oct 13, 2023
7bd5042
fix companion download error
mifi Oct 13, 2023
3fa992b
forward 429 from provider
mifi Oct 13, 2023
0cc6bc5
rename fn
mifi Oct 13, 2023
aad9ae9
add a way to test refresh tokens
mifi Oct 13, 2023
863b60e
fix race condtiion with refreshing token
mifi Oct 13, 2023
7124ef4
implement retry and fix socket
mifi Oct 13, 2023
ffec1af
remove unused socket wrapper
mifi Oct 13, 2023
8612d5d
remove useFastRemoteRetry
mifi Oct 13, 2023
81d63fe
fix error handling
mifi Oct 19, 2023
1a75780
remove unneeded logic
mifi Oct 22, 2023
8989c7e
apply review suggestion
mifi Oct 22, 2023
8435dad
retry awaiting for companion socket too
mifi Oct 22, 2023
635fffe
retry whole operation instead of individually
mifi Oct 22, 2023
82679db
improvements
mifi Oct 23, 2023
0e579f7
Update packages/@uppy/utils/src/fetchWithNetworkError.js
mifi Oct 24, 2023
5fba815
Explain requestClient Symbol with comment
Murderlon Oct 25, 2023
3ee9b49
Update packages/@uppy/provider-views/src/View.js
mifi Oct 25, 2023
ca704b1
fixes
mifi Oct 25, 2023
c83e7af
log if trying to refresh with no token
mifi Oct 25, 2023
a0501a9
log whether we got a refresh token
mifi Oct 25, 2023
acbe2eb
dont log retrying when in reality not
mifi Oct 27, 2023
792163f
always prompt: consent
mifi Oct 27, 2023
d073fb9
add signal to post
mifi Oct 27, 2023
fb61f9c
refactor away eventmanager
mifi Oct 27, 2023
db2596d
don't fail with upload-error
mifi Oct 27, 2023
3b52885
fix expect
mifi Oct 30, 2023
cb9a476
add capabilities support
mifi Oct 30, 2023
6d813c9
make requestClient non-enumerable
mifi Oct 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions packages/@uppy/aws-s3-multipart/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import BasePlugin from '@uppy/core/lib/BasePlugin.js'
import { Provider, RequestClient } from '@uppy/companion-client'
import { RequestClient } from '@uppy/companion-client'
import EventManager from '@uppy/utils/lib/EventManager'
import { RateLimitedQueue } from '@uppy/utils/lib/RateLimitedQueue'
import { filterNonFailedFiles, filterFilesToEmitUploadStarted } from '@uppy/utils/lib/fileFilters'
Expand Down Expand Up @@ -854,11 +854,7 @@ export default class AwsS3Multipart extends BasePlugin {

const promises = filesFiltered.map((file) => {
if (file.isRemote) {
// INFO: the url plugin needs to use RequestClient,
// while others use Provider
const Client = file.remote.providerOptions.provider ? Provider : RequestClient
const getQueue = () => this.requests
const client = new Client(this.uppy, file.remote.providerOptions, getQueue)
this.#setResumableUploadsCapability(false)
const controller = new AbortController()

Expand All @@ -867,10 +863,10 @@ export default class AwsS3Multipart extends BasePlugin {
}
this.uppy.on('file-removed', removedHandler)

const uploadPromise = client.uploadRemoteFile(
const uploadPromise = file.remote[Symbol.for('requestClient')].uploadRemoteFile(
file,
this.#getCompanionClientArgs(file),
{ signal: controller.signal },
{ signal: controller.signal, getQueue },
)

this.requests.wrapSyncFunction(() => {
Expand Down
10 changes: 3 additions & 7 deletions packages/@uppy/aws-s3/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import BasePlugin from '@uppy/core/lib/BasePlugin.js'
import AwsS3Multipart from '@uppy/aws-s3-multipart'
import { RateLimitedQueue, internalRateLimitedQueue } from '@uppy/utils/lib/RateLimitedQueue'
import { RequestClient, Provider } from '@uppy/companion-client'
import { RequestClient } from '@uppy/companion-client'
import { filterNonFailedFiles, filterFilesToEmitUploadStarted } from '@uppy/utils/lib/fileFilters'

import packageJson from '../package.json'
Expand Down Expand Up @@ -273,22 +273,18 @@ export default class AwsS3 extends BasePlugin {
if (file.error) throw new Error(file.error)

if (file.isRemote) {
// INFO: the url plugin needs to use RequestClient,
// while others use Provider
const Client = file.remote.providerOptions.provider ? Provider : RequestClient
const getQueue = () => this.#requests
const client = new Client(this.uppy, file.remote.providerOptions, getQueue)
const controller = new AbortController()

const removedHandler = (removedFile) => {
if (removedFile.id === file.id) controller.abort()
}
this.uppy.on('file-removed', removedHandler)

const uploadPromise = client.uploadRemoteFile(
const uploadPromise = file.remote[Symbol.for('requestClient')].uploadRemoteFile(
file,
this.#getCompanionClientArgs(file),
{ signal: controller.signal },
{ signal: controller.signal, getQueue },
)

this.#requests.wrapSyncFunction(() => {
Expand Down
3 changes: 2 additions & 1 deletion packages/@uppy/companion-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
},
"dependencies": {
"@uppy/utils": "workspace:^",
"namespace-emitter": "^2.0.1"
"namespace-emitter": "^2.0.1",
"p-retry": "^6.1.0"
},
"devDependencies": {
"vitest": "^0.34.5"
Expand Down
16 changes: 9 additions & 7 deletions packages/@uppy/companion-client/src/Provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ function isOriginAllowed (origin, allowedOrigin) {
export default class Provider extends RequestClient {
#refreshingTokenPromise

constructor (uppy, opts, getQueue) {
super(uppy, opts, getQueue)
constructor (uppy, opts) {
super(uppy, opts)
this.provider = opts.provider
this.id = this.provider
this.name = this.opts.name || getName(this.id)
Expand Down Expand Up @@ -140,8 +140,7 @@ export default class Provider extends RequestClient {

authWindow.close()
window.removeEventListener('message', handleToken)
this.setAuthToken(data.token)
resolve()
this.setAuthToken(data.token).then(() => resolve()).catch(reject)
}
window.addEventListener('message', handleToken)
})
Expand All @@ -160,21 +159,24 @@ export default class Provider extends RequestClient {
await this.#refreshingTokenPromise

try {
// throw Object.assign(new Error(), { isAuthError: true }) // testing simulate access token expired (to refresh token)
// A better way to test this is for example with Google Drive:
// to test simulate access token expired (leading to a token token refresh),
// see mockAccessTokenExpiredError in companion/drive.
// If you want to test refresh token *and* access token invalid, do this for example with Google Drive:
// While uploading, go to your google account settings,
// "Third-party apps & services", then click "Companion" and "Remove access".

return await super.request(...args)
} catch (err) {
// only handle auth errors (401 from provider), and only handle them if we have a (refresh) token
if (!err.isAuthError || !(await this.#getAuthToken())) throw err
const authTokenAfter = await this.#getAuthToken()
if (!err.isAuthError || !authTokenAfter) throw err

if (this.#refreshingTokenPromise == null) {
// Many provider requests may be starting at once, however refresh token should only be called once.
// Once a refresh token operation has started, we need all other request to wait for this operation (atomically)
this.#refreshingTokenPromise = (async () => {
try {
this.uppy.log(`[CompanionClient] Refreshing expired auth token`, 'info')
const response = await super.request({ path: this.refreshTokenUrl(), method: 'POST' })
await this.setAuthToken(response.uppyAuthToken)
} catch (refreshTokenErr) {
Expand Down
Loading