overleaf/services/track-changes/app/coffee/DiffManager.coffee

89 lines
3.4 KiB
CoffeeScript
Raw Normal View History

UpdatesManager = require "./UpdatesManager"
2014-03-04 14:05:17 +00:00
DocumentUpdaterManager = require "./DocumentUpdaterManager"
DiffGenerator = require "./DiffGenerator"
logger = require "logger-sharelatex"
2014-03-04 14:05:17 +00:00
module.exports = DiffManager =
getLatestDocAndUpdates: (project_id, doc_id, fromVersion, callback = (error, content, version, updates) ->) ->
# Get updates last, since then they must be ahead and it
# might be possible to rewind to the same version as the doc.
DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) ->
2014-03-04 14:05:17 +00:00
return callback(error) if error?
if !fromVersion? # If we haven't been given a version, just return lastest doc and no updates
return callback(null, content, version, [])
UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, (error, updates) ->
2014-03-04 14:05:17 +00:00
return callback(error) if error?
2014-03-10 16:03:03 +00:00
callback(null, content, version, updates)
2014-03-04 14:05:17 +00:00
getDiff: (project_id, doc_id, fromVersion, toVersion, callback = (error, diff) ->) ->
2014-03-10 16:03:03 +00:00
DiffManager.getDocumentBeforeVersion project_id, doc_id, fromVersion, (error, startingContent, updates) ->
if error?
if error.message == "broken-history"
return callback(null, "history unavailable")
else
return callback(error)
2014-03-04 14:05:17 +00:00
updatesToApply = []
2014-03-10 16:03:03 +00:00
for update in updates.slice().reverse()
if update.v <= toVersion
2014-03-04 14:05:17 +00:00
updatesToApply.push update
try
diff = DiffGenerator.buildDiff startingContent, updatesToApply
catch e
return callback(e)
2014-03-10 16:03:03 +00:00
callback(null, diff)
getDocumentBeforeVersion: (project_id, doc_id, version, _callback = (error, document, rewoundUpdates) ->) ->
# Whichever order we get the latest document and the latest updates,
# there is potential for updates to be applied between them so that
# they do not return the same 'latest' versions.
# If this happens, we just retry and hopefully get them at the compatible
# versions.
retries = 3
callback = (error, args...) ->
if error?
if error.retry and retries > 0
2016-09-30 12:38:47 +00:00
logger.warn {error, project_id, doc_id, version, retries}, "retrying getDocumentBeforeVersion"
retry()
else
_callback(error)
else
_callback(null, args...)
do retry = () ->
retries--
DiffManager._tryGetDocumentBeforeVersion(project_id, doc_id, version, callback)
_tryGetDocumentBeforeVersion: (project_id, doc_id, version, callback = (error, document, rewoundUpdates) ->) ->
2014-03-10 16:03:03 +00:00
logger.log project_id: project_id, doc_id: doc_id, version: version, "getting document before version"
DiffManager.getLatestDocAndUpdates project_id, doc_id, version, (error, content, version, updates) ->
2014-03-10 16:03:03 +00:00
return callback(error) if error?
# bail out if we hit a broken update
for u in updates when u.broken
return callback new Error "broken-history"
2015-12-18 12:38:42 +00:00
# discard any updates which are ahead of this document version
while updates[0]?.v >= version
updates.shift()
2014-03-10 16:03:03 +00:00
lastUpdate = updates[0]
if lastUpdate? and lastUpdate.v != version - 1
error = new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}")
error.retry = true
2016-09-30 12:36:31 +00:00
return callback error
2016-09-22 10:13:26 +00:00
logger.log {docVersion: version, lastUpdateVersion: lastUpdate?.v, updateCount: updates.length}, "rewinding updates"
2014-03-10 16:03:03 +00:00
tryUpdates = updates.slice().reverse()
2014-03-10 16:03:03 +00:00
try
startingContent = DiffGenerator.rewindUpdates content, tryUpdates
# tryUpdates is reversed, and any unapplied ops are marked as broken
2014-03-10 16:03:03 +00:00
catch e
return callback(e)
callback(null, startingContent, tryUpdates)