DiffManager.coffee 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. UpdatesManager = require "./UpdatesManager"
  2. DocumentUpdaterManager = require "./DocumentUpdaterManager"
  3. DiffGenerator = require "./DiffGenerator"
  4. logger = require "logger-sharelatex"
  5. module.exports = DiffManager =
  6. getLatestDocAndUpdates: (project_id, doc_id, fromVersion, callback = (error, content, version, updates) ->) ->
  7. # Get updates last, since then they must be ahead and it
  8. # might be possible to rewind to the same version as the doc.
  9. DocumentUpdaterManager.getDocument project_id, doc_id, (error, content, version) ->
  10. return callback(error) if error?
  11. if !fromVersion? # If we haven't been given a version, just return lastest doc and no updates
  12. return callback(null, content, version, [])
  13. UpdatesManager.getDocUpdatesWithUserInfo project_id, doc_id, from: fromVersion, (error, updates) ->
  14. return callback(error) if error?
  15. callback(null, content, version, updates)
  16. getDiff: (project_id, doc_id, fromVersion, toVersion, callback = (error, diff) ->) ->
  17. DiffManager.getDocumentBeforeVersion project_id, doc_id, fromVersion, (error, startingContent, updates) ->
  18. if error?
  19. if error.message == "broken-history"
  20. return callback(null, "history unavailable")
  21. else
  22. return callback(error)
  23. updatesToApply = []
  24. for update in updates.slice().reverse()
  25. if update.v <= toVersion
  26. updatesToApply.push update
  27. try
  28. diff = DiffGenerator.buildDiff startingContent, updatesToApply
  29. catch e
  30. return callback(e)
  31. callback(null, diff)
  32. getDocumentBeforeVersion: (project_id, doc_id, version, _callback = (error, document, rewoundUpdates) ->) ->
  33. # Whichever order we get the latest document and the latest updates,
  34. # there is potential for updates to be applied between them so that
  35. # they do not return the same 'latest' versions.
  36. # If this happens, we just retry and hopefully get them at the compatible
  37. # versions.
  38. retries = 3
  39. callback = (error, args...) ->
  40. if error?
  41. if error.retry and retries > 0
  42. logger.warn {error, project_id, doc_id, version, retries}, "retrying getDocumentBeforeVersion"
  43. retry()
  44. else
  45. _callback(error)
  46. else
  47. _callback(null, args...)
  48. do retry = () ->
  49. retries--
  50. DiffManager._tryGetDocumentBeforeVersion(project_id, doc_id, version, callback)
  51. _tryGetDocumentBeforeVersion: (project_id, doc_id, version, callback = (error, document, rewoundUpdates) ->) ->
  52. logger.log project_id: project_id, doc_id: doc_id, version: version, "getting document before version"
  53. DiffManager.getLatestDocAndUpdates project_id, doc_id, version, (error, content, version, updates) ->
  54. return callback(error) if error?
  55. # bail out if we hit a broken update
  56. for u in updates when u.broken
  57. return callback new Error "broken-history"
  58. # discard any updates which are ahead of this document version
  59. while updates[0]?.v >= version
  60. updates.shift()
  61. lastUpdate = updates[0]
  62. if lastUpdate? and lastUpdate.v != version - 1
  63. error = new Error("latest update version, #{lastUpdate.v}, does not match doc version, #{version}")
  64. error.retry = true
  65. return callback error
  66. logger.log {docVersion: version, lastUpdateVersion: lastUpdate?.v, updateCount: updates.length}, "rewinding updates"
  67. tryUpdates = updates.slice().reverse()
  68. try
  69. startingContent = DiffGenerator.rewindUpdates content, tryUpdates
  70. # tryUpdates is reversed, and any unapplied ops are marked as broken
  71. catch e
  72. return callback(e)
  73. callback(null, startingContent, tryUpdates)