From da4695177165a78c6e60d9cd6c862a6a9fe0820e Mon Sep 17 00:00:00 2001 From: Prakash Date: Fri, 20 Mar 2026 18:11:22 +0530 Subject: [PATCH] @uppy/aws-s3: wrong upload meta in `upload-success` event (#6234) right now whenever you upload any file through multipart upload you'll find empty body returned in `upload-success` event cause : response from completeMultipartRequest returned in CamelCase which was not parsed correctly ```xml https://testbucketnewfix.s3.eu-north-1.amazonaws.com/13febe01-8d5b-4ed3-bda1-9620fc37c694-VSCode-darwin-universal.dmgtestbucketnewfix13febe01-8d5b-4ed3-bda1-9620fc37c694-VSCode-darwin-universal.dmg"be4129821e06a893b453f1373995202b-49"Wjr7mcJOfU0=FULL_OBJECT ``` --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- packages/@uppy/aws-s3/src/s3-client/S3.ts | 31 +++++++++++++------ packages/@uppy/aws-s3/src/s3-client/index.ts | 1 - packages/@uppy/aws-s3/src/s3-client/types.ts | 28 ----------------- .../aws-s3/tests/s3-client/minio.test.js | 6 ++++ 4 files changed, 27 insertions(+), 39 deletions(-) diff --git a/packages/@uppy/aws-s3/src/s3-client/S3.ts b/packages/@uppy/aws-s3/src/s3-client/S3.ts index 676cf02b7..3436095b2 100644 --- a/packages/@uppy/aws-s3/src/s3-client/S3.ts +++ b/packages/@uppy/aws-s3/src/s3-client/S3.ts @@ -621,25 +621,36 @@ class S3mini { const parsed = U.parseXml(await res.text()) as Record if (parsed && typeof parsed === 'object') { - // Check for both cases + // Check for both cases (camelCase from our parser, PascalCase from S3) const result = parsed.completeMultipartUploadResult || parsed.CompleteMultipartUploadResult || parsed if (result && typeof result === 'object') { - const resultObj = result as Record + const r = result as Record - // Handle ETag in all its variations - const etag = resultObj.ETag || resultObj.eTag || resultObj.etag - if (etag && typeof etag === 'string') { - return { - ...resultObj, - etag: U.sanitizeETag(etag), - } as IT.CompleteMultipartUploadResult + // S3 returns PascalCase (Location, Bucket, Key, ETag). + // Normalize to lowercase for our type interface. + const resultLocation = (r.Location || r.location) as string | undefined + const resultBucket = (r.Bucket || r.bucket) as string | undefined + const resultKey = (r.Key || r.key) as string | undefined + const rawEtag = (r.ETag || r.eTag || r.etag) as string | undefined + + if (!resultLocation || !resultKey) { + throw new Error( + `${C.ERROR_PREFIX}CompleteMultipartUpload response missing Location or Key: ${JSON.stringify(r)}`, + ) } - return result as IT.CompleteMultipartUploadResult + const etag = rawEtag ? U.sanitizeETag(rawEtag) : '' + + return { + location: resultLocation, + bucket: resultBucket ?? '', + key: resultKey, + etag, + } satisfies IT.CompleteMultipartUploadResult } } diff --git a/packages/@uppy/aws-s3/src/s3-client/index.ts b/packages/@uppy/aws-s3/src/s3-client/index.ts index 5c2e6ad7c..719088756 100644 --- a/packages/@uppy/aws-s3/src/s3-client/index.ts +++ b/packages/@uppy/aws-s3/src/s3-client/index.ts @@ -4,7 +4,6 @@ export { S3mini } from './S3.js' export type { CompleteMultipartUploadResult, ErrorWithCode, - ListMultipartUploadResponse, S3Config, UploadPart, } from './types.js' diff --git a/packages/@uppy/aws-s3/src/s3-client/types.ts b/packages/@uppy/aws-s3/src/s3-client/types.ts index 227efd3ab..da1082919 100644 --- a/packages/@uppy/aws-s3/src/s3-client/types.ts +++ b/packages/@uppy/aws-s3/src/s3-client/types.ts @@ -85,30 +85,6 @@ export interface CompleteMultipartUploadResult { bucket: string key: string etag: string - eTag: string // for backward compatibility - ETag: string // for backward compatibility -} - -export interface ListMultipartUploadSuccess { - listMultipartUploadsResult: { - bucket: string - key: string - uploadId: string - size?: number - mtime?: Date - etag?: string - eTag?: string // for backward compatibility - parts: UploadPart[] - isTruncated: boolean - uploads: UploadPart[] - } -} - -export interface MultipartUploadError { - error: { - code: string - message: string - } } export interface ErrorWithCode { @@ -116,10 +92,6 @@ export interface ErrorWithCode { cause?: { code?: string } } -export type ListMultipartUploadResponse = - | ListMultipartUploadSuccess - | MultipartUploadError - export type HttpMethod = 'POST' | 'GET' | 'HEAD' | 'PUT' | 'DELETE' export type XmlValue = string | XmlMap | boolean | number | null diff --git a/packages/@uppy/aws-s3/tests/s3-client/minio.test.js b/packages/@uppy/aws-s3/tests/s3-client/minio.test.js index 493a88dd9..c524a830b 100644 --- a/packages/@uppy/aws-s3/tests/s3-client/minio.test.js +++ b/packages/@uppy/aws-s3/tests/s3-client/minio.test.js @@ -98,6 +98,9 @@ const minioSpecific = (bucket) => { uploaded, ]) expect(result.etag).toBeDefined() + expect(result.location).toBeDefined() + expect(result.location).toContain(key) + expect(result.key).toBe(key) await s3client.deleteObject(key) }) @@ -223,6 +226,9 @@ const minioSpecific = (bucket) => { const result = await s3.completeMultipartUpload(key, uploadId, [uploaded]) expect(result.etag).toBeDefined() + expect(result.location).toBeDefined() + expect(result.location).toContain(key) + expect(result.key).toBe(key) await s3.deleteObject(key) })