Skip to content

Commit

Permalink
Merge pull request #334 from hms-dbmi-cellenics/1835-samples-patch-ap…
Browse files Browse the repository at this point in the history
…i-v2

[BIOMAGE-1835] Implement samples PATCH for api v2
  • Loading branch information
cosa65 authored Apr 19, 2022
2 parents 80f0fac + 483ee59 commit 3f0090c
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/api.v2/controllers/experimentController.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ const createExperiment = async (req, res) => {
const patchExperiment = async (req, res) => {
const { params: { experimentId }, body } = req;

logger.log(`Updating experiment ${experimentId}`);
logger.log(`Patching experiment ${experimentId}`);

const snakeCasedKeysToPatch = _.mapKeys(body, (_value, key) => _.snakeCase(key));

await new Experiment().updateById(experimentId, snakeCasedKeysToPatch);

logger.log(`Finished updating experiment ${experimentId}`);
logger.log(`Finished patching experiment ${experimentId}`);

res.json(OK());
};
Expand Down
19 changes: 19 additions & 0 deletions src/api.v2/controllers/sampleController.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const _ = require('lodash');

const Sample = require('../model/Sample');
const Experiment = require('../model/Experiment');
const MetadataTrack = require('../model/MetadataTrack');
Expand Down Expand Up @@ -36,9 +38,25 @@ const createSample = async (req, res) => {
res.json(OK());
};

const patchSample = async (req, res) => {
const { params: { experimentId, sampleId }, body } = req;

logger.log(`Patching sample ${sampleId} in experiment ${experimentId}`);

const snakeCasedKeysToPatch = _.mapKeys(body, (_value, key) => _.snakeCase(key));

await new Sample().updateById(sampleId, snakeCasedKeysToPatch);

logger.log(`Finished patching sample ${sampleId} in experiment ${experimentId}`);

res.json(OK());
};

const deleteSample = async (req, res) => {
const { params: { experimentId, sampleId } } = req;

logger.log(`Deleting sample ${sampleId} from experiment ${experimentId}`);

await sqlClient.get().transaction(async (trx) => {
await new Sample(trx).destroy(sampleId);
await new Experiment(trx).deleteSample(experimentId, sampleId);
Expand All @@ -51,5 +69,6 @@ const deleteSample = async (req, res) => {

module.exports = {
createSample,
patchSample,
deleteSample,
};
5 changes: 5 additions & 0 deletions src/api.v2/routes/sample.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const {
createSample,
patchSample,
deleteSample,
} = require('../controllers/sampleController');

Expand All @@ -10,6 +11,10 @@ module.exports = {
expressAuthorizationMiddleware,
(req, res, next) => createSample(req, res).catch(next),
],
'sample#patchSample': [
expressAuthorizationMiddleware,
(req, res, next) => patchSample(req, res).catch(next),
],
'sample#deleteSample': [
expressAuthorizationMiddleware,
(req, res, next) => deleteSample(req, res).catch(next),
Expand Down
52 changes: 49 additions & 3 deletions src/specs/api.v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/ExperimentPatch'
$ref: '#/components/schemas/PatchExperiment'
responses:
'200':
description: Create experiment
Expand Down Expand Up @@ -283,6 +283,52 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/HTTPError'
patch:
summary: Patch sample
operationId: patchSample
x-eov-operation-id: sample#patchSample
x-eov-operation-handler: routes/sample
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
additionalProperties: false
responses:
'200':
description: Success
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPSuccess'
'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'
'404':
description: Not found error.
content:
application/json:
schema:
$ref: '#/components/schemas/HTTPError'

delete:
summary: Delete sample
operationId: deleteSample
Expand Down Expand Up @@ -377,8 +423,8 @@ components:
$ref: './models/experiment-bodies/CreateExperiment.v2.yaml'
ExperimentInfo:
$ref: './models/experiment-bodies/ExperimentInfo.v2.yaml'
ExperimentPatch:
$ref: './models/experiment-bodies/ExperimentPatch.v2.yaml'
PatchExperiment:
$ref: './models/experiment-bodies/PatchExperiment.v2.yaml'
GetAllExperiments:
$ref: './models/experiment-bodies/GetAllExperiments.v2.yaml'
CreateSample:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
title: Experiment Patch
title: Patch Experiment
description: The properties of an experiment that can be patched
type: object
properties:
Expand Down
18 changes: 18 additions & 0 deletions tests/api.v2/controllers/sampleController.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,22 @@ describe('sampleController', () => {

expect(mockRes.json).not.toHaveBeenCalled();
});

it('patchSample works correctly', async () => {
const mockSampleNewName = 'theNewName';
const mockReq = {
params: { experimentId: mockExperimentId, sampleId: mockSampleId },
body: { name: mockSampleNewName },
};

sampleInstance.updateById.mockImplementationOnce(() => Promise.resolve());

await sampleController.patchSample(mockReq, mockRes);

expect(sampleInstance.updateById).toHaveBeenCalledWith(
mockSampleId, { name: mockSampleNewName },
);

expect(mockRes.json).toHaveBeenCalledWith(OK());
});
});
46 changes: 41 additions & 5 deletions tests/api.v2/routes/sample.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const sampleController = require('../../../src/api.v2/controllers/sampleControll
jest.mock('../../../src/api.v2/controllers/sampleController', () => ({
createSample: jest.fn(),
deleteSample: jest.fn(),
patchSample: jest.fn(),
}));

jest.mock('../../../src/api.v2/middlewares/authMiddlewares');
Expand Down Expand Up @@ -89,13 +90,8 @@ describe('tests for experiment route', () => {
return Promise.resolve();
});

const invalidExperimentData = {
description: 'experimentDescription',
};

request(app)
.delete(`/v2/experiments/${experimentId}/samples/${sampleId}`)
.send(invalidExperimentData)
.expect(200)
.end((err) => {
if (err) {
Expand All @@ -106,4 +102,44 @@ describe('tests for experiment route', () => {
return done();
});
});

it('Patching a sample works', async (done) => {
sampleController.patchSample.mockImplementationOnce((req, res) => {
res.json(OK());
return Promise.resolve();
});

request(app)
.patch(`/v2/experiments/${experimentId}/samples/${sampleId}`)
.send({ name: 'newSampleName' })
.expect(200)
.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('Patching a sample fails if requestBody is invalid', async (done) => {
sampleController.patchSample.mockImplementationOnce((req, res) => {
res.json(OK());
return Promise.resolve();
});

request(app)
.patch(`/v2/experiments/${experimentId}/samples/${sampleId}`)
.send({ aName: 'newSampleName' })
.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();
});
});
});

0 comments on commit 3f0090c

Please sign in to comment.