Merge pull request #41 from sharelatex/ja-transfer-projects

Add method to transfer projects from one user_id to another
This commit is contained in:
James Allen 2017-10-12 16:27:00 +01:00 committed by GitHub
commit f0079bb7a4
2 changed files with 152 additions and 0 deletions

View file

@ -157,3 +157,55 @@ module.exports = CollaboratorsHandler =
return callback(error)
{owner, members} = ProjectEditorHandler.buildOwnerAndMembersViews(rawMembers)
callback(null, members)
transferProjects: (from_user_id, to_user_id, callback=(err, projects) ->) ->
MEMBER_KEYS = ['collaberator_refs', 'readOnly_refs']
# Find all the projects this user is part of so we can flush them to TPDS
query =
$or:
[{ owner_ref: from_user_id }]
.concat(
MEMBER_KEYS.map (key) ->
q = {}
q[key] = from_user_id
return q
) # [{ collaberator_refs: from_user_id }, ...]
Project.find query, { _id: 1 }, (error, projects = []) ->
return callback(error) if error?
project_ids = projects.map (p) -> p._id
logger.log {project_ids, from_user_id, to_user_id}, "transferring projects"
update_jobs = []
update_jobs.push (cb) ->
Project.update { owner_ref: from_user_id }, { $set: { owner_ref: to_user_id }}, { multi: true }, cb
for key in MEMBER_KEYS
do (key) ->
update_jobs.push (cb) ->
query = {}
addNewUserUpdate = $addToSet: {}
removeOldUserUpdate = $pull: {}
query[key] = from_user_id
removeOldUserUpdate.$pull[key] = from_user_id
addNewUserUpdate.$addToSet[key] = to_user_id
# Mongo won't let us pull and addToSet in the same query, so do it in
# two. Note we need to add first, since the query is based on the old user.
Project.update query, addNewUserUpdate, { multi: true }, (error) ->
return cb(error) if error?
Project.update query, removeOldUserUpdate, { multi: true }, cb
# Flush each project to TPDS to add files to new user's Dropbox
ProjectEntityHandler = require("../Project/ProjectEntityHandler")
flush_jobs = []
for project_id in project_ids
do (project_id) ->
flush_jobs.push (cb) ->
ProjectEntityHandler.flushProjectToThirdPartyDataStore project_id, cb
# Flush in background, no need to block on this
async.series flush_jobs, (error) ->
if error?
logger.err {err: error, project_ids, from_user_id, to_user_id}, "error flushing tranferred projects to TPDS"
async.series update_jobs, callback

View file

@ -344,3 +344,103 @@ describe "CollaboratorsHandler", ->
it 'should not call ProjectEditorHandler.buildOwnerAndMembersViews', ->
@ProjectEditorHandler.buildOwnerAndMembersViews.callCount.should.equal 0
describe 'transferProjects', ->
beforeEach ->
@from_user_id = "from-user-id"
@to_user_id = "to-user-id"
@projects = [{
_id: "project-id-1"
}, {
_id: "project-id-2"
}]
@Project.find = sinon.stub().yields(null, @projects)
@Project.update = sinon.stub().yields()
@ProjectEntityHandler.flushProjectToThirdPartyDataStore = sinon.stub().yields()
describe "successfully", ->
beforeEach ->
@CollaboratorHandler.transferProjects @from_user_id, @to_user_id, @callback
it "should look up the affected projects", ->
@Project.find
.calledWith({
$or : [
{ owner_ref: @from_user_id }
{ collaberator_refs: @from_user_id }
{ readOnly_refs: @from_user_id }
]
})
.should.equal true
it "should transfer owned projects", ->
@Project.update
.calledWith({
owner_ref: @from_user_id
}, {
$set: { owner_ref: @to_user_id }
}, {
multi: true
})
.should.equal true
it "should transfer collaborator projects", ->
@Project.update
.calledWith({
collaberator_refs: @from_user_id
}, {
$addToSet: { collaberator_refs: @to_user_id }
}, {
multi: true
})
.should.equal true
@Project.update
.calledWith({
collaberator_refs: @from_user_id
}, {
$pull: { collaberator_refs: @from_user_id }
}, {
multi: true
})
.should.equal true
it "should transfer read only collaborator projects", ->
@Project.update
.calledWith({
readOnly_refs: @from_user_id
}, {
$addToSet: { readOnly_refs: @to_user_id }
}, {
multi: true
})
.should.equal true
@Project.update
.calledWith({
readOnly_refs: @from_user_id
}, {
$pull: { readOnly_refs: @from_user_id }
}, {
multi: true
})
.should.equal true
it "should flush each project to the TPDS", ->
for project in @projects
@ProjectEntityHandler.flushProjectToThirdPartyDataStore
.calledWith(project._id)
.should.equal true
it "should call the callback", ->
@callback.called.should.equal true
describe "when flushing to TPDS fails", ->
beforeEach ->
@ProjectEntityHandler.flushProjectToThirdPartyDataStore = sinon.stub().yields(new Error('oops'))
@CollaboratorHandler.transferProjects @from_user_id, @to_user_id, @callback
it "should log an error", ->
@logger.err.called.should.equal true
it "should not return an error since it happens in the background", ->
@callback.called.should.equal true
@callback.calledWith(new Error('oops')).should.equal false