From 93fea83a2e5a7d83c64c4c2fef5c20bc3eebd1a3 Mon Sep 17 00:00:00 2001
From: Andrei Cioromila <andrei.cioromila@thinslices.com>
Date: Fri, 26 Oct 2018 10:01:20 +0300
Subject: [PATCH] feature(collections): Add custom delete route to clear all
 linked resources

---
 .../src/Collections.js                        |  6 ++
 .../src/routes/collections/delete.js          | 81 +++++++++++++++++++
 .../src/PackageManager.js                     | 21 +++++
 3 files changed, 108 insertions(+)
 create mode 100644 packages/component-manuscript-manager/src/routes/collections/delete.js

diff --git a/packages/component-manuscript-manager/src/Collections.js b/packages/component-manuscript-manager/src/Collections.js
index 8ce7c7977..178ca4dbc 100644
--- a/packages/component-manuscript-manager/src/Collections.js
+++ b/packages/component-manuscript-manager/src/Collections.js
@@ -36,6 +36,12 @@ const Collections = app => {
     authBearer,
     require(`${routePath}/get`)(app.locals.models),
   )
+
+  app.delete(
+    '/api/collections/:collectionId',
+    authBearer,
+    require(`${routePath}/delete`)(app.locals.models),
+  )
 }
 
 module.exports = Collections
diff --git a/packages/component-manuscript-manager/src/routes/collections/delete.js b/packages/component-manuscript-manager/src/routes/collections/delete.js
new file mode 100644
index 000000000..b2a85a1b0
--- /dev/null
+++ b/packages/component-manuscript-manager/src/routes/collections/delete.js
@@ -0,0 +1,81 @@
+const { get, remove, concat } = require('lodash')
+const config = require('config')
+
+const {
+  Team,
+  services,
+  authsome: authsomeHelper,
+} = require('pubsweet-component-helper-service')
+
+const {
+  deleteFilesS3,
+} = require('pubsweet-component-mts-package/src/PackageManager')
+
+const s3Config = get(config, 'pubsweet-component-aws-s3', {})
+
+module.exports = models => async (req, res) => {
+  const { collectionId } = req.params
+  let collection, fragment
+  try {
+    collection = await models.Collection.find(collectionId)
+
+    const fragmentId = collection.fragments[0]
+    if (!collection.fragments.includes(fragmentId))
+      return res.status(400).json({
+        error: `Collection and fragment do not match.`,
+      })
+
+    fragment = await models.Fragment.find(fragmentId)
+
+    const authsome = authsomeHelper.getAuthsome(models)
+
+    const canDelete = await authsome.can(req.user, 'DELETE', collection)
+    if (!canDelete)
+      return res.status(403).json({
+        error: 'Unauthorized.',
+      })
+
+    const teamHelper = new Team({
+      TeamModel: models.Team,
+      fragmentId,
+      collectionId,
+    })
+    const team = await teamHelper.getTeam({
+      role: 'author',
+      objectType: 'fragment',
+    })
+
+    await Promise.all(
+      team.members.map(async member => {
+        const user = await models.User.find(member)
+
+        remove(user.teams, teamId => teamId === team.id)
+
+        return user.save()
+      }),
+    )
+
+    await team.delete()
+
+    const fileKeys = concat(
+      fragment.files.manuscripts,
+      fragment.files.coverLetter,
+      fragment.files.supplementary,
+    ).map(file => `${fragment.id}/${file.id}`)
+
+    if (fileKeys.length !== 0) {
+      await deleteFilesS3({ fileKeys, s3Config })
+    }
+
+    await fragment.delete()
+
+    await collection.delete()
+
+    return res.status(200).json()
+  } catch (e) {
+    const notFoundError = await services.handleNotFoundError(e, 'Item')
+    return res.status(notFoundError.status).json({
+      error: notFoundError.message,
+    })
+  }
+}
diff --git a/packages/component-mts-package/src/PackageManager.js b/packages/component-mts-package/src/PackageManager.js
index b98dbf103..cb9b8ea4c 100644
--- a/packages/component-mts-package/src/PackageManager.js
+++ b/packages/component-mts-package/src/PackageManager.js
@@ -115,6 +115,26 @@ const uploadFiles = async ({ filename, s3Config, config }) => {
     .catch(fileError(filename))
 }
 
+const deleteFilesS3 = async ({ fileKeys, s3Config }) => {
+  AWS.config.update({
+    secretAccessKey: s3Config.secretAccessKey,
+    accessKeyId: s3Config.accessKeyId,
+    region: s3Config.region,
+  })
+  const s3 = new AWS.S3()
+
+  const params = {
+    Bucket: s3Config.bucket,
+    Delete: {
+      Objects: fileKeys.map(file => ({ Key: file })),
+    },
+  }
+
+  const deleteObjectsS3 = promisify(s3.deleteObjects.bind(s3))
+
+  return deleteObjectsS3(params)
+}
+
 const deleteFile = filename => {
   fs.access(filename, fs.constants.F_OK, err => {
     if (!err) {
@@ -138,4 +158,5 @@ const uploadFTP = ({ filename, config }) => {
 module.exports = {
   createFilesPackage,
   uploadFiles,
+  deleteFilesS3,
 }
-- 
GitLab