[web] Refactor ProjectController.loadEditor p-props promises (#18823)

* Move unawaited promises out of `p-props`: `User.updateOne`

* Move unawaited promises out of `p-props`: `sessionMaintenance`

* Move unawaited promises out of `p-props`: `reactivateProjectIfRequired`

* Move unawaited promises out of `p-props`: `markAsOpened`

* Move unawaited promises out of `p-props`: `flushProjectToTpdsIfNeeded`

This was actually awaited, but is it necessary?

* Use `getUserValues`: `user`

* Use `getUserValues`: `learnedWords`

* Use `getUserValues`: `projectTags`

* Use `getUserValues`: `userHasInstitutionLicence`

* Use `getUserValues`: `subscription`

* Use `getUserValues`: `isTokenMember`

* Use `getUserValues`: `isInvitedMember`

* Update `splitTestAssignments` syntax

* Remove `mapValues` in `pProps`

* Await unused promises again: `flushProjectToTpdsIfNeeded` and `reactivateProjectIfRequired`

* Remove error page file committed by mistake 🤦

GitOrigin-RevId: 6529445c364a6af660bc8ab6dfc836a4ddf7a040
This commit is contained in:
Antoine Clausse 2024-06-21 09:22:52 +02:00 committed by Copybot
parent 271700893a
commit 2c7df10b9a

View file

@ -317,194 +317,111 @@ const _ProjectController = {
}
}
try {
const splitTests = [
!anonymous && 'bib-file-tpr-prompt',
'compile-log-events',
'null-test-share-modal',
'paywall-cta',
'pdf-caching-cached-url-lookup',
'pdf-caching-mode',
'pdf-caching-prefetch-large',
'pdf-caching-prefetching',
'pdf-controls',
'pdfjs-40',
'personal-access-token',
'revert-file',
'table-generator-promotion',
'track-pdf-download',
!anonymous && 'writefull-oauth-promotion',
'ieee-stylesheet',
].filter(Boolean)
const splitTests = [
!anonymous && 'bib-file-tpr-prompt',
'compile-log-events',
'null-test-share-modal',
'paywall-cta',
'pdf-caching-cached-url-lookup',
'pdf-caching-mode',
'pdf-caching-prefetch-large',
'pdf-caching-prefetching',
'pdf-controls',
'pdfjs-40',
'personal-access-token',
'revert-file',
'table-generator-promotion',
'track-pdf-download',
!anonymous && 'writefull-oauth-promotion',
'ieee-stylesheet',
].filter(Boolean)
const responses = await pProps(
_.mapValues(
{
splitTestAssignments: async () => {
const assignments = {}
await Promise.all(
splitTests.map(async splitTest => {
assignments[splitTest] =
await SplitTestHandler.promises.getAssignment(
req,
res,
splitTest
)
})
)
return assignments
},
project: () =>
ProjectGetter.promises.getProject(projectId, {
name: 1,
lastUpdated: 1,
track_changes: 1,
owner_ref: 1,
brandVariationId: 1,
overleaf: 1,
tokens: 1,
}),
user: async () => {
if (!userId) {
SplitTestSessionHandler.promises
.sessionMaintenance(req, null)
.catch(err => {
logger.error(
{ err },
'failed to update split test info in session'
)
})
return defaultSettingsForAnonymousUser(userId)
} else {
User.updateOne(
{ _id: new ObjectId(userId) },
{ $set: { lastActive: new Date() } }
)
.exec()
.catch(err => {
logger.error(
{ err, userId },
'failed to update lastActive for user'
)
})
const user = await User.findById(
userId,
'email first_name last_name referal_id signUpDate featureSwitches features featuresEpoch refProviders alphaProgram betaProgram isAdmin ace labsProgram completedTutorials writefull'
).exec()
// Handle case of deleted user
if (!user) {
UserController.logout(req, res, next)
return
}
logger.debug({ projectId, userId }, 'got user')
SplitTestSessionHandler.promises
.sessionMaintenance(req, user)
.catch(err => {
logger.error(
{ err },
'failed to update split test info in session'
)
})
if (FeaturesUpdater.featuresEpochIsCurrent(user)) {
return user
}
return await ProjectController._refreshFeatures(req, user)
}
},
userHasInstitutionLicence: async () => {
if (!userId) {
return false
}
try {
return await InstitutionsFeatures.promises.hasLicence(userId)
} catch {
// Don't fail if we can't get affiliation licences
return false
}
},
learnedWords() {
if (!userId) {
return []
}
return SpellingHandler.promises.getUserDictionary(userId)
},
subscription() {
if (!userId) {
return
}
return SubscriptionLocator.promises.getUsersSubscription(userId)
},
userIsMemberOfGroupSubscription() {
if (!sessionUser) {
return false
}
return LimitationsManager.promises.userIsMemberOfGroupSubscription(
sessionUser
)
},
activate() {
return InactiveProjectManager.promises.reactivateProjectIfRequired(
projectId
)
},
markAsOpened() {
// don't need to wait for this to complete
ProjectUpdateHandler.promises
.markAsOpened(projectId)
.catch(err => {
logger.error(
{ err, projectId },
'failed to mark project as opened'
)
})
},
isTokenMember() {
if (!userId) {
return
}
return CollaboratorsGetter.promises.userIsTokenMember(
userId,
projectId
)
},
isInvitedMember() {
return CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
userId,
projectId
)
},
flushToTpds: () => {
return TpdsProjectFlusher.promises.flushProjectToTpdsIfNeeded(
projectId
)
},
projectTags() {
if (!userId) {
return []
}
return TagsHandler.promises.getTagsForProject(userId, projectId)
},
},
promise => promise()
)
const getUserValues = async userId =>
pProps(
_.mapValues({
user: (async () => {
const user = await User.findById(
userId,
'email first_name last_name referal_id signUpDate featureSwitches features featuresEpoch refProviders alphaProgram betaProgram isAdmin ace labsProgram completedTutorials writefull'
).exec()
// Handle case of deleted user
if (!user) {
UserController.logout(req, res, next)
return
}
logger.debug({ projectId, userId }, 'got user')
return FeaturesUpdater.featuresEpochIsCurrent(user)
? user
: await ProjectController._refreshFeatures(req, user)
})(),
learnedWords: SpellingHandler.promises.getUserDictionary(userId),
projectTags: TagsHandler.promises.getTagsForProject(
userId,
projectId
),
userHasInstitutionLicence: InstitutionsFeatures.promises
.hasLicence(userId)
.catch(err => {
logger.error({ err, userId }, 'failed to get institution licence')
return false
}),
subscription:
SubscriptionLocator.promises.getUsersSubscription(userId),
isTokenMember: CollaboratorsGetter.promises.userIsTokenMember(
userId,
projectId
),
isInvitedMember:
CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
userId,
projectId
),
})
)
const splitTestAssignments = {}
try {
const responses = await pProps({
userValues: userId ? getUserValues(userId) : defaultUserValues(),
splitTestAssignments: Promise.all(
splitTests.map(async splitTest => {
splitTestAssignments[splitTest] =
await SplitTestHandler.promises.getAssignment(req, res, splitTest)
})
),
project: ProjectGetter.promises.getProject(projectId, {
name: 1,
lastUpdated: 1,
track_changes: 1,
owner_ref: 1,
brandVariationId: 1,
overleaf: 1,
tokens: 1,
}),
userIsMemberOfGroupSubscription: sessionUser
? LimitationsManager.promises.userIsMemberOfGroupSubscription(
sessionUser
)
: false,
_flushToTpds:
TpdsProjectFlusher.promises.flushProjectToTpdsIfNeeded(projectId),
_activate:
InactiveProjectManager.promises.reactivateProjectIfRequired(
projectId
),
})
const { project, userValues, userIsMemberOfGroupSubscription } = responses
const {
project,
user,
userHasInstitutionLicence,
learnedWords,
projectTags,
userHasInstitutionLicence,
subscription,
userIsMemberOfGroupSubscription,
isTokenMember,
isInvitedMember,
splitTestAssignments,
projectTags,
} = responses
} = userValues
const brandVariation = project?.brandVariationId
? await BrandVariationsHandler.promises.getBrandVariationById(
@ -560,6 +477,17 @@ const _ProjectController = {
}
metrics.inc(metricName)
// don't need to wait for these to complete
ProjectUpdateHandler.promises
.markAsOpened(projectId)
.catch(err =>
logger.error({ err, projectId }, 'failed to mark project as opened')
)
SplitTestSessionHandler.promises
.sessionMaintenance(req, userId ? user : null)
.catch(err =>
logger.error({ err }, 'failed to update split test info in session')
)
if (userId) {
AnalyticsManager.recordEventForUserInBackground(
userId,
@ -568,6 +496,17 @@ const _ProjectController = {
projectId: project._id,
}
)
User.updateOne(
{ _id: new ObjectId(userId) },
{ $set: { lastActive: new Date() } }
)
.exec()
.catch(err =>
logger.error(
{ err, userId },
'failed to update lastActive for user'
)
)
}
const isAdminOrTemplateOwner =
@ -753,7 +692,6 @@ const _ProjectController = {
})(),
])
},
_buildProjectList(allProjects, userId) {
let project
const { owned, readAndWrite, readOnly, tokenReadAndWrite, tokenReadOnly } =
@ -826,7 +764,6 @@ const _ProjectController = {
return projects
},
_buildProjectViewModel(project, accessLevel, source, userId) {
const archived = ProjectHelper.isArchived(project, userId)
// If a project is simultaneously trashed and archived, we will consider it archived but not trashed.
@ -851,7 +788,6 @@ const _ProjectController = {
}
return model
},
_buildPortalTemplatesList(affiliations) {
if (affiliations == null) {
affiliations = []
@ -901,6 +837,16 @@ const defaultSettingsForAnonymousUser = userId => ({
},
})
const defaultUserValues = () => ({
user: defaultSettingsForAnonymousUser(null),
learnedWords: [],
projectTags: [],
userHasInstitutionLicence: false,
subscription: undefined,
isTokenMember: false,
isInvitedMember: false,
})
const THEME_LIST = [
'cobalt',
'dracula',