From d02de0d5d68802114e11f914711e9290e3fbfcef Mon Sep 17 00:00:00 2001 From: cosa65 Date: Mon, 19 Feb 2024 11:17:14 -0300 Subject: [PATCH 01/20] Add get url for single part of a multipart upload endpoint Signed-off-by: cosa65 --- src/api.v2/controllers/uploadController.js | 13 ++++++ src/api.v2/helpers/s3/signedUrl.js | 21 ++++++++++ src/api.v2/routes/upload.js | 9 +++++ src/specs/api.v2.yaml | 46 ++++++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 src/api.v2/controllers/uploadController.js create mode 100644 src/api.v2/routes/upload.js diff --git a/src/api.v2/controllers/uploadController.js b/src/api.v2/controllers/uploadController.js new file mode 100644 index 000000000..bc4bc7ae9 --- /dev/null +++ b/src/api.v2/controllers/uploadController.js @@ -0,0 +1,13 @@ +import { getPartUploadSignedUrl } from '../helpers/s3/signedUrl'; + +const getMultipartSignedUrl = async (req, res) => { + const { uploadId, partNumber } = req.params; + const { bucket, key } = req.query; + + const signedUrl = getPartUploadSignedUrl(key, bucket, uploadId, partNumber); + + res.json(signedUrl); +}; + +// eslint-disable-next-line import/prefer-default-export +export { getMultipartSignedUrl }; diff --git a/src/api.v2/helpers/s3/signedUrl.js b/src/api.v2/helpers/s3/signedUrl.js index 1bb0dee2c..69fd761d1 100644 --- a/src/api.v2/helpers/s3/signedUrl.js +++ b/src/api.v2/helpers/s3/signedUrl.js @@ -63,6 +63,26 @@ const createMultipartUpload = async (params, size) => { }; }; +const getPartUploadSignedUrl = async (key, bucketName, uploadId, partNumber) => { + const S3Config = { + apiVersion: '2006-03-01', + signatureVersion: 'v4', + region: config.awsRegion, + }; + + const s3 = new AWS.S3(S3Config); + + const params = { + Bucket: bucketName, + Key: key, + // 1 hour timeout of upload link + Expires: 3600, + UploadId: uploadId, + PartNumber: partNumber, + }; + + return await s3.getSignedUrlPromise('uploadPart', { params }); +}; const completeMultipartUpload = async (key, parts, uploadId, bucketName) => { const params = { @@ -137,4 +157,5 @@ module.exports = { getSignedUrl, createMultipartUpload, completeMultipartUpload, + getPartUploadSignedUrl, }; diff --git a/src/api.v2/routes/upload.js b/src/api.v2/routes/upload.js new file mode 100644 index 000000000..00e385b74 --- /dev/null +++ b/src/api.v2/routes/upload.js @@ -0,0 +1,9 @@ +const { getMultipartSignedUrl } = require('../controllers/uploadController'); +const { expressAuthorizationMiddleware } = require('../middlewares/authMiddlewares'); + +module.exports = { + 'upload#getMultipartSignedUrl': [ + expressAuthorizationMiddleware, + (req, res, next) => getMultipartSignedUrl(req, res).catch(next), + ], +}; diff --git a/src/specs/api.v2.yaml b/src/specs/api.v2.yaml index 85dcabc52..4f5aff68f 100644 --- a/src/specs/api.v2.yaml +++ b/src/specs/api.v2.yaml @@ -1636,6 +1636,52 @@ paths: schema: $ref: "#/components/schemas/HTTPError" + "/experiments/{experimentId}/upload/{uploadId}/part/{partNumber}/signedUrl": + get: + summary: Gets signed url to upload a part + description: Gets the upload url for uploading partNumber of an ongoing multipart upload + operationId: getMultipartSignedUrl + x-eov-operation-id: upload#getMultipartSignedUrl + x-eov-operation-handler: routes/upload + parameters: + - name: experimentId + required: true + in: path + description: ID of the experiment. + schema: + type: string + - name: uploadId + in: path + description: UploadId used to identify s3 multipart uploads. + required: true + schema: + type: string + - name: partNumber + in: path + description: PartNumber used to identify which part of the s3 multipart upload this is. + required: true + schema: + type: integer + - name: bucket + description: Bucket receiving the upload + schema: + type: string + in: query + required: true + - name: key + description: Key of the file being uploaded in the bucket + schema: + type: string + in: query + required: true + responses: + "200": + description: Success + content: + application/json: + schema: + type: string + "/experiments/{experimentId}/sampleFiles/{sampleFileId}/beginUpload": post: summary: Begins the multipart upload of a sample file From d1db729ee0dfae567d0a49ede66c950b4dd9d971 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Mon, 19 Feb 2024 15:56:36 -0300 Subject: [PATCH 02/20] Working version Signed-off-by: cosa65 --- .../controllers/sampleFileController.js | 2 - src/api.v2/controllers/uploadController.js | 10 +++-- src/api.v2/helpers/s3/signedUrl.js | 44 +++++++++++-------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/api.v2/controllers/sampleFileController.js b/src/api.v2/controllers/sampleFileController.js index bfea1b9dd..0a75bf278 100644 --- a/src/api.v2/controllers/sampleFileController.js +++ b/src/api.v2/controllers/sampleFileController.js @@ -25,8 +25,6 @@ const createFile = async (req, res) => { upload_status: 'uploading', }; - - await sqlClient.get().transaction(async (trx) => { await new SampleFile(trx).create(newSampleFile); await new Sample(trx).setNewFile(sampleId, sampleFileId, sampleFileType); diff --git a/src/api.v2/controllers/uploadController.js b/src/api.v2/controllers/uploadController.js index bc4bc7ae9..f3a52bb64 100644 --- a/src/api.v2/controllers/uploadController.js +++ b/src/api.v2/controllers/uploadController.js @@ -1,13 +1,15 @@ -import { getPartUploadSignedUrl } from '../helpers/s3/signedUrl'; +const { getPartUploadSignedUrl } = require('../helpers/s3/signedUrl'); const getMultipartSignedUrl = async (req, res) => { const { uploadId, partNumber } = req.params; const { bucket, key } = req.query; - const signedUrl = getPartUploadSignedUrl(key, bucket, uploadId, partNumber); + const signedUrl = await getPartUploadSignedUrl(key, bucket, uploadId, partNumber); + + console.log('signedUrlDebug'); + console.log(signedUrl); res.json(signedUrl); }; -// eslint-disable-next-line import/prefer-default-export -export { getMultipartSignedUrl }; +module.exports = { getMultipartSignedUrl }; diff --git a/src/api.v2/helpers/s3/signedUrl.js b/src/api.v2/helpers/s3/signedUrl.js index 69fd761d1..bbe2db0ad 100644 --- a/src/api.v2/helpers/s3/signedUrl.js +++ b/src/api.v2/helpers/s3/signedUrl.js @@ -36,30 +36,35 @@ const createMultipartUpload = async (params, size) => { const s3 = new AWS.S3(S3Config); + console.log('paramsDebug'); + console.log(params); + const { UploadId } = await s3.createMultipartUpload(params).promise(); - const baseParams = { - ...params, - UploadId, - }; + // const baseParams = { + // ...params, + // UploadId, + // }; - const promises = []; - const parts = Math.ceil(size / FILE_CHUNK_SIZE); + // const promises = []; + // const parts = Math.ceil(size / FILE_CHUNK_SIZE); - for (let i = 0; i < parts; i += 1) { - promises.push( - s3.getSignedUrlPromise('uploadPart', { - ...baseParams, - PartNumber: i + 1, - }), - ); - } + // for (let i = 0; i < parts; i += 1) { + // promises.push( + // s3.getSignedUrlPromise('uploadPart', { + // ...baseParams, + // PartNumber: i + 1, + // }), + // ); + // } - const signedUrls = await Promise.all(promises); + // const signedUrls = await Promise.all(promises); return { - signedUrls, + signedUrls: null, uploadId: UploadId, + bucket: params.Bucket, + key: params.Key, }; }; @@ -73,15 +78,18 @@ const getPartUploadSignedUrl = async (key, bucketName, uploadId, partNumber) => const s3 = new AWS.S3(S3Config); const params = { - Bucket: bucketName, Key: key, + Bucket: bucketName, // 1 hour timeout of upload link Expires: 3600, UploadId: uploadId, PartNumber: partNumber, }; - return await s3.getSignedUrlPromise('uploadPart', { params }); + // console.log('paramsDebug'); + // console.log(params); + + return await s3.getSignedUrlPromise('uploadPart', params); }; const completeMultipartUpload = async (key, parts, uploadId, bucketName) => { From a705f707e30641e2ed3b06bfb82c283e8a4fac19 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Tue, 20 Feb 2024 11:19:36 -0300 Subject: [PATCH 03/20] Remove size from createMultipartUpload, also from some endpoints that don't require it anymore as a result of the removal Signed-off-by: cosa65 --- .../controllers/cellLevelMetaController.js | 2 +- .../controllers/sampleFileController.js | 4 +-- src/api.v2/helpers/s3/signedUrl.js | 34 +++---------------- .../BeginSampleFileUpload.v2.yaml | 3 -- 4 files changed, 7 insertions(+), 36 deletions(-) diff --git a/src/api.v2/controllers/cellLevelMetaController.js b/src/api.v2/controllers/cellLevelMetaController.js index 2bd8cf734..4b19ac993 100644 --- a/src/api.v2/controllers/cellLevelMetaController.js +++ b/src/api.v2/controllers/cellLevelMetaController.js @@ -28,7 +28,7 @@ const upload = async (req, res) => { await new CellLevelMeta(trx).create(newCellLevelMetaFile); await new CellLevelMetaToExperiment(trx).setNewFile(experimentId, cellLevelMetaKey); - uploadUrlParams = await getFileUploadUrls(cellLevelMetaKey, {}, size, bucketName); + uploadUrlParams = await getFileUploadUrls(cellLevelMetaKey, {}, bucketName); uploadUrlParams = { ...uploadUrlParams, fileId: cellLevelMetaKey }; }); diff --git a/src/api.v2/controllers/sampleFileController.js b/src/api.v2/controllers/sampleFileController.js index 0a75bf278..750244757 100644 --- a/src/api.v2/controllers/sampleFileController.js +++ b/src/api.v2/controllers/sampleFileController.js @@ -39,7 +39,7 @@ const createFile = async (req, res) => { const beginUpload = async (req, res) => { const { params: { experimentId, sampleFileId }, - body: { metadata, size }, + body: { metadata }, } = req; const { uploadStatus } = await new SampleFile().findById(sampleFileId).first(); @@ -55,7 +55,7 @@ const beginUpload = async (req, res) => { logger.log(`Generating multipart upload urls for ${experimentId}, sample file ${sampleFileId}`); const uploadParams = await getFileUploadUrls( - sampleFileId, metadata, size, bucketNames.SAMPLE_FILES, + sampleFileId, metadata, bucketNames.SAMPLE_FILES, ); res.json(uploadParams); diff --git a/src/api.v2/helpers/s3/signedUrl.js b/src/api.v2/helpers/s3/signedUrl.js index bbe2db0ad..fcd1cd388 100644 --- a/src/api.v2/helpers/s3/signedUrl.js +++ b/src/api.v2/helpers/s3/signedUrl.js @@ -22,9 +22,9 @@ const getSignedUrl = async (operation, params) => { return s3.getSignedUrlPromise(operation, params); }; -const FILE_CHUNK_SIZE = 10000000; +// const FILE_CHUNK_SIZE = 10000000; -const createMultipartUpload = async (params, size) => { +const createMultipartUpload = async (params) => { if (!params.Bucket) throw new Error('Bucket is required'); if (!params.Key) throw new Error('Key is required'); @@ -36,32 +36,9 @@ const createMultipartUpload = async (params, size) => { const s3 = new AWS.S3(S3Config); - console.log('paramsDebug'); - console.log(params); - const { UploadId } = await s3.createMultipartUpload(params).promise(); - // const baseParams = { - // ...params, - // UploadId, - // }; - - // const promises = []; - // const parts = Math.ceil(size / FILE_CHUNK_SIZE); - - // for (let i = 0; i < parts; i += 1) { - // promises.push( - // s3.getSignedUrlPromise('uploadPart', { - // ...baseParams, - // PartNumber: i + 1, - // }), - // ); - // } - - // const signedUrls = await Promise.all(promises); - return { - signedUrls: null, uploadId: UploadId, bucket: params.Bucket, key: params.Key, @@ -86,9 +63,6 @@ const getPartUploadSignedUrl = async (key, bucketName, uploadId, partNumber) => PartNumber: partNumber, }; - // console.log('paramsDebug'); - // console.log(params); - return await s3.getSignedUrlPromise('uploadPart', params); }; @@ -110,7 +84,7 @@ const completeMultipartUpload = async (key, parts, uploadId, bucketName) => { await s3.completeMultipartUpload(params).promise(); }; -const getFileUploadUrls = async (key, metadata, size, bucketName) => { +const getFileUploadUrls = async (key, metadata, bucketName) => { const params = { Bucket: bucketName, Key: key, @@ -124,7 +98,7 @@ const getFileUploadUrls = async (key, metadata, size, bucketName) => { }; } - return await createMultipartUpload(params, size); + return await createMultipartUpload(params); }; const fileNameToReturn = { diff --git a/src/specs/models/samples-bodies/BeginSampleFileUpload.v2.yaml b/src/specs/models/samples-bodies/BeginSampleFileUpload.v2.yaml index 427212573..3442cc307 100644 --- a/src/specs/models/samples-bodies/BeginSampleFileUpload.v2.yaml +++ b/src/specs/models/samples-bodies/BeginSampleFileUpload.v2.yaml @@ -2,8 +2,6 @@ title: Begin sample file multipart upload description: 'Data to begin an s3 multipart upload' type: object properties: - size: - type: number metadata: description: Metadata to append to the s3 object on upload type: object @@ -15,7 +13,6 @@ properties: - pattern: v3 required: - - size - metadata additionalProperties: false \ No newline at end of file From 5c806693061d4926e870a4c474b66b1d390d0584 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Tue, 20 Feb 2024 13:09:41 -0300 Subject: [PATCH 04/20] Remove debug log Signed-off-by: cosa65 --- src/api.v2/controllers/uploadController.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/api.v2/controllers/uploadController.js b/src/api.v2/controllers/uploadController.js index f3a52bb64..e75df84ea 100644 --- a/src/api.v2/controllers/uploadController.js +++ b/src/api.v2/controllers/uploadController.js @@ -6,9 +6,6 @@ const getMultipartSignedUrl = async (req, res) => { const signedUrl = await getPartUploadSignedUrl(key, bucket, uploadId, partNumber); - console.log('signedUrlDebug'); - console.log(signedUrl); - res.json(signedUrl); }; From 122b6ca4f1eb25e7842d6bfa9b259e5194821dfe Mon Sep 17 00:00:00 2001 From: cosa65 Date: Tue, 20 Feb 2024 13:50:54 -0300 Subject: [PATCH 05/20] Cleanu p Signed-off-by: cosa65 --- .vscode/settings.json | 2 +- src/api.v2/helpers/s3/signedUrl.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 5fed8149c..7afb7dc9b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "[javascript]": { "editor.tabSize": 2, diff --git a/src/api.v2/helpers/s3/signedUrl.js b/src/api.v2/helpers/s3/signedUrl.js index fcd1cd388..80ef19c7c 100644 --- a/src/api.v2/helpers/s3/signedUrl.js +++ b/src/api.v2/helpers/s3/signedUrl.js @@ -22,8 +22,6 @@ const getSignedUrl = async (operation, params) => { return s3.getSignedUrlPromise(operation, params); }; -// const FILE_CHUNK_SIZE = 10000000; - const createMultipartUpload = async (params) => { if (!params.Bucket) throw new Error('Bucket is required'); if (!params.Key) throw new Error('Key is required'); From ba4c3cc2bbbf317f08414fbb4ca4f7d348e1b05a Mon Sep 17 00:00:00 2001 From: cosa65 Date: Tue, 20 Feb 2024 14:00:42 -0300 Subject: [PATCH 06/20] Merge getFileUploadUrls into createMultipartUpload since now it doesn't really do anything else Signed-off-by: cosa65 --- .../controllers/cellLevelMetaController.js | 4 +- .../controllers/sampleFileController.js | 4 +- src/api.v2/helpers/s3/signedUrl.js | 42 +++++++++---------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/src/api.v2/controllers/cellLevelMetaController.js b/src/api.v2/controllers/cellLevelMetaController.js index 4b19ac993..e0c210534 100644 --- a/src/api.v2/controllers/cellLevelMetaController.js +++ b/src/api.v2/controllers/cellLevelMetaController.js @@ -4,7 +4,7 @@ const bucketNames = require('../../config/bucketNames'); const sqlClient = require('../../sql/sqlClient'); const CellLevelMeta = require('../model/CellLevelMeta'); const CellLevelMetaToExperiment = require('../model/CellLevelMetaToExperiment'); -const { getFileUploadUrls } = require('../helpers/s3/signedUrl'); +const { createMultipartUpload } = require('../helpers/s3/signedUrl'); const getLogger = require('../../utils/getLogger'); const OK = require('../../utils/responses/OK'); @@ -28,7 +28,7 @@ const upload = async (req, res) => { await new CellLevelMeta(trx).create(newCellLevelMetaFile); await new CellLevelMetaToExperiment(trx).setNewFile(experimentId, cellLevelMetaKey); - uploadUrlParams = await getFileUploadUrls(cellLevelMetaKey, {}, bucketName); + uploadUrlParams = await createMultipartUpload(cellLevelMetaKey, {}, bucketName); uploadUrlParams = { ...uploadUrlParams, fileId: cellLevelMetaKey }; }); diff --git a/src/api.v2/controllers/sampleFileController.js b/src/api.v2/controllers/sampleFileController.js index 750244757..df9182710 100644 --- a/src/api.v2/controllers/sampleFileController.js +++ b/src/api.v2/controllers/sampleFileController.js @@ -4,7 +4,7 @@ const Sample = require('../model/Sample'); const SampleFile = require('../model/SampleFile'); const bucketNames = require('../../config/bucketNames'); -const { getFileUploadUrls, getSampleFileDownloadUrl } = require('../helpers/s3/signedUrl'); +const { getSampleFileDownloadUrl, createMultipartUpload } = require('../helpers/s3/signedUrl'); const { OK, MethodNotAllowedError } = require('../../utils/responses'); const getLogger = require('../../utils/getLogger'); @@ -54,7 +54,7 @@ const beginUpload = async (req, res) => { } logger.log(`Generating multipart upload urls for ${experimentId}, sample file ${sampleFileId}`); - const uploadParams = await getFileUploadUrls( + const uploadParams = await createMultipartUpload( sampleFileId, metadata, bucketNames.SAMPLE_FILES, ); diff --git a/src/api.v2/helpers/s3/signedUrl.js b/src/api.v2/helpers/s3/signedUrl.js index 80ef19c7c..300729658 100644 --- a/src/api.v2/helpers/s3/signedUrl.js +++ b/src/api.v2/helpers/s3/signedUrl.js @@ -22,9 +22,9 @@ const getSignedUrl = async (operation, params) => { return s3.getSignedUrlPromise(operation, params); }; -const createMultipartUpload = async (params) => { - if (!params.Bucket) throw new Error('Bucket is required'); - if (!params.Key) throw new Error('Key is required'); +const createMultipartUpload = async (key, metadata, bucket) => { + if (!key) throw new Error('key is required'); + if (!bucket) throw new Error('bucket is required'); const S3Config = { apiVersion: '2006-03-01', @@ -32,14 +32,28 @@ const createMultipartUpload = async (params) => { region: config.awsRegion, }; + const params = { + Bucket: bucket, + Key: key, + // 1 hour timeout of upload link + Expires: 3600, + }; + + if (metadata.cellrangerVersion) { + params.Metadata = { + cellranger_version: metadata.cellrangerVersion, + }; + } + const s3 = new AWS.S3(S3Config); + // @ts-ignore const { UploadId } = await s3.createMultipartUpload(params).promise(); return { uploadId: UploadId, - bucket: params.Bucket, - key: params.Key, + bucket, + key, }; }; @@ -82,23 +96,6 @@ const completeMultipartUpload = async (key, parts, uploadId, bucketName) => { await s3.completeMultipartUpload(params).promise(); }; -const getFileUploadUrls = async (key, metadata, bucketName) => { - const params = { - Bucket: bucketName, - Key: key, - // 1 hour timeout of upload link - Expires: 3600, - }; - - if (metadata.cellrangerVersion) { - params.Metadata = { - cellranger_version: metadata.cellrangerVersion, - }; - } - - return await createMultipartUpload(params); -}; - const fileNameToReturn = { matrix10x: 'matrix.mtx.gz', barcodes10x: 'barcodes.tsv.gz', @@ -132,7 +129,6 @@ const getSampleFileDownloadUrl = async (experimentId, sampleId, fileType) => { }; module.exports = { - getFileUploadUrls, getSampleFileDownloadUrl, getSignedUrl, createMultipartUpload, From 6f79111ad21c8c6762678686e31f005c0ad54af9 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Tue, 20 Feb 2024 14:10:53 -0300 Subject: [PATCH 07/20] Rename getMultipartSignedUrl to getUploadPartSignedUrl Signed-off-by: cosa65 --- src/api.v2/controllers/uploadController.js | 4 ++-- src/api.v2/routes/upload.js | 6 +++--- src/specs/api.v2.yaml | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api.v2/controllers/uploadController.js b/src/api.v2/controllers/uploadController.js index e75df84ea..44453e8e8 100644 --- a/src/api.v2/controllers/uploadController.js +++ b/src/api.v2/controllers/uploadController.js @@ -1,6 +1,6 @@ const { getPartUploadSignedUrl } = require('../helpers/s3/signedUrl'); -const getMultipartSignedUrl = async (req, res) => { +const getUploadPartSignedUrl = async (req, res) => { const { uploadId, partNumber } = req.params; const { bucket, key } = req.query; @@ -9,4 +9,4 @@ const getMultipartSignedUrl = async (req, res) => { res.json(signedUrl); }; -module.exports = { getMultipartSignedUrl }; +module.exports = { getUploadPartSignedUrl }; diff --git a/src/api.v2/routes/upload.js b/src/api.v2/routes/upload.js index 00e385b74..4b75ec088 100644 --- a/src/api.v2/routes/upload.js +++ b/src/api.v2/routes/upload.js @@ -1,9 +1,9 @@ -const { getMultipartSignedUrl } = require('../controllers/uploadController'); +const { getUploadPartSignedUrl } = require('../controllers/uploadController'); const { expressAuthorizationMiddleware } = require('../middlewares/authMiddlewares'); module.exports = { - 'upload#getMultipartSignedUrl': [ + 'upload#getUploadPartSignedUrl': [ expressAuthorizationMiddleware, - (req, res, next) => getMultipartSignedUrl(req, res).catch(next), + (req, res, next) => getUploadPartSignedUrl(req, res).catch(next), ], }; diff --git a/src/specs/api.v2.yaml b/src/specs/api.v2.yaml index 4f5aff68f..d228f4b8b 100644 --- a/src/specs/api.v2.yaml +++ b/src/specs/api.v2.yaml @@ -1640,8 +1640,8 @@ paths: get: summary: Gets signed url to upload a part description: Gets the upload url for uploading partNumber of an ongoing multipart upload - operationId: getMultipartSignedUrl - x-eov-operation-id: upload#getMultipartSignedUrl + operationId: getUploadPartSignedUrl + x-eov-operation-id: upload#getUploadPartSignedUrl x-eov-operation-handler: routes/upload parameters: - name: experimentId From 88827a13f412a993b08f420ae9d430630411e912 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 21 Feb 2024 15:00:51 -0300 Subject: [PATCH 08/20] Remove size from proeprties in openai Signed-off-by: cosa65 --- src/specs/api.v2.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/specs/api.v2.yaml b/src/specs/api.v2.yaml index d228f4b8b..171da9fe2 100644 --- a/src/specs/api.v2.yaml +++ b/src/specs/api.v2.yaml @@ -1114,8 +1114,8 @@ paths: properties: name: type: string - size: - type: integer + required: + - name responses: "200": description: Success From 7c52b692b04b6b5dae4deb128fffee0b63363665 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 21 Feb 2024 16:07:37 -0300 Subject: [PATCH 09/20] Update log Signed-off-by: cosa65 --- src/api.v2/controllers/sampleFileController.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api.v2/controllers/sampleFileController.js b/src/api.v2/controllers/sampleFileController.js index df9182710..6fae75064 100644 --- a/src/api.v2/controllers/sampleFileController.js +++ b/src/api.v2/controllers/sampleFileController.js @@ -53,7 +53,7 @@ const beginUpload = async (req, res) => { ); } - logger.log(`Generating multipart upload urls for ${experimentId}, sample file ${sampleFileId}`); + logger.log(`Creating multipart upload for ${experimentId}, sample file ${sampleFileId}`); const uploadParams = await createMultipartUpload( sampleFileId, metadata, bucketNames.SAMPLE_FILES, ); From 5a118b738d8cc137211038a993e3bdf81503e010 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 21 Feb 2024 16:08:12 -0300 Subject: [PATCH 10/20] Tests: remove checks on removed function signedUrl.getFileUploadUrls Signed-off-by: cosa65 --- tests/api.v2/controllers/sampleFileController.test.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/api.v2/controllers/sampleFileController.test.js b/tests/api.v2/controllers/sampleFileController.test.js index c31771491..327d9e0c1 100644 --- a/tests/api.v2/controllers/sampleFileController.test.js +++ b/tests/api.v2/controllers/sampleFileController.test.js @@ -29,8 +29,6 @@ describe('sampleFileController', () => { beforeEach(async () => { jest.clearAllMocks(); - - signedUrl.getFileUploadUrls.mockReturnValue(Promise.resolve(mockUploadParams)); }); it('createFile works correctly', async () => { @@ -104,10 +102,6 @@ describe('sampleFileController', () => { await sampleFileController.beginUpload(mockReq, mockRes); - expect(signedUrl.getFileUploadUrls).toHaveBeenCalledWith( - sampleFileId, metadata, size, bucketNames.SAMPLE_FILES, - ); - expect(mockRes.json).toHaveBeenCalledWith(mockUploadParams); expect(sampleFileInstance.findById.mock.calls).toMatchSnapshot(); @@ -128,10 +122,6 @@ describe('sampleFileController', () => { await sampleFileController.beginUpload(mockReq, mockRes); - expect(signedUrl.getFileUploadUrls).toHaveBeenCalledWith( - sampleFileId, metadata, size, bucketNames.SAMPLE_FILES, - ); - expect(mockRes.json).toHaveBeenCalledWith(mockUploadParams); expect(sampleFileInstance.findById.mock.calls).toMatchSnapshot(); @@ -157,7 +147,6 @@ describe('sampleFileController', () => { ), ); - expect(signedUrl.getFileUploadUrls).not.toHaveBeenCalled(); expect(mockRes.json).not.toHaveBeenCalled(); }); From 256060e489fd6202c2dd30ddbc6c3f44fe91639b Mon Sep 17 00:00:00 2001 From: cosa65 Date: Wed, 21 Feb 2024 16:11:35 -0300 Subject: [PATCH 11/20] Mock createMultipartUpload Signed-off-by: cosa65 --- tests/api.v2/controllers/sampleFileController.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/api.v2/controllers/sampleFileController.test.js b/tests/api.v2/controllers/sampleFileController.test.js index 327d9e0c1..c6c0140c8 100644 --- a/tests/api.v2/controllers/sampleFileController.test.js +++ b/tests/api.v2/controllers/sampleFileController.test.js @@ -25,10 +25,12 @@ const mockRes = { }; describe('sampleFileController', () => { - const mockUploadParams = { signedUrls: ['signedUrl1', 'signedUrl2'], fileId: 'mockfileId' }; + const mockUploadParams = { fileId: 'mockfileId', bucket: 'mockBucket', key: 'mockKey' }; beforeEach(async () => { jest.clearAllMocks(); + + signedUrl.createMultipartUpload.mockReturnValueOnce(Promise.resolve(mockUploadParams)); }); it('createFile works correctly', async () => { From 11075d94ccef5cf8e2754a9f93464529533bb6fc Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 09:29:31 -0300 Subject: [PATCH 12/20] Remove tests for removed functionality Signed-off-by: cosa65 --- .../s3/__snapshots__/signedUrl.test.js.snap | 159 ------------------ tests/api.v2/helpers/s3/signedUrl.test.js | 39 +---- 2 files changed, 1 insertion(+), 197 deletions(-) diff --git a/tests/api.v2/helpers/s3/__snapshots__/signedUrl.test.js.snap b/tests/api.v2/helpers/s3/__snapshots__/signedUrl.test.js.snap index 38032893d..f9ad13170 100644 --- a/tests/api.v2/helpers/s3/__snapshots__/signedUrl.test.js.snap +++ b/tests/api.v2/helpers/s3/__snapshots__/signedUrl.test.js.snap @@ -35,165 +35,6 @@ exports[`completeMultipartUpload works correctly 1`] = ` } `; -exports[`getFileUploadUrls works correctly with metadata cellrangerVersion 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - Object { - "Bucket": "biomage-originals-test-000000000000", - "Expires": 3600, - "Key": "mockSampleFileId", - }, - ], - Array [ - Object { - "Bucket": "biomage-originals-test-000000000000", - "Expires": 3600, - "Key": "mockSampleFileId", - "Metadata": Object { - "cellranger_version": "v2", - }, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "promise": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "UploadId": "uploadId", - }, - }, - ], - }, - }, - }, - Object { - "type": "return", - "value": Object { - "promise": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "UploadId": "uploadId", - }, - }, - ], - }, - }, - }, - ], -} -`; - -exports[`getFileUploadUrls works correctly with metadata cellrangerVersion 2`] = ` -[MockFunction] { - "calls": Array [ - Array [ - "uploadPart", - Object { - "Bucket": "biomage-originals-test-000000000000", - "Expires": 3600, - "Key": "mockSampleFileId", - "PartNumber": 1, - "UploadId": "uploadId", - }, - ], - Array [ - "uploadPart", - Object { - "Bucket": "biomage-originals-test-000000000000", - "Expires": 3600, - "Key": "mockSampleFileId", - "Metadata": Object { - "cellranger_version": "v2", - }, - "PartNumber": 1, - "UploadId": "uploadId", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": "signedUrl", - }, - Object { - "type": "return", - "value": "signedUrl", - }, - ], -} -`; - -exports[`getFileUploadUrls works correctly without metadata 1`] = ` -[MockFunction] { - "calls": Array [ - Array [ - Object { - "Bucket": "biomage-originals-test-000000000000", - "Expires": 3600, - "Key": "mockSampleFileId", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "promise": [MockFunction] { - "calls": Array [ - Array [], - ], - "results": Array [ - Object { - "type": "return", - "value": Object { - "UploadId": "uploadId", - }, - }, - ], - }, - }, - }, - ], -} -`; - -exports[`getFileUploadUrls works correctly without metadata 2`] = ` -[MockFunction] { - "calls": Array [ - Array [ - "uploadPart", - Object { - "Bucket": "biomage-originals-test-000000000000", - "Expires": 3600, - "Key": "mockSampleFileId", - "PartNumber": 1, - "UploadId": "uploadId", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": "signedUrl", - }, - ], -} -`; - exports[`getSampleFileDownloadUrl works correctly 1`] = ` [MockFunction] { "calls": Array [ diff --git a/tests/api.v2/helpers/s3/signedUrl.test.js b/tests/api.v2/helpers/s3/signedUrl.test.js index aef38f59c..e5fca5fdc 100644 --- a/tests/api.v2/helpers/s3/signedUrl.test.js +++ b/tests/api.v2/helpers/s3/signedUrl.test.js @@ -1,13 +1,12 @@ // @ts-nocheck const SampleFile = require('../../../../src/api.v2/model/SampleFile'); const signedUrl = require('../../../../src/api.v2/helpers/s3/signedUrl'); -const bucketNames = require('../../../../src/config/bucketNames'); const AWS = require('../../../../src/utils/requireAWS'); const { NotFoundError } = require('../../../../src/utils/responses'); const { - getSignedUrl, getSampleFileDownloadUrl, getFileUploadUrls, completeMultipartUpload, + getSignedUrl, getSampleFileDownloadUrl, completeMultipartUpload, } = signedUrl; const sampleFileInstance = new SampleFile(); @@ -68,42 +67,6 @@ describe('getSignedUrl', () => { }); }); -describe('getFileUploadUrls', () => { - const mockSampleFileId = 'mockSampleFileId'; - - const signedUrlResponse = { signedUrls: ['signedUrl'], uploadId: 'uploadId' }; - - const createMultipartUploadSpy = jest.fn(); - const getSignedUrlPromiseSpy = jest.fn(); - - beforeEach(() => { - createMultipartUploadSpy.mockReturnValue({ promise: jest.fn().mockReturnValue({ UploadId: 'uploadId' }) }); - getSignedUrlPromiseSpy.mockReturnValue('signedUrl'); - - AWS.S3.mockReset(); - AWS.S3.mockImplementation(() => ({ - createMultipartUpload: createMultipartUploadSpy, - getSignedUrlPromise: getSignedUrlPromiseSpy, - })); - }); - - it('works correctly without metadata', async () => { - const response = await getFileUploadUrls(mockSampleFileId, {}, 1, bucketNames.SAMPLE_FILES); - - expect(response).toEqual(signedUrlResponse); - expect(createMultipartUploadSpy).toMatchSnapshot(); - expect(getSignedUrlPromiseSpy).toMatchSnapshot(); - }); - - it('works correctly with metadata cellrangerVersion', async () => { - const response = await getFileUploadUrls(mockSampleFileId, { cellrangerVersion: 'v2' }, 1, bucketNames.SAMPLE_FILES); - - expect(response).toEqual(signedUrlResponse); - expect(createMultipartUploadSpy).toMatchSnapshot(); - expect(getSignedUrlPromiseSpy).toMatchSnapshot(); - }); -}); - describe('completeMultipartUpload', () => { const mockSampleFileId = 'mockSampleFileId'; const mockParts = []; From 52019a18731dfcd1d48d67e5efafa8f2e6cdcb3a Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 09:32:14 -0300 Subject: [PATCH 13/20] Update test Signed-off-by: cosa65 --- tests/api.v2/controllers/cellLevelMetaController.test.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/api.v2/controllers/cellLevelMetaController.test.js b/tests/api.v2/controllers/cellLevelMetaController.test.js index b408ee52f..ebb9bcf65 100644 --- a/tests/api.v2/controllers/cellLevelMetaController.test.js +++ b/tests/api.v2/controllers/cellLevelMetaController.test.js @@ -2,8 +2,9 @@ const cellLevelMetaController = require('../../../src/api.v2/controllers/cellLevelMetaController'); const CellLevelMeta = require('../../../src/api.v2/model/CellLevelMeta'); const CellLevelMetaToExperiment = require('../../../src/api.v2/model/CellLevelMetaToExperiment'); -const { getFileUploadUrls } = require('../../../src/api.v2/helpers/s3/signedUrl'); +const { createMultipartUpload } = require('../../../src/api.v2/helpers/s3/signedUrl'); const { OK } = require('../../../src/utils/responses'); +const bucketNames = require('../../../src/config/bucketNames'); const { mockSqlClient, mockTrx } = require('../mocks/getMockSqlClient')(); jest.mock('../../../src/api.v2/model/CellLevelMeta'); @@ -33,13 +34,13 @@ describe('CellLevelMetaController', () => { const mockUploadUrlParams = { url: 'signedUrl', fileId: 'fileId' }; - getFileUploadUrls.mockResolvedValue(mockUploadUrlParams); + createMultipartUpload.mockResolvedValue(mockUploadUrlParams); await cellLevelMetaController.upload(mockReq, mockRes); expect(CellLevelMeta).toHaveBeenCalledWith(mockTrx); expect(CellLevelMetaToExperiment).toHaveBeenCalledWith(mockTrx); - expect(getFileUploadUrls).toHaveBeenCalledWith( - expect.any(String), {}, mockReq.body.size, expect.any(String), + expect(createMultipartUpload).toHaveBeenCalledWith( + expect.any(String), {}, bucketNames.CELL_LEVEL_META, ); expect(mockRes.json).toHaveBeenCalledWith( From 905c701a713adc57d1e76107baaa9ff12fb1655a Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 09:49:14 -0300 Subject: [PATCH 14/20] Fix tests Signed-off-by: cosa65 --- tests/api.v2/routes/cellLevelMeta.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/api.v2/routes/cellLevelMeta.test.js b/tests/api.v2/routes/cellLevelMeta.test.js index 5a8c761c2..81accd59f 100644 --- a/tests/api.v2/routes/cellLevelMeta.test.js +++ b/tests/api.v2/routes/cellLevelMeta.test.js @@ -13,7 +13,7 @@ jest.mock('../../../src/api.v2/middlewares/authMiddlewares'); const experimentId = 'experiment-id'; const newCellLevelFileData = { - id: experimentId, + name: 'metaDataName', }; describe('cell level metadata route', () => { @@ -41,6 +41,7 @@ describe('cell level metadata route', () => { return done(); }); }); + it('downloading a new cell level meta file results in a successful response', async (done) => { cellLevelMetaController.download.mockImplementationOnce((req, res) => { res.json(OK()); From 0862b8a07ee1270fecc69c228c9e5dd4ff9f413d Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 10:05:53 -0300 Subject: [PATCH 15/20] Fix test Signed-off-by: cosa65 --- tests/api.v2/routes/sampleFile.test.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/api.v2/routes/sampleFile.test.js b/tests/api.v2/routes/sampleFile.test.js index 3ae2de73d..c701d3dd6 100644 --- a/tests/api.v2/routes/sampleFile.test.js +++ b/tests/api.v2/routes/sampleFile.test.js @@ -153,10 +153,9 @@ describe('tests for experiment route', () => { const experimentId = 'mockExperimentId'; const sampleFileId = 'mockSampleFileId'; - const size = 10; const metadata = {}; - const body = { metadata, size }; + const body = { metadata }; request(app) .post(`/v2/experiments/${experimentId}/sampleFiles/${sampleFileId}/beginUpload`) From 2c5648cefcbbaaaab1f65a4cfa1c10d644048c7c Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 16:33:22 -0300 Subject: [PATCH 16/20] Add getPartUploadSignedUrl Signed-off-by: cosa65 --- tests/api.v2/helpers/s3/signedUrl.test.js | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/api.v2/helpers/s3/signedUrl.test.js b/tests/api.v2/helpers/s3/signedUrl.test.js index e5fca5fdc..d627310da 100644 --- a/tests/api.v2/helpers/s3/signedUrl.test.js +++ b/tests/api.v2/helpers/s3/signedUrl.test.js @@ -91,6 +91,30 @@ describe('completeMultipartUpload', () => { }); }); +describe('getPartUploadSignedUrl', () => { + const signedUrlPromiseSpy = jest.fn(); + + beforeEach(() => { + AWS.S3.mockReset(); + AWS.S3.mockImplementation(() => ({ + getSignedUrlPromise: signedUrlPromiseSpy, + })); + }); + + it('works correctly', async () => { + const mockSignedUrl = 'mockSignedUrl'; + signedUrlPromiseSpy.mockResolvedValueOnce(mockSignedUrl); + + const key = 'mockKey'; + const bucketName = 'mockBucket'; + const uploadId = 'mockUploadId'; + const partNumber = 'mockPartNumber'; + + const response = await signedUrl.getPartUploadSignedUrl(key, bucketName, uploadId, partNumber); + + expect(response).toEqual(mockSignedUrl); + }); +}); describe('getSampleFileDownloadUrl', () => { const experimentId = 'mockExperimentId'; From 8d6860bd3554cb4eae32b0b3ae2e8e1831936c3c Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 17:10:39 -0300 Subject: [PATCH 17/20] Add test and logs to uploadCOntroller Signed-off-by: cosa65 --- src/api.v2/controllers/uploadController.js | 10 ++- .../controllers/uploadController.test.js | 74 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/api.v2/controllers/uploadController.test.js diff --git a/src/api.v2/controllers/uploadController.js b/src/api.v2/controllers/uploadController.js index 44453e8e8..16b611df6 100644 --- a/src/api.v2/controllers/uploadController.js +++ b/src/api.v2/controllers/uploadController.js @@ -1,11 +1,19 @@ const { getPartUploadSignedUrl } = require('../helpers/s3/signedUrl'); +const getLogger = require('../../utils/getLogger'); + +const logger = getLogger('[uploadController] - '); + const getUploadPartSignedUrl = async (req, res) => { - const { uploadId, partNumber } = req.params; + const { experimentId, uploadId, partNumber } = req.params; const { bucket, key } = req.query; + logger.log(`Experiment: ${experimentId}, getting part ${partNumber} for upload: ${uploadId}`); + const signedUrl = await getPartUploadSignedUrl(key, bucket, uploadId, partNumber); + logger.log(`Finished getting part ${partNumber} for experiment ${experimentId}, upload: ${uploadId}`); + res.json(signedUrl); }; diff --git a/tests/api.v2/controllers/uploadController.test.js b/tests/api.v2/controllers/uploadController.test.js new file mode 100644 index 000000000..ce558d1f0 --- /dev/null +++ b/tests/api.v2/controllers/uploadController.test.js @@ -0,0 +1,74 @@ +// @ts-nocheck + +const { getUploadPartSignedUrl } = require('../../../src/api.v2/controllers/uploadController'); +const signedUrl = require('../../../src/api.v2/helpers/s3/signedUrl'); + +jest.mock('../../../src/api.v2/helpers/s3/signedUrl'); + +const mockRes = { + json: jest.fn(), +}; + +describe('sampleFileController', () => { + const mockSignedUrl = 'mockSignedUrl'; + + const mockExperimentId = 'mockExperimentId'; + const uploadId = 'mockUploadId'; + const partNumber = 'mockPartNumber1'; + const bucket = 'mockBucket'; + const key = 'mockKey'; + + beforeEach(async () => { + jest.clearAllMocks(); + + signedUrl.getPartUploadSignedUrl.mockReturnValueOnce(Promise.resolve(mockSignedUrl)); + }); + + it('getPartUploadSignedUrl works correctly', async () => { + const mockReq = { + params: { mockExperimentId, uploadId, partNumber }, + query: { bucket, key }, + }; + + await getUploadPartSignedUrl(mockReq, mockRes); + + expect(signedUrl.getPartUploadSignedUrl).toHaveBeenCalledWith( + key, bucket, uploadId, partNumber, + ); + + expect(mockRes.json).toHaveBeenCalledWith(mockSignedUrl); + }); + + // it('createFile works correctly', async () => { + // const experimentId = 'experimentId'; + // const sampleId = 'sampleId'; + // const sampleFileType = 'features10x'; + + // const sampleFileId = 'sampleFileId'; + // const size = 'size'; + // const metadata = {}; + + // const mockReq = { + // params: { experimentId, sampleId, sampleFileType }, + // body: { sampleFileId, size, metadata }, + // }; + + // await sampleFileController.createFile(mockReq, mockRes); + + // // Used with transactions + // expect(Sample).toHaveBeenCalledWith(mockTrx); + // expect(SampleFile).toHaveBeenCalledWith(mockTrx); + + // // Not used without transactions + // expect(Sample).not.toHaveBeenCalledWith(mockSqlClient); + // expect(SampleFile).not.toHaveBeenCalledWith(mockSqlClient); + + // expect(sampleFileInstance.create).toHaveBeenCalledWith({ + // id: 'sampleFileId', s3_path: 'sampleFileId', sample_file_type: 'features10x', size: 'size', upload_status: 'uploading', + // }); + // expect(sampleInstance.setNewFile).toHaveBeenCalledWith('sampleId', 'sampleFileId', 'features10x'); + + // // Response is generated signed url + // expect(mockRes.json).toHaveBeenCalledWith(OK()); + // }); +}); From 73c863e5a60c0d1ce6fc853b020856fa6a04a6ea Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 17:11:00 -0300 Subject: [PATCH 18/20] Clean up Signed-off-by: cosa65 --- .../controllers/uploadController.test.js | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/tests/api.v2/controllers/uploadController.test.js b/tests/api.v2/controllers/uploadController.test.js index ce558d1f0..9e6cfa425 100644 --- a/tests/api.v2/controllers/uploadController.test.js +++ b/tests/api.v2/controllers/uploadController.test.js @@ -38,37 +38,4 @@ describe('sampleFileController', () => { expect(mockRes.json).toHaveBeenCalledWith(mockSignedUrl); }); - - // it('createFile works correctly', async () => { - // const experimentId = 'experimentId'; - // const sampleId = 'sampleId'; - // const sampleFileType = 'features10x'; - - // const sampleFileId = 'sampleFileId'; - // const size = 'size'; - // const metadata = {}; - - // const mockReq = { - // params: { experimentId, sampleId, sampleFileType }, - // body: { sampleFileId, size, metadata }, - // }; - - // await sampleFileController.createFile(mockReq, mockRes); - - // // Used with transactions - // expect(Sample).toHaveBeenCalledWith(mockTrx); - // expect(SampleFile).toHaveBeenCalledWith(mockTrx); - - // // Not used without transactions - // expect(Sample).not.toHaveBeenCalledWith(mockSqlClient); - // expect(SampleFile).not.toHaveBeenCalledWith(mockSqlClient); - - // expect(sampleFileInstance.create).toHaveBeenCalledWith({ - // id: 'sampleFileId', s3_path: 'sampleFileId', sample_file_type: 'features10x', size: 'size', upload_status: 'uploading', - // }); - // expect(sampleInstance.setNewFile).toHaveBeenCalledWith('sampleId', 'sampleFileId', 'features10x'); - - // // Response is generated signed url - // expect(mockRes.json).toHaveBeenCalledWith(OK()); - // }); }); From 1044ea36164954a982ae3245f90b0acc7ca9e4ef Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 17:11:26 -0300 Subject: [PATCH 19/20] Remove extra line Signed-off-by: cosa65 --- tests/api.v2/controllers/uploadController.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/api.v2/controllers/uploadController.test.js b/tests/api.v2/controllers/uploadController.test.js index 9e6cfa425..908984906 100644 --- a/tests/api.v2/controllers/uploadController.test.js +++ b/tests/api.v2/controllers/uploadController.test.js @@ -1,5 +1,4 @@ // @ts-nocheck - const { getUploadPartSignedUrl } = require('../../../src/api.v2/controllers/uploadController'); const signedUrl = require('../../../src/api.v2/helpers/s3/signedUrl'); From 4e307685232ab8b256b983485cc972c8f4fc8e18 Mon Sep 17 00:00:00 2001 From: cosa65 Date: Thu, 22 Feb 2024 17:23:06 -0300 Subject: [PATCH 20/20] Add route tsts Signed-off-by: cosa65 --- src/specs/api.v2.yaml | 18 +++++++++ tests/api.v2/routes/upload.test.js | 62 ++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 tests/api.v2/routes/upload.test.js diff --git a/src/specs/api.v2.yaml b/src/specs/api.v2.yaml index 171da9fe2..19f5e8757 100644 --- a/src/specs/api.v2.yaml +++ b/src/specs/api.v2.yaml @@ -1681,6 +1681,24 @@ paths: application/json: schema: type: string + "400": + description: Bad Request + content: + application/json: + schema: + $ref: "#/components/schemas/HTTPError" + "401": + description: The request lacks authentication credentials. + content: + application/json: + schema: + $ref: "#/components/schemas/HTTPError" + "403": + description: Forbidden request for this user. + content: + application/json: + schema: + $ref: "#/components/schemas/HTTPError" "/experiments/{experimentId}/sampleFiles/{sampleFileId}/beginUpload": post: diff --git a/tests/api.v2/routes/upload.test.js b/tests/api.v2/routes/upload.test.js new file mode 100644 index 000000000..36b1620ab --- /dev/null +++ b/tests/api.v2/routes/upload.test.js @@ -0,0 +1,62 @@ +// @ts-nocheck + +const express = require('express'); +const request = require('supertest'); +const expressLoader = require('../../../src/loaders/express'); + +const uploadController = require('../../../src/api.v2/controllers/uploadController'); + +jest.mock('../../../src/api.v2/controllers/uploadController', () => ({ + getUploadPartSignedUrl: jest.fn(), +})); + +jest.mock('../../../src/api.v2/middlewares/authMiddlewares'); + +describe('tests for upload routes', () => { + let app; + + beforeEach(async () => { + const mockApp = await expressLoader(express()); + app = mockApp.app; + }); + + it('getUploadPartSignedUrl results in a successful response', async (done) => { + uploadController.getUploadPartSignedUrl.mockImplementationOnce((req, res) => { + res.json('mockSignedUrl'); + return Promise.resolve(); + }); + + const queryParams = new URLSearchParams({ bucket: 'mockBucket', key: 'mockKey' }); + + request(app) + .get(`/v2/experiments/mockExperimentId/upload/mockUploadId/part/1/signedUrl?${queryParams}`) + .expect(JSON.stringify('mockSignedUrl')) + .end((err) => { + if (err) { + return done(err); + } + // there is no point testing for the values of the response body + // - if something is wrong, the schema validator will catch it + return done(); + }); + }); + + it('getUploadPartSignedUrl fails 400 if query params are missing', async (done) => { + uploadController.getUploadPartSignedUrl.mockImplementationOnce((req, res) => { + res.json('mockSignedUrl'); + return Promise.resolve(); + }); + + request(app) + .get('/v2/experiments/mockExperimentId/upload/mockUploadId/part/1/signedUrl') + .expect(400) + .end((err) => { + if (err) { + return done(err); + } + // there is no point testing for the values of the response body + // - if something is wrong, the schema validator will catch it + return done(); + }); + }); +});