version document renames

This commit is contained in:
Hayden Faulds 2017-11-01 19:16:49 +00:00
parent b8052e7612
commit 6d571e6d23
9 changed files with 205 additions and 2 deletions

View file

@ -47,6 +47,7 @@ app.post '/project/:project_id/doc/:doc_id', HttpCont
app.post '/project/:project_id/doc/:doc_id/flush', HttpController.flushDocIfLoaded
app.delete '/project/:project_id/doc/:doc_id', HttpController.flushAndDeleteDoc
app.delete '/project/:project_id', HttpController.deleteProject
app.post '/project/:project_id', HttpController.updateProject
app.post '/project/:project_id/flush', HttpController.flushProject
app.post '/project/:project_id/doc/:doc_id/change/:change_id/accept', HttpController.acceptChanges
app.post '/project/:project_id/doc/:doc_id/change/accept', HttpController.acceptChanges

View file

@ -7,6 +7,7 @@ HistoryManager = require "./HistoryManager"
RealTimeRedisManager = require "./RealTimeRedisManager"
Errors = require "./Errors"
RangesManager = require "./RangesManager"
async = require "async"
MAX_UNFLUSHED_AGE = 300 * 1000 # 5 mins, document should be flushed to mongo this time after a change
@ -155,6 +156,14 @@ module.exports = DocumentManager =
return callback(error) if error?
callback()
renameDoc: (project_id, doc_id, user_id, update, _callback = (error) ->) ->
timer = new Metrics.Timer("docManager.updateProject")
callback = (args...) ->
timer.done()
_callback(args...)
RedisManager.renameDoc project_id, doc_id, user_id, update, callback
getDocAndFlushIfOld: (project_id, doc_id, callback = (error, doc) ->) ->
DocumentManager.getDoc project_id, doc_id, (error, lines, version, ranges, pathname, unflushedTime, alreadyLoaded) ->
return callback(error) if error?
@ -197,3 +206,7 @@ module.exports = DocumentManager =
deleteCommentWithLock: (project_id, doc_id, thread_id, callback = (error) ->) ->
UpdateManager = require "./UpdateManager"
UpdateManager.lockUpdatesAndDo DocumentManager.deleteComment, project_id, doc_id, thread_id, callback
renameDocWithLock: (project_id, doc_id, user_id, update, callback = (error) ->) ->
UpdateManager = require "./UpdateManager"
UpdateManager.lockUpdatesAndDo DocumentManager.renameDoc, project_id, doc_id, user_id, update, callback

View file

@ -152,4 +152,14 @@ module.exports = HttpController =
logger.log {project_id, doc_id, comment_id}, "deleted comment via http"
res.send 204 # No Content
updateProject: (req, res, next = (error) ->) ->
timer = new Metrics.Timer("http.updateProject")
project_id = req.params.project_id
{userId, docUpdates} = req.body
logger.log {project_id, docUpdates}, "updating project via http"
ProjectManager.updateProjectWithLocks project_id, userId, docUpdates, (error) ->
timer.done()
return next(error) if error?
logger.log project_id: project_id, "updated project via http"
res.send 204 # No Content

View file

@ -93,3 +93,15 @@ module.exports = ProjectManager =
clearProjectState: (project_id, callback = (error) ->) ->
RedisManager.clearProjectState project_id, callback
updateProjectWithLocks: (project_id, user_id, updates, _callback = (error) ->) ->
timer = new Metrics.Timer("projectManager.updateProject")
callback = (args...) ->
timer.done()
_callback(args...)
handleUpdate = (update, cb) ->
doc_id = update.id
DocumentManager.renameDocWithLock project_id, doc_id, user_id, update, cb
async.each updates, handleUpdate, callback

View file

@ -272,6 +272,22 @@ module.exports = RedisManager =
else
callback null, docUpdateCount
renameDoc: (project_id, doc_id, user_id, update, callback = (error) ->) ->
update =
doc: doc_id
pathname: update.pathname
new_pathname: update.newPathname
meta:
user_id: user_id
ts: new Date()
jsonUpdate = JSON.stringify(update)
RedisManager.getDoc project_id, doc_id, (error, lines, version) ->
return callback(error) if error?
if lines? and version?
rclient.set keys.pathname(doc_id:doc_id), update.new_pathname
rclient.rpush projectHistoryKeys.projectHistoryOps({project_id}), jsonUpdate, callback
clearUnflushedTime: (doc_id, callback = (error) ->) ->
rclient.del keys.unflushedTime(doc_id:doc_id), callback

View file

@ -23,6 +23,7 @@ describe "DocumentManager", ->
"./RangesManager": @RangesManager = {}
@project_id = "project-id-123"
@doc_id = "doc-id-123"
@user_id = 1234
@callback = sinon.stub()
@lines = ["one", "two", "three"]
@version = 42
@ -439,3 +440,20 @@ describe "DocumentManager", ->
it "should call the callback with the lines and versions", ->
@callback.calledWith(null, @lines, @version).should.equal true
describe "renameDoc", ->
beforeEach ->
@update = 'some-update'
@RedisManager.renameDoc = sinon.stub().yields()
describe "successfully", ->
beforeEach ->
@DocumentManager.renameDoc @project_id, @doc_id, @user_id, @update, @callback
it "should rename the document", ->
@RedisManager.renameDoc
.calledWith(@project_id, @doc_id, @user_id, @update)
.should.equal true
it "should call the callback", ->
@callback.called.should.equal true

View file

@ -492,3 +492,40 @@ describe "HttpController", ->
@next
.calledWith(new Error("oops"))
.should.equal true
describe "updateProject", ->
beforeEach ->
@userId = "user-id-123"
@docUpdates = sinon.stub()
@req =
body: {@userId, @docUpdates}
params:
project_id: @project_id
describe "successfully", ->
beforeEach ->
@ProjectManager.updateProjectWithLocks = sinon.stub().callsArgWith(3)
@HttpController.updateProject(@req, @res, @next)
it "should accept the change", ->
@ProjectManager.updateProjectWithLocks
.calledWith(@project_id, @userId, @docUpdates)
.should.equal true
it "should return a successful No Content response", ->
@res.send
.calledWith(204)
.should.equal true
it "should time the request", ->
@Metrics.Timer::done.called.should.equal true
describe "when an errors occurs", ->
beforeEach ->
@ProjectManager.updateProjectWithLocks = sinon.stub().callsArgWith(3, new Error("oops"))
@HttpController.updateProject(@req, @res, @next)
it "should call next with the error", ->
@next
.calledWith(new Error("oops"))
.should.equal true

View file

@ -0,0 +1,54 @@
sinon = require('sinon')
chai = require('chai')
should = chai.should()
modulePath = "../../../../app/js/ProjectManager.js"
SandboxedModule = require('sandboxed-module')
describe "ProjectManager", ->
beforeEach ->
@ProjectManager = SandboxedModule.require modulePath, requires:
"./RedisManager": @RedisManager = {}
"./DocumentManager": @DocumentManager = {}
"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
"./Metrics": @Metrics =
Timer: class Timer
done: sinon.stub()
@project_id = "project-id-123"
@user_id = "user-id-123"
@callback = sinon.stub()
describe "updateProjectWithLocks", ->
beforeEach ->
@firstUpdate =
id: 1
update: 'foo'
@secondUpdate =
id: 2
update: 'bar'
@updates = [ @firstUpdate, @secondUpdate ]
describe "successfully", ->
beforeEach ->
@DocumentManager.renameDocWithLock = sinon.stub().yields()
@ProjectManager.updateProjectWithLocks @project_id, @user_id, @updates, @callback
it "should rename the documents in the updates", ->
@DocumentManager.renameDocWithLock
.calledWith(@project_id, @firstUpdate.id, @user_id, @firstUpdate)
.should.equal true
@DocumentManager.renameDocWithLock
.calledWith(@project_id, @secondUpdate.id, @user_id, @secondUpdate)
.should.equal true
it "should call the callback", ->
@callback.called.should.equal true
describe "when renaming a doc fails", ->
beforeEach ->
@error = new Error('error')
@DocumentManager.renameDocWithLock = sinon.stub().yields(@error)
@ProjectManager.updateProjectWithLocks @project_id, @user_id, @updates, @callback
it "should call the callback with the error", ->
@callback.calledWith(@error).should.equal true

View file

@ -673,3 +673,45 @@ describe "RedisManager", ->
@rclient.del
.calledWith("ProjectState:#{@project_id}")
.should.equal true
describe "renameDoc", ->
beforeEach () ->
@rclient.rpush = sinon.stub().callsArg(2)
@rclient.set = sinon.stub()
@update =
id: @doc_id
pathname: @pathname = 'pathname'
newPathname: @newPathname = 'new-pathname'
describe "the document is cached in redis", ->
beforeEach ->
@RedisManager.getDoc = sinon.stub().callsArgWith(2, null, 'lines', 'version')
@RedisManager.renameDoc @project_id, @doc_id, @userId, @update, @callback
it "update the cached pathname", ->
@rclient.set
.calledWith("Pathname:#{@doc_id}", @newPathname)
.should.equal true
it "should queue an update", ->
update =
doc: @doc_id
pathname: @pathname
new_pathname: @newPathname
meta:
user_id: @userId
ts: new Date()
@rclient.rpush
.calledWith("ProjectHistory:Ops:#{@project_id}", JSON.stringify(update))
.should.equal true
it "should call the callback", ->
@callback.calledWith().should.equal true
describe "the document is not cached in redis", ->
beforeEach ->
@RedisManager.getDoc = sinon.stub().callsArgWith(2, null, null, null)
@RedisManager.renameDoc @project_id, @doc_id, @userId, @update, @callback
it "does not update the cached pathname", ->
@rclient.set.called.should.equal false