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" logger = require "logger-sharelatex"
request = require "request" request = require "request"
Settings = require "settings-sharelatex" Settings = require "settings-sharelatex"
AuthenticationController = require "../Authentication/AuthenticationController"
module.exports = CompileController = 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) ->)-> downloadPdf: (req, res, next = (error) ->)->
Metrics.inc "pdf-downloads" Metrics.inc "pdf-downloads"
project_id = req.params.Project_id project_id = req.params.Project_id

View file

@ -19,8 +19,7 @@ module.exports = CompileManager =
@_checkIfAutoCompileLimitHasBeenHit opt.isAutoCompile, (err, canCompile)-> @_checkIfAutoCompileLimitHasBeenHit opt.isAutoCompile, (err, canCompile)->
if !canCompile if !canCompile
err = {rateLimitHit:true} return callback null, "autocompile-backoff", []
return callback(err)
logger.log project_id: project_id, user_id: user_id, "compiling project" logger.log project_id: project_id, user_id: user_id, "compiling project"
CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) -> CompileManager._checkIfRecentlyCompiled project_id, user_id, (error, recentlyCompiled) ->
return callback(error) if error? 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', SecurityManager.requestCanAccessProject, ProjectController.loadEditor
app.get '/Project/:Project_id/file/:File_id', SecurityManager.requestCanAccessProject, FileStoreController.getFile 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/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf
app.get /^\/project\/([^\/]*)\/output\/(.*)$/, app.get /^\/project\/([^\/]*)\/output\/(.*)$/,
((req, res, next) -> ((req, res, next) ->
@ -320,6 +321,7 @@ module.exports = class Router
AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) => AuthorizationManager.ensureClientCanAdminProject client, (error, project_id) =>
EditorController.setPublicAccessLevel(project_id, newAccessLevel, callback) EditorController.setPublicAccessLevel(project_id, newAccessLevel, callback)
# Deprecated and can be removed after deploying.
client.on 'pdfProject', (opts, callback)-> client.on 'pdfProject', (opts, callback)->
AuthorizationManager.ensureClientCanViewProject client, (error, project_id) => AuthorizationManager.ensureClientCanViewProject client, (error, project_id) =>
CompileManager.compile project_id, user._id, opts, (error, status, outputFiles) -> CompileManager.compile project_id, user._id, opts, (error, status, outputFiles) ->

View file

@ -234,14 +234,19 @@
script(type="text/template")#compileErrorTemplate script(type="text/template")#compileErrorTemplate
li.alert.alert-error li.alert.alert-error
strong Compile Error: strong Server 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. 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 script(type="text/template")#compileFailedTemplate
li.alert.alert-error 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 script(type="text/template")#compileLogEntryTemplate
li.alert.clickable(class="alert-{{ type }}") li.alert.clickable(class="alert-{{ type }}")
strong {{ title }}: strong {{ title }}:

View file

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

View file

@ -124,7 +124,7 @@ define [
@ide.on "afterJoinProject", () => @ide.on "afterJoinProject", () =>
@_refreshPdfWhenProjectIsLoaded(opts) @_refreshPdfWhenProjectIsLoaded(opts)
_refreshPdfWhenProjectIsLoaded: (opts) -> _refreshPdfWhenProjectIsLoaded: (opts = {}) ->
doneCompiling = _.once => doneCompiling = _.once =>
@compiling = false @compiling = false
@view.doneCompiling() @view.doneCompiling()
@ -143,17 +143,23 @@ define [
@view.onCompiling() @view.onCompiling()
@syncButtonsView?.hide() @syncButtonsView?.hide()
@compiling = true @compiling = true
@ide.socket.emit "pdfProject", opts, (err, pdfExists, outputFiles) => @_doCompile opts.isAutoCompile, (error, status, outputFiles) =>
@compiling = false @compiling = false
doneCompiling() 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() @view.showBeforeCompile()
else else
if err? pdfExists = (status == "success")
@view.updateLog(pdfExists: false, logExists: false) @fetchLogAndUpdateView(pdfExists)
else
@fetchLogAndUpdateView(pdfExists)
if pdfExists if pdfExists
@view.setPdf("/project/#{@ide.project_id}/output/output.pdf?cache_bust=#{Date.now()}") @view.setPdf("/project/#{@ide.project_id}/output/output.pdf?cache_bust=#{Date.now()}")
@ -166,6 +172,22 @@ define [
if outputFiles? if outputFiles?
@view.showOutputFileDownloadLinks(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) -> fetchLogAndUpdateView: (pdfExists) ->
$.ajax( $.ajax(
url: "/project/#{@ide.project_id}/output/output.log" 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() } "logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
"../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() } "../../infrastructure/Metrics": @Metrics = { inc: sinon.stub() }
"./CompileManager":@CompileManager "./CompileManager":@CompileManager
"../Authentication/AuthenticationController": @AuthenticationController = {}
@project_id = "project-id" @project_id = "project-id"
@next = sinon.stub() @next = sinon.stub()
@req = new MockRequest() @req = new MockRequest()
@res = new MockResponse() @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", -> describe "downloadPdf", ->
beforeEach -> beforeEach ->
@req.params = @req.params =

View file

@ -90,13 +90,8 @@ describe "CompileManager", ->
describe "should check the rate limit", -> describe "should check the rate limit", ->
it "should return", (done)-> it "should return", (done)->
@CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false) @CompileManager._checkIfAutoCompileLimitHasBeenHit = sinon.stub().callsArgWith(1, null, false)
@CompileManager.compile @project_id, @user_id, {}, (err)-> @CompileManager.compile @project_id, @user_id, {}, (err, status)->
done() status.should.equal "autocompile-backoff"
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
done() done()
describe "getLogLines", -> describe "getLogLines", ->