DocManager.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-dupe-keys,
  5. no-undef,
  6. standard/no-callback-literal,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS207: Consider shorter variations of null checks
  14. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  15. */
  16. let DocManager
  17. const MongoManager = require('./MongoManager')
  18. const Errors = require('./Errors')
  19. const logger = require('logger-sharelatex')
  20. const _ = require('underscore')
  21. const DocArchive = require('./DocArchiveManager')
  22. const RangeManager = require('./RangeManager')
  23. module.exports = DocManager = {
  24. // TODO: For historical reasons, the doc version is currently stored in the docOps
  25. // collection (which is all that this collection contains). In future, we should
  26. // migrate this version property to be part of the docs collection, to guarantee
  27. // consitency between lines and version when writing/reading, and for a simpler schema.
  28. _getDoc(project_id, doc_id, filter, callback) {
  29. if (filter == null) {
  30. filter = {}
  31. }
  32. if (callback == null) {
  33. callback = function(error, doc) {}
  34. }
  35. if (filter.inS3 !== true) {
  36. return callback('must include inS3 when getting doc')
  37. }
  38. return MongoManager.findDoc(project_id, doc_id, filter, function(err, doc) {
  39. if (err != null) {
  40. return callback(err)
  41. } else if (doc == null) {
  42. return callback(
  43. new Errors.NotFoundError(
  44. `No such doc: ${doc_id} in project ${project_id}`
  45. )
  46. )
  47. } else if (doc != null ? doc.inS3 : undefined) {
  48. return DocArchive.unarchiveDoc(project_id, doc_id, function(err) {
  49. if (err != null) {
  50. logger.err({ err, project_id, doc_id }, 'error unarchiving doc')
  51. return callback(err)
  52. }
  53. return DocManager._getDoc(project_id, doc_id, filter, callback)
  54. })
  55. } else {
  56. if (filter.version) {
  57. return MongoManager.getDocVersion(doc_id, function(error, version) {
  58. if (error != null) {
  59. return callback(error)
  60. }
  61. doc.version = version
  62. return callback(err, doc)
  63. })
  64. } else {
  65. return callback(err, doc)
  66. }
  67. }
  68. })
  69. },
  70. checkDocExists(project_id, doc_id, callback) {
  71. if (callback == null) {
  72. callback = function(err, exists) {}
  73. }
  74. return DocManager._getDoc(
  75. project_id,
  76. doc_id,
  77. { _id: 1, inS3: true },
  78. function(err, doc) {
  79. if (err != null) {
  80. return callback(err)
  81. }
  82. return callback(err, doc != null)
  83. }
  84. )
  85. },
  86. getFullDoc(project_id, doc_id, callback) {
  87. if (callback == null) {
  88. callback = function(err, doc) {}
  89. }
  90. return DocManager._getDoc(
  91. project_id,
  92. doc_id,
  93. {
  94. lines: true,
  95. rev: true,
  96. deleted: true,
  97. version: true,
  98. ranges: true,
  99. inS3: true
  100. },
  101. function(err, doc) {
  102. if (err != null) {
  103. return callback(err)
  104. }
  105. return callback(err, doc)
  106. }
  107. )
  108. },
  109. getDocLines(project_id, doc_id, callback) {
  110. if (callback == null) {
  111. callback = function(err, doc) {}
  112. }
  113. return DocManager._getDoc(
  114. project_id,
  115. doc_id,
  116. { lines: true, inS3: true },
  117. function(err, doc) {
  118. if (err != null) {
  119. return callback(err)
  120. }
  121. return callback(err, doc)
  122. }
  123. )
  124. },
  125. getAllNonDeletedDocs(project_id, filter, callback) {
  126. if (callback == null) {
  127. callback = function(error, docs) {}
  128. }
  129. return DocArchive.unArchiveAllDocs(project_id, function(error) {
  130. if (error != null) {
  131. return callback(error)
  132. }
  133. return MongoManager.getProjectsDocs(
  134. project_id,
  135. { include_deleted: false },
  136. filter,
  137. function(error, docs) {
  138. if (typeof err !== 'undefined' && err !== null) {
  139. return callback(error)
  140. } else if (docs == null) {
  141. return callback(
  142. new Errors.NotFoundError(`No docs for project ${project_id}`)
  143. )
  144. } else {
  145. return callback(null, docs)
  146. }
  147. }
  148. )
  149. })
  150. },
  151. updateDoc(project_id, doc_id, lines, version, ranges, callback) {
  152. if (callback == null) {
  153. callback = function(error, modified, rev) {}
  154. }
  155. if (lines == null || version == null || ranges == null) {
  156. return callback(new Error('no lines, version or ranges provided'))
  157. }
  158. return DocManager._getDoc(
  159. project_id,
  160. doc_id,
  161. {
  162. version: true,
  163. rev: true,
  164. lines: true,
  165. version: true,
  166. ranges: true,
  167. inS3: true
  168. },
  169. function(err, doc) {
  170. let updateLines, updateRanges, updateVersion
  171. if (err != null && !(err instanceof Errors.NotFoundError)) {
  172. logger.err(
  173. { project_id, doc_id, err },
  174. 'error getting document for update'
  175. )
  176. return callback(err)
  177. }
  178. ranges = RangeManager.jsonRangesToMongo(ranges)
  179. if (doc == null) {
  180. // If the document doesn't exist, we'll make sure to create/update all parts of it.
  181. updateLines = true
  182. updateVersion = true
  183. updateRanges = true
  184. } else {
  185. updateLines = !_.isEqual(doc.lines, lines)
  186. updateVersion = doc.version !== version
  187. updateRanges = RangeManager.shouldUpdateRanges(doc.ranges, ranges)
  188. }
  189. let modified = false
  190. let rev = (doc != null ? doc.rev : undefined) || 0
  191. const updateLinesAndRangesIfNeeded = function(cb) {
  192. if (updateLines || updateRanges) {
  193. const update = {}
  194. if (updateLines) {
  195. update.lines = lines
  196. }
  197. if (updateRanges) {
  198. update.ranges = ranges
  199. }
  200. logger.log({ project_id, doc_id }, 'updating doc lines and ranges')
  201. modified = true
  202. rev += 1 // rev will be incremented in mongo by MongoManager.upsertIntoDocCollection
  203. return MongoManager.upsertIntoDocCollection(
  204. project_id,
  205. doc_id,
  206. update,
  207. cb
  208. )
  209. } else {
  210. logger.log(
  211. { project_id, doc_id },
  212. 'doc lines have not changed - not updating'
  213. )
  214. return cb()
  215. }
  216. }
  217. const updateVersionIfNeeded = function(cb) {
  218. if (updateVersion) {
  219. logger.log(
  220. {
  221. project_id,
  222. doc_id,
  223. oldVersion: doc != null ? doc.version : undefined,
  224. newVersion: version
  225. },
  226. 'updating doc version'
  227. )
  228. modified = true
  229. return MongoManager.setDocVersion(doc_id, version, cb)
  230. } else {
  231. logger.log(
  232. { project_id, doc_id, version },
  233. 'doc version has not changed - not updating'
  234. )
  235. return cb()
  236. }
  237. }
  238. return updateLinesAndRangesIfNeeded(function(error) {
  239. if (error != null) {
  240. return callback(error)
  241. }
  242. return updateVersionIfNeeded(function(error) {
  243. if (error != null) {
  244. return callback(error)
  245. }
  246. return callback(null, modified, rev)
  247. })
  248. })
  249. }
  250. )
  251. },
  252. deleteDoc(project_id, doc_id, callback) {
  253. if (callback == null) {
  254. callback = function(error) {}
  255. }
  256. return DocManager.checkDocExists(project_id, doc_id, function(
  257. error,
  258. exists
  259. ) {
  260. if (error != null) {
  261. return callback(error)
  262. }
  263. if (!exists) {
  264. return callback(
  265. new Errors.NotFoundError(
  266. `No such project/doc to delete: ${project_id}/${doc_id}`
  267. )
  268. )
  269. }
  270. return MongoManager.markDocAsDeleted(project_id, doc_id, callback)
  271. })
  272. }
  273. }