diff --git a/services/web/app/coffee/Features/Exports/ExportsHandler.coffee b/services/web/app/coffee/Features/Exports/ExportsHandler.coffee index 523e5d5cfe..4569cd4350 100644 --- a/services/web/app/coffee/Features/Exports/ExportsHandler.coffee +++ b/services/web/app/coffee/Features/Exports/ExportsHandler.coffee @@ -29,7 +29,7 @@ module.exports = ExportsHandler = self = ProjectGetter.getProject project_id, cb # TODO: when we update async, signature will change from (cb, results) to (results, cb) rootDoc: [ 'project', (cb, results) -> - ProjectRootDocManager.ensureRootDocumentIsSet project_id, (error) -> + ProjectRootDocManager.ensureRootDocumentIsValid project_id, (error) -> return callback(error) if error? ProjectLocator.findRootDoc {project: results.project, project_id: project_id}, cb ] diff --git a/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee b/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee index 8474573be2..201ffca56a 100644 --- a/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee +++ b/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee @@ -67,3 +67,24 @@ module.exports = ProjectRootDocManager = callback() else ProjectRootDocManager.setRootDocAutomatically project_id, callback + + ensureRootDocumentIsValid: (project_id, callback = (error) ->) -> + ProjectGetter.getProject project_id, rootDoc_id: 1, (error, project) -> + return callback(error) if error? + if !project? + return callback new Error("project not found") + + if project.rootDoc_id? + ProjectEntityHandler.getAllDocPathsFromProjectById project_id, (error, docPaths) -> + return callback(error) if error? + rootDocValid = false + for doc_id, _path of docPaths + if doc_id == project.rootDoc_id + rootDocValid = true + if rootDocValid + callback() + else + ProjectEntityUpdateHandler.setRootDoc project_id, null, -> + ProjectRootDocManager.setRootDocAutomatically project_id, callback + else + ProjectRootDocManager.setRootDocAutomatically project_id, callback diff --git a/services/web/test/unit/coffee/Exports/ExportsHandlerTests.coffee b/services/web/test/unit/coffee/Exports/ExportsHandlerTests.coffee index da63882c4a..3145bb8634 100644 --- a/services/web/test/unit/coffee/Exports/ExportsHandlerTests.coffee +++ b/services/web/test/unit/coffee/Exports/ExportsHandlerTests.coffee @@ -104,7 +104,7 @@ describe 'ExportsHandler', -> @ProjectGetter.getProject = sinon.stub().yields(null, @project) @ProjectHistoryHandler.ensureHistoryExistsForProject = sinon.stub().yields(null) @ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, {fileSystem: 'main.tex'}]) - @ProjectRootDocManager.ensureRootDocumentIsSet = sinon.stub().callsArgWith(1, null) + @ProjectRootDocManager.ensureRootDocumentIsValid = sinon.stub().callsArgWith(1, null) @UserGetter.getUser = sinon.stub().yields(null, @user) @ExportsHandler._requestVersion = sinon.stub().yields(null, @historyVersion) done() @@ -214,7 +214,51 @@ describe 'ExportsHandler', -> done() it "should set a root doc", -> - @ProjectRootDocManager.ensureRootDocumentIsSet.called + @ProjectRootDocManager.ensureRootDocumentIsValid.called + .should.equal true + + it "should return export data", -> + expected_export_data = + project: + id: @project_id + rootDocPath: 'other.tex' + historyId: @project_history_id + historyVersion: @historyVersion + v1ProjectId: @project_history_id + metadata: + compiler: 'pdflatex' + imageName: 'mock-image-name' + title: @title + description: @description + author: @author + license: @license + showSource: @show_source + user: + id: @user_id + firstName: @user.first_name + lastName: @user.last_name + email: @user.email + orcidId: null + v1UserId: 876 + destination: + brandVariationId: @brand_variation_id + options: + callbackUrl: null + @callback.calledWith(null, expected_export_data) + .should.equal true + + describe "when project has an invalid root doc", -> + describe "when a new root doc can be set automatically", -> + beforeEach (done) -> + @fakeDoc_id = '1a2b3c4d5e6f' + @project.rootDoc_id = @fakeDoc_id + @ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, {fileSystem: 'other.tex'}]) + @ExportsHandler._buildExport @export_params, (error, export_data) => + @callback(error, export_data) + done() + + it "should set a valid root doc", -> + @ProjectRootDocManager.ensureRootDocumentIsValid.called .should.equal true it "should return export data", -> diff --git a/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee b/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee index 10717be532..fc8fdec7bc 100644 --- a/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectRootDocManagerTests.coffee @@ -7,6 +7,11 @@ SandboxedModule = require('sandboxed-module') describe 'ProjectRootDocManager', -> beforeEach -> @project_id = "project-123" + @docPaths = + "doc-id-1": "/chapter1.tex" + "doc-id-2": "/main.tex" + "doc-id-3": "/nested/chapter1a.tex" + "doc-id-4": "/nested/chapter1b.tex" @sl_req_id = "sl-req-id-123" @callback = sinon.stub() @ProjectRootDocManager = SandboxedModule.require modulePath, requires: @@ -98,11 +103,6 @@ describe 'ProjectRootDocManager', -> describe "when there is a suitable root doc but the leading slash is missing", -> beforeEach (done)-> - @docPaths = - "doc-id-1": "/chapter1.tex" - "doc-id-2": "/main.tex" - "doc-id-3": "/nested/chapter1a.tex" - "doc-id-4": "/nested/chapter1b.tex" @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectRootDocManager.setRootDocFromName @project_id, 'main.tex', done @@ -117,11 +117,6 @@ describe 'ProjectRootDocManager', -> describe "when there is a suitable root doc with a basename match", -> beforeEach (done)-> - @docPaths = - "doc-id-1": "/chapter1.tex" - "doc-id-2": "/main.tex" - "doc-id-3": "/nested/chapter1a.tex" - "doc-id-4": "/nested/chapter1b.tex" @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectRootDocManager.setRootDocFromName @project_id, 'chapter1a.tex', done @@ -136,11 +131,6 @@ describe 'ProjectRootDocManager', -> describe "when there is a suitable root doc but the filename is in quotes", -> beforeEach (done)-> - @docPaths = - "doc-id-1": "/chapter1.tex" - "doc-id-2": "/main.tex" - "doc-id-3": "/nested/chapter1a.tex" - "doc-id-4": "/nested/chapter1b.tex" @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectRootDocManager.setRootDocFromName @project_id, "'main.tex'", done @@ -155,11 +145,6 @@ describe 'ProjectRootDocManager', -> describe "when there is no suitable root doc", -> beforeEach (done)-> - @docPaths = - "doc-id-1": "/chapter1.tex" - "doc-id-2": "/main.tex" - "doc-id-3": "/nested/chapter1a.tex" - "doc-id-4": "/nested/chapter1b.tex" @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectRootDocManager.setRootDocFromName @project_id, "other.tex", done @@ -179,7 +164,7 @@ describe 'ProjectRootDocManager', -> @project.rootDoc_id = "root-doc-id" @ProjectRootDocManager.ensureRootDocumentIsSet(@project_id, @callback) - it "should find the project with only the rootDoc_id fiel", -> + it "should find the project fetching only the rootDoc_id field", -> @ProjectGetter.getProject .calledWith(@project_id, rootDoc_id: 1) .should.equal true @@ -216,3 +201,76 @@ describe 'ProjectRootDocManager', -> it "should call the callback with an error", -> @callback.calledWith(new Error("project not found")).should.equal true + describe "ensureRootDocumentIsValid", -> + beforeEach -> + @project = {} + @ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, @project) + @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().yields() + @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths) + @ProjectRootDocManager.setRootDocAutomatically = sinon.stub().callsArgWith(1, null) + + describe "when the root doc is set", -> + describe "when the root doc is valid", -> + beforeEach -> + @project.rootDoc_id = "doc-id-2" + @ProjectRootDocManager.ensureRootDocumentIsValid(@project_id, @callback) + + it "should find the project fetching only the rootDoc_id field", -> + @ProjectGetter.getProject + .calledWith(@project_id, rootDoc_id: 1) + .should.equal true + + it "should not try to update the project rootDoc_id", -> + @ProjectRootDocManager.setRootDocAutomatically + .called.should.equal false + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when the root doc is not valid", -> + beforeEach -> + @project.rootDoc_id = "bogus-doc-id" + @ProjectRootDocManager.ensureRootDocumentIsValid(@project_id, @callback) + + it "should find the project fetching only the rootDoc_id field", -> + @ProjectGetter.getProject + .calledWith(@project_id, rootDoc_id: 1) + .should.equal true + + it "should null the rootDoc_id field", -> + @ProjectEntityUpdateHandler.setRootDoc + .calledWith(@project_id, null) + .should.equal true + + it "should try to find a new rootDoc", -> + @ProjectRootDocManager.setRootDocAutomatically + .called.should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when the root doc is not set", -> + beforeEach -> + @ProjectRootDocManager.ensureRootDocumentIsSet(@project_id, @callback) + + it "should find the project fetching only the rootDoc_id fiel", -> + @ProjectGetter.getProject + .calledWith(@project_id, rootDoc_id: 1) + .should.equal true + + it "should update the project rootDoc_id", -> + @ProjectRootDocManager.setRootDocAutomatically + .calledWith(@project_id) + .should.equal true + + it "should call the callback", -> + @callback.called.should.equal true + + describe "when the project does not exist", -> + beforeEach -> + @ProjectGetter.getProject = sinon.stub().callsArgWith(2, null, null) + @ProjectRootDocManager.ensureRootDocumentIsSet(@project_id, @callback) + + it "should call the callback with an error", -> + @callback.calledWith(new Error("project not found")).should.equal true +