diff --git a/src/app/drive/services/thumbnail.service.test.ts b/src/app/drive/services/thumbnail.service.test.ts index 4759e93c9..d750b2f3b 100644 --- a/src/app/drive/services/thumbnail.service.test.ts +++ b/src/app/drive/services/thumbnail.service.test.ts @@ -1,9 +1,14 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import Resizer from 'react-image-file-resizer'; import { ErrorLoadingVideoFileError } from './errors/thumbnail.service.errors'; -import { getVideoFrame, getImageThumbnail } from './thumbnail.service'; +import { getVideoFrame, getImageThumbnail, downloadThumbnail } from './thumbnail.service'; +import localStorageService from 'services/local-storage.service'; +import fetchFileBlob from './download.service/fetchFileBlob'; +import { Thumbnail } from '@internxt/sdk/dist/drive/storage/types'; vi.mock('react-image-file-resizer'); +vi.mock('services/local-storage.service'); +vi.mock('./download.service/fetchFileBlob'); const flushPromises = () => new Promise((resolve) => setTimeout(resolve, 0)); @@ -240,4 +245,79 @@ describe('Thumbnail Service', () => { expect(result).toBeNull(); }); }); + + describe('Download Thumbnail', () => { + const workspaceBucket = 'workspace-bucket-123'; + const personalBucket = 'personal-bucket-456'; + + const mockThumbnail: Thumbnail = { + id: 1, + file_id: 123, + bucket_id: workspaceBucket, + bucket_file: 'bucket-file-hash', + type: 'image/png', + size: 1024, + max_width: 300, + max_height: 300, + encrypt_version: '03-aes', + }; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(fetchFileBlob).mockResolvedValue(new Blob(['thumbnail-data'])); + }); + + test('When downloading thumbnail in workspace context with workspace bucket, then it uses workspace credentials', async () => { + vi.mocked(localStorageService.getUser).mockReturnValue({ bucket: personalBucket } as any); + + await downloadThumbnail(mockThumbnail, true); + + expect(fetchFileBlob).toHaveBeenCalledWith( + expect.objectContaining({ + fileId: mockThumbnail.bucket_file, + bucketId: mockThumbnail.bucket_id, + }), + expect.objectContaining({ + isWorkspace: true, + }), + ); + }); + + test('When downloading thumbnail in workspace context with personal bucket, then it uses personal credentials for backward compatibility', async () => { + const thumbnailInPersonalBucket: Thumbnail = { + ...mockThumbnail, + bucket_id: personalBucket, + }; + + vi.mocked(localStorageService.getUser).mockReturnValue({ bucket: personalBucket } as any); + + await downloadThumbnail(thumbnailInPersonalBucket, true); + + expect(fetchFileBlob).toHaveBeenCalledWith( + expect.objectContaining({ + fileId: thumbnailInPersonalBucket.bucket_file, + bucketId: thumbnailInPersonalBucket.bucket_id, + }), + expect.objectContaining({ + isWorkspace: false, + }), + ); + }); + + test('When downloading thumbnail in personal context, then it uses personal credentials', async () => { + vi.mocked(localStorageService.getUser).mockReturnValue({ bucket: personalBucket } as any); + + await downloadThumbnail(mockThumbnail, false); + + expect(fetchFileBlob).toHaveBeenCalledWith( + expect.objectContaining({ + fileId: mockThumbnail.bucket_file, + bucketId: mockThumbnail.bucket_id, + }), + expect.objectContaining({ + isWorkspace: false, + }), + ); + }); + }); }); diff --git a/src/app/drive/services/thumbnail.service.ts b/src/app/drive/services/thumbnail.service.ts index 5002b21aa..273e28a42 100644 --- a/src/app/drive/services/thumbnail.service.ts +++ b/src/app/drive/services/thumbnail.service.ts @@ -1,5 +1,6 @@ import { StorageTypes } from '@internxt/sdk/dist/drive'; import { Thumbnail } from '@internxt/sdk/dist/drive/storage/types'; +import { UserSettings } from '@internxt/sdk/dist/shared/types/userSettings'; import { thumbnailableExtension, thumbnailableImageExtension, @@ -283,10 +284,21 @@ export const downloadThumbnail = async (thumbnailToDownload: Thumbnail, isWorksp return; }; const abortController = new AbortController(); - // TODO: CHECK WHY WITH THUMBNAILS NOT HAS TO USE WORKSPACE CREDENTIALS + + let useWorkspaceCredentials = isWorkspace; + + if (isWorkspace) { + const user = localStorageService.getUser() as UserSettings; + const isInPersonalBucket = thumbnailToDownload.bucket_id === user.bucket; + + if (isInPersonalBucket) { + useWorkspaceCredentials = false; + } + } + return await fetchFileBlob( { fileId: thumbnailToDownload.bucket_file, bucketId: thumbnailToDownload.bucket_id } as Downloadable, - { isWorkspace: false, updateProgressCallback, abortController }, + { isWorkspace: useWorkspaceCredentials, updateProgressCallback, abortController }, ); }; diff --git a/src/app/network/UploadManager.test.ts b/src/app/network/UploadManager.test.ts index 718b1fac4..97ae95fc2 100644 --- a/src/app/network/UploadManager.test.ts +++ b/src/app/network/UploadManager.test.ts @@ -558,4 +558,116 @@ describe('checkUploadFiles', () => { merge: { status: TaskStatus.Error, subtitle: expect.any(String) }, }); }); + + it('When uploading a file to a workspace, then it uses workspace credentials and same bucket for file and thumbnail', async () => { + const workspaceBucket = 'workspace-bucket-123'; + const workspaceId = 'workspace-id-456'; + const uploadFileSpy = (uploadFile as Mock).mockResolvedValueOnce(mockFile1); + + vi.spyOn(tasksService, 'create').mockReturnValue('taskId'); + vi.spyOn(tasksService, 'updateTask').mockReturnValue(); + vi.spyOn(tasksService, 'addListener').mockReturnValue(); + vi.spyOn(tasksService, 'removeListener').mockReturnValue(); + vi.spyOn(errorService, 'castError').mockResolvedValue(new AppError('error')); + + await uploadFileWithManager( + [ + { + taskId: 'taskId', + filecontent: { + content: 'file-content' as unknown as File, + type: 'text/plain', + name: 'file.txt', + size: 1024, + parentFolderId: 'folder-1', + }, + userEmail: 'user@test.com', + parentFolderId: '', + }, + ], + openMaxSpaceOccupiedDialogMock, + DatabaseUploadRepository.getInstance(), + undefined, + { + ownerUserAuthenticationData: { + bucketId: workspaceBucket, + workspaceId: workspaceId, + bridgeUser: 'bridge-user', + bridgePass: 'bridge-pass', + encryptionKey: 'encryption-key', + token: 'token', + workspacesToken: 'workspaces-token', + resourcesToken: 'resources-token', + }, + sharedItemData: { + isDeepFolder: false, + currentFolderId: 'parentFolderId', + }, + isUploadedFromFolder: true, + }, + ); + + expect(uploadFileSpy).toHaveBeenCalledWith( + 'user@test.com', + expect.any(Object), + expect.any(Function), + expect.objectContaining({ + isTeam: true, + ownerUserAuthenticationData: expect.objectContaining({ + bucketId: workspaceBucket, + workspaceId: workspaceId, + }), + }), + expect.any(Object), + ); + }); + + it('When uploading a personal file, then it uses personal credentials', async () => { + const uploadFileSpy = (uploadFile as Mock).mockResolvedValueOnce(mockFile1); + + vi.spyOn(tasksService, 'create').mockReturnValue('taskId'); + vi.spyOn(tasksService, 'updateTask').mockReturnValue(); + vi.spyOn(tasksService, 'addListener').mockReturnValue(); + vi.spyOn(tasksService, 'removeListener').mockReturnValue(); + vi.spyOn(errorService, 'castError').mockResolvedValue(new AppError('error')); + + await uploadFileWithManager( + [ + { + taskId: 'taskId', + filecontent: { + content: 'file-content' as unknown as File, + type: 'text/plain', + name: 'file.txt', + size: 1024, + parentFolderId: 'folder-1', + }, + userEmail: 'user@test.com', + parentFolderId: '', + }, + ], + openMaxSpaceOccupiedDialogMock, + DatabaseUploadRepository.getInstance(), + undefined, + { + ownerUserAuthenticationData: undefined, + sharedItemData: { + isDeepFolder: false, + currentFolderId: 'parentFolderId', + }, + isUploadedFromFolder: true, + }, + ); + + expect(uploadFileSpy).toHaveBeenCalledWith( + 'user@test.com', + expect.any(Object), + expect.any(Function), + expect.objectContaining({ + isTeam: false, + ownerUserAuthenticationData: undefined, + }), + expect.any(Object), + ); + }); }); diff --git a/src/app/network/UploadManager.ts b/src/app/network/UploadManager.ts index 1d842dc36..393aad37d 100644 --- a/src/app/network/UploadManager.ts +++ b/src/app/network/UploadManager.ts @@ -178,7 +178,7 @@ class UploadManager { } }, { - isTeam: false, + isTeam: !!this.options?.ownerUserAuthenticationData?.workspaceId, abortController: this.abortController ?? fileData.abortController, ownerUserAuthenticationData: this.options?.ownerUserAuthenticationData, abortCallback: (abort?: () => void) => {