diff --git a/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.spec.tsx b/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.spec.tsx index c5945be6c8..42b6883b49 100644 --- a/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.spec.tsx +++ b/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.spec.tsx @@ -96,6 +96,8 @@ describe('', () => { modelName.DepositedFiles, depositedFile.id, file, + modelName.FileDepositories, + depositedFile.file_depository.id, ); }); diff --git a/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.tsx b/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.tsx index 115612e41b..35fbeb0351 100644 --- a/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.tsx +++ b/src/frontend/apps/lti_site/apps/deposit/components/Dashboard/DashboardStudent/UploadFiles/index.tsx @@ -91,7 +91,13 @@ export const UploadFiles = () => { size: file.size, filename: file.name, }); - addUpload(modelName.DepositedFiles, depositedFile.id, file); + addUpload( + modelName.DepositedFiles, + depositedFile.id, + file, + modelName.FileDepositories, + depositedFile.file_depository.id, + ); refreshDepositedFiles(); } catch (error) { if ((error as object).hasOwnProperty('size') && metadata.data) { diff --git a/src/frontend/apps/lti_site/components/LTIRoutes/index.tsx b/src/frontend/apps/lti_site/components/LTIRoutes/index.tsx index 6a550e012c..daae8f99ad 100644 --- a/src/frontend/apps/lti_site/components/LTIRoutes/index.tsx +++ b/src/frontend/apps/lti_site/components/LTIRoutes/index.tsx @@ -8,6 +8,7 @@ import { UploadForm, UploadHandlers, UploadManager, + UploadingObject, WithParams, builderFullScreenErrorRoute, modelName, @@ -130,11 +131,13 @@ export const LTIInnerRoutes = () => { path={UPLOAD_FORM_ROUTE.default} element={ - {({ objectId, objectType }) => + {({ objectId, objectType, parentType, parentId }) => objectId && objectType ? ( ) : ( ; + parentId?: Maybe; } -export const UploadField = ({ objectType, objectId }: UploadFieldProps) => { +export const UploadField = ({ + objectType, + objectId, + parentType, + parentId, +}: UploadFieldProps) => { const { addUpload } = useUploadManager(); const [file, setFile] = useState>(undefined); const intl = useIntl(); const onDrop = (files: File[]) => { setFile(files[0]); - addUpload(objectType, objectId, files[0]); + addUpload(objectType, objectId, files[0], parentType, parentId); }; return ( diff --git a/src/frontend/packages/lib_components/src/common/UploadForm/index.tsx b/src/frontend/packages/lib_components/src/common/UploadForm/index.tsx index fdae484570..c048ddd1af 100644 --- a/src/frontend/packages/lib_components/src/common/UploadForm/index.tsx +++ b/src/frontend/packages/lib_components/src/common/UploadForm/index.tsx @@ -14,6 +14,7 @@ import { Loader } from '@lib-components/common/Loader'; import { UploadField } from '@lib-components/common/UploadField'; import { UploadManagerStatus, + UploadingObject, useUploadManager, } from '@lib-components/common/UploadManager'; import { builderDashboardRoute } from '@lib-components/data/routes'; @@ -113,9 +114,16 @@ const UploadFormBack = styled.div` export interface UploadFormProps { objectId: UploadableObject['id']; objectType: uploadableModelName; + parentType?: Maybe; + parentId?: Maybe; } -export const UploadForm = ({ objectId, objectType }: UploadFormProps) => { +export const UploadForm = ({ + objectId, + objectType, + parentType, + parentId, +}: UploadFormProps) => { const appData = useAppConfig(); const { uploadManagerState, resetUpload } = useUploadManager(); const objectStatus = uploadManagerState[objectId]?.status; @@ -199,7 +207,9 @@ export const UploadForm = ({ objectId, objectType }: UploadFormProps) => { /> - + diff --git a/src/frontend/packages/lib_components/src/common/UploadManager/index.tsx b/src/frontend/packages/lib_components/src/common/UploadManager/index.tsx index 4d482fb5e7..9a513da7d6 100644 --- a/src/frontend/packages/lib_components/src/common/UploadManager/index.tsx +++ b/src/frontend/packages/lib_components/src/common/UploadManager/index.tsx @@ -1,3 +1,4 @@ +import { Maybe } from '@lib-common/types'; import React, { createContext, useCallback, @@ -31,6 +32,12 @@ export interface UploadingObject { progress: number; status: UploadManagerStatus; message?: string; + parentType?: + | modelName.VIDEOS + | MarkdownDocumentModelName.MARKDOWN_DOCUMENTS + | ClassroomModelName.CLASSROOMS + | FileDepositoryModelName.FileDepositories; + parentId?: string; } export interface UploadManagerState { @@ -82,7 +89,7 @@ export const UploadManager = ({ Object.values(uploadManagerState) .filter(({ status }) => status === UploadManagerStatus.INIT) - .forEach(({ file, objectId, objectType }) => { + .forEach(({ file, objectId, objectType, parentId, parentType }) => { (async () => { let presignedPost: AWSPresignedPost; try { @@ -92,6 +99,8 @@ export const UploadManager = ({ file.name, file.type, file.size, + parentType, + parentId, ); } catch (error) { if ((error as ApiException).type === 'SizeError') { @@ -196,7 +205,13 @@ export const useUploadManager = () => { useContext(UploadManagerContext); const addUpload = useCallback( - (objectType: uploadableModelName, objectId: string, file: File) => { + ( + objectType: uploadableModelName, + objectId: string, + file: File, + parentType?: Maybe, + parentId?: Maybe, + ) => { setUploadState((state) => ({ ...state, [objectId]: { @@ -205,6 +220,8 @@ export const useUploadManager = () => { file, progress: 0, status: UploadManagerStatus.INIT, + parentType, + parentId, }, })); }, diff --git a/src/frontend/packages/lib_components/src/data/sideEffects/initiateUpload/index.ts b/src/frontend/packages/lib_components/src/data/sideEffects/initiateUpload/index.ts index f0f4d02879..fb74d4be7d 100644 --- a/src/frontend/packages/lib_components/src/data/sideEffects/initiateUpload/index.ts +++ b/src/frontend/packages/lib_components/src/data/sideEffects/initiateUpload/index.ts @@ -1,3 +1,6 @@ +import { Maybe } from '@lib-common/types'; + +import { UploadingObject } from '@lib-components/common'; import { fetchWrapper } from '@lib-components/common/queries/fetchWrapper'; import { useJwt } from '@lib-components/hooks/stores'; import { API_ENDPOINT } from '@lib-components/settings'; @@ -10,6 +13,11 @@ import { UploadableObject } from '@lib-components/types/tracks'; * policy to authenticate this upload with S3. * @param objectType The kind of object for which we're uploading a file (model name). * @param objectId The ID of the object for which we're uploading a file. + * @param filename The name of the file we're uploading. + * @param mimetype The mimetype of the file we're uploading. + * @param size The size of the file we're uploading. + * @param parentType The kind of parent object for which we're uploading a file (nullable model name). + * @param parentId The ID of the parent object for which we're uploading a file (nullable). */ export const initiateUpload = async ( objectType: uploadableModelName, @@ -17,22 +25,25 @@ export const initiateUpload = async ( filename: string, mimetype: string, size: number, + parentType?: Maybe, + parentId?: Maybe, ) => { - const response = await fetchWrapper( - `${API_ENDPOINT}/${objectType}/${objectId}/initiate-upload/`, - { - body: JSON.stringify({ - filename, - mimetype, - size, - }), - headers: { - Authorization: `Bearer ${useJwt.getState().getJwt() ?? ''}`, - 'Content-Type': 'application/json', - }, - method: 'POST', + let input = `${API_ENDPOINT}/${objectType}/${objectId}/initiate-upload/`; + if (parentId && parentType) { + input = `${API_ENDPOINT}/${parentType}/${parentId}/${objectType}/${objectId}/initiate-upload/`; + } + const response = await fetchWrapper(input, { + body: JSON.stringify({ + filename, + mimetype, + size, + }), + headers: { + Authorization: `Bearer ${useJwt.getState().getJwt() ?? ''}`, + 'Content-Type': 'application/json', }, - ); + method: 'POST', + }); if (!response.ok) { const contentType = response.headers.get('content-type'); diff --git a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx index c021391361..efa7396958 100644 --- a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.spec.tsx @@ -551,7 +551,7 @@ describe('', () => { // Drop an image const markdownImageId = '5459a5b2-2f81-11ed-ab8f-47c92ec0ac16'; fetchMock.postOnce( - `/api/markdown-images/`, + `/api/markdown-documents/1/markdown-images/`, markdownImageMockFactory({ id: markdownImageId, active_stamp: null, @@ -562,7 +562,7 @@ describe('', () => { }), ); fetchMock.postOnce( - `/api/markdown-images/${markdownImageId}/initiate-upload/`, + `/api/markdown-documents/1/markdown-images/${markdownImageId}/initiate-upload/`, { fields: { key: 'foo', @@ -589,7 +589,7 @@ describe('', () => { // Image is uploaded fetchMock.get( - `/api/markdown-images/${markdownImageId}/`, + `/api/markdown-documents/1/markdown-images/${markdownImageId}/`, markdownImageMockFactory({ id: markdownImageId, is_ready_to_show: true, diff --git a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx index 275adf1d18..16e7547066 100644 --- a/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MarkdownEditor/index.tsx @@ -125,7 +125,10 @@ export const MarkdownEditor = ({ markdownDocumentId }: MarkdownEditorProps) => { ); }; - const { addImageUpload } = useImageUploadManager(onImageUploadFinished); + const { addImageUpload } = useImageUploadManager( + markdownDocumentId, + onImageUploadFinished, + ); // note: we don't want to fetch the markdown document regularly to prevent // any editor update while the user has not saved her document. diff --git a/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.spec.tsx b/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.spec.tsx index 3d77fb25d0..7ae39b0c36 100644 --- a/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.spec.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.spec.tsx @@ -381,7 +381,7 @@ describe('', () => { const markdownText = fs.readFileSync(file, { encoding: 'utf8' }); fetchMock.getOnce( - '/api/markdown-images/981a52a2-7caf-49a5-bb36-1d8512152214/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/981a52a2-7caf-49a5-bb36-1d8512152214/`, markdownImageMockFactory({ id: '981a52a2-7caf-49a5-bb36-1d8512152214', url: 'https://s3.link/easy-to-find.png', @@ -389,7 +389,7 @@ describe('', () => { ); fetchMock.getOnce( - '/api/markdown-images/066036cc-2dde-11ed-89f4-afcf72a20b4c/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/066036cc-2dde-11ed-89f4-afcf72a20b4c/`, markdownImageMockFactory({ id: '066036cc-2dde-11ed-89f4-afcf72a20b4c', url: null, diff --git a/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.tsx b/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.tsx index b4aa71d05a..c824071212 100644 --- a/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MdxRenderer/index.tsx @@ -90,7 +90,10 @@ export const MdxRenderer = ({ remarkLatexPlugin(markdownDocumentId), remarkMermaidPlugin, remarkMath, - remarkLocallyHostedImagePlugin(localImagesUrlCache.current), + remarkLocallyHostedImagePlugin( + markdownDocumentId, + localImagesUrlCache.current, + ), ]; const rehypePlugins: PluggableList = [ options?.useMathjax ? rehypeMathjax : rehypeKatex, diff --git a/src/frontend/packages/lib_markdown/src/components/MdxRenderer/remarkLocallyHostedImagePlugin.tsx b/src/frontend/packages/lib_markdown/src/components/MdxRenderer/remarkLocallyHostedImagePlugin.tsx index 03ea4a3ea9..aba6c03bef 100644 --- a/src/frontend/packages/lib_markdown/src/components/MdxRenderer/remarkLocallyHostedImagePlugin.tsx +++ b/src/frontend/packages/lib_markdown/src/components/MdxRenderer/remarkLocallyHostedImagePlugin.tsx @@ -11,6 +11,7 @@ import { fetchOneMarkdownImage } from '@lib-markdown/data/queries'; import { MarkdownImageCache } from './types'; const remarkLocallyHostedImagePlugin = ( + markdownDocumentId: string, localImagesUrlCache: MarkdownImageCache, ) => { // `markdownDocumentId` is mandatory to allow API calls @@ -41,7 +42,10 @@ const remarkLocallyHostedImagePlugin = ( } if (image.url.startsWith('/uploaded/image/')) { - const response = await fetchOneMarkdownImage(imageId); + const response = await fetchOneMarkdownImage( + markdownDocumentId, + imageId, + ); if (response.url) { localImagesUrlCache[imageId] = { url: response.url, diff --git a/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.spec.tsx b/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.spec.tsx index bb90e94e99..fa4f92c2bf 100644 --- a/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.spec.tsx +++ b/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.spec.tsx @@ -23,13 +23,17 @@ jest.mock('lib-components', () => ({ describe('useImageUploadManager', () => { let getLatestUseImageUploadManagerHookValues: () => any = () => {}; let getLatestUseUploadManagerHookValues: () => any = () => {}; + const markdownDocumentId = 'truc'; const onImageUploadFinished = jest.fn(); const TestComponent = () => { const uploadManager = useUploadManager(); getLatestUseUploadManagerHookValues = () => uploadManager; - const imageUploadManager = useImageUploadManager(onImageUploadFinished); + const imageUploadManager = useImageUploadManager( + markdownDocumentId, + onImageUploadFinished, + ); getLatestUseImageUploadManagerHookValues = () => imageUploadManager; return null; }; @@ -49,8 +53,8 @@ describe('useImageUploadManager', () => { const initiateUploadDeferred = new Deferred(); - const mockcreateMarkdownImage = fetchMock.postOnce( - `/api/markdown-images/`, + const mockCreateMarkdownImage = fetchMock.postOnce( + `/api/markdown-documents/${markdownDocumentId}/markdown-images/`, markdownImageMockFactory({ id: objectId, active_stamp: null, @@ -62,7 +66,7 @@ describe('useImageUploadManager', () => { ); const mockInitiateUpload = fetchMock.postOnce( - `/api/markdown-images/${objectId}/initiate-upload/`, + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${objectId}/initiate-upload/`, initiateUploadDeferred.promise, ); @@ -93,15 +97,19 @@ describe('useImageUploadManager', () => { file, progress: 0, status: UploadManagerStatus.INIT, + parentType: modelName.MARKDOWN_DOCUMENTS, + parentId: markdownDocumentId, }, }); expect( mockInitiateUpload.calls( - `/api/markdown-images/${objectId}/initiate-upload/`, + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${objectId}/initiate-upload/`, ), ).toHaveLength(1); expect( - mockcreateMarkdownImage.calls(`/api/markdown-images/`), + mockCreateMarkdownImage.calls( + `/api/markdown-documents/${markdownDocumentId}/markdown-images/`, + ), ).toHaveLength(1); } { @@ -122,6 +130,8 @@ describe('useImageUploadManager', () => { file, progress: 0, status: UploadManagerStatus.UPLOADING, + parentType: modelName.MARKDOWN_DOCUMENTS, + parentId: markdownDocumentId, }, }); expect(screen.getByRole('status')).toHaveTextContent('course.gif0%'); @@ -129,7 +139,7 @@ describe('useImageUploadManager', () => { // When status will turn to SUCCESS the image polling will start fetchMock.get( - `/api/markdown-images/${objectId}/`, + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${objectId}/`, markdownImageMockFactory({ id: objectId, is_ready_to_show: false, @@ -152,6 +162,8 @@ describe('useImageUploadManager', () => { file, progress: 0, status: UploadManagerStatus.SUCCESS, + parentType: modelName.MARKDOWN_DOCUMENTS, + parentId: markdownDocumentId, }, }); expect(screen.getByRole('status')).toHaveTextContent( @@ -162,7 +174,7 @@ describe('useImageUploadManager', () => { { act(() => { fetchMock.get( - `/api/markdown-images/${objectId}/`, + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${objectId}/`, markdownImageMockFactory({ id: objectId, is_ready_to_show: true, diff --git a/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.tsx b/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.tsx index 8a1f04d203..15a5d751fe 100644 --- a/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.tsx +++ b/src/frontend/packages/lib_markdown/src/components/useImageUploadManager/index.tsx @@ -33,6 +33,7 @@ const toasterStyle = { }; export const useImageUploadManager = ( + markdownDocumentId: string, onImageUploadFinished: (imageId: string, imageFileName: string) => void, ) => { const intl = useIntl(); @@ -55,7 +56,7 @@ export const useImageUploadManager = ( // Once the update is done, the file will be processed, we have to wait for the processing // to be done too, hence the polling. await toast.promise( - pollForMarkdownImage(imageId), + pollForMarkdownImage(markdownDocumentId, imageId), { loading: intl.formatMessage(messages.processing, { imageName: uploadingObject.file.name, @@ -83,16 +84,28 @@ export const useImageUploadManager = ( resetUpload(imageId); } }); - }, [intl, onImageUploadFinished, resetUpload, uploadManagerState]); + }, [ + intl, + markdownDocumentId, + onImageUploadFinished, + resetUpload, + uploadManagerState, + ]); const addImageUpload = useCallback( async (file: File) => { - const response = await createMarkdownImage(); + const response = await createMarkdownImage(markdownDocumentId); const markdownImageId = response.id; - addUpload(modelName.MARKDOWN_IMAGES, markdownImageId, file); + addUpload( + modelName.MARKDOWN_IMAGES, + markdownImageId, + file, + modelName.MARKDOWN_DOCUMENTS, + markdownDocumentId, + ); return markdownImageId; }, - [addUpload], + [addUpload, markdownDocumentId], ); return { addImageUpload }; diff --git a/src/frontend/packages/lib_markdown/src/data/queries/index.tsx b/src/frontend/packages/lib_markdown/src/data/queries/index.tsx index 3463bf7321..488c6bf3ab 100644 --- a/src/frontend/packages/lib_markdown/src/data/queries/index.tsx +++ b/src/frontend/packages/lib_markdown/src/data/queries/index.tsx @@ -212,10 +212,16 @@ export const markdownRenderLatex = ( }; // It has to be called outside hook context -export const fetchOneMarkdownImage = (markdownImageId: string): any => { +export const fetchOneMarkdownImage = ( + markdownDocumentId: string, + markdownImageId: string, +): any => { return fetchOne({ meta: undefined, pageParam: undefined, - queryKey: ['markdown-images', markdownImageId], + queryKey: [ + `markdown-documents/${markdownDocumentId}/markdown-images`, + markdownImageId, + ], }); }; diff --git a/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.spec.tsx b/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.spec.tsx index bc17d7b4e4..a008009193 100644 --- a/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.spec.tsx +++ b/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.spec.tsx @@ -1,9 +1,11 @@ import fetchMock from 'fetch-mock'; import { useJwt } from 'lib-components'; +import { v4 as uuidv4 } from 'uuid'; import { createMarkdownImage } from './index'; describe('createMarkdownImage', () => { + const markdownDocumentId = uuidv4(); beforeEach(() => { useJwt.setState({ jwt: 'token', @@ -13,17 +15,20 @@ describe('createMarkdownImage', () => { afterEach(() => fetchMock.restore()); it('creates a new shared live media and returns it', async () => { - fetchMock.mock('/api/markdown-images/', { - active_stamp: null, - filename: null, - id: '5570eb90-764e-4300-b92e-d3426e9046d2', - is_ready_to_show: false, - upload_state: 'pending', - url: null, - markdown_document: '72f53735-3283-456c-a562-4e1b59e2a686', - }); + fetchMock.mock( + `/api/markdown-documents/${markdownDocumentId}/markdown-images/`, + { + active_stamp: null, + filename: null, + id: '5570eb90-764e-4300-b92e-d3426e9046d2', + is_ready_to_show: false, + upload_state: 'pending', + url: null, + markdown_document: markdownDocumentId, + }, + ); - const markdownImage = await createMarkdownImage(); + const markdownImage = await createMarkdownImage(markdownDocumentId); const fetchArgs = fetchMock.lastCall()![1]!; @@ -34,7 +39,7 @@ describe('createMarkdownImage', () => { is_ready_to_show: false, upload_state: 'pending', url: null, - markdown_document: '72f53735-3283-456c-a562-4e1b59e2a686', + markdown_document: markdownDocumentId, }); expect(fetchArgs.headers).toEqual({ Authorization: 'Bearer token', @@ -45,19 +50,22 @@ describe('createMarkdownImage', () => { it('throws when it fails to create the Markdown image (request failure)', async () => { fetchMock.mock( - '/api/markdown-images/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/`, Promise.reject(new Error('Failed to perform the request')), ); - await expect(createMarkdownImage()).rejects.toThrow( + await expect(createMarkdownImage(markdownDocumentId)).rejects.toThrow( 'Failed to perform the request', ); }); it('throws when it fails to create the Markdown image (API error)', async () => { - fetchMock.mock('/api/markdown-images/', 400); + fetchMock.mock( + `/api/markdown-documents/${markdownDocumentId}/markdown-images/`, + 400, + ); - await expect(createMarkdownImage()).rejects.toThrow( + await expect(createMarkdownImage(markdownDocumentId)).rejects.toThrow( 'Failed to create a new markdown image.', ); }); diff --git a/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.ts b/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.ts index 7d19d3ae7f..4ac7a4605d 100644 --- a/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.ts +++ b/src/frontend/packages/lib_markdown/src/data/sideEffects/createMarkdownImage/index.ts @@ -8,9 +8,9 @@ import { useJwt, } from 'lib-components'; -export const createMarkdownImage = async () => { +export const createMarkdownImage = async (markdownDocumentId: string) => { const response = await fetchWrapper( - `${API_ENDPOINT}/${modelName.MARKDOWN_IMAGES}/`, + `${API_ENDPOINT}/${modelName.MARKDOWN_DOCUMENTS}/${markdownDocumentId}/${modelName.MARKDOWN_IMAGES}/`, { headers: { Authorization: `Bearer ${useJwt.getState().getJwt()}`, diff --git a/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.spec.tsx b/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.spec.tsx index 4c22cce959..0500208fc1 100644 --- a/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.spec.tsx +++ b/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.spec.tsx @@ -2,6 +2,7 @@ import { waitFor } from '@testing-library/react'; import fetchMock from 'fetch-mock'; import { markdownImageMockFactory } from 'index'; import { report } from 'lib-components'; +import { v4 as uuidv4 } from 'uuid'; import { pollForMarkdownImage } from './index'; @@ -10,6 +11,9 @@ jest.mock('lib-components', () => ({ report: jest.fn(), })); +const markdownDocumentId = uuidv4(); +const markdownImageId = uuidv4(); + describe('pollForMarkdownImage', () => { beforeEach(() => { jest.clearAllMocks(); @@ -22,10 +26,10 @@ describe('pollForMarkdownImage', () => { it('polls the image, backing off until it is ready and resolves with a success', async () => { fetchMock.mock( - '/api/markdown-images/c43f0c8f-4d3b-4219-86c3-86367b2b88cc/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/`, JSON.stringify( markdownImageMockFactory({ - id: 'c43f0c8f-4d3b-4219-86c3-86367b2b88cc', + id: markdownImageId, is_ready_to_show: false, }), ), @@ -33,14 +37,15 @@ describe('pollForMarkdownImage', () => { ); const promise = pollForMarkdownImage( - 'c43f0c8f-4d3b-4219-86c3-86367b2b88cc', + markdownDocumentId, + markdownImageId, 1, ); await waitFor(() => { expect( fetchMock.calls( - '/api/markdown-images/c43f0c8f-4d3b-4219-86c3-86367b2b88cc/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/`, { method: 'GET', }, @@ -49,11 +54,11 @@ describe('pollForMarkdownImage', () => { }); const markdownImage = markdownImageMockFactory({ - id: 'c43f0c8f-4d3b-4219-86c3-86367b2b88cc', + id: markdownImageId, is_ready_to_show: true, }); fetchMock.mock( - '/api/markdown-images/c43f0c8f-4d3b-4219-86c3-86367b2b88cc/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/`, JSON.stringify(markdownImage), { method: 'GET', @@ -64,7 +69,7 @@ describe('pollForMarkdownImage', () => { await waitFor(() => { expect( fetchMock.calls( - '/api/markdown-images/c43f0c8f-4d3b-4219-86c3-86367b2b88cc/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/`, { method: 'GET', }, @@ -77,26 +82,26 @@ describe('pollForMarkdownImage', () => { it('polls non-existing image', async () => { fetchMock.mock( - '/api/markdown-images/c43f0c8f-4d3b-4219-86c3-86367b2b88cc/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/`, 404, { method: 'GET' }, ); await expect(async () => { - await pollForMarkdownImage('c43f0c8f-4d3b-4219-86c3-86367b2b88cc'); + await pollForMarkdownImage(markdownDocumentId, markdownImageId); }).rejects.toThrow( - 'Failed to get /api/markdown-images/c43f0c8f-4d3b-4219-86c3-86367b2b88cc/.', + `Failed to get /api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/.`, ); expect(report).toHaveBeenCalledWith( Error( - 'Failed to get /api/markdown-images/c43f0c8f-4d3b-4219-86c3-86367b2b88cc/.', + `Failed to get /api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/.`, ), ); }); it('resolves with a failure and reports it when it fails to poll the image', async () => { fetchMock.mock( - '/api/markdown-images/15cf570a-5dc6-421a-9856-59e1b008a6fb/', + `/api/markdown-documents/${markdownDocumentId}/markdown-images/${markdownImageId}/`, Promise.reject(new Error('Failed to get the image')), { method: 'GET', @@ -105,7 +110,7 @@ describe('pollForMarkdownImage', () => { ); await expect(async () => { - await pollForMarkdownImage('15cf570a-5dc6-421a-9856-59e1b008a6fb'); + await pollForMarkdownImage(markdownDocumentId, markdownImageId); }).rejects.toThrow('Failed to get the image'); expect(report).toHaveBeenCalledWith(Error('Failed to get the image')); diff --git a/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.tsx b/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.tsx index a529c35e4e..c04d675a50 100644 --- a/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.tsx +++ b/src/frontend/packages/lib_markdown/src/data/sideEffects/pollForMarkdownImage/index.tsx @@ -6,12 +6,13 @@ import { MarkdownImage, report } from 'lib-components'; import { fetchOneMarkdownImage } from '@lib-markdown/data/queries'; export async function pollForMarkdownImage( - resourceId: string, + documentId: string, + imageId: string, timer = 15, counter = 1, ): Promise { try { - const image = await fetchOneMarkdownImage(resourceId); + const image = await fetchOneMarkdownImage(documentId, imageId); if (image.is_ready_to_show) { return image; @@ -19,7 +20,7 @@ export async function pollForMarkdownImage( counter++; timer = timer * counter; await new Promise((resolve) => window.setTimeout(resolve, 100 * timer)); - return await pollForMarkdownImage(resourceId, timer, counter); + return await pollForMarkdownImage(documentId, imageId, timer, counter); } } catch (error) { report(error);