Просмотр исходного кода

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

James Allen 12 лет назад
Родитель
Сommit
7fed2155be

+ 14 - 0
services/web/app/coffee/Features/Compile/CompileController.coffee

@@ -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

+ 1 - 2
services/web/app/coffee/Features/Compile/CompileManager.coffee

@@ -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(err)
+				return callback null, "autocompile-backoff", []
 			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?

+ 2 - 0
services/web/app/coffee/router.coffee

@@ -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) ->

+ 9 - 4
services/web/app/views/templates.jade

@@ -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: 
-			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
 	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 }}: 

+ 8 - 6
services/web/public/coffee/pdf/CompiledView.coffee

@@ -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 !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
 			else if pdfExists && compileErrors.all.length == 0
 				errorLogs.prepend($(@templates.compileSuccess))
 				errorLogs.prepend($(@templates.compileSuccess))
 
 

+ 29 - 7
services/web/public/coffee/pdf/PdfManager.coffee

@@ -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?
-							@view.updateLog(pdfExists: false, logExists: false)
-						else
-							@fetchLogAndUpdateView(pdfExists)
+						pdfExists = (status == "success")
+						@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"

+ 47 - 0
services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee

@@ -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 =

+ 2 - 7
services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee

@@ -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)->
-					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()
 					done()
 
 
 	describe "getLogLines", ->
 	describe "getLogLines", ->