DocstoreManager.js 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /* eslint-disable
  2. camelcase,
  3. node/handle-callback-err,
  4. max-len,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS207: Consider shorter variations of null checks
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. const request = require('request').defaults({ jar: false })
  16. const OError = require('@overleaf/o-error')
  17. const logger = require('logger-sharelatex')
  18. const settings = require('@overleaf/settings')
  19. const Errors = require('../Errors/Errors')
  20. const { promisifyAll } = require('../../util/promises')
  21. const TIMEOUT = 30 * 1000 // request timeout
  22. const DocstoreManager = {
  23. deleteDoc(project_id, doc_id, name, deletedAt, callback) {
  24. if (callback == null) {
  25. callback = function (error) {}
  26. }
  27. const url = `${settings.apis.docstore.url}/project/${project_id}/doc/${doc_id}`
  28. const docMetaData = { deleted: true, deletedAt, name }
  29. const options = { url, json: docMetaData, timeout: TIMEOUT }
  30. request.patch(options, function (error, res) {
  31. if (error != null) {
  32. return callback(error)
  33. }
  34. if (res.statusCode >= 200 && res.statusCode < 300) {
  35. return callback(null)
  36. } else if (res.statusCode === 404) {
  37. error = new Errors.NotFoundError({
  38. message: 'tried to delete doc not in docstore',
  39. info: {
  40. project_id,
  41. doc_id,
  42. },
  43. })
  44. return callback(error) // maybe suppress the error when delete doc which is not present?
  45. } else {
  46. error = new OError(
  47. `docstore api responded with non-success code: ${res.statusCode}`,
  48. {
  49. project_id,
  50. doc_id,
  51. }
  52. )
  53. return callback(error)
  54. }
  55. })
  56. },
  57. getAllDocs(project_id, callback) {
  58. if (callback == null) {
  59. callback = function (error) {}
  60. }
  61. const url = `${settings.apis.docstore.url}/project/${project_id}/doc`
  62. return request.get(
  63. {
  64. url,
  65. timeout: TIMEOUT,
  66. json: true,
  67. },
  68. function (error, res, docs) {
  69. if (error != null) {
  70. return callback(error)
  71. }
  72. if (res.statusCode >= 200 && res.statusCode < 300) {
  73. return callback(null, docs)
  74. } else {
  75. error = new OError(
  76. `docstore api responded with non-success code: ${res.statusCode}`,
  77. { project_id }
  78. )
  79. return callback(error)
  80. }
  81. }
  82. )
  83. },
  84. getAllDeletedDocs(project_id, callback) {
  85. const url = `${settings.apis.docstore.url}/project/${project_id}/doc-deleted`
  86. request.get(
  87. { url, timeout: TIMEOUT, json: true },
  88. function (error, res, docs) {
  89. if (error) {
  90. callback(
  91. OError.tag(error, 'could not get deleted docs from docstore')
  92. )
  93. } else if (res.statusCode === 200) {
  94. callback(null, docs)
  95. } else {
  96. callback(
  97. new OError(
  98. `docstore api responded with non-success code: ${res.statusCode}`,
  99. { project_id }
  100. )
  101. )
  102. }
  103. }
  104. )
  105. },
  106. getAllRanges(project_id, callback) {
  107. if (callback == null) {
  108. callback = function (error) {}
  109. }
  110. const url = `${settings.apis.docstore.url}/project/${project_id}/ranges`
  111. return request.get(
  112. {
  113. url,
  114. timeout: TIMEOUT,
  115. json: true,
  116. },
  117. function (error, res, docs) {
  118. if (error != null) {
  119. return callback(error)
  120. }
  121. if (res.statusCode >= 200 && res.statusCode < 300) {
  122. return callback(null, docs)
  123. } else {
  124. error = new OError(
  125. `docstore api responded with non-success code: ${res.statusCode}`,
  126. { project_id }
  127. )
  128. return callback(error)
  129. }
  130. }
  131. )
  132. },
  133. getDoc(project_id, doc_id, options, callback) {
  134. if (options == null) {
  135. options = {}
  136. }
  137. if (callback == null) {
  138. callback = function (error, lines, rev, version) {}
  139. }
  140. if (typeof options === 'function') {
  141. callback = options
  142. options = {}
  143. }
  144. let url = `${settings.apis.docstore.url}/project/${project_id}/doc/${doc_id}`
  145. if (options.include_deleted) {
  146. url += '?include_deleted=true'
  147. }
  148. return request.get(
  149. {
  150. url,
  151. timeout: TIMEOUT,
  152. json: true,
  153. },
  154. function (error, res, doc) {
  155. if (error != null) {
  156. return callback(error)
  157. }
  158. if (res.statusCode >= 200 && res.statusCode < 300) {
  159. logger.log(
  160. { doc_id, project_id, version: doc.version, rev: doc.rev },
  161. 'got doc from docstore api'
  162. )
  163. return callback(null, doc.lines, doc.rev, doc.version, doc.ranges)
  164. } else if (res.statusCode === 404) {
  165. error = new Errors.NotFoundError({
  166. message: 'doc not found in docstore',
  167. info: {
  168. project_id,
  169. doc_id,
  170. },
  171. })
  172. return callback(error)
  173. } else {
  174. error = new OError(
  175. `docstore api responded with non-success code: ${res.statusCode}`,
  176. {
  177. project_id,
  178. doc_id,
  179. }
  180. )
  181. return callback(error)
  182. }
  183. }
  184. )
  185. },
  186. isDocDeleted(project_id, doc_id, callback) {
  187. const url = `${settings.apis.docstore.url}/project/${project_id}/doc/${doc_id}/deleted`
  188. request.get(
  189. { url, timeout: TIMEOUT, json: true },
  190. function (err, res, body) {
  191. if (err) {
  192. callback(err)
  193. } else if (res.statusCode === 200) {
  194. callback(null, body.deleted)
  195. } else if (res.statusCode === 404) {
  196. callback(
  197. new Errors.NotFoundError({
  198. message: 'doc does not exist in project',
  199. info: { project_id, doc_id },
  200. })
  201. )
  202. } else {
  203. callback(
  204. new OError(
  205. `docstore api responded with non-success code: ${res.statusCode}`,
  206. { project_id, doc_id }
  207. )
  208. )
  209. }
  210. }
  211. )
  212. },
  213. updateDoc(project_id, doc_id, lines, version, ranges, callback) {
  214. if (callback == null) {
  215. callback = function (error, modified, rev) {}
  216. }
  217. const url = `${settings.apis.docstore.url}/project/${project_id}/doc/${doc_id}`
  218. return request.post(
  219. {
  220. url,
  221. timeout: TIMEOUT,
  222. json: {
  223. lines,
  224. version,
  225. ranges,
  226. },
  227. },
  228. function (error, res, result) {
  229. if (error != null) {
  230. return callback(error)
  231. }
  232. if (res.statusCode >= 200 && res.statusCode < 300) {
  233. logger.log(
  234. { project_id, doc_id },
  235. 'update doc in docstore url finished'
  236. )
  237. return callback(null, result.modified, result.rev)
  238. } else {
  239. error = new OError(
  240. `docstore api responded with non-success code: ${res.statusCode}`,
  241. { project_id, doc_id }
  242. )
  243. return callback(error)
  244. }
  245. }
  246. )
  247. },
  248. archiveProject(project_id, callback) {
  249. DocstoreManager._operateOnProject(project_id, 'archive', callback)
  250. },
  251. unarchiveProject(project_id, callback) {
  252. DocstoreManager._operateOnProject(project_id, 'unarchive', callback)
  253. },
  254. destroyProject(project_id, callback) {
  255. DocstoreManager._operateOnProject(project_id, 'destroy', callback)
  256. },
  257. _operateOnProject(project_id, method, callback) {
  258. const url = `${settings.apis.docstore.url}/project/${project_id}/${method}`
  259. logger.log({ project_id }, `calling ${method} for project in docstore`)
  260. // use default timeout for archiving/unarchiving/destroying
  261. request.post(url, function (err, res, docs) {
  262. if (err != null) {
  263. OError.tag(err, `error calling ${method} project in docstore`, {
  264. project_id,
  265. })
  266. return callback(err)
  267. }
  268. if (res.statusCode >= 200 && res.statusCode < 300) {
  269. callback()
  270. } else {
  271. const error = new Error(
  272. `docstore api responded with non-success code: ${res.statusCode}`
  273. )
  274. logger.warn(
  275. { err: error, project_id },
  276. `error calling ${method} project in docstore`
  277. )
  278. callback(error)
  279. }
  280. })
  281. },
  282. }
  283. module.exports = DocstoreManager
  284. module.exports.promises = promisifyAll(DocstoreManager, {
  285. multiResult: {
  286. getDoc: ['lines', 'rev', 'version', 'ranges'],
  287. updateDoc: ['modified', 'rev'],
  288. },
  289. })