Просмотр исходного кода

Merge pull request #14989 from overleaf/em-promisify-collaborators-invite-controller

Promisify CollaboratorsInviteController

GitOrigin-RevId: 007b77823a9332464123c11da55dda8571803095
Eric Mc Sween 3 лет назад
Родитель
Сommit
0f473d9a9f

+ 202 - 288
services/web/app/src/Features/Collaborators/CollaboratorsInviteController.js

@@ -1,5 +1,4 @@
-let CollaboratorsInviteController
-const OError = require('@overleaf/o-error')
+const { callbackify } = require('util')
 const ProjectGetter = require('../Project/ProjectGetter')
 const LimitationsManager = require('../Subscription/LimitationsManager')
 const UserGetter = require('../User/UserGetter')
@@ -12,6 +11,7 @@ const EditorRealTimeController = require('../Editor/EditorRealTimeController')
 const AnalyticsManager = require('../Analytics/AnalyticsManager')
 const SessionManager = require('../Authentication/SessionManager')
 const { RateLimiter } = require('../../infrastructure/RateLimiter')
+const { expressify } = require('../../util/promises')
 
 // This rate limiter allows a different number of requests depending on the
 // number of callaborators a user is allowed. This is implemented by providing
@@ -28,76 +28,62 @@ const rateLimiter = new RateLimiter('invite-to-project-by-user-id', {
   duration: 60 * 30,
 })
 
-module.exports = CollaboratorsInviteController = {
-  getAllInvites(req, res, next) {
+const CollaboratorsInviteController = {
+  async getAllInvites(req, res) {
     const projectId = req.params.Project_id
     logger.debug({ projectId }, 'getting all active invites for project')
-    CollaboratorsInviteHandler.getAllInvites(
-      projectId,
-      function (err, invites) {
-        if (err) {
-          OError.tag(err, 'error getting invites for project', {
-            projectId,
-          })
-          return next(err)
-        }
-        res.json({ invites })
-      }
+    const invites = await CollaboratorsInviteHandler.promises.getAllInvites(
+      projectId
     )
+    res.json({ invites })
   },
 
-  _checkShouldInviteEmail(email, callback) {
+  async _checkShouldInviteEmail(email) {
     if (Settings.restrictInvitesToExistingAccounts === true) {
       logger.debug({ email }, 'checking if user exists with this email')
-      UserGetter.getUserByAnyEmail(email, { _id: 1 }, function (err, user) {
-        if (err) {
-          return callback(err)
-        }
-        const userExists = user?._id != null
-        callback(null, userExists)
+      const user = await UserGetter.promises.getUserByAnyEmail(email, {
+        _id: 1,
       })
+      const userExists = user?._id != null
+      return userExists
     } else {
-      callback(null, true)
+      return true
     }
   },
 
-  _checkRateLimit(userId, callback) {
-    LimitationsManager.allowedNumberOfCollaboratorsForUser(
-      userId,
-      (err, collabLimit) => {
-        if (err) {
-          return callback(err)
-        }
-        if (collabLimit == null || collabLimit === 0) {
-          collabLimit = 1
-        } else if (collabLimit < 0 || collabLimit > 20) {
-          collabLimit = 20
-        }
-
-        // Consume enough points to hit the rate limit at 10 * collabLimit
-        const maxRequests = 10 * collabLimit
-        const points = Math.floor(RATE_LIMIT_POINTS / maxRequests)
-        rateLimiter
-          .consume(userId, points)
-          .then(() => {
-            callback(null, true)
-          })
-          .catch(err => {
-            if (err instanceof Error) {
-              callback(err)
-            } else {
-              callback(null, false)
-            }
-          })
+  async _checkRateLimit(userId) {
+    let collabLimit =
+      await LimitationsManager.promises.allowedNumberOfCollaboratorsForUser(
+        userId
+      )
+
+    if (collabLimit == null || collabLimit === 0) {
+      collabLimit = 1
+    } else if (collabLimit < 0 || collabLimit > 20) {
+      collabLimit = 20
+    }
+
+    // Consume enough points to hit the rate limit at 10 * collabLimit
+    const maxRequests = 10 * collabLimit
+    const points = Math.floor(RATE_LIMIT_POINTS / maxRequests)
+    try {
+      await rateLimiter.consume(userId, points)
+    } catch (err) {
+      if (err instanceof Error) {
+        throw err
+      } else {
+        return false
       }
-    )
+    }
+    return true
   },
 
-  inviteToProject(req, res, next) {
+  async inviteToProject(req, res) {
     const projectId = req.params.Project_id
-    let { email } = req.body
+    let { email, privileges } = req.body
     const sendingUser = SessionManager.getSessionUser(req.session)
     const sendingUserId = sendingUser._id
+
     if (email === sendingUser.email) {
       logger.debug(
         { projectId, email, sendingUserId },
@@ -105,243 +91,163 @@ module.exports = CollaboratorsInviteController = {
       )
       return res.json({ invite: null, error: 'cannot_invite_self' })
     }
+
     logger.debug({ projectId, email, sendingUserId }, 'inviting to project')
-    LimitationsManager.canAddXCollaborators(projectId, 1, (error, allowed) => {
-      let privileges
-      if (error) {
-        return next(error)
-      }
-      if (!allowed) {
-        logger.debug(
-          { projectId, email, sendingUserId },
-          'not allowed to invite more users to project'
-        )
-        return res.json({ invite: null })
-      }
-      ;({ email, privileges } = req.body)
-      email = EmailHelper.parseEmail(email, true)
-      if (email == null || email === '') {
-        logger.debug(
-          { projectId, email, sendingUserId },
-          'invalid email address'
-        )
-        return res.status(400).json({ errorReason: 'invalid_email' })
-      }
-      CollaboratorsInviteController._checkRateLimit(
-        sendingUserId,
-        function (error, underRateLimit) {
-          if (error) {
-            return next(error)
-          }
-          if (!underRateLimit) {
-            return res.sendStatus(429)
-          }
-          CollaboratorsInviteController._checkShouldInviteEmail(
-            email,
-            function (err, shouldAllowInvite) {
-              if (err) {
-                OError.tag(
-                  err,
-                  'error checking if we can invite this email address',
-                  {
-                    email,
-                    projectId,
-                    sendingUserId,
-                  }
-                )
-                return next(err)
-              }
-              if (!shouldAllowInvite) {
-                logger.debug(
-                  { email, projectId, sendingUserId },
-                  'not allowed to send an invite to this email address'
-                )
-                return res.json({
-                  invite: null,
-                  error: 'cannot_invite_non_user',
-                })
-              }
-              CollaboratorsInviteHandler.inviteToProject(
-                projectId,
-                sendingUser,
-                email,
-                privileges,
-                function (err, invite) {
-                  if (err) {
-                    OError.tag(err, 'error creating project invite', {
-                      projectId,
-                      email,
-                      sendingUserId,
-                    })
-                    return next(err)
-                  }
-                  logger.debug(
-                    { projectId, email, sendingUserId },
-                    'invite created'
-                  )
-                  EditorRealTimeController.emitToRoom(
-                    projectId,
-                    'project:membership:changed',
-                    { invites: true }
-                  )
-                  res.json({ invite })
-                }
-              )
-            }
-          )
-        }
+
+    const allowed = await LimitationsManager.promises.canAddXCollaborators(
+      projectId,
+      1
+    )
+    if (!allowed) {
+      logger.debug(
+        { projectId, email, sendingUserId },
+        'not allowed to invite more users to project'
       )
-    })
+      return res.json({ invite: null })
+    }
+
+    email = EmailHelper.parseEmail(email, true)
+    if (email == null || email === '') {
+      logger.debug({ projectId, email, sendingUserId }, 'invalid email address')
+      return res.status(400).json({ errorReason: 'invalid_email' })
+    }
+
+    const underRateLimit = await CollaboratorsInviteController._checkRateLimit(
+      sendingUserId
+    )
+    if (!underRateLimit) {
+      return res.sendStatus(429)
+    }
+
+    const shouldAllowInvite =
+      await CollaboratorsInviteController._checkShouldInviteEmail(email)
+    if (!shouldAllowInvite) {
+      logger.debug(
+        { email, projectId, sendingUserId },
+        'not allowed to send an invite to this email address'
+      )
+      return res.json({
+        invite: null,
+        error: 'cannot_invite_non_user',
+      })
+    }
+
+    const invite = await CollaboratorsInviteHandler.promises.inviteToProject(
+      projectId,
+      sendingUser,
+      email,
+      privileges
+    )
+    logger.debug({ projectId, email, sendingUserId }, 'invite created')
+    EditorRealTimeController.emitToRoom(
+      projectId,
+      'project:membership:changed',
+      { invites: true }
+    )
+    res.json({ invite })
   },
 
-  revokeInvite(req, res, next) {
+  async revokeInvite(req, res) {
     const projectId = req.params.Project_id
     const inviteId = req.params.invite_id
+
     logger.debug({ projectId, inviteId }, 'revoking invite')
-    CollaboratorsInviteHandler.revokeInvite(
+    await CollaboratorsInviteHandler.promises.revokeInvite(projectId, inviteId)
+    EditorRealTimeController.emitToRoom(
       projectId,
-      inviteId,
-      function (err) {
-        if (err) {
-          OError.tag(err, 'error revoking invite', {
-            projectId,
-            inviteId,
-          })
-          return next(err)
-        }
-        EditorRealTimeController.emitToRoom(
-          projectId,
-          'project:membership:changed',
-          { invites: true }
-        )
-        res.sendStatus(201)
-      }
+      'project:membership:changed',
+      { invites: true }
     )
+
+    res.sendStatus(201)
   },
 
-  resendInvite(req, res, next) {
+  async resendInvite(req, res) {
     const projectId = req.params.Project_id
     const inviteId = req.params.invite_id
+
     logger.debug({ projectId, inviteId }, 'resending invite')
     const sendingUser = SessionManager.getSessionUser(req.session)
-    CollaboratorsInviteController._checkRateLimit(
-      sendingUser._id,
-      function (error, underRateLimit) {
-        if (error) {
-          return next(error)
-        }
-        if (!underRateLimit) {
-          return res.sendStatus(429)
-        }
-        CollaboratorsInviteHandler.resendInvite(
-          projectId,
-          sendingUser,
-          inviteId,
-          function (err) {
-            if (err) {
-              OError.tag(err, 'error resending invite', {
-                projectId,
-                inviteId,
-              })
-              return next(err)
-            }
-            res.sendStatus(201)
-          }
-        )
-      }
+    const underRateLimit = await CollaboratorsInviteController._checkRateLimit(
+      sendingUser._id
+    )
+    if (!underRateLimit) {
+      return res.sendStatus(429)
+    }
+
+    await CollaboratorsInviteHandler.promises.resendInvite(
+      projectId,
+      sendingUser,
+      inviteId
     )
+
+    res.sendStatus(201)
   },
 
-  viewInvite(req, res, next) {
+  async viewInvite(req, res) {
     const projectId = req.params.Project_id
     const { token } = req.params
     const _renderInvalidPage = function () {
       logger.debug({ projectId }, 'invite not valid, rendering not-valid page')
       res.render('project/invite/not-valid', { title: 'Invalid Invite' })
     }
+
     // check if the user is already a member of the project
     const currentUser = SessionManager.getSessionUser(req.session)
-    CollaboratorsGetter.isUserInvitedMemberOfProject(
-      currentUser._id,
+    const isMember =
+      await CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
+        currentUser._id,
+        projectId
+      )
+    if (isMember) {
+      logger.debug(
+        { projectId, userId: currentUser._id },
+        'user is already a member of this project, redirecting'
+      )
+      return res.redirect(`/project/${projectId}`)
+    }
+
+    // get the invite
+    const invite = await CollaboratorsInviteHandler.promises.getInviteByToken(
       projectId,
-      function (err, isMember) {
-        if (err) {
-          OError.tag(err, 'error checking if user is member of project', {
-            projectId,
-          })
-          return next(err)
-        }
-        if (isMember) {
-          logger.debug(
-            { projectId, userId: currentUser._id },
-            'user is already a member of this project, redirecting'
-          )
-          return res.redirect(`/project/${projectId}`)
-        }
-        // get the invite
-        CollaboratorsInviteHandler.getInviteByToken(
-          projectId,
-          token,
-          function (err, invite) {
-            if (err) {
-              OError.tag(err, 'error getting invite by token', {
-                projectId,
-              })
-              return next(err)
-            }
-            // check if invite is gone, or otherwise non-existent
-            if (invite == null) {
-              logger.debug({ projectId }, 'no invite found for this token')
-              return _renderInvalidPage()
-            }
-            // check the user who sent the invite exists
-            UserGetter.getUser(
-              { _id: invite.sendingUserId },
-              { email: 1, first_name: 1, last_name: 1 },
-              function (err, owner) {
-                if (err) {
-                  OError.tag(err, 'error getting project owner', {
-                    projectId,
-                  })
-                  return next(err)
-                }
-                if (owner == null) {
-                  logger.debug({ projectId }, 'no project owner found')
-                  return _renderInvalidPage()
-                }
-                // fetch the project name
-                ProjectGetter.getProject(
-                  projectId,
-                  {},
-                  function (err, project) {
-                    if (err) {
-                      OError.tag(err, 'error getting project', {
-                        projectId,
-                      })
-                      return next(err)
-                    }
-                    if (project == null) {
-                      logger.debug({ projectId }, 'no project found')
-                      return _renderInvalidPage()
-                    }
-                    // finally render the invite
-                    res.render('project/invite/show', {
-                      invite,
-                      project,
-                      owner,
-                      title: 'Project Invite',
-                    })
-                  }
-                )
-              }
-            )
-          }
-        )
-      }
+      token
     )
+
+    // check if invite is gone, or otherwise non-existent
+    if (invite == null) {
+      logger.debug({ projectId }, 'no invite found for this token')
+      return _renderInvalidPage()
+    }
+
+    // check the user who sent the invite exists
+    const owner = await UserGetter.promises.getUser(
+      { _id: invite.sendingUserId },
+      { email: 1, first_name: 1, last_name: 1 }
+    )
+    if (owner == null) {
+      logger.debug({ projectId }, 'no project owner found')
+      return _renderInvalidPage()
+    }
+
+    // fetch the project name
+    const project = await ProjectGetter.promises.getProject(projectId, {
+      name: 1,
+    })
+    if (project == null) {
+      logger.debug({ projectId }, 'no project found')
+      return _renderInvalidPage()
+    }
+
+    // finally render the invite
+    res.render('project/invite/show', {
+      invite,
+      project,
+      owner,
+      title: 'Project Invite',
+    })
   },
 
-  acceptInvite(req, res, next) {
+  async acceptInvite(req, res) {
     const projectId = req.params.Project_id
     const { token } = req.params
     const currentUser = SessionManager.getSessionUser(req.session)
@@ -349,36 +255,44 @@ module.exports = CollaboratorsInviteController = {
       { projectId, userId: currentUser._id },
       'got request to accept invite'
     )
-    CollaboratorsInviteHandler.acceptInvite(
+
+    await CollaboratorsInviteHandler.promises.acceptInvite(
       projectId,
       token,
-      currentUser,
-      function (err) {
-        if (err) {
-          OError.tag(err, 'error accepting invite by token', {
-            projectId,
-            token,
-          })
-          return next(err)
-        }
-        EditorRealTimeController.emitToRoom(
-          projectId,
-          'project:membership:changed',
-          { invites: true, members: true }
-        )
-        AnalyticsManager.recordEventForUser(
-          currentUser._id,
-          'project-invite-accept',
-          {
-            projectId,
-          }
-        )
-        if (req.xhr) {
-          res.sendStatus(204) //  Done async via project page notification
-        } else {
-          res.redirect(`/project/${projectId}`)
-        }
+      currentUser
+    )
+
+    EditorRealTimeController.emitToRoom(
+      projectId,
+      'project:membership:changed',
+      { invites: true, members: true }
+    )
+    AnalyticsManager.recordEventForUser(
+      currentUser._id,
+      'project-invite-accept',
+      {
+        projectId,
       }
     )
+
+    if (req.xhr) {
+      res.sendStatus(204) //  Done async via project page notification
+    } else {
+      res.redirect(`/project/${projectId}`)
+    }
   },
 }
+
+module.exports = {
+  promises: CollaboratorsInviteController,
+  getAllInvites: expressify(CollaboratorsInviteController.getAllInvites),
+  inviteToProject: expressify(CollaboratorsInviteController.inviteToProject),
+  revokeInvite: expressify(CollaboratorsInviteController.revokeInvite),
+  resendInvite: expressify(CollaboratorsInviteController.resendInvite),
+  viewInvite: expressify(CollaboratorsInviteController.viewInvite),
+  acceptInvite: expressify(CollaboratorsInviteController.acceptInvite),
+  _checkShouldInviteEmail: callbackify(
+    CollaboratorsInviteController._checkShouldInviteEmail
+  ),
+  _checkRateLimit: callbackify(CollaboratorsInviteController._checkRateLimit),
+}

Разница между файлами не показана из-за своего большого размера
+ 247 - 256
services/web/test/unit/src/Collaborators/CollaboratorsInviteControllerTests.js


Некоторые файлы не были показаны из-за большого количества измененных файлов