fix(core): Preserve binary data metadata on S3 copy and rename (#33089)

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Mike Repeć
2026-06-26 10:12:49 +00:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 37a27ba601
commit 4cff812fc7
2 changed files with 33 additions and 6 deletions
@@ -26,8 +26,8 @@ const otherFileId = toFileId(otherWorkflowId, otherExecutionId, otherFileUuid);
const mockBuffer = Buffer.from('Test data');
const mockStream = toStream(mockBuffer);
beforeAll(() => {
vi.restoreAllMocks();
beforeEach(() => {
vi.resetAllMocks();
});
describe('store()', () => {
@@ -112,14 +112,27 @@ describe('getMetadata()', () => {
});
describe('copyByFileId()', () => {
it('should copy by file ID and return the file ID', async () => {
it('should copy by file ID, forwarding source metadata, and return the file ID', async () => {
const mimeType = 'text/plain';
const fileName = 'file.txt';
objectStoreService.getMetadata.mockResolvedValue(
mock<MetadataResponseHeaders>({
'content-length': String(mockBuffer.length),
'content-type': mimeType,
'x-amz-meta-filename': fileName,
}),
);
const targetFileId = await objectStoreManager.copyByFileId(
{ type: 'execution', workflowId, executionId },
fileId,
);
const lastPutCall = objectStoreService.put.mock.lastCall;
expect(targetFileId.startsWith(prefix)).toBe(true);
expect(objectStoreService.get).toHaveBeenCalledWith(fileId, { mode: 'buffer' });
expect(lastPutCall?.[0]).toBe(targetFileId);
expect(lastPutCall?.[2]).toEqual(expect.objectContaining({ mimeType, fileName }));
});
});
@@ -143,13 +156,26 @@ describe('copyByFilePath()', () => {
});
describe('rename()', () => {
it('should rename a file', async () => {
it('should rename a file, forwarding the original metadata', async () => {
const mimeType = 'text/plain';
const fileName = 'file.txt';
objectStoreService.getMetadata.mockResolvedValue(
mock<MetadataResponseHeaders>({
'content-length': String(mockBuffer.length),
'content-type': mimeType,
'x-amz-meta-filename': fileName,
}),
);
const promise = objectStoreManager.rename(fileId, otherFileId);
await expect(promise).resolves.not.toThrow();
const lastPutCall = objectStoreService.put.mock.lastCall;
expect(objectStoreService.get).toHaveBeenCalledWith(fileId, { mode: 'buffer' });
expect(objectStoreService.getMetadata).toHaveBeenCalledWith(fileId);
expect(lastPutCall?.[0]).toBe(otherFileId);
expect(lastPutCall?.[2]).toEqual(expect.objectContaining({ mimeType, fileName }));
expect(objectStoreService.deleteOne).toHaveBeenCalledWith(fileId);
});
});
@@ -59,8 +59,9 @@ export class ObjectStoreManager implements BinaryData.Manager {
const targetFileId = this.toFileId(targetLocation);
const sourceFile = await this.objectStoreService.get(sourceFileId, { mode: 'buffer' });
const sourceMetadata = await this.getMetadata(sourceFileId);
await this.objectStoreService.put(targetFileId, sourceFile);
await this.objectStoreService.put(targetFileId, sourceFile, sourceMetadata);
return targetFileId;
}
@@ -83,7 +84,7 @@ export class ObjectStoreManager implements BinaryData.Manager {
async rename(oldFileId: string, newFileId: string) {
const oldFile = await this.objectStoreService.get(oldFileId, { mode: 'buffer' });
const oldFileMetadata = await this.objectStoreService.getMetadata(oldFileId);
const oldFileMetadata = await this.getMetadata(oldFileId);
await this.objectStoreService.put(newFileId, oldFile, oldFileMetadata);
await this.objectStoreService.deleteOne(oldFileId);