diff --git a/src/item/api.ts b/src/item/api.ts index f8a5d6c58..895361a8f 100644 --- a/src/item/api.ts +++ b/src/item/api.ts @@ -148,16 +148,6 @@ export const getSharedItems = async ({ .then(({ data }) => data), ); -export const getFileContent = async ( - id: UUID, - { API_HOST, axios }: PartialQueryConfigForApi, -) => - axios - .get(`${API_HOST}/${buildDownloadFilesRoute(id)}`, { - responseType: 'blob', - }) - .then(({ data }) => data); - export const getFileContentUrl = async ( id: UUID, { API_HOST, axios }: PartialQueryConfigForApi, diff --git a/src/item/hooks.test.ts b/src/item/hooks.test.ts index 889005d6d..7e04fa9a8 100644 --- a/src/item/hooks.test.ts +++ b/src/item/hooks.test.ts @@ -12,7 +12,7 @@ import nock from 'nock'; import { afterEach, describe, expect, it } from 'vitest'; import { - FILE_RESPONSE, + THUMBNAIL_URL_RESPONSE, UNAUTHORIZED_RESPONSE, buildResultOfData, generateFolders, @@ -413,22 +413,21 @@ describe('useItems', () => { // TODO: errors, contains errors, full errors }); -describe('useFileContent', () => { +describe('useFileContentUrl', () => { afterEach(() => { nock.cleanAll(); queryClient.clear(); }); - const response = FILE_RESPONSE; + const response = THUMBNAIL_URL_RESPONSE; const { id } = LocalFileItemFactory(); - const route = `/${buildDownloadFilesRoute(id)}`; - const hook = () => hooks.useFileContent(id); - const key = itemKeys.single(id).file({ replyUrl: false }); + const route = `/${buildDownloadFilesRoute(id)}?replyUrl=true`; + const hook = () => hooks.useFileContentUrl(id); + const key = itemKeys.single(id).file({ replyUrl: true }); - it(`Receive file content`, async () => { + it(`Receive file url`, async () => { const endpoints = [{ route, response }]; const { data } = await mockHook({ endpoints, hook, wrapper }); - expect(data).toBeTruthy(); // verify cache keys expect(queryClient.getQueryData(key)).toBeTruthy(); @@ -438,7 +437,7 @@ describe('useFileContent', () => { const endpoints = [{ route, response }]; const { data, isFetched } = await mockHook({ endpoints, - hook: () => hooks.useFileContent(undefined), + hook: () => hooks.useFileContentUrl(undefined), wrapper, enabled: false, }); @@ -453,7 +452,7 @@ describe('useFileContent', () => { // build endpoint for each item const endpoints: Endpoint[] = []; const { data, isFetched } = await mockHook({ - hook: () => hooks.useFileContent(id, { enabled: false }), + hook: () => hooks.useFileContentUrl(id, { enabled: false }), endpoints, wrapper, enabled: false, diff --git a/src/item/hooks.ts b/src/item/hooks.ts index ef268bf89..9ee7dd7ce 100644 --- a/src/item/hooks.ts +++ b/src/item/hooks.ts @@ -24,7 +24,7 @@ import { } from './accessible/hooks.js'; import * as Api from './api.js'; import { useDescendants } from './descendants/hooks.js'; -import { useItemThumbnail, useItemThumbnailUrl } from './thumbnail/hooks.js'; +import { useItemThumbnailUrl } from './thumbnail/hooks.js'; import { ItemChildrenParams } from './types.js'; const config = ( @@ -170,28 +170,6 @@ const config = ( }); }, - /** - * @deprecated use url alternative when possible - * @param id itemId to download content from - * @returns Blob of the content - */ - useFileContent: ( - id?: UUID, - { enabled = true }: { enabled?: boolean } = {}, - ) => - useQuery({ - queryKey: itemKeys.single(id).file({ replyUrl: false }), - queryFn: () => { - if (!id) { - throw new UndefinedArgument(); - } - return Api.getFileContent(id, queryConfig); - }, - enabled: Boolean(id) && enabled, - ...defaultQueryOptions, - staleTime: CONSTANT_KEY_STALE_TIME_MILLISECONDS, - }), - useFileContentUrl: ( id?: UUID, { enabled = true }: { enabled?: boolean } = {}, @@ -211,7 +189,6 @@ const config = ( useItemFeedbackUpdates: itemWsHooks?.useItemFeedbackUpdates, - useItemThumbnail: useItemThumbnail(queryConfig), useItemThumbnailUrl: useItemThumbnailUrl(queryConfig), }; }; diff --git a/src/item/thumbnail/api.ts b/src/item/thumbnail/api.ts index 36165408d..ccd99736a 100644 --- a/src/item/thumbnail/api.ts +++ b/src/item/thumbnail/api.ts @@ -11,23 +11,6 @@ import { buildUploadItemThumbnailRoute, } from '../routes.js'; -export const downloadItemThumbnail = async ( - { id, size = DEFAULT_THUMBNAIL_SIZE }: { id: UUID; size?: string }, - { API_HOST, axios }: PartialQueryConfigForApi, -) => - axios - .get( - `${API_HOST}/${buildDownloadItemThumbnailRoute({ - id, - size, - replyUrl: false, - })}`, - { - responseType: 'blob', - }, - ) - .then(({ data }) => data); - export const downloadItemThumbnailUrl = async ( { id, size = DEFAULT_THUMBNAIL_SIZE }: { id: UUID; size?: string }, { API_HOST, axios }: PartialQueryConfigForApi, diff --git a/src/item/thumbnail/hooks.test.ts b/src/item/thumbnail/hooks.test.ts index 0c8644e92..77c201e22 100644 --- a/src/item/thumbnail/hooks.test.ts +++ b/src/item/thumbnail/hooks.test.ts @@ -5,7 +5,6 @@ import nock from 'nock'; import { afterEach, describe, expect, it } from 'vitest'; import { - THUMBNAIL_BLOB_RESPONSE, THUMBNAIL_URL_RESPONSE, UNAUTHORIZED_RESPONSE, } from '../../../test/constants.js'; @@ -15,134 +14,6 @@ import { buildDownloadItemThumbnailRoute } from '../routes.js'; const { hooks, wrapper, queryClient } = setUpTest(); -describe('useItemThumbnail', () => { - afterEach(() => { - nock.cleanAll(); - queryClient.clear(); - }); - - const item = FolderItemFactory(); - const replyUrl = false; - const key = itemKeys.single(item.id).thumbnail({ replyUrl }); - const response = THUMBNAIL_BLOB_RESPONSE; - const route = `/${buildDownloadItemThumbnailRoute({ - id: item.id, - replyUrl, - })}`; - const hook = () => hooks.useItemThumbnail({ id: item.id }); - - it(`Receive default thumbnail`, async () => { - const endpoints = [ - { - route, - response, - headers: { - 'Content-Type': 'image/jpeg', - }, - }, - ]; - const { data } = await mockHook({ endpoints, hook, wrapper }); - - expect(data).toBeTruthy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeTruthy(); - }); - - it(`Receive large thumbnail`, async () => { - const size = ThumbnailSize.Large; - const routeLarge = `/${buildDownloadItemThumbnailRoute({ - id: item.id, - size, - replyUrl, - })}`; - const hookLarge = () => hooks.useItemThumbnail({ id: item.id, size }); - const keyLarge = itemKeys.single(item.id).thumbnail({ size }); - - const endpoints = [ - { - route: routeLarge, - response, - headers: { - 'Content-Type': 'image/jpeg', - }, - }, - ]; - const { data } = await mockHook({ - endpoints, - hook: hookLarge, - wrapper, - }); - - expect(data).toBeTruthy(); - // verify cache keys - expect(queryClient.getQueryData(keyLarge)).toBeTruthy(); - }); - - it(`Undefined id does not fetch`, async () => { - const endpoints = [ - { - route, - response, - }, - ]; - const { data, isFetched } = await mockHook({ - endpoints, - hook: () => hooks.useItemThumbnail({ id: undefined }), - wrapper, - enabled: false, - }); - - expect(data).toBeFalsy(); - expect(isFetched).toBeFalsy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeFalsy(); - }); - - it(`Does not fetch if item has no thumbnail`, async () => { - const itemWithoutThumbnail = { - ...item, - settings: { hasThumbnail: false }, - }; - queryClient.setQueryData( - itemKeys.single(itemWithoutThumbnail.id).content, - itemWithoutThumbnail, - ); - const endpoints = [ - { - route, - response, - }, - ]; - const { data, isFetched } = await mockHook({ - endpoints, - hook: () => hooks.useItemThumbnail({ id: itemWithoutThumbnail.id }), - wrapper, - enabled: false, - }); - - expect(data).toBeFalsy(); - expect(isFetched).toBeFalsy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeFalsy(); - }); - - it(`Unauthorized`, async () => { - const endpoints = [ - { - route, - response: UNAUTHORIZED_RESPONSE, - statusCode: StatusCodes.UNAUTHORIZED, - }, - ]; - const { data, isError } = await mockHook({ endpoints, hook, wrapper }); - - expect(data).toBeFalsy(); - expect(isError).toBeTruthy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeFalsy(); - }); -}); - describe('useItemThumbnailUrl', () => { afterEach(() => { nock.cleanAll(); diff --git a/src/item/thumbnail/hooks.ts b/src/item/thumbnail/hooks.ts index 0d3a6dc92..31373ec83 100644 --- a/src/item/thumbnail/hooks.ts +++ b/src/item/thumbnail/hooks.ts @@ -9,35 +9,7 @@ import { import { UndefinedArgument } from '../../config/errors.js'; import { itemKeys } from '../../keys.js'; import { QueryClientConfig } from '../../types.js'; -import { downloadItemThumbnail, downloadItemThumbnailUrl } from './api.js'; - -/** - * @deprecated use useItemThumbnailUrl - */ -export const useItemThumbnail = - (queryConfig: QueryClientConfig) => - ({ id, size = DEFAULT_THUMBNAIL_SIZE }: { id?: UUID; size?: string }) => { - const { defaultQueryOptions } = queryConfig; - const queryClient = useQueryClient(); - let shouldFetch = true; - if (id) { - shouldFetch = - queryClient.getQueryData(itemKeys.single(id).content) - ?.settings?.hasThumbnail ?? true; - } - return useQuery({ - queryKey: itemKeys.single(id).thumbnail({ size, replyUrl: false }), - queryFn: () => { - if (!id) { - throw new UndefinedArgument(); - } - return downloadItemThumbnail({ id, size }, queryConfig); - }, - ...defaultQueryOptions, - enabled: Boolean(id) && shouldFetch, - staleTime: CONSTANT_KEY_STALE_TIME_MILLISECONDS, - }); - }; +import { downloadItemThumbnailUrl } from './api.js'; // create a new thumbnail hook because of key content /** diff --git a/src/item/thumbnail/mutations.test.ts b/src/item/thumbnail/mutations.test.ts index 22d2a68d6..8abefde21 100644 --- a/src/item/thumbnail/mutations.test.ts +++ b/src/item/thumbnail/mutations.test.ts @@ -1,17 +1,10 @@ -import { - FolderItemFactory, - HttpMethod, - MAX_FILE_SIZE, - ThumbnailSize, -} from '@graasp/sdk'; -import { SUCCESS_MESSAGES } from '@graasp/translations'; +import { HttpMethod, MAX_FILE_SIZE } from '@graasp/sdk'; import { act } from '@testing-library/react'; import { StatusCodes } from 'http-status-codes'; import { describe, expect, it, vi } from 'vitest'; import { - THUMBNAIL_BLOB_RESPONSE, UNAUTHORIZED_RESPONSE, generateFolders, } from '../../../test/constants.js'; @@ -35,104 +28,6 @@ const { wrapper, queryClient, mutations } = setUpTest({ notifier: mockedNotifier, }); -describe('useUploadItemThumbnailFeedback', () => { - const mutation = mutations.useUploadItemThumbnailFeedback; - const { id } = FolderItemFactory(); - - it('Upload thumbnail', async () => { - const route = `/${buildUploadItemThumbnailRoute(id)}`; - - // set data in cache - Object.values(ThumbnailSize).forEach((size) => { - const key = itemKeys.single(id).thumbnail({ size }); - queryClient.setQueryData(key, 'thumbnail'); - }); - - const response = THUMBNAIL_BLOB_RESPONSE; - - const endpoints = [ - { - response, - method: HttpMethod.Post, - route, - }, - ]; - - const mockedMutation = await mockMutation({ - endpoints, - mutation, - wrapper, - }); - - await act(async () => { - mockedMutation.mutate({ id, data: [id] }); - await waitForMutation(); - }); - - // verify item is still available - // in real cases, the path should be different - for (const size of Object.values(ThumbnailSize)) { - const key = itemKeys.single(id).thumbnail({ size }); - const state = queryClient.getQueryState(key); - expect(state?.isInvalidated).toBeTruthy(); - } - expect(mockedNotifier).toHaveBeenCalledWith({ - type: uploadItemThumbnailRoutine.SUCCESS, - payload: { message: SUCCESS_MESSAGES.UPLOAD_ITEM_THUMBNAIL }, - }); - }); - - it('Unauthorized to upload a thumbnail', async () => { - const route = `/${buildUploadItemThumbnailRoute(id)}`; - // set data in cache - Object.values(ThumbnailSize).forEach((size) => { - const key = itemKeys.single(id).thumbnail({ size }); - queryClient.setQueryData(key, 'thumbnail'); - }); - - const response = UNAUTHORIZED_RESPONSE; - - const endpoints = [ - { - response, - statusCode: StatusCodes.UNAUTHORIZED, - method: HttpMethod.Post, - route, - }, - ]; - - const mockedMutation = await mockMutation({ - endpoints, - mutation, - wrapper, - }); - - const error = new Error(`${StatusCodes.UNAUTHORIZED}`); - - await act(async () => { - mockedMutation.mutate({ - id, - error, - }); - await waitForMutation(); - }); - - // verify item is still available - // in real cases, the path should be different - for (const size of Object.values(ThumbnailSize)) { - const key = itemKeys.single(id).thumbnail({ size }); - const state = queryClient.getQueryState(key); - expect(state?.isInvalidated).toBeTruthy(); - } - expect(mockedNotifier).toHaveBeenCalledWith({ - type: uploadItemThumbnailRoutine.FAILURE, - payload: { - error, - }, - }); - }); -}); - describe('useDeleteItemThumbnail', () => { const mutation = mutations.useDeleteItemThumbnail; const items = generateFolders(1); diff --git a/src/member/api.ts b/src/member/api.ts index 81289f316..8d8013a75 100644 --- a/src/member/api.ts +++ b/src/member/api.ts @@ -165,19 +165,6 @@ export const uploadAvatar = async ( .then(({ data }) => data); }; -export const downloadAvatar = async ( - { id, size = DEFAULT_THUMBNAIL_SIZE }: { id: UUID; size?: string }, - { API_HOST, axios }: PartialQueryConfigForApi, -) => - axios - .get( - `${API_HOST}/${buildDownloadAvatarRoute({ id, size, replyUrl: false })}`, - { - responseType: 'blob', - }, - ) - .then(({ data }) => data); - export const downloadAvatarUrl = async ( { id, size = DEFAULT_THUMBNAIL_SIZE }: { id: UUID; size?: string }, { API_HOST, axios }: PartialQueryConfigForApi, diff --git a/src/member/hooks.test.ts b/src/member/hooks.test.ts index 6452f10d7..5299d92d8 100644 --- a/src/member/hooks.test.ts +++ b/src/member/hooks.test.ts @@ -11,7 +11,6 @@ import nock from 'nock'; import { afterEach, describe, expect, it } from 'vitest'; import { - AVATAR_BLOB_RESPONSE, AVATAR_URL_RESPONSE, FILE_NOT_FOUND_RESPONSE, UNAUTHORIZED_RESPONSE, @@ -120,111 +119,6 @@ describe('Member Hooks', () => { }); }); - describe('useAvatar', () => { - const account = AccountFactory(); - const replyUrl = false; - const response = AVATAR_BLOB_RESPONSE; - const route = `/${buildDownloadAvatarRoute({ id: account.id, replyUrl })}`; - const hook = () => hooks.useAvatar({ id: account.id }); - const key = memberKeys.single(account.id).avatar({ replyUrl }); - - it(`Receive default avatar`, async () => { - const endpoints = [ - { route, response, headers: { 'Content-Type': 'image/jpeg' } }, - ]; - const { data } = await mockHook({ endpoints, hook, wrapper }); - - expect(data).toBeTruthy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeTruthy(); - }); - - it(`Receive large avatar`, async () => { - const size = ThumbnailSize.Large; - const routeLarge = `/${buildDownloadAvatarRoute({ - id: account.id, - replyUrl, - size, - })}`; - const hookLarge = () => hooks.useAvatar({ id: account.id, size }); - const keyLarge = memberKeys.single(account.id).avatar({ size, replyUrl }); - - const endpoints = [ - { - route: routeLarge, - response, - headers: { 'Content-Type': 'image/jpeg' }, - }, - ]; - const { data } = await mockHook({ - endpoints, - hook: hookLarge, - wrapper, - }); - - expect(data).toBeTruthy(); - // verify cache keys - expect(queryClient.getQueryData(keyLarge)).toBeTruthy(); - }); - - it(`Undefined id does not fetch`, async () => { - const endpoints = [ - { - route, - response, - }, - ]; - const { data, isFetched } = await mockHook({ - endpoints, - hook: () => hooks.useAvatar({ id: undefined }), - wrapper, - enabled: false, - }); - - expect(data).toBeFalsy(); - expect(isFetched).toBeFalsy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeFalsy(); - }); - - it(`Error fetching avatar`, async () => { - const endpoints = [ - { - route, - response: FILE_NOT_FOUND_RESPONSE, - statusCode: StatusCodes.NOT_FOUND, - }, - ]; - const { data, isFetched, isError } = await mockHook({ - endpoints, - hook: () => hooks.useAvatar({ id: account.id }), - wrapper, - }); - - expect(data).toBeFalsy(); - expect(isFetched).toBeTruthy(); - expect(isError).toBeTruthy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeFalsy(); - }); - - it(`Unauthorized`, async () => { - const endpoints = [ - { - route, - response: UNAUTHORIZED_RESPONSE, - statusCode: StatusCodes.UNAUTHORIZED, - }, - ]; - const { data, isError } = await mockHook({ endpoints, hook, wrapper }); - - expect(data).toBeFalsy(); - expect(isError).toBeTruthy(); - // verify cache keys - expect(queryClient.getQueryData(key)).toBeFalsy(); - }); - }); - describe('useAvatarUrl', () => { const member = AccountFactory(); const replyUrl = true; @@ -277,7 +171,7 @@ describe('Member Hooks', () => { ]; const { data, isFetched } = await mockHook({ endpoints, - hook: () => hooks.useAvatar({ id: undefined }), + hook: () => hooks.useAvatarUrl({ id: undefined }), wrapper, enabled: false, }); @@ -298,7 +192,7 @@ describe('Member Hooks', () => { ]; const { data, isFetched, isError } = await mockHook({ endpoints, - hook: () => hooks.useAvatar({ id: member.id }), + hook: () => hooks.useAvatarUrl({ id: member.id }), wrapper, }); diff --git a/src/member/hooks.ts b/src/member/hooks.ts index fda3b86f8..30244e559 100644 --- a/src/member/hooks.ts +++ b/src/member/hooks.ts @@ -35,39 +35,6 @@ export default (queryConfig: QueryClientConfig) => { ...defaultQueryOptions, }), - useAvatar: ({ - id, - size = DEFAULT_THUMBNAIL_SIZE, - }: { - id?: UUID; - size?: string; - }) => { - const queryClient = useQueryClient(); - let shouldFetch = true; - if (id) { - // TODO: this casting is totally wrong, but allows to work for current member - // to be fixed - shouldFetch = - ( - queryClient.getQueryData( - memberKeys.single(id).content, - ) as CompleteMember - )?.extra?.hasAvatar ?? true; - } - return useQuery({ - queryKey: memberKeys.single(id).avatar({ size, replyUrl: false }), - queryFn: () => { - if (!id) { - throw new UndefinedArgument(); - } - return Api.downloadAvatar({ id, size }, queryConfig); - }, - ...defaultQueryOptions, - enabled: Boolean(id) && shouldFetch, - staleTime: CONSTANT_KEY_STALE_TIME_MILLISECONDS, - }); - }, - // use another hook because of key content useAvatarUrl: ({ id, diff --git a/test/constants.ts b/test/constants.ts index 9d8fad5dd..e5a212587 100644 --- a/test/constants.ts +++ b/test/constants.ts @@ -169,19 +169,7 @@ export const ITEM_LOGIN_RESPONSE: ItemLoginSchema = { status: ItemLoginSchemaStatus.Active, }; -const BlobMock = { - blob: () => 'blob', -}; - -export const FILE_RESPONSE = 'somedata'; - -export const S3_FILE_RESPONSE = { - key: 'someurl', -}; -export const S3_FILE_BLOB_RESPONSE = BlobMock; -export const THUMBNAIL_BLOB_RESPONSE = BlobMock; export const THUMBNAIL_URL_RESPONSE = 'some-thumbnail-url'; -export const AVATAR_BLOB_RESPONSE = BlobMock; export const AVATAR_URL_RESPONSE = 'some-avatar-url'; export const buildMentionResponse = (