DocManager.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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 (
  39. err,
  40. doc
  41. ) {
  42. if (err != null) {
  43. return callback(err)
  44. } else if (doc == null) {
  45. return callback(
  46. new Errors.NotFoundError(
  47. `No such doc: ${doc_id} in project ${project_id}`
  48. )
  49. )
  50. } else if (doc != null ? doc.inS3 : undefined) {
  51. return DocArchive.unarchiveDoc(project_id, doc_id, function (err) {
  52. if (err != null) {
  53. logger.err({ err, project_id, doc_id }, 'error unarchiving doc')
  54. return callback(err)
  55. }
  56. return DocManager._getDoc(project_id, doc_id, filter, callback)
  57. })
  58. } else {
  59. if (filter.version) {
  60. return MongoManager.getDocVersion(doc_id, function (error, version) {
  61. if (error != null) {
  62. return callback(error)
  63. }
  64. doc.version = version
  65. return callback(err, doc)
  66. })
  67. } else {
  68. return callback(err, doc)
  69. }
  70. }
  71. })
  72. },
  73. checkDocExists(project_id, doc_id, callback) {
  74. if (callback == null) {
  75. callback = function (err, exists) {}
  76. }
  77. return DocManager._getDoc(
  78. project_id,
  79. doc_id,
  80. { _id: 1, inS3: true },
  81. function (err, doc) {
  82. if (err != null) {
  83. return callback(err)
  84. }
  85. return callback(err, doc != null)
  86. }
  87. )
  88. },
  89. isDocDeleted(projectId, docId, callback) {
  90. MongoManager.findDoc(projectId, docId, { deleted: true }, function (
  91. err,
  92. doc
  93. ) {
  94. if (err) {
  95. return callback(err)
  96. }
  97. if (!doc) {
  98. return callback(
  99. new Errors.NotFoundError(`No such project/doc: ${projectId}/${docId}`)
  100. )
  101. }
  102. // `doc.deleted` is `undefined` for non deleted docs
  103. callback(null, Boolean(doc.deleted))
  104. })
  105. },
  106. getFullDoc(project_id, doc_id, callback) {
  107. if (callback == null) {
  108. callback = function (err, doc) {}
  109. }
  110. return DocManager._getDoc(
  111. project_id,
  112. doc_id,
  113. {
  114. lines: true,
  115. rev: true,
  116. deleted: true,
  117. version: true,
  118. ranges: true,
  119. inS3: true
  120. },
  121. function (err, doc) {
  122. if (err != null) {
  123. return callback(err)
  124. }
  125. return callback(err, doc)
  126. }
  127. )
  128. },
  129. getDocLines(project_id, doc_id, callback) {
  130. if (callback == null) {
  131. callback = function (err, doc) {}
  132. }
  133. return DocManager._getDoc(
  134. project_id,
  135. doc_id,
  136. { lines: true, inS3: true },
  137. function (err, doc) {
  138. if (err != null) {
  139. return callback(err)
  140. }
  141. return callback(err, doc)
  142. }
  143. )
  144. },
  145. getAllNonDeletedDocs(project_id, filter, callback) {
  146. if (callback == null) {
  147. callback = function (error, docs) {}
  148. }
  149. return DocArchive.unArchiveAllDocs(project_id, function (error) {
  150. if (error != null) {
  151. return callback(error)
  152. }
  153. return MongoManager.getProjectsDocs(
  154. project_id,
  155. { include_deleted: false },
  156. filter,
  157. function (error, docs) {
  158. if (typeof err !== 'undefined' && err !== null) {
  159. return callback(error)
  160. } else if (docs == null) {
  161. return callback(
  162. new Errors.NotFoundError(`No docs for project ${project_id}`)
  163. )
  164. } else {
  165. return callback(null, docs)
  166. }
  167. }
  168. )
  169. })
  170. },
  171. updateDoc(project_id, doc_id, lines, version, ranges, callback) {
  172. if (callback == null) {
  173. callback = function (error, modified, rev) {}
  174. }
  175. if (lines == null || version == null || ranges == null) {
  176. return callback(new Error('no lines, version or ranges provided'))
  177. }
  178. return DocManager._getDoc(
  179. project_id,
  180. doc_id,
  181. {
  182. version: true,
  183. rev: true,
  184. lines: true,
  185. version: true,
  186. ranges: true,
  187. inS3: true
  188. },
  189. function (err, doc) {
  190. let updateLines, updateRanges, updateVersion
  191. if (err != null && !(err instanceof Errors.NotFoundError)) {
  192. logger.err(
  193. { project_id, doc_id, err },
  194. 'error getting document for update'
  195. )
  196. return callback(err)
  197. }
  198. ranges = RangeManager.jsonRangesToMongo(ranges)
  199. if (doc == null) {
  200. // If the document doesn't exist, we'll make sure to create/update all parts of it.
  201. updateLines = true
  202. updateVersion = true
  203. updateRanges = true
  204. } else {
  205. updateLines = !_.isEqual(doc.lines, lines)
  206. updateVersion = doc.version !== version
  207. updateRanges = RangeManager.shouldUpdateRanges(doc.ranges, ranges)
  208. }
  209. let modified = false
  210. let rev = (doc != null ? doc.rev : undefined) || 0
  211. const updateLinesAndRangesIfNeeded = function (cb) {
  212. if (updateLines || updateRanges) {
  213. const update = {}
  214. if (updateLines) {
  215. update.lines = lines
  216. }
  217. if (updateRanges) {
  218. update.ranges = ranges
  219. }
  220. logger.log({ project_id, doc_id }, 'updating doc lines and ranges')
  221. modified = true
  222. rev += 1 // rev will be incremented in mongo by MongoManager.upsertIntoDocCollection
  223. return MongoManager.upsertIntoDocCollection(
  224. project_id,
  225. doc_id,
  226. update,
  227. cb
  228. )
  229. } else {
  230. logger.log(
  231. { project_id, doc_id },
  232. 'doc lines have not changed - not updating'
  233. )
  234. return cb()
  235. }
  236. }
  237. const updateVersionIfNeeded = function (cb) {
  238. if (updateVersion) {
  239. logger.log(
  240. {
  241. project_id,
  242. doc_id,
  243. oldVersion: doc != null ? doc.version : undefined,
  244. newVersion: version
  245. },
  246. 'updating doc version'
  247. )
  248. modified = true
  249. return MongoManager.setDocVersion(doc_id, version, cb)
  250. } else {
  251. logger.log(
  252. { project_id, doc_id, version },
  253. 'doc version has not changed - not updating'
  254. )
  255. return cb()
  256. }
  257. }
  258. return updateLinesAndRangesIfNeeded(function (error) {
  259. if (error != null) {
  260. return callback(error)
  261. }
  262. return updateVersionIfNeeded(function (error) {
  263. if (error != null) {
  264. return callback(error)
  265. }
  266. return callback(null, modified, rev)
  267. })
  268. })
  269. }
  270. )
  271. },
  272. deleteDoc(project_id, doc_id, callback) {
  273. if (callback == null) {
  274. callback = function (error) {}
  275. }
  276. return DocManager.checkDocExists(project_id, doc_id, function (
  277. error,
  278. exists
  279. ) {
  280. if (error != null) {
  281. return callback(error)
  282. }
  283. if (!exists) {
  284. return callback(
  285. new Errors.NotFoundError(
  286. `No such project/doc to delete: ${project_id}/${doc_id}`
  287. )
  288. )
  289. }
  290. return MongoManager.markDocAsDeleted(project_id, doc_id, callback)
  291. })
  292. }
  293. }