Merge pull request #1040 from sharelatex/bg-start-history-in-v2-for-old-projects

allow creating v2 history for existing project without v2 history

GitOrigin-RevId: 2ccbe55f5c203c37d37be00306de5e265555f909
This commit is contained in:
Brian Gough 2018-10-29 13:43:46 +00:00 committed by sharelatex
parent 3d94be22b1
commit 707a258d5b
3 changed files with 158 additions and 0 deletions

View file

@ -27,6 +27,17 @@ module.exports = HistoryManager =
error = new Error("project-history returned a non-success status code: #{res.statusCode}")
callback error
flushProject: (project_id, callback = (error) ->) ->
request.post {
url: "#{settings.apis.project_history.url}/project/#{project_id}/flush"
}, (error, res, body)->
return callback(error) if error?
if res.statusCode >= 200 and res.statusCode < 300
callback()
else
error = new Error("project-history returned a non-success status code: #{res.statusCode}")
callback error
injectUserDetails: (data, callback = (error, data_with_users) ->) ->
# data can be either:
# {

View file

@ -0,0 +1,42 @@
Project = require('../../models/Project').Project
ProjectDetailsHandler = require "./ProjectDetailsHandler"
logger = require('logger-sharelatex')
settings = require("settings-sharelatex")
HistoryManager = require "../History/HistoryManager"
ProjectEntityUpdateHandler = require "./ProjectEntityUpdateHandler"
module.exports = ProjectHistoryHandler =
setHistoryId: (project_id, history_id, callback = (err) ->) ->
# reject invalid history ids
return callback(new Error("invalid history id")) if !history_id or typeof(history_id) isnt 'number'
# use $exists:false to prevent overwriting any existing history id, atomically
Project.update {_id: project_id, "overleaf.history.id": {$exists:false}}, {"overleaf.history.id":history_id}, (err, result)->
return callback(err) if err?
return callback(new Error("history exists")) if result?.n == 0
callback()
getHistoryId: (project_id, callback = (err, result) ->) ->
ProjectDetailsHandler.getDetails project_id, (err, project) ->
return callback(err) if err? # n.b. getDetails returns an error if the project doesn't exist
return callback(new Error("history exists")) if project.overleaf?.history?.id?
callback(null, project?.overleaf?.history?.id)
ensureHistoryExistsForProject: (project_id, callback = (err) ->) ->
# We can only set a history id for a project that doesn't have one. The
# history id is cached in the project history service, and changing an
# existing value corrupts the history, leaving it in an irrecoverable
# state. Setting a history id when one wasn't present before is ok,
# because undefined history ids aren't cached.
ProjectHistoryHandler.getHistoryId project_id, (err, history_id) ->
return callback(err) if err?
return callback() if history_id? # history already exists, success
HistoryManager.initializeProject (err, history) ->
return callback(err) if err?
return callback(new Error("failed to initialize history id")) if !history?.overleaf_id
ProjectHistoryHandler.setHistoryId project_id, history.overleaf_id, (err) ->
return callback(err) if err?
ProjectEntityUpdateHandler.resyncProjectHistory project_id, (err) ->
return callback(err) if err?
logger.log {project_id: project_id, history_id: history.overleaf_id}, "started syncing project with new history id"
HistoryManager.flushProject project_id, callback

View file

@ -0,0 +1,105 @@
chai = require('chai')
assert = require('chai').assert
should = chai.should()
expect = chai.expect
sinon = require 'sinon'
modulePath = "../../../../app/js/Features/Project/ProjectHistoryHandler"
SandboxedModule = require('sandboxed-module')
ObjectId = require("mongoose").Types.ObjectId
describe 'ProjectHistoryHandler', ->
project_id = '4eecb1c1bffa66588e0000a1'
userId = 1234
beforeEach ->
@ProjectModel = class Project
constructor:(options)->
@._id = project_id
@name = "project_name_here"
@rev = 0
rootFolder:[@rootFolder]
@project = new @ProjectModel()
@callback = sinon.stub()
@ProjectHistoryHandler = SandboxedModule.require modulePath, requires:
'logger-sharelatex': @logger = {log:sinon.stub(), error: sinon.stub(), err:->}
'settings-sharelatex': @Settings = {}
'../../models/Project': Project:@ProjectModel
'./ProjectDetailsHandler': @ProjectDetailsHandler = {}
'../History/HistoryManager': @HistoryManager = {}
'./ProjectEntityUpdateHandler': @ProjectEntityUpdateHandler = {}
describe "starting history for an existing project", ->
beforeEach ->
@newHistoryId = 123456789
@HistoryManager.initializeProject = sinon.stub().callsArgWith(0, null, {overleaf_id: @newHistoryId})
@HistoryManager.flushProject = sinon.stub().callsArg(1)
@ProjectEntityUpdateHandler.resyncProjectHistory = sinon.stub().callsArg(1)
describe "when the history does not already exist", ->
beforeEach ->
@ProjectDetailsHandler.getDetails = sinon.stub().withArgs(project_id).callsArgWith(1, null, @project)
@ProjectModel.update = sinon.stub().callsArgWith(2,null,{n:1})
@ProjectHistoryHandler.ensureHistoryExistsForProject project_id, @callback
it "should get any existing history id for the project", ->
@ProjectDetailsHandler.getDetails
.calledWith(project_id)
.should.equal true
it "should initialize a new history in the v1 history service", ->
@HistoryManager.initializeProject
.called.should.equal.true
it "should set the new history id on the project", ->
@ProjectModel.update
.calledWith({_id: project_id, "overleaf.history.id": {$exists:false}}, {"overleaf.history.id":@newHistoryId})
.should.equal true
it "should resync the project history", ->
@ProjectEntityUpdateHandler.resyncProjectHistory
.calledWith(project_id)
.should.equal true
it "should flush the project history", ->
@HistoryManager.flushProject
.calledWith(project_id)
.should.equal true
it "should call the callback without an error", ->
@callback.called.should.equal true
describe "when the history already exists", ->
beforeEach ->
@project.overleaf = {history: {id: 1234}}
@ProjectDetailsHandler.getDetails = sinon.stub().withArgs(project_id).callsArgWith(1, null, @project)
@ProjectModel.update = sinon.stub()
@ProjectHistoryHandler.ensureHistoryExistsForProject project_id, @callback
it "should get any existing history id for the project", ->
@ProjectDetailsHandler.getDetails
.calledWith(project_id)
.should.equal true
it "should not initialize a new history in the v1 history service", ->
@HistoryManager.initializeProject
.called.should.equal false
it "should not set the new history id on the project", ->
@ProjectModel.update
.called
.should.equal false
it "should not resync the project history", ->
@ProjectEntityUpdateHandler.resyncProjectHistory
.called
.should.equal false
it "should not flush the project history", ->
@HistoryManager.flushProject
.called
.should.equal false
it "should call the callback", ->
@callback.calledWith().should.equal true