From 47e96a4d94a3e28e30fb40d035f9978576bc4c5c Mon Sep 17 00:00:00 2001 From: Simon Detheridge Date: Sat, 14 Mar 2020 14:56:29 +0000 Subject: [PATCH] Add endpoint to delete a project Needs acceptance tests --- services/filestore/app.js | 5 +++++ services/filestore/app/js/FileController.js | 17 +++++++++++++++ services/filestore/app/js/FileHandler.js | 12 +++++++++++ services/filestore/app/js/KeyBuilder.js | 8 +++++++ .../test/unit/js/FileControllerTests.js | 19 +++++++++++++++++ .../test/unit/js/FileHandlerTests.js | 21 +++++++++++++++++++ 6 files changed, 82 insertions(+) diff --git a/services/filestore/app.js b/services/filestore/app.js index ea2c2ca1d8..2d5b27ee6b 100644 --- a/services/filestore/app.js +++ b/services/filestore/app.js @@ -61,6 +61,11 @@ app.delete( keyBuilder.userFileKeyMiddleware, fileController.deleteFile ) +app.delete( + '/project/:project_id', + keyBuilder.userProjectKeyMiddleware, + fileController.deleteProject +) app.head( '/template/:template_id/v/:version/:format', diff --git a/services/filestore/app/js/FileController.js b/services/filestore/app/js/FileController.js index 930434dc9d..9ddafb9f69 100644 --- a/services/filestore/app/js/FileController.js +++ b/services/filestore/app/js/FileController.js @@ -13,6 +13,7 @@ module.exports = { insertFile, copyFile, deleteFile, + deleteProject, directorySize } @@ -158,6 +159,22 @@ function deleteFile(req, res, next) { }) } +function deleteProject(req, res, next) { + metrics.inc('deleteProject') + const { project_id: projectId, bucket } = req + + req.requestLogger.setMessage('getting project size') + req.requestLogger.addFields({ projectId, bucket }) + + FileHandler.deleteProject(bucket, projectId, function(err) { + if (err) { + next(err) + } else { + res.sendStatus(204) + } + }) +} + function directorySize(req, res, next) { metrics.inc('projectSize') const { project_id: projectId, bucket } = req diff --git a/services/filestore/app/js/FileHandler.js b/services/filestore/app/js/FileHandler.js index 9b592df34e..40bdc95e34 100644 --- a/services/filestore/app/js/FileHandler.js +++ b/services/filestore/app/js/FileHandler.js @@ -10,6 +10,7 @@ const { ConversionError, WriteError } = require('./Errors') module.exports = { insertFile: callbackify(insertFile), deleteFile: callbackify(deleteFile), + deleteProject: callbackify(deleteProject), getFile: callbackify(getFile), getFileSize: callbackify(getFileSize), getDirectorySize: callbackify(getDirectorySize), @@ -17,6 +18,7 @@ module.exports = { getFile, insertFile, deleteFile, + deleteProject, getFileSize, getDirectorySize } @@ -48,6 +50,16 @@ async function deleteFile(bucket, key) { ]) } +async function deleteProject(bucket, key) { + if (!key.match(/^[0-9a-f]{24}\//i)) { + throw new WriteError({ + message: 'key does not match validation regex', + info: { bucket, key } + }) + } + await PersistorManager.promises.deleteDirectory(bucket, key) +} + async function getFile(bucket, key, opts) { opts = opts || {} if (!opts.format && !opts.style) { diff --git a/services/filestore/app/js/KeyBuilder.js b/services/filestore/app/js/KeyBuilder.js index 66cf563014..9968753349 100644 --- a/services/filestore/app/js/KeyBuilder.js +++ b/services/filestore/app/js/KeyBuilder.js @@ -4,6 +4,7 @@ module.exports = { getConvertedFolderKey, addCachingToKey, userFileKeyMiddleware, + userProjectKeyMiddleware, publicFileKeyMiddleware, publicProjectKeyMiddleware, bucketFileKeyMiddleware, @@ -37,6 +38,13 @@ function userFileKeyMiddleware(req, res, next) { next() } +function userProjectKeyMiddleware(req, res, next) { + const { project_id: projectId } = req.params + req.key = `${projectId}/` + req.bucket = settings.filestore.stores.user_files + next() +} + function publicFileKeyMiddleware(req, res, next) { if (settings.filestore.stores.public_files == null) { return res.status(501).send('public files not available') diff --git a/services/filestore/test/unit/js/FileControllerTests.js b/services/filestore/test/unit/js/FileControllerTests.js index 2d1411ea27..4a99a875a9 100644 --- a/services/filestore/test/unit/js/FileControllerTests.js +++ b/services/filestore/test/unit/js/FileControllerTests.js @@ -40,6 +40,7 @@ describe('FileController', function() { getFile: sinon.stub().yields(null, fileStream), getFileSize: sinon.stub().yields(null, fileSize), deleteFile: sinon.stub().yields(), + deleteProject: sinon.stub().yields(), insertFile: sinon.stub().yields(), getDirectorySize: sinon.stub().yields(null, fileSize) } @@ -67,6 +68,7 @@ describe('FileController', function() { req = { key: key, bucket: bucket, + project_id: projectId, query: {}, params: { project_id: projectId, @@ -257,6 +259,23 @@ describe('FileController', function() { }) }) + describe('delete project', function() { + it('should tell the file handler', function(done) { + res.sendStatus = code => { + code.should.equal(204) + expect(FileHandler.deleteProject).to.have.been.calledWith(bucket, projectId) + done() + } + FileController.deleteProject(req, res, next) + }) + + it('should send a 500 if there was an error', function() { + FileHandler.deleteProject.yields(error) + FileController.deleteProject(req, res, next) + expect(next).to.have.been.calledWith(error) + }) + }) + describe('directorySize', function() { it('should return total directory size bytes', function(done) { FileController.directorySize(req, { diff --git a/services/filestore/test/unit/js/FileHandlerTests.js b/services/filestore/test/unit/js/FileHandlerTests.js index 9692521531..acd3b8fc86 100644 --- a/services/filestore/test/unit/js/FileHandlerTests.js +++ b/services/filestore/test/unit/js/FileHandlerTests.js @@ -27,6 +27,7 @@ describe('FileHandler', function() { const bucket = 'my_bucket' const key = `${ObjectId()}/${ObjectId()}` const convertedFolderKey = `${ObjectId()}/${ObjectId()}` + const projectKey = `${ObjectId()}/` const sourceStream = 'sourceStream' const convertedKey = 'convertedKey' const readStream = { @@ -154,6 +155,26 @@ describe('FileHandler', function() { }) }) + describe('deleteProject', function() { + it('should tell the filestore manager to delete the folder', function(done) { + FileHandler.deleteProject(bucket, projectKey, err => { + expect(err).not.to.exist + expect(PersistorManager.promises.deleteDirectory).to.have.been.calledWith( + bucket, + projectKey + ) + done() + }) + }) + + it('should throw an error when the key is in the wrong format', function(done) { + FileHandler.deleteProject(bucket, 'wombat', err => { + expect(err).to.exist + done() + }) + }) + }) + describe('getFile', function() { it('should return the source stream no format or style are defined', function(done) { FileHandler.getFile(bucket, key, null, (err, stream) => {