Skip to content

Commit

Permalink
fix: S3 Protocol allow overwriting metadata on copy
Browse files Browse the repository at this point in the history
  • Loading branch information
fenos committed Nov 5, 2024
1 parent 6b052d8 commit 888eb45
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/storage/backend/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export abstract class StorageBackendAdapter {
* @param version
* @param destination
* @param destinationVersion
* @param metadata
* @param conditions
*/
async copyObject(
Expand All @@ -113,6 +114,7 @@ export abstract class StorageBackendAdapter {
version: string | undefined,
destination: string,
destinationVersion: string | undefined,
metadata?: { cacheControl?: string; mimetype?: string },
conditions?: {
ifMatch?: string
ifNoneMatch?: string
Expand Down
7 changes: 5 additions & 2 deletions src/storage/backend/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,15 @@ export class FileBackend implements StorageBackendAdapter {
* @param version
* @param destination
* @param destinationVersion
* @param metadata
*/
async copyObject(
bucket: string,
source: string,
version: string | undefined,
destination: string,
destinationVersion: string
destinationVersion: string,
metadata: { cacheControl?: string; contentType?: string }
): Promise<Pick<ObjectMetadata, 'httpStatusCode' | 'eTag' | 'lastModified'>> {
const srcFile = path.resolve(this.filePath, withOptionalVersion(`${bucket}/${source}`, version))
const destFile = path.resolve(
Expand All @@ -199,7 +201,8 @@ export class FileBackend implements StorageBackendAdapter {
await fs.ensureFile(destFile)
await fs.copyFile(srcFile, destFile)

await this.setFileMetadata(destFile, await this.getFileMetadata(srcFile))
const originalMetadata = await this.getFileMetadata(srcFile)
await this.setFileMetadata(destFile, Object.assign({}, originalMetadata, metadata))

const fileStat = await fs.lstat(destFile)
const checksum = await fileChecksum(destFile)
Expand Down
4 changes: 4 additions & 0 deletions src/storage/backend/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ export class S3Backend implements StorageBackendAdapter {
* @param version
* @param destination
* @param destinationVersion
* @param metadata
* @param conditions
*/
async copyObject(
Expand All @@ -217,6 +218,7 @@ export class S3Backend implements StorageBackendAdapter {
version: string | undefined,
destination: string,
destinationVersion: string | undefined,
metadata?: { cacheControl?: string; mimetype?: string },
conditions?: {
ifMatch?: string
ifNoneMatch?: string
Expand All @@ -233,6 +235,8 @@ export class S3Backend implements StorageBackendAdapter {
CopySourceIfNoneMatch: conditions?.ifNoneMatch,
CopySourceIfModifiedSince: conditions?.ifModifiedSince,
CopySourceIfUnmodifiedSince: conditions?.ifUnmodifiedSince,
ContentType: metadata?.mimetype,
CacheControl: metadata?.cacheControl,
})
const data = await this.client.send(command)
return {
Expand Down
9 changes: 8 additions & 1 deletion src/storage/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ interface CopyObjectParams {
owner?: string
copyMetadata?: boolean
upsert?: boolean
metadata?: {
cacheControl?: string
mimetype?: string
}
conditions?: {
ifMatch?: string
ifNoneMatch?: string
Expand Down Expand Up @@ -283,6 +287,7 @@ export class ObjectStorage {
conditions,
copyMetadata,
upsert,
metadata: userMetadata,
}: CopyObjectParams) {
mustBeValidKey(destinationKey)

Expand Down Expand Up @@ -323,6 +328,7 @@ export class ObjectStorage {
originObject.version,
s3DestinationKey,
newVersion,
userMetadata,
conditions
)

Expand All @@ -343,12 +349,13 @@ export class ObjectStorage {
}
)

const destinationMetadata = Object.assign({}, metadata || {}, userMetadata || {})
const destinationObject = await db.upsertObject({
...originObject,
bucket_id: destinationBucket,
name: destinationKey,
owner,
metadata,
metadata: destinationMetadata,
user_metadata: copyMetadata ? originObject.user_metadata : undefined,
version: newVersion,
})
Expand Down
4 changes: 4 additions & 0 deletions src/storage/protocols/s3/s3-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1029,6 +1029,10 @@ export class S3ProtocolHandler {
ifModifiedSince: command.CopySourceIfModifiedSince,
ifUnmodifiedSince: command.CopySourceIfUnmodifiedSince,
},
metadata: {
cacheControl: command.CacheControl,
mimetype: command.ContentType,
},
copyMetadata: command.MetadataDirective === 'COPY',
})

Expand Down
25 changes: 25 additions & 0 deletions src/test/s3-protocol.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,31 @@ describe('S3 Protocol', () => {
expect(resp.CopyObjectResult?.ETag).toBeTruthy()
})

it('will copy an object overwriting the metadata', async () => {
const bucketName = await createBucket(client)
await uploadFile(client, bucketName, 'test-copy-1.jpg', 1)

const copyObjectCommand = new CopyObjectCommand({
Bucket: bucketName,
Key: 'test-copied-2.png',
CopySource: `${bucketName}/test-copy-1.jpg`,
ContentType: 'image/png',
CacheControl: 'max-age=2009',
})

const resp = await client.send(copyObjectCommand)
expect(resp.CopyObjectResult?.ETag).toBeTruthy()

const headObjectCommand = new HeadObjectCommand({
Bucket: bucketName,
Key: 'test-copied-2.png',
})

const headObj = await client.send(headObjectCommand)
expect(headObj.ContentType).toBe('image/png')
expect(headObj.CacheControl).toBe('max-age=2009')
})

it('will not be able to copy an object that doesnt exists', async () => {
const bucketName1 = await createBucket(client)
await uploadFile(client, bucketName1, 'test-copy-1.jpg', 1)
Expand Down

0 comments on commit 888eb45

Please sign in to comment.