Merge branch 'master' of github.com:sharelatex/web-sharelatex-internal

This commit is contained in:
Shane Kilkelly 2017-11-13 10:45:29 +00:00
commit a0c7295b36
8 changed files with 291 additions and 170 deletions

View file

@ -204,6 +204,48 @@ module.exports = DocumentUpdaterHandler =
logger.error {project_id, doc_id, thread_id}, "doc updater returned a non-success status code: #{res.statusCode}"
callback new Error("doc updater returned a non-success status code: #{res.statusCode}")
updateProjectStructure : (project_id, userId, oldDocs, newDocs, oldFiles, newFiles, callback = (error) ->)->
return callback() if !settings.apis.project_history?.enabled
docUpdates = DocumentUpdaterHandler._getRenameUpdates('doc', oldDocs, newDocs)
fileUpdates = DocumentUpdaterHandler._getRenameUpdates('file', oldFiles, newFiles)
timer = new metrics.Timer("set-document")
url = "#{settings.apis.documentupdater.url}/project/#{project_id}"
body =
url: url
json: { docUpdates, fileUpdates, userId }
return callback() if (docUpdates.length + fileUpdates.length) < 1
request.post body, (error, res, body)->
timer.done()
if error?
logger.error {error, url, project_id}, "error update project structure in doc updater"
callback(error)
else if res.statusCode >= 200 and res.statusCode < 300
logger.error {project_id}, "updated project structure in doc updater"
callback(null)
else
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}")
_getRenameUpdates: (entityType, oldEntities, newEntities) ->
updates = []
for oldEntity in oldEntities
id = oldEntity[entityType]._id
newEntity = _.find newEntities, (newEntity) ->
newEntity[entityType]._id.toString() == id.toString()
if newEntity.path != oldEntity.path
updates.push
id: id
pathname: oldEntity.path
newPathname: newEntity.path
updates
PENDINGUPDATESKEY = "PendingUpdates"
DOCLINESKEY = "doclines"
DOCIDSWITHPENDINGUPDATES = "DocsWithPendingUpdates"

View file

@ -150,11 +150,11 @@ module.exports = EditorController =
logger.log project_id:project_id, "recived message to delete project"
ProjectDeleter.deleteProject project_id, callback
renameEntity: (project_id, entity_id, entityType, newName, callback)->
renameEntity: (project_id, entity_id, entityType, newName, userId, callback)->
newName = sanitize.escape(newName)
Metrics.inc "editor.rename-entity"
logger.log entity_id:entity_id, entity_id:entity_id, entity_id:entity_id, "reciving new name for entity for project"
ProjectEntityHandler.renameEntity project_id, entity_id, entityType, newName, ->
ProjectEntityHandler.renameEntity project_id, entity_id, entityType, newName, userId, (err) ->
if err?
logger.err err:err, project_id:project_id, entity_id:entity_id, entityType:entityType, newName:newName, "error renaming entity"
return callback(err)
@ -162,13 +162,13 @@ module.exports = EditorController =
EditorRealTimeController.emitToRoom project_id, 'reciveEntityRename', entity_id, newName
callback?()
moveEntity: (project_id, entity_id, folder_id, entityType, callback)->
moveEntity: (project_id, entity_id, folder_id, entityType, userId, callback)->
Metrics.inc "editor.move-entity"
LockManager.getLock project_id, (err)->
if err?
logger.err err:err, project_id:project_id, "could not get lock for move entity"
return callback(err)
ProjectEntityHandler.moveEntity project_id, entity_id, folder_id, entityType, =>
ProjectEntityHandler.moveEntity project_id, entity_id, folder_id, entityType, userId, =>
if err?
logger.err err:err, project_id:project_id, entity_id:entity_id, folder_id:folder_id, "error moving entity"
return callback(err)

View file

@ -12,6 +12,7 @@ CollaboratorsHandler = require("../Collaborators/CollaboratorsHandler")
CollaboratorsInviteHandler = require("../Collaborators/CollaboratorsInviteHandler")
PrivilegeLevels = require "../Authorization/PrivilegeLevels"
TokenAccessHandler = require '../TokenAccess/TokenAccessHandler'
AuthenticationController = require "../Authentication/AuthenticationController"
module.exports = EditorHttpController =
joinProject: (req, res, next) ->
@ -112,9 +113,10 @@ module.exports = EditorHttpController =
entity_id = req.params.entity_id
entity_type = req.params.entity_type
name = req.body.name
user_id = AuthenticationController.getLoggedInUserId(req)
if !EditorHttpController._nameIsAcceptableLength(name)
return res.sendStatus 400
EditorController.renameEntity project_id, entity_id, entity_type, name, (error) ->
EditorController.renameEntity project_id, entity_id, entity_type, name, user_id, (error) ->
return next(error) if error?
res.sendStatus 204
@ -123,7 +125,8 @@ module.exports = EditorHttpController =
entity_id = req.params.entity_id
entity_type = req.params.entity_type
folder_id = req.body.folder_id
EditorController.moveEntity project_id, entity_id, folder_id, entity_type, (error) ->
user_id = AuthenticationController.getLoggedInUserId(req)
EditorController.moveEntity project_id, entity_id, folder_id, entity_type, user_id, (error) ->
return next(error) if error?
res.sendStatus 204

View file

@ -355,47 +355,50 @@ module.exports = ProjectEntityHandler =
else
callback()
moveEntity: (project_id, entity_id, folder_id, entityType, callback = (error) ->)->
moveEntity: (project_id, entity_id, destFolderId, entityType, userId, callback = (error) ->)->
self = @
destinationFolder_id = folder_id
logger.log entityType:entityType, entity_id:entity_id, project_id:project_id, folder_id:folder_id, "moving entity"
logger.log {entityType, entity_id, project_id, destFolderId}, "moving entity"
if !entityType?
logger.err err: "No entityType set", project_id: project_id, entity_id: entity_id
logger.err {err: "No entityType set", project_id, entity_id}
return callback("No entityType set")
entityType = entityType.toLowerCase()
ProjectGetter.getProject project_id, {rootFolder:true, name:true}, (err, project)=>
return callback(err) if err?
projectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (err, entity, path)->
projectLocator.findElement {project, element_id: entity_id, type: entityType}, (err, entity, entityPath)->
return callback(err) if err?
if entityType.match(/folder/)
ensureFolderIsNotMovedIntoChild = (callback = (error) ->) ->
projectLocator.findElement {project: project, element_id: folder_id, type:"folder"}, (err, destEntity, destPath) ->
logger.log destPath: destPath.fileSystem, folderPath: path.fileSystem, "checking folder is not moving into child folder"
if (destPath.fileSystem.slice(0, path.fileSystem.length) == path.fileSystem)
logger.log "destination is a child folder, aborting"
callback(new Error("destination folder is a child folder of me"))
else
callback()
else
ensureFolderIsNotMovedIntoChild = (callback = () ->) -> callback()
ensureFolderIsNotMovedIntoChild (error) ->
self._checkValidMove project, entityType, entityPath, destFolderId, (error) ->
return callback(error) if error?
self._removeElementFromMongoArray Project, project_id, path.mongo, (err)->
return callback(err) if err?
# We've updated the project structure by removing the element, so must refresh it.
ProjectGetter.getProject project_id, {rootFolder:true, name:true}, (err, project)=>
self.getAllEntitiesFromProject project, (error, oldDocs, oldFiles) =>
return callback(error) if error?
self._removeElementFromMongoArray Project, project_id, entityPath.mongo, (err, newProject)->
return callback(err) if err?
ProjectEntityHandler._putElement project, destinationFolder_id, entity, entityType, (err, result)->
self._putElement newProject, destFolderId, entity, entityType, (err, result, newProject)->
return callback(err) if err?
opts =
project_id:project_id
project_name:project.name
startPath:path.fileSystem
endPath:result.path.fileSystem,
rev:entity.rev
tpdsUpdateSender.moveEntity opts, callback
project_id: project_id
project_name: project.name
startPath: entityPath.fileSystem
endPath: result.path.fileSystem,
rev: entity.rev
tpdsUpdateSender.moveEntity opts
self.getAllEntitiesFromProject newProject, (error, newDocs, newFiles
) =>
return callback(error) if error?
documentUpdaterHandler = require('../../Features/DocumentUpdater/DocumentUpdaterHandler')
documentUpdaterHandler.updateProjectStructure project_id, userId, oldDocs, newDocs, oldFiles, newFiles, callback
_checkValidMove: (project, entityType, entityPath, destFolderId, callback = (error) ->) ->
return callback() if !entityType.match(/folder/)
projectLocator.findElement { project, element_id: destFolderId, type:"folder"}, (err, destEntity, destFolderPath) ->
return callback(err) if err?
logger.log destFolderPath: destFolderPath.fileSystem, folderPath: entityPath.fileSystem, "checking folder is not moving into child folder"
isNestedFolder = destFolderPath.fileSystem.slice(0, entityPath.fileSystem.length) == entityPath.fileSystem
if isNestedFolder
callback(new Error("destination folder is a child folder of me"))
else
callback()
deleteEntity: (project_id, entity_id, entityType, callback = (error) ->)->
self = @
@ -417,25 +420,30 @@ module.exports = ProjectEntityHandler =
callback null
renameEntity: (project_id, entity_id, entityType, newName, callback)->
renameEntity: (project_id, entity_id, entityType, newName, userId, callback)->
logger.log(entity_id: entity_id, project_id: project_id, ('renaming '+entityType))
if !entityType?
logger.err err: "No entityType set", project_id: project_id, entity_id: entity_id
return callback("No entityType set")
entityType = entityType.toLowerCase()
ProjectGetter.getProject project_id, {rootFolder:true, name:true}, (err, project)=>
projectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (err, entity, path, folder)=>
if err?
return callback err
conditons = {_id:project_id}
update = "$set":{}
namePath = path.mongo+".name"
update["$set"][namePath] = newName
endPath = path.fileSystem.replace(entity.name, newName)
tpdsUpdateSender.moveEntity({project_id:project_id, startPath:path.fileSystem, endPath:endPath, project_name:project.name, rev:entity.rev})
Project.update conditons, update, {}, (err)->
if callback?
callback err
ProjectGetter.getProject project_id, {rootFolder:true, name:true}, (error, project)=>
return callback(error) if error?
ProjectEntityHandler.getAllEntitiesFromProject project, (error, oldDocs, oldFiles) =>
return callback(error) if error?
projectLocator.findElement {project:project, element_id:entity_id, type:entityType}, (error, entity, path)=>
return callback(error) if error?
endPath = path.fileSystem.replace(entity.name, newName)
conditions = {_id:project_id}
update = "$set":{}
namePath = path.mongo+".name"
update["$set"][namePath] = newName
tpdsUpdateSender.moveEntity({project_id:project_id, startPath:path.fileSystem, endPath:endPath, project_name:project.name, rev:entity.rev})
Project.findOneAndUpdate conditions, update, { "new": true}, (error, newProject) ->
return callback(error) if error?
ProjectEntityHandler.getAllEntitiesFromProject newProject, (error, newDocs, newFiles) =>
return callback(error) if error?
documentUpdaterHandler = require('../../Features/DocumentUpdater/DocumentUpdaterHandler')
documentUpdaterHandler.updateProjectStructure project_id, userId, oldDocs, newDocs, oldFiles, newFiles, callback
_cleanUpEntity: (project, entity, entityType, callback = (error) ->) ->
if(entityType.indexOf("file") != -1)
@ -487,17 +495,15 @@ module.exports = ProjectEntityHandler =
async.series jobs, callback
_removeElementFromMongoArray : (model, model_id, path, callback)->
conditons = {_id:model_id}
_removeElementFromMongoArray : (model, model_id, path, callback = (err, project) ->)->
conditions = {_id:model_id}
update = {"$unset":{}}
update["$unset"][path] = 1
model.update conditons, update, {}, (err)->
model.update conditions, update, {}, (err)->
pullUpdate = {"$pull":{}}
nonArrayPath = path.slice(0, path.lastIndexOf("."))
pullUpdate["$pull"][nonArrayPath] = null
model.update conditons, pullUpdate, {}, (err)->
if callback?
callback(err)
model.findOneAndUpdate conditions, pullUpdate, {"new": true}, callback
_insertDeletedDocReference: (project_id, doc, callback = (error) ->) ->
Project.update {
@ -534,8 +540,7 @@ module.exports = ProjectEntityHandler =
countFolder project.rootFolder[0], callback
_putElement: (project, folder_id, element, type, callback = (err, path)->)->
_putElement: (project, folder_id, element, type, callback = (err, path, project)->)->
sanitizeTypeOfElement = (elementType)->
lastChar = elementType.slice -1
if lastChar != "s"
@ -576,11 +581,11 @@ module.exports = ProjectEntityHandler =
update = "$push":{}
update["$push"][mongopath] = element
logger.log project_id: project._id, element_id: element._id, fileType: type, folder_id: folder_id, mongopath:mongopath, "adding element to project"
Project.update conditions, update, {}, (err)->
Project.findOneAndUpdate conditions, update, {"new": true}, (err, project)->
if err?
logger.err err: err, project_id: project._id, 'error saving in putElement project'
return callback(err)
callback(err, {path:newPath})
callback(err, {path:newPath}, project)
confirmFolder = (project, folder_id, callback)->

View file

@ -6,6 +6,7 @@ SandboxedModule = require('sandboxed-module')
assert = require('chai').assert
path = require 'path'
_ = require 'underscore'
ObjectId = require("mongojs").ObjectId;
modulePath = path.join __dirname, '../../../../app/js/Features/DocumentUpdater/DocumentUpdaterHandler'
describe 'DocumentUpdaterHandler', ->
@ -20,8 +21,14 @@ describe 'DocumentUpdaterHandler', ->
@request = {}
@projectEntityHandler = {}
@settings =
apis : documentupdater: url : "http://something.com"
@settings =
apis:
documentupdater:
url : "http://document_updater.example.com"
project_history:
url: "http://project_history.example.com"
@callback = sinon.stub()
@handler = SandboxedModule.require modulePath, requires:
'request': defaults:=> return @request
'settings-sharelatex':@settings
@ -29,14 +36,11 @@ describe 'DocumentUpdaterHandler', ->
'../Project/ProjectEntityHandler':@projectEntityHandler
"../../models/Project": Project: @Project={}
'../../Features/Project/ProjectLocator':{}
"metrics-sharelatex":
"metrics-sharelatex":
Timer:->
done:->
describe 'flushProjectToMongo', ->
beforeEach ->
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 204}, "")
@ -68,9 +72,6 @@ describe 'DocumentUpdaterHandler', ->
.should.equal true
describe 'flushProjectToMongoAndDelete', ->
beforeEach ->
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@request.del = sinon.stub().callsArgWith(1, null, {statusCode: 204}, "")
@ -102,9 +103,6 @@ describe 'DocumentUpdaterHandler', ->
.should.equal true
describe 'flushDocToMongo', ->
beforeEach ->
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 204}, "")
@ -136,9 +134,6 @@ describe 'DocumentUpdaterHandler', ->
.should.equal true
describe "deleteDoc", ->
beforeEach ->
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@request.del = sinon.stub().callsArgWith(1, null, {statusCode: 204}, "")
@ -171,7 +166,6 @@ describe 'DocumentUpdaterHandler', ->
describe "setDocument", ->
beforeEach ->
@callback = sinon.stub()
@source = "dropbox"
describe "successfully", ->
@ -213,9 +207,6 @@ describe 'DocumentUpdaterHandler', ->
.should.equal true
describe "getDocument", ->
beforeEach ->
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@body = JSON.stringify
@ -254,7 +245,6 @@ describe 'DocumentUpdaterHandler', ->
describe "getProjectDocsIfMatch", ->
beforeEach ->
@callback = sinon.stub()
@project_state_hash = "1234567890abcdef"
describe "successfully", ->
@ -295,9 +285,6 @@ describe 'DocumentUpdaterHandler', ->
describe "clearProjectState", ->
beforeEach ->
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 200})
@ -332,7 +319,6 @@ describe 'DocumentUpdaterHandler', ->
describe "acceptChanges", ->
beforeEach ->
@change_id = "mock-change-id-1"
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@ -370,7 +356,6 @@ describe 'DocumentUpdaterHandler', ->
describe "deleteThread", ->
beforeEach ->
@thread_id = "mock-thread-id-1"
@callback = sinon.stub()
describe "successfully", ->
beforeEach ->
@ -401,3 +386,54 @@ describe 'DocumentUpdaterHandler', ->
@callback
.calledWith(new Error("doc updater returned failure status code: 500"))
.should.equal true
describe "updateProjectStructure ", ->
beforeEach ->
@user_id = 1234
@docIdA = new ObjectId()
@docIdB = new ObjectId()
@oldDocs = [
{ path: '/old_a', doc: _id: @docIdA }
{ path: '/old_b', doc: _id: @docIdB }
]
# create new instances of the same ObjectIds so that == doens't pass
@newDocs = [
{ path: '/old_a', doc: _id: new ObjectId(@docIdA.toString()) }
{ path: '/new_b', doc: _id: new ObjectId(@docIdB.toString()) }
]
@oldFiles = []
@newFiles = []
describe "with project history disabled", ->
beforeEach ->
@settings.apis.project_history.enabled = false
@request.post = sinon.stub()
@handler.updateProjectStructure @project_id, @user_id, @oldDocs, @newDocs, @oldFiles, @newFiles, @callback
it 'does not make a web request', ->
@request.post.called.should.equal false
it 'calls the callback', ->
@callback.called.should.equal true
describe "with project history enabled", ->
beforeEach ->
@settings.apis.project_history.enabled = true
@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 204}, "")
@handler.updateProjectStructure @project_id, @user_id, @oldDocs, @newDocs, @oldFiles, @newFiles, @callback
it 'should send the structure update to the document updater', ->
docUpdates = [
id: @docIdB,
pathname: "/old_b"
newPathname: "/new_b"
]
url = "#{@settings.apis.documentupdater.url}/project/#{@project_id}"
@request.post
.calledWith(url: url, json: {docUpdates, fileUpdates: [], userId: @user_id})
.should.equal true
it "should call the callback with no error", ->
@callback.calledWith(null).should.equal true

View file

@ -489,55 +489,51 @@ describe "EditorController", ->
describe "renameEntity", ->
beforeEach ->
@err = "errro"
@entity_id = "entity_id_here"
@entityType = "doc"
@newName = "bobsfile.tex"
@ProjectEntityHandler.renameEntity = sinon.stub().callsArgWith(4, @err)
@ProjectEntityHandler.renameEntity = sinon.stub().callsArg(5)
@EditorRealTimeController.emitToRoom = sinon.stub()
it "should call the project handler", (done)->
@EditorController.renameEntity @project_id, @entity_id, @entityType, @newName, =>
@ProjectEntityHandler.renameEntity.calledWith(@project_id, @entity_id, @entityType, @newName).should.equal true
@EditorController.renameEntity @project_id, @entity_id, @entityType, @newName, @user_id, =>
@ProjectEntityHandler.renameEntity.calledWith(@project_id, @entity_id, @entityType, @newName, @user_id).should.equal true
done()
it "should emit the update to the room", (done)->
@EditorController.renameEntity @project_id, @entity_id, @entityType, @newName, =>
@EditorController.renameEntity @project_id, @entity_id, @entityType, @newName, @user_id, =>
@EditorRealTimeController.emitToRoom.calledWith(@project_id, 'reciveEntityRename', @entity_id, @newName).should.equal true
done()
describe "moveEntity", ->
beforeEach ->
@err = "errro"
@entity_id = "entity_id_here"
@entityType = "doc"
@folder_id = "313dasd21dasdsa"
@ProjectEntityHandler.moveEntity = sinon.stub().callsArgWith(4, @err)
@ProjectEntityHandler.moveEntity = sinon.stub().callsArg(5)
@EditorRealTimeController.emitToRoom = sinon.stub()
@LockManager.releaseLock.callsArgWith(1)
@LockManager.getLock.callsArgWith(1)
it "should call the ProjectEntityHandler", (done)->
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, =>
@ProjectEntityHandler.moveEntity.calledWith(@project_id, @entity_id, @folder_id, @entityType).should.equal true
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, @user_id, =>
@ProjectEntityHandler.moveEntity.calledWith(@project_id, @entity_id, @folder_id, @entityType, @user_id).should.equal true
done()
it "should take the lock", (done)->
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, =>
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, @user_id, =>
@LockManager.getLock.calledWith(@project_id).should.equal true
done()
it "should release the lock", (done)->
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, =>
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, @user_id, =>
@LockManager.releaseLock.calledWith(@project_id).should.equal true
done()
it "should emit the update to the room", (done)->
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, =>
@EditorController.moveEntity @project_id, @entity_id, @folder_id, @entityType, @user_id, =>
@EditorRealTimeController.emitToRoom.calledWith(@project_id, 'reciveEntityMove', @entity_id, @folder_id).should.equal true
done()

View file

@ -19,11 +19,14 @@ describe "EditorHttpController", ->
"../Collaborators/CollaboratorsHandler": @CollaboratorsHandler = {}
"../Collaborators/CollaboratorsInviteHandler": @CollaboratorsInviteHandler = {}
"../TokenAccess/TokenAccessHandler": @TokenAccessHandler = {}
"../Authentication/AuthenticationController": @AuthenticationController = {}
@project_id = "mock-project-id"
@doc_id = "mock-doc-id"
@user_id = "mock-user-id"
@parent_folder_id = "mock-folder-id"
@userId = 1234
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId)
@req = {}
@res =
send: sinon.stub()
@ -264,12 +267,12 @@ describe "EditorHttpController", ->
entity_type: @entity_type = "entity-type"
@req.body =
name: @name = "new-name"
@EditorController.renameEntity = sinon.stub().callsArg(4)
@EditorController.renameEntity = sinon.stub().callsArg(5)
@EditorHttpController.renameEntity @req, @res
it "should call EditorController.renameEntity", ->
@EditorController.renameEntity
.calledWith(@project_id, @entity_id, @entity_type, @name)
.calledWith(@project_id, @entity_id, @entity_type, @name, @userId)
.should.equal true
it "should send back a success response", ->
@ -290,7 +293,6 @@ describe "EditorHttpController", ->
@res.sendStatus.calledWith(400).should.equal true
describe "rename entity with 0 length name", ->
beforeEach ->
@req.params =
Project_id: @project_id
@ -304,7 +306,6 @@ describe "EditorHttpController", ->
it "should send back a bad request status code", ->
@res.sendStatus.calledWith(400).should.equal true
describe "moveEntity", ->
beforeEach ->
@req.params =
@ -313,12 +314,12 @@ describe "EditorHttpController", ->
entity_type: @entity_type = "entity-type"
@req.body =
folder_id: @folder_id = "folder-id-123"
@EditorController.moveEntity = sinon.stub().callsArg(4)
@EditorController.moveEntity = sinon.stub().callsArg(5)
@EditorHttpController.moveEntity @req, @res
it "should call EditorController.moveEntity", ->
@EditorController.moveEntity
.calledWith(@project_id, @entity_id, @folder_id, @entity_type)
.calledWith(@project_id, @entity_id, @folder_id, @entity_type, @userId)
.should.equal true
it "should send back a success response", ->

View file

@ -14,6 +14,7 @@ describe 'ProjectEntityHandler', ->
doc_id = '4eecb1c1bffa66588e0000a2'
folder_id = "4eecaffcbffa66588e000008"
rootFolderId = "4eecaffcbffa66588e000007"
userId = 1234
beforeEach ->
@FileStoreHandler =
@ -235,10 +236,18 @@ describe 'ProjectEntityHandler', ->
@pathAfterMove = {
fileSystem: "/somewhere/else.txt"
}
@ProjectEntityHandler._removeElementFromMongoArray = sinon.stub().callsArg(3)
@ProjectEntityHandler._removeElementFromMongoArray = sinon.stub().callsArgWith(3, null, @project)
@ProjectEntityHandler._putElement = sinon.stub().callsArgWith(4, null, path: @pathAfterMove)
@ProjectGetter.getProject.callsArgWith(2, null, @project)
@tpdsUpdateSender.moveEntity = sinon.stub().callsArg(1)
@tpdsUpdateSender.moveEntity = sinon.stub()
@documentUpdaterHandler.updateProjectStructure = sinon.stub().callsArg(6)
@ProjectEntityHandler.getAllEntitiesFromProject = sinon.stub()
@ProjectEntityHandler.getAllEntitiesFromProject
.onFirstCall()
.callsArgWith(1, null, @oldDocs = ['old-doc'], @oldFiles = ['old-file'])
@ProjectEntityHandler.getAllEntitiesFromProject
.onSecondCall()
.callsArgWith(1, null, @newDocs = ['new-doc'], @newFiles = ['new-file'])
describe "moving a doc", ->
beforeEach (done) ->
@ -246,14 +255,26 @@ describe 'ProjectEntityHandler', ->
@doc = {lines:["1234","312343d"], rev: "1234"}
@path = {
mongo:"folders[0]"
fileSystem:"/somewhere.txt"
fileSystem:"/old_folder/somewhere.txt"
}
@projectLocator.findElement = sinon.stub().callsArgWith(1, null, @doc, @path)
@ProjectEntityHandler.moveEntity project_id, @docId, folder_id, "docs", done
@destFolder = { name: "folder" }
@destFolderPath = {
mongo: "folders[0]"
fileSystem: "/dest_folder"
}
@projectLocator.findElement = sinon.stub()
@projectLocator.findElement.withArgs({project: @project, element_id: @docId, type: 'docs'})
.callsArgWith(1, null, @doc, @path)
@ProjectEntityHandler.moveEntity project_id, @docId, folder_id, "docs", userId, done
it 'should find the project then element', ->
it 'should find the doc to move', ->
@projectLocator.findElement.calledWith({element_id: @docId, type: "docs", project: @project }).should.equal true
it "should should send the update to the doc updater", ->
@documentUpdaterHandler.updateProjectStructure
.calledWith(project_id, userId, @oldDocs, @newDocs, @oldFiles, @newFiles)
.should.equal true
it 'should remove the element from its current position', ->
@ProjectEntityHandler._removeElementFromMongoArray
.calledWith(@ProjectModel, project_id, @path.mongo ).should.equal true
@ -278,28 +299,19 @@ describe 'ProjectEntityHandler', ->
@move_to_folder_id = "folder-to-move-to"
@folder = { name: "folder" }
@folder_to_move_to = { name: "folder to move to" }
@path = {
mongo: "folders[0]"
fileSystem: "/somewhere.txt"
}
@pathToMoveTo = {
mongo: "folders[0]"
fileSystem: "/somewhere.txt"
}
@projectLocator.findElement = (options, callback) =>
if options.element_id == @folder_id
callback(null, @folder, @path)
else if options.element_id == @move_to_folder_id
callback(null, @folder_to_move_to, @pathToMoveTo)
else
console.log "UNKNOWN ID", options
sinon.spy @projectLocator, "findElement"
@path = { mongo:"folders[0]" }
@pathToMoveTo = { mongo: "folders[0]" }
@projectLocator.findElement = sinon.stub()
@projectLocator.findElement.withArgs({project: @project, element_id: @folder_id, type: 'folder'})
.callsArgWith(1, null, @folder, @path)
@projectLocator.findElement.withArgs({project: @project, element_id: @move_to_folder_id, type: 'folder'})
.callsArgWith(1, null, @folder_to_move_to, @pathToMoveTo)
describe "when the destination folder is outside the moving folder", ->
beforeEach (done) ->
@path.fileSystem = "/one/directory"
@pathToMoveTo.fileSystem = "/another/directory"
@ProjectEntityHandler.moveEntity project_id, @folder_id, @move_to_folder_id, "folder", done
@path.fileSystem = "/one/src_dir"
@pathToMoveTo.fileSystem = "/two/dest_dir"
@ProjectEntityHandler.moveEntity project_id, @folder_id, @move_to_folder_id, "folder", userId, done
it 'should find the project then element', ->
@projectLocator.findElement
@ -310,6 +322,11 @@ describe 'ProjectEntityHandler', ->
})
.should.equal true
it "should should send the update to the doc updater", ->
@documentUpdaterHandler.updateProjectStructure
.calledWith(project_id, userId, @oldDocs, @newDocs, @oldFiles, @newFiles)
.should.equal true
it 'should remove the element from its current position', ->
@ProjectEntityHandler._removeElementFromMongoArray
.calledWith(
@ -345,9 +362,9 @@ describe 'ProjectEntityHandler', ->
@path.fileSystem = "/one/two"
@pathToMoveTo.fileSystem = "/one/two/three"
@callback = sinon.stub()
@ProjectEntityHandler.moveEntity project_id, @folder_id, @move_to_folder_id, "folder", @callback
@ProjectEntityHandler.moveEntity project_id, @folder_id, @move_to_folder_id, "folder", userId, @callback
it 'should find the folder we are moving to as well element', ->
it 'should find the folder we are moving to element', ->
@projectLocator.findElement
.calledWith({
element_id: @move_to_folder_id,
@ -361,23 +378,29 @@ describe 'ProjectEntityHandler', ->
.calledWith(new Error("destination folder is a child folder of me"))
.should.equal true
describe 'removing element from mongo array', ->
it 'should call update with log the path', (done)->
mongoPath = "folders[0].folders[5]"
id = "12344"
firstUpdate = true
model =
update: (conditions, update, opts, callback)->
if firstUpdate
conditions._id.should.equal id
update.$unset[mongoPath].should.equal 1
firstUpdate = false
callback()
else
conditions._id.should.equal id
assert.deepEqual update, { '$pull': { 'folders[0]': null } }
done()
@ProjectEntityHandler._removeElementFromMongoArray model, id, mongoPath, ->
describe '_removeElementFromMongoArray ', ->
beforeEach ->
@mongoPath = "folders[0].folders[5]"
@id = "12344"
@project = 'a project'
@ProjectModel.update = sinon.stub().callsArg(3)
@ProjectModel.findOneAndUpdate = sinon.stub().callsArgWith(3, null, @project)
@ProjectEntityHandler._removeElementFromMongoArray @ProjectModel, @id, @mongoPath, @callback
it 'should unset', ->
update = { '$unset': { } }
update['$unset'][@mongoPath] = 1
@ProjectModel.update
.calledWith({ _id: @id }, update, {})
.should.equal true
it 'should pull', ->
@ProjectModel.findOneAndUpdate
.calledWith({ _id: @id }, { '$pull': { 'folders[0]': null } }, {'new': true})
.should.equal true
it 'should call the callback', ->
@callback.calledWith(null, @project).should.equal true
describe 'getDoc', ->
beforeEach ->
@ -976,20 +999,36 @@ describe 'ProjectEntityHandler', ->
@entityType = "doc"
@newName = "new.tex"
@path = mongo: "mongo.path", fileSystem: "/file/system/old.tex"
@projectLocator.findElement = sinon.stub().callsArgWith(1, null, @entity = { _id: @entity_id, name:"old.tex", rev:4 }, @path)
@ProjectModel.update = sinon.stub().callsArgWith(3)
@tpdsUpdateSender.moveEntity = sinon.stub()
@ProjectGetter.getProject.callsArgWith(2, null, @project)
@ProjectEntityHandler.getAllEntitiesFromProject = sinon.stub()
@ProjectEntityHandler.getAllEntitiesFromProject
.onFirstCall()
.callsArgWith(1, null, @oldDocs = ['old-doc'], @oldFiles = ['old-file'])
@ProjectEntityHandler.getAllEntitiesFromProject
.onSecondCall()
.callsArgWith(1, null, @newDocs = ['new-doc'], @newFiles = ['new-file'])
@projectLocator.findElement = sinon.stub().callsArgWith(1, null, @entity = { _id: @entity_id, name:"old.tex", rev:4 }, @path)
@tpdsUpdateSender.moveEntity = sinon.stub()
@ProjectModel.findOneAndUpdate = sinon.stub().callsArgWith(3, null, @project)
@documentUpdaterHandler.updateProjectStructure = sinon.stub().callsArg(6)
it "should should send the old and new project structure to the doc updater", (done) ->
@ProjectEntityHandler.renameEntity project_id, @entity_id, @entityType, @newName, userId, =>
@documentUpdaterHandler.updateProjectStructure
.calledWith(project_id, userId, @oldDocs, @newDocs, @oldFiles, @newFiles)
.should.equal true
done()
it "should update the name in mongo", (done)->
@ProjectEntityHandler.renameEntity @project_id, @entity_id, @entityType, @newName, =>
@ProjectModel.update.calledWith({_id : @project_id}, {"$set":{"mongo.path.name":@newName}}).should.equal true
@ProjectEntityHandler.renameEntity project_id, @entity_id, @entityType, @newName, userId, =>
@ProjectModel.findOneAndUpdate.calledWith({_id: project_id}, {"$set":{"mongo.path.name": @newName}}, {"new": true}).should.equal true
done()
it "should send the update to the tpds", (done)->
@ProjectEntityHandler.renameEntity @project_id, @entity_id, @entityType, @newName, =>
@tpdsUpdateSender.moveEntity.calledWith({project_id:@project_id, startPath:@path.fileSystem, endPath:"/file/system/new.tex", project_name:@project.name, rev:4}).should.equal true
@ProjectEntityHandler.renameEntity project_id, @entity_id, @entityType, @newName, userId, =>
@tpdsUpdateSender.moveEntity.calledWith({project_id:project_id, startPath:@path.fileSystem, endPath:"/file/system/new.tex", project_name:@project.name, rev:4}).should.equal true
done()
describe "_insertDeletedDocReference", ->
@ -1085,27 +1124,28 @@ describe 'ProjectEntityHandler', ->
@path = mongo: "mongo.path", fileSystem: "/file/system/old.tex"
@ProjectGetter.getProject.callsArgWith(2, null, @project)
@projectLocator.findElement.callsArgWith(1, null, @folder, @path)
@ProjectUpdateStub.callsArgWith(3)
@ProjectModel.findOneAndUpdate = sinon.stub().callsArgWith(3, null, @project)
describe "updating the project", ->
it "should use the correct mongo path", (done)->
@ProjectEntityHandler._putElement @project, @folder._id, @doc, "docs", (err)=>
@ProjectModel.update.args[0][0]._id.should.equal @project._id
assert.deepEqual @ProjectModel.update.args[0][1].$push[@path.mongo+".docs"], @doc
@ProjectModel.findOneAndUpdate.args[0][0]._id.should.equal @project._id
assert.deepEqual @ProjectModel.findOneAndUpdate.args[0][1].$push[@path.mongo+".docs"], @doc
done()
it "should return the project in the callback", (done)->
@ProjectEntityHandler._putElement @project, @folder._id, @doc, "docs", (err, path, project)=>
expect(project).to.equal @project
done()
it "should add an s onto the type if not included", (done)->
@ProjectEntityHandler._putElement @project, @folder._id, @doc, "doc", (err)=>
assert.deepEqual @ProjectModel.update.args[0][1].$push[@path.mongo+".docs"], @doc
assert.deepEqual @ProjectModel.findOneAndUpdate.args[0][1].$push[@path.mongo+".docs"], @doc
done()
it "should not call update if elemenet is null", (done)->
it "should not call update if element is null", (done)->
@ProjectEntityHandler._putElement @project, @folder._id, null, "doc", (err)=>
@ProjectModel.update.called.should.equal false
@ProjectModel.findOneAndUpdate.called.should.equal false
done()
it "should default to root folder insert", (done)->
@ -1117,11 +1157,9 @@ describe 'ProjectEntityHandler', ->
doc =
name:"something"
@ProjectEntityHandler._putElement @project, @folder._id, doc, "doc", (err)=>
@ProjectModel.update.called.should.equal false
@ProjectModel.findOneAndUpdate.called.should.equal false
done()
describe "_countElements", ->
beforeEach ->