diff --git a/src/elements/content-sharing/__tests__/sharingService.test.ts b/src/elements/content-sharing/__tests__/sharingService.test.ts index 83b5c973b7..0301bd59b1 100644 --- a/src/elements/content-sharing/__tests__/sharingService.test.ts +++ b/src/elements/content-sharing/__tests__/sharingService.test.ts @@ -1,4 +1,4 @@ -import { PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW } from '../../../constants'; +import { ACCESS_NONE, PERMISSION_CAN_DOWNLOAD, PERMISSION_CAN_PREVIEW } from '../../../constants'; import { CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS } from '../constants'; import { createSharingService } from '../sharingService'; import { convertSharedLinkPermissions, convertSharedLinkSettings } from '../utils'; @@ -7,9 +7,20 @@ jest.mock('../utils'); const mockItemApiInstance = { updateSharedLink: jest.fn(), + share: jest.fn(), }; const options = { id: '123', permissions: { can_set_share_access: true, can_share: true } }; -const mockOnSuccess = jest.fn(); +const mockOnUpdateSharedLink = jest.fn(); +const mockOnRemoveSharedLink = jest.fn(); + +const createSharingServiceWrapper = () => { + return createSharingService({ + itemApiInstance: mockItemApiInstance, + onUpdateSharedLink: mockOnUpdateSharedLink, + onRemoveSharedLink: mockOnRemoveSharedLink, + options, + }); +}; describe('elements/content-sharing/sharingService', () => { beforeEach(() => { @@ -29,23 +40,13 @@ describe('elements/content-sharing/sharingService', () => { describe('changeSharedLinkPermission', () => { test('should return an object with changeSharedLinkPermission method', () => { - const service = createSharingService({ - itemApiInstance: mockItemApiInstance, - onSuccess: mockOnSuccess, - options, - }); - + const service = createSharingServiceWrapper(); expect(service).toHaveProperty('changeSharedLinkPermission'); expect(typeof service.changeSharedLinkPermission).toBe('function'); }); test('should call updateSharedLink with correct parameters when changeSharedLinkPermission is called', async () => { - const service = createSharingService({ - itemApiInstance: mockItemApiInstance, - onSuccess: mockOnSuccess, - options, - }); - + const service = createSharingServiceWrapper(); const permissionLevel = PERMISSION_CAN_DOWNLOAD; const expectedPermissions = { [PERMISSION_CAN_DOWNLOAD]: true, @@ -57,7 +58,73 @@ describe('elements/content-sharing/sharingService', () => { expect(mockItemApiInstance.updateSharedLink).toHaveBeenCalledWith( options, { permissions: expectedPermissions }, - mockOnSuccess, + mockOnUpdateSharedLink, + {}, + CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, + ); + }); + }); + + describe('changeSharedLinkAccess', () => { + test('should return an object with changeSharedLinkAccess method', () => { + const service = createSharingServiceWrapper(); + expect(service).toHaveProperty('changeSharedLinkAccess'); + expect(typeof service.changeSharedLinkAccess).toBe('function'); + }); + + test.each(['open', 'company', 'collaborators'])( + 'should call share with correct parameters when changeSharedLinkAccess is called', + async access => { + const service = createSharingServiceWrapper(); + await service.changeSharedLinkAccess(access); + + expect(mockItemApiInstance.share).toHaveBeenCalledWith( + options, + access, + mockOnUpdateSharedLink, + {}, + CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, + ); + }, + ); + }); + + describe('createSharedLink', () => { + test('should return an object with createSharedLink method', () => { + const service = createSharingServiceWrapper(); + expect(service).toHaveProperty('createSharedLink'); + expect(typeof service.createSharedLink).toBe('function'); + }); + + test('should call share with correct parameters when createSharedLink is called', async () => { + const service = createSharingServiceWrapper(); + await service.createSharedLink(); + + expect(mockItemApiInstance.share).toHaveBeenCalledWith( + options, + undefined, + mockOnUpdateSharedLink, + {}, + CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, + ); + }); + }); + + describe('deleteSharedLink', () => { + test('should return an object with deleteSharedLink method', () => { + const service = createSharingServiceWrapper(); + expect(service).toHaveProperty('deleteSharedLink'); + expect(typeof service.deleteSharedLink).toBe('function'); + }); + + test('should call share with ACCESS_NONE and onRemoveSharedLink when deleteSharedLink is called', async () => { + const service = createSharingServiceWrapper(); + await service.deleteSharedLink(); + + expect(mockItemApiInstance.share).toHaveBeenCalledWith( + options, + ACCESS_NONE, + mockOnRemoveSharedLink, {}, CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, ); @@ -66,22 +133,14 @@ describe('elements/content-sharing/sharingService', () => { describe('updateSharedLink', () => { test('should return an object with updateSharedLink method', () => { - const service = createSharingService({ - itemApiInstance: mockItemApiInstance, - onSuccess: mockOnSuccess, - options, - }); + const service = createSharingServiceWrapper(); expect(service).toHaveProperty('updateSharedLink'); expect(typeof service.updateSharedLink).toBe('function'); }); test('should call updateSharedLink with basic shared link settings', async () => { - const service = createSharingService({ - itemApiInstance: mockItemApiInstance, - onSuccess: mockOnSuccess, - options, - }); + const service = createSharingServiceWrapper(); const sharedLinkSettings = { expiration: null, @@ -108,7 +167,7 @@ describe('elements/content-sharing/sharingService', () => { expect(mockItemApiInstance.updateSharedLink).toHaveBeenCalledWith( options, expectedConvertedSettings, - mockOnSuccess, + mockOnUpdateSharedLink, {}, CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, ); @@ -126,7 +185,8 @@ describe('elements/content-sharing/sharingService', () => { const service = createSharingService({ itemApiInstance: mockItemApiInstance, - onSuccess: mockOnSuccess, + onUpdateSharedLink: mockOnUpdateSharedLink, + onRemoveSharedLink: mockOnRemoveSharedLink, options: { ...options, access: 'open', @@ -155,18 +215,14 @@ describe('elements/content-sharing/sharingService', () => { expect(mockItemApiInstance.updateSharedLink).toHaveBeenCalledWith( options, mockConvertedSharedLinkSettings, - mockOnSuccess, + mockOnUpdateSharedLink, {}, CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, ); }); test('should handle shared link settings correctly', async () => { - const service = createSharingService({ - itemApiInstance: mockItemApiInstance, - onSuccess: mockOnSuccess, - options, - }); + const service = createSharingServiceWrapper(); const expirationDate = new Date('2024-12-31T23:59:59Z'); const sharedLinkSettings = { diff --git a/src/elements/content-sharing/hooks/__tests__/useSharingService.test.ts b/src/elements/content-sharing/hooks/__tests__/useSharingService.test.ts index 1e375a9210..61d7d9c1fa 100644 --- a/src/elements/content-sharing/hooks/__tests__/useSharingService.test.ts +++ b/src/elements/content-sharing/hooks/__tests__/useSharingService.test.ts @@ -12,11 +12,12 @@ const mockApi = { getFileAPI: jest.fn(), getFolderAPI: jest.fn(), }; -const mockItemApiInstance = { - updateSharedLink: jest.fn(), -}; const mockSharingService = { + createSharedLink: jest.fn(), + changeSharedLinkAccess: jest.fn(), changeSharedLinkPermission: jest.fn(), + deleteSharedLink: jest.fn(), + updateSharedLink: jest.fn(), }; const mockItemId = '123'; @@ -43,6 +44,11 @@ const mockSharingServiceProps = { can_share: true, }; +const mockConvertedData = { + item: mockItem, + sharedLink: mockSharedLink, +}; + const mockSetItem = jest.fn(); const mockSetSharedLink = jest.fn(); @@ -104,7 +110,7 @@ describe('elements/content-sharing/hooks/useSharingService', () => { describe('when itemType is TYPE_FILE', () => { beforeEach(() => { - mockApi.getFileAPI.mockReturnValue(mockItemApiInstance); + mockApi.getFileAPI.mockReturnValue({}); }); test('should create file API instance and sharing service', () => { @@ -114,8 +120,9 @@ describe('elements/content-sharing/hooks/useSharingService', () => { expect(mockApi.getFolderAPI).not.toHaveBeenCalled(); expect(result.current.sharingService).toBe(mockSharingService); expect(createSharingService).toHaveBeenCalledWith({ - itemApiInstance: mockItemApiInstance, - onSuccess: expect.any(Function), + itemApiInstance: {}, + onUpdateSharedLink: expect.any(Function), + onRemoveSharedLink: expect.any(Function), options: { access: mockSharedLink.access, isDownloadAvailable: mockSharedLink.settings.isDownloadAvailable, @@ -126,22 +133,28 @@ describe('elements/content-sharing/hooks/useSharingService', () => { }); }); - test('should handle success callback correctly', () => { - const mockConvertedData = { - item: { - id: mockItemId, - permissions: { can_download: false, can_preview: true }, - }, - sharedLink: {}, - }; + test('should handle update shared link success callback correctly', () => { + (convertItemResponse as jest.Mock).mockReturnValue(mockConvertedData); + renderHookWithProps(); + + // Get the callbacks that were passed to mock createSharingService + const createSharingServiceArgs = (createSharingService as jest.Mock).mock.calls[0][0]; + createSharingServiceArgs.onUpdateSharedLink(mockConvertedData); + + expect(convertItemResponse).toHaveBeenCalledWith(mockConvertedData); + expect(mockSetItem).toHaveBeenCalledTimes(1); + expect(mockSetSharedLink).toHaveBeenCalledTimes(1); + }); + test('should handle remove shared link success callback correctly', () => { (convertItemResponse as jest.Mock).mockReturnValue(mockConvertedData); renderHookWithProps(); - // Get the onSuccess callback that was passed to mock createSharingService - const onSuccessCallback = (createSharingService as jest.Mock).mock.calls[0][0].onSuccess; - onSuccessCallback(mockConvertedData); + // Get the callbacks that were passed to mock createSharingService + const createSharingServiceArgs = (createSharingService as jest.Mock).mock.calls[0][0]; + createSharingServiceArgs.onRemoveSharedLink(mockConvertedData); + expect(convertItemResponse).toHaveBeenCalledWith(mockConvertedData); expect(mockSetItem).toHaveBeenCalledTimes(1); expect(mockSetSharedLink).toHaveBeenCalledTimes(1); }); @@ -149,7 +162,7 @@ describe('elements/content-sharing/hooks/useSharingService', () => { describe('when itemType is TYPE_FOLDER', () => { beforeEach(() => { - mockApi.getFolderAPI.mockReturnValue(mockItemApiInstance); + mockApi.getFolderAPI.mockReturnValue({}); }); test('should create folder API instance and sharing service', () => { @@ -159,8 +172,9 @@ describe('elements/content-sharing/hooks/useSharingService', () => { expect(mockApi.getFileAPI).not.toHaveBeenCalled(); expect(result.current.sharingService).toBe(mockSharingService); expect(createSharingService).toHaveBeenCalledWith({ - itemApiInstance: mockItemApiInstance, - onSuccess: expect.any(Function), + itemApiInstance: {}, + onUpdateSharedLink: expect.any(Function), + onRemoveSharedLink: expect.any(Function), options: { access: mockSharedLink.access, isDownloadAvailable: mockSharedLink.settings.isDownloadAvailable, @@ -170,5 +184,31 @@ describe('elements/content-sharing/hooks/useSharingService', () => { }, }); }); + + test('should handle update shared link success callback correctly for folders', () => { + (convertItemResponse as jest.Mock).mockReturnValue(mockConvertedData); + renderHookWithProps({ itemType: TYPE_FOLDER }); + + // Get the callbacks that were passed to mock createSharingService + const createSharingServiceArgs = (createSharingService as jest.Mock).mock.calls[0][0]; + createSharingServiceArgs.onUpdateSharedLink(mockConvertedData); + + expect(convertItemResponse).toHaveBeenCalledWith(mockConvertedData); + expect(mockSetItem).toHaveBeenCalledTimes(1); + expect(mockSetSharedLink).toHaveBeenCalledTimes(1); + }); + + test('should handle remove shared link success callback correctly for folders', () => { + (convertItemResponse as jest.Mock).mockReturnValue(mockConvertedData); + renderHookWithProps({ itemType: TYPE_FOLDER }); + + // Get the callbacks that were passed to mock createSharingService + const createSharingServiceArgs = (createSharingService as jest.Mock).mock.calls[0][0]; + createSharingServiceArgs.onRemoveSharedLink(mockConvertedData); + + expect(convertItemResponse).toHaveBeenCalledWith(mockConvertedData); + expect(mockSetItem).toHaveBeenCalledTimes(1); + expect(mockSetSharedLink).toHaveBeenCalledTimes(1); + }); }); }); diff --git a/src/elements/content-sharing/hooks/useSharingService.ts b/src/elements/content-sharing/hooks/useSharingService.ts index 606b58da2d..90edab4177 100644 --- a/src/elements/content-sharing/hooks/useSharingService.ts +++ b/src/elements/content-sharing/hooks/useSharingService.ts @@ -43,13 +43,24 @@ export const useSharingService = ({ isDownloadAvailable: sharedLink.settings?.isDownloadAvailable ?? false, }; - const handleSuccess = updatedItemData => { + const handleUpdateSharedLink = updatedItemData => { const { item: updatedItem, sharedLink: updatedSharedLink } = convertItemResponse(updatedItemData); setItem(prevItem => ({ ...prevItem, ...updatedItem })); setSharedLink(prevSharedLink => ({ ...prevSharedLink, ...updatedSharedLink })); }; - return createSharingService({ itemApiInstance, onSuccess: handleSuccess, options }); + const handleRemoveSharedLink = itemData => { + const { item: updatedItem } = convertItemResponse(itemData); + setItem(prevItem => ({ ...prevItem, ...updatedItem })); + setSharedLink({}); + }; + + return createSharingService({ + itemApiInstance, + onUpdateSharedLink: handleUpdateSharedLink, + onRemoveSharedLink: handleRemoveSharedLink, + options, + }); }, [itemApiInstance, itemId, sharedLink, sharingServiceProps, setItem, setSharedLink]); return { sharingService }; diff --git a/src/elements/content-sharing/sharingService.ts b/src/elements/content-sharing/sharingService.ts index 18f1cdb569..41c53aab58 100644 --- a/src/elements/content-sharing/sharingService.ts +++ b/src/elements/content-sharing/sharingService.ts @@ -1,4 +1,5 @@ import type { API } from '../../api'; +import { ACCESS_NONE } from '../../constants'; import { CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS } from './constants'; import { convertSharedLinkPermissions, convertSharedLinkSettings } from './utils'; @@ -18,20 +19,36 @@ export interface Options extends ItemData { serverURL?: string; } -export interface CreateSharingServiceProps { +export interface CreateSharingServiceArgs { itemApiInstance: API; - onSuccess: (itemData: ItemData) => void; + onUpdateSharedLink: (itemData: ItemData) => void; + onRemoveSharedLink: (itemData: ItemData) => void; options: Options; } -export const createSharingService = ({ itemApiInstance, onSuccess, options }: CreateSharingServiceProps) => { +export const createSharingService = ({ + itemApiInstance, + onUpdateSharedLink, + onRemoveSharedLink, + options, +}: CreateSharingServiceArgs) => { const { id, permissions } = options; + const changeSharedLinkAccess = async (access: string) => { + return itemApiInstance.share( + { id, permissions }, + access, + onUpdateSharedLink, + {}, + CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, + ); + }; + const changeSharedLinkPermission = async (permissionLevel: string) => { return itemApiInstance.updateSharedLink( { id, permissions }, { permissions: convertSharedLinkPermissions(permissionLevel) }, - onSuccess, + onUpdateSharedLink, {}, CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, ); @@ -43,14 +60,37 @@ export const createSharingService = ({ itemApiInstance, onSuccess, options }: Cr return itemApiInstance.updateSharedLink( { id, permissions }, convertSharedLinkSettings(sharedLinkSettings, access, isDownloadAvailable, serverURL), - onSuccess, + onUpdateSharedLink, + {}, + CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, + ); + }; + + const createSharedLink = async () => { + return itemApiInstance.share( + { id, permissions }, + undefined, // if "access" is undefined, the backend will set the default access level for the shared link + onUpdateSharedLink, + {}, + CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, + ); + }; + + const deleteSharedLink = async () => { + return itemApiInstance.share( + { id, permissions }, + ACCESS_NONE, + onRemoveSharedLink, {}, CONTENT_SHARING_SHARED_LINK_UPDATE_PARAMS, ); }; return { + createSharedLink, + changeSharedLinkAccess, changeSharedLinkPermission, + deleteSharedLink, updateSharedLink, }; };