diff --git a/src/api.v2/controllers/experimentController.js b/src/api.v2/controllers/experimentController.js index df68fb284..45faefc0f 100644 --- a/src/api.v2/controllers/experimentController.js +++ b/src/api.v2/controllers/experimentController.js @@ -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()); }; diff --git a/src/api.v2/controllers/sampleController.js b/src/api.v2/controllers/sampleController.js index 03521f2be..e2898a612 100644 --- a/src/api.v2/controllers/sampleController.js +++ b/src/api.v2/controllers/sampleController.js @@ -1,3 +1,5 @@ +const _ = require('lodash'); + const Sample = require('../model/Sample'); const Experiment = require('../model/Experiment'); const MetadataTrack = require('../model/MetadataTrack'); @@ -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); @@ -51,5 +69,6 @@ const deleteSample = async (req, res) => { module.exports = { createSample, + patchSample, deleteSample, }; diff --git a/src/api.v2/routes/sample.js b/src/api.v2/routes/sample.js index 9f09cba17..872b8bf6e 100644 --- a/src/api.v2/routes/sample.js +++ b/src/api.v2/routes/sample.js @@ -1,5 +1,6 @@ const { createSample, + patchSample, deleteSample, } = require('../controllers/sampleController'); @@ -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), diff --git a/src/specs/api.v2.yaml b/src/specs/api.v2.yaml index 39f2ba18f..911b89a36 100644 --- a/src/specs/api.v2.yaml +++ b/src/specs/api.v2.yaml @@ -168,7 +168,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ExperimentPatch' + $ref: '#/components/schemas/PatchExperiment' responses: '200': description: Create experiment @@ -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 @@ -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: diff --git a/src/specs/models/experiment-bodies/ExperimentPatch.v2.yaml b/src/specs/models/experiment-bodies/PatchExperiment.v2.yaml similarity index 89% rename from src/specs/models/experiment-bodies/ExperimentPatch.v2.yaml rename to src/specs/models/experiment-bodies/PatchExperiment.v2.yaml index 7ebbd8520..a99124eb1 100644 --- a/src/specs/models/experiment-bodies/ExperimentPatch.v2.yaml +++ b/src/specs/models/experiment-bodies/PatchExperiment.v2.yaml @@ -1,4 +1,4 @@ -title: Experiment Patch +title: Patch Experiment description: The properties of an experiment that can be patched type: object properties: diff --git a/tests/api.v2/controllers/sampleController.test.js b/tests/api.v2/controllers/sampleController.test.js index d8b9db81f..76782ff86 100644 --- a/tests/api.v2/controllers/sampleController.test.js +++ b/tests/api.v2/controllers/sampleController.test.js @@ -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()); + }); }); diff --git a/tests/api.v2/routes/sample.test.js b/tests/api.v2/routes/sample.test.js index 11c118f0a..46fc32691 100644 --- a/tests/api.v2/routes/sample.test.js +++ b/tests/api.v2/routes/sample.test.js @@ -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'); @@ -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) { @@ -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(); + }); + }); });