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

added inactive and reactivate project logic

Henry Oswald 11 лет назад
Родитель
Сommit
a0142d4415

+ 28 - 0
services/web/app/coffee/Features/Docstore/DocstoreManager.coffee

@@ -67,3 +67,31 @@ module.exports = DocstoreManager =
 				error = new Error("docstore api responded with non-success code: #{res.statusCode}")
 				logger.error err: error, project_id: project_id, doc_id: doc_id, "error updating doc in docstore"
 				callback(error)
+
+	archiveProject: (project_id, callback)->
+		url = "#{settings.apis.docstore.url}/project/#{project_id}/archive"
+		logger.log project_id:project_id, "archiving project in docstore"
+		request.post url, (err, res, docs) ->
+			if err?
+				logger.err err:err, project_id:project_id, "error archving project in docstore"
+				return callback(err)
+			if 200 <= res.statusCode < 300
+				callback()	
+			else
+				error = new Error("docstore api responded with non-success code: #{res.statusCode}")
+				logger.err err: error, project_id: project_id, "error archiving project in docstore"
+				return callback(error)
+
+	unarchiveProject: (project_id, callback)->
+		url = "#{settings.apis.docstore.url}/project/#{project_id}/unarchive"
+		logger.log project_id:project_id, "unarchiving project in docstore"
+		request.post url, (err, res, docs) ->
+			if err?
+				logger.err err:err, project_id:project_id, "error unarchiving project in docstore"
+				return callback(err)
+			if 200 <= res.statusCode < 300
+				callback()	
+			else
+				error = new Error("docstore api responded with non-success code: #{res.statusCode}")
+				logger.err err: error, project_id: project_id, "error unarchiving project in docstore"
+				return callback(error)

+ 11 - 0
services/web/app/coffee/Features/InactiveData/InactiveProjectController.coffee

@@ -0,0 +1,11 @@
+InactiveProjectManager = require("./InactiveProjectManager")
+
+module.exports = 
+
+	deactivateOldProjects: (req, res)->
+		InactiveProjectManager.deactivateOldProjects 10, (err)->
+			if err?
+				res.sendStatus(500)
+			else
+				res.sendStatus(200)
+

+ 53 - 0
services/web/app/coffee/Features/InactiveData/InactiveProjectManager.coffee

@@ -0,0 +1,53 @@
+async = require("async")
+_ = require("lodash")
+logger = require("logger-sharelatex")
+DocstoreManager = require("../Docstore/DocstoreManager")
+ProjectGetter = require("../Project/ProjectGetter")
+ProjectUpdateHandler = require("../Project/ProjectUpdateHandler")
+Project = require("../../models/Project").Project
+
+MILISECONDS_IN_DAY = 86400000
+module.exports = InactiveProjectManager =
+
+	reactivateProjectIfRequired: (project_id, callback)->
+		ProjectGetter.getProject project_id, {inactive:true}, (err, project)->
+			if err?
+				logger.err err:err, project_id:project_id, "error getting project"
+				return callback(err)
+			logger.log project_id:project_id, inactive:project.inactive, "seeing if need to reactivate project"
+
+			if !project.inactive
+				return callback()
+
+			DocstoreManager.unarchiveProject project_id, (err)->
+				if err?
+					logger.err err:err, project_id:project_id, "error reactivating project in docstore"
+					return callback(err)
+				ProjectUpdateHandler.markAsActive project_id, callback
+
+	deactivateOldProjects: (limit, callback)->
+
+		sixMonthsAgo = new Date() - (MILISECONDS_IN_DAY * 1)
+		Project.find()
+			.where("lastOpened").lt(sixMonthsAgo)
+			.where("inactive").ne(true)
+			.select("_id")
+			.limit(limit)
+			.exec (err, projects)->
+				if err?
+					logger.err err:err, "could not get projects for deactivating"
+				jobs = _.map projects, (project)->
+					return (cb)->
+						InactiveProjectManager.deactivateProject project._id, cb
+				logger.log numberOfProjects:projects?.length, "deactivating projects"
+				async.series jobs, callback
+
+
+	deactivateProject: (project_id, callback)->
+		logger.log project_id:project_id, "deactivating inactive project"
+		DocstoreManager.archiveProject project_id, (err)->
+			if err?
+				logger.err err:err, project_id:project_id, "error deactivating project in docstore"
+				return callback(err)
+			ProjectUpdateHandler.markAsInactive project_id, callback
+

+ 6 - 0
services/web/app/coffee/Features/Project/ProjectController.coffee

@@ -14,6 +14,8 @@ _ = require("underscore")
 Settings = require("settings-sharelatex")
 SecurityManager = require("../../managers/SecurityManager")
 fs = require "fs"
+InactiveProjectManager = require("../InactiveData/InactiveProjectManager")
+ProjectUpdateHandler = require("./ProjectUpdateHandler")
 
 module.exports = ProjectController =
 
@@ -189,6 +191,10 @@ module.exports = ProjectController =
 				if user_id == 'openUser'
 					return cb()
 				SubscriptionLocator.getUsersSubscription user_id, cb
+			activate: (cb)->
+				InactiveProjectManager.reactivateProjectIfRequired project_id, cb
+			markOpened: (cb)-> 
+				ProjectUpdateHandler.markOpened project_id, cb
 		}, (err, results)->
 			if err?
 				logger.err err:err, "error getting details for project page"

+ 22 - 0
services/web/app/coffee/Features/Project/ProjectUpdateHandler.coffee

@@ -1,5 +1,6 @@
 Project = require('../../models/Project').Project
 logger = require('logger-sharelatex')
+Project = require("../../models/Project").Project
 
 module.exports = 
 	markAsUpdated : (project_id, callback)->
@@ -8,3 +9,24 @@ module.exports =
 		Project.update conditions, update, {}, (err)->
 			if callback?
 				callback()
+
+	markAsOpened : (project_id, callback)->
+		conditions = {_id:project_id}
+		update = {lastOpened:Date.now()}
+		Project.update conditions, update, {}, (err)->
+			if callback?
+				callback()
+
+	markAsInactive: (project_id, callback)->
+		conditions = {_id:project_id}
+		update = {inactive:true}
+		Project.update conditions, update, {}, (err)->
+			if callback?
+				callback()
+
+	markAsActive: (project_id, callback)->
+		conditions = {_id:project_id}
+		update = { $unset: { inactive: true }}
+		Project.update conditions, update, {}, (err)->
+			if callback?
+				callback()

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

@@ -17,6 +17,8 @@ DeletedDocSchema = new Schema
 ProjectSchema = new Schema
 	name              :   {type:String, default:'new project'}
 	lastUpdated       :   {type:Date, default: () -> new Date()}
+	lastOpened		  :   {type:Date}
+	inactive		  :   { type: Boolean }
 	owner_ref         :   {type:ObjectId, ref:'User'}
 	collaberator_refs :   [ type:ObjectId, ref:'User' ]
 	readOnly_refs     :   [ type:ObjectId, ref:'User' ]

+ 40 - 1
services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee

@@ -13,7 +13,7 @@ describe "DocstoreManager", ->
 				apis:
 					docstore:
 						url: "docstore.sharelatex.com"
-			"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()}
+			"logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub(), err:->}
 
 		@requestDefaults.calledWith(jar: false).should.equal true
 
@@ -179,3 +179,42 @@ describe "DocstoreManager", ->
 						project_id: @project_id
 					}, "error getting all docs from docstore")
 					.should.equal true
+
+
+	describe "archiveProject", ->
+		describe "with a successful response code", ->
+			beforeEach ->
+				@request.post = sinon.stub().callsArgWith(1, null, statusCode: 204)
+				@DocstoreManager.archiveProject @project_id, @callback
+
+			it "should call the callback", ->
+				@callback.called.should.equal true
+
+		describe "with a failed response code", ->
+			beforeEach ->
+				@request.post = sinon.stub().callsArgWith(1, null, statusCode: 500)
+				@DocstoreManager.archiveProject @project_id, @callback
+
+			it "should call the callback with an error", ->
+				@callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true
+
+
+
+	describe "unarchiveProject", ->
+		describe "with a successful response code", ->
+			beforeEach ->
+				@request.post = sinon.stub().callsArgWith(1, null, statusCode: 204)
+				@DocstoreManager.unarchiveProject @project_id, @callback
+
+			it "should call the callback", ->
+				@callback.called.should.equal true
+
+		describe "with a failed response code", ->
+			beforeEach ->
+				@request.post = sinon.stub().callsArgWith(1, null, statusCode: 500)
+				@DocstoreManager.unarchiveProject @project_id, @callback
+
+			it "should call the callback with an error", ->
+				@callback.calledWith(new Error("docstore api responded with non-success code: 500")).should.equal true
+
+

+ 85 - 0
services/web/test/UnitTests/coffee/InactiveData/InactiveProjectManagerTests.coffee

@@ -0,0 +1,85 @@
+should = require('chai').should()
+SandboxedModule = require('sandboxed-module')
+assert = require('assert')
+path = require('path')
+sinon = require('sinon')
+modulePath = path.join __dirname, "../../../../app/js/Features/InactiveData/InactiveProjectManager"
+expect = require("chai").expect
+
+describe "InactiveProjectManager", ->
+
+	beforeEach ->
+
+		@settings = {}
+		@DocstoreManager =
+			unarchiveProject:sinon.stub()
+		@ProjectUpdateHandler = 
+			markAsActive:sinon.stub()
+			markAsInactive:sinon.stub()
+		@ProjectGetter = 
+			getProject:sinon.stub()
+		@InactiveProjectManager = SandboxedModule.require modulePath, requires:
+			"settings-sharelatex":@settings
+			"logger-sharelatex": 
+				log:->
+				err:->
+			"../Docstore/DocstoreManager":@DocstoreManager
+			"../Project/ProjectUpdateHandler":@ProjectUpdateHandler
+			"../Project/ProjectGetter":@ProjectGetter
+
+		@project_id = "1234"
+
+	describe "reactivateProjectIfRequired", ->
+
+		beforeEach ->
+			@project = {inactive:true}
+			@ProjectGetter.getProject.callsArgWith(2, null, @project)
+			@ProjectUpdateHandler.markAsActive.callsArgWith(1)
+
+		it "should call unarchiveProject", (done)->
+			@DocstoreManager.unarchiveProject.callsArgWith(1)
+			@InactiveProjectManager.reactivateProjectIfRequired @project_id, (err)=>
+				@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal true
+				@ProjectUpdateHandler.markAsActive.calledWith(@project_id).should.equal true
+				done()
+
+		it "should not mark project as active if error with unarchinging", (done)->
+			@DocstoreManager.unarchiveProject.callsArgWith(1, "error")
+			@InactiveProjectManager.reactivateProjectIfRequired @project_id, (err)=>
+				err.should.equal "error"
+				@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal true
+				@ProjectUpdateHandler.markAsActive.calledWith(@project_id).should.equal false
+				done()
+
+
+		it "should not call unarchiveProject if it is not inactive", (done)->
+			delete @project.inactive
+			@DocstoreManager.unarchiveProject.callsArgWith(1)
+			@InactiveProjectManager.reactivateProjectIfRequired @project_id, (err)=>
+				@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal false
+				@ProjectUpdateHandler.markAsActive.calledWith(@project_id).should.equal false
+				done()
+
+
+	describe "deactivateProject", ->
+
+		beforeEach ->
+
+		it "should call unarchiveProject and markAsInactive", (done)->
+			@DocstoreManager.unarchiveProject.callsArgWith(1)
+			@ProjectUpdateHandler.markAsInactive.callsArgWith(1)
+
+			@InactiveProjectManager.deactivateProject @project_id, (err)->
+				@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal true
+				@ProjectUpdateHandler.markAsInactive.callsArgWith(@project_id).should.equal true
+				done()
+
+		it "should not call markAsInactive if there was a problem unarchiving", (done)->
+			@DocstoreManager.unarchiveProject.callsArgWith(1, "errorrr")
+			@ProjectUpdateHandler.markAsInactive.callsArgWith(1)
+
+			@InactiveProjectManager.deactivateProject @project_id, (err)->
+				err.should.equal "errorrr"
+				@DocstoreManager.unarchiveProject.calledWith(@project_id).should.equal true
+				@ProjectUpdateHandler.markAsInactive.callsArgWith(@project_id).should.equal false
+				done()

+ 22 - 0
services/web/test/UnitTests/coffee/Project/ProjectControllerTests.coffee

@@ -42,6 +42,10 @@ describe "ProjectController", ->
 			userCanAccessProject:sinon.stub()
 		@EditorController = 
 			renameProject:sinon.stub()
+		@InactiveProjectManager =
+			reactivateProjectIfRequired:sinon.stub()
+		@ProjectUpdateHandler =
+			markOpened: sinon.stub()
 		@ProjectController = SandboxedModule.require modulePath, requires:
 			"settings-sharelatex":@settings
 			"logger-sharelatex": 
@@ -57,6 +61,8 @@ describe "ProjectController", ->
 			'../../models/Project': Project:@ProjectModel
 			"../../models/User":User:@UserModel
 			"../../managers/SecurityManager":@SecurityManager
+			"../InactiveData/InactiveProjectManager":@InactiveProjectManager
+			"./ProjectUpdateHandler":@ProjectUpdateHandler
 
 		@user = 
 			_id:"!£123213kjljkl"
@@ -282,6 +288,9 @@ describe "ProjectController", ->
 			@SubscriptionLocator.getUsersSubscription.callsArgWith(1, null, {})
 			@SecurityManager.userCanAccessProject.callsArgWith 2, true, "owner"
 			@ProjectDeleter.unmarkAsDeletedByExternalSource = sinon.stub()
+			@InactiveProjectManager.reactivateProjectIfRequired.callsArgWith(1)
+			@ProjectUpdateHandler.markOpened.callsArgWith(1)
+
 
 		it "should render the project/editor page", (done)->
 			@res.render = (pageName, opts)=>
@@ -321,3 +330,16 @@ describe "ProjectController", ->
 				resCode.should.equal 401
 				done()
 			@ProjectController.loadEditor @req, @res
+
+		it "should reactivateProjectIfRequired", (done)->
+			@res.render = (pageName, opts)=>
+				@InactiveProjectManager.reactivateProjectIfRequired.calledWith(@project_id).should.equal true
+				done()
+			@ProjectController.loadEditor @req, @res
+
+		it "should mark project as opened", (done)->
+			@res.render = (pageName, opts)=>
+				@ProjectUpdateHandler.markOpened.calledWith(@project_id).should.equal true
+				done()
+			@ProjectController.loadEditor @req, @res
+

+ 34 - 1
services/web/test/UnitTests/coffee/Project/ProjectUpdateHandlerTests.coffee

@@ -3,7 +3,7 @@ chai = require('chai').should()
 modulePath = "../../../../app/js/Features/Project/ProjectUpdateHandler.js"
 SandboxedModule = require('sandboxed-module')
 
-describe 'updating a project', ->
+describe 'ProjectUpdateHandler', ->
 
 
 	beforeEach ->
@@ -22,3 +22,36 @@ describe 'updating a project', ->
 				now = Date.now()+""
 				date.substring(0,5).should.equal now.substring(0,5)
 				done()
+
+	describe "markAsOpened", ->
+
+		it 'should send an update to mongo', (done)->
+			project_id = "project_id"
+			@handler.markAsOpened project_id, (err)=>
+				args = @ProjectModel.update.args[0]
+				args[0]._id.should.equal project_id
+				date = args[1].lastOpened+""
+				now = Date.now()+""
+				date.substring(0,5).should.equal now.substring(0,5)
+				done()
+
+	describe "markAsInactive", ->
+
+		it 'should send an update to mongo', (done)->
+			project_id = "project_id"
+			@handler.markAsInactive project_id, (err)=>
+				args = @ProjectModel.update.args[0]
+				args[0]._id.should.equal project_id
+				args[1].inactive.should.equal true
+				done()
+
+	describe "markAsActive", ->
+		it 'should send an update to mongo', (done)->
+			project_id = "project_id"
+			@handler.markAsActive project_id, (err)=>
+				args = @ProjectModel.update.args[0]
+				args[0]._id.should.equal project_id
+				args[1]["$unset"].inactive.should.equal true
+				done()
+
+