From 1627fb146ac0c5858df4332ef473e6aa23803b34 Mon Sep 17 00:00:00 2001
From: ahamelers <audrey@ahamelers.com>
Date: Wed, 23 Jan 2019 17:33:59 +0000
Subject: [PATCH] citations, normal journal added correctly now, WIP: still
 working on unmatched citation

---
 .../citation-search/PubMedSearch.jsx          | 198 ++++---
 .../citation-search/UnmatchedCitation.jsx     |   2 +-
 app/components/citation-search/operations.js  |  45 ++
 .../submission-wizard/CreatePage.jsx          |   5 +-
 .../submission-wizard/CreateSetupPage.jsx     |   2 +-
 .../submission-wizard/SubmitPage.jsx          |  11 +-
 .../submission-wizard/operations.js           |   8 -
 .../entities/journal/data-access.js           |  13 +-
 server/xpub-model/entities/journal/index.js   |   1 +
 .../xpub-model/entities/manuscript/index.js   |   8 +-
 server/xpub-model/index.js                    |   2 +
 .../xpub-server/entities/journal/resolvers.js |  17 +
 .../entities/journal/typeDefs.graphqls        |   3 +
 .../entities/manuscript/typeDefs.graphqls     |   2 +-
 .../entities/note/resolvers.test.js           | 520 ------------------
 server/xpub-server/index.js                   |   1 +
 16 files changed, 199 insertions(+), 639 deletions(-)
 create mode 100644 app/components/citation-search/operations.js
 create mode 100644 server/xpub-server/entities/journal/resolvers.js
 create mode 100644 server/xpub-server/entities/journal/typeDefs.graphqls
 delete mode 100644 server/xpub-server/entities/note/resolvers.test.js

diff --git a/app/components/citation-search/PubMedSearch.jsx b/app/components/citation-search/PubMedSearch.jsx
index 7c40bf292..13ebd3c1e 100755
--- a/app/components/citation-search/PubMedSearch.jsx
+++ b/app/components/citation-search/PubMedSearch.jsx
@@ -1,6 +1,6 @@
 import React from 'react'
 import ReactDOM from 'react-dom'
-import { withRouter } from 'react-router-dom'
+import { ApolloConsumer } from 'react-apollo'
 import styled from 'styled-components'
 import { th } from '@pubsweet/ui-toolkit'
 import { Action, Button, Icon, H2, Link } from '@pubsweet/ui'
@@ -18,6 +18,7 @@ import {
   SearchForm,
   ZebraList,
 } from '../ui'
+import { JOURNAL_INFO } from './operations'
 import PubMedSearchResult from './PubMedSearchResult'
 import UnmatchedCitation from './UnmatchedCitation'
 
@@ -54,9 +55,9 @@ class PubMedSearch extends React.Component {
       inEPMC: false,
       unmatched:
         this.props.metadata &&
-        (!this.props.metadata.articleId ||
-          this.props.metadata.articleId.length === 0),
-      // pubProvided: null,
+        (!this.props.metadata.articleIds ||
+          this.props.metadata.articleIds.length === 0),
+      pubProvided: false,
     }
     this.onSearch = this.onSearch.bind(this)
     this.onLoad = this.onLoad.bind(this)
@@ -117,7 +118,7 @@ class PubMedSearch extends React.Component {
     }
     this.setState({ loading: false })
   }
-  async onSelected(result) {
+  async onSelected(result, journal) {
     const inPMC = result.articleids.find(id => id.idtype === 'pmc')
     if (inPMC) {
       const obj = {
@@ -134,43 +135,49 @@ class PubMedSearch extends React.Component {
         obj.inEPMC = true
       }
       this.setState(obj)
+    } else if (journal.meta.pmcStatus) {
+      this.setState({ pubProvided: journal.journalTitle })
     } else {
-      // TODO: Add test for journals that participate in (are indexed by) PMC. Processing does not need to continue for these.
-
-      /* const journalUrl = `https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi?db=nlmcatalog&id=${result.nlmuniqueid}&retmode=xml`
-      const response = await fetch(journalUrl)
-      const xml = await response.text()
-      const indexedby = xml.match(/<IndexingSourceName(.*)<\/IndexingSourceName>/g)
-      console.log(indexedby) */
       const date = new Date(result.sortpubdate)
       const citationData = {
-        title: result.title,
-        articleIds: [
-          {
-            pubIdType: 'pmid',
-            id: result.uid,
-          },
-        ],
-        publicationDates: [
-          {
-            type: 'ppub',
-            date,
-          },
-        ],
-        nlmId: result.nlmuniqueid,
+        journalId: journal.id,
+        meta: {
+          title: result.title,
+          articleIds: [
+            {
+              pubIdType: 'pmid',
+              id: result.uid,
+            },
+          ],
+          publicationDates: [
+            {
+              type: 'ppub',
+              date,
+            },
+          ],
+        },
       }
       this.props.citationData(citationData)
     }
   }
   render() {
-    const { results, hitcount, loading, query, inPMC, pmcid } = this.state
-    const { metadata } = this.props
-    const { title, journalMeta, customMeta, notes } = metadata || {}
+    const {
+      results,
+      hitcount,
+      loading,
+      query,
+      inPMC,
+      inEPMC,
+      pubProvided,
+      pmcid,
+    } = this.state
+    const { manuscript } = this.props
+    const { meta, notes, journal } = manuscript || {}
     const note = notes ? notes.find(n => n.notesType === 'user citation') : ''
-    let journal = journalMeta
-    if (!journal && customMeta && customMeta.unmatchedJournal) {
-      journal = {
-        title: customMeta.unmatchedJournal,
+    let journalInfo = journal
+    if (!journalInfo && meta && meta.unmatchedJournal) {
+      journalInfo = {
+        journalTitle: meta.unmatchedJournal,
       }
     }
     if (this.state.unmatched) {
@@ -181,9 +188,9 @@ class PubMedSearch extends React.Component {
               <CloseIcon onClick={() => this.setState({ unmatched: false })} />
             </Close>
             <UnmatchedCitation
-              journal={journal}
+              journal={journalInfo}
               note={note ? note.content : ''}
-              title={title}
+              title={meta.title}
               unmatchedInfo={this.handleUnmatchedInfo}
             />
           </Page>
@@ -193,31 +200,43 @@ class PubMedSearch extends React.Component {
     }
     return (
       <SearchArea>
-        {inPMC ? (
-          <div>
+        {inPMC || pubProvided ? (
+          <React.Fragment>
             <H2>No further action is required for this manuscript</H2>
-            <p>
-              The full text of the article &apos;
-              <HTMLString string={inPMC} />
-              &apos;
-              {` has already been sent to Europe PMC. Please use `}
-              <B>{pmcid}</B>
-              {` for grant reporting purposes.`}
-            </p>
-            {this.state.inEPMC && (
-              <Links>
-                <A href={`https://europepmc.org/articles/${pmcid}`}>
-                  <Icon color="currentColor">arrow-right-circle</Icon>
-                  View this article on Europe PMC
-                </A>
-                <br />
-                <A
-                  href={`https://europepmc.org/orcid/import?3-1.ILinkListener-startwizard?query=${pmcid}`}
-                >
-                  <Icon color="currentColor">arrow-right-circle</Icon>
-                  Claim this article to your ORCID
-                </A>
-              </Links>
+            {inPMC ? (
+              <React.Fragment>
+                <p>
+                  The full text of the article &apos;
+                  <HTMLString string={inPMC} />
+                  &apos;
+                  {` has already been sent to Europe PMC. Please use `}
+                  <B>{pmcid}</B>
+                  {` for grant reporting purposes.`}
+                </p>
+                {inEPMC && (
+                  <Links>
+                    <A href={`https://europepmc.org/articles/${pmcid}`}>
+                      <Icon color="currentColor">arrow-right-circle</Icon>
+                      View this article on Europe PMC
+                    </A>
+                    {/* TODO: ORCID claiming
+                    <br />
+                    <A
+                      href={`https://europepmc.org/orcid/import?3-1.ILinkListener-startwizard?query=${pmcid}`}
+                    >
+                      <Icon color="currentColor">arrow-right-circle</Icon>
+                      Claim this article to your ORCID
+                    </A> */}
+                  </Links>
+                )}
+              </React.Fragment>
+            ) : (
+              <p>
+                Your journal, {pubProvided.journalTitle}, participates in PMC.
+                Your article will be provided to Europe PMC, through PMC, by the
+                publisher. Please contact the publisher regarding any delays in
+                this process.
+              </p>
             )}
             <Buttons>
               <Link to="/">
@@ -229,15 +248,16 @@ class PubMedSearch extends React.Component {
                     inPMC: null,
                     pmcid: null,
                     inEPMC: false,
+                    pubProvided: null,
                   })
                 }
               >
                 Back to Search
               </Button>
             </Buttons>
-          </div>
+          </React.Fragment>
         ) : (
-          <div>
+          <React.Fragment>
             <SearchForm
               buttonLabel="Search"
               disabled={!this.state.enabled}
@@ -266,39 +286,49 @@ class PubMedSearch extends React.Component {
               </Notice>
             )}
             {results.length > 0 && (
-              <ZebraList style={{ textAlign: 'center' }}>
-                {results.map(
-                  result =>
-                    result.fulljournalname && (
-                      <PubMedSearchResult
-                        key={result.uid}
-                        onClick={() => this.onSelected(result)}
-                        result={result}
-                      />
-                    ),
-                )}
-                {loading && (
-                  <Loading>
-                    <LoadingIcon />
-                  </Loading>
-                )}
-                {results.length < hitcount && !loading && (
-                  <LoadMore onClick={this.onLoad} secondary>
-                    Load More Results
-                  </LoadMore>
+              <ApolloConsumer>
+                {client => (
+                  <ZebraList style={{ textAlign: 'center' }}>
+                    {results.map(
+                      result =>
+                        result.fulljournalname && (
+                          <PubMedSearchResult
+                            key={result.uid}
+                            onClick={async () => {
+                              const { data } = await client.query({
+                                query: JOURNAL_INFO,
+                                variables: { nlmId: result.nlmuniqueid },
+                              })
+                              this.onSelected(result, data.selectWithNLM)
+                            }}
+                            result={result}
+                          />
+                        ),
+                    )}
+                    {loading && (
+                      <Loading>
+                        <LoadingIcon />
+                      </Loading>
+                    )}
+                    {results.length < hitcount && !loading && (
+                      <LoadMore onClick={this.onLoad} secondary>
+                        Load More Results
+                      </LoadMore>
+                    )}
+                  </ZebraList>
                 )}
-              </ZebraList>
+              </ApolloConsumer>
             )}
             {results.length === 0 && loading && (
               <Loading>
                 <LoadingIcon />
               </Loading>
             )}
-          </div>
+          </React.Fragment>
         )}
       </SearchArea>
     )
   }
 }
 
-export default withRouter(PubMedSearch)
+export default PubMedSearch
diff --git a/app/components/citation-search/UnmatchedCitation.jsx b/app/components/citation-search/UnmatchedCitation.jsx
index 72de2aacb..cfa26696b 100755
--- a/app/components/citation-search/UnmatchedCitation.jsx
+++ b/app/components/citation-search/UnmatchedCitation.jsx
@@ -17,7 +17,7 @@ export default class UnmatchedCitation extends React.Component {
     this.state = {
       loading: false,
       title: this.props.title,
-      query: this.props.journal ? this.props.journal.title : '',
+      query: this.props.journal ? this.props.journal.journalTitle : '',
       journal: this.props.journal,
       note: this.props.note,
       enabled: this.props.title && this.props.journal,
diff --git a/app/components/citation-search/operations.js b/app/components/citation-search/operations.js
new file mode 100644
index 000000000..c11190531
--- /dev/null
+++ b/app/components/citation-search/operations.js
@@ -0,0 +1,45 @@
+import gql from 'graphql-tag'
+
+export const JOURNAL_INFO = gql`
+  query($nlmId: String!) {
+    selectWithNLM(nlmId: $nlmId) {
+      id
+      journalTitle
+      meta {
+        nlmta
+        pmcStatus
+        pubmedStatus
+      }
+    }
+  }
+`
+
+export const CREATE_NOTE = gql`
+  mutation CreateNote($data: NewNoteInput!) {
+    createNote(data: $data) {
+      id
+      meta {
+        notes {
+          id
+          notesType
+          content
+        }
+      }
+    }
+  }
+`
+
+export const UPDATE_NOTE = gql`
+  mutation UpdateNote($data: NoteInput!) {
+    updateNote(data: $data) {
+      id
+      meta {
+        notes {
+          id
+          notesType
+          content
+        }
+      }
+    }
+  }
+`
diff --git a/app/components/submission-wizard/CreatePage.jsx b/app/components/submission-wizard/CreatePage.jsx
index 23e0b231e..c0d426166 100755
--- a/app/components/submission-wizard/CreatePage.jsx
+++ b/app/components/submission-wizard/CreatePage.jsx
@@ -147,7 +147,8 @@ class Created extends React.Component {
   render() {
     const currentUser = this.context
     const { currentStep, status, error, checked } = this.state
-    const { id: mId, meta, journal, files: allfiles } = this.props.manuscript
+    const { manuscript } = this.props
+    const { id: mId, meta, journal, files: allfiles } = manuscript
     const { notes } = meta
     const files = allfiles
       ? allfiles.filter(
@@ -213,7 +214,7 @@ class Created extends React.Component {
                           <H2>Citation</H2>
                           <PubMedSearch
                             citationData={changeCitation}
-                            metadata={meta}
+                            manuscript={manuscript}
                           />
                         </React.Fragment>
                       ) : (
diff --git a/app/components/submission-wizard/CreateSetupPage.jsx b/app/components/submission-wizard/CreateSetupPage.jsx
index 073a4f16e..4c41f3871 100755
--- a/app/components/submission-wizard/CreateSetupPage.jsx
+++ b/app/components/submission-wizard/CreateSetupPage.jsx
@@ -17,7 +17,7 @@ const manuscriptSetup = async (
     const created = await createManuscript()
     const { id } = created.data.createManuscript
     await updateManuscript({
-      variables: { data: { id, meta: citationData } },
+      variables: { data: { id, ...citationData } },
     })
     const route = `/submission/${id}/create`
     history.push(route)
diff --git a/app/components/submission-wizard/SubmitPage.jsx b/app/components/submission-wizard/SubmitPage.jsx
index c0fa9686f..df41cf01a 100755
--- a/app/components/submission-wizard/SubmitPage.jsx
+++ b/app/components/submission-wizard/SubmitPage.jsx
@@ -116,13 +116,8 @@ class Submit extends React.Component {
     `${name.title ? `${name.title} ` : ''}${name.givenNames} ${name.surname}`
   render() {
     const currentUser = this.context
-    const {
-      id: mId,
-      meta,
-      files: allfiles,
-      status,
-      teams,
-    } = this.props.manuscript
+    const { manuscript } = this.props
+    const { id: mId, meta, files: allfiles, status, teams } = manuscript
     if (teams && allfiles) {
       const { editing, references, regexes } = this.state
       const fundingGroup = meta.fundingGroup ? meta.fundingGroup : []
@@ -170,7 +165,7 @@ class Submit extends React.Component {
                   <H3>Citation</H3>
                   <PubMedSearch
                     citationData={this.createNewManuscriptVersion}
-                    metadata={meta}
+                    manuscript={manuscript}
                   />
                 </div>
               ),
diff --git a/app/components/submission-wizard/operations.js b/app/components/submission-wizard/operations.js
index c52a14982..ec6b7347c 100644
--- a/app/components/submission-wizard/operations.js
+++ b/app/components/submission-wizard/operations.js
@@ -150,14 +150,6 @@ export const UPDATE_NOTE = gql`
   }
 `
 
-export const DELETE_NOTE = gql`
-  mutation DeleteNote($id: ID!) {
-    deleteNote(id: $id) {
-      id
-    }
-  }
-`
-
 export const SUBMIT_MANUSCRIPT = gql`
   mutation SubmitManuscript($data: ManuscriptInput!) {
     submitManuscript(data: $data) {
diff --git a/server/xpub-model/entities/journal/data-access.js b/server/xpub-model/entities/journal/data-access.js
index 6ef633f05..dc623e0b6 100644
--- a/server/xpub-model/entities/journal/data-access.js
+++ b/server/xpub-model/entities/journal/data-access.js
@@ -60,18 +60,17 @@ class Journal extends EpmcBaseModel {
     return rowToEntity(rows[0])
   }
 
-  static async getIdByNlmId(nlmId) {
-    const row = await runQuery(
+  static async selectByNlmId(nlmId) {
+    const rows = await runQuery(
       buildQuery
-        .select('journal.id')
+        .select('journal.*')
         .from('journal')
-        .where('meta,nlmuniqueid', nlmId)
-        .first(),
+        .where('meta,nlmuniqueid', nlmId),
     )
-    if (!row) {
+    if (!rows) {
       throw new Error('journal not found')
     }
-    return row.id
+    return rowToEntity(rows[0])
   }
 
   static async selectAll() {
diff --git a/server/xpub-model/entities/journal/index.js b/server/xpub-model/entities/journal/index.js
index 2c47dfdac..832a196d6 100755
--- a/server/xpub-model/entities/journal/index.js
+++ b/server/xpub-model/entities/journal/index.js
@@ -23,6 +23,7 @@ const JournalManager = {
 
     return { ...journal, id }
   },
+  selectWithNLM: async nlmId => Journal.selectByNlmId(nlmId),
 }
 
 module.exports = JournalManager
diff --git a/server/xpub-model/entities/manuscript/index.js b/server/xpub-model/entities/manuscript/index.js
index dfa41dbef..ca6dc2b31 100755
--- a/server/xpub-model/entities/manuscript/index.js
+++ b/server/xpub-model/entities/manuscript/index.js
@@ -9,7 +9,6 @@ const FileAccess = require('../file/data-access')
 const NoteAccess = require('../note/data-access')
 const ReviewAccess = require('../review/data-access')
 const Team = require('../team/data-access')
-const Journal = require('../journal/data-access')
 const { dManuscriptUpdate, gManuscript } = require('./helpers/transform')
 
 const mergeObjects = (...inputs) =>
@@ -139,12 +138,7 @@ const Manuscript = {
     if (!originalMan) {
       throw new Error('Manuscript not found')
     }
-    const newInput = input
-    if (input.meta.nlmId) {
-      input.journalId = await Journal.getIdByNlmId(input.meta.nlmId)
-      delete newInput.meta.nlmId
-    }
-    const manuscriptUpdate = dManuscriptUpdate(newInput, userId)
+    const manuscriptUpdate = dManuscriptUpdate(input, userId)
     lodash.assign(originalMan, manuscriptUpdate)
     await originalMan.save()
     const updatedMan = await ManuscriptAccess.selectById(input.id, true)
diff --git a/server/xpub-model/index.js b/server/xpub-model/index.js
index 5b8e19742..f31cd1750 100644
--- a/server/xpub-model/index.js
+++ b/server/xpub-model/index.js
@@ -5,6 +5,7 @@ const NoteManager = require('./entities/note')
 const RoleManager = require('./entities/role')
 const TeamManager = require('./entities/team')
 const UserManager = require('./entities/user')
+const JournalManager = require('./entities/journal')
 const OrganizationManager = require('./entities/organization')
 
 module.exports = {
@@ -15,5 +16,6 @@ module.exports = {
   RoleManager,
   TeamManager,
   UserManager,
+  JournalManager,
   OrganizationManager,
 }
diff --git a/server/xpub-server/entities/journal/resolvers.js b/server/xpub-server/entities/journal/resolvers.js
new file mode 100644
index 000000000..415fcf7c4
--- /dev/null
+++ b/server/xpub-server/entities/journal/resolvers.js
@@ -0,0 +1,17 @@
+const rfr = require('rfr')
+
+const { JournalManager } = rfr('server/xpub-model')
+
+const resolvers = {
+  Query: {
+    async selectWithNLM(_, { nlmId }, { user }) {
+      if (!user) {
+        throw new Error('You are not authenticated!')
+      }
+
+      return JournalManager.selectWithNLM(nlmId)
+    },
+  },
+}
+
+module.exports = resolvers
diff --git a/server/xpub-server/entities/journal/typeDefs.graphqls b/server/xpub-server/entities/journal/typeDefs.graphqls
new file mode 100644
index 000000000..4b5b4c416
--- /dev/null
+++ b/server/xpub-server/entities/journal/typeDefs.graphqls
@@ -0,0 +1,3 @@
+extend type Query {
+  selectWithNLM(nlmId: String!): Journal!
+}
\ No newline at end of file
diff --git a/server/xpub-server/entities/manuscript/typeDefs.graphqls b/server/xpub-server/entities/manuscript/typeDefs.graphqls
index 54d9d236d..ef2a633ad 100644
--- a/server/xpub-server/entities/manuscript/typeDefs.graphqls
+++ b/server/xpub-server/entities/manuscript/typeDefs.graphqls
@@ -29,6 +29,7 @@ extend type Mutation {
 input ManuscriptInput {
   id: ID!
   status: String
+  journalId: ID
   meta: ManuscriptMetaInput
 }
 
@@ -39,7 +40,6 @@ input ManuscriptMetaInput {
   fundingGroup: [FundingGroupInput]
   releaseDelay: String
   unmatchedJournal: String
-  nlmId: String
 }
 
 input ArticleIdInput {
diff --git a/server/xpub-server/entities/note/resolvers.test.js b/server/xpub-server/entities/note/resolvers.test.js
deleted file mode 100644
index 3e3d26d07..000000000
--- a/server/xpub-server/entities/note/resolvers.test.js
+++ /dev/null
@@ -1,520 +0,0 @@
-/* jest.mock('@pubsweet/logger')
-jest.mock('@elifesciences/xpub-meca-export', () => ({
-  mecaExport: jest.fn(() => Promise.resolve()),
-})) 
-
-const lodash = require('lodash')
-const config = require('config')
-const fs = require('fs-extra')
-const stream = require('stream')
-const logger = require('@pubsweet/logger')
-const { createTables } = require('@pubsweet/db-manager')
-const mailer = require('@pubsweet/component-send-email')
-// const { mecaExport } = require('@elifesciences/xpub-meca-export')
-const startS3rver = require('../../test/mock-s3-server')
-const {
-  UserManager: User,
-  FileManager,
-  ManuscriptManager: Manuscript,
-} = require('@elifesciences/xpub-model')
-const { Mutation, Query } = require('./resolvers')
-const {
-  userData,
-  badUserData,
-  expectedManuscript,
-  manuscriptInput,
-} = require('./resolvers.test.data')
-
-// const replaySetup = require('../../../../test/helpers/replay-setup')
-
-describe('Manuscripts', () => {
-  const profileId = userData.identities[0].identifier
-  const badProfileId = badUserData.identities[0].identifier
-  let userId
-
-  beforeEach(async () => {
-    // replaySetup('success')
-    await Promise.all([
-      fs.remove(config.get('pubsweet-server.uploads')),
-      createTables(true),
-    ])
-    const [user] = await Promise.all([
-      User.save(userData),
-      User.save(badUserData),
-    ])
-    userId = user.id
-    mailer.clearMails()
-  })
-
-  describe('manuscript', () => {
-    it('Gets form data', async () => {
-      const manuscriptData = {
-        createdBy: userId,
-        meta: { title: 'title' },
-        status: 'INITIAL',
-      }
-      const { id } = await Manuscript.save(manuscriptData)
-
-      const manuscript = await Query.manuscript({}, { id }, { user: profileId })
-      expect(manuscript).toMatchObject(manuscriptData)
-    })
-  })
-
-  describe('createManuscript', () => {
-    it('fails if no authenticated user', async () => {
-      await expect(Mutation.createManuscript({}, {}, {})).rejects.toThrow(
-        'Not logged in',
-      )
-    })
-
-    it('adds new manuscript to the db for current user with status INITIAL', async () => {
-      const manuscript = await Mutation.createManuscript(
-        {},
-        {},
-        { user: profileId },
-      )
-
-      const manuscripts = await Manuscript.findByStatus('INITIAL', userId)
-      expect(manuscripts.length).toBeGreaterThan(0)
-      expect(manuscripts[0].id).toBe(manuscript.id)
-    })
-  })
-
-  describe('updateManuscript', () => {
-    it("fails if manuscript doesn't belong to user", async () => {
-      const blankManuscript = Manuscript.new({ createdBy: userId })
-      const manuscript = await Manuscript.save(blankManuscript)
-      await expect(
-        Mutation.updateManuscript(
-          {},
-          { data: { id: manuscript.id } },
-          { user: badProfileId },
-        ),
-      ).rejects.toThrow('Manuscript not found')
-    })
-
-    it('fails if manuscript has already been submitted', async () => {
-      const blankManuscript = Manuscript.new({
-        createdBy: userId,
-        status: Manuscript.statuses.MECA_EXPORT_PENDING,
-      })
-      const manuscript = await Manuscript.save(blankManuscript)
-      await expect(
-        Mutation.updateManuscript(
-          {},
-          { data: { id: manuscript.id } },
-          { user: profileId },
-        ),
-      ).rejects.toThrow(
-        'Cannot update manuscript with status of MECA_EXPORT_PENDING',
-      )
-    })
-
-    it('updates the current submission for user with data and emails', async () => {
-      const NUM_EMAILS = 1
-      const blankManuscript = Manuscript.new({ createdBy: userId })
-      const manuscript = await Manuscript.save(blankManuscript)
-
-      await Mutation.updateManuscript(
-        {},
-        { data: { id: manuscript.id, ...manuscriptInput } },
-        { user: profileId },
-      )
-      await waitforEmails(NUM_EMAILS)
-
-      const allEmails = mailer.getMails()
-      expect(allEmails).toHaveLength(NUM_EMAILS)
-
-      // Check the dashboard email
-      expect(allEmails[0]).toMatchObject({
-        to: 'mymail@mail.com',
-        subject: 'Libero Submission System: New Submission',
-      })
-
-      const actualManuscript = await Manuscript.find(manuscript.id, userId)
-      expect(actualManuscript).toMatchObject(expectedManuscript)
-    })
-  })
-
-  describe('submitManuscript', () => {
-    let id, initialManuscript
-
-    beforeAll(() =>
-      jest
-        .spyOn(FileManager, 'getContent')
-        .mockImplementation(() => 'A real PDF'))
-
-    afterAll(() => FileManager.getContent.mockRestore())
-
-    beforeEach(async () => {
-      jest.clearAllMocks()
-
-      initialManuscript = Manuscript.new({ createdBy: userId })
-      initialManuscript.files = [
-        {
-          url: 'fake-path.pdf',
-          filename: 'FakeManuscript.pdf',
-          type: 'MANUSCRIPT_SOURCE',
-        },
-      ]
-
-      const manuscript = await Manuscript.save(initialManuscript)
-      id = manuscript.id
-    })
-
-    it('stores data with new status', async () => {
-      const returnedManuscript = await Mutation.submitManuscript(
-        {},
-        { data: { ...manuscriptInput, id } },
-        { user: profileId },
-      )
-
-      expect(returnedManuscript.status).toBe(
-        Manuscript.statuses.MECA_EXPORT_PENDING,
-      )
-
-      const storedManuscript = await Manuscript.find(id, userId)
-
-      expect(storedManuscript).toMatchObject({
-        ...expectedManuscript,
-        status: expect.stringMatching(/^MECA_EXPORT_(SUCCEEDED|PENDING)/),
-      })
-    })
-
-    it('calls meca export with correct arguments', async () => {
-      const ip = '1.2.3.4'
-      await Mutation.submitManuscript(
-        {},
-        { data: { ...manuscriptInput, id } },
-        { user: profileId, ip },
-      )
-
-      expect(mecaExport).toHaveBeenCalled()
-      const [
-        actualManuscript,
-        actualContent,
-        actualIp,
-      ] = mecaExport.mock.calls[0]
-      expect(actualManuscript.id).toBe(id)
-      expect(actualContent).toBe('A real PDF')
-      expect(actualIp).toBe(ip)
-    })
-
-    it('removes blank optional reviewer rows', async () => {
-      const manuscript = lodash.cloneDeep(manuscriptInput)
-      manuscript.id = id
-      manuscript.suggestedReviewers = [
-        { name: 'Reviewer 1', email: 'reviewer1@mail.com' },
-        { name: 'Reviewer 2', email: 'reviewer2@mail.com' },
-        { name: 'Reviewer 3', email: 'reviewer3@mail.com' },
-        { name: '', email: '' },
-        { name: 'Reviewer 4', email: 'reviewer4@mail.com' },
-        { name: '', email: '' },
-      ]
-      await Mutation.submitManuscript(
-        {},
-        { data: manuscript },
-        { user: profileId },
-      )
-
-      const storedManuscript = await Manuscript.find(id, userId)
-      const team = storedManuscript.teams.find(
-        t => t.role === 'suggestedReviewer',
-      )
-      expect(team.teamMembers.map(member => member.meta)).toEqual([
-        { name: 'Reviewer 1', email: 'reviewer1@mail.com' },
-        { name: 'Reviewer 2', email: 'reviewer2@mail.com' },
-        { name: 'Reviewer 3', email: 'reviewer3@mail.com' },
-        { name: 'Reviewer 4', email: 'reviewer4@mail.com' },
-      ])
-    })
-
-    it("fails if manuscript doesn't belong to user", async () => {
-      const blankManuscript = Manuscript.new({ createdBy: userId })
-      const manuscript = await Manuscript.save(blankManuscript)
-      await expect(
-        Mutation.submitManuscript(
-          {},
-          { data: { id: manuscript.id } },
-          { user: badProfileId },
-        ),
-      ).rejects.toThrow('Manuscript not found')
-    })
-
-    it('fails if manuscript has already been submitted', async () => {
-      const blankManuscript = Manuscript.new({
-        createdBy: userId,
-        status: Manuscript.statuses.MECA_EXPORT_PENDING,
-      })
-      const manuscript = await Manuscript.save(blankManuscript)
-      await expect(
-        Mutation.submitManuscript(
-          {},
-          { data: { id: manuscript.id } },
-          { user: profileId },
-        ),
-      ).rejects.toThrow(
-        'Cannot submit manuscript with status of MECA_EXPORT_PENDING',
-      )
-    })
-
-    // TODO more tests needed here
-    it('fails when manuscript has incomplete data', async () => {
-      const badManuscript = {
-        id,
-        title: 'Some Title',
-      }
-      await expect(
-        Mutation.submitManuscript(
-          {},
-          { data: badManuscript },
-          { user: profileId },
-        ),
-      ).rejects.toThrow()
-    })
-
-    it('fails when manuscript has bad data types', async () => {
-      const badManuscript = lodash.cloneDeep(manuscriptInput)
-      lodash.merge(badManuscript, {
-        id,
-        title: 100,
-        manuscriptType: {},
-      })
-      await expect(
-        Mutation.submitManuscript(
-          {},
-          { data: badManuscript },
-          { user: profileId },
-        ),
-      ).rejects.toThrow(
-        '"title" is not allowed. "manuscriptType" is not allowed',
-      )
-    })
-
-    it('sends email and updates status when export fails', async () => {
-      const NUM_EMAILS = 1
-      jest.spyOn(logger, 'error').mockImplementationOnce(() => {})
-      mecaExport.mockImplementationOnce(() =>
-        Promise.reject(new Error('Broked')),
-      )
-
-      const manuscript = lodash.cloneDeep(manuscriptInput)
-      manuscript.id = id
-      await Mutation.submitManuscript(
-        {},
-        { data: manuscript },
-        { user: profileId },
-      )
-
-      await waitforEmails(NUM_EMAILS)
-
-      const allEmails = mailer.getMails()
-      expect(allEmails).toHaveLength(NUM_EMAILS)
-
-      // Check the MECA fail email
-      expect(allEmails[0]).toMatchObject({
-        to: 'test@example.com',
-        subject: 'MECA export failed',
-      })
-
-      expect(logger.error).toHaveBeenCalled()
-      const updatedManuscript = await Manuscript.find(manuscript.id, userId)
-      expect(updatedManuscript.status).toBe(
-        Manuscript.statuses.MECA_EXPORT_FAILED,
-      )
-    })
-  })
-
-  describe('uploadManuscript', () => {
-    let s3Server
-
-    beforeEach(async () => {
-      const server = await startS3rver({
-        ...config.get('aws.credentials'),
-        ...config.get('aws.s3'),
-      })
-      s3Server = server.instance
-    })
-
-    afterEach(done => {
-      s3Server.close(done)
-    })
-
-    it("fails if manuscript doesn't belong to user", async () => {
-      const blankManuscript = Manuscript.new()
-      blankManuscript.createdBy = userId
-      const manuscript = await Manuscript.save(blankManuscript)
-
-      await expect(
-        Mutation.uploadManuscript(
-          {},
-          { id: manuscript.id },
-          { user: badProfileId },
-        ),
-      ).rejects.toThrow('Manuscript not found')
-    })
-
-    it('saves manuscript to S3', async () => {
-      const blankManuscript = Manuscript.new()
-      blankManuscript.createdBy = userId
-      const { id } = await Manuscript.save(blankManuscript)
-      const file = {
-        filename: 'manuscript.pdf',
-        stream: fs.createReadStream(
-          `${__dirname}/../../../../test/fixtures/dummy-manuscript-2.pdf`,
-        ),
-        mimetype: 'application/pdf',
-      }
-      const manuscript = await Mutation.uploadManuscript(
-        {},
-        { id, file, fileSize: 73947 },
-        { user: profileId },
-      )
-
-      const pdfBinary = await FileManager.getContent(
-        Manuscript.getSource(manuscript),
-      )
-      expect(pdfBinary.toString().substr(0, 6)).toEqual('%PDF-1')
-    })
-
-    it('fails if S3 upload fails', async () => {
-      jest
-        .spyOn(FileManager, 'putContent')
-        .mockImplementationOnce(() =>
-          Promise.reject(new Error('Failed to persist file')),
-        )
-      const blankManuscript = Manuscript.new({ createdBy: userId })
-      const { id } = await Manuscript.save(blankManuscript)
-      const file = {
-        filename: 'manuscript.pdf',
-        stream: fs.createReadStream(
-          `${__dirname}/../../../../test/fixtures/dummy-manuscript-2.pdf`,
-        ),
-        mimetype: 'application/pdf',
-      }
-      await expect(
-        Mutation.uploadManuscript(
-          {},
-          { id, file, fileSize: 73947 },
-          { user: profileId },
-        ),
-      ).rejects.toThrow('Failed to persist file')
-
-      const manuscript = await Manuscript.find(id, userId)
-      expect(manuscript.files).toHaveLength(0)
-    })
-
-    it('sets empty title if ScienceBeam fails', async () => {
-      jest.spyOn(logger, 'warn').mockImplementationOnce(() => {})
-      // replaySetup('error')
-      const blankManuscript = Manuscript.new()
-      blankManuscript.createdBy = userId
-      const { id } = await Manuscript.save(blankManuscript)
-      const file = {
-        filename: 'manuscript.pdf',
-        stream: fs.createReadStream(
-          `${__dirname}/../../../../test/fixtures/dummy-manuscript-2.pdf`,
-        ),
-        mimetype: 'application/pdf',
-      }
-      const manuscript = await Mutation.uploadManuscript(
-        {},
-        { id, file, fileSize: 73947 },
-        { user: profileId },
-      )
-      expect(manuscript).toMatchObject({
-        id,
-        meta: { title: '' },
-        files: [{ filename: 'manuscript.pdf' }],
-      })
-      expect(logger.warn).toHaveBeenCalled()
-    })
-
-    it('extracts title from PDF', async () => {
-      const blankManuscript = Manuscript.new()
-      blankManuscript.createdBy = userId
-      const { id } = await Manuscript.save(blankManuscript)
-      const file = {
-        filename: 'manuscript.pdf',
-        stream: fs.createReadStream(
-          `${__dirname}/../../../../test/fixtures/dummy-manuscript-2.pdf`,
-        ),
-        mimetype: 'application/pdf',
-      }
-      const manuscript = await Mutation.uploadManuscript(
-        {},
-        { id, file, fileSize: 73947 },
-        { user: profileId },
-      )
-      expect(manuscript).toMatchObject({
-        id,
-        meta: {
-          title:
-            'The Relationship Between Lamport Clocks and Interrupts Using Obi',
-        },
-        files: [{ filename: 'manuscript.pdf' }],
-      })
-    })
-
-    it(`fails if manuscript size is bigger than ${config.get(
-      'fileUpload.maxSizeMB',
-    )}MB`, async () => {
-      const blankManuscript = Manuscript.new()
-      blankManuscript.createdBy = userId
-      const { id } = await Manuscript.save(blankManuscript)
-
-      const maxFileSize = config.get('fileUpload.maxSizeMB')
-      const fileSize = maxFileSize * 1e6 + 1
-      const bufferStream = new stream.PassThrough()
-      bufferStream.end(Buffer.alloc(fileSize))
-      const file = {
-        filename: 'manuscript.pdf',
-        stream: bufferStream,
-        mimetype: 'application/pdf',
-      }
-      await expect(
-        Mutation.uploadManuscript(
-          {},
-          { id, file, fileSize },
-          { user: profileId },
-        ),
-      ).rejects.toThrow(`File size shouldn't exceed ${maxFileSize}MB`)
-    })
-  })
-
-  describe('deleteManuscript', () => {
-    it("fails if manuscript doesn't belong to user", async () => {
-      const blankManuscript = Manuscript.new()
-      blankManuscript.createdBy = userId
-      const manuscript = await Manuscript.save(blankManuscript)
-
-      await expect(
-        Mutation.deleteManuscript(
-          {},
-          { id: manuscript.id },
-          { user: badProfileId },
-        ),
-      ).rejects.toThrow('Manuscript not found')
-    })
-
-    it('removes manuscript from database', async () => {
-      const blankManuscript = Manuscript.new()
-      blankManuscript.createdBy = userId
-      const manuscript = await Manuscript.save(blankManuscript)
-      await Mutation.deleteManuscript(
-        {},
-        { id: manuscript.id },
-        { user: profileId },
-      )
-
-      const manuscripts = await Manuscript.all(userId)
-      expect(manuscripts).toEqual([])
-    })
-  })
-})
-async function waitforEmails(NUM_EMAILS) {
-  for (let i = 0; i < 100 && mailer.getMails().length < NUM_EMAILS; i += 1) {
-    // eslint-disable-next-line no-await-in-loop
-    await new Promise(resolve => setTimeout(resolve, 10))
-  }
-} */
diff --git a/server/xpub-server/index.js b/server/xpub-server/index.js
index 39898cdaa..cc800fec3 100644
--- a/server/xpub-server/index.js
+++ b/server/xpub-server/index.js
@@ -8,6 +8,7 @@ const entities = [
   'file',
   'identity',
   'manuscript',
+  'journal',
   'note',
   'team',
   'role',
-- 
GitLab