Merge pull request #214 from sharelatex/hof-version-entity-deletion

version entity deletion
This commit is contained in:
James Allen 2017-12-19 14:59:37 +00:00 committed by GitHub
commit e18dc1e41d
15 changed files with 285 additions and 83 deletions

View file

@ -207,8 +207,8 @@ module.exports = DocumentUpdaterHandler =
updateProjectStructure : (project_id, userId, changes, callback = (error) ->)-> updateProjectStructure : (project_id, userId, changes, callback = (error) ->)->
return callback() if !settings.apis.project_history?.enabled return callback() if !settings.apis.project_history?.enabled
docUpdates = DocumentUpdaterHandler._getRenameUpdates('doc', changes.oldDocs, changes.newDocs) docUpdates = DocumentUpdaterHandler._getUpdates('doc', changes.oldDocs, changes.newDocs)
fileUpdates = DocumentUpdaterHandler._getRenameUpdates('file', changes.oldFiles, changes.newFiles) fileUpdates = DocumentUpdaterHandler._getUpdates('file', changes.oldFiles, changes.newFiles)
timer = new metrics.Timer("set-document") timer = new metrics.Timer("set-document")
url = "#{settings.apis.documentupdater.url}/project/#{project_id}" url = "#{settings.apis.documentupdater.url}/project/#{project_id}"
@ -230,7 +230,7 @@ module.exports = DocumentUpdaterHandler =
logger.error {project_id, url}, "doc updater returned a non-success status code: #{res.statusCode}" logger.error {project_id, url}, "doc updater returned a non-success status code: #{res.statusCode}"
callback new Error("doc updater returned a non-success status code: #{res.statusCode}") callback new Error("doc updater returned a non-success status code: #{res.statusCode}")
_getRenameUpdates: (entityType, oldEntities, newEntities) -> _getUpdates: (entityType, oldEntities, newEntities) ->
oldEntities ||= [] oldEntities ||= []
newEntities ||= [] newEntities ||= []
updates = [] updates = []
@ -255,6 +255,16 @@ module.exports = DocumentUpdaterHandler =
pathname: oldEntity.path pathname: oldEntity.path
newPathname: newEntity.path newPathname: newEntity.path
for id, oldEntity of oldEntitiesHash
newEntity = newEntitiesHash[id]
if !newEntity?
# entity deleted
updates.push
id: id
pathname: oldEntity.path
newPathname: ''
updates updates
PENDINGUPDATESKEY = "PendingUpdates" PENDINGUPDATESKEY = "PendingUpdates"

View file

@ -105,19 +105,19 @@ module.exports = EditorController =
async.series jobs, (err)-> async.series jobs, (err)->
callback err, newFolders, lastFolder callback err, newFolders, lastFolder
deleteEntity : (project_id, entity_id, entityType, source, callback)-> deleteEntity : (project_id, entity_id, entityType, source, userId, callback)->
LockManager.getLock project_id, (err)-> LockManager.getLock project_id, (err)->
if err? if err?
logger.err err:err, project_id:project_id, "could not get lock to deleteEntity" logger.err err:err, project_id:project_id, "could not get lock to deleteEntity"
return callback(err) return callback(err)
EditorController.deleteEntityWithoutLock project_id, entity_id, entityType, source, (err)-> EditorController.deleteEntityWithoutLock project_id, entity_id, entityType, source, userId, (err)->
LockManager.releaseLock project_id, ()-> LockManager.releaseLock project_id, ()->
callback(err) callback(err)
deleteEntityWithoutLock: (project_id, entity_id, entityType, source, callback)-> deleteEntityWithoutLock: (project_id, entity_id, entityType, source, userId, callback)->
logger.log {project_id, entity_id, entityType, source}, "start delete process of entity" logger.log {project_id, entity_id, entityType, source}, "start delete process of entity"
Metrics.inc "editor.delete-entity" Metrics.inc "editor.delete-entity"
ProjectEntityHandler.deleteEntity project_id, entity_id, entityType, (err)-> ProjectEntityHandler.deleteEntity project_id, entity_id, entityType, userId, (err)->
if err? if err?
logger.err err:err, project_id:project_id, entity_id:entity_id, entityType:entityType, "error deleting entity" logger.err err:err, project_id:project_id, entity_id:entity_id, entityType:entityType, "error deleting entity"
return callback(err) return callback(err)

View file

@ -147,6 +147,7 @@ module.exports = EditorHttpController =
project_id = req.params.Project_id project_id = req.params.Project_id
entity_id = req.params.entity_id entity_id = req.params.entity_id
entity_type = req.params.entity_type entity_type = req.params.entity_type
EditorController.deleteEntity project_id, entity_id, entity_type, "editor", (error) -> user_id = AuthenticationController.getLoggedInUserId(req)
EditorController.deleteEntity project_id, entity_id, entity_type, "editor", user_id, (error) ->
return next(error) if error? return next(error) if error?
res.sendStatus 204 res.sendStatus 204

View file

@ -412,7 +412,7 @@ module.exports = ProjectEntityHandler =
callback() callback()
deleteEntity: (project_id, entity_id, entityType, callback = (error) ->)-> deleteEntity: (project_id, entity_id, entityType, userId, callback = (error) ->)->
self = @ self = @
logger.log entity_id:entity_id, entityType:entityType, project_id:project_id, "deleting project entity" logger.log entity_id:entity_id, entityType:entityType, project_id:project_id, "deleting project entity"
if !entityType? if !entityType?
@ -423,7 +423,7 @@ module.exports = ProjectEntityHandler =
return callback(error) if error? return callback(error) if error?
projectLocator.findElement {project: project, element_id: entity_id, type: entityType}, (error, entity, path)=> projectLocator.findElement {project: project, element_id: entity_id, type: entityType}, (error, entity, path)=>
return callback(error) if error? return callback(error) if error?
ProjectEntityHandler._cleanUpEntity project, entity, entityType, (error) -> ProjectEntityHandler._cleanUpEntity project, entity, entityType, path.fileSystem, userId, (error) ->
return callback(error) if error? return callback(error) if error?
tpdsUpdateSender.deleteEntity project_id:project_id, path:path.fileSystem, project_name:project.name, (error) -> tpdsUpdateSender.deleteEntity project_id:project_id, path:path.fileSystem, project_name:project.name, (error) ->
return callback(error) if error? return callback(error) if error?
@ -456,17 +456,17 @@ module.exports = ProjectEntityHandler =
return callback(error) if error? return callback(error) if error?
DocumentUpdaterHandler.updateProjectStructure project_id, userId, {oldDocs, newDocs, oldFiles, newFiles}, callback DocumentUpdaterHandler.updateProjectStructure project_id, userId, {oldDocs, newDocs, oldFiles, newFiles}, callback
_cleanUpEntity: (project, entity, entityType, callback = (error) ->) -> _cleanUpEntity: (project, entity, entityType, path, userId, callback = (error) ->) ->
if(entityType.indexOf("file") != -1) if(entityType.indexOf("file") != -1)
ProjectEntityHandler._cleanUpFile project, entity, callback ProjectEntityHandler._cleanUpFile project, entity, path, userId, callback
else if (entityType.indexOf("doc") != -1) else if (entityType.indexOf("doc") != -1)
ProjectEntityHandler._cleanUpDoc project, entity, callback ProjectEntityHandler._cleanUpDoc project, entity, path, userId, callback
else if (entityType.indexOf("folder") != -1) else if (entityType.indexOf("folder") != -1)
ProjectEntityHandler._cleanUpFolder project, entity, callback ProjectEntityHandler._cleanUpFolder project, entity, path, userId, callback
else else
callback() callback()
_cleanUpDoc: (project, doc, callback = (error) ->) -> _cleanUpDoc: (project, doc, path, userId, callback = (error) ->) ->
project_id = project._id.toString() project_id = project._id.toString()
doc_id = doc._id.toString() doc_id = doc._id.toString()
unsetRootDocIfRequired = (callback) => unsetRootDocIfRequired = (callback) =>
@ -483,26 +483,33 @@ module.exports = ProjectEntityHandler =
return callback(error) if error? return callback(error) if error?
DocstoreManager.deleteDoc project_id, doc_id, (error) -> DocstoreManager.deleteDoc project_id, doc_id, (error) ->
return callback(error) if error? return callback(error) if error?
callback() changes = oldDocs: [ {doc, path} ]
DocumentUpdaterHandler.updateProjectStructure project_id, userId, changes, callback
_cleanUpFile: (project, file, callback = (error) ->) -> _cleanUpFile: (project, file, path, userId, callback = (error) ->) ->
project_id = project._id.toString() project_id = project._id.toString()
file_id = file._id.toString() file_id = file._id.toString()
FileStoreHandler.deleteFile project_id, file_id, callback FileStoreHandler.deleteFile project_id, file_id, (error) ->
return callback(error) if error?
changes = oldFiles: [ {file, path} ]
DocumentUpdaterHandler.updateProjectStructure project_id, userId, changes, callback
_cleanUpFolder: (project, folder, callback = (error) ->) -> _cleanUpFolder: (project, folder, folderPath, userId, callback = (error) ->) ->
jobs = [] jobs = []
for doc in folder.docs for doc in folder.docs
do (doc) -> do (doc) ->
jobs.push (callback) -> ProjectEntityHandler._cleanUpDoc project, doc, callback docPath = path.join(folderPath, doc.name)
jobs.push (callback) -> ProjectEntityHandler._cleanUpDoc project, doc, docPath, userId, callback
for file in folder.fileRefs for file in folder.fileRefs
do (file) -> do (file) ->
jobs.push (callback) -> ProjectEntityHandler._cleanUpFile project, file, callback filePath = path.join(folderPath, file.name)
jobs.push (callback) -> ProjectEntityHandler._cleanUpFile project, file, filePath, userId, callback
for childFolder in folder.folders for childFolder in folder.folders
do (childFolder) -> do (childFolder) ->
jobs.push (callback) -> ProjectEntityHandler._cleanUpFolder project, childFolder, callback folderPath = path.join(folderPath, childFolder.name)
jobs.push (callback) -> ProjectEntityHandler._cleanUpFolder project, childFolder, folderPath, userId, callback
async.series jobs, callback async.series jobs, callback

View file

@ -47,7 +47,7 @@ module.exports =
logger.log user_id:user_id, filePath:path, projectName:projectName, project_id:project._id, "project found for delete update, path is root so marking project as deleted" logger.log user_id:user_id, filePath:path, projectName:projectName, project_id:project._id, "project found for delete update, path is root so marking project as deleted"
return projectDeleter.markAsDeletedByExternalSource project._id, callback return projectDeleter.markAsDeletedByExternalSource project._id, callback
else else
updateMerger.deleteUpdate project._id, path, source, (err)-> updateMerger.deleteUpdate user_id, project._id, path, source, (err)->
callback(err) callback(err)

View file

@ -32,13 +32,13 @@ module.exports =
else else
self.p.processDoc project_id, elementId, user_id, fsPath, path, source, callback self.p.processDoc project_id, elementId, user_id, fsPath, path, source, callback
deleteUpdate: (project_id, path, source, callback)-> deleteUpdate: (user_id, project_id, path, source, callback)->
projectLocator.findElementByPath project_id, path, (err, element, type)-> projectLocator.findElementByPath project_id, path, (err, element, type)->
if err? || !element? if err? || !element?
logger.log element:element, project_id:project_id, path:path, "could not find entity for deleting, assuming it was already deleted" logger.log element:element, project_id:project_id, path:path, "could not find entity for deleting, assuming it was already deleted"
return callback() return callback()
logger.log project_id:project_id, path:path, type:type, element:element, "processing update to delete entity from tpds" logger.log project_id:project_id, path:path, type:type, element:element, "processing update to delete entity from tpds"
editorController.deleteEntity project_id, element._id, type, source, (err)-> editorController.deleteEntity project_id, element._id, type, source, user_id, (err)->
logger.log project_id:project_id, path:path, "finished processing update to delete entity from tpds" logger.log project_id:project_id, path:path, "finished processing update to delete entity from tpds"
callback() callback()

View file

@ -91,6 +91,7 @@ describe "ProjectStructureChanges", ->
throw error if error? throw error if error?
if res.statusCode < 200 || res.statusCode >= 300 if res.statusCode < 200 || res.statusCode >= 300
throw new Error("failed to add doc #{res.statusCode}") throw new Error("failed to add doc #{res.statusCode}")
@example_doc_id = body._id
done() done()
it "should version the doc added", -> it "should version the doc added", ->
@ -162,6 +163,8 @@ describe "ProjectStructureChanges", ->
if res.statusCode < 200 || res.statusCode >= 300 if res.statusCode < 200 || res.statusCode >= 300
throw new Error("failed to upload file #{res.statusCode}") throw new Error("failed to upload file #{res.statusCode}")
@example_file_id = JSON.parse(body).entity_id
updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).fileUpdates updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).fileUpdates
expect(updates.length).to.equal(1) expect(updates.length).to.equal(1)
update = updates[0] update = updates[0]
@ -199,6 +202,120 @@ describe "ProjectStructureChanges", ->
done() done()
describe "moving entities", ->
before (done) ->
@owner.request.post {
uri: "project/#{@example_project_id}/folder",
formData:
name: 'foo'
}, (error, res, body) =>
throw error if error?
@example_folder_id_1 = JSON.parse(body)._id
done()
beforeEach () ->
MockDocUpdaterApi.clearProjectStructureUpdates()
it "should version moving a doc", (done) ->
@owner.request.post {
uri: "project/#{@example_project_id}/Doc/#{@example_doc_id}/move",
json:
folder_id: @example_folder_id_1
}, (error, res, body) =>
throw error if error?
if res.statusCode < 200 || res.statusCode >= 300
throw new Error("failed to move doc #{res.statusCode}")
updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).docUpdates
expect(updates.length).to.equal(1)
update = updates[0]
expect(update.userId).to.equal(@owner._id)
expect(update.pathname).to.equal("/new.tex")
expect(update.newPathname).to.equal("/foo/new.tex")
done()
it "should version moving a file", (done) ->
@owner.request.post {
uri: "project/#{@example_project_id}/File/#{@example_file_id}/move",
json:
folder_id: @example_folder_id_1
}, (error, res, body) =>
throw error if error?
if res.statusCode < 200 || res.statusCode >= 300
throw new Error("failed to move file #{res.statusCode}")
updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).fileUpdates
expect(updates.length).to.equal(1)
update = updates[0]
expect(update.userId).to.equal(@owner._id)
expect(update.pathname).to.equal("/1pixel.png")
expect(update.newPathname).to.equal("/foo/1pixel.png")
done()
it "should version moving a folder", (done) ->
@owner.request.post {
uri: "project/#{@example_project_id}/folder",
formData:
name: 'bar'
}, (error, res, body) =>
throw error if error?
@example_folder_id_2 = JSON.parse(body)._id
@owner.request.post {
uri: "project/#{@example_project_id}/Folder/#{@example_folder_id_1}/move",
json:
folder_id: @example_folder_id_2
}, (error, res, body) =>
throw error if error?
if res.statusCode < 200 || res.statusCode >= 300
throw new Error("failed to move folder #{res.statusCode}")
updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).docUpdates
expect(updates.length).to.equal(1)
update = updates[0]
expect(update.userId).to.equal(@owner._id)
expect(update.pathname).to.equal("/foo/new.tex")
expect(update.newPathname).to.equal("/bar/foo/new.tex")
updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).fileUpdates
expect(updates.length).to.equal(1)
update = updates[0]
expect(update.userId).to.equal(@owner._id)
expect(update.pathname).to.equal("/foo/1pixel.png")
expect(update.newPathname).to.equal("/bar/foo/1pixel.png")
done()
describe "deleting entities", ->
beforeEach () ->
MockDocUpdaterApi.clearProjectStructureUpdates()
it "should version deleting a folder", (done) ->
@owner.request.delete {
uri: "project/#{@example_project_id}/Folder/#{@example_folder_id_2}",
}, (error, res, body) =>
throw error if error?
if res.statusCode < 200 || res.statusCode >= 300
throw new Error("failed to delete folder #{res.statusCode}")
updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).docUpdates
expect(updates.length).to.equal(1)
update = updates[0]
expect(update.userId).to.equal(@owner._id)
expect(update.pathname).to.equal("/bar/foo/new.tex")
expect(update.newPathname).to.equal("")
updates = MockDocUpdaterApi.getProjectStructureUpdates(@example_project_id).fileUpdates
expect(updates.length).to.equal(1)
update = updates[0]
expect(update.userId).to.equal(@owner._id)
expect(update.pathname).to.equal("/bar/foo/1pixel.png")
expect(update.newPathname).to.equal("")
done()
describe "tpds", -> describe "tpds", ->
before (done) -> before (done) ->
@tpds_project_name = "tpds-project-#{new ObjectId().toString()}" @tpds_project_name = "tpds-project-#{new ObjectId().toString()}"
@ -305,3 +422,25 @@ describe "ProjectStructureChanges", ->
done() done()
image_file.pipe(req) image_file.pipe(req)
it "should version deleting a doc", (done) ->
req = @owner.request.delete {
uri: "/user/#{@owner._id}/update/#{@tpds_project_name}/test.tex",
auth:
user: _.keys(Settings.httpAuthUsers)[0]
pass: _.values(Settings.httpAuthUsers)[0]
sendImmediately: true
}, (error, res, body) =>
throw error if error?
if res.statusCode < 200 || res.statusCode >= 300
throw new Error("failed to delete doc #{res.statusCode}")
updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).docUpdates
expect(updates.length).to.equal(1)
update = updates[0]
expect(update.userId).to.equal(@owner._id)
expect(update.pathname).to.equal("/test.tex")
expect(update.newPathname).to.equal("")
done()

View file

@ -33,6 +33,9 @@ module.exports = MockDocUpdaterApi =
@addProjectStructureUpdates(project_id, userId, docUpdates, fileUpdates) @addProjectStructureUpdates(project_id, userId, docUpdates, fileUpdates)
res.sendStatus 200 res.sendStatus 200
app.delete "/project/:project_id/doc/:doc_id", (req, res, next) =>
res.send 204
app.listen 3003, (error) -> app.listen 3003, (error) ->
throw error if error? throw error if error?
.on "error", (error) -> .on "error", (error) ->

View file

@ -23,6 +23,16 @@ module.exports = MockDocStoreApi =
docs = (doc for doc_id, doc of @docs[req.params.project_id]) docs = (doc for doc_id, doc of @docs[req.params.project_id])
res.send JSON.stringify docs res.send JSON.stringify docs
app.delete "/project/:project_id/doc/:doc_id", (req, res, next) =>
{project_id, doc_id} = req.params
if !@docs[project_id]?
res.send 404
else if !@docs[project_id][doc_id]?
res.send 404
else
@docs[project_id][doc_id] = undefined
res.send 204
app.listen 3016, (error) -> app.listen 3016, (error) ->
throw error if error? throw error if error?
.on "error", (error) -> .on "error", (error) ->

View file

@ -478,14 +478,22 @@ describe 'DocumentUpdaterHandler', ->
.should.equal true .should.equal true
done() done()
describe "when a doc has been deleted", -> describe "when an entity has been deleted", ->
it 'should do nothing', (done) -> it 'should end the structure update to the document updater', (done) ->
@docId = new ObjectId() @docId = new ObjectId()
@changes = oldDocs: [ @changes = oldDocs: [
{ path: '/foo', docLines: 'a\nb', doc: _id: @docId } { path: '/foo', docLines: 'a\nb', doc: _id: @docId }
] ]
docUpdates = [
id: @docId.toString(),
pathname: '/foo',
newPathname: ''
]
@handler.updateProjectStructure @project_id, @user_id, @changes, () => @handler.updateProjectStructure @project_id, @user_id, @changes, () =>
@request.post.called.should.equal false @request.post
.calledWith(url: @url, json: {docUpdates, fileUpdates: [], userId: @user_id})
.should.equal true
done() done()

View file

@ -376,58 +376,53 @@ describe "EditorController", ->
err.should.equal "timed out" err.should.equal "timed out"
done() done()
describe "deleteEntity", -> describe "deleteEntity", ->
beforeEach -> beforeEach ->
@LockManager.getLock.callsArgWith(1) @LockManager.getLock.callsArgWith(1)
@LockManager.releaseLock.callsArgWith(1) @LockManager.releaseLock.callsArgWith(1)
@EditorController.deleteEntityWithoutLock = sinon.stub().callsArgWith(4) @EditorController.deleteEntityWithoutLock = sinon.stub().callsArgWith(5)
it "should call deleteEntityWithoutLock", (done)-> it "should call deleteEntityWithoutLock", (done)->
@EditorController.deleteEntity @project_id, @entity_id, @type, @source, => @EditorController.deleteEntity @project_id, @entity_id, @type, @source, @user_id, =>
@EditorController.deleteEntityWithoutLock.calledWith(@project_id, @entity_id, @type, @source).should.equal true @EditorController.deleteEntityWithoutLock
.calledWith(@project_id, @entity_id, @type, @source, @user_id)
.should.equal true
done() done()
it "should take the lock", (done)-> it "should take the lock", (done)->
@EditorController.deleteEntity @project_id, @entity_id, @type, @source, => @EditorController.deleteEntity @project_id, @entity_id, @type, @source, @user_id, =>
@LockManager.getLock.calledWith(@project_id).should.equal true @LockManager.getLock.calledWith(@project_id).should.equal true
done() done()
it "should release the lock", (done)-> it "should release the lock", (done)->
@EditorController.deleteEntity @project_id, @entity_id, @type, @source, (error)=> @EditorController.deleteEntity @project_id, @entity_id, @type, @source, @user_id, (error) =>
@LockManager.releaseLock.calledWith(@project_id).should.equal true @LockManager.releaseLock.calledWith(@project_id).should.equal true
done() done()
it "should error if it can't cat the lock", (done)-> it "should error if it can't cat the lock", (done)->
@LockManager.getLock = sinon.stub().callsArgWith(1, "timed out") @LockManager.getLock = sinon.stub().callsArgWith(1, "timed out")
@EditorController.deleteEntity @project_id, @entity_id, @type, @source, (err)=> @EditorController.deleteEntity @project_id, @entity_id, @type, @source, @user_id, (error) =>
expect(err).to.exist expect(error).to.exist
err.should.equal "timed out" error.should.equal "timed out"
done() done()
describe 'deleteEntityWithoutLock', -> describe 'deleteEntityWithoutLock', ->
beforeEach -> beforeEach (done) ->
@ProjectEntityHandler.deleteEntity = (project_id, entity_id, type, callback)-> callback()
@entity_id = "entity_id_here" @entity_id = "entity_id_here"
@type = "doc" @type = "doc"
@EditorRealTimeController.emitToRoom = sinon.stub() @EditorRealTimeController.emitToRoom = sinon.stub()
@ProjectEntityHandler.deleteEntity = sinon.stub().callsArg(4)
@EditorController.deleteEntityWithoutLock @project_id, @entity_id, @type, @source, @user_id, done
it 'should delete the folder using the project entity handler', (done)-> it 'should delete the folder using the project entity handler', ->
mock = sinon.mock(@ProjectEntityHandler).expects("deleteEntity").withArgs(@project_id, @entity_id, @type).callsArg(3) @ProjectEntityHandler.deleteEntity
.calledWith(@project_id, @entity_id, @type, @user_id)
.should.equal.true
@EditorController.deleteEntityWithoutLock @project_id, @entity_id, @type, @source, -> it 'notify users an entity has been deleted', ->
mock.verify() @EditorRealTimeController.emitToRoom
done() .calledWith(@project_id, "removeEntity", @entity_id, @source)
.should.equal true
it 'notify users an entity has been deleted', (done)->
@EditorController.deleteEntityWithoutLock @project_id, @entity_id, @type, @source, =>
@EditorRealTimeController.emitToRoom
.calledWith(@project_id, "removeEntity", @entity_id, @source)
.should.equal true
done()
describe "getting a list of project paths", -> describe "getting a list of project paths", ->

View file

@ -331,12 +331,12 @@ describe "EditorHttpController", ->
Project_id: @project_id Project_id: @project_id
entity_id: @entity_id = "entity-id-123" entity_id: @entity_id = "entity-id-123"
entity_type: @entity_type = "entity-type" entity_type: @entity_type = "entity-type"
@EditorController.deleteEntity = sinon.stub().callsArg(4) @EditorController.deleteEntity = sinon.stub().callsArg(5)
@EditorHttpController.deleteEntity @req, @res @EditorHttpController.deleteEntity @req, @res
it "should call EditorController.deleteEntity", -> it "should call EditorController.deleteEntity", ->
@EditorController.deleteEntity @EditorController.deleteEntity
.calledWith(@project_id, @entity_id, @entity_type, "editor") .calledWith(@project_id, @entity_id, @entity_type, "editor", @userId)
.should.equal true .should.equal true
it "should send back a success response", -> it "should send back a success response", ->

View file

@ -157,13 +157,13 @@ describe 'ProjectEntityHandler', ->
@ProjectGetter.getProject.callsArgWith(2, null, @project) @ProjectGetter.getProject.callsArgWith(2, null, @project)
@tpdsUpdateSender.deleteEntity = sinon.stub().callsArg(1) @tpdsUpdateSender.deleteEntity = sinon.stub().callsArg(1)
@ProjectEntityHandler._removeElementFromMongoArray = sinon.stub().callsArg(3) @ProjectEntityHandler._removeElementFromMongoArray = sinon.stub().callsArg(3)
@ProjectEntityHandler._cleanUpEntity = sinon.stub().callsArg(3) @ProjectEntityHandler._cleanUpEntity = sinon.stub().callsArg(5)
@path = mongo: "mongo.path", fileSystem: "/file/system/path" @path = mongo: "mongo.path", fileSystem: "/file/system/path"
@projectLocator.findElement = sinon.stub().callsArgWith(1, null, @entity = { _id: entity_id }, @path) @projectLocator.findElement = sinon.stub().callsArgWith(1, null, @entity = { _id: entity_id }, @path)
describe "deleting from Mongo", -> describe "deleting from Mongo", ->
beforeEach (done) -> beforeEach (done) ->
@ProjectEntityHandler.deleteEntity project_id, entity_id, @type = 'file', done @ProjectEntityHandler.deleteEntity project_id, entity_id, @type = 'file', userId, done
it "should retreive the path", -> it "should retreive the path", ->
@projectLocator.findElement.called.should.equal true @projectLocator.findElement.called.should.equal true
@ -182,7 +182,7 @@ describe 'ProjectEntityHandler', ->
it "should clean up the entity from the rest of the system", -> it "should clean up the entity from the rest of the system", ->
@ProjectEntityHandler._cleanUpEntity @ProjectEntityHandler._cleanUpEntity
.calledWith(@project, @entity, @type) .calledWith(@project, @entity, @type, @path.fileSystem, userId)
.should.equal true .should.equal true
describe "_cleanUpEntity", -> describe "_cleanUpEntity", ->
@ -193,7 +193,9 @@ describe 'ProjectEntityHandler', ->
describe "a file", -> describe "a file", ->
beforeEach (done) -> beforeEach (done) ->
@ProjectEntityHandler._cleanUpEntity @project, _id: @entity_id, 'file', done @path = "/file/system/path.png"
@entity = _id: @entity_id
@ProjectEntityHandler._cleanUpEntity @project, @entity, 'file', @path, userId, done
it "should delete the file from FileStoreHandler", -> it "should delete the file from FileStoreHandler", ->
@FileStoreHandler.deleteFile.calledWith(project_id, @entity_id).should.equal true @FileStoreHandler.deleteFile.calledWith(project_id, @entity_id).should.equal true
@ -201,38 +203,56 @@ describe 'ProjectEntityHandler', ->
it "should not attempt to delete from the document updater", -> it "should not attempt to delete from the document updater", ->
@documentUpdaterHandler.deleteDoc.called.should.equal false @documentUpdaterHandler.deleteDoc.called.should.equal false
it "should should send the update to the doc updater", ->
oldFiles = [ file: @entity, path: @path ]
@documentUpdaterHandler.updateProjectStructure
.calledWith(project_id, userId, {oldFiles})
.should.equal true
describe "a doc", -> describe "a doc", ->
beforeEach (done) -> beforeEach (done) ->
@ProjectEntityHandler._cleanUpDoc = sinon.stub().callsArg(2) @path = "/file/system/path.tex"
@ProjectEntityHandler._cleanUpEntity @project, @entity = {_id: @entity_id}, 'doc', done @ProjectEntityHandler._cleanUpDoc = sinon.stub().callsArg(4)
@entity = {_id: @entity_id}
@ProjectEntityHandler._cleanUpEntity @project, @entity, 'doc', @path, userId, done
it "should clean up the doc", -> it "should clean up the doc", ->
@ProjectEntityHandler._cleanUpDoc @ProjectEntityHandler._cleanUpDoc
.calledWith(@project, @entity) .calledWith(@project, @entity, @path, userId)
.should.equal true .should.equal true
describe "a folder", -> describe "a folder", ->
beforeEach (done) -> beforeEach (done) ->
@folder = @folder =
folders: [ folders: [
fileRefs: [ @file1 = {_id: "file-id-1" } ] name: "subfolder"
docs: [ @doc1 = { _id: "doc-id-1" } ] fileRefs: [ @file1 = { _id: "file-id-1", name: "file-name-1"} ]
docs: [ @doc1 = { _id: "doc-id-1", name: "doc-name-1" } ]
folders: [] folders: []
] ]
fileRefs: [ @file2 = { _id: "file-id-2" } ] fileRefs: [ @file2 = { _id: "file-id-2", name: "file-name-2" } ]
docs: [ @doc2 = { _id: "doc-id-2" } ] docs: [ @doc2 = { _id: "doc-id-2", name: "doc-name-2" } ]
@ProjectEntityHandler._cleanUpDoc = sinon.stub().callsArg(2) @ProjectEntityHandler._cleanUpDoc = sinon.stub().callsArg(4)
@ProjectEntityHandler._cleanUpFile = sinon.stub().callsArg(2) @ProjectEntityHandler._cleanUpFile = sinon.stub().callsArg(4)
@ProjectEntityHandler._cleanUpEntity @project, @folder, "folder", done path = "/folder"
@ProjectEntityHandler._cleanUpEntity @project, @folder, "folder", path, userId, done
it "should clean up all sub files", -> it "should clean up all sub files", ->
@ProjectEntityHandler._cleanUpFile.calledWith(@project, @file1).should.equal true @ProjectEntityHandler._cleanUpFile
@ProjectEntityHandler._cleanUpFile.calledWith(@project, @file2).should.equal true .calledWith(@project, @file1, "/folder/subfolder/file-name-1", userId)
.should.equal true
@ProjectEntityHandler._cleanUpFile
.calledWith(@project, @file2, "/folder/file-name-2", userId)
.should.equal true
it "should clean up all sub docs", -> it "should clean up all sub docs", ->
@ProjectEntityHandler._cleanUpDoc.calledWith(@project, @doc1).should.equal true @ProjectEntityHandler._cleanUpDoc
@ProjectEntityHandler._cleanUpDoc.calledWith(@project, @doc2).should.equal true .calledWith(@project, @doc1, "/folder/subfolder/doc-name-1", userId)
.should.equal true
@ProjectEntityHandler._cleanUpDoc
.calledWith(@project, @doc2, "/folder/doc-name-2", userId)
.should.equal true
describe 'moveEntity', -> describe 'moveEntity', ->
beforeEach -> beforeEach ->
@ -1116,6 +1136,7 @@ describe 'ProjectEntityHandler', ->
@doc = @doc =
_id: ObjectId() _id: ObjectId()
name: "test.tex" name: "test.tex"
@path = "/path/to/doc"
@ProjectEntityHandler.unsetRootDoc = sinon.stub().callsArg(1) @ProjectEntityHandler.unsetRootDoc = sinon.stub().callsArg(1)
@ProjectEntityHandler._insertDeletedDocReference = sinon.stub().callsArg(2) @ProjectEntityHandler._insertDeletedDocReference = sinon.stub().callsArg(2)
@documentUpdaterHandler.deleteDoc = sinon.stub().callsArg(2) @documentUpdaterHandler.deleteDoc = sinon.stub().callsArg(2)
@ -1125,7 +1146,7 @@ describe 'ProjectEntityHandler', ->
describe "when the doc is the root doc", -> describe "when the doc is the root doc", ->
beforeEach -> beforeEach ->
@project.rootDoc_id = @doc._id @project.rootDoc_id = @doc._id
@ProjectEntityHandler._cleanUpDoc @project, @doc, @callback @ProjectEntityHandler._cleanUpDoc @project, @doc, @path, userId, @callback
it "should unset the root doc", -> it "should unset the root doc", ->
@ProjectEntityHandler.unsetRootDoc @ProjectEntityHandler.unsetRootDoc
@ -1146,13 +1167,19 @@ describe 'ProjectEntityHandler', ->
.calledWith(project_id, @doc._id.toString()) .calledWith(project_id, @doc._id.toString())
.should.equal true .should.equal true
it "should should send the update to the doc updater", ->
oldDocs = [ doc: @doc, path: @path ]
@documentUpdaterHandler.updateProjectStructure
.calledWith(project_id, userId, {oldDocs})
.should.equal true
it "should call the callback", -> it "should call the callback", ->
@callback.called.should.equal true @callback.called.should.equal true
describe "when the doc is not the root doc", -> describe "when the doc is not the root doc", ->
beforeEach -> beforeEach ->
@project.rootDoc_id = ObjectId() @project.rootDoc_id = ObjectId()
@ProjectEntityHandler._cleanUpDoc @project, @doc, @callback @ProjectEntityHandler._cleanUpDoc @project, @doc, @path, userId, @callback
it "should not unset the root doc", -> it "should not unset the root doc", ->
@ProjectEntityHandler.unsetRootDoc.called.should.equal false @ProjectEntityHandler.unsetRootDoc.called.should.equal false

View file

@ -8,7 +8,7 @@ describe 'TpdsUpdateHandler', ->
beforeEach -> beforeEach ->
@requestQueuer = {} @requestQueuer = {}
@updateMerger = @updateMerger =
deleteUpdate: (user_id, path, source, cb)->cb() deleteUpdate: (user_id, project_id, path, source, cb)->cb()
mergeUpdate:(user_id, project_id, path, update, source, cb)->cb() mergeUpdate:(user_id, project_id, path, update, source, cb)->cb()
@editorController = {} @editorController = {}
@project_id = "dsjajilknaksdn" @project_id = "dsjajilknaksdn"
@ -107,11 +107,13 @@ describe 'TpdsUpdateHandler', ->
it 'should call deleteEntity in the collaberation manager', (done)-> it 'should call deleteEntity in the collaberation manager', (done)->
path = "/delete/this" path = "/delete/this"
update = {} update = {}
@updateMerger.deleteUpdate = sinon.stub().callsArg(3) @updateMerger.deleteUpdate = sinon.stub().callsArg(4)
@handler.deleteUpdate @user_id, @project.name, path, @source, => @handler.deleteUpdate @user_id, @project.name, path, @source, =>
@projectDeleter.markAsDeletedByExternalSource.calledWith(@project._id).should.equal false @projectDeleter.markAsDeletedByExternalSource.calledWith(@project._id).should.equal false
@updateMerger.deleteUpdate.calledWith(@project_id, path, @source).should.equal true @updateMerger.deleteUpdate
.calledWith(@user_id, @project_id, path, @source)
.should.equal true
done() done()
it 'should mark the project as deleted by external source if path is a single slash', (done)-> it 'should mark the project as deleted by external source if path is a single slash', (done)->

View file

@ -145,13 +145,13 @@ describe 'UpdateMerger :', ->
it 'should get the element id', -> it 'should get the element id', ->
@projectLocator.findElementByPath = sinon.spy() @projectLocator.findElementByPath = sinon.spy()
@updateMerger.deleteUpdate @project_id, @path, @source, -> @updateMerger.deleteUpdate @user_id, @project_id, @path, @source, ->
@projectLocator.findElementByPath.calledWith(@project_id, @path).should.equal true @projectLocator.findElementByPath.calledWith(@project_id, @path).should.equal true
it 'should delete the entity in the editor controller with the correct type', (done)-> it 'should delete the entity in the editor controller with the correct type', (done)->
@entity.lines = [] @entity.lines = []
mock = sinon.mock(@editorController).expects("deleteEntity").withArgs(@project_id, @entity_id, @type, @source).callsArg(4) mock = sinon.mock(@editorController).expects("deleteEntity").withArgs(@project_id, @entity_id, @type, @source, @user_id).callsArg(5)
@updateMerger.deleteUpdate @project_id, @path, @source, -> @updateMerger.deleteUpdate @user_id, @project_id, @path, @source, ->
mock.verify() mock.verify()
done() done()