Differentiate between timeouts, server errors, and LaTeX failures in client

This commit is contained in:
James Allen 2014-05-19 15:28:35 +01:00
parent 6c5a2c2740
commit 7fed2155be
8 changed files with 112 additions and 26 deletions

View file

@ -4,8 +4,22 @@ CompileManager = require("./CompileManager")
logger = require "logger-sharelatex"
request = require "request"
Settings = require "settings-sharelatex"
AuthenticationController = require "../Authentication/AuthenticationController"
module.exports = CompileController =
compile: (req, res, next = (error) ->) ->
project_id = req.params.Project_id
isAutoCompile = !!req.query?.auto_compile
AuthenticationController.getLoggedInUserId req, (error, user_id) ->
return next(error) if error?
CompileManager.compile project_id, user_id, { isAutoCompile }, (error, status, outputFiles) ->
return next(error) if error?
res.contentType("application/json")
res.send 200, JSON.stringify {
status: status
outputFiles: outputFiles
}
downloadPdf: (req, res, next = (error) ->)->
Metrics.inc "pdf-downloads"
project_id = req.params.Project_id

View file

@ -19,8 +19,7 @@ module.exports = CompileManager =
@_checkIfAutoCompileLimitHasBeenHit opt.isAutoCompile, (err, canCompile)->
if !canCompile
err = {rateLimitHit:true}
return callback(err)
return callback null, "autocompile-backoff", []
logger.log project_id: project_id, user_id: user_id, "compiling project"
CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
return callback(error) if error?

View file

@ -104,6 +104,7 @@ module.exports = class Router
app.get '/Project/:Project_id', SecurityManager.requestCanAccessProject, ProjectController.loadEditor
app.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile
app.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile
app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
app.get /^\/project\/([^\/]*)\/output\/(.*)$/,
((req, res, next) ->
@ -320,6 +321,7 @@ module.exports = class Router
AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) =>
EditorController.setPublicAccessLevel(project_id, newAccessLevel, callback)
# Deprecated and can be removed after deploying.
client.on 'pdfProject', (opts, callback)->
AuthorizationManager.ensureClientCanViewProject client, (error, project_id) =>
CompileManager.compile project_id, user._id, opts, (error, status, outputFiles) ->

View file

@ -234,14 +234,19 @@
script(type="text/template")#compileErrorTemplate
li.alert.alert-error
strong Compile Error:
span Sorry, something went wrong and the project could not be compiled. This may be due to our compiler being overloaded or an incompatibility with the project. Please try again in a few moments and if the problem continues let us know via the feedback tab at the top.
strong Server Error.
span Sorry, something went wrong and the project could not be compiled. Please try again in a few moments and if the problem continues please contact support.
script(type="text/template")#compileFailedTemplate
li.alert.alert-error
strong Ooops, your LaTeX code couldn't compile for some reason. Please check these errors for details:
strong Ooops, your LaTeX code couldn't compile for some reason. Please check the errors below for details, or view the raw log.
script(type="text/template")#compileTimeoutTemplate
li.alert.alert-error
strong Timed out.
span Sorry, your compile was taking too long and timed out.
| This may be due to a large number of high-res images, or lots of complicated diagrams.
| Please try to make your document simpler, or contact support for help.
script(type="text/template")#compileLogEntryTemplate
li.alert.clickable(class="alert-{{ type }}")
strong {{ title }}:

View file

@ -37,6 +37,7 @@ define [
compileSuccess: $('#compileSuccessTemplate').html()
compileFailed: $('#compileFailedTemplate').html()
compileError: $('#compileErrorTemplate').html()
compileTimeout: $('#compileTimeoutTemplate').html()
outputFileLink: $('#outputFileLinkTemplate').html()
events:
@ -79,7 +80,7 @@ define [
@pdfView.onResize?()
updateLog: (options) ->
{pdfExists, logExists, compileErrors, rawLog} = options
{pdfExists, logExists, compileErrors, rawLog, timedOut, systemError} = options
if @errorViews?
for errorView in @errorViews
@ -110,11 +111,12 @@ define [
@$("#showLog").html(logButtonHtml)
if !pdfExists
if !compileErrors?
errorLogs.prepend($(@templates.compileError))
else
errorLogs.prepend($(@templates.compileFailed))
if timedOut
errorLogs.prepend($(@templates.compileTimeout))
else if systemError
errorLogs.prepend($(@templates.compileError))
else if !pdfExists
errorLogs.prepend($(@templates.compileFailed))
else if pdfExists && compileErrors.all.length == 0
errorLogs.prepend($(@templates.compileSuccess))

View file

@ -124,7 +124,7 @@ define [
@ide.on "afterJoinProject", () =>
@_refreshPdfWhenProjectIsLoaded(opts)
_refreshPdfWhenProjectIsLoaded: (opts) ->
_refreshPdfWhenProjectIsLoaded: (opts = {}) ->
doneCompiling = _.once =>
@compiling = false
@view.doneCompiling()
@ -143,17 +143,23 @@ define [
@view.onCompiling()
@syncButtonsView?.hide()
@compiling = true
@ide.socket.emit "pdfProject", opts, (err, pdfExists, outputFiles) =>
@_doCompile opts.isAutoCompile, (error, status, outputFiles) =>
@compiling = false
doneCompiling()
if err? and err.rateLimitHit
if error?
@view.updateLog(systemError: true)
@view.unsetPdf()
@view.showLog()
else if status == "timedout"
@view.updateLog(timedOut: true)
@view.unsetPdf()
@view.showLog()
else if status == "autocompile-backoff"
@view.showBeforeCompile()
else
if err?
@view.updateLog(pdfExists: false, logExists: false)
else
@fetchLogAndUpdateView(pdfExists)
pdfExists = (status == "success")
@fetchLogAndUpdateView(pdfExists)
if pdfExists
@view.setPdf("/project/#{@ide.project_id}/output/output.pdf?cache_bust=#{Date.now()}")
@ -166,6 +172,22 @@ define [
if outputFiles?
@view.showOutputFileDownloadLinks(outputFiles)
_doCompile: (isAutoCompile, callback = (error, status, outputFiles) ->) ->
url = "/project/#{@ide.project_id}/compile"
if isAutoCompile
url += "?auto_compile=true"
$.ajax(
url: url
type: "POST"
headers:
"X-CSRF-Token": window.csrfToken
dataType: 'json'
success: (body, status, response) ->
callback null, body.status, body.outputFiles
error: (error) ->
callback error
)
fetchLogAndUpdateView: (pdfExists) ->
$.ajax(
url: "/project/#{@ide.project_id}/output/output.log"

View file

@ -21,11 +21,58 @@ describe "CompileController", ->
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
"../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() }
"./CompileManager":@CompileManager
"../Authentication/AuthenticationController": @AuthenticationController = {}
@project_id = "project-id"
@next = sinon.stub()
@req = new MockRequest()
@res = new MockResponse()
describe "compile", ->
describe "when not an auto compile", ->
beforeEach ->
@req.params =
Project_id: @project_id
@AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id")
@CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"])
@CompileController.compile @req, @res, @next
it "should look up the user id", ->
@AuthenticationController.getLoggedInUserId
.calledWith(@req)
.should.equal true
it "should do the compile without the auto compile flag", ->
@CompileManager.compile
.calledWith(@project_id, @user_id, { isAutoCompile: false })
.should.equal true
it "should set the content-type of the response to application/json", ->
@res.contentType
.calledWith("application/json")
.should.equal true
it "should send a successful response reporting the status and files", ->
@res.statusCode.should.equal 200
@res.body.should.equal JSON.stringify({
status: @status
outputFiles: @outputFiles
})
describe "when an auto compile", ->
beforeEach ->
@req.params =
Project_id: @project_id
@req.query =
auto_compile: "true"
@AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id")
@CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"])
@CompileController.compile @req, @res, @next
it "should do the compile with the auto compile flag", ->
@CompileManager.compile
.calledWith(@project_id, @user_id, { isAutoCompile: true })
.should.equal true
describe "downloadPdf", ->
beforeEach ->
@req.params =

View file

@ -90,13 +90,8 @@ describe "CompileManager", ->
describe "should check the rate limit", ->
it "should return", (done)->
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false)
@CompileManager.compile @project_id, @user_id, {}, (err)->
done()
it "should not error if the autocompile limit has not been hit", (done)->
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, true)
@CompileManager.compile @project_id, @user_id, {}, (err)->
assert.equal null, err
@CompileManager.compile @project_id, @user_id, {}, (err, status)->
status.should.equal "autocompile-backoff"
done()
describe "getLogLines", ->