From e4cc1b1403f1221ed47297a0ba344a1d361da37e Mon Sep 17 00:00:00 2001 From: Jessica Lawshe Date: Tue, 7 Jan 2020 09:24:03 -0600 Subject: [PATCH] Merge pull request #2496 from overleaf/cmg-clear-leaving-archived Clear archived/trashed status when leaving a project GitOrigin-RevId: ca7026bb7848fd7b17c13856bc65b28e577a8236 --- .../Collaborators/CollaboratorsHandler.js | 51 ++++-- .../CollaboratorsHandlerTests.js | 153 +++++++++++++++--- 2 files changed, 172 insertions(+), 32 deletions(-) diff --git a/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js b/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js index 219b30a946..f013ce75f3 100644 --- a/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js +++ b/services/web/app/src/Features/Collaborators/CollaboratorsHandler.js @@ -2,6 +2,7 @@ const { callbackify } = require('util') const OError = require('@overleaf/o-error') const { Project } = require('../../models/Project') const ProjectGetter = require('../Project/ProjectGetter') +const ProjectHelper = require('../Project/ProjectHelper') const logger = require('logger-sharelatex') const ContactManager = require('../Contacts/ContactManager') const PrivilegeLevels = require('../Authorization/PrivilegeLevels') @@ -27,17 +28,47 @@ module.exports = { async function removeUserFromProject(projectId, userId) { try { - await Project.update( - { _id: projectId }, - { - $pull: { - collaberator_refs: userId, - readOnly_refs: userId, - tokenAccessReadOnly_refs: userId, - tokenAccessReadAndWrite_refs: userId + const project = await Project.findOne({ _id: projectId }).exec() + + // Deal with the old type of boolean value for archived + // In order to clear it + if (typeof project.archived === 'boolean') { + let archived = ProjectHelper.calculateArchivedArray( + project, + userId, + 'ARCHIVE' + ) + + archived = archived.filter(id => id.toString() !== userId.toString()) + + await Project.update( + { _id: projectId }, + { + $set: { archived: archived }, + $pull: { + collaberator_refs: userId, + readOnly_refs: userId, + tokenAccessReadOnly_refs: userId, + tokenAccessReadAndWrite_refs: userId, + trashed: userId + } } - } - ).exec() + ) + } else { + await Project.update( + { _id: projectId }, + { + $pull: { + collaberator_refs: userId, + readOnly_refs: userId, + tokenAccessReadOnly_refs: userId, + tokenAccessReadAndWrite_refs: userId, + archived: userId, + trashed: userId + } + } + ) + } } catch (err) { throw new OError({ message: 'problem removing user from project collaborators', diff --git a/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js b/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js index 9839416ebe..f06cac54a9 100644 --- a/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js +++ b/services/web/test/unit/src/Collaborators/CollaboratorsHandlerTests.js @@ -27,6 +27,16 @@ describe('CollaboratorsHandler', function() { _id: ObjectId() } + this.archivedProject = { + _id: ObjectId(), + archived: [ObjectId(this.userId)] + } + + this.oldArchivedProject = { + _id: ObjectId(), + archived: true + } + this.UserGetter = { promises: { getUser: sinon.stub().resolves(null) @@ -46,6 +56,10 @@ describe('CollaboratorsHandler', function() { getProject: sinon.stub().resolves(this.project) } } + + this.ProjectHelper = { + calculateArchivedArray: sinon.stub() + } this.CollaboratorsGetter = { promises: { getProjectsUserIsMemberOf: sinon.stub() @@ -62,6 +76,7 @@ describe('CollaboratorsHandler', function() { '../../models/Project': { Project }, '../ThirdPartyDataStore/TpdsProjectFlusher': this.TpdsProjectFlusher, '../Project/ProjectGetter': this.ProjectGetter, + '../Project/ProjectHelper': this.ProjectHelper, '../Errors/Errors': Errors, './CollaboratorsGetter': this.CollaboratorsGetter } @@ -73,29 +88,115 @@ describe('CollaboratorsHandler', function() { }) describe('removeUserFromProject', function() { - beforeEach(function() {}) - - it('should remove the user from mongo', async function() { - this.ProjectMock.expects('update') - .withArgs( - { + describe('a non-archived project', function() { + beforeEach(function() { + this.ProjectMock.expects('findOne') + .withArgs({ _id: this.project._id - }, - { - $pull: { - collaberator_refs: this.userId, - readOnly_refs: this.userId, - tokenAccessReadOnly_refs: this.userId, - tokenAccessReadAndWrite_refs: this.userId + }) + .chain('exec') + .resolves(this.project) + }) + + it('should remove the user from mongo', async function() { + this.ProjectMock.expects('update') + .withArgs( + { + _id: this.project._id + }, + { + $pull: { + collaberator_refs: this.userId, + readOnly_refs: this.userId, + tokenAccessReadOnly_refs: this.userId, + tokenAccessReadAndWrite_refs: this.userId, + archived: this.userId, + trashed: this.userId + } } - } + ) + .chain('exec') + .resolves() + await this.CollaboratorsHandler.promises.removeUserFromProject( + this.project._id, + this.userId ) - .chain('exec') - .resolves() - await this.CollaboratorsHandler.promises.removeUserFromProject( - this.project._id, - this.userId - ) + }) + }) + + describe('an archived project, archived with a boolean value', function() { + beforeEach(function() { + let archived = [ObjectId(this.userId)] + this.ProjectHelper.calculateArchivedArray.returns(archived) + + this.ProjectMock.expects('findOne') + .withArgs({ + _id: this.oldArchivedProject._id + }) + .chain('exec') + .resolves(this.oldArchivedProject) + }) + + it('should remove the user from mongo', async function() { + this.ProjectMock.expects('update') + .withArgs( + { + _id: this.oldArchivedProject._id + }, + { + $set: { + archived: [] + }, + $pull: { + collaberator_refs: this.userId, + readOnly_refs: this.userId, + tokenAccessReadOnly_refs: this.userId, + tokenAccessReadAndWrite_refs: this.userId, + trashed: this.userId + } + } + ) + .resolves() + await this.CollaboratorsHandler.promises.removeUserFromProject( + this.oldArchivedProject._id, + this.userId + ) + }) + }) + + describe('an archived project, archived with an array value', function() { + beforeEach(function() { + this.ProjectMock.expects('findOne') + .withArgs({ + _id: this.archivedProject._id + }) + .chain('exec') + .resolves(this.archivedProject) + }) + + it('should remove the user from mongo', async function() { + this.ProjectMock.expects('update') + .withArgs( + { + _id: this.archivedProject._id + }, + { + $pull: { + collaberator_refs: this.userId, + readOnly_refs: this.userId, + tokenAccessReadOnly_refs: this.userId, + tokenAccessReadAndWrite_refs: this.userId, + archived: this.userId, + trashed: this.userId + } + } + ) + .resolves() + await this.CollaboratorsHandler.promises.removeUserFromProject( + this.archivedProject._id, + this.userId + ) + }) }) }) @@ -240,6 +341,13 @@ describe('CollaboratorsHandler', function() { 'token-read-only-1' ] for (const projectId of expectedProjects) { + this.ProjectMock.expects('findOne') + .withArgs({ + _id: projectId + }) + .chain('exec') + .resolves({ _id: projectId }) + this.ProjectMock.expects('update') .withArgs( { @@ -250,11 +358,12 @@ describe('CollaboratorsHandler', function() { collaberator_refs: this.userId, readOnly_refs: this.userId, tokenAccessReadOnly_refs: this.userId, - tokenAccessReadAndWrite_refs: this.userId + tokenAccessReadAndWrite_refs: this.userId, + archived: this.userId, + trashed: this.userId } } ) - .chain('exec') .resolves() } await this.CollaboratorsHandler.promises.removeUserFromAllProjects(