Skip to content

Commit

Permalink
@uppy/aws-s3: allow uploads to fail/succeed independently
Browse files Browse the repository at this point in the history
  • Loading branch information
Murderlon committed Jan 14, 2025
1 parent a2f1b0c commit 4d7448a
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 14 deletions.
53 changes: 40 additions & 13 deletions packages/@uppy/aws-s3/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ describe('AwsS3Multipart', () => {

const signPart = vi.fn(async (file, { partNumber }) => {
return {
url: `https://bucket.s3.us-east-2.amazonaws.com/test/upload/multitest.dat?partNumber=${partNumber}&uploadId=6aeb1980f3fc7ce0b5454d25b71992&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIATEST%2F20210729%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210729T014044Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=test`,
url: `https://bucket.s3.us-east-2.amazonaws.com/test/upload/${file.name}?partNumber=${partNumber}&uploadId=6aeb1980f3fc7ce0b5454d25b71992&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIATEST%2F20210729%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20210729T014044Z&X-Amz-Expires=600&X-Amz-SignedHeaders=host&X-Amz-Signature=test`,
}
})

Expand Down Expand Up @@ -282,38 +282,65 @@ describe('AwsS3Multipart', () => {
expect(awsS3Multipart.opts.uploadPartBytes.mock.calls.length).toEqual(3)
})

it('calls `upload-error` when uploadPartBytes fails after all retries', async () => {
it.only('calls `upload-error` when uploadPartBytes fails after all retries', async () => {
const core = new Core().use(AwsS3Multipart, {
shouldUseMultipart: true,
retryDelays: [10],
createMultipartUpload,
createMultipartUpload: vi.fn((file) => ({
uploadId: '6aeb1980f3fc7ce0b5454d25b71992',
key: `test/upload/${file.name}`,
})),
completeMultipartUpload: vi.fn(async () => ({ location: 'test' })),
abortMultipartUpload: vi.fn(),
signPart,
uploadPartBytes: uploadPartBytes.mockImplementation(() =>
uploadPartBytes: uploadPartBytes.mockImplementation((options) => {
if (options.signature.url.includes('succeed.dat')) {
return new Promise((resolve) => {
// delay until after multitest.dat has failed.
setTimeout(() => resolve({ status: 200 }), 100)
})
}
// eslint-disable-next-line prefer-promise-reject-errors
Promise.reject({ source: { status: 500 } }),
),
return Promise.reject({ source: { status: 500 } })
}),
listParts: undefined as any,
})
const awsS3Multipart = core.getPlugin('AwsS3Multipart')!
const fileSize = 5 * MB + 1 * MB
const mock = vi.fn()
core.on('upload-error', mock)
const awsS3Multipart = core.getPlugin('AwsS3Multipart')!
const uploadErrorMock = vi.fn()
const uploadSuccessMock = vi.fn()
core.on('upload-error', uploadErrorMock)
core.on('upload-success', uploadSuccessMock)

core.addFile({
source: 'vi',
name: 'multitest.dat',
name: 'fail.dat',
type: 'application/octet-stream',
data: new File([new Uint8Array(fileSize)], '', {
type: 'application/octet-stream',
}),
})

core.addFile({
source: 'vi',
name: 'succeed.dat',
type: 'application/octet-stream',
data: new File([new Uint8Array(fileSize)], '', {
type: 'application/octet-stream',
}),
})

await expect(core.upload()).rejects.toEqual({ source: { status: 500 } })
try {
const results = await core.upload()
expect(results!.successful!.length).toEqual(1)
expect(results!.failed!.length).toEqual(1)
} catch {
// Catch Promise.all reject
}

expect(awsS3Multipart.opts.uploadPartBytes.mock.calls.length).toEqual(3)
expect(mock.mock.calls.length).toEqual(1)
expect(awsS3Multipart.opts.uploadPartBytes.mock.calls.length).toEqual(5)
expect(uploadErrorMock.mock.calls.length).toEqual(1)
expect(uploadSuccessMock.mock.calls.length).toEqual(1) // This fails for me becuase upload returned early.
})
})

Expand Down
2 changes: 1 addition & 1 deletion packages/@uppy/aws-s3/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ export default class AwsS3Multipart<
return this.#uploadLocalFile(file)
})

const upload = await Promise.all(promises)
const upload = await Promise.allSettled(promises)
// After the upload is done, another upload may happen with only local files.
// We reset the capability so that the next upload can use resumable uploads.
this.#setResumableUploadsCapability(true)
Expand Down

0 comments on commit 4d7448a

Please sign in to comment.