diff --git a/packages/component-invite/src/routes/collectionsInvitations/patch.js b/packages/component-invite/src/routes/collectionsInvitations/patch.js index 19e95891cbedfb9e8ec0f0309986e78fe404803b..dcfde76b44eb1915df3db8f7aa1a6caabeec257b 100644 --- a/packages/component-invite/src/routes/collectionsInvitations/patch.js +++ b/packages/component-invite/src/routes/collectionsInvitations/patch.js @@ -21,20 +21,21 @@ module.exports = models => async (req, res) => { const invitation = await collection.invitations.find( invitation => invitation.id === invitationId, ) - if (invitation === undefined) { - res.status(404).json({ + if (invitation === undefined) + return res.status(404).json({ error: `Invitation ${invitationId} not found`, }) - return - } - if (invitation.userId !== user.id) { - res.status(403).json({ + if (invitation.hasAnswer) + return res + .status(400) + .json({ error: `${invitation.id} has already been answered` }) + if (invitation.userId !== user.id) + return res.status(403).json({ error: `User ${user.email} is not allowed to modify invitation ${ invitation.id }`, }) - return - } + if (invitation.role === 'handlingEditor') await collectionHelper.updateHandlingEditor(collection, isAccepted) invitation.timestamp = Date.now() diff --git a/packages/component-invite/src/tests/collectionsInvitations/delete.test.js b/packages/component-invite/src/tests/collectionsInvitations/delete.test.js index 8b8b78eaad94c4ea18994dc4acd9e47a096c38e0..38ca5f0f27a654940ee63b22411a73e2b2b6bbac 100644 --- a/packages/component-invite/src/tests/collectionsInvitations/delete.test.js +++ b/packages/component-invite/src/tests/collectionsInvitations/delete.test.js @@ -11,12 +11,13 @@ jest.mock('pubsweet-component-mail-service', () => ({ })) const deletePath = '../../routes/collectionsInvitations/delete' -const models = Model.build() describe('Delete Collections Invitations route handler', () => { let testFixtures = {} + let models beforeEach(() => { testFixtures = cloneDeep(fixtures) + models = Model.build(testFixtures) }) it('should return an error when the collection does not exist', async () => { const { editorInChief } = testFixtures.users diff --git a/packages/component-invite/src/tests/collectionsInvitations/get.test.js b/packages/component-invite/src/tests/collectionsInvitations/get.test.js index b594ef52666a89e8e619b7e63862c2e6eb3ea49e..7d64dcf8bfb2344606bbfb1bbbb7410194549662 100644 --- a/packages/component-invite/src/tests/collectionsInvitations/get.test.js +++ b/packages/component-invite/src/tests/collectionsInvitations/get.test.js @@ -9,8 +9,10 @@ const cloneDeep = require('lodash/cloneDeep') const getPath = '../../routes/collectionsInvitations/get' describe('Get collection invitations route handler', () => { let testFixtures = {} + let models beforeEach(() => { testFixtures = cloneDeep(fixtures) + models = Model.build(testFixtures) }) it('should return success when the request data is correct', async () => { const { editorInChief, handlingEditor } = testFixtures.users @@ -23,7 +25,6 @@ describe('Get collection invitations route handler', () => { req.params.collectionId = collection.id req.user = editorInChief.id const res = httpMocks.createResponse() - const models = Model.build() await require(getPath)(models)(req, res) expect(res.statusCode).toBe(200) @@ -36,7 +37,6 @@ describe('Get collection invitations route handler', () => { req.query = {} req.user = editorInChief.id const res = httpMocks.createResponse() - const models = Model.build() await require(getPath)(models)(req, res) expect(res.statusCode).toBe(400) const data = JSON.parse(res._getData()) @@ -53,7 +53,6 @@ describe('Get collection invitations route handler', () => { req.params.collectionId = 'invalid-id' req.user = editorInChief.id const res = httpMocks.createResponse() - const models = Model.build() await require(getPath)(models)(req, res) expect(res.statusCode).toBe(404) const data = JSON.parse(res._getData()) @@ -70,7 +69,6 @@ describe('Get collection invitations route handler', () => { req.params.collectionId = collection.id req.user = editorInChief.id const res = httpMocks.createResponse() - const models = Model.build() await require(getPath)(models)(req, res) expect(res.statusCode).toBe(400) const data = JSON.parse(res._getData()) @@ -88,7 +86,6 @@ describe('Get collection invitations route handler', () => { req.params.collectionId = collection.id req.user = editorInChief.id const res = httpMocks.createResponse() - const models = Model.build() await require(getPath)(models)(req, res) expect(res.statusCode).toBe(400) const data = JSON.parse(res._getData()) diff --git a/packages/component-invite/src/tests/collectionsInvitations/patch.test.js b/packages/component-invite/src/tests/collectionsInvitations/patch.test.js index 62c4332bd2fbadaba33ceda8f3cfa2a98c6eed90..5bd2442091472233b2e08a0f0cb2e2c139db545c 100644 --- a/packages/component-invite/src/tests/collectionsInvitations/patch.test.js +++ b/packages/component-invite/src/tests/collectionsInvitations/patch.test.js @@ -6,7 +6,6 @@ const fixtures = require('./../fixtures/fixtures') const Model = require('./../helpers/Model') const cloneDeep = require('lodash/cloneDeep') -const models = Model.build() jest.mock('pubsweet-component-mail-service', () => ({ setupAssignEmail: jest.fn(), setupAgreeEmail: jest.fn(), @@ -20,9 +19,11 @@ const patchPath = '../../routes/collectionsInvitations/patch' describe('Patch collections invitations route handler', () => { let testFixtures = {} let body = {} + let models beforeEach(() => { testFixtures = cloneDeep(fixtures) body = cloneDeep(reqBody) + models = Model.build(testFixtures) }) it('should return success when the handling editor accepts work on a collection', async () => { const { handlingEditor } = testFixtures.users @@ -33,7 +34,7 @@ describe('Patch collections invitations route handler', () => { req.user = handlingEditor.id req.params.collectionId = collection.id const heInv = collection.invitations.find( - inv => inv.role === 'handlingEditor', + inv => inv.role === 'handlingEditor' && inv.hasAnswer === false, ) req.params.invitationId = heInv.id const res = httpMocks.createResponse() @@ -65,7 +66,10 @@ describe('Patch collections invitations route handler', () => { }) req.user = handlingEditor.id req.params.collectionId = collection.id - req.params.invitationId = collection.invitations[0].id + const heInv = collection.invitations.find( + inv => inv.role === 'handlingEditor' && inv.hasAnswer == false, + ) + req.params.invitationId = heInv.id const res = httpMocks.createResponse() await require(patchPath)(models)(req, res) @@ -127,7 +131,7 @@ describe('Patch collections invitations route handler', () => { req.user = reviewer.id req.params.collectionId = collection.id const inv = collection.invitations.find( - inv => inv.role === 'handlingEditor', + inv => inv.role === 'handlingEditor' && inv.hasAnswer === false, ) req.params.invitationId = inv.id const res = httpMocks.createResponse() @@ -138,4 +142,20 @@ describe('Patch collections invitations route handler', () => { `User ${reviewer.email} is not allowed to modify invitation ${inv.id}`, ) }) + it('should return an error when the invitation is already answered', async () => { + const { handlingEditor } = testFixtures.users + const { collection } = testFixtures.collections + const req = httpMocks.createRequest({ + body, + }) + req.user = handlingEditor.id + req.params.collectionId = collection.id + const inv = collection.invitations.find(inv => inv.hasAnswer) + req.params.invitationId = inv.id + const res = httpMocks.createResponse() + await require(patchPath)(models)(req, res) + expect(res.statusCode).toBe(400) + const data = JSON.parse(res._getData()) + expect(data.error).toEqual(`${inv.id} has already been answered`) + }) }) diff --git a/packages/component-invite/src/tests/collectionsInvitations/post.test.js b/packages/component-invite/src/tests/collectionsInvitations/post.test.js index 14573ae7393e4f03910742ce25e2fc2975f024ba..5378ad0e6b6b0e81858874106ad58aeeab28a0b6 100644 --- a/packages/component-invite/src/tests/collectionsInvitations/post.test.js +++ b/packages/component-invite/src/tests/collectionsInvitations/post.test.js @@ -11,7 +11,6 @@ const cloneDeep = require('lodash/cloneDeep') const configRoles = config.get('roles') -const models = Model.build() jest.mock('pubsweet-component-mail-service', () => ({ setupInviteEmail: jest.fn(), setupAssignEmail: jest.fn(), @@ -34,9 +33,11 @@ const postPath = '../../routes/collectionsInvitations/post' describe('Post collections invitations route handler', () => { let testFixtures = {} let body = {} + let models beforeEach(() => { testFixtures = cloneDeep(fixtures) body = cloneDeep(reqBody) + models = Model.build(testFixtures) }) it('should return an error params are missing', async () => { const { admin } = testFixtures.users diff --git a/packages/component-invite/src/tests/fixtures/collections.js b/packages/component-invite/src/tests/fixtures/collections.js index 78920b4aa04df9a88ae59d850cf3daa0fb95d4bb..9331af1e1f653959caaf886ea8cc0e51c59b9324 100644 --- a/packages/component-invite/src/tests/fixtures/collections.js +++ b/packages/component-invite/src/tests/fixtures/collections.js @@ -35,6 +35,14 @@ const collections = { userId: reviewer.id, timestamp: chance.timestamp(), }, + { + id: chance.guid(), + role: 'handlingEditor', + hasAnswer: true, + isAccepted: false, + userId: handlingEditor.id, + timestamp: chance.timestamp(), + }, ], handlingEditor: { id: handlingEditor.id, diff --git a/packages/component-invite/src/tests/helpers/Model.js b/packages/component-invite/src/tests/helpers/Model.js index c1ded2c8e9866d90df89faa05d681b49dd207311..7337e1aa65d689787330b3996571b22c5c39504c 100644 --- a/packages/component-invite/src/tests/helpers/Model.js +++ b/packages/component-invite/src/tests/helpers/Model.js @@ -1,4 +1,4 @@ -const fixtures = require('../fixtures/fixtures') +// const fixtures = require('../fixtures/fixtures') const UserMock = require('../mocks/User') const TeamMock = require('../mocks/Team') @@ -7,23 +7,23 @@ const notFoundError = new Error() notFoundError.name = 'NotFoundError' notFoundError.status = 404 -const build = () => { +const build = fixtures => { const models = { User: {}, Collection: { - find: jest.fn(id => findMock(id, 'collections')), + find: jest.fn(id => findMock(id, 'collections', fixtures)), }, Team: {}, Fragment: { - find: jest.fn(id => findMock(id, 'fragments')), + find: jest.fn(id => findMock(id, 'fragments', fixtures)), }, } - UserMock.find = jest.fn(id => findMock(id, 'users')) - UserMock.findByEmail = jest.fn(email => findByEmailMock(email)) + UserMock.find = jest.fn(id => findMock(id, 'users', fixtures)) + UserMock.findByEmail = jest.fn(email => findByEmailMock(email, fixtures)) UserMock.all = jest.fn(() => Object.values(fixtures.users)) - TeamMock.find = jest.fn(id => findMock(id, 'teams')) + TeamMock.find = jest.fn(id => findMock(id, 'teams', fixtures)) TeamMock.updateProperties = jest.fn(team => - updatePropertiesMock(team, 'teams'), + updatePropertiesMock(team, 'teams', fixtures), ) TeamMock.all = jest.fn(() => Object.values(fixtures.teams)) @@ -32,7 +32,7 @@ const build = () => { return models } -const findMock = (id, type) => { +const findMock = (id, type, fixtures) => { const foundObj = Object.values(fixtures[type]).find( fixtureObj => fixtureObj.id === id, ) @@ -41,7 +41,7 @@ const findMock = (id, type) => { return Promise.resolve(foundObj) } -const findByEmailMock = email => { +const findByEmailMock = (email, fixtures) => { const foundUser = Object.values(fixtures.users).find( fixtureUser => fixtureUser.email === email, ) @@ -50,7 +50,7 @@ const findByEmailMock = email => { return Promise.resolve(foundUser) } -const updatePropertiesMock = (obj, type) => { +const updatePropertiesMock = (obj, type, fixtures) => { const foundObj = Object.values(fixtures[type]).find( fixtureObj => fixtureObj === obj, ) diff --git a/packages/component-mail-service/src/Mail.js b/packages/component-mail-service/src/Mail.js index e5e4bbbd209e1cf81e098cc74e83f8de2decf83f..d0cd73a98305e197cb38d923b02b51979fddcd41 100644 --- a/packages/component-mail-service/src/Mail.js +++ b/packages/component-mail-service/src/Mail.js @@ -161,7 +161,9 @@ module.exports = { email: invitedUser.email, token: invitedUser.passwordResetToken, collectionId: collection.id, + fragmentId, agree: true, + invitationId, })}` } diff --git a/packages/component-user-manager/src/helpers/Collection.js b/packages/component-user-manager/src/helpers/Collection.js index 40cf70f3bd2965e1e6d5b2bc86d3b30372391a3d..85853a987c05a85f28010d59a9579ee503969dd4 100644 --- a/packages/component-user-manager/src/helpers/Collection.js +++ b/packages/component-user-manager/src/helpers/Collection.js @@ -13,6 +13,11 @@ module.exports = { collection.authors = collection.authors || [] const author = { userId: user.id, + firstName: user.firstName || '', + lastName: user.lastName || '', + email: user.email, + title: user.title || '', + affiliation: user.affiliation || '', isSubmitting, isCorresponding, } diff --git a/packages/component-user-manager/src/routes/collectionsUsers/get.js b/packages/component-user-manager/src/routes/collectionsUsers/get.js index 7f11e412a000edc026e8b7ce336b2ec39fb46ed3..1d6b395dd79dfeaa3e5b38f8c00a4b29d6c5716b 100644 --- a/packages/component-user-manager/src/routes/collectionsUsers/get.js +++ b/packages/component-user-manager/src/routes/collectionsUsers/get.js @@ -2,7 +2,7 @@ const helpers = require('../../helpers/helpers') const teamHelper = require('../../helpers/Team') module.exports = models => async (req, res) => { - // TO DO: add authsom + // TO DO: add authsome const { collectionId } = req.params try { const collection = await models.Collection.find(collectionId) diff --git a/packages/components-faraday/src/components/Dashboard/DashboardCard.js b/packages/components-faraday/src/components/Dashboard/DashboardCard.js index dc6058df1952753ac83d84766fcbecb644ce7204..d291f88149fbee610b0d3a9efadb861e9360efed 100644 --- a/packages/components-faraday/src/components/Dashboard/DashboardCard.js +++ b/packages/components-faraday/src/components/Dashboard/DashboardCard.js @@ -108,14 +108,14 @@ const DashboardCard = ({ <DetailsView> <Top> <AuthorList> - {version.authors.map( + {project.authors.map( ( { affiliation, firstName, lastName, - middleName, email, + userId, isSubmitting, isCorresponding, }, @@ -124,16 +124,15 @@ const DashboardCard = ({ ) => ( <AuthorTooltip affiliation={affiliation} - authorName={`${firstName} ${lastName}`} + authorName={`${firstName || ''} ${lastName || ''}`} + currentUser={currentUser} email={email} isCorresponding={isCorresponding} isSubmitting={isSubmitting} - key={email} + key={userId} > <Author> - <AuthorName> - {firstName} {middleName} {lastName} - </AuthorName> + <AuthorName>{`${firstName} ${lastName}`}</AuthorName> {isSubmitting && <AuthorStatus>SA</AuthorStatus>} {isCorresponding && <AuthorStatus>CA</AuthorStatus>} {arr.length - 1 === index ? '' : ','} @@ -224,6 +223,7 @@ const AuthorList = styled.span` const AuthorName = styled.span` text-decoration: underline; padding-left: 2px; + cursor: default; ` const Author = styled.div` padding-right: ${th('subGridUnit')}; diff --git a/packages/xpub-faraday-server/src/AuthorBackend.js b/packages/xpub-faraday-server/src/AuthorBackend.js index 3c0fbcc1dab501f51fb9080d8612ada06746fcde..d05bb8349c8ae0e5dcc051102e710c9c2196871c 100644 --- a/packages/xpub-faraday-server/src/AuthorBackend.js +++ b/packages/xpub-faraday-server/src/AuthorBackend.js @@ -40,7 +40,7 @@ const AuthorBackend = app => { if (submittingAuthors.length > 0) { res .status(400) - .json({ error: 'There can only be one sumbitting author' }) + .json({ error: 'There can only be one submitting author' }) return } } diff --git a/packages/xpub-faraday-server/src/AuthorBackend.test.js b/packages/xpub-faraday-server/src/AuthorBackend.test.js index 697e0b8237acb26da5f79904d80b2bf6e01b543b..ab4ee126dd3f531778bcfb68f9fc19ee43e2cd35 100644 --- a/packages/xpub-faraday-server/src/AuthorBackend.test.js +++ b/packages/xpub-faraday-server/src/AuthorBackend.test.js @@ -130,7 +130,7 @@ describe('Author Backend API', () => { .post(createAuthorUrl) .set('Authorization', 'Bearer 123') .send(testFixtures.authors.newSubmittingAuthor) - .expect(400, '{"error":"There can only be one sumbitting author"}') + .expect(400, '{"error":"There can only be one submitting author"}') }) it('should return success when saving a new author', () => {