Skip to content
Snippets Groups Projects
Commit fbb4e609 authored by Sebastian Mihalache's avatar Sebastian Mihalache
Browse files

feat(component-manuscript-manager): add submit endpoint and EiC email

parent 0b0239cd
No related branches found
No related tags found
1 merge request!13Sprint #14
......@@ -347,6 +347,30 @@ class Email {
},
})
}
async setupManuscriptSubmittedEmail() {
const {
baseUrl,
UserModel,
collection,
parsedFragment: { id, title },
authors: { submittingAuthor: { firstName = '', lastName = '' } },
} = this
const userHelper = new User({ UserModel })
const eic = await userHelper.getEditorInChief()
mailService.sendSimpleEmail({
toEmail: eic.email,
emailType: 'manuscript-submitted',
dashboardUrl: baseUrl,
meta: {
collection,
fragment: { id, authorName: `${firstName} ${lastName}`, title },
eicName: `${eic.firstName} ${eic.lastName}`,
},
})
}
}
const getSubject = recommendation =>
......
......@@ -7,10 +7,10 @@ const resetPath = config.get('invite-reset-password.url')
module.exports = {
sendSimpleEmail: async ({
toEmail,
user,
emailType,
dashboardUrl,
toEmail = '',
user = {},
emailType = '',
dashboardUrl = '',
meta = {},
}) => {
let subject, textBody
......@@ -117,6 +117,26 @@ module.exports = {
textBody = `${replacements.headline}`
emailTemplate = 'noCTA'
break
case 'manuscript-submitted':
subject = `${meta.collection.customId}: Manuscript Submitted`
replacements.previewText = 'A new manuscript has been submitted'
replacements.headline = `A new manuscript has been submitted.`
replacements.paragraph = `You can view the full manuscript titled "${
meta.fragment.title
}" by ${
meta.fragment.authorName
} and take further actions by clicking on the following link:`
replacements.buttonText = 'MANUSCRIPT DETAILS'
replacements.url = helpers.createUrl(
dashboardUrl,
`/projects/${meta.collection.id}/versions/${
meta.fragment.id
}/details`,
)
textBody = `${replacements.headline} ${replacements.paragraph} ${
replacements.url
} ${replacements.buttonText}`
break
default:
subject = 'Welcome to Hindawi!'
break
......
......@@ -9,7 +9,7 @@ const Fragments = app => {
})
/**
* @api {patch} /api/collections/:collectionId/fragments/:fragmentId/submit Submit a revision for a manuscript
* @apiGroup FragmentsRecommendations
* @apiGroup Fragments
* @apiParam {collectionId} collectionId Collection id
* @apiParam {fragmentId} fragmentId Fragment id
* @apiSuccessExample {json} Success
......@@ -28,6 +28,27 @@ const Fragments = app => {
authBearer,
require(`${routePath}/patch`)(app.locals.models),
)
/**
* @api {post} /api/collections/:collectionId/fragments/:fragmentId/submit Submit a manuscript
* @apiGroup Fragments
* @apiParam {collectionId} collectionId Collection id
* @apiParam {fragmentId} fragmentId Fragment id
* @apiSuccessExample {json} Success
* HTTP/1.1 200 OK
* {
*
* }
* @apiErrorExample {json} Invite user errors
* HTTP/1.1 403 Forbidden
* HTTP/1.1 400 Bad Request
* HTTP/1.1 404 Not Found
* HTTP/1.1 500 Internal Server Error
*/
app.post(
`${basePath}`,
authBearer,
require(`${routePath}/post`)(app.locals.models),
)
}
module.exports = Fragments
const {
Email,
Fragment,
services,
authsome: authsomeHelper,
} = require('pubsweet-component-helper-service')
module.exports = models => async (req, res) => {
const { collectionId, fragmentId } = req.params
let collection, fragment
try {
collection = await models.Collection.find(collectionId)
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 target = {
fragment,
path: req.route.path,
}
const canPost = await authsome.can(req.user, 'POST', target)
if (!canPost)
return res.status(403).json({
error: 'Unauthorized.',
})
fragment.submitted = Date.now()
fragment = await fragment.save()
const fragmentHelper = new Fragment({ fragment })
const parsedFragment = await fragmentHelper.getFragmentData({
handlingEditor: collection.handlingEditor,
})
const authors = await fragmentHelper.getAuthorData({
UserModel: models.User,
})
const email = new Email({
authors,
collection,
parsedFragment,
UserModel: models.User,
baseUrl: services.getBaseUrl(req),
})
email.setupManuscriptSubmittedEmail()
return res.status(200).json(fragment)
} catch (e) {
const notFoundError = await services.handleNotFoundError(e, 'Item')
return res.status(notFoundError.status).json({
error: notFoundError.message,
})
}
}
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
process.env.SUPPRESS_NO_CONFIG_WARNING = true
const cloneDeep = require('lodash/cloneDeep')
const fixturesService = require('pubsweet-component-fixture-service')
const requests = require('../requests')
const { Model, fixtures } = fixturesService
jest.mock('pubsweet-component-mail-service', () => ({
sendNotificationEmail: jest.fn(),
}))
const reqBody = {}
const path = '../routes/fragments/post'
const route = {
path: '/api/collections/:collectionId/fragments/:fragmentId/submit',
}
describe('Post fragments 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 parameters are correct', async () => {
const { user } = testFixtures.users
const { collection } = testFixtures.collections
const { fragment } = testFixtures.fragments
const res = await requests.sendRequest({
body,
userId: user.id,
models,
route,
path,
params: {
collectionId: collection.id,
fragmentId: fragment.id,
},
})
expect(res.statusCode).toBe(200)
})
it('should return an error when the fragmentId does not match the collectionId', async () => {
const { user } = testFixtures.users
const { collection } = testFixtures.collections
const { noParentFragment } = testFixtures.fragments
const res = await requests.sendRequest({
body,
userId: user.id,
models,
route,
path,
params: {
collectionId: collection.id,
fragmentId: noParentFragment.id,
},
})
expect(res.statusCode).toBe(400)
const data = JSON.parse(res._getData())
expect(data.error).toEqual('Collection and fragment do not match.')
})
it('should return an error when the collection does not exist', async () => {
const { user } = testFixtures.users
const { fragment } = testFixtures.fragments
const res = await requests.sendRequest({
body,
userId: user.id,
models,
route,
path,
params: {
collectionId: 'invalid-id',
fragmentId: fragment.id,
},
})
expect(res.statusCode).toBe(404)
const data = JSON.parse(res._getData())
expect(data.error).toEqual('Item not found')
})
})
......@@ -192,6 +192,14 @@ async function applyAuthenticatedUserPolicy(user, operation, object, context) {
roles: ['reviewer', 'handlingEditor'],
})
}
// allow owner to submit a manuscript
if (
get(object, 'path') ===
'/api/collections/:collectionId/fragments/:fragmentId/submit'
) {
return helpers.isOwner({ user, object: object.fragment })
}
}
if (operation === 'PATCH') {
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment