From cb5fe82cd5ec08b5021fc45fb1e57ae15e13637e Mon Sep 17 00:00:00 2001
From: Bogdan Cochior <bogdan.cochior@thinslices.com>
Date: Fri, 16 Mar 2018 09:41:01 +0200
Subject: [PATCH] chore: update packages

---
 packages/component-aws-download/index.js      |   5 -
 packages/component-aws-download/package.json  |  18 --
 .../component-aws-download/src/FileBackend.js |  62 ------
 packages/component-local-aws/index.js         |   5 -
 packages/component-local-aws/package.json     |  18 --
 .../component-local-aws/src/FileBackend.js    |  45 -----
 .../src/FileBackend.test.js                   | 188 ------------------
 .../src/middeware/upload.js                   |  53 -----
 .../src/routeHandlers/deleteFile.js           |  22 --
 .../src/routeHandlers/getSignedUrl.js         |  24 ---
 .../src/routeHandlers/postFile.js             |  15 --
 .../src/routeHandlers/zipFiles.js             |  55 -----
 .../app/config/journal/submit-wizard.js       |   4 +-
 packages/xpub-faraday/config/components.json  |   4 +-
 packages/xpub-faraday/package.json            |   7 +-
 yarn.lock                                     |  74 +++++--
 16 files changed, 67 insertions(+), 532 deletions(-)
 delete mode 100644 packages/component-aws-download/index.js
 delete mode 100644 packages/component-aws-download/package.json
 delete mode 100644 packages/component-aws-download/src/FileBackend.js
 delete mode 100644 packages/component-local-aws/index.js
 delete mode 100644 packages/component-local-aws/package.json
 delete mode 100644 packages/component-local-aws/src/FileBackend.js
 delete mode 100644 packages/component-local-aws/src/FileBackend.test.js
 delete mode 100644 packages/component-local-aws/src/middeware/upload.js
 delete mode 100644 packages/component-local-aws/src/routeHandlers/deleteFile.js
 delete mode 100644 packages/component-local-aws/src/routeHandlers/getSignedUrl.js
 delete mode 100644 packages/component-local-aws/src/routeHandlers/postFile.js
 delete mode 100644 packages/component-local-aws/src/routeHandlers/zipFiles.js

diff --git a/packages/component-aws-download/index.js b/packages/component-aws-download/index.js
deleted file mode 100644
index 140b65ac0..000000000
--- a/packages/component-aws-download/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-require('dotenv').config()
-
-module.exports = {
-  backend: () => app => require('./src/FileBackend')(app),
-}
diff --git a/packages/component-aws-download/package.json b/packages/component-aws-download/package.json
deleted file mode 100644
index 4d54d2c1d..000000000
--- a/packages/component-aws-download/package.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "name": "component-aws-download",
-  "version": "0.0.1",
-  "main": "index.js",
-  "license": "MIT",
-  "dependencies": {
-    "archiver": "^2.1.1",
-    "aws-sdk": "^2.185.0",
-    "body-parser": "^1.17.2",
-    "multer": "^1.3.0",
-    "multer-s3": "^2.7.0",
-    "node-mocks-http": "^1.6.6"
-  },
-  "peerDependencies": {
-    "@pubsweet/logger": "^0.0.1",
-    "pubsweet-server": "^1.0.1"
-  }
-}
diff --git a/packages/component-aws-download/src/FileBackend.js b/packages/component-aws-download/src/FileBackend.js
deleted file mode 100644
index 74c4a5a76..000000000
--- a/packages/component-aws-download/src/FileBackend.js
+++ /dev/null
@@ -1,62 +0,0 @@
-const AWS = require('aws-sdk')
-const _ = require('lodash')
-const util = require('util')
-const config = require('config')
-const archiver = require('archiver')
-// const logger = require('@pubsweet/logger')
-
-const s3Config = _.get(config, 'pubsweet-component-aws-s3')
-
-const FileBackend = app => {
-  const authBearer = app.locals.passport.authenticate('bearer', {
-    session: false,
-  })
-  AWS.config.update({
-    secretAccessKey: s3Config.secretAccessKey,
-    accessKeyId: s3Config.accessKeyId,
-    region: s3Config.region,
-  })
-  const s3 = new AWS.S3()
-
-  app.get('/api/fileZip/:fragmentId', authBearer, async (req, res) => {
-    const archive = archiver('zip')
-    const { fragmentId } = req.params
-    const getObject = util.promisify(s3.getObject.bind(s3))
-    const listObjects = util.promisify(s3.listObjects.bind(s3))
-
-    try {
-      archive.pipe(res)
-      res.attachment(`${fragmentId}-archive.zip`)
-
-      const params = {
-        Bucket: s3Config.bucket,
-        Prefix: `${fragmentId}`,
-      }
-
-      const s3Items = await listObjects(params)
-      const s3Files = await Promise.all(
-        s3Items.Contents.map(content =>
-          getObject({
-            Bucket: s3Config.bucket,
-            Key: content.Key,
-          }),
-        ),
-      )
-
-      s3Files.forEach(f => {
-        archive.append(f.Body, {
-          name: `${_.get(f, 'Metadata.filetype') || 'supplementary'}/${_.get(
-            f,
-            'Metadata.filename',
-          ) || f.ETag}`,
-        })
-      })
-
-      archive.finalize()
-    } catch (err) {
-      res.status(err.statusCode).json({ error: err.message })
-    }
-  })
-}
-
-module.exports = FileBackend
diff --git a/packages/component-local-aws/index.js b/packages/component-local-aws/index.js
deleted file mode 100644
index 140b65ac0..000000000
--- a/packages/component-local-aws/index.js
+++ /dev/null
@@ -1,5 +0,0 @@
-require('dotenv').config()
-
-module.exports = {
-  backend: () => app => require('./src/FileBackend')(app),
-}
diff --git a/packages/component-local-aws/package.json b/packages/component-local-aws/package.json
deleted file mode 100644
index b38c67c0a..000000000
--- a/packages/component-local-aws/package.json
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "name": "component-local-aws",
-  "version": "1.0.0",
-  "main": "index.js",
-  "license": "MIT",
-  "dependencies": {
-    "aws-sdk": "^2.185.0",
-    "body-parser": "^1.17.2",
-    "multer": "^1.3.0",
-    "multer-s3": "^2.7.0",
-    "node-mocks-http": "^1.6.6",
-    "nodemailer": "^4.4.2"
-  },
-  "peerDependencies": {
-    "@pubsweet/logger": "^0.0.1",
-    "pubsweet-server": "^1.0.1"
-  }
-}
diff --git a/packages/component-local-aws/src/FileBackend.js b/packages/component-local-aws/src/FileBackend.js
deleted file mode 100644
index b7866aa58..000000000
--- a/packages/component-local-aws/src/FileBackend.js
+++ /dev/null
@@ -1,45 +0,0 @@
-const _ = require('lodash')
-const AWS = require('aws-sdk')
-const config = require('config')
-
-const s3Config = _.get(config, 'pubsweet-component-aws-s3')
-
-const FileBackend = app => {
-  const authBearer = app.locals.passport.authenticate('bearer', {
-    session: false,
-  })
-  AWS.config.update({
-    secretAccessKey: s3Config.secretAccessKey,
-    accessKeyId: s3Config.accessKeyId,
-    region: s3Config.region,
-  })
-  const s3 = new AWS.S3()
-  const upload = require('./middeware/upload').setupMulter(s3)
-
-  app.post(
-    '/api/files',
-    authBearer,
-    upload.single('file'),
-    require('./routeHandlers/postFile'),
-  )
-
-  app.get(
-    '/api/files/:fragmentId/:fileId',
-    authBearer,
-    require('./routeHandlers/getSignedUrl')(s3, s3Config),
-  )
-
-  app.get(
-    '/api/files/:fragmentId',
-    authBearer,
-    require('./routeHandlers/zipFiles')(s3, s3Config),
-  )
-
-  app.delete(
-    '/api/files/:fragmentId/:fileId',
-    authBearer,
-    require('./routeHandlers/deleteFile')(s3, s3Config),
-  )
-}
-
-module.exports = FileBackend
diff --git a/packages/component-local-aws/src/FileBackend.test.js b/packages/component-local-aws/src/FileBackend.test.js
deleted file mode 100644
index 95a026ff5..000000000
--- a/packages/component-local-aws/src/FileBackend.test.js
+++ /dev/null
@@ -1,188 +0,0 @@
-process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
-process.env.SUPPRESS_NO_CONFIG_WARNING = true
-
-const httpMocks = require('node-mocks-http')
-
-const zipHandler = require('./routeHandlers/zipFiles')
-
-describe('ValidateFile for multer fileFilter', () => {
-  it('should return TRUE when fileType is supplementary', () => {
-    const validateFile = require('./middeware/upload').validateFile(
-      ...buildValidateFileParams('supplementary', 'image/png'),
-    )
-    expect(validateFile).toBe(true)
-  })
-  it('should return TRUE when fileType is manuscripts or coverLetter and the file is either Word Doc or PDF', () => {
-    const randFileType = getRandValueFromArray(['manuscripts', 'coverLetter'])
-    const randMimeType = getRandValueFromArray([
-      'application/pdf',
-      'application/msword',
-      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
-    ])
-
-    const validateFile = require('./middeware/upload').validateFile(
-      ...buildValidateFileParams(randFileType, randMimeType),
-    )
-    expect(validateFile).toBe(true)
-  })
-  it('should return FALSE when fileType is manuscripts or coverLetter and the file is neither Word Doc or PDF', () => {
-    const randFileType = getRandValueFromArray(['manuscripts', 'coverLetter'])
-    const randMimeType = getRandValueFromArray([
-      'text/plain',
-      'text/html',
-      'image/jpeg',
-      'image/png',
-    ])
-
-    const validateFile = require('./middeware/upload').validateFile(
-      ...buildValidateFileParams(randFileType, randMimeType),
-    )
-    expect(validateFile).toBe(false)
-  })
-})
-
-describe('Upload file route handler', () => {
-  it('should return success when the file passed validation', async () => {
-    const file = {
-      key: '123abc',
-      originalname: 'file.txt',
-      size: 128,
-    }
-    const req = httpMocks.createRequest({
-      file,
-    })
-    const res = httpMocks.createResponse()
-    await require('./routeHandlers/postFile')(req, res)
-    expect(res.statusCode).toBe(200)
-    const data = JSON.parse(res._getData())
-    expect(data.id).toEqual(file.key)
-    expect(data.name).toEqual(file.originalname)
-    expect(data.size).toEqual(file.size)
-  })
-  it('should return an error when the file failed validation', async () => {
-    const req = httpMocks.createRequest({
-      fileValidationError: 'Only Word documents and PDFs are allowed',
-    })
-    const res = httpMocks.createResponse()
-    await require('./routeHandlers/postFile')(req, res)
-    expect(res.statusCode).toBe(400)
-    const data = JSON.parse(res._getData())
-    expect(data.error).toEqual(req.fileValidationError)
-  })
-})
-
-describe('Zip files endpoint', () => {
-  const testObject = {
-    Key: 'KeyToMyHeart',
-    Body: Buffer.alloc(10),
-    Metadata: {
-      filetype: 'manuscripts',
-      filename: 'test.txt',
-    },
-  }
-  const mockBucket = {
-    RandomKey: {
-      Contents: [testObject, testObject],
-    },
-  }
-
-  const s3 = {}
-  const mocks = {}
-  let mockArchiver = null
-  const s3Config = {
-    bucket: 'TestBucket',
-  }
-  beforeAll(() => {
-    s3.getObject = jest.fn((params, cb) => {
-      cb(null, testObject)
-    })
-
-    s3.listObjects = jest.fn((params, cb) => {
-      cb(null, mockBucket[params.Prefix])
-    })
-    mocks.pipe = jest.fn()
-    mocks.append = jest.fn()
-    mocks.finalize = jest.fn()
-    mocks.attachment = jest.fn()
-
-    mockArchiver = jest.fn(p => ({
-      pipe: mocks.pipe,
-      append: mocks.append,
-      finalize: mocks.finalize,
-    }))
-  })
-  afterEach(() => {
-    s3.getObject.mockClear()
-    s3.listObjects.mockClear()
-    mocks.pipe.mockClear()
-    mocks.append.mockClear()
-    mocks.attachment.mockClear()
-  })
-  it(`no zipping if no files found`, async () => {
-    const zipFiles = zipHandler(s3, s3Config, mockArchiver)
-    const request = httpMocks.createRequest({
-      method: 'GET',
-      params: {
-        fragmentId: 'NotFoundKey',
-      },
-    })
-    const response = httpMocks.createResponse()
-    response.attachment = mocks.attachment
-
-    await zipFiles(request, response)
-
-    expect(s3.listObjects.mock.calls).toHaveLength(1)
-    expect(s3.getObject.mock.calls).toHaveLength(0)
-    expect(mocks.attachment.mock.calls).toHaveLength(0)
-    expect(mocks.append.mock.calls).toHaveLength(0)
-    expect(mocks.pipe.mock.calls).toHaveLength(0)
-
-    const responseData = JSON.parse(response._getData())
-
-    expect(responseData.message).toEqual(
-      `No files found for the requested manuscript.`,
-    )
-    expect(response._getStatusCode()).toEqual(204)
-  })
-  it('zips all the files', async () => {
-    const zipFiles = zipHandler(s3, s3Config, mockArchiver)
-
-    const request = httpMocks.createRequest({
-      method: 'GET',
-      params: {
-        fragmentId: 'RandomKey',
-      },
-    })
-    const response = httpMocks.createResponse()
-    response.attachment = mocks.attachment
-
-    await zipFiles(request, response)
-
-    expect(s3.listObjects.mock.calls).toHaveLength(1)
-    expect(s3.getObject.mock.calls).toHaveLength(2)
-    expect(mocks.attachment.mock.calls).toHaveLength(1)
-    expect(mocks.attachment.mock.calls[0][0]).toEqual('RandomKey-archive.zip')
-    expect(mocks.append.mock.calls).toHaveLength(2)
-    expect(mocks.pipe.mock.calls).toHaveLength(1)
-    expect(response._getStatusCode()).toEqual(200)
-  })
-})
-
-const getRandValueFromArray = arr => arr[Math.floor(Math.random() * arr.length)]
-
-const buildValidateFileParams = (fileType, mimetype) => {
-  const req = {
-    body: {
-      fileType,
-    },
-  }
-  const file = {
-    mimetype,
-  }
-  const cb = (p1, p2) => {
-    if (p2 === true) return true
-    return false
-  }
-
-  return [req, file, cb]
-}
diff --git a/packages/component-local-aws/src/middeware/upload.js b/packages/component-local-aws/src/middeware/upload.js
deleted file mode 100644
index f46f41766..000000000
--- a/packages/component-local-aws/src/middeware/upload.js
+++ /dev/null
@@ -1,53 +0,0 @@
-const Joi = require('joi')
-const uuid = require('uuid')
-const config = require('config')
-const multer = require('multer')
-const { get } = require('lodash')
-const multerS3 = require('multer-s3')
-
-const s3Config = get(config, 'pubsweet-component-aws-s3')
-const uploadValidations = require(s3Config.validations)
-
-const setupMulter = s3 => {
-  const upload = multer({
-    storage: multerS3({
-      s3,
-      bucket: s3Config.bucket,
-      contentType: (req, file, cb) => {
-        cb(null, file.mimetype)
-      },
-      metadata: (req, file, cb) => {
-        cb(null, {
-          FileType: get(req, 'body.fileType'),
-          FileName: get(file, 'originalname'),
-        })
-      },
-      key: (req, file, cb) => {
-        const fileKey = `${req.body.fragmentId}/${uuid.v4()}`
-        cb(null, fileKey)
-      },
-    }),
-    fileFilter: (req, file, cb) => validateFile(req, file, cb),
-  })
-
-  return upload
-}
-
-const validateFile = (req, file, cb) => {
-  const { fileType } = req.body
-  const { mimetype } = file
-
-  const valid = Joi.validate({ [fileType]: mimetype }, uploadValidations)
-
-  if (valid.error) {
-    req.fileValidationError = valid.error.message
-    return cb(null, false)
-  }
-
-  return cb(null, true)
-}
-
-module.exports = {
-  setupMulter,
-  validateFile,
-}
diff --git a/packages/component-local-aws/src/routeHandlers/deleteFile.js b/packages/component-local-aws/src/routeHandlers/deleteFile.js
deleted file mode 100644
index 9aa8974b7..000000000
--- a/packages/component-local-aws/src/routeHandlers/deleteFile.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const { promisify } = require('util')
-const logger = require('@pubsweet/logger')
-
-const deleteFile = (s3, s3Config) => {
-  const asyncDeleteObject = promisify(s3.deleteObject.bind(s3))
-  return async (req, res) => {
-    const params = {
-      Bucket: s3Config.bucket,
-      Key: `${req.params.fragmentId}/${req.params.fileId}`,
-    }
-
-    try {
-      await asyncDeleteObject(params)
-      res.status(204)
-    } catch (err) {
-      logger.error(err.message)
-      res.status(err.statusCode).json({ error: err.message })
-    }
-  }
-}
-
-module.exports = deleteFile
diff --git a/packages/component-local-aws/src/routeHandlers/getSignedUrl.js b/packages/component-local-aws/src/routeHandlers/getSignedUrl.js
deleted file mode 100644
index edbad7102..000000000
--- a/packages/component-local-aws/src/routeHandlers/getSignedUrl.js
+++ /dev/null
@@ -1,24 +0,0 @@
-const { promisify } = require('util')
-const logger = require('@pubsweet/logger')
-
-const getSignedUrl = (s3, s3Config) => {
-  const asyncGetSignedUrl = promisify(s3.getSignedUrl.bind(s3))
-  return async (req, res) => {
-    const params = {
-      Bucket: s3Config.bucket,
-      Key: `${req.params.fragmentId}/${req.params.fileId}`,
-    }
-
-    try {
-      const signedUrl = await asyncGetSignedUrl(params)
-      res.status(200).json({
-        signedUrl,
-      })
-    } catch (err) {
-      logger.error(err.message)
-      res.status(err.statusCode).json({ error: err.message })
-    }
-  }
-}
-
-module.exports = getSignedUrl
diff --git a/packages/component-local-aws/src/routeHandlers/postFile.js b/packages/component-local-aws/src/routeHandlers/postFile.js
deleted file mode 100644
index 228c78fed..000000000
--- a/packages/component-local-aws/src/routeHandlers/postFile.js
+++ /dev/null
@@ -1,15 +0,0 @@
-const logger = require('@pubsweet/logger')
-
-module.exports = async (req, res) => {
-  if (req.fileValidationError !== undefined) {
-    logger.error(req.fileValidationError)
-    return res.status(400).json({ error: req.fileValidationError })
-  }
-  logger.debug(`${req.file.originalname} has been uploaded`)
-
-  return res.status(200).json({
-    id: req.file.key,
-    name: req.file.originalname,
-    size: req.file.size,
-  })
-}
diff --git a/packages/component-local-aws/src/routeHandlers/zipFiles.js b/packages/component-local-aws/src/routeHandlers/zipFiles.js
deleted file mode 100644
index 272eccbfb..000000000
--- a/packages/component-local-aws/src/routeHandlers/zipFiles.js
+++ /dev/null
@@ -1,55 +0,0 @@
-const { get } = require('lodash')
-const { promisify } = require('util')
-const nodeArchiver = require('archiver')
-const logger = require('@pubsweet/logger')
-
-const zipFiles = (s3, s3Config, archiver = nodeArchiver) => {
-  const asyncGetObject = promisify(s3.getObject.bind(s3))
-  const asyncListObjects = promisify(s3.listObjects.bind(s3))
-  return async (req, res) => {
-    const { fragmentId } = req.params
-    try {
-      const params = {
-        Bucket: s3Config.bucket,
-        Prefix: `${fragmentId}`,
-      }
-      const s3Items = await asyncListObjects(params)
-
-      if (s3Items) {
-        const s3Files = await Promise.all(
-          s3Items.Contents.map(content =>
-            asyncGetObject({
-              Bucket: s3Config.bucket,
-              Key: content.Key,
-            }),
-          ),
-        )
-
-        if (s3Files) {
-          const archive = archiver('zip')
-          archive.pipe(res)
-          res.attachment(`${fragmentId}-archive.zip`)
-
-          s3Files.forEach(f => {
-            archive.append(f.Body, {
-              name: `${get(f, 'Metadata.filetype') || 'supplementary'}/${get(
-                f,
-                'Metadata.filename',
-              ) || f.ETag}`,
-            })
-          })
-          archive.finalize()
-        }
-      } else {
-        res.status(204).json({
-          message: `No files found for the requested manuscript.`,
-        })
-      }
-    } catch (err) {
-      logger.error(err.message)
-      res.status(err.statusCode).json({ error: err.message })
-    }
-  }
-}
-
-module.exports = zipFiles
diff --git a/packages/xpub-faraday/app/config/journal/submit-wizard.js b/packages/xpub-faraday/app/config/journal/submit-wizard.js
index 5f3ff6b74..b4b8cc37a 100644
--- a/packages/xpub-faraday/app/config/journal/submit-wizard.js
+++ b/packages/xpub-faraday/app/config/journal/submit-wizard.js
@@ -1,9 +1,7 @@
 import React from 'react'
 import styled from 'styled-components'
 import uploadFileFn from 'xpub-upload'
-// TODO: Add back abstract when xpub-edit is published
-// import { AbstractEditor, TitleEditor } from 'xpub-edit'
-import { TitleEditor } from 'xpub-edit'
+import { AbstractEditor, TitleEditor } from 'xpub-edit'
 import { Menu, YesOrNo, TextField, CheckboxGroup } from '@pubsweet/ui'
 import { required, minChars, minSize } from 'xpub-validators'
 import { AuthorList, Files } from 'pubsweet-components-faraday/src/components'
diff --git a/packages/xpub-faraday/config/components.json b/packages/xpub-faraday/config/components.json
index 7a1555ce9..4f60ab58b 100644
--- a/packages/xpub-faraday/config/components.json
+++ b/packages/xpub-faraday/config/components.json
@@ -6,7 +6,5 @@
   "pubsweet-component-modal",
   "pubsweet-components-faraday",
   "@pubsweet/component-aws-s3",
-  "pubsweet-component-invite",
-  "component-aws-download",
-  "component-local-aws"
+  "pubsweet-component-invite"
 ]
diff --git a/packages/xpub-faraday/package.json b/packages/xpub-faraday/package.json
index 7a7955bd8..f4ffca9a6 100644
--- a/packages/xpub-faraday/package.json
+++ b/packages/xpub-faraday/package.json
@@ -8,11 +8,10 @@
     "url": "https://gitlab.coko.foundation/xpub/xpub-faraday"
   },
   "dependencies": {
-    "@pubsweet/component-aws-s3": "^0.1.1",
-    "@pubsweet/ui": "^3.0.0",
+    "@pubsweet/ui": "^3.2.0",
+    "@pubsweet/component-aws-s3": "^1.0.0",
     "aws-sdk": "^2.197.0",
     "babel-core": "^6.26.0",
-    "component-aws-download": "0.0.1",
     "config": "^1.26.2",
     "dotenv": "^5.0.0",
     "flavors": "^3.3.1",
@@ -44,7 +43,7 @@
     "typeface-ubuntu-mono": "^0.0.54",
     "winston": "^2.4.0",
     "xpub-connect": "^0.0.3",
-    "xpub-edit": "^0.0.3",
+    "xpub-edit": "^0.0.4",
     "xpub-faraday-server": "^0.0.1",
     "xpub-journal": "^0.0.3",
     "xpub-selectors": "^0.0.3",
diff --git a/yarn.lock b/yarn.lock
index a20d4b3bb..50204ef2a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -71,10 +71,11 @@
     lodash "^4.2.0"
     to-fast-properties "^2.0.0"
 
-"@pubsweet/component-aws-s3@^0.1.1":
-  version "0.1.1"
-  resolved "https://registry.yarnpkg.com/@pubsweet/component-aws-s3/-/component-aws-s3-0.1.1.tgz#2faa379c1b8c12dbc6573a5627b09f536a484cc3"
+"@pubsweet/component-aws-s3@^1.0.0":
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/@pubsweet/component-aws-s3/-/component-aws-s3-1.0.0.tgz#5cb1715709ba5ad7b8ad79d1ef42d6818d17ca3e"
   dependencies:
+    archiver "^2.1.1"
     aws-sdk "^2.185.0"
     body-parser "^1.17.2"
     multer "^1.3.0"
@@ -164,7 +165,7 @@
     redux-form "^7.0.3"
     styled-components "^2.4.0"
 
-"@pubsweet/ui@^3.0.0", "@pubsweet/ui@^3.1.0":
+"@pubsweet/ui@^3.1.0":
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/@pubsweet/ui/-/ui-3.1.0.tgz#24c25c29fc36e34b9f654fe4378502232f8204fa"
   dependencies:
@@ -186,6 +187,28 @@
     redux-form "^7.0.3"
     styled-components "^2.4.0"
 
+"@pubsweet/ui@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@pubsweet/ui/-/ui-3.2.0.tgz#b787f1bb53fa818aade863c221bf47e2929e669e"
+  dependencies:
+    babel-jest "^21.2.0"
+    classnames "^2.2.5"
+    enzyme "^3.2.0"
+    enzyme-adapter-react-16 "^1.1.1"
+    humps "^2.0.1"
+    lodash "^4.17.4"
+    prop-types "^15.5.10"
+    react "^16.2.0"
+    react-dom "^16.2.0"
+    react-feather "^1.0.8"
+    react-redux "^5.0.2"
+    react-router-dom "^4.2.2"
+    react-tag-autocomplete "^5.5.0"
+    recompose "^0.26.0"
+    redux "^3.6.0"
+    redux-form "^7.0.3"
+    styled-components "^2.4.0"
+
 "@types/node@*":
   version "9.4.6"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-9.4.6.tgz#d8176d864ee48753d053783e4e463aec86b8d82e"
@@ -3132,7 +3155,19 @@ enzyme-adapter-react-15@^1.0.5:
     object.values "^1.0.4"
     prop-types "^15.5.10"
 
-enzyme-adapter-utils@^1.1.0:
+enzyme-adapter-react-16@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.1.1.tgz#a8f4278b47e082fbca14f5bfb1ee50ee650717b4"
+  dependencies:
+    enzyme-adapter-utils "^1.3.0"
+    lodash "^4.17.4"
+    object.assign "^4.0.4"
+    object.values "^1.0.4"
+    prop-types "^15.6.0"
+    react-reconciler "^0.7.0"
+    react-test-renderer "^16.0.0-0"
+
+enzyme-adapter-utils@^1.1.0, enzyme-adapter-utils@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.3.0.tgz#d6c85756826c257a8544d362cc7a67e97ea698c7"
   dependencies:
@@ -8375,6 +8410,15 @@ react-proxy@^3.0.0-alpha.0:
   dependencies:
     lodash "^4.6.1"
 
+react-reconciler@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.7.0.tgz#9614894103e5f138deeeb5eabaf3ee80eb1d026d"
+  dependencies:
+    fbjs "^0.8.16"
+    loose-envify "^1.1.0"
+    object-assign "^4.1.1"
+    prop-types "^15.6.0"
+
 react-redux@^5.0.2, react-redux@^5.0.6, react-redux@^5.0.7:
   version "5.0.7"
   resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.0.7.tgz#0dc1076d9afb4670f993ffaef44b8f8c1155a4c8"
@@ -8417,10 +8461,18 @@ react-router@^4.2.0:
     prop-types "^15.5.4"
     warning "^3.0.0"
 
-react-tag-autocomplete@^5.4.1:
+react-tag-autocomplete@^5.4.1, react-tag-autocomplete@^5.5.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/react-tag-autocomplete/-/react-tag-autocomplete-5.5.0.tgz#49841388b88323f6bccb0c10039bd0252875b49f"
 
+react-test-renderer@^16.0.0-0:
+  version "16.2.0"
+  resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.2.0.tgz#bddf259a6b8fcd8555f012afc8eacc238872a211"
+  dependencies:
+    fbjs "^0.8.16"
+    object-assign "^4.1.1"
+    prop-types "^15.6.0"
+
 react-transition-group@^2.0.0, react-transition-group@^2.2.0:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.1.tgz#e9fb677b79e6455fd391b03823afe84849df4a10"
@@ -10754,11 +10806,11 @@ xpub-connect@^0.0.3:
     redux "^3.6.0"
     styled-components "^2.4.0"
 
-xpub-edit@^0.0.3:
-  version "0.0.3"
-  resolved "https://registry.yarnpkg.com/xpub-edit/-/xpub-edit-0.0.3.tgz#76947a18140d1b61c460fcd96127569d322e0da7"
+xpub-edit@^0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/xpub-edit/-/xpub-edit-0.0.4.tgz#2d20d4ab0ba83ac0b4b3d3f273a74da1518b55d1"
   dependencies:
-    "@pubsweet/ui" "3.0.0"
+    "@pubsweet/ui" "^3.2.0"
     prosemirror-commands "^1.0.1"
     prosemirror-dropcursor "^1.0.0"
     prosemirror-gapcursor "^1.0.0"
@@ -10768,8 +10820,6 @@ xpub-edit@^0.0.3:
     prosemirror-model "^1.0.0"
     prosemirror-state "^1.0.1"
     prosemirror-view "^1.0.0"
-    react "^16.2.0"
-    react-dom "^16.2.0"
 
 xpub-journal@^0.0.3:
   version "0.0.3"
-- 
GitLab