mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Support an incoming undo flag for history restores
This commit is contained in:
parent
a3a5524778
commit
0245bfd031
6 changed files with 91 additions and 19 deletions
|
@ -43,7 +43,7 @@ module.exports = DocumentManager =
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
callback null, lines, version, ops, ranges
|
callback null, lines, version, ops, ranges
|
||||||
|
|
||||||
setDoc: (project_id, doc_id, newLines, source, user_id, _callback = (error) ->) ->
|
setDoc: (project_id, doc_id, newLines, source, user_id, undoing, _callback = (error) ->) ->
|
||||||
timer = new Metrics.Timer("docManager.setDoc")
|
timer = new Metrics.Timer("docManager.setDoc")
|
||||||
callback = (args...) ->
|
callback = (args...) ->
|
||||||
timer.done()
|
timer.done()
|
||||||
|
@ -63,6 +63,9 @@ module.exports = DocumentManager =
|
||||||
logger.log doc_id: doc_id, project_id: project_id, oldLines: oldLines, newLines: newLines, "setting a document via http"
|
logger.log doc_id: doc_id, project_id: project_id, oldLines: oldLines, newLines: newLines, "setting a document via http"
|
||||||
DiffCodec.diffAsShareJsOp oldLines, newLines, (error, op) ->
|
DiffCodec.diffAsShareJsOp oldLines, newLines, (error, op) ->
|
||||||
return callback(error) if error?
|
return callback(error) if error?
|
||||||
|
if undoing
|
||||||
|
for o in op or []
|
||||||
|
o.u = true # Turn on undo flag for each op for track changes
|
||||||
update =
|
update =
|
||||||
doc: doc_id
|
doc: doc_id
|
||||||
op: op
|
op: op
|
||||||
|
@ -161,9 +164,9 @@ module.exports = DocumentManager =
|
||||||
UpdateManager = require "./UpdateManager"
|
UpdateManager = require "./UpdateManager"
|
||||||
UpdateManager.lockUpdatesAndDo DocumentManager.getDocAndRecentOps, project_id, doc_id, fromVersion, callback
|
UpdateManager.lockUpdatesAndDo DocumentManager.getDocAndRecentOps, project_id, doc_id, fromVersion, callback
|
||||||
|
|
||||||
setDocWithLock: (project_id, doc_id, lines, source, user_id, callback = (error) ->) ->
|
setDocWithLock: (project_id, doc_id, lines, source, user_id, undoing, callback = (error) ->) ->
|
||||||
UpdateManager = require "./UpdateManager"
|
UpdateManager = require "./UpdateManager"
|
||||||
UpdateManager.lockUpdatesAndDo DocumentManager.setDoc, project_id, doc_id, lines, source, user_id, callback
|
UpdateManager.lockUpdatesAndDo DocumentManager.setDoc, project_id, doc_id, lines, source, user_id, undoing, callback
|
||||||
|
|
||||||
flushDocIfLoadedWithLock: (project_id, doc_id, callback = (error) ->) ->
|
flushDocIfLoadedWithLock: (project_id, doc_id, callback = (error) ->) ->
|
||||||
UpdateManager = require "./UpdateManager"
|
UpdateManager = require "./UpdateManager"
|
||||||
|
|
|
@ -40,16 +40,14 @@ module.exports = HttpController =
|
||||||
setDoc: (req, res, next = (error) ->) ->
|
setDoc: (req, res, next = (error) ->) ->
|
||||||
doc_id = req.params.doc_id
|
doc_id = req.params.doc_id
|
||||||
project_id = req.params.project_id
|
project_id = req.params.project_id
|
||||||
lines = req.body.lines
|
{lines, source, user_id, undoing} = req.body
|
||||||
source = req.body.source
|
|
||||||
user_id = req.body.user_id
|
|
||||||
lineSize = HttpController._getTotalSizeOfLines(lines)
|
lineSize = HttpController._getTotalSizeOfLines(lines)
|
||||||
if lineSize > TWO_MEGABYTES
|
if lineSize > TWO_MEGABYTES
|
||||||
logger.log {project_id, doc_id, source, lineSize, user_id}, "document too large, returning 406 response"
|
logger.log {project_id, doc_id, source, lineSize, user_id}, "document too large, returning 406 response"
|
||||||
return res.send 406
|
return res.send 406
|
||||||
logger.log project_id: project_id, doc_id: doc_id, lines: lines, source: source, user_id: user_id, "setting doc via http"
|
logger.log {project_id, doc_id, lines, source, user_id, undoing}, "setting doc via http"
|
||||||
timer = new Metrics.Timer("http.setDoc")
|
timer = new Metrics.Timer("http.setDoc")
|
||||||
DocumentManager.setDocWithLock project_id, doc_id, lines, source, user_id, (error) ->
|
DocumentManager.setDocWithLock project_id, doc_id, lines, source, user_id, undoing, (error) ->
|
||||||
timer.done()
|
timer.done()
|
||||||
return next(error) if error?
|
return next(error) if error?
|
||||||
logger.log project_id: project_id, doc_id: doc_id, "set doc via http"
|
logger.log project_id: project_id, doc_id: doc_id, "set doc via http"
|
||||||
|
|
|
@ -41,7 +41,7 @@ describe "Setting a document", ->
|
||||||
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
||||||
throw error if error?
|
throw error if error?
|
||||||
setTimeout () =>
|
setTimeout () =>
|
||||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, (error, res, body) =>
|
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, false, (error, res, body) =>
|
||||||
@statusCode = res.statusCode
|
@statusCode = res.statusCode
|
||||||
done()
|
done()
|
||||||
, 200
|
, 200
|
||||||
|
@ -74,7 +74,7 @@ describe "Setting a document", ->
|
||||||
before (done) ->
|
before (done) ->
|
||||||
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||||
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||||
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, (error, res, body) =>
|
DocUpdaterClient.setDocLines @project_id, @doc_id, @newLines, @source, @user_id, false, (error, res, body) =>
|
||||||
@statusCode = res.statusCode
|
@statusCode = res.statusCode
|
||||||
setTimeout done, 200
|
setTimeout done, 200
|
||||||
|
|
||||||
|
@ -94,3 +94,60 @@ describe "Setting a document", ->
|
||||||
throw error if error?
|
throw error if error?
|
||||||
expect(lines).to.not.exist
|
expect(lines).to.not.exist
|
||||||
done()
|
done()
|
||||||
|
|
||||||
|
describe "with track changes", ->
|
||||||
|
before ->
|
||||||
|
@lines = ["one", "one and a half", "two", "three"]
|
||||||
|
@id_seed = "587357bd35e64f6157"
|
||||||
|
@update =
|
||||||
|
doc: @doc_id
|
||||||
|
op: [{
|
||||||
|
d: "one and a half\n"
|
||||||
|
p: 4
|
||||||
|
}]
|
||||||
|
meta:
|
||||||
|
tc: @id_seed
|
||||||
|
user_id: @user_id
|
||||||
|
v: @version
|
||||||
|
|
||||||
|
describe "with the undo flag", ->
|
||||||
|
before (done) ->
|
||||||
|
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||||
|
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||||
|
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||||
|
throw error if error?
|
||||||
|
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
||||||
|
throw error if error?
|
||||||
|
# Go back to old lines, with undo flag
|
||||||
|
DocUpdaterClient.setDocLines @project_id, @doc_id, @lines, @source, @user_id, true, (error, res, body) =>
|
||||||
|
@statusCode = res.statusCode
|
||||||
|
setTimeout done, 200
|
||||||
|
|
||||||
|
it "should undo the tracked changes", (done) ->
|
||||||
|
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, data) =>
|
||||||
|
throw error if error?
|
||||||
|
ranges = data.ranges
|
||||||
|
expect(ranges.changes).to.be.undefined
|
||||||
|
done()
|
||||||
|
|
||||||
|
describe "without the undo flag", ->
|
||||||
|
before (done) ->
|
||||||
|
[@project_id, @doc_id] = [DocUpdaterClient.randomId(), DocUpdaterClient.randomId()]
|
||||||
|
MockWebApi.insertDoc @project_id, @doc_id, {lines: @lines, version: @version}
|
||||||
|
DocUpdaterClient.preloadDoc @project_id, @doc_id, (error) =>
|
||||||
|
throw error if error?
|
||||||
|
DocUpdaterClient.sendUpdate @project_id, @doc_id, @update, (error) =>
|
||||||
|
throw error if error?
|
||||||
|
# Go back to old lines, without undo flag
|
||||||
|
DocUpdaterClient.setDocLines @project_id, @doc_id, @lines, @source, @user_id, false, (error, res, body) =>
|
||||||
|
@statusCode = res.statusCode
|
||||||
|
setTimeout done, 200
|
||||||
|
|
||||||
|
it "should not undo the tracked changes", (done) ->
|
||||||
|
DocUpdaterClient.getDoc @project_id, @doc_id, (error, res, data) =>
|
||||||
|
throw error if error?
|
||||||
|
ranges = data.ranges
|
||||||
|
expect(ranges.changes.length).to.equal 1
|
||||||
|
done()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -53,13 +53,14 @@ module.exports = DocUpdaterClient =
|
||||||
request.post "http://localhost:3003/project/#{project_id}/doc/#{doc_id}/flush", (error, res, body) ->
|
request.post "http://localhost:3003/project/#{project_id}/doc/#{doc_id}/flush", (error, res, body) ->
|
||||||
callback error, res, body
|
callback error, res, body
|
||||||
|
|
||||||
setDocLines: (project_id, doc_id, lines, source, user_id, callback = (error) ->) ->
|
setDocLines: (project_id, doc_id, lines, source, user_id, undoing, callback = (error) ->) ->
|
||||||
request.post {
|
request.post {
|
||||||
url: "http://localhost:3003/project/#{project_id}/doc/#{doc_id}"
|
url: "http://localhost:3003/project/#{project_id}/doc/#{doc_id}"
|
||||||
json:
|
json:
|
||||||
lines: lines
|
lines: lines
|
||||||
source: source
|
source: source
|
||||||
user_id: user_id
|
user_id: user_id
|
||||||
|
undoing: undoing
|
||||||
}, (error, res, body) ->
|
}, (error, res, body) ->
|
||||||
callback error, res, body
|
callback error, res, body
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,7 @@ describe "DocumentManager", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@beforeLines = ["before", "lines"]
|
@beforeLines = ["before", "lines"]
|
||||||
@afterLines = ["after", "lines"]
|
@afterLines = ["after", "lines"]
|
||||||
|
@ops = [{ i: "foo", p: 4 }, { d: "bar", p: 42 }]
|
||||||
@DocumentManager.getDoc = sinon.stub().callsArgWith(2, null, @beforeLines, @version, @ranges, true)
|
@DocumentManager.getDoc = sinon.stub().callsArgWith(2, null, @beforeLines, @version, @ranges, true)
|
||||||
@DiffCodec.diffAsShareJsOp = sinon.stub().callsArgWith(2, null, @ops)
|
@DiffCodec.diffAsShareJsOp = sinon.stub().callsArgWith(2, null, @ops)
|
||||||
@UpdateManager.applyUpdate = sinon.stub().callsArgWith(3, null)
|
@UpdateManager.applyUpdate = sinon.stub().callsArgWith(3, null)
|
||||||
|
@ -202,7 +203,7 @@ describe "DocumentManager", ->
|
||||||
|
|
||||||
describe "when already loaded", ->
|
describe "when already loaded", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@DocumentManager.setDoc @project_id, @doc_id, @afterLines, @source, @user_id, @callback
|
@DocumentManager.setDoc @project_id, @doc_id, @afterLines, @source, @user_id, false, @callback
|
||||||
|
|
||||||
it "should get the current doc lines", ->
|
it "should get the current doc lines", ->
|
||||||
@DocumentManager.getDoc
|
@DocumentManager.getDoc
|
||||||
|
@ -246,7 +247,7 @@ describe "DocumentManager", ->
|
||||||
describe "when not already loaded", ->
|
describe "when not already loaded", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@DocumentManager.getDoc = sinon.stub().callsArgWith(2, null, @beforeLines, @version, false)
|
@DocumentManager.getDoc = sinon.stub().callsArgWith(2, null, @beforeLines, @version, false)
|
||||||
@DocumentManager.setDoc @project_id, @doc_id, @afterLines, @source, @user_id, @callback
|
@DocumentManager.setDoc @project_id, @doc_id, @afterLines, @source, @user_id, false, @callback
|
||||||
|
|
||||||
it "should flush and delete the doc from the doc updater", ->
|
it "should flush and delete the doc from the doc updater", ->
|
||||||
@DocumentManager.flushAndDeleteDoc
|
@DocumentManager.flushAndDeleteDoc
|
||||||
|
@ -255,13 +256,24 @@ describe "DocumentManager", ->
|
||||||
|
|
||||||
describe "without new lines", ->
|
describe "without new lines", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@DocumentManager.setDoc @project_id, @doc_id, null, @source, @user_id, @callback
|
@DocumentManager.setDoc @project_id, @doc_id, null, @source, @user_id, false, @callback
|
||||||
|
|
||||||
it "should return the callback with an error", ->
|
it "should return the callback with an error", ->
|
||||||
@callback.calledWith(new Error("No lines were passed to setDoc"))
|
@callback.calledWith(new Error("No lines were passed to setDoc"))
|
||||||
|
|
||||||
it "should not try to get the doc lines", ->
|
it "should not try to get the doc lines", ->
|
||||||
@DocumentManager.getDoc.called.should.equal false
|
@DocumentManager.getDoc.called.should.equal false
|
||||||
|
|
||||||
|
describe "with the undoing flag", ->
|
||||||
|
beforeEach ->
|
||||||
|
# Copy ops so we don't interfere with other tests
|
||||||
|
@ops = [{ i: "foo", p: 4 }, { d: "bar", p: 42 }]
|
||||||
|
@DiffCodec.diffAsShareJsOp = sinon.stub().callsArgWith(2, null, @ops)
|
||||||
|
@DocumentManager.setDoc @project_id, @doc_id, @afterLines, @source, @user_id, true, @callback
|
||||||
|
|
||||||
|
it "should set the undo flag on each op", ->
|
||||||
|
for op in @ops
|
||||||
|
op.u.should.equal true
|
||||||
|
|
||||||
describe "acceptChanges", ->
|
describe "acceptChanges", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
|
|
@ -125,15 +125,16 @@ describe "HttpController", ->
|
||||||
lines: @lines
|
lines: @lines
|
||||||
source: @source
|
source: @source
|
||||||
user_id: @user_id
|
user_id: @user_id
|
||||||
|
undoing: @undoing = true
|
||||||
|
|
||||||
describe "successfully", ->
|
describe "successfully", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@DocumentManager.setDocWithLock = sinon.stub().callsArgWith(5)
|
@DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6)
|
||||||
@HttpController.setDoc(@req, @res, @next)
|
@HttpController.setDoc(@req, @res, @next)
|
||||||
|
|
||||||
it "should set the doc", ->
|
it "should set the doc", ->
|
||||||
@DocumentManager.setDocWithLock
|
@DocumentManager.setDocWithLock
|
||||||
.calledWith(@project_id, @doc_id, @lines, @source, @user_id)
|
.calledWith(@project_id, @doc_id, @lines, @source, @user_id, @undoing)
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should return a successful No Content response", ->
|
it "should return a successful No Content response", ->
|
||||||
|
@ -143,7 +144,7 @@ describe "HttpController", ->
|
||||||
|
|
||||||
it "should log the request", ->
|
it "should log the request", ->
|
||||||
@logger.log
|
@logger.log
|
||||||
.calledWith(doc_id: @doc_id, project_id: @project_id, lines: @lines, source: @source, user_id: @user_id, "setting doc via http")
|
.calledWith(doc_id: @doc_id, project_id: @project_id, lines: @lines, source: @source, user_id: @user_id, undoing: @undoing, "setting doc via http")
|
||||||
.should.equal true
|
.should.equal true
|
||||||
|
|
||||||
it "should time the request", ->
|
it "should time the request", ->
|
||||||
|
@ -151,7 +152,7 @@ describe "HttpController", ->
|
||||||
|
|
||||||
describe "when an errors occurs", ->
|
describe "when an errors occurs", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@DocumentManager.setDocWithLock = sinon.stub().callsArgWith(5, new Error("oops"))
|
@DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6, new Error("oops"))
|
||||||
@HttpController.setDoc(@req, @res, @next)
|
@HttpController.setDoc(@req, @res, @next)
|
||||||
|
|
||||||
it "should call next with the error", ->
|
it "should call next with the error", ->
|
||||||
|
@ -165,7 +166,7 @@ describe "HttpController", ->
|
||||||
for _ in [0..200000]
|
for _ in [0..200000]
|
||||||
lines.push "test test test"
|
lines.push "test test test"
|
||||||
@req.body.lines = lines
|
@req.body.lines = lines
|
||||||
@DocumentManager.setDocWithLock = sinon.stub().callsArgWith(5)
|
@DocumentManager.setDocWithLock = sinon.stub().callsArgWith(6)
|
||||||
@HttpController.setDoc(@req, @res, @next)
|
@HttpController.setDoc(@req, @res, @next)
|
||||||
|
|
||||||
it 'should send back a 406 response', ->
|
it 'should send back a 406 response', ->
|
||||||
|
|
Loading…
Reference in a new issue