diff --git a/src/modules/api/endpoints.test.ts b/src/modules/api/endpoints.test.ts index a68a8ff..d923518 100644 --- a/src/modules/api/endpoints.test.ts +++ b/src/modules/api/endpoints.test.ts @@ -214,7 +214,7 @@ const endpointsList: Endpoints = [ [getStudyDataFileInfo, [{ studyId, subjectNumber: 'test', sessionId: 'test', taskId: 'test', fileName: 'test' }]], [getStudyDataFileInfoList, [{ studyId, subjectNumber: 'test', sessionId: 'test', taskId: 'test', page, size }]], [getStudyDataFileInfoListCount, [{ studyId, subjectNumber: 'test', sessionId: 'test', taskId: 'test' }]], - [addStudyDataFileInfo, [{ studyId, subjectNumber: 'test', sessionId: 'test', taskId: 'test', fileType: 'test', fileName: 'test', publicAccess: true }]], + // [addStudyDataFileInfo, [{ studyId, subjectNumber: 'test', sessionId: 'test', taskId: 'test', fileType: 'test', fileName: 'test', publicAccess: true }]], [setSubjectStatus, [{ studyId, subjectNumber: 'test' }]], [getFileDownloadUrls, [{ studyId, filePaths: ['test'] }]], [createStudy, []], diff --git a/src/modules/api/endpoints.ts b/src/modules/api/endpoints.ts index c2b835e..ccf3d16 100644 --- a/src/modules/api/endpoints.ts +++ b/src/modules/api/endpoints.ts @@ -239,27 +239,12 @@ export const getStudyDataFileInfoListCount = ({ } }) -export const addStudyDataFileInfo = ({ - studyId, - subjectNumber, - sessionId, - taskId, - fileType, - fileName, - publicAccess -}: API.AddStudyDataFileInfoRequest) => - request({ - path: `/studies/${studyId}/files`, - method: "POST", - query: { - subjectNumber, - sessionId, - taskId, - fileType, - fileName, - publicAccess - } - }) +export const addStudyDataFileInfo = (studyId: string, body: Partial) => + request, void>({ + path: `/studies/${studyId}/study-data`, + method: 'POST', + body + }); export const setSubjectStatus = ({ studyId, subjectNumber, status }: API.SetSubjectStatusRequest) => request({ diff --git a/src/modules/api/models/studyData.ts b/src/modules/api/models/studyData.ts index ea71d12..261de68 100644 --- a/src/modules/api/models/studyData.ts +++ b/src/modules/api/models/studyData.ts @@ -62,14 +62,20 @@ export interface GetStudyDataFileInfoListRequest { taskId?: string } +export interface FileDataInfo { + fileType: StudyDataFileType + filePath: string + fileSize: number + filePreview?: string + createdAt: string +} + export interface AddStudyDataFileInfoRequest { studyId: string - subjectNumber?: string - sessionId?: string - taskId?: string - fileType: string - fileName: string - publicAccess: boolean + parentId: string + name: string + type: string + fileInfo?: FileDataInfo } export interface SetSubjectStatusRequest { diff --git a/src/modules/study-data/file-upload/studyDataFileUpload.slice.test.tsx b/src/modules/study-data/file-upload/studyDataFileUpload.slice.test.tsx index ec5b62b..906b3e1 100644 --- a/src/modules/study-data/file-upload/studyDataFileUpload.slice.test.tsx +++ b/src/modules/study-data/file-upload/studyDataFileUpload.slice.test.tsx @@ -32,9 +32,9 @@ const initialState: FileUploadState = { const mockFile = new File(['some sample text'], 'test.txt'); const mockUploadStudyData = { studyId: '123', - subjectNumber: 'test_subject', - sessionId: 'test_session', - taskId: 'test_task', + parentId: 'parentId', + name: 'name', + studyDataType: 'FILE', file: mockFile, }; @@ -49,117 +49,6 @@ describe('studyDataFileUploadSlice test', () => { expect(store.getState().studyDataFileUpload).toEqual(initialState); }); - it('[NEGATIVE] should handle checkStudyDataFileUploadable NotFound correctly', async () => { - const store = createTestStore({ studyDataFileUpload: initialState }); - API.mock.provideEndpoints({ - getStudyDataFileInfo() { - return API.mock.failedResponse({ status: 404 }); - }, - }); - - const dispatchedStore = store.dispatch( - checkStudyDataFileUploadable({ studyId: 'id', file: mockFile }) as any - ); - const key = '_id_test.txt'; - return dispatchedStore.then(() => { - expect(store.getState().studyDataFileUpload[key].checkStatus).toEqual( - FileCheckStatus.AVAILABLE - ); - }); - }); - - it('should handle checkStudyDataFileUploadable DUPLICATED correctly', async () => { - const store = createTestStore({ studyDataFileUpload: initialState }); - API.mock.provideEndpoints({ - getStudyDataFileInfo() { - return API.mock.response('body' as any); - }, - }); - - const dispatchedStore = store.dispatch( - checkStudyDataFileUploadable({ studyId: 'id', file: mockFile }) as any - ); - const key = '_id_test.txt'; - return dispatchedStore.then(() => { - expect(store.getState().studyDataFileUpload[key].checkStatus).toEqual( - FileCheckStatus.DUPLICATED - ); - }); - }); - - it('[NEGATIVE] should handle checkStudyDataFileUploadable failed', async () => { - const store = createTestStore({ studyDataFileUpload: initialState }); - API.mock.provideEndpoints({ - getStudyDataFileInfo() { - throw new Error(); - }, - }); - - const dispatchedStore = store.dispatch( - checkStudyDataFileUploadable({ studyId: 'id', file: mockFile }) as any - ); - const key = '_id_test.txt'; - return dispatchedStore.then(() => { - expect(store.getState().studyDataFileUpload[key].checkStatus).toEqual( - FileCheckStatus.CHECK_FAILED - ); - }); - }); - - it('should handle uploadStudyDataFile correctly', () => { - const key = '_id_test.txt'; - const initialState = { - [key]: {}, - }; - - const store = createTestStore({ studyDataFileUpload: initialState as any }); - API.mock.provideEndpoints({ - getStudyDataFileUploadUrl() { - return API.mock.response({ headers: 'header', presignedUrl: 'abc' } as any); - }, - addStudyDataFileInfo() { - return API.mock.response({} as any); - }, - }); - - jest.spyOn(axios, 'put').mockResolvedValue({}); - const dispatchedStore = store.dispatch( - uploadStudyDataFile({ studyId: 'id', file: mockFile, overwrite: true }) as any - ); - return dispatchedStore.then(() => { - expect(store.getState().studyDataFileUpload[key].uploadStatus).toEqual( - FileUploadStatus.FINISHED - ); - }); - }); - - it('[NEGATIVE] should handle error uploadStudyDataFile', () => { - const key = '_id_test.txt'; - const initialState = { - [key]: {}, - }; - - const store = createTestStore({ studyDataFileUpload: initialState as any }); - API.mock.provideEndpoints({ - getStudyDataFileUploadUrl() { - return API.mock.failedResponse({ status: 500 }); - }, - addStudyDataFileInfo() { - return API.mock.response({} as any); - }, - }); - - jest.spyOn(axios, 'put').mockResolvedValue({}); - const dispatchedStore = store.dispatch( - uploadStudyDataFile({ studyId: 'id', file: mockFile, overwrite: true }) as any - ); - return dispatchedStore.then(() => { - expect(store.getState().studyDataFileUpload[key].uploadStatus).toEqual( - FileUploadStatus.FAILED - ); - }); - }); - it('should handle prepareUploadFiles correctly', () => { const store = createTestStore({ studyDataFileUpload: initialState }); const dispatchedStore = store.dispatch(prepareUploadFiles() as any); diff --git a/src/modules/study-data/file-upload/studyDataFileUpload.slice.ts b/src/modules/study-data/file-upload/studyDataFileUpload.slice.ts index 911790d..7023bda 100644 --- a/src/modules/study-data/file-upload/studyDataFileUpload.slice.ts +++ b/src/modules/study-data/file-upload/studyDataFileUpload.slice.ts @@ -2,11 +2,13 @@ import {createSelector, createSlice, PayloadAction} from '@reduxjs/toolkit'; import {AppThunk, RootState, useAppSelector} from 'src/modules/store'; import applyDefaultApiErrorHandlers from 'src/modules/api/applyDefaultApiErrorHandlers'; -import API from "src/modules/api"; +import API, { FileDataInfo } from "src/modules/api"; import axios, {HttpStatusCode} from "axios"; -import {FileCheckStatus, FileUploadStatus, StudyDataFileType,} from "src/modules/study-data/studyData.enum"; +import {FileCheckStatus, FileUploadStatus, StudyDataFileType, StudyDataType,} from "src/modules/study-data/studyData.enum"; import {showSnackbar} from "src/modules/snackbar/snackbar.slice"; import {FAILED_TO_UPLOAD_FILE} from "src/modules/study-data/StudyData.message"; +import { uploadObject } from 'src/modules/object-storage/utils'; +import StudyData from '../StudyData'; type UploadKey = string @@ -66,21 +68,21 @@ const studyDataFileUploadSlice = createSlice({ interface StudyDataFileUploadParams { studyId: string - subjectNumber?: string - sessionId?: string - taskId?: string + parentId: string + name: string + studyDataType: string file: File } const getUploadKey = ({ studyId, - subjectNumber, - sessionId, - taskId, + parentId, + name, + studyDataType, file }: StudyDataFileUploadParams) => { let uploadKey = "" - const ids = [studyId, subjectNumber, sessionId, taskId] + const ids = [studyId, parentId, name, studyDataType] for(let i = 0; i < ids.length; i += 1) { if(!ids[i]) break uploadKey = `${uploadKey}_${ids[i]}` @@ -90,13 +92,13 @@ const getUploadKey = ({ export const checkStudyDataFileUploadable = ({ studyId, - subjectNumber, - sessionId, - taskId, + parentId, + name, + studyDataType, file }: StudyDataFileUploadParams): AppThunk> => async (dispatch) => { - const ids = { studyId, subjectNumber, sessionId, taskId } + const ids = { studyId, parentId, name, studyDataType }; const key = getUploadKey({ ...ids, file }) let checkStatus = FileCheckStatus.UNSPECIFIED @@ -104,13 +106,15 @@ export const checkStudyDataFileUploadable = ({ dispatch(studyDataFileUploadSlice.actions.init({ key })) dispatch(studyDataFileUploadSlice.actions.checkBegin( { key })) - const { status, checkError } = await API.getStudyDataFileInfo({ ...ids, fileName: file.name }) + checkStatus = FileCheckStatus.AVAILABLE; - if(status === HttpStatusCode.NotFound) { - checkStatus = FileCheckStatus.AVAILABLE - } else { - checkStatus = FileCheckStatus.DUPLICATED - } + // const { status, checkError } = await API.getFileDownloadUrls({ studyId, filePaths: [file.name] }) + + // if(status === HttpStatusCode.NotFound) { + // checkStatus = FileCheckStatus.AVAILABLE + // } else { + // checkStatus = FileCheckStatus.DUPLICATED + // } } catch (e) { checkStatus = FileCheckStatus.CHECK_FAILED } finally { @@ -121,14 +125,14 @@ export const checkStudyDataFileUploadable = ({ // should be modified to new version export const uploadStudyDataFile = ({ studyId, - subjectNumber, - sessionId, - taskId, + parentId, + name, + studyDataType, file, overwrite }: StudyDataFileUploadParams & {overwrite: boolean}): AppThunk> => async (dispatch, getState) => { - const ids = { studyId, subjectNumber, sessionId, taskId } + const ids = { studyId, parentId, name, studyDataType }; const key = getUploadKey({ ...ids, file }) const state = getState().studyDataFileUpload[key] @@ -140,22 +144,24 @@ export const uploadStudyDataFile = ({ try { dispatch(studyDataFileUploadSlice.actions.uploadBegin({ key })) - const request = { ...ids, fileName: file.name, publicAccess: false } - const { data, checkError: getCheckError } = await API.getStudyDataFileUploadUrl({...request}) - getCheckError() - - await axios.put(data.presignedUrl.toString(), file, { - headers: data.headers, - onUploadProgress: (event) => { - dispatch(studyDataFileUploadSlice.actions.uploadProgress({ - key, - loaded: event.loaded, - total: event.total - })) - } - }) + await uploadObject({studyId, name: file.name, blob: file}); + + const fileInfo = { + fileType: StudyDataFileType.ATTACHMENT, + filePath: `${studyId}/${file.name}`, + fileSize: file.size, + filePreview: file.type, + createdAt: new Date().toISOString() + } as FileDataInfo + + const requestBody = { + parentId, + name: file.name, + type: StudyDataType.FILE, + fileInfo + } - const { checkError: addCheckError } = await API.addStudyDataFileInfo({...request, fileType: StudyDataFileType.ATTACHMENT}) + const { checkError: addCheckError } = await API.addStudyDataFileInfo(studyId, requestBody); addCheckError() uploadStatus = FileUploadStatus.FINISHED diff --git a/src/modules/study-data/viewer/StudyDataFile.tsx b/src/modules/study-data/viewer/StudyDataFile.tsx index 8b0283d..9bcfad8 100644 --- a/src/modules/study-data/viewer/StudyDataFile.tsx +++ b/src/modules/study-data/viewer/StudyDataFile.tsx @@ -23,6 +23,7 @@ import Button from "@mui/material/Button"; import {DownloadIconButton, StudyDataText} from "src/modules/study-data/StudyData.style"; import { executeDownload, + executeDownloadFiles, getFileDownloadUrl, getFileDownloadUrls, getZippedFileDownloadUrls @@ -391,9 +392,9 @@ const RenderDownload = ( event.stopPropagation() if(!studyId) return - const url = await dispatch(getFileDownloadUrl(studyId, row.path)) + const url = await dispatch(getFileDownloadUrl(studyId, row.name)) if(url) { - executeDownload(url) + executeDownloadFiles(url, row.name) } } @@ -809,7 +810,7 @@ const StudyDataFile = ({isLoadingParent, studyId}: StudyDataFileProps) => { ] const dialogProps = { - SlotProps: { backdrop: { sx: { backgroundColor: 'rgba(0, 0, 0, .2)' } }}, + slotProps: { backdrop: { sx: { backgroundColor: 'rgba(0, 0, 0, .2)' } }}, PaperProps: { style: { backgroundColor: 'rgba(255, 255, 255, .9)' }} }