diff --git a/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee b/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee index c5a74fea28..04c11c6419 100644 --- a/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee +++ b/services/web/app/coffee/Features/DocumentUpdater/DocumentUpdaterHandler.coffee @@ -205,29 +205,32 @@ module.exports = DocumentUpdaterHandler = updateProjectStructure : (project_id, userId, changes, callback = (error) ->)-> return callback() if !settings.apis.project_history?.sendProjectStructureOps + Project.findOne {_id: project_id}, {version:true}, (err, currentProject) -> + return callback(err) if err? + return callback new Error("project not found") if !currentProject? - docUpdates = DocumentUpdaterHandler._getUpdates('doc', changes.oldDocs, changes.newDocs) - fileUpdates = DocumentUpdaterHandler._getUpdates('file', changes.oldFiles, changes.newFiles) + docUpdates = DocumentUpdaterHandler._getUpdates('doc', changes.oldDocs, changes.newDocs) + fileUpdates = DocumentUpdaterHandler._getUpdates('file', changes.oldFiles, changes.newFiles) - timer = new metrics.Timer("set-document") - url = "#{settings.apis.documentupdater.url}/project/#{project_id}" - body = - url: url - json: { docUpdates, fileUpdates, userId } + timer = new metrics.Timer("set-document") + url = "#{settings.apis.documentupdater.url}/project/#{project_id}" + body = + url: url + json: { docUpdates, fileUpdates, userId, version: currentProject.version } - return callback() if (docUpdates.length + fileUpdates.length) < 1 + 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.log {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}") + 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.log {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}") _getUpdates: (entityType, oldEntities, newEntities) -> oldEntities ||= [] diff --git a/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee index eaf92df92a..47a8d0c553 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee @@ -56,6 +56,11 @@ module.exports = ProjectEntityMongoUpdateHandler = self = conditions = _id:project._id inc = {} inc["#{path.mongo}.rev"] = 1 + # currently we do not need to increment the project version number for changes that are replacements + # but when we make switch to having immutable files the replace operation will add a new file, and + # this will require a version increase. We will start incrementing the project version now as it does + # no harm and will help to test it. + inc['version'] = 1 set = {} set["#{path.mongo}.created"] = new Date() update = @@ -144,9 +149,11 @@ module.exports = ProjectEntityMongoUpdateHandler = self = return callback(error) if error? endPath = path.join(path.dirname(entPath.fileSystem), newName) conditions = {_id:project_id} - update = "$set":{} + update = "$set":{}, "$inc":{} namePath = entPath.mongo+".name" update["$set"][namePath] = newName + # we need to increment the project version number for any structure change + update["$inc"]["version"] = 1 Project.findOneAndUpdate conditions, update, { "new": true}, (error, newProject) -> return callback(error) if error? ProjectEntityHandler.getAllEntitiesFromProject newProject, (error, newDocs, newFiles) => @@ -174,9 +181,11 @@ module.exports = ProjectEntityMongoUpdateHandler = self = update = {"$unset":{}} update["$unset"][path] = 1 model.update conditions, update, {}, (err)-> - pullUpdate = {"$pull":{}} + pullUpdate = {"$pull":{}, "$inc":{}} nonArrayPath = path.slice(0, path.lastIndexOf(".")) pullUpdate["$pull"][nonArrayPath] = null + # we need to increment the project version number for any structure change + pullUpdate["$inc"]["version"] = 1 model.findOneAndUpdate conditions, pullUpdate, {"new": true}, callback _countElements: (project)-> @@ -245,14 +254,16 @@ module.exports = ProjectEntityMongoUpdateHandler = self = element._id = require('mongoose').Types.ObjectId(id) conditions = _id:project._id mongopath = "#{path.mongo}.#{type}" - update = "$push":{} + update = "$push":{}, "$inc":{} update["$push"][mongopath] = element + # we need to increment the project version number for any structure change + update["$inc"]["version"] = 1 # increment project version number logger.log project_id: project._id, element_id: element._id, fileType: type, folder_id: folder_id, mongopath:mongopath, "adding element to project" - Project.findOneAndUpdate conditions, update, {"new": true}, (err, project)-> + Project.findOneAndUpdate conditions, update, {"new": true}, (err, newProject)-> if err? logger.err err: err, project_id: project._id, 'error saving in putElement project' return callback(err) - callback(err, {path:newPath}, project) + callback(err, {path:newPath}, newProject) _checkValidElementName: (folder, name, callback = (err) ->) -> # check if the name is already taken by a doc, file or diff --git a/services/web/app/coffee/models/Project.coffee b/services/web/app/coffee/models/Project.coffee index 252388b169..fb0bc0fd6b 100644 --- a/services/web/app/coffee/models/Project.coffee +++ b/services/web/app/coffee/models/Project.coffee @@ -22,6 +22,7 @@ ProjectSchema = new Schema readOnly_refs : [ type:ObjectId, ref:'User' ] rootDoc_id : {type: ObjectId} rootFolder : [FolderSchema] + version : {type: Number} # incremented for every change in the project structure (folders and filenames) publicAccesLevel : {type: String, default: 'private'} compiler : {type:String, default:'pdflatex'} spellCheckLanguage : {type:String, default:'en'} diff --git a/services/web/test/acceptance/coffee/ProjectStructureTests.coffee b/services/web/test/acceptance/coffee/ProjectStructureTests.coffee index 77106e20a3..51bd4a26c7 100644 --- a/services/web/test/acceptance/coffee/ProjectStructureTests.coffee +++ b/services/web/test/acceptance/coffee/ProjectStructureTests.coffee @@ -35,21 +35,23 @@ describe "ProjectStructureChanges", -> done() it "should version creating a doc", -> - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates + {docUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) expect(updates.length).to.equal(2) _.each updates, (update) => expect(update.userId).to.equal(@owner._id) expect(update.docLines).to.be.a('string') expect(_.where(updates, pathname: "/main.tex").length).to.equal 1 expect(_.where(updates, pathname: "/references.bib").length).to.equal 1 + expect(version).to.equal(3) it "should version creating a file", -> - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) expect(updates.length).to.equal(1) update = updates[0] expect(update.userId).to.equal(@owner._id) expect(update.pathname).to.equal("/universe.jpg") expect(update.url).to.be.a('string'); + expect(version).to.equal(3) describe "duplicating a project", -> before (done) -> @@ -67,21 +69,23 @@ describe "ProjectStructureChanges", -> done() it "should version the docs created", -> - updates = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id).docUpdates + {docUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id) expect(updates.length).to.equal(2) _.each updates, (update) => expect(update.userId).to.equal(@owner._id) expect(update.docLines).to.be.a('string') expect(_.where(updates, pathname: "/main.tex").length).to.equal(1) expect(_.where(updates, pathname: "/references.bib").length).to.equal(1) + expect(version).to.equal(3) it "should version the files created", -> - updates = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id).fileUpdates + {fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@dup_project_id) expect(updates.length).to.equal(1) update = updates[0] expect(update.userId).to.equal(@owner._id) expect(update.pathname).to.equal("/universe.jpg") expect(update.url).to.be.a('string'); + expect(version).to.equal(3) describe "adding a doc", -> before (done) -> @@ -89,6 +93,7 @@ describe "ProjectStructureChanges", -> ProjectGetter.getProject example_project_id, (error, project) => throw error if error? + @project_0 = project @owner.request.post { uri: "project/#{example_project_id}/doc", json: @@ -99,15 +104,22 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to add doc #{res.statusCode}") example_doc_id = body._id - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + done() it "should version the doc added", -> - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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.docLines).to.be.a('string'); + expect(version).to.equal(@project_0.version + 1) + + it "should increment the project structure version number", -> + expect(@project_1.version).to.equal(@project_0.version + 1) describe "uploading a project", -> before (done) -> @@ -127,31 +139,32 @@ describe "ProjectStructureChanges", -> done() it "should version the dosc created", -> - updates = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id).docUpdates + {docUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id) expect(updates.length).to.equal(1) update = updates[0] expect(update.userId).to.equal(@owner._id) expect(update.pathname).to.equal("/main.tex") expect(update.docLines).to.equal("Test") + expect(version).to.equal(2) it "should version the files created", -> - updates = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id).fileUpdates + {fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@uploaded_project_id) 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.url).to.be.a('string'); + expect(version).to.equal(2) describe "uploading a file", -> - before (done) -> + beforeEach (done) -> + MockDocUpdaterApi.clearProjectStructureUpdates() ProjectGetter.getProject example_project_id, (error, project) => throw error if error? @root_folder_id = project.rootFolder[0]._id.toString() + @project_0 = project done() - - beforeEach () -> - MockDocUpdaterApi.clearProjectStructureUpdates() - + it "should version a newly uploaded file", (done) -> image_file = fs.createReadStream(Path.resolve(__dirname + '/../files/1pixel.png')) @@ -172,15 +185,21 @@ describe "ProjectStructureChanges", -> example_file_id = JSON.parse(body).entity_id - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates: updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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.url).to.be.a('string'); @original_file_url = update.url + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # uploading a new file does change the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() it "should version a replacement file", (done) -> image_file = fs.createReadStream(Path.resolve(__dirname + '/../files/2pixel.png')) @@ -200,14 +219,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to upload file #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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.url).to.be.a('string'); + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() describe "moving entities", -> before (done) -> @@ -220,8 +245,13 @@ describe "ProjectStructureChanges", -> example_folder_id_1 = JSON.parse(body)._id done() - beforeEach () -> + beforeEach (done) -> MockDocUpdaterApi.clearProjectStructureUpdates() + ProjectGetter.getProject example_project_id, (error, project) => + throw error if error? + @root_folder_id = project.rootFolder[0]._id.toString() + @project_0 = project + done() it "should version moving a doc", (done) -> @owner.request.post { @@ -233,14 +263,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to move doc #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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") + expect(version).to.equal(@project_0.version + 2) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 2) # 2 because it's a delete and then add + done() it "should version moving a file", (done) -> @owner.request.post { @@ -252,14 +288,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to move file #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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") + expect(version).to.equal(@project_0.version + 2) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 2) # 2 because it's a delete and then add + done() it "should version moving a folder", (done) -> @owner.request.post { @@ -279,25 +321,37 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to move folder #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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") + expect(version).to.equal(@project_0.version + 3) - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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") + expect(version).to.equal(@project_0.version + 3) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 3) # because folder and 2 files move + done() describe "renaming entities", -> - beforeEach () -> + beforeEach (done) -> MockDocUpdaterApi.clearProjectStructureUpdates() + ProjectGetter.getProject example_project_id, (error, project) => + throw error if error? + @root_folder_id = project.rootFolder[0]._id.toString() + @project_0 = project + done() it "should version renaming a doc", (done) -> @owner.request.post { @@ -309,14 +363,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to move doc #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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("/bar/foo/new_renamed.tex") + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() it "should version renaming a file", (done) -> @owner.request.post { @@ -328,14 +388,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to move file #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) 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("/bar/foo/1pixel_renamed.png") + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() it "should version renaming a folder", (done) -> @owner.request.post { @@ -347,25 +413,38 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to move folder #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) expect(updates.length).to.equal(1) update = updates[0] expect(update.userId).to.equal(@owner._id) expect(update.pathname).to.equal("/bar/foo/new_renamed.tex") expect(update.newPathname).to.equal("/bar/foo_renamed/new_renamed.tex") + expect(version).to.equal(@project_0.version + 1) - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) expect(updates.length).to.equal(1) update = updates[0] expect(update.userId).to.equal(@owner._id) expect(update.pathname).to.equal("/bar/foo/1pixel_renamed.png") expect(update.newPathname).to.equal("/bar/foo_renamed/1pixel_renamed.png") + expect(version).to.equal(@project_0.version + 1) + + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() - done() describe "deleting entities", -> - beforeEach () -> + beforeEach (done) -> MockDocUpdaterApi.clearProjectStructureUpdates() + ProjectGetter.getProject example_project_id, (error, project) => + throw error if error? + @root_folder_id = project.rootFolder[0]._id.toString() + @project_0 = project + done() it "should version deleting a folder", (done) -> @owner.request.delete { @@ -375,21 +454,28 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to delete folder #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) expect(updates.length).to.equal(1) update = updates[0] expect(update.userId).to.equal(@owner._id) expect(update.pathname).to.equal("/bar/foo_renamed/new_renamed.tex") expect(update.newPathname).to.equal("") + expect(version).to.equal(@project_0.version + 1) - updates = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(example_project_id) expect(updates.length).to.equal(1) update = updates[0] expect(update.userId).to.equal(@owner._id) expect(update.pathname).to.equal("/bar/foo_renamed/1pixel_renamed.png") expect(update.newPathname).to.equal("") + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject example_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() describe "tpds", -> before (done) -> @@ -399,8 +485,13 @@ describe "ProjectStructureChanges", -> @tpds_project_id = project_id mkdirp Settings.path.dumpFolder, done - beforeEach () -> + beforeEach (done) -> MockDocUpdaterApi.clearProjectStructureUpdates() + ProjectGetter.getProject @tpds_project_id, (error, project) => + throw error if error? + @root_folder_id = project.rootFolder[0]._id.toString() + @project_0 = project + done() it "should version adding a doc", (done) -> tex_file = fs.createReadStream(Path.resolve(__dirname + '/../files/test.tex')) @@ -423,14 +514,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to upload file #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id) 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.docLines).to.equal("Test") + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject @tpds_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() tex_file.pipe(req) @@ -455,14 +552,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to upload file #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id) 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.url).to.be.a('string'); + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject @tpds_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() image_file.pipe(req) @@ -487,14 +590,20 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to upload file #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).fileUpdates + {fileUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id) 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.url).to.be.a('string'); + expect(version).to.equal(@project_0.version + 1) - done() + ProjectGetter.getProject @tpds_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() image_file.pipe(req) @@ -510,12 +619,18 @@ describe "ProjectStructureChanges", -> if res.statusCode < 200 || res.statusCode >= 300 throw new Error("failed to delete doc #{res.statusCode}") - updates = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id).docUpdates + {docUpdates:updates, version} = MockDocUpdaterApi.getProjectStructureUpdates(@tpds_project_id) 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() + expect(version).to.equal(@project_0.version + 1) + + ProjectGetter.getProject @tpds_project_id, (error, newProject) => + throw error if error? + @project_1 = newProject + # replacing a file should update the project structure + expect(@project_1.version).to.equal(@project_0.version + 1) + done() diff --git a/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee b/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee index b21fb1adab..a9a2c49875 100644 --- a/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee +++ b/services/web/test/acceptance/coffee/helpers/MockDocUpdaterApi.coffee @@ -12,7 +12,7 @@ module.exports = MockDocUpdaterApi = getProjectStructureUpdates: (project_id) -> @updates[project_id] || { docUpdates: [], fileUpdates: [] } - addProjectStructureUpdates: (project_id, userId, docUpdates, fileUpdates) -> + addProjectStructureUpdates: (project_id, userId, docUpdates, fileUpdates, version) -> @updates[project_id] ||= { docUpdates: [], fileUpdates: [] } for update in docUpdates @@ -22,6 +22,8 @@ module.exports = MockDocUpdaterApi = for update in fileUpdates update.userId = userId @updates[project_id].fileUpdates.push(update) + + @updates[project_id].version = version run: () -> app.post "/project/:project_id/flush", (req, res, next) => @@ -29,8 +31,8 @@ module.exports = MockDocUpdaterApi = app.post "/project/:project_id", jsonParser, (req, res, next) => project_id = req.params.project_id - {userId, docUpdates, fileUpdates} = req.body - @addProjectStructureUpdates(project_id, userId, docUpdates, fileUpdates) + {userId, docUpdates, fileUpdates, version} = req.body + @addProjectStructureUpdates(project_id, userId, docUpdates, fileUpdates, version) res.sendStatus 200 app.delete "/project/:project_id/doc/:doc_id", (req, res, next) => diff --git a/services/web/test/unit/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee b/services/web/test/unit/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee index a4e0a4dc53..8db9ed4830 100644 --- a/services/web/test/unit/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee +++ b/services/web/test/unit/coffee/DocumentUpdater/DocumentUpdaterHandlerTests.coffee @@ -390,6 +390,8 @@ describe 'DocumentUpdaterHandler', -> describe "updateProjectStructure ", -> beforeEach -> @user_id = 1234 + @version = 999 + @Project.findOne = sinon.stub().callsArgWith(2,null, {_id: @project_id, version:@version}) describe "with project history disabled", -> beforeEach -> @@ -434,7 +436,7 @@ describe 'DocumentUpdaterHandler', -> @handler.updateProjectStructure @project_id, @user_id, @changes, () => @request.post - .calledWith(url: @url, json: {docUpdates, fileUpdates: [], userId: @user_id}) + .calledWith(url: @url, json: {docUpdates, fileUpdates: [], userId: @user_id, version:@version}) .should.equal true done() @@ -454,7 +456,7 @@ describe 'DocumentUpdaterHandler', -> @handler.updateProjectStructure @project_id, @user_id, @changes, () => @request.post - .calledWith(url: @url, json: {docUpdates, fileUpdates: [], userId: @user_id}) + .calledWith(url: @url, json: {docUpdates, fileUpdates: [], userId: @user_id, version:@version}) .should.equal true done() @@ -474,7 +476,7 @@ describe 'DocumentUpdaterHandler', -> @handler.updateProjectStructure @project_id, @user_id, @changes, () => @request.post - .calledWith(url: @url, json: {docUpdates: [], fileUpdates, userId: @user_id}) + .calledWith(url: @url, json: {docUpdates: [], fileUpdates, userId: @user_id, version:@version}) .should.equal true done() @@ -493,7 +495,7 @@ describe 'DocumentUpdaterHandler', -> @handler.updateProjectStructure @project_id, @user_id, @changes, () => @request.post - .calledWith(url: @url, json: {docUpdates, fileUpdates: [], userId: @user_id}) + .calledWith(url: @url, json: {docUpdates, fileUpdates: [], userId: @user_id, version:@version}) .should.equal true done() diff --git a/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee b/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee index d2a5a334b4..be5b177786 100644 --- a/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee @@ -117,7 +117,7 @@ describe 'ProjectEntityMongoUpdateHandler', -> .calledWith( { _id: project_id }, { - '$inc': { 'file.png.rev': 1 } + '$inc': { 'file.png.rev': 1, 'version': 1 } '$set': { 'file.png.created': new Date() } } {} @@ -324,7 +324,7 @@ describe 'ProjectEntityMongoUpdateHandler', -> @ProjectModel.findOneAndUpdate .calledWith( { _id: project_id }, - { $set: { "mongo.path.name": @newName } }, + { $set: { "mongo.path.name": @newName }, $inc: {"version": 1} }, { new: true } ).should.equal true @@ -384,7 +384,7 @@ describe 'ProjectEntityMongoUpdateHandler', -> it 'should pull', -> @ProjectModel.findOneAndUpdate - .calledWith({ _id: @id }, { '$pull': { 'folders[0]': null } }, {'new': true}) + .calledWith({ _id: @id }, { '$pull': { 'folders[0]': null }, '$inc': {'version': 1} }, {'new': true}) .should.equal true it 'should call the callback', ->