Skip to content

Commit

Permalink
Merge pull request #956 from biomage-org/65-restrict-upload-status-ch…
Browse files Browse the repository at this point in the history
…anges

Restrict upload status changes
  • Loading branch information
cosa65 authored Nov 30, 2023
2 parents d8560e4 + 67dab90 commit 085738c
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from 'components/data-management/SamplesTableCells';
import UploadStatus, { messageForStatus } from 'utils/upload/UploadStatus';
import { makeStore } from 'redux/store';
import mockAPI, { generateDefaultMockAPIResponses } from '__test__/test-utils/mockAPI';
import mockAPI, { generateDefaultMockAPIResponses, statusResponse } from '__test__/test-utils/mockAPI';
import { loadSamples, updateSampleFileUpload } from 'redux/actions/samples';

import fake from '__test__/test-utils/constants';
Expand All @@ -40,6 +40,7 @@ jest.mock('swr', () => () => ({

const experimentId = `${fake.EXPERIMENT_ID}-0`;
const sampleId = `${fake.SAMPLE_ID}-0`;
const sampleFileId = `${fake.SAMPLE_FILE_ID}`;

enableFetchMocks();

Expand All @@ -50,7 +51,10 @@ describe('UploadCell', () => {

beforeEach(async () => {
fetchMock.mockClear();
fetchMock.mockIf(/.*/, mockAPI(generateDefaultMockAPIResponses(experimentId)));
fetchMock.mockIf(/.*/, mockAPI({
[`experiments/${experimentId}/sampleFiles/${sampleFileId}$`]: () => statusResponse(200, 'OK'),
...generateDefaultMockAPIResponses(experimentId),
}));

storeState = makeStore();

Expand All @@ -73,7 +77,7 @@ describe('UploadCell', () => {

await storeState.dispatch(
updateSampleFileUpload(
experimentId, sampleId, 'features10x', UploadStatus.UPLOADING, percentUploaded,
experimentId, sampleId, sampleFileId, 'features10x', UploadStatus.UPLOADING, percentUploaded,
),
);

Expand Down
1 change: 1 addition & 0 deletions src/__test__/test-utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
EXPERIMENT_NAME: 'Test Experiment',
PROJECT_ID: 'test24b6-8600-test-mock-63822d2fmock',
SAMPLE_ID: 'test9188-d682-test-mock-cb6d644cmock',
SAMPLE_FILE_ID: 'test4321-1234-test-mock-sampfilemock',
API_ENDPOINT: /^http?:\/\/localhost:3000.*$/,
S3_ENDPOINT: 'http://mock.s3.amazonaws.com',
// pragma: allowlist secret
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ exports[`processUpload Should not upload files if there are errors beginning the
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/features10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploadError"}",
"headers": {
Expand All @@ -83,7 +83,7 @@ exports[`processUpload Should not upload files if there are errors beginning the
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/barcodes10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploadError"}",
"headers": {
Expand All @@ -93,7 +93,7 @@ exports[`processUpload Should not upload files if there are errors beginning the
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/matrix10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploadError"}",
"headers": {
Expand Down Expand Up @@ -193,7 +193,7 @@ exports[`processUpload Updates redux correctly when there are file upload errors
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/features10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploadError"}",
"headers": {
Expand All @@ -203,7 +203,7 @@ exports[`processUpload Updates redux correctly when there are file upload errors
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/barcodes10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploadError"}",
"headers": {
Expand All @@ -213,7 +213,7 @@ exports[`processUpload Updates redux correctly when there are file upload errors
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/matrix10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploadError"}",
"headers": {
Expand Down Expand Up @@ -363,7 +363,7 @@ exports[`processUpload Uploads and updates redux correctly when there are no err
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/features10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploaded"}",
"headers": {
Expand All @@ -373,7 +373,7 @@ exports[`processUpload Uploads and updates redux correctly when there are no err
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/barcodes10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploaded"}",
"headers": {
Expand All @@ -383,7 +383,7 @@ exports[`processUpload Uploads and updates redux correctly when there are no err
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/matrix10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploaded"}",
"headers": {
Expand Down Expand Up @@ -533,7 +533,7 @@ exports[`processUpload Uploads and updates redux correctly when there are no err
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/features10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploaded"}",
"headers": {
Expand All @@ -543,7 +543,7 @@ exports[`processUpload Uploads and updates redux correctly when there are no err
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/barcodes10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploaded"}",
"headers": {
Expand All @@ -553,7 +553,7 @@ exports[`processUpload Uploads and updates redux correctly when there are no err
},
],
[
"http://localhost:3000/v2/experiments/project-uuid/samples/mockSampleId/sampleFiles/matrix10x",
"http://localhost:3000/v2/experiments/project-uuid/sampleFiles/mockSampleFileId",
{
"body": "{"uploadStatus":"uploaded"}",
"headers": {
Expand Down
6 changes: 6 additions & 0 deletions src/__test__/utils/upload/process10XUpload.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,16 @@ const mockProcessUploadCalls = () => {
result = { status: 200, body: JSON.stringify({ WT13: sampleId }) };
}

// Create sample file
if (new RegExp(`/v2/experiments/${mockExperimentId}/samples/.*/sampleFiles/.*`).test(url)) {
result = { status: 200, body: JSON.stringify({}) };
}

// Update sample file status
if (new RegExp(`/v2/experiments/${mockExperimentId}/sampleFiles/.*`).test(url)) {
result = { status: 200, body: JSON.stringify({}) };
}

if (url.endsWith(`/v2/experiments/${mockExperimentId}/sampleFiles/${sampleFileId}/beginUpload`)) {
result = { status: 200, body: JSON.stringify(mockUploadUrlParams) };
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const AddMetadataButton = ({ samplesTableRef }) => {
}

try {
await prepareAndUploadFileToS3(file, uploadUrlParams, 'cellLevelMeta', onUpdateUploadStatus);
await prepareAndUploadFileToS3(file, uploadUrlParams, 'cellLevelMeta', new AbortController(), onUpdateUploadStatus);
} catch (e) {
pushNotificationMessage('error', 'Something went wrong while uploading your metadata file.');
console.log(e);
Expand Down
8 changes: 6 additions & 2 deletions src/redux/actions/samples/createSampleFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ const createSampleFile = (
) => async (dispatch) => {
const updatedAt = dayjs().toISOString();

const sampleFileId = uuidv4();

try {
const url = `/v2/experiments/${experimentId}/samples/${sampleId}/sampleFiles/${type}`;
const body = {
sampleFileId: uuidv4(),
sampleFileId,
size: fileForApiV1.size,
};

Expand Down Expand Up @@ -52,7 +54,9 @@ const createSampleFile = (

return body.sampleFileId;
} catch (e) {
dispatch(updateSampleFileUpload(experimentId, sampleId, type, UploadStatus.UPLOAD_ERROR));
dispatch(updateSampleFileUpload(
experimentId, sampleId, sampleFileId, type, UploadStatus.UPLOAD_ERROR,
));

throw e;
}
Expand Down
4 changes: 2 additions & 2 deletions src/redux/actions/samples/updateSampleFileUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import UploadStatus from 'utils/upload/UploadStatus';
import fileNameForApiV1 from 'utils/upload/fileNameForApiV1';

const updateSampleFileUpload = (
experimentId, sampleId, type, uploadStatus, uploadProgress,
experimentId, sampleId, sampleFileId, type, uploadStatus, uploadProgress,
) => async (dispatch) => {
const updatedAt = dayjs().toISOString();

// Don't send an api update whenever the progress bar is updated, only for uploadStatus changes
// TODO: move progress to not even be a part of redux, manage it in a different way
if (uploadStatus !== UploadStatus.UPLOADING) {
const url = `/v2/experiments/${experimentId}/samples/${sampleId}/sampleFiles/${type}`;
const url = `/v2/experiments/${experimentId}/sampleFiles/${sampleFileId}`;
const body = { uploadStatus };

try {
Expand Down
8 changes: 5 additions & 3 deletions src/utils/upload/processMultipartUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const FILE_CHUNK_SIZE = 10000000;
const MAX_RETRIES = 2;

const putPartInS3 = async (
blob, signedUrl, onUploadProgress, currentRetry = 0, abortController = null,
blob, signedUrl, onUploadProgress, abortController, currentRetry = 0,
) => {
try {
return await axios.request({
Expand All @@ -19,7 +19,9 @@ const putPartInS3 = async (
});
} catch (e) {
if (currentRetry < MAX_RETRIES) {
return await putPartInS3(blob, signedUrl, onUploadProgress, currentRetry + 1);
return await putPartInS3(
blob, signedUrl, onUploadProgress, abortController, currentRetry + 1,
);
}

throw e;
Expand All @@ -42,8 +44,8 @@ const processMultipartUpload = async (
blob,
signedUrl,
createOnUploadProgressForPart(index),
0,
abortController,
0,
);

promises.push(req);
Expand Down
14 changes: 8 additions & 6 deletions src/utils/upload/processUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import endUserMessages from 'utils/endUserMessages';
import pushNotificationMessage from 'utils/pushNotificationMessage';

const prepareAndUploadFileToS3 = async (
file, uploadUrlParams, type, onStatusUpdate = () => { }, abortController = null,
file, uploadUrlParams, type, abortController, onStatusUpdate = () => { },
) => {
let parts = null;
const { signedUrls, uploadId, fileId } = uploadUrlParams;
Expand Down Expand Up @@ -89,7 +89,7 @@ const createAndUploadSampleFile = async (file, experimentId, sampleId, dispatch,
try {
file.fileObject = await loadAndCompressIfNecessary(file, () => {
dispatch(updateSampleFileUpload(
experimentId, sampleId, fileType, UploadStatus.COMPRESSING,
experimentId, sampleId, sampleFileId, fileType, UploadStatus.COMPRESSING,
));
});

Expand All @@ -99,7 +99,9 @@ const createAndUploadSampleFile = async (file, experimentId, sampleId, dispatch,
? UploadStatus.FILE_READ_ABORTED
: UploadStatus.FILE_READ_ERROR;

dispatch(updateSampleFileUpload(experimentId, sampleId, fileType, fileErrorStatus));
dispatch(updateSampleFileUpload(
experimentId, sampleId, sampleFileId, fileType, fileErrorStatus,
));
return;
}
}
Expand All @@ -114,15 +116,15 @@ const createAndUploadSampleFile = async (file, experimentId, sampleId, dispatch,

const updateSampleFileUploadProgress = (status, percentProgress = 0) => dispatch(
updateSampleFileUpload(
experimentId, sampleId, fileType, status, percentProgress,
experimentId, sampleId, sampleFileId, fileType, status, percentProgress,
),
);

const uploadUrlParams = { signedUrls, uploadId, fileId: sampleFileId };
await prepareAndUploadFileToS3(file, uploadUrlParams, 'sample', updateSampleFileUploadProgress, abortController);
await prepareAndUploadFileToS3(file, uploadUrlParams, 'sample', abortController, updateSampleFileUploadProgress);
} catch (e) {
dispatch(updateSampleFileUpload(
experimentId, sampleId, fileType, UploadStatus.UPLOAD_ERROR,
experimentId, sampleId, sampleFileId, fileType, UploadStatus.UPLOAD_ERROR,
));
}
};
Expand Down

0 comments on commit 085738c

Please sign in to comment.