Merge pull request #24433 from overleaf/em-pending-reviewers

Support reviewers in the collaborator limit enforcement logic

GitOrigin-RevId: f11a8e37ca6ef36f9894233803c6ee8363bf0ff8
This commit is contained in:
Eric Mc Sween
2025-03-27 14:16:48 +00:00
committed by Copybot
parent 373b222929
commit f46fd6f2d5
17 changed files with 268 additions and 63 deletions
@@ -992,6 +992,10 @@ class User {
updateOp = {
$addToSet: { readOnly_refs: user._id, pendingEditor_refs: user._id },
}
} else if (privileges === 'pendingReviewer') {
updateOp = {
$addToSet: { readOnly_refs: user._id, pendingReviewer_refs: user._id },
}
} else if (privileges === 'review') {
updateOp = {
$addToSet: { reviewer_refs: user._id },
@@ -18,6 +18,7 @@ describe('CollaboratorsGetter', function () {
this.readOnlyRef1 = new ObjectId()
this.readOnlyRef2 = new ObjectId()
this.pendingEditorRef = new ObjectId()
this.pendingReviewerRef = new ObjectId()
this.readWriteRef1 = new ObjectId()
this.readWriteRef2 = new ObjectId()
this.reviewer1Ref = new ObjectId()
@@ -32,8 +33,10 @@ describe('CollaboratorsGetter', function () {
this.readOnlyRef1,
this.readOnlyRef2,
this.pendingEditorRef,
this.pendingReviewerRef,
],
pendingEditor_refs: [this.pendingEditorRef],
pendingReviewer_refs: [this.pendingReviewerRef],
collaberator_refs: [this.readWriteRef1, this.readWriteRef2],
reviewer_refs: [this.reviewer1Ref, this.reviewer2Ref],
tokenAccessReadAndWrite_refs: [this.readWriteTokenRef],
@@ -114,6 +117,12 @@ describe('CollaboratorsGetter', function () {
source: 'invite',
pendingEditor: true,
},
{
id: this.pendingReviewerRef.toString(),
privilegeLevel: 'readOnly',
source: 'invite',
pendingReviewer: true,
},
{
id: this.readOnlyTokenRef.toString(),
privilegeLevel: 'readOnly',
@@ -165,6 +174,7 @@ describe('CollaboratorsGetter', function () {
this.readWriteRef1.toString(),
this.readWriteRef2.toString(),
this.pendingEditorRef.toString(),
this.pendingReviewerRef.toString(),
this.readWriteTokenRef.toString(),
this.readOnlyTokenRef.toString(),
this.reviewer1Ref.toString(),
@@ -186,6 +196,7 @@ describe('CollaboratorsGetter', function () {
this.readWriteRef1.toString(),
this.readWriteRef2.toString(),
this.pendingEditorRef.toString(),
this.pendingReviewerRef.toString(),
this.reviewer1Ref.toString(),
this.reviewer2Ref.toString(),
])
@@ -545,12 +556,12 @@ describe('CollaboratorsGetter', function () {
})
describe('getInvitedPendingEditorCount', function () {
it('should return the count of pending editors', async function () {
it('should return the count of pending editors and reviewers', async function () {
const count =
await this.CollaboratorsGetter.promises.getInvitedPendingEditorCount(
this.project._id
)
expect(count).to.equal(1)
expect(count).to.equal(2)
})
})
})
@@ -111,6 +111,7 @@ describe('CollaboratorsHandler', function () {
reviewer_refs: this.userId,
readOnly_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
tokenAccessReadOnly_refs: this.userId,
tokenAccessReadAndWrite_refs: this.userId,
archived: this.userId,
@@ -155,6 +156,7 @@ describe('CollaboratorsHandler', function () {
reviewer_refs: this.userId,
readOnly_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
tokenAccessReadOnly_refs: this.userId,
tokenAccessReadAndWrite_refs: this.userId,
trashed: this.userId,
@@ -191,6 +193,7 @@ describe('CollaboratorsHandler', function () {
reviewer_refs: this.userId,
readOnly_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
tokenAccessReadOnly_refs: this.userId,
tokenAccessReadAndWrite_refs: this.userId,
archived: this.userId,
@@ -278,6 +281,32 @@ describe('CollaboratorsHandler', function () {
)
})
})
describe('with pendingReviewer flag', function () {
it('should add them to the pending reviewer refs', async function () {
this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.project._id,
},
{
$addToSet: {
readOnly_refs: this.userId,
pendingReviewer_refs: this.userId,
},
}
)
.chain('exec')
.resolves()
await this.CollaboratorsHandler.promises.addUserIdToProject(
this.project._id,
this.addingUserId,
this.userId,
'readOnly',
{ pendingReviewer: true }
)
})
})
})
describe('as readAndWrite', function () {
@@ -451,6 +480,7 @@ describe('CollaboratorsHandler', function () {
reviewer_refs: this.userId,
readOnly_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
tokenAccessReadOnly_refs: this.userId,
tokenAccessReadAndWrite_refs: this.userId,
archived: this.userId,
@@ -549,6 +579,24 @@ describe('CollaboratorsHandler', function () {
)
.chain('exec')
.resolves()
this.ProjectMock.expects('updateMany')
.withArgs(
{ pendingReviewer_refs: this.fromUserId },
{
$addToSet: { pendingReviewer_refs: this.toUserId },
}
)
.chain('exec')
.resolves()
this.ProjectMock.expects('updateMany')
.withArgs(
{ pendingReviewer_refs: this.fromUserId },
{
$pull: { pendingReviewer_refs: this.fromUserId },
}
)
.chain('exec')
.resolves()
})
describe('successfully', function () {
@@ -586,7 +634,7 @@ describe('CollaboratorsHandler', function () {
this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.projectId,
_id: this.project._id,
$or: [
{ collaberator_refs: this.userId },
{ readOnly_refs: this.userId },
@@ -597,6 +645,7 @@ describe('CollaboratorsHandler', function () {
$pull: {
collaberator_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
reviewer_refs: this.userId,
},
$addToSet: { readOnly_refs: this.userId },
@@ -605,7 +654,7 @@ describe('CollaboratorsHandler', function () {
.chain('exec')
.resolves({ matchedCount: 1 })
await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
this.projectId,
this.project._id,
this.userId,
'readOnly'
)
@@ -615,7 +664,7 @@ describe('CollaboratorsHandler', function () {
this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.projectId,
_id: this.project._id,
$or: [
{ collaberator_refs: this.userId },
{ readOnly_refs: this.userId },
@@ -628,13 +677,14 @@ describe('CollaboratorsHandler', function () {
readOnly_refs: this.userId,
reviewer_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
},
}
)
.chain('exec')
.resolves({ matchedCount: 1 })
await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
this.projectId,
this.project._id,
this.userId,
'readAndWrite'
)
@@ -653,7 +703,7 @@ describe('CollaboratorsHandler', function () {
this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.projectId,
_id: this.project._id,
$or: [
{ collaberator_refs: this.userId },
{ readOnly_refs: this.userId },
@@ -667,13 +717,14 @@ describe('CollaboratorsHandler', function () {
readOnly_refs: this.userId,
collaberator_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
},
}
)
.chain('exec')
.resolves({ matchedCount: 1 })
await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
this.projectId,
this.project._id,
this.userId,
'review'
)
@@ -695,7 +746,7 @@ describe('CollaboratorsHandler', function () {
this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.projectId,
_id: this.project._id,
$or: [
{ collaberator_refs: this.userId },
{ readOnly_refs: this.userId },
@@ -709,13 +760,14 @@ describe('CollaboratorsHandler', function () {
readOnly_refs: this.userId,
collaberator_refs: this.userId,
pendingEditor_refs: this.userId,
pendingReviewer_refs: this.userId,
},
}
)
.chain('exec')
.resolves({ matchedCount: 1 })
await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
this.projectId,
this.project._id,
this.userId,
'review'
)
@@ -726,7 +778,7 @@ describe('CollaboratorsHandler', function () {
this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.projectId,
_id: this.project._id,
$or: [
{ collaberator_refs: this.userId },
{ readOnly_refs: this.userId },
@@ -741,26 +793,60 @@ describe('CollaboratorsHandler', function () {
$pull: {
collaberator_refs: this.userId,
reviewer_refs: this.userId,
pendingReviewer_refs: this.userId,
},
}
)
.chain('exec')
.resolves({ matchedCount: 1 })
await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
this.projectId,
this.project._id,
this.userId,
'readOnly',
{ pendingEditor: true }
)
})
it('sets a collaborator to read-only as a pendingReviewer', async function () {
this.ProjectMock.expects('updateOne')
.withArgs(
{
_id: this.project._id,
$or: [
{ collaberator_refs: this.userId },
{ readOnly_refs: this.userId },
{ reviewer_refs: this.userId },
],
},
{
$addToSet: {
readOnly_refs: this.userId,
pendingReviewer_refs: this.userId,
},
$pull: {
collaberator_refs: this.userId,
reviewer_refs: this.userId,
pendingEditor_refs: this.userId,
},
}
)
.chain('exec')
.resolves({ matchedCount: 1 })
await this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
this.project._id,
this.userId,
'readOnly',
{ pendingReviewer: true }
)
})
it('throws a NotFoundError if the project or collaborator does not exist', async function () {
this.ProjectMock.expects('updateOne')
.chain('exec')
.resolves({ matchedCount: 0 })
await expect(
this.CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
this.projectId,
this.project._id,
this.userId,
'readAndWrite'
)
@@ -561,7 +561,7 @@ describe('CollaboratorsInviteHandler', function () {
'editor-moved-to-pending',
null,
null,
{ userId: this.userId.toString() }
{ userId: this.userId.toString(), role: 'editor' }
)
this.CollaboratorsHandler.promises.addUserIdToProject.should.have.been.calledWith(
this.projectId,