Skip to content

Commit

Permalink
Merge pull request #100 from biomage-org/clone-experiment-to-user
Browse files Browse the repository at this point in the history
[BIOMAGE-2335] Use the programmatic interface to automate the data upload for workshops
  • Loading branch information
StefanBabukov authored Feb 9, 2023
2 parents 37f8aa7 + 0550483 commit 5ec06ef
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 4 deletions.
15 changes: 11 additions & 4 deletions src/api.v2/controllers/experimentController.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ const Experiment = require('../model/Experiment');
const UserAccess = require('../model/UserAccess');

const getLogger = require('../../utils/getLogger');
const { OK, NotFoundError } = require('../../utils/responses');
const { OK, NotFoundError, UnauthorizedError } = require('../../utils/responses');
const sqlClient = require('../../sql/sqlClient');

const getExperimentBackendStatus = require('../helpers/backendStatus/getExperimentBackendStatus');
const Sample = require('../model/Sample');
const invalidatePlotsForEvent = require('../../utils/plotConfigInvalidation/invalidatePlotsForEvent');
const events = require('../../utils/plotConfigInvalidation/events');
const getAdminSub = require('../../utils/getAdminSub');

const logger = getLogger('[ExperimentController] - ');

Expand Down Expand Up @@ -155,23 +156,29 @@ const cloneExperiment = async (req, res) => {
const { samplesOrder } = await new Experiment().findById(experimentId).first();
return samplesOrder;
};

const userId = req.user.sub;
const {
params: { experimentId: fromExperimentId },
body: {
samplesToCloneIds = await getAllSampleIds(fromExperimentId),
name = null,
toUserId = userId,
},
user: { sub: userId },
} = req;

const adminSub = await getAdminSub();

if (toUserId !== userId && userId !== adminSub) {
throw new UnauthorizedError(`User ${userId} cannot clone experiments for other users.`);
}

logger.log(`Creating experiment to clone ${fromExperimentId} to`);

let toExperimentId;

await sqlClient.get().transaction(async (trx) => {
toExperimentId = await new Experiment(trx).createCopy(fromExperimentId, name);
await new UserAccess(trx).createNewExperimentPermissions(userId, toExperimentId);
await new UserAccess(trx).createNewExperimentPermissions(toUserId, toExperimentId);
});

logger.log(`Cloning experiment samples from experiment ${fromExperimentId} into ${toExperimentId}`);
Expand Down
2 changes: 2 additions & 0 deletions src/specs/api.v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,8 @@ paths:
type: string
name:
type: string
toUserId:
type: string
additionalProperties: false
responses:
"200":
Expand Down
37 changes: 37 additions & 0 deletions tests/api.v2/controllers/experimentController.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@ jest.mock('../../../src/api.v2/helpers/pipeline/getPipelineStatus');
jest.mock('../../../src/api.v2/helpers/worker/getWorkerStatus');

jest.mock('../../../src/utils/plotConfigInvalidation/invalidatePlotsForEvent');
jest.mock('../../../src/utils/getAdminSub');

const getExperimentResponse = require('../mocks/data/getExperimentResponse.json');
const getAllExperimentsResponse = require('../mocks/data/getAllExperimentsResponse.json');

const experimentController = require('../../../src/api.v2/controllers/experimentController');
const { OK, NotFoundError } = require('../../../src/utils/responses');
const getAdminSub = require('../../../src/utils/__mocks__/getAdminSub');

const mockReqCreateExperiment = {
params: {
Expand Down Expand Up @@ -456,4 +458,39 @@ describe('experimentController', () => {

expect(mockRes.json).toHaveBeenCalledWith(toExperimentId);
});

it('Clone experiment for another user works', async () => {
const mockClonedExperimentName = 'cloned this experiment for you';
const toExperimentId = 'mockToExperimentId';

const mockReq = {
params: { experimentId: mockExperiment.id },
body: {
name: mockClonedExperimentName,
toUserId: 'mockUserId-asdasd-343-123sd',
},
user: { sub: await getAdminSub() },
};
const allSampleIds = ['mockSample1', 'mockSample2', 'mockSample3', 'mockSample4'];
const clonedSamplesIds = ['mockClonedSample1', 'mockClonedSample2', 'mockClonedSample3', 'mockClonedSample4'];

experimentInstance.createCopy.mockImplementation(() => Promise.resolve(toExperimentId));
experimentInstance.findById.mockReturnValue(
{ first: () => Promise.resolve({ samplesOrder: allSampleIds }) },
);
experimentInstance.updateById.mockImplementation(() => Promise.resolve());
sampleInstance.copyTo.mockImplementation(
() => Promise.resolve(clonedSamplesIds),
);

// this request should pass
await expect(experimentController.cloneExperiment(mockReq, mockRes))
.resolves;

// should fail if the request is not from the admin
mockReq.user.sub = 'not-admin-user';
await expect(experimentController.cloneExperiment(mockReq, mockRes))
. rejects
.toThrow(`User ${mockReq.user.sub} cannot clone experiments for other users.`);
});
});

0 comments on commit 5ec06ef

Please sign in to comment.