Merge pull request #1126 from sharelatex/mm-ensure-valid-rootdoc

Ensure project's root doc is valid before exporting

GitOrigin-RevId: 6f15955015097df9464267f458b8dbb126c22173
This commit is contained in:
Ersun Warncke 2018-11-09 04:09:13 -04:00 committed by sharelatex
parent ecd7e1d0c2
commit de4f1a5abd
4 changed files with 147 additions and 24 deletions

View file

@ -29,7 +29,7 @@ module.exports = ExportsHandler = self =
ProjectGetter.getProject project_id, cb ProjectGetter.getProject project_id, cb
# TODO: when we update async, signature will change from (cb, results) to (results, cb) # TODO: when we update async, signature will change from (cb, results) to (results, cb)
rootDoc: [ 'project', (cb, results) -> rootDoc: [ 'project', (cb, results) ->
ProjectRootDocManager.ensureRootDocumentIsSet project_id, (error) -> ProjectRootDocManager.ensureRootDocumentIsValid project_id, (error) ->
return callback(error) if error? return callback(error) if error?
ProjectLocator.findRootDoc {project: results.project, project_id: project_id}, cb ProjectLocator.findRootDoc {project: results.project, project_id: project_id}, cb
] ]

View file

@ -67,3 +67,24 @@ module.exports = ProjectRootDocManager =
callback() callback()
else else
ProjectRootDocManager.setRootDocAutomatically project_id, callback 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

View file

@ -104,7 +104,7 @@ describe 'ExportsHandler', ->
@ProjectGetter.getProject = sinon.stub().yields(null, @project) @ProjectGetter.getProject = sinon.stub().yields(null, @project)
@ProjectHistoryHandler.ensureHistoryExistsForProject = sinon.stub().yields(null) @ProjectHistoryHandler.ensureHistoryExistsForProject = sinon.stub().yields(null)
@ProjectLocator.findRootDoc = sinon.stub().yields(null, [null, {fileSystem: 'main.tex'}]) @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) @UserGetter.getUser = sinon.stub().yields(null, @user)
@ExportsHandler._requestVersion = sinon.stub().yields(null, @historyVersion) @ExportsHandler._requestVersion = sinon.stub().yields(null, @historyVersion)
done() done()
@ -214,7 +214,51 @@ describe 'ExportsHandler', ->
done() done()
it "should set a root doc", -> 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 .should.equal true
it "should return export data", -> it "should return export data", ->

View file

@ -7,6 +7,11 @@ SandboxedModule = require('sandboxed-module')
describe 'ProjectRootDocManager', -> describe 'ProjectRootDocManager', ->
beforeEach -> beforeEach ->
@project_id = "project-123" @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" @sl_req_id = "sl-req-id-123"
@callback = sinon.stub() @callback = sinon.stub()
@ProjectRootDocManager = SandboxedModule.require modulePath, requires: @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", -> describe "when there is a suitable root doc but the leading slash is missing", ->
beforeEach (done)-> 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) @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths)
@ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
@ProjectRootDocManager.setRootDocFromName @project_id, 'main.tex', done @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", -> describe "when there is a suitable root doc with a basename match", ->
beforeEach (done)-> 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) @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths)
@ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
@ProjectRootDocManager.setRootDocFromName @project_id, 'chapter1a.tex', done @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", -> describe "when there is a suitable root doc but the filename is in quotes", ->
beforeEach (done)-> 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) @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths)
@ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
@ProjectRootDocManager.setRootDocFromName @project_id, "'main.tex'", done @ProjectRootDocManager.setRootDocFromName @project_id, "'main.tex'", done
@ -155,11 +145,6 @@ describe 'ProjectRootDocManager', ->
describe "when there is no suitable root doc", -> describe "when there is no suitable root doc", ->
beforeEach (done)-> 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) @ProjectEntityHandler.getAllDocPathsFromProjectById = sinon.stub().callsArgWith(1, null, @docPaths)
@ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2) @ProjectEntityUpdateHandler.setRootDoc = sinon.stub().callsArgWith(2)
@ProjectRootDocManager.setRootDocFromName @project_id, "other.tex", done @ProjectRootDocManager.setRootDocFromName @project_id, "other.tex", done
@ -179,7 +164,7 @@ describe 'ProjectRootDocManager', ->
@project.rootDoc_id = "root-doc-id" @project.rootDoc_id = "root-doc-id"
@ProjectRootDocManager.ensureRootDocumentIsSet(@project_id, @callback) @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 @ProjectGetter.getProject
.calledWith(@project_id, rootDoc_id: 1) .calledWith(@project_id, rootDoc_id: 1)
.should.equal true .should.equal true
@ -216,3 +201,76 @@ describe 'ProjectRootDocManager', ->
it "should call the callback with an error", -> it "should call the callback with an error", ->
@callback.calledWith(new Error("project not found")).should.equal true @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