RangesManager.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. /* eslint-disable
  2. camelcase,
  3. */
  4. // TODO: This file was created by bulk-decaffeinate.
  5. // Fix any style issues and re-enable lint.
  6. /*
  7. * decaffeinate suggestions:
  8. * DS101: Remove unnecessary use of Array.from
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * DS207: Consider shorter variations of null checks
  11. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  12. */
  13. let RangesManager
  14. const RangesTracker = require('./RangesTracker')
  15. const logger = require('@overleaf/logger')
  16. const _ = require('lodash')
  17. module.exports = RangesManager = {
  18. MAX_COMMENTS: 500,
  19. MAX_CHANGES: 2000,
  20. applyUpdate(project_id, doc_id, entries, updates, newDocLines, callback) {
  21. let error
  22. if (entries == null) {
  23. entries = {}
  24. }
  25. if (updates == null) {
  26. updates = []
  27. }
  28. if (callback == null) {
  29. callback = function () {}
  30. }
  31. const { changes, comments } = _.cloneDeep(entries)
  32. const rangesTracker = new RangesTracker(changes, comments)
  33. const emptyRangeCountBefore = RangesManager._emptyRangesCount(rangesTracker)
  34. for (const update of Array.from(updates)) {
  35. rangesTracker.track_changes = !!update.meta.tc
  36. if (update.meta.tc) {
  37. rangesTracker.setIdSeed(update.meta.tc)
  38. }
  39. for (const op of Array.from(update.op)) {
  40. try {
  41. rangesTracker.applyOp(op, {
  42. user_id: update.meta != null ? update.meta.user_id : undefined,
  43. })
  44. } catch (error1) {
  45. error = error1
  46. return callback(error)
  47. }
  48. }
  49. }
  50. if (
  51. (rangesTracker.changes != null
  52. ? rangesTracker.changes.length
  53. : undefined) > RangesManager.MAX_CHANGES ||
  54. (rangesTracker.comments != null
  55. ? rangesTracker.comments.length
  56. : undefined) > RangesManager.MAX_COMMENTS
  57. ) {
  58. return callback(new Error('too many comments or tracked changes'))
  59. }
  60. try {
  61. // This is a consistency check that all of our ranges and
  62. // comments still match the corresponding text
  63. rangesTracker.validate(newDocLines.join('\n'))
  64. } catch (error2) {
  65. error = error2
  66. logger.error(
  67. { err: error, project_id, doc_id, newDocLines, updates },
  68. 'error validating ranges'
  69. )
  70. return callback(error)
  71. }
  72. const emptyRangeCountAfter = RangesManager._emptyRangesCount(rangesTracker)
  73. const rangesWereCollapsed = emptyRangeCountAfter > emptyRangeCountBefore
  74. const response = RangesManager._getRanges(rangesTracker)
  75. logger.debug(
  76. {
  77. project_id,
  78. doc_id,
  79. changesCount:
  80. response.changes != null ? response.changes.length : undefined,
  81. commentsCount:
  82. response.comments != null ? response.comments.length : undefined,
  83. rangesWereCollapsed,
  84. },
  85. 'applied updates to ranges'
  86. )
  87. return callback(null, response, rangesWereCollapsed)
  88. },
  89. acceptChanges(change_ids, ranges, callback) {
  90. if (callback == null) {
  91. callback = function () {}
  92. }
  93. const { changes, comments } = ranges
  94. logger.debug(`accepting ${change_ids.length} changes in ranges`)
  95. const rangesTracker = new RangesTracker(changes, comments)
  96. rangesTracker.removeChangeIds(change_ids)
  97. const response = RangesManager._getRanges(rangesTracker)
  98. return callback(null, response)
  99. },
  100. deleteComment(comment_id, ranges, callback) {
  101. if (callback == null) {
  102. callback = function () {}
  103. }
  104. const { changes, comments } = ranges
  105. logger.debug({ comment_id }, 'deleting comment in ranges')
  106. const rangesTracker = new RangesTracker(changes, comments)
  107. rangesTracker.removeCommentId(comment_id)
  108. const response = RangesManager._getRanges(rangesTracker)
  109. return callback(null, response)
  110. },
  111. _getRanges(rangesTracker) {
  112. // Return the minimal data structure needed, since most documents won't have any
  113. // changes or comments
  114. let response = {}
  115. if (
  116. (rangesTracker.changes != null
  117. ? rangesTracker.changes.length
  118. : undefined) > 0
  119. ) {
  120. if (response == null) {
  121. response = {}
  122. }
  123. response.changes = rangesTracker.changes
  124. }
  125. if (
  126. (rangesTracker.comments != null
  127. ? rangesTracker.comments.length
  128. : undefined) > 0
  129. ) {
  130. if (response == null) {
  131. response = {}
  132. }
  133. response.comments = rangesTracker.comments
  134. }
  135. return response
  136. },
  137. _emptyRangesCount(ranges) {
  138. let count = 0
  139. for (const comment of Array.from(ranges.comments || [])) {
  140. if (comment.op.c === '') {
  141. count++
  142. }
  143. }
  144. for (const change of Array.from(ranges.changes || [])) {
  145. if (change.op.i != null) {
  146. if (change.op.i === '') {
  147. count++
  148. }
  149. }
  150. }
  151. return count
  152. },
  153. }