Skip to content
Snippets Groups Projects
Commit 0a2a7583 authored by Sebastian Mihalache's avatar Sebastian Mihalache :hammer_pick:
Browse files

refactor(recommendations): fully implemented strategy pattern

parent 515cf4a9
No related branches found
No related tags found
3 merge requests!196S25 - EiC submit revision,!189S25,!177Hin 230 eic request revision
Showing
with 169 additions and 101 deletions
......@@ -231,6 +231,10 @@ class Collection {
this.collection.technicalChecks = {}
await this.collection.save()
}
hasHandlingEditor() {
return has(this.collection, 'handlingEditor')
}
}
const sendMTSPackage = async ({
......
const { get, remove, findLast } = require('lodash')
const { get, remove, findLast, pick, chain } = require('lodash')
const config = require('config')
const User = require('./User')
......@@ -230,6 +230,31 @@ class Fragment {
this.fragment.recommendations.push(newRecommendation)
await this.fragment.save()
}
async addRevision() {
this.fragment.revision = pick(this.fragment, [
'authors',
'files',
'metadata',
])
await this.fragment.save()
}
hasReviewers() {
const { fragment: invitations = [] } = this
return invitations.length > 0
}
getLatestUserRecommendation(userId) {
return findLast(this.fragment.recommendations, r => r.userId === userId)
}
getLatestRecommendation() {
return chain(this.fragment)
.get('recommendations', [])
.last()
.value()
}
}
module.exports = Fragment
const { pick, isEmpty, chain, findLast } = require('lodash')
const config = require('config')
const { v4 } = require('uuid')
......@@ -11,11 +10,14 @@ const {
const { recommendations } = config
const Notification = require('../../notifications/notification')
const rejectAsHE = require('./strategies/heReject')
const publishAsHE = require('./strategies/hePublish')
const publishAsEiC = require('./strategies/eicPublish')
const rejectAsEiC = require('./strategies/eicReject')
const publishAsEiC = require('./strategies/eicPublish')
const returnToHE = require('./strategies/eicReturnToHE')
const Notification = require('../../notifications/notification')
const createReview = require('./strategies/reviewerCreateReview')
const requestRevisionAsHE = require('./strategies/heRequestRevision')
module.exports = models => async (req, res) => {
const { recommendation, comments, recommendationType } = req.body
......@@ -23,6 +25,8 @@ module.exports = models => async (req, res) => {
return res.status(400).json({ error: 'Recommendation type is required.' })
const reqUser = await models.User.find(req.user)
const userId = reqUser.id
// console.log('REQ USER', reqUser)
const isEditorInChief = reqUser.editorInChief || reqUser.admin
const { collectionId, fragmentId } = req.params
......@@ -78,71 +82,13 @@ module.exports = models => async (req, res) => {
return res.status(400).json({ error })
}
// const currentUserRecommendations = get(
// fragment,
// 'recommendations',
// [],
// ).filter(r => r.userId === req.user)
const latestUserRecommendation = findLast(
fragment.recommendations,
r => r.userId === req.user,
)
if (latestUserRecommendation) {
if (recommendationType === recommendations.type.review) {
return res
.status(400)
.json({ error: 'Cannot write another review on this version.' })
}
if (
recommendationType === recommendations.type.editor &&
!isEditorInChief &&
!fragmentHelper.canHEMakeAnotherRecommendation(latestUserRecommendation)
) {
return res.status(400).json({
error: 'Cannot make another recommendation on this version.',
})
}
const lastFragmentRecommendation = chain(fragment)
.get('recommendations', [])
.last()
.value()
if (
recommendationType === recommendations.type.editor &&
isEditorInChief &&
recommendation !== recommendations.reject &&
lastFragmentRecommendation.recommendation === 'return-to-handling-editor'
) {
return res.status(400).json({
error: 'Cannot make another recommendation on this version.',
})
}
}
let role = ''
switch (recommendationType) {
case 'review':
role = 'reviewer'
break
case 'editorRecommendation':
role = isEditorInChief ? 'eic' : 'he'
break
default:
return res.status(400).json({
error: `Recommendation ${recommendation} is not defined.`,
})
}
const newRecommendation = {
userId,
id: v4(),
userId: reqUser.id,
recommendation,
recommendationType,
createdOn: Date.now(),
updatedOn: Date.now(),
recommendationType,
recommendation,
comments: comments || [],
}
......@@ -156,51 +102,54 @@ module.exports = models => async (req, res) => {
const strategies = {
he: {
reject: rejectAsHE,
publish: publishAsHE,
major: requestRevisionAsHE,
minor: requestRevisionAsHE,
},
eic: {
publish: publishAsEiC,
reject: rejectAsEiC,
publish: publishAsEiC,
'return-to-handling-editor': returnToHE,
},
}
let role = ''
switch (recommendationType) {
case 'review':
role = 'reviewer'
try {
await createReview.execute({
userId,
fragmentHelper,
newRecommendation,
})
} catch (e) {
return res.status(400).json({ error: e.message })
}
return res.status(200).json(newRecommendation)
case 'editorRecommendation':
role = isEditorInChief ? 'eic' : 'he'
break
default:
return res.status(400).json({
error: `Recommendation ${recommendation} is not defined.`,
})
}
try {
strategies[role][recommendation].execute({
await strategies[role][recommendation].execute({
userId,
models,
fragments,
notification,
fragmentHelper,
collectionHelper,
newRecommendation,
})
} catch (e) {
return res.status(400).json({ error: e.message })
}
if (recommendationType === 'editorRecommendation') {
await collectionHelper.updateStatusOnRecommendation({
isEditorInChief,
recommendation,
})
if (!isEditorInChief && ['minor', 'major'].includes(recommendation)) {
fragment.revision = pick(fragment, ['authors', 'files', 'metadata'])
}
const hasPeerReview = !isEmpty(collection.handlingEditor)
if (collection.status === 'revisionRequested') {
notification.notifySAWhenHERequestsRevision()
}
if (hasPeerReview) {
notification.notifyReviewersWhenHEMakesRecommendation()
notification.notifyEiCWhenHEMakesRecommendation()
}
}
fragment.recommendations.push(newRecommendation)
fragment.save()
return res.status(200).json(newRecommendation)
}
......@@ -10,6 +10,13 @@ module.exports = {
collectionHelper,
newRecommendation,
}) => {
const latestRecommendation = fragmentHelper.getLatestRecommendation()
if (latestRecommendation.recommendation === 'return-to-handling-editor') {
throw new Error(
'Cannot make decision to publish after the manuscript has been returned to Handling Editor.',
)
}
await fragmentHelper.addRecommendation(newRecommendation)
let newStatus = ''
......
......@@ -9,7 +9,11 @@ module.exports = {
await collectionHelper.updateStatus({ newStatus: 'rejected' })
notification.notifyAuthorsWhenEiCMakesDecision()
notification.notifyHEWhenEiCMakesDecision()
notification.notifyReviewersWhenEiCMakesDecision()
if (collectionHelper.hasHandlingEditor()) {
notification.notifyHEWhenEiCMakesDecision()
}
if (fragmentHelper.hasReviewers()) {
notification.notifyReviewersWhenEiCMakesDecision()
}
},
}
......@@ -5,6 +5,11 @@ module.exports = {
collectionHelper,
newRecommendation,
}) => {
const latestRecommendation = fragmentHelper.getLatestRecommendation()
if (latestRecommendation.recommendation === 'return-to-handling-editor') {
throw new Error('Cannot return to Handling Editor again.')
}
if (collectionHelper.hasEQA()) {
await collectionHelper.removeTechnicalChecks()
}
......
module.exports = {
execute: async ({
collectionHelper,
userId,
fragments,
fragmentHelper,
notification,
fragmentHelper,
collectionHelper,
newRecommendation,
}) => {
if (!collectionHelper.canHEMakeRecommendation(fragments, fragmentHelper)) {
throw new Error('Cannot publish without at least one reviewer report.')
}
const latestUserRecommendation = fragmentHelper.getLatestUserRecommendation(
userId,
)
if (
latestUserRecommendation &&
!fragmentHelper.canHEMakeAnotherRecommendation(latestUserRecommendation)
) {
throw new Error('Cannot make another recommendation on this version.')
}
await fragmentHelper.addRecommendation(newRecommendation)
await collectionHelper.updateStatus({ newStatus: 'pendingApproval' })
......
module.exports = {
execute: async ({
userId,
notification,
fragmentHelper,
collectionHelper,
newRecommendation,
}) => {
const latestUserRecommendation = fragmentHelper.getLatestUserRecommendation(
userId,
)
if (
latestUserRecommendation &&
!fragmentHelper.canHEMakeAnotherRecommendation(latestUserRecommendation)
) {
throw new Error('Cannot make another recommendation on this version.')
}
await fragmentHelper.addRecommendation(newRecommendation)
await collectionHelper.updateStatus({ newStatus: 'pendingApproval' })
if (fragmentHelper.hasReviewers()) {
notification.notifyReviewersWhenHEMakesRecommendation()
}
notification.notifyEiCWhenHEMakesRecommendation()
},
}
module.exports = {
execute: async ({
userId,
notification,
fragmentHelper,
collectionHelper,
newRecommendation,
}) => {
const latestUserRecommendation = fragmentHelper.getLatestUserRecommendation(
userId,
)
if (
latestUserRecommendation &&
!fragmentHelper.canHEMakeAnotherRecommendation(latestUserRecommendation)
) {
throw new Error('Cannot make another recommendation on this version.')
}
await fragmentHelper.addRevision()
await collectionHelper.updateStatus({ newStatus: 'revisionRequested' })
await fragmentHelper.addRecommendation(newRecommendation)
notification.notifySAWhenHERequestsRevision()
notification.notifyEiCWhenHEMakesRecommendation()
if (fragmentHelper.hasReviewers()) {
notification.notifyReviewersWhenHEMakesRecommendation()
}
},
}
module.exports = {
execute: async ({ fragmentHelper, newRecommendation, userId }) => {
if (fragmentHelper.getLatestUserRecommendation(userId)) {
throw new Error('Cannot write another review on this version.')
}
await fragmentHelper.addRecommendation(newRecommendation)
},
}
......@@ -63,6 +63,7 @@ describe('Post fragments recommendations route handler', () => {
const { reviewer } = testFixtures.users
const { collection } = testFixtures.collections
const { fragment } = testFixtures.fragments
body.recommendationType = 'review'
const res = await requests.sendRequest({
body,
......@@ -102,7 +103,7 @@ describe('Post fragments recommendations route handler', () => {
expect(data.userId).toEqual(noRecommendationHE.id)
})
it('should return an error when creating a recommendation with publish as a HE when there is a single version and there are no reviews.', async () => {
it('should return an error when recommending to publish as HE when there is a single version and there are no reviews.', async () => {
const { handlingEditor } = testFixtures.users
const { collection } = testFixtures.collections
const { fragment } = testFixtures.fragments
......@@ -445,9 +446,6 @@ describe('Post fragments recommendations route handler', () => {
body.recommendationType = 'editorRecommendation'
body.comments = 'This needs more work'
delete fragment.recommendations
delete fragment.revision
delete fragment.invitations
collection.technicalChecks.eqa = false
const res = await requests.sendRequest({
......@@ -464,7 +462,6 @@ describe('Post fragments recommendations route handler', () => {
expect(res.statusCode).toBe(200)
const data = JSON.parse(res._getData())
expect(collection.status).toBe('reviewCompleted')
expect(collection.technicalChecks).not.toHaveProperty('token')
expect(collection.technicalChecks).not.toHaveProperty('eqa')
......
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