-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #354 from hms-dbmi-cellenics/add-v2-cellsets-endpo…
…ints [BIOMAGE-1878] - Add V2 cell sets endpoint
- Loading branch information
Showing
23 changed files
with
895 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
const mockGetCellSets = jest.fn(); | ||
const mockPatchCellSets = jest.fn(); | ||
|
||
module.exports = { | ||
getCellSets: mockGetCellSets, | ||
patchCellSets: mockPatchCellSets, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
const getLogger = require('../../utils/getLogger'); | ||
|
||
const getS3Object = require('../helpers/s3/getObject'); | ||
const bucketNames = require('../helpers/s3/bucketNames'); | ||
const formatExperimentId = require('../../utils/v1Compatibility/formatExperimentId'); | ||
|
||
const { OK } = require('../../utils/responses'); | ||
|
||
const patchCellSetsObject = require('../helpers/s3/patchCellSetsObject'); | ||
|
||
const logger = getLogger('[CellSetsController] - '); | ||
|
||
const getCellSets = async (req, res) => { | ||
let { experimentId } = req.params; | ||
|
||
experimentId = experimentId.replace(/-/g, ''); | ||
|
||
logger.log(`Getting cell sets for experiment ${experimentId}`); | ||
|
||
const cellSets = await getS3Object({ | ||
Bucket: bucketNames.CELL_SETS, | ||
Key: formatExperimentId(experimentId), | ||
}); | ||
|
||
logger.log(`Finished getting cell sets for experiment ${experimentId}`); | ||
|
||
res.send(cellSets); | ||
}; | ||
|
||
const patchCellSets = async (req, res) => { | ||
const { experimentId } = req.params; | ||
const patch = req.body; | ||
|
||
logger.log(`Patching cell sets for ${experimentId}`); | ||
await patchCellSetsObject(formatExperimentId(experimentId), patch); | ||
|
||
logger.log(`Finished patching cell sets for experiment ${experimentId}`); | ||
|
||
res.json(OK()); | ||
}; | ||
|
||
module.exports = { | ||
getCellSets, | ||
patchCellSets, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
const getS3Client = require('./getS3Client'); | ||
const NotFoundError = require('../../../utils/responses/NotFoundError'); | ||
|
||
const getObject = async (params) => { | ||
if (!params.Bucket) throw new Error('Bucket is required'); | ||
if (!params.Key) throw new Error('Key is required'); | ||
|
||
const s3 = getS3Client(); | ||
|
||
try { | ||
const outputObject = await s3.getObject(params).promise(); | ||
const data = outputObject.Body.toString(); | ||
return data; | ||
} catch (e) { | ||
if (e.code === 'NoSuchKey') { | ||
throw new NotFoundError(`Couldn't find object with key: ${params.Key}`); | ||
} | ||
|
||
if (e.code === 'NoSuchBucket') { | ||
throw new NotFoundError(`Couldn't find bucket with key: ${params.Bucket}`); | ||
} | ||
|
||
throw e; | ||
} | ||
}; | ||
|
||
module.exports = getObject; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
const AWS = require('../../../utils/requireAWS'); | ||
const config = require('../../../config'); | ||
|
||
// Wanted to make this a wrapper class that extends S3, | ||
// but it's not advisable to do so: | ||
// https://github.com/aws/aws-sdk-js/issues/2006 | ||
const getS3Client = (options) => { | ||
const S3Config = { | ||
apiVersion: '2006-03-01', | ||
signatureVersion: 'v4', | ||
region: config.awsRegion, | ||
...options, | ||
}; | ||
|
||
return new AWS.S3(S3Config); | ||
}; | ||
|
||
module.exports = getS3Client; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
const jsonMerger = require('json-merger'); | ||
|
||
const bucketNames = require('./bucketNames'); | ||
const getObject = require('./getObject'); | ||
const putObject = require('./putObject'); | ||
|
||
const validateRequest = require('../../../utils/schema-validator'); | ||
|
||
const patchCellSetsObject = async (experimentId, patch) => { | ||
const currentCellSet = await getObject({ | ||
Bucket: bucketNames.CELL_SETS, | ||
Key: experimentId, | ||
}); | ||
|
||
const { cellSets: prePatchCellSets } = JSON.parse(currentCellSet); | ||
|
||
/** | ||
* The $remove operation will replace the element in the array with an | ||
* undefined value. We will therefore remove this from the array. | ||
* | ||
* We use the $remove operation in the worker to update cell clusters, | ||
* and we may end up using it in other places in the future. | ||
*/ | ||
const patchedCellSetslist = jsonMerger.mergeObjects( | ||
[prePatchCellSets, patch], | ||
); | ||
|
||
const patchedCellSets = { cellSets: patchedCellSetslist }; | ||
|
||
await validateRequest(patchedCellSets, 'cell-sets-bodies/CellSets.v2.yaml'); | ||
|
||
await putObject({ | ||
Bucket: bucketNames.CELL_SETS, | ||
Key: experimentId, | ||
Body: JSON.stringify(patchedCellSets), | ||
}); | ||
}; | ||
|
||
module.exports = patchCellSetsObject; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
const NotFoundError = require('../../../utils/responses/NotFoundError'); | ||
const getS3Client = require('./getS3Client'); | ||
|
||
const pubObject = async (params) => { | ||
if (!params.Bucket) throw new Error('Bucket is required'); | ||
if (!params.Key) throw new Error('Key is required'); | ||
if (!params.Body) throw new Error('Body is required'); | ||
|
||
const s3 = getS3Client(); | ||
|
||
try { | ||
await s3.putObject(params).promise(); | ||
} catch (e) { | ||
if (e.code === 'NoSuchBucket') { | ||
throw new NotFoundError(`Couldn't find bucket with key: ${params.Bucket}`); | ||
} | ||
|
||
throw e; | ||
} | ||
}; | ||
|
||
module.exports = pubObject; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
const { | ||
getCellSets, | ||
patchCellSets, | ||
} = require('../controllers/cellSetsController'); | ||
|
||
const { expressAuthorizationMiddleware } = require('../middlewares/authMiddlewares'); | ||
|
||
module.exports = { | ||
'cellSets#getCellSets': [ | ||
expressAuthorizationMiddleware, | ||
(req, res, next) => getCellSets(req, res).catch(next), | ||
], | ||
'cellSets#patchCellSets': [ | ||
expressAuthorizationMiddleware, | ||
(req, res, next) => patchCellSets(req, res).catch(next), | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
title: Cell Set | ||
description: An object representing a cell set (e.g. Cluster 1) | ||
type: object | ||
properties: | ||
key: | ||
type: string | ||
name: | ||
type: string | ||
rootNode: | ||
type: boolean | ||
cellIds: | ||
type: array | ||
items: | ||
type: integer | ||
required: | ||
- key | ||
- name | ||
- cellIds |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
title: Cell Set | ||
description: An object representing a cell set (e.g. Cluster 1) | ||
type: object | ||
properties: | ||
key: | ||
type: string | ||
name: | ||
type: string | ||
rootNode: | ||
type: boolean | ||
children: | ||
type: array | ||
items: | ||
$ref: ./CellSet.v2.yaml | ||
required: | ||
- key | ||
- name | ||
- children |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
title: Cell Sets object | ||
description: Schema for CellSets object | ||
type: object | ||
properties: | ||
cellSets: | ||
type: array | ||
items: | ||
$ref: ./CellSetClass.v2.yaml | ||
required: | ||
- cellSets |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// Existing experimentId in S3 (v1) are MD5 hashes not UUIDs. | ||
// They have the same number of alhpanum characters as UUIDs but no dashes | ||
// To maintain compatibility with v1, we remove the dashes from UUIDs. | ||
// This function should be removed once we have migrated to v2. | ||
// TODO: migrate existing cellsets to the dashes uuidv4 format so this function is not necessary | ||
|
||
const formatExperimentId = (experimentId) => experimentId.replace(/-/g, ''); | ||
|
||
module.exports = formatExperimentId; |
Oops, something went wrong.