Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const {
supplementaryFiles,
updateAda,
metaSource,
publishedReviewUsers,
// publishedReviewUsers,
removeAuthor,
} = require('../../../controllers/manuscript/manuscript.controllers')

Expand Down Expand Up @@ -397,8 +397,9 @@ const resolvers = {
},
},
PublishedReview: {
async users(parent) {
return publishedReviewUsers(parent)
async users(review, _, ctx) {
// return publishedReviewUsers(parent)
return ctx.loaders.Review.usersLoader.load(review.id)
},
},
}
Expand Down
210 changes: 115 additions & 95 deletions packages/server/controllers/manuscript/manuscript.controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const { chunk, orderBy, uniqBy, flatten } = require('lodash')
const { ref, raw } = require('objection')
const axios = require('axios')

const { File } = require('@coko/server')
const { File, useTransaction } = require('@coko/server')

const {
CoarNotification,
Expand Down Expand Up @@ -113,6 +113,8 @@ const {

const { applyTemplate, generateCss } = require('../../utils/applyTemplate')
const { decrypt } = require('../../utils/encryptDecryptUtils')
const { usersLoader } = require('../../models/review/review.loaders')
const { defaultIdentitiesLoader } = require('../../models/user/user.loaders')

// #endregion import

Expand Down Expand Up @@ -699,32 +701,38 @@ const getRelatedReviews = async (
manuscript,
userId,
shouldGetPublicReviewsOnly = false,
options = {},
) => {
const reviewForm = await getReviewForm(manuscript.groupId)
const decisionForm = await getDecisionForm(manuscript.groupId)
const { trx } = options
const reviewForm = await getReviewForm(manuscript.groupId, { trx })
const decisionForm = await getDecisionForm(manuscript.groupId, { trx })

let reviews =
manuscript.reviews ||
(await Manuscript.relatedQuery('reviews').for(manuscript.id)) ||
(await Manuscript.relatedQuery('reviews', trx).for(manuscript.id)) ||
[]

reviews = await ReviewModel.orderReviewPerUsername(reviews)
reviews = await ReviewModel.orderReviewPerUsername(reviews, { trx })

// eslint-disable-next-line no-restricted-syntax
for (const review of reviews) {
// eslint-disable-next-line no-await-in-loop
await convertFilesToFullObjects(
review,
review.isDecision ? decisionForm : reviewForm,
async ids => File.query().findByIds(ids),
async ids => File.query(trx).findByIds(ids),
)
}

const userRoles = shouldGetPublicReviewsOnly
? {}
: await getUserRolesInManuscript(userId, manuscript.id)
: await getUserRolesInManuscript(userId, manuscript.id, { trx })

const sharedReviewersIds = await getSharedReviewersIds(manuscript.id, userId)
const sharedReviewersIds = await getSharedReviewersIds(
manuscript.id,
userId,
{ trx },
)

// Insert isShared flag before userIds are stripped
// TODO Should this step and the removal of confidential data be moved to the review resolver?
Expand Down Expand Up @@ -1132,51 +1140,57 @@ const publishedManuscript = async id => {
}

const publishedManuscriptDecisions = async (manuscript, userId) => {
// filtering decisions in Kotahi itself so that we can change
// the logic easily in future.
const reviews = await getRelatedReviews(manuscript, userId)
return useTransaction(async trx => {
// filtering decisions in Kotahi itself so that we can change
// the logic easily in future.
const reviews = await getRelatedReviews(manuscript, userId, false, { trx })

if (!Array.isArray(reviews)) {
return []
}
if (!Array.isArray(reviews)) {
return []
}

const decisions = reviews.filter(review => review.isDecision)
const decisionForm = await getDecisionForm(manuscript.groupId)
const decisions = reviews.filter(review => review.isDecision)
const decisionForm = await getDecisionForm(manuscript.groupId, { trx })

const threadedDiscussions =
manuscript.threadedDiscussions ||
(await getThreadedDiscussionsForManuscript(manuscript, getUsersById))
const threadedDiscussions =
manuscript.threadedDiscussions ||
(await getThreadedDiscussionsForManuscript(manuscript, getUsersById, {
trx,
}))

return getPublishableReviewFields(
decisions,
decisionForm,
threadedDiscussions,
manuscript,
)
return getPublishableReviewFields(
decisions,
decisionForm,
threadedDiscussions,
manuscript,
)
})
}

const publishedManuscriptEditors = async manuscript => {
const teams = await Team.query()
.where({ objectId: manuscript.id })
.whereIn('role', ['seniorEditor', 'handlingEditor', 'editor'])

const teamMembers = await TeamMember.query().whereIn(
'team_id',
teams.map(t => t.id),
)
return useTransaction(async trx => {
const teams = await Team.query(trx)
.where({ objectId: manuscript.id })
.whereIn('role', ['seniorEditor', 'handlingEditor', 'editor'])

const teamMembers = await TeamMember.query(trx).whereIn(
'team_id',
teams.map(t => t.id),
)

const editorAndRoles = await Promise.all(
teamMembers.map(async member => {
const user = await User.query().findById(member.userId)
const team = teams.find(t => t.id === member.teamId)
return {
name: user.username,
role: team.role,
}
}),
)
const editorAndRoles = await Promise.all(
teamMembers.map(async member => {
const user = await User.query(trx).findById(member.userId)
const team = teams.find(t => t.id === member.teamId)
return {
name: user.username,
role: team.role,
}
}),
)

return editorAndRoles
return editorAndRoles
})
}

const publishedManuscripts = async (sort, offset, limit, groupId) => {
Expand All @@ -1200,74 +1214,80 @@ const publishedManuscripts = async (sort, offset, limit, groupId) => {
}

const publishedManuscriptReviews = async (manuscript, userId) => {
let reviews = await getRelatedReviews(manuscript, userId, true)
return useTransaction(async trx => {
let reviews = await getRelatedReviews(manuscript, userId, true, { trx })

if (!Array.isArray(reviews)) {
return []
}
if (!Array.isArray(reviews)) {
return []
}

reviews = reviews.filter(review => !review.isDecision)
const reviewForm = await getReviewForm(manuscript.groupId)
reviews = reviews.filter(review => !review.isDecision)
const reviewForm = await getReviewForm(manuscript.groupId, { trx })

const threadedDiscussions =
manuscript.threadedDiscussions ||
(await getThreadedDiscussionsForManuscript(manuscript, getUsersById))
const threadedDiscussions =
manuscript.threadedDiscussions ||
(await getThreadedDiscussionsForManuscript(manuscript, getUsersById, {
trx,
}))

// eslint-disable-next-line no-restricted-syntax
for (const review of reviews) {
const jsonData = JSON.parse(review.jsonData)
// eslint-disable-next-line no-restricted-syntax
for (const review of reviews) {
const jsonData = JSON.parse(review.jsonData)

if (review.isCollaborative) {
const collaborativeFormData =
// eslint-disable-next-line no-await-in-loop
await CollaborativeDoc.getFormData(review.id, reviewForm)
if (review.isCollaborative) {
const collaborativeFormData =
// eslint-disable-next-line no-await-in-loop
await CollaborativeDoc.getFormData(review.id, reviewForm, { trx })

review.jsonData = JSON.stringify({
...jsonData,
...collaborativeFormData,
})
review.jsonData = JSON.stringify({
...jsonData,
...collaborativeFormData,
})
}
}
}

return getPublishableReviewFields(
reviews,
reviewForm,
threadedDiscussions,
manuscript,
)
return getPublishableReviewFields(
reviews,
reviewForm,
threadedDiscussions,
manuscript,
)
})
}

const publishedReviewUsers = async review => {
if (review.isHiddenReviewerName) {
return [{ id: '', username: 'Anonymous User' }]
}

let users = []
return useTransaction(async trx => {
if (review.isHiddenReviewerName) {
return [{ id: '', username: 'Anonymous User' }]
}

if (review.isCollaborative) {
const manuscript = await Manuscript.query().findById(review.manuscriptId)
let users = []

const existingTeam = await manuscript
.$relatedQuery('teams')
.where('role', 'collaborativeReviewer')
.first()
if (review.isCollaborative) {
const manuscript = await Manuscript.query(trx).findById(
review.manuscriptId,
)

users = await existingTeam.$relatedQuery('users')
} else {
users = await User.query().where({ id: review.userId })
}
const existingTeam = await manuscript
.$relatedQuery('teams', trx)
.where('role', 'collaborativeReviewer')
.first()

users = await Promise.all(
users.map(async user => {
const defaultIdentity = await cachedGet(
`defaultIdentityOfUser:${user.id}`,
)
users = await existingTeam.$relatedQuery('users', trx)
} else {
users = await usersLoader([review.userId], { trx })
}

return { defaultIdentity, ...user }
}),
)
const defaultIdentities = await defaultIdentitiesLoader(
users.map(u => u.id),
{ trx },
)

return users
return users.map((user, i) => ({
...user,
defaultIdentity: defaultIdentities[i],
}))
})
}

// TODO: useTransaction to handle rollbacks
Expand Down
20 changes: 14 additions & 6 deletions packages/server/controllers/threadedDiscussion.controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ const { getUsersById, getUserRolesInManuscript } = require('./user.controllers')
const seekEvent = require('../services/notification.service')

/** Get the threaded discussion with "author" user object added to each commentVersion and pendingVersion */
const addUserObjectsToDiscussion = async (discussion, getUsersByIdFunc) => {
const addUserObjectsToDiscussion = async (
discussion,
getUsersByIdFunc,
options = {},
) => {
const { trx } = options
const userIds = getAllUserIdsInDiscussion(discussion)
const users = await getUsersByIdFunc(userIds)
const users = await getUsersByIdFunc(userIds, { trx })
const usersMap = {}
users.forEach(u => (usersMap[u.id] = u))

Expand Down Expand Up @@ -225,16 +230,19 @@ const getOriginalVersionManuscriptId = async manuscriptId => {
const getThreadedDiscussionsForManuscript = async (
manuscript,
getUsersByIdFunc,
) =>
Promise.all(
options = {},
) => {
const { trx } = options
return Promise.all(
(
await ThreadedDiscussion.query().where({
await ThreadedDiscussion.query(trx).where({
manuscriptId: manuscript.parentId || manuscript.id,
})
).map(discussion =>
addUserObjectsToDiscussion(discussion, getUsersByIdFunc),
addUserObjectsToDiscussion(discussion, getUsersByIdFunc, { trx }),
),
)
}

const isNewEmptyComment = (pendingVersion, commentVersions) =>
(!pendingVersion || pendingVersion.comment === '<p class="paragraph"></p>') &&
Expand Down
17 changes: 13 additions & 4 deletions packages/server/controllers/user.controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,17 @@ const getReciever = async (selectedEmail, externalName, trx) => {
* also 'shared' and have COMPLETED their review.
* If the current user isn't a 'shared' reviewer, return an empty array.
*/
const getSharedReviewersIds = async (manuscriptId, currentUserId) => {
const getSharedReviewersIds = async (
manuscriptId,
currentUserId,
options = {},
) => {
if (!currentUserId) return []

const reviewers = await Team.relatedQuery('members')
.for(Team.query().where({ objectId: manuscriptId, role: 'reviewer' }))
const { trx } = options

const reviewers = await Team.relatedQuery('members', trx)
.for(Team.query(trx).where({ objectId: manuscriptId, role: 'reviewer' }))
.select('userId')
.where({ isShared: true })
.where(builder =>
Expand Down Expand Up @@ -304,7 +310,10 @@ const getUsers = async groupId => {
.withGraphFetched('defaultIdentity')
}

const getUsersById = async userIds => User.query().findByIds(userIds)
const getUsersById = async (userIds, options = {}) => {
const { trx } = options
return User.query(trx).findByIds(userIds)
}

const isUserOnline = async user => {
const currentDateTime = new Date()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ class CollaborativeDoc extends BaseModel {
}
}

static async getFormData(objectId, form) {
const collaborativeDoc = await CollaborativeDoc.query().findOne({
static async getFormData(objectId, form, options = {}) {
const { trx } = options

const collaborativeDoc = await CollaborativeDoc.query(trx).findOne({
objectId,
})

Expand Down
Loading
Loading