Skip to content

Commit

Permalink
🚚(frontend) use nested routes for markdown related resources
Browse files Browse the repository at this point in the history
As we want to only use API nested routes, our markdown components needs
to handle them.
  • Loading branch information
kernicPanel committed Aug 10, 2023
1 parent 8f35f7a commit dc7204f
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ describe('<MarkdownEditor />', () => {
// 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,
Expand All @@ -562,7 +562,7 @@ describe('<MarkdownEditor />', () => {
}),
);
fetchMock.postOnce(
`/api/markdown-images/${markdownImageId}/initiate-upload/`,
`/api/markdown-documents/1/markdown-images/${markdownImageId}/initiate-upload/`,
{
fields: {
key: 'foo',
Expand All @@ -589,7 +589,7 @@ describe('<MarkdownEditor />', () => {

// Image is uploaded
fetchMock.get(
`/api/markdown-images/${markdownImageId}/`,
`/api/markdown-documents/1/markdown-images/${markdownImageId}/`,
markdownImageMockFactory({
id: markdownImageId,
is_ready_to_show: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,15 +381,15 @@ describe('<MdxRenderer />', () => {
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',
}),
);

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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand All @@ -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,
Expand All @@ -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,
);

Expand Down Expand Up @@ -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);
}
{
Expand All @@ -122,14 +130,16 @@ describe('useImageUploadManager', () => {
file,
progress: 0,
status: UploadManagerStatus.UPLOADING,
parentType: modelName.MARKDOWN_DOCUMENTS,
parentId: markdownDocumentId,
},
});
expect(screen.getByRole('status')).toHaveTextContent('course.gif0%');
}

// 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,
Expand All @@ -152,6 +162,8 @@ describe('useImageUploadManager', () => {
file,
progress: 0,
status: UploadManagerStatus.SUCCESS,
parentType: modelName.MARKDOWN_DOCUMENTS,
parentId: markdownDocumentId,
},
});
expect(screen.getByRole('status')).toHaveTextContent(
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
MarkdownDocument,
UploadManagerStatus,
MarkdownDocumentModelName as modelName,
useUploadManager,
Expand Down Expand Up @@ -33,6 +34,7 @@ const toasterStyle = {
};

export const useImageUploadManager = (
markdownDocumentId: MarkdownDocument['id'],
onImageUploadFinished: (imageId: string, imageFileName: string) => void,
) => {
const intl = useIntl();
Expand All @@ -55,7 +57,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,
Expand Down Expand Up @@ -83,16 +85,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 };
Expand Down
11 changes: 9 additions & 2 deletions src/frontend/packages/lib_markdown/src/data/queries/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
} from '@tanstack/react-query';
import {
MarkdownDocument,
MarkdownImage,
MarkdownSaveTranslationsRequest,
MarkdownSaveTranslationsResponse,
actionOne,
Expand Down Expand Up @@ -212,10 +213,16 @@ export const markdownRenderLatex = (
};

// It has to be called outside hook context
export const fetchOneMarkdownImage = (markdownImageId: string): any => {
export const fetchOneMarkdownImage = (
markdownDocumentId: MarkdownDocument['id'],
markdownImageId: MarkdownImage['id'],
): any => {
return fetchOne({
meta: undefined,
pageParam: undefined,
queryKey: ['markdown-images', markdownImageId],
queryKey: [
`markdown-documents/${markdownDocumentId}/markdown-images`,
markdownImageId,
],
});
};
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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]!;

Expand All @@ -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',
Expand All @@ -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.',
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import {
API_ENDPOINT,
MarkdownDocument,
MarkdownImage,
fetchWrapper,
MarkdownDocumentModelName as modelName,
useJwt,
} from 'lib-components';

export const createMarkdownImage = async () => {
export const createMarkdownImage = async (
markdownDocumentId: MarkdownDocument['id'],
) => {
const response = await fetchWrapper(
`${API_ENDPOINT}/${modelName.MARKDOWN_IMAGES}/`,
`${API_ENDPOINT}/${modelName.MARKDOWN_DOCUMENTS}/${markdownDocumentId}/${modelName.MARKDOWN_IMAGES}/`,
{
headers: {
Authorization: `Bearer ${useJwt.getState().getJwt()}`,
Expand Down
Loading

0 comments on commit dc7204f

Please sign in to comment.