Jelajahi Sumber

Merge pull request #81 from sharelatex/hof-init-project-history

initialize project history on project creation
Hayden Faulds 9 tahun lalu
induk
melakukan
79d9756bea

+ 23 - 0
services/web/app/coffee/Features/History/HistoryController.coffee

@@ -4,6 +4,29 @@ settings = require "settings-sharelatex"
 AuthenticationController = require "../Authentication/AuthenticationController"
 
 module.exports = HistoryController =
+	initializeProject: (callback = (error, history_id) ->) ->
+		return callback() if !settings.apis.project_history?.enabled
+		request.post {
+			url: "#{settings.apis.project_history.url}/project"
+		}, (error, res, body)->
+			return callback(error) if error?
+
+			if res.statusCode >= 200 and res.statusCode < 300
+				try
+					project = JSON.parse(body)
+				catch error
+					return callback(error)
+
+				overleaf_id = project?.project?.id
+				if !overleaf_id
+					error = new Error("project-history did not provide an id", project)
+					return callback(error)
+
+				callback null, { overleaf_id }
+			else
+				error = new Error("project-history returned a non-success status code: #{res.statusCode}")
+				callback error
+
 	proxyToHistoryApi: (req, res, next = (error) ->) ->
 		user_id = AuthenticationController.getLoggedInUserId req
 		url = HistoryController.buildHistoryServiceUrl() + req.url

+ 28 - 14
services/web/app/coffee/Features/Project/ProjectCreationHandler.coffee

@@ -2,11 +2,12 @@ logger = require('logger-sharelatex')
 async = require("async")
 metrics = require('metrics-sharelatex')
 Settings = require('settings-sharelatex')
-ObjectId = require('mongoose').Types.ObjectId	
+ObjectId = require('mongoose').Types.ObjectId
 Project = require('../../models/Project').Project
 Folder = require('../../models/Folder').Folder
 ProjectEntityHandler = require('./ProjectEntityHandler')
 ProjectDetailsHandler = require('./ProjectDetailsHandler')
+HistoryController = require('../History/HistoryController')
 User = require('../../models/User').User
 fs = require('fs')
 Path = require "path"
@@ -14,23 +15,36 @@ _ = require "underscore"
 
 module.exports = ProjectCreationHandler =
 
-	createBlankProject : (owner_id, projectName, callback = (error, project) ->)->
+	createBlankProject : (owner_id, projectName, projectHistoryId, callback = (error, project) ->)->
 		metrics.inc("project-creation")
+		if arguments.length == 3
+			callback = projectHistoryId
+			projectHistoryId = null
+
 		ProjectDetailsHandler.validateProjectName projectName, (error) ->
 			return callback(error) if error?
 			logger.log owner_id:owner_id, projectName:projectName, "creating blank project"
-			rootFolder = new Folder {'name':'rootFolder'}
-			project = new Project
-				 owner_ref  : new ObjectId(owner_id)
-				 name       : projectName
-			if Settings.currentImageName?
-				project.imageName = Settings.currentImageName
-			project.rootFolder[0] = rootFolder
-			User.findById owner_id, "ace.spellCheckLanguage", (err, user)->
-				project.spellCheckLanguage = user.ace.spellCheckLanguage
-				project.save (err)->
-					return callback(err) if err?
-					callback err, project
+			if projectHistoryId?
+				ProjectCreationHandler._createBlankProject owner_id, projectName, projectHistoryId, callback
+			else
+				HistoryController.initializeProject (error, history) ->
+					return callback(error) if error?
+					ProjectCreationHandler._createBlankProject owner_id, projectName, history?.overleaf_id, callback
+
+	_createBlankProject : (owner_id, projectName, projectHistoryId, callback = (error, project) ->)->
+		rootFolder = new Folder {'name':'rootFolder'}
+		project = new Project
+			 owner_ref          : new ObjectId(owner_id)
+			 name               : projectName
+		project.overleaf.history.id = projectHistoryId
+		if Settings.currentImageName?
+			project.imageName = Settings.currentImageName
+		project.rootFolder[0] = rootFolder
+		User.findById owner_id, "ace.spellCheckLanguage", (err, user)->
+			project.spellCheckLanguage = user.ace.spellCheckLanguage
+			project.save (err)->
+				return callback(err) if err?
+				callback err, project
 
 	createBasicProject :  (owner_id, projectName, callback = (error, project) ->)->
 		self = @

+ 2 - 0
services/web/app/coffee/models/Project.coffee

@@ -38,6 +38,8 @@ ProjectSchema = new Schema
 		imported_at_ver_id : { type: Number }
 		token           : { type: String }
 		read_token      : { type: String }
+		history         :
+			id            : { type: Number }
 
 ProjectSchema.statics.getProject = (project_or_id, fields, callback)->
 	if project_or_id._id?

+ 72 - 6
services/web/test/UnitTests/coffee/History/HistoryControllerTests.coffee

@@ -6,6 +6,7 @@ SandboxedModule = require('sandboxed-module')
 
 describe "HistoryController", ->
 	beforeEach ->
+		@callback = sinon.stub()
 		@user_id = "user-id-123"
 		@AuthenticationController =
 			getLoggedInUserId: sinon.stub().returns(@user_id)
@@ -14,18 +15,18 @@ describe "HistoryController", ->
 			"settings-sharelatex": @settings = {}
 			"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()}
 			"../Authentication/AuthenticationController": @AuthenticationController
+		@settings.apis =
+			trackchanges:
+				enabled: false
+				url: "http://trackchanges.example.com"
+			project_history:
+				url: "http://project_history.example.com"
 
 	describe "proxyToHistoryApi", ->
 		beforeEach ->
 			@req = { url: "/mock/url", method: "POST" }
 			@res = "mock-res"
 			@next = sinon.stub()
-			@settings.apis =
-				trackchanges:
-					enabled: false
-					url: "http://trackchanges.example.com"
-				project_history:
-					url: "http://project_history.example.com"
 			@proxy =
 				events: {}
 				pipe: sinon.stub()
@@ -80,3 +81,68 @@ describe "HistoryController", ->
 
 			it "should pass the error up the call chain", ->
 				@next.calledWith(@error).should.equal true
+
+	describe "initializeProject", ->
+		describe "with project history enabled", ->
+			beforeEach ->
+				@settings.apis.project_history.enabled = true
+
+			describe "project history returns a successful response", ->
+				beforeEach ->
+					@overleaf_id = 1234
+					@res = statusCode: 200
+					@body = JSON.stringify(project: id: @overleaf_id)
+					@request.post = sinon.stub().callsArgWith(1, null, @res, @body)
+
+					@HistoryController.initializeProject @callback
+
+				it "should call the project history api", ->
+					@request.post.calledWith(
+						url: "#{@settings.apis.project_history.url}/project"
+					).should.equal true
+
+				it "should return the callback with the overleaf id", ->
+					@callback.calledWithExactly(null, { @overleaf_id }).should.equal true
+
+			describe "project history returns a response without the project id", ->
+				beforeEach ->
+					@res = statusCode: 200
+					@body = JSON.stringify(project: {})
+					@request.post = sinon.stub().callsArgWith(1, null, @res, @body)
+
+					@HistoryController.initializeProject @callback
+
+				it "should return the callback with an error", ->
+					@callback
+						.calledWith(sinon.match.has("message", "project-history did not provide an id"))
+						.should.equal true
+
+			describe "project history returns a unsuccessful response", ->
+				beforeEach ->
+					@res = statusCode: 404
+					@request.post = sinon.stub().callsArgWith(1, null, @res)
+
+					@HistoryController.initializeProject @callback
+
+				it "should return the callback with an error", ->
+					@callback
+						.calledWith(sinon.match.has("message", "project-history returned a non-success status code: 404"))
+						.should.equal true
+
+			describe "project history errors", ->
+				beforeEach ->
+					@error = sinon.stub()
+					@request.post = sinon.stub().callsArgWith(1, @error)
+
+					@HistoryController.initializeProject @callback
+
+				it "should return the callback with the error", ->
+					@callback.calledWithExactly(@error).should.equal true
+
+		describe "with project history disabled", ->
+			beforeEach ->
+				@settings.apis.project_history.enabled = false
+				@HistoryController.initializeProject @callback
+
+			it "should return the callback", ->
+				@callback.calledWithExactly().should.equal true

+ 32 - 11
services/web/test/UnitTests/coffee/Project/ProjectCreationHandlerTests.coffee

@@ -22,6 +22,8 @@ describe 'ProjectCreationHandler', ->
 				@._id = project_id
 				@owner_ref = options.owner_ref
 				@name = options.name
+				@overleaf =
+					history: {}
 			save: sinon.stub().callsArg(0)
 			rootFolder:[{
 				_id: rootFolderId
@@ -36,11 +38,13 @@ describe 'ProjectCreationHandler', ->
 			setRootDoc: sinon.stub().callsArg(2)
 		@ProjectDetailsHandler =
 			validateProjectName: sinon.stub().yields()
+		@HistoryController =
+			initializeProject: sinon.stub().callsArg(0)
 
-		@user = 
+		@user =
 			first_name:"first name here"
 			last_name:"last name here"
-			ace: 
+			ace:
 				spellCheckLanguage:"de"
 
 		@User = findById:sinon.stub().callsArgWith(2, null, @user)
@@ -49,6 +53,7 @@ describe 'ProjectCreationHandler', ->
 			'../../models/User': User:@User
 			'../../models/Project':{Project:@ProjectModel}
 			'../../models/Folder':{Folder:@FolderModel}
+			'../History/HistoryController': @HistoryController
 			'./ProjectEntityHandler':@ProjectEntityHandler
 			"./ProjectDetailsHandler":@ProjectDetailsHandler
 			"settings-sharelatex": @Settings = {}
@@ -60,32 +65,48 @@ describe 'ProjectCreationHandler', ->
 
 	describe 'Creating a Blank project', ->
 		beforeEach ->
+			@overleaf_id = 1234
+			@HistoryController.initializeProject = sinon.stub().callsArgWith(0, null, { @overleaf_id })
 			@ProjectModel::save = sinon.stub().callsArg(0)
 
 		describe "successfully", ->
-
 			it "should save the project", (done)->
 				@handler.createBlankProject ownerId, projectName, =>
 					@ProjectModel::save.called.should.equal true
 					done()
-				
+
 			it "should return the project in the callback", (done)->
 				@handler.createBlankProject ownerId, projectName, (err, project)->
 					project.name.should.equal projectName
 					(project.owner_ref + "").should.equal ownerId
 					done()
 
+			it "should initialize the project overleaf if history id not provided", (done)->
+				@handler.createBlankProject ownerId, projectName, done
+				@HistoryController.initializeProject.calledWith().should.equal true
+
+			it "should set the overleaf id if overleaf id not provided", (done)->
+				@handler.createBlankProject ownerId, projectName, (err, project)=>
+					project.overleaf.history.id.should.equal @overleaf_id
+					done()
+
+			it "should set the overleaf id if overleaf id provided", (done)->
+				overleaf_id = 2345
+				@handler.createBlankProject ownerId, projectName, overleaf_id, (err, project)->
+					project.overleaf.history.id.should.equal overleaf_id
+					done()
+
 			it "should set the language from the user", (done)->
 				@handler.createBlankProject ownerId, projectName, (err, project)->
 					project.spellCheckLanguage.should.equal "de"
 					done()
-			
+
 			it "should set the imageName to currentImageName if set", (done) ->
 				@Settings.currentImageName = "mock-image-name"
 				@handler.createBlankProject ownerId, projectName, (err, project)=>
 					project.imageName.should.equal @Settings.currentImageName
 					done()
-			
+
 			it "should not set the imageName if no currentImageName", (done) ->
 				@Settings.currentImageName = null
 				@handler.createBlankProject ownerId, projectName, (err, project)=>
@@ -96,21 +117,21 @@ describe 'ProjectCreationHandler', ->
 			beforeEach ->
 				@ProjectModel::save = sinon.stub().callsArgWith(0, new Error("something went wrong"))
 				@handler.createBlankProject ownerId, projectName, @callback
-			
+
 			it 'should return the error to the callback', ->
 				should.exist @callback.args[0][0]
-		
+
 		describe "with an invalid name", ->
 			beforeEach ->
 				@ProjectDetailsHandler.validateProjectName = sinon.stub().yields(new Error("bad name"))
 				@handler.createBlankProject ownerId, projectName, @callback
-			
+
 			it 'should return the error to the callback', ->
 				should.exist @callback.args[0][0]
-			
+
 			it 'should not try to create the project', ->
 				@ProjectModel::save.called.should.equal false
-				
+
 
 	describe 'Creating a basic project', ->
 		beforeEach ->