diff --git a/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee index 419607763c..0d2864081c 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee @@ -1,3 +1,4 @@ +_ = require 'lodash' async = require 'async' logger = require('logger-sharelatex') path = require('path') @@ -63,21 +64,31 @@ module.exports = ProjectEntityUpdateHandler = self = return callback(new Errors.NotFoundError("project not found")) if !project? logger.log project_id: project_id, doc_id: doc_id, "updating doc lines" ProjectLocator.findElement {project:project, element_id:doc_id, type:"docs"}, (err, doc, path)-> + isDeletedDoc = false if err? - logger.error err: err, doc_id: doc_id, project_id: project_id, lines: lines, "error finding doc while updating doc lines" - return callback err - if !doc? - error = new Errors.NotFoundError("doc not found") - logger.error err: error, doc_id: doc_id, project_id: project_id, lines: lines, "doc not found while updating doc lines" - return callback(error) + if err instanceof Errors.NotFoundError + # We need to be able to update the doclines of deleted docs. This is + # so the doc-updater can flush a doc's content to the doc-store after + # the doc is deleted. + isDeletedDoc = true + doc = _.find project.deletedDocs, (doc) -> + doc._id.toString() == doc_id.toString() + else + return callback(err) - logger.log project_id: project_id, doc_id: doc_id, "telling docstore manager to update doc" + if !doc? + # Do not allow an update to a doc which has never exist on this project + logger.error {doc_id, project_id, lines}, "doc not found while updating doc lines" + return callback(new Errors.NotFoundError('doc not found')) + + logger.log {project_id, doc_id}, "telling docstore manager to update doc" DocstoreManager.updateDoc project_id, doc_id, lines, version, ranges, (err, modified, rev) -> if err? - logger.error err: err, doc_id: doc_id, project_id:project_id, lines: lines, "error sending doc to docstore" + logger.error {err, doc_id, project_id, lines}, "error sending doc to docstore" return callback(err) - logger.log project_id: project_id, doc_id: doc_id, modified:modified, "finished updating doc lines" - if modified + logger.log {project_id, doc_id, modified}, "finished updating doc lines" + # path will only be present if the doc is not deleted + if modified && !isDeletedDoc # Don't need to block for marking as updated ProjectUpdateHandler.markAsUpdated project_id TpdsUpdateSender.addDoc {project_id:project_id, path:path.fileSystem, doc_id:doc_id, project_name:project.name, rev:rev}, callback @@ -315,9 +326,9 @@ module.exports = ProjectEntityUpdateHandler = self = unsetRootDocIfRequired (error) -> return callback(error) if error? - DocumentUpdaterHandler.deleteDoc project_id, doc_id, (error) -> + self._insertDeletedDocReference project._id, doc, (error) -> return callback(error) if error? - self._insertDeletedDocReference project._id, doc, (error) -> + DocumentUpdaterHandler.deleteDoc project_id, doc_id, (error) -> return callback(error) if error? DocstoreManager.deleteDoc project_id, doc_id, (error) -> return callback(error) if error? diff --git a/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee b/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee index 33eb8ebe99..d1fca71bc1 100644 --- a/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee @@ -46,6 +46,7 @@ describe 'ProjectEntityUpdateHandler', -> 'logger-sharelatex': @logger = {log:sinon.stub(), error: sinon.stub(), err:->} '../../models/Doc': Doc:@DocModel '../Docstore/DocstoreManager': @DocstoreManager = {} + '../Errors/Errors': Errors '../../Features/DocumentUpdater/DocumentUpdaterHandler':@DocumentUpdaterHandler = updateProjectStructure: sinon.stub().yields() '../../models/File': File:@FileModel @@ -178,18 +179,32 @@ describe 'ProjectEntityUpdateHandler', -> it "should call the callback", -> @callback.called.should.equal true - describe "when the project is not found", -> + describe "when the doc has been deleted", -> beforeEach -> - @ProjectGetter.getProjectWithoutDocLines = sinon.stub().yields() - @ProjectEntityUpdateHandler.updateDocLines project_id, doc_id, @docLines, @ranges, @version, @callback + @project.deletedDocs = [ _id: doc_id ] + @ProjectGetter.getProjectWithoutDocLines = sinon.stub().yields(null, @project) + @ProjectLocator.findElement = sinon.stub().yields(new Errors.NotFoundError) + @DocstoreManager.updateDoc = sinon.stub().yields() + @ProjectEntityUpdateHandler.updateDocLines project_id, doc_id, @docLines, @version, @ranges, @callback - it "should return a not found error", -> - @callback.calledWith(new Errors.NotFoundError()).should.equal true + it "should update the doc in the docstore", -> + @DocstoreManager.updateDoc + .calledWith(project_id, doc_id, @docLines, @version, @ranges) + .should.equal true - describe "when the doc is not found", -> + it "should not mark the project as updated", -> + @ProjectUpdater.markAsUpdated.called.should.equal false + + it "should not send the doc the to the TPDS", -> + @TpdsUpdateSender.addDoc.called.should.equal false + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when the doc is not related to the project", -> beforeEach -> @ProjectLocator.findElement = sinon.stub().yields() - @ProjectEntityUpdateHandler.updateDocLines project_id, doc_id, @docLines, @ranges, @version, @callback + @ProjectEntityUpdateHandler.updateDocLines project_id, doc_id, @docLines, @version, @ranges, @callback it "should log out the error", -> @logger.error @@ -197,7 +212,6 @@ describe 'ProjectEntityUpdateHandler', -> project_id: project_id doc_id: doc_id lines: @docLines - err: new Errors.NotFoundError("doc not found") "doc not found while updating doc lines" ) .should.equal true @@ -205,6 +219,14 @@ describe 'ProjectEntityUpdateHandler', -> it "should return a not found error", -> @callback.calledWith(new Errors.NotFoundError()).should.equal true + describe "when the project is not found", -> + beforeEach -> + @ProjectGetter.getProjectWithoutDocLines = sinon.stub().yields() + @ProjectEntityUpdateHandler.updateDocLines project_id, doc_id, @docLines, @version, @ranges, @callback + + it "should return a not found error", -> + @callback.calledWith(new Errors.NotFoundError()).should.equal true + describe "setRootDoc", -> it "should call Project.update", -> rootDoc_id = "root-doc-id-123123"