RangesManager.js 4.8 KB

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