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

Merge branch 'per-user-containers-part-2'

Conflicts:
	app/coffee/Features/Compile/ClsiManager.coffee
	app/coffee/Features/Compile/CompileController.coffee
	app/coffee/Features/Compile/CompileManager.coffee
Brian Gough 10 лет назад
Родитель
Сommit
49a7c3c27b

+ 31 - 29
services/web/app/coffee/Features/Compile/ClsiManager.coffee

@@ -13,7 +13,7 @@ ClsiFormatChecker = require("./ClsiFormatChecker")
 
 
 module.exports = ClsiManager =
 module.exports = ClsiManager =
 
 
-	sendRequest: (project_id, options = {}, callback = (error, status, outputFiles, clsiServerId, validationProblems) ->) ->
+	sendRequest: (project_id, user_id, options = {}, callback = (error, status, outputFiles, clsiServerId, validationProblems) ->) ->
 		ClsiManager._buildRequest project_id, options, (error, req) ->
 		ClsiManager._buildRequest project_id, options, (error, req) ->
 			return callback(error) if error?
 			return callback(error) if error?
 			logger.log project_id: project_id, "sending compile to CLSI"
 			logger.log project_id: project_id, "sending compile to CLSI"
@@ -24,26 +24,25 @@ module.exports = ClsiManager =
 				if validationProblems?
 				if validationProblems?
 					logger.log project_id:project_id, validationProblems:validationProblems, "problems with users latex before compile was attempted"
 					logger.log project_id:project_id, validationProblems:validationProblems, "problems with users latex before compile was attempted"
 					return callback(null, "validation-problems", null, null, validationProblems)
 					return callback(null, "validation-problems", null, null, validationProblems)
-				ClsiManager._postToClsi project_id, req, options.compileGroup, (error, response) ->
-					if error?
-						logger.err err:error, project_id:project_id, "error sending request to clsi"
-						return callback(error)
-					logger.log project_id: project_id, outputFilesLength: response?.outputFiles?.length, status: response?.status, "received compile response from CLSI"
-					ClsiCookieManager._getServerId project_id, (err, clsiServerId)->
-						if err?
-							logger.err err:err, project_id:project_id, "error getting server id"
-							return callback(err)
-						outputFiles = ClsiManager._parseOutputFiles(project_id, response?.compile?.outputFiles)
-						callback(null, response?.compile?.status, outputFiles, clsiServerId)
-
-	deleteAuxFiles: (project_id, options, callback = (error) ->) ->
-		compilerUrl = @_getCompilerUrl(options?.compileGroup)
+			ClsiManager._postToClsi project_id, user_id, req, options.compileGroup, (error, response) ->
+				if error?
+					logger.err err:error, project_id:project_id, "error sending request to clsi"
+					return callback(error)
+				logger.log project_id: project_id, outputFilesLength: response?.outputFiles?.length, status: response?.status, "received compile response from CLSI"
+				ClsiCookieManager._getServerId project_id, (err, clsiServerId)->
+					if err?
+						logger.err err:err, project_id:project_id, "error getting server id"
+						return callback(err)
+					outputFiles = ClsiManager._parseOutputFiles(project_id, response?.compile?.outputFiles)
+					callback(null, response?.compile?.status, outputFiles, clsiServerId)
+
+	deleteAuxFiles: (project_id, user_id, options, callback = (error) ->) ->
+		compilerUrl = @_getCompilerUrl(options?.compileGroup, project_id, user_id)
 		opts =
 		opts =
-			url:"#{compilerUrl}/project/#{project_id}"
+			url:compilerUrl
 			method:"DELETE"
 			method:"DELETE"
 		ClsiManager._makeRequest project_id, opts, callback
 		ClsiManager._makeRequest project_id, opts, callback
 
 
-
 	_makeRequest: (project_id, opts, callback)->
 	_makeRequest: (project_id, opts, callback)->
 		ClsiCookieManager.getCookieJar project_id, (err, jar)->
 		ClsiCookieManager.getCookieJar project_id, (err, jar)->
 			if err?
 			if err?
@@ -60,14 +59,17 @@ module.exports = ClsiManager =
 						
 						
 					return callback err, response, body
 					return callback err, response, body
 
 
+	_getCompilerUrl: (compileGroup, project_id, user_id, action) ->
+		host = Settings.apis.clsi.url
+		path = "/project/#{project_id}"
+		path += "/user/#{user_id}" if user_id?
+		path += "/#{action}" if action?
+		return "#{host}#{path}"
 
 
-	_getCompilerUrl: (compileGroup) ->
-		return Settings.apis.clsi.url
-
-	_postToClsi: (project_id, req, compileGroup, callback = (error, response) ->) ->
-		compilerUrl = Settings.apis.clsi.url
-		opts = 
-			url:  "#{compilerUrl}/project/#{project_id}/compile"
+	_postToClsi: (project_id, user_id, req, compileGroup, callback = (error, response) ->) ->
+		compileUrl = @_getCompilerUrl(compileGroup, project_id, user_id, "compile")
+		opts =
+			url:  compileUrl
 			json: req
 			json: req
 			method: "POST"
 			method: "POST"
 		ClsiManager._makeRequest project_id, opts, (error, response, body) ->
 		ClsiManager._makeRequest project_id, opts, (error, response, body) ->
@@ -142,15 +144,15 @@ module.exports = ClsiManager =
 							resources: resources
 							resources: resources
 					}
 					}
 
 
-	wordCount: (project_id, file, options, callback = (error, response) ->) ->
+	wordCount: (project_id, user_id, file, options, callback = (error, response) ->) ->
 		ClsiManager._buildRequest project_id, options, (error, req) ->
 		ClsiManager._buildRequest project_id, options, (error, req) ->
-			compilerUrl = ClsiManager._getCompilerUrl(options?.compileGroup)
 			filename = file || req?.compile?.rootResourcePath
 			filename = file || req?.compile?.rootResourcePath
-			wordcount_url = "#{compilerUrl}/project/#{project_id}/wordcount?file=#{encodeURIComponent(filename)}"
-			if req.compile.options.imageName?
-				wordcount_url += "&image=#{encodeURIComponent(req.compile.options.imageName)}"
+			wordcount_url = ClsiManager._getCompilerUrl(options?.compileGroup, project_id, user_id, "wordcount")
 			opts =
 			opts =
 				url: wordcount_url
 				url: wordcount_url
+				qs:
+					file: filename
+					image: req.compile.options.imageName
 				method: "GET"
 				method: "GET"
 			ClsiManager._makeRequest project_id, opts, (error, response, body) ->
 			ClsiManager._makeRequest project_id, opts, (error, response, body) ->
 				return callback(error) if error?
 				return callback(error) if error?

+ 49 - 19
services/web/app/coffee/Features/Compile/CompileController.coffee

@@ -29,7 +29,9 @@ module.exports = CompileController =
 				options.compiler = req.body.compiler
 				options.compiler = req.body.compiler
 			if req.body?.draft
 			if req.body?.draft
 				options.draft = req.body.draft
 				options.draft = req.body.draft
-			logger.log {options, project_id}, "got compile request"
+			if req.query?.isolated is "true"
+				options.isolated = true
+			logger.log {options:options, project_id:project_id, user_id:user_id}, "got compile request"
 			CompileManager.compile project_id, user_id, options, (error, status, outputFiles, clsiServerId, limits, validationProblems) ->
 			CompileManager.compile project_id, user_id, options, (error, status, outputFiles, clsiServerId, limits, validationProblems) ->
 				return next(error) if error?
 				return next(error) if error?
 				res.contentType("application/json")
 				res.contentType("application/json")
@@ -41,6 +43,13 @@ module.exports = CompileController =
 					validationProblems:validationProblems
 					validationProblems:validationProblems
 				}
 				}
 
 
+	_compileAsUser: (req, callback) ->
+		isolated = req.query?.isolated is "true"
+		if isolated
+			AuthenticationController.getLoggedInUserId req, callback
+		else
+			callback()
+
 	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
@@ -77,28 +86,40 @@ module.exports = CompileController =
 
 
 	deleteAuxFiles: (req, res, next) ->
 	deleteAuxFiles: (req, res, next) ->
 		project_id = req.params.Project_id
 		project_id = req.params.Project_id
-		CompileManager.deleteAuxFiles project_id, (error) ->
+		CompileController._compileAsUser req, (error, user_id) ->
 			return next(error) if error?
 			return next(error) if error?
-			res.sendStatus(200)
+			CompileManager.deleteAuxFiles project_id, user_id, (error) ->
+				return next(error) if error?
+				res.sendStatus(200)
 
 
 	compileAndDownloadPdf: (req, res, next)->
 	compileAndDownloadPdf: (req, res, next)->
 		project_id = req.params.project_id
 		project_id = req.params.project_id
-		CompileManager.compile project_id, null, {}, (err)->
-			if err?
-				logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
-				res.sendStatus 500
-			url = "/project/#{project_id}/output/output.pdf"
-			CompileController.proxyToClsi project_id, url, req, res, next
+		CompileController._compileAsUser req, (error, user_id) ->
+			return next(error) if error?
+			CompileManager.compile project_id, user_id, {}, (err)->
+				if err?
+					logger.err err:err, project_id:project_id, "something went wrong compile and downloading pdf"
+					res.sendStatus 500
+				url = "/project/#{project_id}/output/output.pdf"
+				CompileController.proxyToClsi project_id, user_id, url, req, res, next
 
 
 	getFileFromClsi: (req, res, next = (error) ->) ->
 	getFileFromClsi: (req, res, next = (error) ->) ->
 		project_id = req.params.Project_id
 		project_id = req.params.Project_id
-		build = req.params.build
-		if build?
-			url = "/project/#{project_id}/build/#{build}/output/#{req.params.file}"
+		build_id = req.params.build_id
+		user_id = req.params.user_id
+		if user_id? and build_id?
+			url = "/project/#{project_id}/user/#{user_id}/build/#{build_id}/output/#{req.params.file}"
+		else if build_id?
+			url = "/project/#{project_id}/build/#{build_id}/output/#{req.params.file}"
 		else
 		else
 			url = "/project/#{project_id}/output/#{req.params.file}"
 			url = "/project/#{project_id}/output/#{req.params.file}"
 		CompileController.proxyToClsi(project_id, url, req, res, next)
 		CompileController.proxyToClsi(project_id, url, req, res, next)
 
 
+	_getUrl: (project_id, user_id, action) ->
+		path = "/project/#{project_id}"
+		path += "/user/#{user_id}" if user_id?
+		return "#{path}/#{action}"
+
 	proxySyncPdf: (req, res, next = (error) ->) ->
 	proxySyncPdf: (req, res, next = (error) ->) ->
 		project_id = req.params.Project_id
 		project_id = req.params.Project_id
 		{page, h, v} = req.query
 		{page, h, v} = req.query
@@ -108,8 +129,12 @@ module.exports = CompileController =
 			return next(new Error("invalid h parameter"))
 			return next(new Error("invalid h parameter"))
 		if not v?.match(/^\d+\.\d+$/)
 		if not v?.match(/^\d+\.\d+$/)
 			return next(new Error("invalid v parameter"))
 			return next(new Error("invalid v parameter"))
-		destination = {url: "/project/#{project_id}/sync/pdf", qs: {page, h, v}}
-		CompileController.proxyToClsi(project_id, destination, req, res, next)
+		url = CompileController._getUrl(project_id, user_id, "sync/pdf")
+		destination = {url: url, qs: {page, h, v}}
+		# whether this request is going to a per-user container
+		CompileController._compileAsUser req, (error, user_id) ->
+			return next(error) if error?
+			CompileController.proxyToClsi(project_id, destination, req, res, next)
 
 
 	proxySyncCode: (req, res, next = (error) ->) ->
 	proxySyncCode: (req, res, next = (error) ->) ->
 		project_id = req.params.Project_id
 		project_id = req.params.Project_id
@@ -120,8 +145,11 @@ module.exports = CompileController =
 			return next(new Error("invalid line parameter"))
 			return next(new Error("invalid line parameter"))
 		if not column?.match(/^\d+$/)
 		if not column?.match(/^\d+$/)
 			return next(new Error("invalid column parameter"))
 			return next(new Error("invalid column parameter"))
-		destination = {url:"/project/#{project_id}/sync/code", qs: {file, line, column}}
-		CompileController.proxyToClsi(project_id, destination, req, res, next)
+		url = CompileController._getUrl(project_id, user_id, "sync/code")
+		destination = {url:url, qs: {file, line, column}}
+		CompileController._compileAsUser req, (error, user_id) ->
+			return next(error) if error?
+			CompileController.proxyToClsi(project_id, destination, req, res, next)
 
 
 	proxyToClsi: (project_id, url, req, res, next = (error) ->) ->
 	proxyToClsi: (project_id, url, req, res, next = (error) ->) ->
 		if req.query?.compileGroup
 		if req.query?.compileGroup
@@ -166,7 +194,9 @@ module.exports = CompileController =
 	wordCount: (req, res, next) ->
 	wordCount: (req, res, next) ->
 		project_id = req.params.Project_id
 		project_id = req.params.Project_id
 		file   = req.query.file || false
 		file   = req.query.file || false
-		CompileManager.wordCount project_id, file, (error, body) ->
+		CompileController._compileAsUser req, (error, user_id) ->
 			return next(error) if error?
 			return next(error) if error?
-			res.contentType("application/json")
-			res.send body
+			CompileManager.wordCount project_id, user_id, file, (error, body) ->
+				return next(error) if error?
+				res.contentType("application/json")
+				res.send body

+ 7 - 5
services/web/app/coffee/Features/Compile/CompileManager.coffee

@@ -37,15 +37,17 @@ module.exports = CompileManager =
 							return callback(error) if error?
 							return callback(error) if error?
 							for key, value of limits
 							for key, value of limits
 								options[key] = value
 								options[key] = value
-							ClsiManager.sendRequest project_id, options, (error, status, outputFiles, clsiServerId, validationProblems) ->
+							# only pass user_id down to clsi if this is a per-user compile
+							compileAsUser = if options.isolated then user_id else undefined
+							ClsiManager.sendRequest project_id, compileAsUser, options, (error, status, outputFiles, clsiServerId, validationProblems) ->
 								return callback(error) if error?
 								return callback(error) if error?
 								logger.log files: outputFiles, "output files"
 								logger.log files: outputFiles, "output files"
 								callback(null, status, outputFiles, clsiServerId, limits, validationProblems)
 								callback(null, status, outputFiles, clsiServerId, limits, validationProblems)
 								
 								
-	deleteAuxFiles: (project_id, callback = (error) ->) ->
+	deleteAuxFiles: (project_id, user_id, callback = (error) ->) ->
 		CompileManager.getProjectCompileLimits project_id, (error, limits) ->
 		CompileManager.getProjectCompileLimits project_id, (error, limits) ->
 			return callback(error) if error?
 			return callback(error) if error?
-			ClsiManager.deleteAuxFiles project_id, limits, callback
+			ClsiManager.deleteAuxFiles project_id, user_id, limits, callback
 
 
 	getProjectCompileLimits: (project_id, callback = (error, limits) ->) ->
 	getProjectCompileLimits: (project_id, callback = (error, limits) ->) ->
 		Project.findById project_id, {owner_ref: 1}, (error, project) ->
 		Project.findById project_id, {owner_ref: 1}, (error, project) ->
@@ -92,7 +94,7 @@ module.exports = CompileManager =
 			else
 			else
 				ProjectRootDocManager.setRootDocAutomatically project_id, callback
 				ProjectRootDocManager.setRootDocAutomatically project_id, callback
 		
 		
-	wordCount: (project_id, file, callback = (error) ->) ->
+	wordCount: (project_id, user_id, file, callback = (error) ->) ->
 		CompileManager.getProjectCompileLimits project_id, (error, limits) ->
 		CompileManager.getProjectCompileLimits project_id, (error, limits) ->
 			return callback(error) if error?
 			return callback(error) if error?
-			ClsiManager.wordCount project_id, file, limits, callback
+			ClsiManager.wordCount project_id, user_id, file, limits, callback

+ 14 - 1
services/web/app/coffee/router.coffee

@@ -119,12 +119,25 @@ module.exports = class Router
 			((req, res, next) ->
 			((req, res, next) ->
 				params =
 				params =
 					"Project_id": req.params[0]
 					"Project_id": req.params[0]
-					"build":      req.params[1]
+					"build_id":   req.params[1]
 					"file":       req.params[2]
 					"file":       req.params[2]
 				req.params = params
 				req.params = params
 				next()
 				next()
 			), AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.getFileFromClsi
 			), AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.getFileFromClsi
 
 
+		# direct url access to output files for a specific user and build (query string not required)
+		webRouter.get  /^\/project\/([^\/]*)\/user\/([0-9a-f]+)\/build\/([0-9a-f-]+)\/output\/(.*)$/,
+			((req, res, next) ->
+				params =
+					"Project_id": req.params[0]
+					"user_id":    req.params[1]
+					"build_id":   req.params[2]
+					"file":       req.params[3]
+				req.params = params
+				next()
+			), AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.getFileFromClsi
+
+
 		webRouter.delete "/project/:Project_id/output", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.deleteAuxFiles
 		webRouter.delete "/project/:Project_id/output", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.deleteAuxFiles
 		webRouter.get "/project/:Project_id/sync/code", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySyncCode
 		webRouter.get "/project/:Project_id/sync/code", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySyncCode
 		webRouter.get "/project/:Project_id/sync/pdf", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySyncPdf
 		webRouter.get "/project/:Project_id/sync/pdf", AuthorizationMiddlewear.ensureUserCanReadProject, CompileController.proxySyncPdf

+ 10 - 3
services/web/public/coffee/ide/pdf/controllers/PdfController.coffee

@@ -5,6 +5,8 @@ define [
 ], (App, LogParser, BibLogParser) ->
 ], (App, LogParser, BibLogParser) ->
 	App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) ->
 	App.controller "PdfController", ($scope, $http, ide, $modal, synctex, event_tracking, localStorage) ->
 
 
+		# enable per-user containers if querystring includes isolated=true
+		perUserCompile = window.location?.search?.match(/isolated=true/)? or undefined
 		autoCompile = true
 		autoCompile = true
 
 
 		# pdf.view = uncompiled | pdf | errors
 		# pdf.view = uncompiled | pdf | errors
@@ -28,13 +30,16 @@ define [
 
 
 		sendCompileRequest = (options = {}) ->
 		sendCompileRequest = (options = {}) ->
 			url = "/project/#{$scope.project_id}/compile"
 			url = "/project/#{$scope.project_id}/compile"
+			params = {}
 			if options.isAutoCompile
 			if options.isAutoCompile
-				url += "?auto_compile=true"
+				params["auto_compile"]=true
+			if perUserCompile # send ?isolated=true for per-user compiles
+				params["isolated"] = true
 			return $http.post url, {
 			return $http.post url, {
 				rootDoc_id: options.rootDocOverride_id or null
 				rootDoc_id: options.rootDocOverride_id or null
 				draft: $scope.draft
 				draft: $scope.draft
 				_csrf: window.csrfToken
 				_csrf: window.csrfToken
-			}
+			}, {params: params}
 
 
 		parseCompileResponse = (response) ->		
 		parseCompileResponse = (response) ->		
 
 
@@ -132,7 +137,6 @@ define [
 				opts =
 				opts =
 					method:"GET"
 					method:"GET"
 					params:
 					params:
-						build:file.build
 						clsiserverid:ide.clsiServerId
 						clsiserverid:ide.clsiServerId
 				if file.url?  # FIXME clean this up when we have file.urls out consistently
 				if file.url?  # FIXME clean this up when we have file.urls out consistently
 					opts.url = file.url
 					opts.url = file.url
@@ -246,6 +250,7 @@ define [
 				method: "DELETE"
 				method: "DELETE"
 				params:
 				params:
 					clsiserverid:ide.clsiServerId
 					clsiserverid:ide.clsiServerId
+					isolated: perUserCompile
 				headers:
 				headers:
 					"X-Csrf-Token": window.csrfToken
 					"X-Csrf-Token": window.csrfToken
 			}
 			}
@@ -329,6 +334,7 @@ define [
 							line: row + 1
 							line: row + 1
 							column: column
 							column: column
 							clsiserverid:ide.clsiServerId
 							clsiserverid:ide.clsiServerId
+							isolated: perUserCompile
 						}
 						}
 					})
 					})
 					.success (data) ->
 					.success (data) ->
@@ -357,6 +363,7 @@ define [
 							h: position.offset.left.toFixed(2)
 							h: position.offset.left.toFixed(2)
 							v: position.offset.top.toFixed(2)
 							v: position.offset.top.toFixed(2)
 							clsiserverid:ide.clsiServerId
 							clsiserverid:ide.clsiServerId
+							isolated: perUserCompile
 						}
 						}
 					})
 					})
 					.success (data) ->
 					.success (data) ->

+ 22 - 22
services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee

@@ -31,6 +31,7 @@ describe "ClsiManager", ->
 			"request": @request = sinon.stub()
 			"request": @request = sinon.stub()
 			"./ClsiFormatChecker": @ClsiFormatChecker
 			"./ClsiFormatChecker": @ClsiFormatChecker
 		@project_id = "project-id"
 		@project_id = "project-id"
+		@user_id = "user-id"
 		@callback = sinon.stub()
 		@callback = sinon.stub()
 
 
 	describe "sendRequest", ->
 	describe "sendRequest", ->
@@ -40,22 +41,22 @@ describe "ClsiManager", ->
 
 
 		describe "with a successful compile", ->
 		describe "with a successful compile", ->
 			beforeEach ->
 			beforeEach ->
-				@ClsiManager._postToClsi = sinon.stub().callsArgWith(3, null, {
+				@ClsiManager._postToClsi = sinon.stub().callsArgWith(4, null, {
 					compile:
 					compile:
 						status: @status = "success"
 						status: @status = "success"
 						outputFiles: [{
 						outputFiles: [{
-							url: "#{@settings.apis.clsi.url}/project/#{@project_id}/build/1234/output/output.pdf"
+							url: "#{@settings.apis.clsi.url}/project/#{@project_id}/user/#{@user_id}/build/1234/output/output.pdf"
 							path: "output.pdf"
 							path: "output.pdf"
 							type: "pdf"
 							type: "pdf"
 							build: 1234
 							build: 1234
 						},{
 						},{
-							url: "#{@settings.apis.clsi.url}/project/#{@project_id}/build/1234/output/output.log"
+							url: "#{@settings.apis.clsi.url}/project/#{@project_id}/user/#{@user_id}/build/1234/output/output.log"
 							path: "output.log"
 							path: "output.log"
 							type: "log"
 							type: "log"
 							build: 1234
 							build: 1234
 						}]
 						}]
 				})
 				})
-				@ClsiManager.sendRequest @project_id, {compileGroup:"standard"}, @callback
+				@ClsiManager.sendRequest @project_id, @user_id, {compileGroup:"standard"}, @callback
 
 
 			it "should build the request", ->
 			it "should build the request", ->
 				@ClsiManager._buildRequest
 				@ClsiManager._buildRequest
@@ -64,17 +65,17 @@ describe "ClsiManager", ->
 
 
 			it "should send the request to the CLSI", ->
 			it "should send the request to the CLSI", ->
 				@ClsiManager._postToClsi
 				@ClsiManager._postToClsi
-					.calledWith(@project_id, @request, "standard")
+					.calledWith(@project_id, @user_id, @request, "standard")
 					.should.equal true
 					.should.equal true
 
 
 			it "should call the callback with the status and output files", ->
 			it "should call the callback with the status and output files", ->
 				outputFiles = [{
 				outputFiles = [{
-					url: "/project/#{@project_id}/build/1234/output/output.pdf"
+					url: "/project/#{@project_id}/user/#{@user_id}/build/1234/output/output.pdf"
 					path: "output.pdf"
 					path: "output.pdf"
 					type: "pdf"
 					type: "pdf"
 					build: 1234
 					build: 1234
 				},{
 				},{
-					url: "/project/#{@project_id}/build/1234/output/output.log"
+					url: "/project/#{@project_id}/user/#{@user_id}/build/1234/output/output.log"
 					path: "output.log"
 					path: "output.log"
 					type: "log"
 					type: "log"
 					build: 1234
 					build: 1234
@@ -83,11 +84,11 @@ describe "ClsiManager", ->
 
 
 		describe "with a failed compile", ->
 		describe "with a failed compile", ->
 			beforeEach ->
 			beforeEach ->
-				@ClsiManager._postToClsi = sinon.stub().callsArgWith(3, null, {
+				@ClsiManager._postToClsi = sinon.stub().callsArgWith(4, null, {
 					compile:
 					compile:
 						status: @status = "failure"
 						status: @status = "failure"
 				})
 				})
-				@ClsiManager.sendRequest @project_id, {}, @callback
+				@ClsiManager.sendRequest @project_id, @user_id, {}, @callback
 			
 			
 			it "should call the callback with a failure statue", ->
 			it "should call the callback with a failure statue", ->
 				@callback.calledWith(null, @status).should.equal true
 				@callback.calledWith(null, @status).should.equal true
@@ -98,11 +99,11 @@ describe "ClsiManager", ->
 			
 			
 		describe "with the standard compileGroup", ->
 		describe "with the standard compileGroup", ->
 			beforeEach ->
 			beforeEach ->
-				@ClsiManager.deleteAuxFiles @project_id, {compileGroup: "standard"}, @callback
+				@ClsiManager.deleteAuxFiles @project_id, @user_id, {compileGroup: "standard"}, @callback
 
 
 			it "should call the delete method in the standard CLSI", ->
 			it "should call the delete method in the standard CLSI", ->
 				@ClsiManager._makeRequest
 				@ClsiManager._makeRequest
-					.calledWith(@project_id, { method:"DELETE", url:"#{@settings.apis.clsi.url}/project/#{@project_id}"})
+					.calledWith(@project_id, { method:"DELETE", url:"#{@settings.apis.clsi.url}/project/#{@project_id}/user/#{@user_id}"})
 					.should.equal true
 					.should.equal true
 
 
 			it "should call the callback", ->
 			it "should call the callback", ->
@@ -242,10 +243,10 @@ describe "ClsiManager", ->
 		describe "successfully", ->
 		describe "successfully", ->
 			beforeEach ->
 			beforeEach ->
 				@ClsiManager._makeRequest = sinon.stub().callsArgWith(2, null, {statusCode: 204}, @body = { mock: "foo" })
 				@ClsiManager._makeRequest = sinon.stub().callsArgWith(2, null, {statusCode: 204}, @body = { mock: "foo" })
-				@ClsiManager._postToClsi @project_id, @req, "standard", @callback
+				@ClsiManager._postToClsi @project_id, @user_id, @req, "standard", @callback
 
 
 			it 'should send the request to the CLSI', ->
 			it 'should send the request to the CLSI', ->
-				url = "#{@settings.apis.clsi.url}/project/#{@project_id}/compile"
+				url = "#{@settings.apis.clsi.url}/project/#{@project_id}/user/#{@user_id}/compile"
 				@ClsiManager._makeRequest.calledWith(@project_id, {
 				@ClsiManager._makeRequest.calledWith(@project_id, {
 					method: "POST",
 					method: "POST",
 					url: url
 					url: url
@@ -258,7 +259,7 @@ describe "ClsiManager", ->
 		describe "when the CLSI returns an error", ->
 		describe "when the CLSI returns an error", ->
 			beforeEach ->
 			beforeEach ->
 				@ClsiManager._makeRequest = sinon.stub().callsArgWith(2, null, {statusCode: 500}, @body = { mock: "foo" })
 				@ClsiManager._makeRequest = sinon.stub().callsArgWith(2, null, {statusCode: 500}, @body = { mock: "foo" })
-				@ClsiManager._postToClsi @project_id, @req, "standard", @callback
+				@ClsiManager._postToClsi @project_id, @user_id, @req, "standard", @callback
 
 
 			it "should call the callback with the body and the error", ->
 			it "should call the callback with the body and the error", ->
 				@callback.calledWith(new Error("CLSI returned non-success code: 500"), @body).should.equal true
 				@callback.calledWith(new Error("CLSI returned non-success code: 500"), @body).should.equal true
@@ -268,37 +269,36 @@ describe "ClsiManager", ->
 		beforeEach ->
 		beforeEach ->
 			@ClsiManager._makeRequest = sinon.stub().callsArgWith(2, null, {statusCode: 200}, @body = { mock: "foo" })
 			@ClsiManager._makeRequest = sinon.stub().callsArgWith(2, null, {statusCode: 200}, @body = { mock: "foo" })
 			@ClsiManager._buildRequest = sinon.stub().callsArgWith(2, null, @req = { compile: { rootResourcePath: "rootfile.text", options: {} } })
 			@ClsiManager._buildRequest = sinon.stub().callsArgWith(2, null, @req = { compile: { rootResourcePath: "rootfile.text", options: {} } })
-			@ClsiManager._getCompilerUrl = sinon.stub().returns "compiler.url"
 
 
 		describe "with root file", ->
 		describe "with root file", ->
 			beforeEach ->
 			beforeEach ->
-				@ClsiManager.wordCount @project_id, false, {}, @callback
+				@ClsiManager.wordCount @project_id, @user_id, false, {}, @callback
 
 
 			it "should call wordCount with root file", ->
 			it "should call wordCount with root file", ->
 				@ClsiManager._makeRequest
 				@ClsiManager._makeRequest
-					.calledWith(@project_id, { method: "GET", url: "compiler.url/project/#{@project_id}/wordcount?file=rootfile.text" })
-					.should.equal true
+				.calledWith(@project_id, {method: "GET", url: "http://clsi.example.com/project/#{@project_id}/user/#{@user_id}/wordcount", qs: {file: "rootfile.text",image:undefined}})
+				.should.equal true
 
 
 			it "should call the callback", ->
 			it "should call the callback", ->
 				@callback.called.should.equal true
 				@callback.called.should.equal true
 				
 				
 		describe "with param file", ->
 		describe "with param file", ->
 			beforeEach ->
 			beforeEach ->
-				@ClsiManager.wordCount @project_id, "main.tex", {}, @callback
+				@ClsiManager.wordCount @project_id, @user_id, "main.tex", {}, @callback
 
 
 			it "should call wordCount with param file", ->
 			it "should call wordCount with param file", ->
 				@ClsiManager._makeRequest
 				@ClsiManager._makeRequest
-					.calledWith(@project_id, { method: "GET", url: "compiler.url/project/#{@project_id}/wordcount?file=main.tex" })
+					.calledWith(@project_id, { method: "GET", url: "http://clsi.example.com/project/#{@project_id}/user/#{@user_id}/wordcount", qs:{file:"main.tex",image:undefined}})
 					.should.equal true
 					.should.equal true
 					
 					
 		describe "with image", ->
 		describe "with image", ->
 			beforeEach ->
 			beforeEach ->
 				@req.compile.options.imageName = @image = "example.com/mock/image"
 				@req.compile.options.imageName = @image = "example.com/mock/image"
-				@ClsiManager.wordCount @project_id, "main.tex", {}, @callback
+				@ClsiManager.wordCount @project_id, @user_id, "main.tex", {}, @callback
 
 
 			it "should call wordCount with file and image", ->
 			it "should call wordCount with file and image", ->
 				@ClsiManager._makeRequest
 				@ClsiManager._makeRequest
-					.calledWith(@project_id, { method: "GET", url: "compiler.url/project/#{@project_id}/wordcount?file=main.tex&image=#{encodeURIComponent(@image)}" })
+					.calledWith(@project_id, { method: "GET", url: "http://clsi.example.com/project/#{@project_id}/user/#{@user_id}/wordcount", qs:{file:"main.tex",image:@image}})
 					.should.equal true
 					.should.equal true
 
 
 
 

+ 11 - 5
services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee

@@ -45,13 +45,13 @@ describe "CompileController", ->
 		@next = sinon.stub()
 		@next = sinon.stub()
 		@req = new MockRequest()
 		@req = new MockRequest()
 		@res = new MockResponse()
 		@res = new MockResponse()
+		@AuthenticationController.getLoggedInUserId = sinon.stub().callsArgWith(1, null, @user_id = "mock-user-id")
 
 
 	describe "compile", ->
 	describe "compile", ->
 		beforeEach ->
 		beforeEach ->
 			@req.params =
 			@req.params =
 				Project_id: @project_id
 				Project_id: @project_id
 			@req.session = {}
 			@req.session = {}
-			@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"])
 			@CompileManager.compile = sinon.stub().callsArgWith(3, null, @status = "success", @outputFiles = ["mock-output-files"])
 
 
 		describe "when not an auto compile", ->
 		describe "when not an auto compile", ->
@@ -317,7 +317,7 @@ describe "CompileController", ->
 
 
 	describe "deleteAuxFiles", ->
 	describe "deleteAuxFiles", ->
 		beforeEach ->
 		beforeEach ->
-			@CompileManager.deleteAuxFiles = sinon.stub().callsArg(1)
+			@CompileManager.deleteAuxFiles = sinon.stub().callsArg(2)
 			@req.params =
 			@req.params =
 				Project_id: @project_id
 				Project_id: @project_id
 			@res.sendStatus = sinon.stub()
 			@res.sendStatus = sinon.stub()
@@ -338,6 +338,8 @@ describe "CompileController", ->
 			@req =
 			@req =
 				params:
 				params:
 					project_id:@project_id
 					project_id:@project_id
+				query:
+					isolated: "true"
 			@CompileManager.compile.callsArgWith(3)
 			@CompileManager.compile.callsArgWith(3)
 			@CompileController.proxyToClsi = sinon.stub()
 			@CompileController.proxyToClsi = sinon.stub()
 			@res = 
 			@res = 
@@ -350,21 +352,25 @@ describe "CompileController", ->
 
 
 		it "should proxy the res to the clsi with correct url", (done)->
 		it "should proxy the res to the clsi with correct url", (done)->
 			@CompileController.compileAndDownloadPdf @req, @res
 			@CompileController.compileAndDownloadPdf @req, @res
-			@CompileController.proxyToClsi.calledWith(@project_id, "/project/#{@project_id}/output/output.pdf", @req, @res).should.equal true
+			sinon.assert.calledWith @CompileController.proxyToClsi, @project_id, @user_id, "/project/#{@project_id}/output/output.pdf", @req, @res
+
+			@CompileController.proxyToClsi.calledWith(@project_id, @user_id, "/project/#{@project_id}/output/output.pdf", @req, @res).should.equal true
 			done()
 			done()
 
 
 	describe "wordCount", ->
 	describe "wordCount", ->
 		beforeEach ->
 		beforeEach ->
-			@CompileManager.wordCount = sinon.stub().callsArgWith(2, null, {content:"body"})
+			@CompileManager.wordCount = sinon.stub().callsArgWith(3, null, {content:"body"})
 			@req.params =
 			@req.params =
 				Project_id: @project_id
 				Project_id: @project_id
+			@req.query =
+				isolated: "true"
 			@res.send = sinon.stub()
 			@res.send = sinon.stub()
 			@res.contentType = sinon.stub()
 			@res.contentType = sinon.stub()
 			@CompileController.wordCount @req, @res, @next
 			@CompileController.wordCount @req, @res, @next
 
 
 		it "should proxy to the CLSI", ->
 		it "should proxy to the CLSI", ->
 			@CompileManager.wordCount
 			@CompileManager.wordCount
-				.calledWith(@project_id, false)
+				.calledWith(@project_id, @user_id, false)
 				.should.equal true
 				.should.equal true
 
 
 		it "should return a 200 and body", ->
 		it "should return a 200 and body", ->

+ 8 - 8
services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee

@@ -42,7 +42,7 @@ describe "CompileManager", ->
 			@CompileManager._ensureRootDocumentIsSet = sinon.stub().callsArgWith(1, null)
 			@CompileManager._ensureRootDocumentIsSet = sinon.stub().callsArgWith(1, null)
 			@DocumentUpdaterHandler.flushProjectToMongo = sinon.stub().callsArgWith(1, null)
 			@DocumentUpdaterHandler.flushProjectToMongo = sinon.stub().callsArgWith(1, null)
 			@CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith(1, null, @limits)
 			@CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith(1, null, @limits)
-			@ClsiManager.sendRequest = sinon.stub().callsArgWith(2, null, @status = "mock-status", @outputFiles = "mock output files", @output = "mock output")
+			@ClsiManager.sendRequest = sinon.stub().callsArgWith(3, null, @status = "mock-status", @outputFiles = "mock output files", @output = "mock output")
 
 
 		describe "succesfully", ->
 		describe "succesfully", ->
 			beforeEach ->
 			beforeEach ->
@@ -71,7 +71,7 @@ describe "CompileManager", ->
 
 
 			it "should run the compile with the compile limits", ->
 			it "should run the compile with the compile limits", ->
 				@ClsiManager.sendRequest
 				@ClsiManager.sendRequest
-					.calledWith(@project_id, {
+					.calledWith(@project_id, undefined, {
 						timeout: @limits.timeout
 						timeout: @limits.timeout
 					})
 					})
 					.should.equal true
 					.should.equal true
@@ -135,8 +135,8 @@ describe "CompileManager", ->
 	describe "deleteAuxFiles", ->
 	describe "deleteAuxFiles", ->
 		beforeEach ->
 		beforeEach ->
 			@CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith 1, null, @limits = { compileGroup: "mock-compile-group" }
 			@CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith 1, null, @limits = { compileGroup: "mock-compile-group" }
-			@ClsiManager.deleteAuxFiles = sinon.stub().callsArg(2)
-			@CompileManager.deleteAuxFiles @project_id, @callback
+			@ClsiManager.deleteAuxFiles = sinon.stub().callsArg(3)
+			@CompileManager.deleteAuxFiles @project_id, @user_id, @callback
 			
 			
 		it "should look up the compile group to use", ->
 		it "should look up the compile group to use", ->
 			@CompileManager.getProjectCompileLimits
 			@CompileManager.getProjectCompileLimits
@@ -145,7 +145,7 @@ describe "CompileManager", ->
 				
 				
 		it "should delete the aux files", ->
 		it "should delete the aux files", ->
 			@ClsiManager.deleteAuxFiles
 			@ClsiManager.deleteAuxFiles
-				.calledWith(@project_id, @limits)
+				.calledWith(@project_id, @user_id, @limits)
 				.should.equal true
 				.should.equal true
 				
 				
 		it "should call the callback", ->
 		it "should call the callback", ->
@@ -260,8 +260,8 @@ describe "CompileManager", ->
 	describe "wordCount", ->
 	describe "wordCount", ->
 		beforeEach ->
 		beforeEach ->
 			@CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith 1, null, @limits = { compileGroup: "mock-compile-group" }
 			@CompileManager.getProjectCompileLimits = sinon.stub().callsArgWith 1, null, @limits = { compileGroup: "mock-compile-group" }
-			@ClsiManager.wordCount = sinon.stub().callsArg(3)
-			@CompileManager.wordCount @project_id, false, @callback
+			@ClsiManager.wordCount = sinon.stub().callsArg(4)
+			@CompileManager.wordCount @project_id, @user_id, false, @callback
 			
 			
 		it "should look up the compile group to use", ->
 		it "should look up the compile group to use", ->
 			@CompileManager.getProjectCompileLimits
 			@CompileManager.getProjectCompileLimits
@@ -270,7 +270,7 @@ describe "CompileManager", ->
 				
 				
 		it "should call wordCount for project", ->
 		it "should call wordCount for project", ->
 			@ClsiManager.wordCount
 			@ClsiManager.wordCount
-				.calledWith(@project_id, false, @limits)
+				.calledWith(@project_id, @user_id, false, @limits)
 				.should.equal true
 				.should.equal true
 				
 				
 		it "should call the callback", ->
 		it "should call the callback", ->