From d256e9a1981dda3f206cd182e52d513dc1cdd1ca Mon Sep 17 00:00:00 2001 From: douglas-xt Date: Wed, 4 Feb 2026 08:10:49 +0100 Subject: [PATCH] fix(thumbnail): use file bucket instead of request bucket for storage --- src/modules/file/file.controller.spec.ts | 1 + .../thumbnail/dto/create-thumbnail.dto.ts | 6 +- .../thumbnail/thumbnail.usecase.spec.ts | 72 ++++++++++++++++++- src/modules/thumbnail/thumbnail.usecase.ts | 15 ++-- 4 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/modules/file/file.controller.spec.ts b/src/modules/file/file.controller.spec.ts index 5bd3cc3b7..d8f74157d 100644 --- a/src/modules/file/file.controller.spec.ts +++ b/src/modules/file/file.controller.spec.ts @@ -292,6 +292,7 @@ describe('FileController', () => { const thumbnailDto: ThumbnailDto = { id: 1, ...createThumbnailDto, + bucketId: createThumbnailDto.bucketId!, createdAt: new Date(), updatedAt: new Date(), }; diff --git a/src/modules/thumbnail/dto/create-thumbnail.dto.ts b/src/modules/thumbnail/dto/create-thumbnail.dto.ts index 9ea9a4cbf..f2c92819f 100644 --- a/src/modules/thumbnail/dto/create-thumbnail.dto.ts +++ b/src/modules/thumbnail/dto/create-thumbnail.dto.ts @@ -73,10 +73,12 @@ export class CreateThumbnailDto { @ApiProperty({ description: 'The bucket id where the file is stored', example: 'my-bucket', + deprecated: true, + required: false, }) - @IsNotEmpty() + @IsOptional() @IsString() - bucketId: string; + bucketId?: string; @ApiProperty({ description: 'The id of file in the bucket', diff --git a/src/modules/thumbnail/thumbnail.usecase.spec.ts b/src/modules/thumbnail/thumbnail.usecase.spec.ts index 74c67f978..44c548337 100644 --- a/src/modules/thumbnail/thumbnail.usecase.spec.ts +++ b/src/modules/thumbnail/thumbnail.usecase.spec.ts @@ -27,6 +27,7 @@ describe('ThumbnailUseCases', () => { id: 1, fileId: 123456, fileUuid: fileUuid, + bucketId: 'oldBucketId', bucketFile: 'existingBucketFile', } as any; @@ -57,6 +58,7 @@ describe('ThumbnailUseCases', () => { id: 123456, uuid: fileUuid, userId: userMocked.id, + bucket: 'fileBucketId', } as any); }); @@ -74,6 +76,7 @@ describe('ThumbnailUseCases', () => { expect(thumbnailRepository.findByFileUuid).toHaveBeenCalledWith(fileUuid); expect(thumbnailRepository.create).toHaveBeenCalledWith({ ...createThumbnailDto, + bucketId: 'fileBucketId', fileId: 123456, fileUuid: fileUuid, createdAt: expect.any(Date), @@ -101,11 +104,11 @@ describe('ThumbnailUseCases', () => { expect(networkService.deleteFile).toHaveBeenCalledWith( userMocked, - createThumbnailDto.bucketId, + existingThumbnail.bucketId, existingThumbnail.bucketFile, ); expect(thumbnailRepository.update).toHaveBeenCalledWith( - createThumbnailDto, + { ...createThumbnailDto, bucketId: 'fileBucketId' }, { id: existingThumbnail.id, fileUuid: existingThumbnail.fileUuid, @@ -140,7 +143,7 @@ describe('ThumbnailUseCases', () => { expect.stringContaining('Error deleting existent thumbnail'), ); expect(thumbnailRepository.update).toHaveBeenCalledWith( - createThumbnailDto, + { ...createThumbnailDto, bucketId: 'fileBucketId' }, { id: existingThumbnail.id, fileUuid: existingThumbnail.fileUuid, @@ -149,6 +152,68 @@ describe('ThumbnailUseCases', () => { expect(result).toEqual(existingThumbnail); }); + it('When file bucket differs from DTO bucket, it should use file bucket for thumbnail', async () => { + const dtoWithDifferentBucket = { + ...createThumbnailDto, + bucketId: 'dtoBucketId', + }; + + jest.spyOn(thumbnailRepository, 'findByFileUuid').mockResolvedValue(null); + jest + .spyOn(thumbnailRepository, 'create') + .mockResolvedValue(existingThumbnail); + + await thumbnailUseCases.createThumbnail( + userMocked, + dtoWithDifferentBucket, + ); + + expect(thumbnailRepository.create).toHaveBeenCalledWith({ + ...dtoWithDifferentBucket, + bucketId: 'fileBucketId', + fileId: 123456, + fileUuid: fileUuid, + createdAt: expect.any(Date), + updatedAt: expect.any(Date), + }); + }); + + it('When existing thumbnail has different bucket than file, it should delete from old bucket and update with file bucket', async () => { + const dtoWithDifferentBucket = { + ...createThumbnailDto, + bucketId: 'dtoBucketId', + }; + + jest + .spyOn(thumbnailRepository, 'findByFileUuid') + .mockResolvedValue(existingThumbnail); + jest.spyOn(networkService, 'deleteFile').mockResolvedValue(undefined); + jest + .spyOn(thumbnailRepository, 'update') + .mockResolvedValue(existingThumbnail); + jest + .spyOn(thumbnailRepository, 'findByFileUuid') + .mockResolvedValue(existingThumbnail); + + await thumbnailUseCases.createThumbnail( + userMocked, + dtoWithDifferentBucket, + ); + + expect(networkService.deleteFile).toHaveBeenCalledWith( + userMocked, + existingThumbnail.bucketId, + existingThumbnail.bucketFile, + ); + expect(thumbnailRepository.update).toHaveBeenCalledWith( + { ...dtoWithDifferentBucket, bucketId: 'fileBucketId' }, + { + id: existingThumbnail.id, + fileUuid: existingThumbnail.fileUuid, + }, + ); + }); + it('When an error occurs while creating a new thumbnail, it should handle the error', async () => { jest.spyOn(thumbnailRepository, 'findByFileUuid').mockResolvedValue(null); jest @@ -193,6 +258,7 @@ describe('ThumbnailUseCases', () => { expect(thumbnailRepository.create).toHaveBeenCalledWith({ ...createThumbnailDto, + bucketId: 'fileBucketId', fileId: 123456, fileUuid: fileUuid, createdAt: expect.any(Date), diff --git a/src/modules/thumbnail/thumbnail.usecase.ts b/src/modules/thumbnail/thumbnail.usecase.ts index 6f7e44a4f..ee2b2b212 100644 --- a/src/modules/thumbnail/thumbnail.usecase.ts +++ b/src/modules/thumbnail/thumbnail.usecase.ts @@ -34,6 +34,7 @@ export class ThumbnailUseCases { 'You do not have permission to modify this file', ); } + const bucketId = file.bucket; const existingThumbnail = await this.thumbnailRepository.findByFileUuid( file.uuid, ); @@ -41,7 +42,7 @@ export class ThumbnailUseCases { try { await this.network.deleteFile( user, - thumbnail.bucketId, + existingThumbnail.bucketId, existingThumbnail.bucketFile, ); } catch (error) { @@ -49,15 +50,19 @@ export class ThumbnailUseCases { `[THUMBNAIL/CREATE] Error deleting existent thumbnail. Error: ${error.message}`, ); } - await this.thumbnailRepository.update(thumbnail, { - id: existingThumbnail.id, - fileUuid: existingThumbnail.fileUuid, - }); + await this.thumbnailRepository.update( + { ...thumbnail, bucketId }, + { + id: existingThumbnail.id, + fileUuid: existingThumbnail.fileUuid, + } + ); return this.thumbnailRepository.findByFileUuid(file.uuid); } const newThumbnailObject = { ...thumbnail, + bucketId, fileId: file.id, fileUuid: file.uuid, createdAt: new Date(),