Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
kernicPanel committed Aug 3, 2023
1 parent a9804d6 commit 7a9a6d6
Show file tree
Hide file tree
Showing 28 changed files with 314 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ describe('<UploadFiles />', () => {
modelName.DepositedFiles,
depositedFile.id,
file,
modelName.FileDepositories,
depositedFile.file_depository.id,
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
5 changes: 4 additions & 1 deletion src/frontend/apps/lti_site/components/LTIRoutes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
UploadForm,
UploadHandlers,
UploadManager,
UploadingObject,
WithParams,
builderFullScreenErrorRoute,
modelName,
Expand Down Expand Up @@ -130,11 +131,13 @@ export const LTIInnerRoutes = () => {
path={UPLOAD_FORM_ROUTE.default}
element={
<WithParams>
{({ objectId, objectType }) =>
{({ objectId, objectType, parentType, parentId }) =>
objectId && objectType ? (
<UploadForm
objectId={objectId}
objectType={objectType as modelName}
parentType={parentType as UploadingObject['parentType']}
parentId={parentId}
/>
) : (
<Navigate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ describe('<UploadDocuments />', () => {
modelName.CLASSROOM_DOCUMENTS,
classroomDocument.id,
file,
modelName.CLASSROOMS,
'1',
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,13 @@ export const UploadDocuments = ({ classroomId }: UploadDocumentsProps) => {
size: file.size,
classroom: classroomId,
});
addUpload(modelName.CLASSROOM_DOCUMENTS, document.id, file);
addUpload(
modelName.CLASSROOM_DOCUMENTS,
document.id,
file,
modelName.CLASSROOMS,
classroomId,
);
refreshClassroomDocuments();
} catch (error) {
if ((error as object).hasOwnProperty('size') && metadata.data) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import Dropzone from 'react-dropzone';
import { defineMessages, useIntl } from 'react-intl';
import styled from 'styled-components';

import { useUploadManager } from '@lib-components/common/UploadManager';
import {
UploadingObject,
useUploadManager,
} from '@lib-components/common/UploadManager';
import { uploadableModelName } from '@lib-components/types/models';

import { DropzonePlaceholder } from './DropzonePlaceholder';
Expand All @@ -25,16 +28,23 @@ const DropzoneStyled = styled.div`
export interface UploadFieldProps {
objectType: uploadableModelName;
objectId: string;
parentType?: Maybe<UploadingObject['parentType']>;
parentId?: Maybe<string>;
}

export const UploadField = ({ objectType, objectId }: UploadFieldProps) => {
export const UploadField = ({
objectType,
objectId,
parentType,
parentId,
}: UploadFieldProps) => {
const { addUpload } = useUploadManager();
const [file, setFile] = useState<Maybe<File>>(undefined);
const intl = useIntl();

const onDrop = (files: File[]) => {
setFile(files[0]);
addUpload(objectType, objectId, files[0]);
addUpload(objectType, objectId, files[0], parentType, parentId);
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -113,9 +114,16 @@ const UploadFormBack = styled.div`
export interface UploadFormProps {
objectId: UploadableObject['id'];
objectType: uploadableModelName;
parentType?: Maybe<UploadingObject['parentType']>;
parentId?: Maybe<string>;
}

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;
Expand Down Expand Up @@ -199,7 +207,9 @@ export const UploadForm = ({ objectId, objectType }: UploadFormProps) => {
/>
</IframeHeadingWithLayout>
<UploadFieldContainer>
<UploadField {...{ objectType, objectId }} />
<UploadField
{...{ objectType, objectId, parentType, parentId }}
/>
</UploadFieldContainer>
</UploadFormContainer>
<UploadFormBack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,99 @@ describe('<UploadManager />', () => {
}
});

it('uploads the file with a parent path', async () => {
const objectType = modelName.THUMBNAILS;
const objectId = uuidv4();
const parentType = modelName.VIDEOS;
const parentId = uuidv4();
const file = new File(['(⌐□_□)'], 'course.jpg', { type: 'image/jpeg' });

const initiateUploadDeferred = new Deferred();
const mockInitiateUpload = fetchMock.mock(
`/api/videos/${parentId}/thumbnails/${objectId}/initiate-upload/`,
initiateUploadDeferred.promise,
{ method: 'POST' },
);

const fileUploadDeferred = new Deferred<MockResponse>();
xhrMock.post(
'https://s3.aws.example.com/',
() => fileUploadDeferred.promise,
);

render(
<UploadManager>
<TestComponent />
</UploadManager>,
);

{
const { addUpload, uploadManagerState } = getLatestHookValues();
expect(uploadManagerState).toEqual({});
act(() => addUpload(objectType, objectId, file, parentType, parentId));
}
{
const { uploadManagerState } = getLatestHookValues();
expect(uploadManagerState).toEqual({
[objectId]: {
objectId,
objectType,
file,
progress: 0,
status: UploadManagerStatus.INIT,
parentType,
parentId,
},
});
expect(mockInitiateUpload.calls()).toHaveLength(1);
}
{
await act(async () => {
initiateUploadDeferred.resolve({
fields: {
key: 'foo',
},
url: 'https://s3.aws.example.com/',
});
// We need to wait for the upload to complete before we can check the state
await new Promise((resolve) => setTimeout(resolve, 100));
});
const { uploadManagerState } = getLatestHookValues();
expect(uploadManagerState).toEqual({
[objectId]: {
objectId,
objectType,
file,
progress: 0,
status: UploadManagerStatus.UPLOADING,
parentType,
parentId,
},
});
}
{
await act(async () => {
fileUploadDeferred.resolve(
new MockResponse().body('form data body').status(204),
);
// We need to wait for the upload to complete before we can check the state
await new Promise((resolve) => setTimeout(resolve, 100));
});
const { uploadManagerState } = getLatestHookValues();
expect(uploadManagerState).toEqual({
[objectId]: {
objectId,
objectType,
file,
progress: 0,
status: UploadManagerStatus.SUCCESS,
parentType,
parentId,
},
});
}
});

it('reports the error and does not upload to AWS when it fails to get the policy', async () => {
const objectType = modelName.VIDEOS;
const objectId = uuidv4();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Maybe } from '@lib-common/types';
import React, {
createContext,
useCallback,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -92,6 +99,8 @@ export const UploadManager = ({
file.name,
file.type,
file.size,
parentType,
parentId,
);
} catch (error) {
if ((error as ApiException).type === 'SizeError') {
Expand Down Expand Up @@ -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<UploadingObject['parentType']>,
parentId?: Maybe<string>,
) => {
setUploadState((state) => ({
...state,
[objectId]: {
Expand All @@ -205,6 +220,8 @@ export const useUploadManager = () => {
file,
progress: 0,
status: UploadManagerStatus.INIT,
parentType,
parentId,
},
}));
},
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -10,29 +13,37 @@ 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,
objectId: UploadableObject['id'],
filename: string,
mimetype: string,
size: number,
parentType?: Maybe<UploadingObject['parentType']>,
parentId?: Maybe<string>,
) => {
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');
Expand Down
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
Loading

0 comments on commit 7a9a6d6

Please sign in to comment.