mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #1750 from overleaf/cmg-project-metadata
Preserve project metadata when deleting GitOrigin-RevId: 38efb139406d6111f5b6186d82c3fbfb5ab9b2b9
This commit is contained in:
parent
b84c24520c
commit
29408b301c
7 changed files with 97 additions and 111 deletions
|
@ -90,18 +90,18 @@ module.exports = ProjectController =
|
|||
project_id = req.params.Project_id
|
||||
forever = req.query?.forever?
|
||||
logger.log project_id: project_id, forever: forever, "received request to archive project"
|
||||
|
||||
if forever
|
||||
doDelete = projectDeleter.deleteProject
|
||||
else
|
||||
doDelete = projectDeleter.archiveProject
|
||||
|
||||
doDelete project_id, (err)->
|
||||
user = AuthenticationController.getSessionUser(req)
|
||||
cb = (err)->
|
||||
if err?
|
||||
res.sendStatus 500
|
||||
else
|
||||
res.sendStatus 200
|
||||
|
||||
if forever
|
||||
projectDeleter.deleteProject project_id, {deleterUser: user, ipAddress:req.ip} , cb
|
||||
else
|
||||
projectDeleter.archiveProject project_id, cb
|
||||
|
||||
restoreProject: (req, res) ->
|
||||
project_id = req.params.Project_id
|
||||
logger.log project_id:project_id, "received request to restore project"
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
Project = require('../../models/Project').Project
|
||||
DeletedProject = require('../../models/DeletedProject').DeletedProject
|
||||
logger = require('logger-sharelatex')
|
||||
documentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
|
||||
tagsHandler = require("../Tags/TagsHandler")
|
||||
async = require("async")
|
||||
FileStoreHandler = require("../FileStore/FileStoreHandler")
|
||||
CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
|
||||
{db, ObjectId} = require("../../infrastructure/mongojs")
|
||||
|
||||
module.exports = ProjectDeleter =
|
||||
|
||||
|
@ -17,64 +17,67 @@ module.exports = ProjectDeleter =
|
|||
Project.update conditions, update, {}, (err)->
|
||||
require('../Editor/EditorController').notifyUsersProjectHasBeenDeletedOrRenamed project_id, ->
|
||||
callback()
|
||||
|
||||
|
||||
unmarkAsDeletedByExternalSource: (project_id, callback = (error) ->) ->
|
||||
logger.log project_id: project_id, "removing flag marking project as deleted by external data source"
|
||||
conditions = {_id:project_id.toString()}
|
||||
update = {deletedByExternalDataSource: false}
|
||||
Project.update conditions, update, {}, callback
|
||||
|
||||
deleteUsersProjects: (user_id, callback)->
|
||||
deleteUsersProjects: (user_id, callback) ->
|
||||
logger.log {user_id}, "deleting users projects"
|
||||
ProjectDeleter._deleteUsersProjectWithMethod user_id, ProjectDeleter.deleteProject, callback
|
||||
|
||||
softDeleteUsersProjectsForMigration: (user_id, callback)->
|
||||
logger.log {user_id}, "soft-deleting users projects"
|
||||
ProjectDeleter._deleteUsersProjectWithMethod user_id, ProjectDeleter.softDeleteProjectForMigration, callback
|
||||
|
||||
_deleteUsersProjectWithMethod: (user_id, deleteMethod, callback) ->
|
||||
Project.find {owner_ref: user_id}, (error, projects) ->
|
||||
return callback(error) if error?
|
||||
async.each(
|
||||
projects,
|
||||
(project, cb) ->
|
||||
deleteMethod project._id, cb
|
||||
ProjectDeleter.deleteProject project._id, cb
|
||||
(err) ->
|
||||
return callback(err) if err?
|
||||
CollaboratorsHandler.removeUserFromAllProjets user_id, callback
|
||||
)
|
||||
|
||||
softDeleteProjectForMigration: (project_id, callback) ->
|
||||
logger.log project_id: project_id, "soft-deleting project"
|
||||
deleteProject: (project_id, options = {}, callback = (error) ->) ->
|
||||
data = {}
|
||||
logger.log project_id: project_id, "deleting project"
|
||||
|
||||
if typeof options == 'function'
|
||||
callback = options
|
||||
options = {}
|
||||
|
||||
async.waterfall [
|
||||
(cb) ->
|
||||
Project.findOne {_id: project_id}, (err, project) -> cb(err, project)
|
||||
(project, cb) ->
|
||||
return callback(new Errors.NotFoundError("project not found")) unless project?
|
||||
project.deletedAt = new Date()
|
||||
db.projectsDeletedByMigration.insert project, (err) -> cb(err)
|
||||
(cb) ->
|
||||
ProjectDeleter.deleteProject project_id, cb
|
||||
], callback
|
||||
deletedProject = new DeletedProject()
|
||||
deletedProject.project = project
|
||||
deletedProject.deleterData =
|
||||
deletedAt: new Date()
|
||||
deleterId: options.deleterUser?._id
|
||||
deleterIpAddress: options.ipAddress
|
||||
|
||||
deleteProject: (project_id, callback = (error) ->) ->
|
||||
logger.log project_id: project_id, "deleting project"
|
||||
async.series [
|
||||
(cb)->
|
||||
documentUpdaterHandler.flushProjectToMongoAndDelete project_id, cb
|
||||
(cb)->
|
||||
return callback(new Errors.NotFoundError("project not found")) unless project?
|
||||
|
||||
deletedProject.save (err) ->
|
||||
cb(err, deletedProject)
|
||||
(deletedProject, cb) ->
|
||||
documentUpdaterHandler.flushProjectToMongoAndDelete project_id, (err) ->
|
||||
cb(err, deletedProject)
|
||||
(deletedProject, cb) ->
|
||||
CollaboratorsHandler.getMemberIds project_id, (error, member_ids = []) ->
|
||||
for member_id in member_ids
|
||||
tagsHandler.removeProjectFromAllTags member_id, project_id, (err)->
|
||||
cb() #doesn't matter if this fails or the order it happens in
|
||||
(cb) ->
|
||||
Project.remove _id: project_id, cb
|
||||
], (err) ->
|
||||
cb(null, deletedProject) #doesn't matter if this fails or the order it happens in
|
||||
(deletedProject, cb) ->
|
||||
Project.remove _id: project_id, (err) ->
|
||||
cb(err, deletedProject)
|
||||
], (err, deletedProject) ->
|
||||
if err?
|
||||
logger.err err:err, "problem deleting project"
|
||||
return callback(err)
|
||||
logger.log project_id:project_id, "successfully deleting project from user request"
|
||||
callback()
|
||||
callback(null, deletedProject)
|
||||
|
||||
archiveProject: (project_id, callback = (error) ->)->
|
||||
logger.log project_id:project_id, "archived project from user request"
|
||||
|
|
|
@ -23,7 +23,7 @@ module.exports = UserDeleter =
|
|||
(cb) ->
|
||||
UserDeleter._cleanupUser user, cb
|
||||
(cb) ->
|
||||
ProjectDeleter.softDeleteUsersProjectsForMigration user._id, cb
|
||||
ProjectDeleter.deleteUsersProjects user._id, cb
|
||||
(cb) ->
|
||||
user.deletedAt = new Date()
|
||||
db.usersDeletedByMigration.insert user, cb
|
||||
|
|
27
services/web/app/coffee/models/DeletedProject.coffee
Normal file
27
services/web/app/coffee/models/DeletedProject.coffee
Normal file
|
@ -0,0 +1,27 @@
|
|||
mongoose = require('mongoose')
|
||||
Settings = require 'settings-sharelatex'
|
||||
ProjectSchema = require('./Project.js').ProjectSchema
|
||||
|
||||
Schema = mongoose.Schema
|
||||
ObjectId = Schema.ObjectId
|
||||
|
||||
DeleterDataSchema = new Schema
|
||||
deleterId: {type: ObjectId, ref: 'User'}
|
||||
deleterIpAddress: { type: String }
|
||||
deletedAt: { type: Date }
|
||||
|
||||
DeletedProjectSchema = new Schema({
|
||||
deleterData : [DeleterDataSchema]
|
||||
project: [ProjectSchema]
|
||||
}, collection: 'deletedProjects')
|
||||
|
||||
conn = mongoose.createConnection(Settings.mongo.url, {
|
||||
server: {poolSize: Settings.mongo.poolSize || 10},
|
||||
config: {autoIndex: false}
|
||||
})
|
||||
|
||||
DeletedProject = conn.model('DeletedProject', DeletedProjectSchema)
|
||||
|
||||
mongoose.model 'DeletedProject', DeletedProjectSchema
|
||||
exports.DeletedProject = DeletedProject
|
||||
exports.DeletedProjectSchema = DeletedProjectSchema
|
|
@ -31,7 +31,7 @@ describe "ProjectController", ->
|
|||
@token = 'some-token'
|
||||
@ProjectDeleter =
|
||||
archiveProject: sinon.stub().callsArg(1)
|
||||
deleteProject: sinon.stub().callsArg(1)
|
||||
deleteProject: sinon.stub().callsArg(2)
|
||||
restoreProject: sinon.stub().callsArg(1)
|
||||
findArchivedProjects: sinon.stub()
|
||||
@ProjectDuplicator =
|
||||
|
@ -140,6 +140,7 @@ describe "ProjectController", ->
|
|||
projectName: @projectName
|
||||
i18n:
|
||||
translate:->
|
||||
ip: "192.170.18.1"
|
||||
@res =
|
||||
locals:
|
||||
jsPath:"js path here"
|
||||
|
@ -230,7 +231,7 @@ describe "ProjectController", ->
|
|||
it "should tell the project deleter to delete when forever=true", (done)->
|
||||
@req.query = forever: "true"
|
||||
@res.sendStatus = (code)=>
|
||||
@ProjectDeleter.deleteProject.calledWith(@project_id).should.equal true
|
||||
@ProjectDeleter.deleteProject.calledWith(@project_id, {deleterUser: @user, ipAddress:@req.ip}).should.equal true
|
||||
code.should.equal 200
|
||||
done()
|
||||
@ProjectController.deleteProject @req, @res
|
||||
|
|
|
@ -3,7 +3,6 @@ modulePath = "../../../../app/js/Features/Project/ProjectDeleter"
|
|||
SandboxedModule = require('sandboxed-module')
|
||||
sinon = require('sinon')
|
||||
|
||||
|
||||
describe 'ProjectDeleter', ->
|
||||
|
||||
beforeEach ->
|
||||
|
@ -16,14 +15,10 @@ describe 'ProjectDeleter', ->
|
|||
owner_ref:"owner ref here"
|
||||
remove: sinon.stub().callsArg(0)
|
||||
|
||||
@user_id = 1234
|
||||
|
||||
@mongojs =
|
||||
db:
|
||||
deletedProjects:
|
||||
insert: sinon.stub().callsArg(1)
|
||||
projectsDeletedByMigration:
|
||||
insert: sinon.stub().callsArg(1)
|
||||
@user =
|
||||
_id:"588f3ddae8ebc1bac07c9fa4"
|
||||
first_name: "bjkdsjfk"
|
||||
features: {}
|
||||
|
||||
@Project =
|
||||
update: sinon.stub().callsArgWith(3)
|
||||
|
@ -31,6 +26,9 @@ describe 'ProjectDeleter', ->
|
|||
findOne: sinon.stub().callsArgWith(1, null, @project)
|
||||
find: sinon.stub().callsArgWith(1, null, [@project])
|
||||
applyToAllFilesRecursivly: sinon.stub()
|
||||
@DeletedProject = class DeletedProject
|
||||
constructor: ->
|
||||
save: sinon.stub().callsArgWith(0)
|
||||
@documentUpdaterHandler =
|
||||
flushProjectToMongoAndDelete:sinon.stub().callsArgWith(1)
|
||||
@editorController = notifyUsersProjectHasBeenDeletedOrRenamed : sinon.stub().callsArgWith(1)
|
||||
|
@ -42,11 +40,11 @@ describe 'ProjectDeleter', ->
|
|||
@deleter = SandboxedModule.require modulePath, requires:
|
||||
"../Editor/EditorController": @editorController
|
||||
'../../models/Project':{Project:@Project}
|
||||
'../../models/DeletedProject':{DeletedProject:@DeletedProject}
|
||||
'../DocumentUpdater/DocumentUpdaterHandler': @documentUpdaterHandler
|
||||
"../Tags/TagsHandler":@TagsHandler
|
||||
"../FileStore/FileStoreHandler": @FileStoreHandler = {}
|
||||
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler
|
||||
"../../infrastructure/mongojs": @mongojs
|
||||
'logger-sharelatex':
|
||||
log:->
|
||||
|
||||
|
@ -83,12 +81,12 @@ describe 'ProjectDeleter', ->
|
|||
@deleter.deleteProject = sinon.stub().callsArg(1)
|
||||
|
||||
it "should find all the projects owned by the user_id", (done)->
|
||||
@deleter.deleteUsersProjects @user_id, =>
|
||||
sinon.assert.calledWith(@Project.find, owner_ref: @user_id)
|
||||
@deleter.deleteUsersProjects @user._id, =>
|
||||
sinon.assert.calledWith(@Project.find, owner_ref: @user._id)
|
||||
done()
|
||||
|
||||
it "should call deleteProject on the found projects", (done)->
|
||||
@deleter.deleteUsersProjects @user_id, =>
|
||||
@deleter.deleteUsersProjects @user._id, =>
|
||||
sinon.assert.calledWith(@deleter.deleteProject, @project._id)
|
||||
done()
|
||||
|
||||
|
@ -96,54 +94,31 @@ describe 'ProjectDeleter', ->
|
|||
@Project.find.callsArgWith(1, null, [
|
||||
{_id: 'potato'}, {_id: 'wombat'}
|
||||
])
|
||||
@deleter.deleteUsersProjects @user_id, =>
|
||||
@deleter.deleteUsersProjects @user._id, =>
|
||||
sinon.assert.calledTwice(@deleter.deleteProject)
|
||||
sinon.assert.calledWith(@deleter.deleteProject, 'wombat')
|
||||
sinon.assert.calledWith(@deleter.deleteProject, 'potato')
|
||||
done()
|
||||
|
||||
it "should remove all the projects the user is a collaborator of", (done)->
|
||||
@deleter.deleteUsersProjects @user_id, =>
|
||||
@CollaboratorsHandler.removeUserFromAllProjets.calledWith(@user_id).should.equal true
|
||||
done()
|
||||
|
||||
describe "softDeleteUsersProjectsForMigrationForMigration", ->
|
||||
beforeEach ->
|
||||
@deleter.softDeleteProjectForMigration = sinon.stub().callsArg(1)
|
||||
|
||||
it "should find all the projects owned by the user_id", (done)->
|
||||
@deleter.softDeleteUsersProjectsForMigration @user_id, =>
|
||||
@Project.find.calledWith(owner_ref: @user_id).should.equal true
|
||||
done()
|
||||
|
||||
it "should call deleteProject on the found projects", (done)->
|
||||
@deleter.softDeleteUsersProjectsForMigration @user_id, =>
|
||||
sinon.assert.calledWith(@deleter.softDeleteProjectForMigration, @project._id)
|
||||
done()
|
||||
|
||||
it "should call deleteProject once for each project", (done)->
|
||||
@Project.find.callsArgWith(1, null, [
|
||||
{_id: 'potato'}, {_id: 'wombat'}
|
||||
])
|
||||
@deleter.softDeleteUsersProjectsForMigration @user_id, =>
|
||||
sinon.assert.calledTwice(@deleter.softDeleteProjectForMigration)
|
||||
sinon.assert.calledWith(@deleter.softDeleteProjectForMigration, 'wombat')
|
||||
sinon.assert.calledWith(@deleter.softDeleteProjectForMigration, 'potato')
|
||||
done()
|
||||
|
||||
it "should remove all the projects the user is a collaborator of", (done)->
|
||||
@deleter.softDeleteUsersProjectsForMigration @user_id, =>
|
||||
@CollaboratorsHandler.removeUserFromAllProjets.calledWith(@user_id).should.equal true
|
||||
@deleter.deleteUsersProjects @user._id, =>
|
||||
@CollaboratorsHandler.removeUserFromAllProjets.calledWith(@user._id).should.equal true
|
||||
done()
|
||||
|
||||
describe "deleteProject", ->
|
||||
beforeEach (done) ->
|
||||
beforeEach () ->
|
||||
@project_id = "mock-project-id-123"
|
||||
@Project.remove.callsArgWith(1)
|
||||
done()
|
||||
@ip = "192.170.18.1"
|
||||
|
||||
it "should save a DeletedProject with additional deleterData", (done) ->
|
||||
@deleter.deleteProject @project_id, {deleterUser: @user, ipAddress: @ip}, (err, deletedProject) =>
|
||||
@DeletedProject::save.called.should.equal true
|
||||
deletedProject.deleterData.deleterIpAddress.should.equal(@ip)
|
||||
deletedProject.deleterData.deleterId.should.equal(@user._id)
|
||||
done()
|
||||
|
||||
it "should flushProjectToMongoAndDelete in doc updater", (done)->
|
||||
@deleter.deleteProject @project_id, =>
|
||||
@deleter.deleteProject @project_id, {deleterUser: @user, ipAddress: @ip}, =>
|
||||
@documentUpdaterHandler.flushProjectToMongoAndDelete.calledWith(@project_id).should.equal true
|
||||
done()
|
||||
|
||||
|
@ -160,25 +135,6 @@ describe 'ProjectDeleter', ->
|
|||
}).should.equal true
|
||||
done()
|
||||
|
||||
describe "softDeleteProjectForMigration", ->
|
||||
beforeEach ->
|
||||
@deleter.deleteProject = sinon.stub().callsArg(1)
|
||||
|
||||
it "should set the deletedAt time", (done)->
|
||||
@deleter.softDeleteProjectForMigration @project_id, =>
|
||||
@project.deletedAt.should.exist
|
||||
done()
|
||||
|
||||
it "should insert the project into the deleted projects collection", (done)->
|
||||
@deleter.softDeleteProjectForMigration @project_id, =>
|
||||
sinon.assert.calledWith(@mongojs.db.projectsDeletedByMigration.insert, @project)
|
||||
done()
|
||||
|
||||
it "should delete the project", (done)->
|
||||
@deleter.softDeleteProjectForMigration @project_id, =>
|
||||
sinon.assert.calledWith(@deleter.deleteProject, @project_id)
|
||||
done()
|
||||
|
||||
describe "archiveProject", ->
|
||||
beforeEach ->
|
||||
@Project.update.callsArgWith(2)
|
||||
|
|
|
@ -8,7 +8,7 @@ describe "UserDeleter", ->
|
|||
|
||||
beforeEach ->
|
||||
@user =
|
||||
_id:"12390i"
|
||||
_id: "12390i"
|
||||
email: "bob@bob.com"
|
||||
remove: sinon.stub().callsArgWith(0)
|
||||
|
||||
|
@ -20,7 +20,6 @@ describe "UserDeleter", ->
|
|||
|
||||
@ProjectDeleter =
|
||||
deleteUsersProjects: sinon.stub().callsArgWith(1)
|
||||
softDeleteUsersProjectsForMigration: sinon.stub().callsArgWith(1)
|
||||
|
||||
@SubscriptionHandler =
|
||||
cancelSubscription: sinon.stub().callsArgWith(1)
|
||||
|
@ -85,9 +84,9 @@ describe "UserDeleter", ->
|
|||
@deleteAffiliations.calledWith(@user._id).should.equal true
|
||||
done()
|
||||
|
||||
it "should soft-delete all the projects of a user", (done)->
|
||||
it "should delete all the projects of a user", (done)->
|
||||
@UserDeleter.softDeleteUserForMigration @user._id, (err)=>
|
||||
@ProjectDeleter.softDeleteUsersProjectsForMigration.calledWith(@user._id).should.equal true
|
||||
@ProjectDeleter.deleteUsersProjects.calledWith(@user._id).should.equal true
|
||||
done()
|
||||
|
||||
it "should remove user memberships", (done)->
|
||||
|
|
Loading…
Reference in a new issue