ReferencesHandler.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /* eslint-disable
  2. node/handle-callback-err,
  3. max-len,
  4. no-unused-vars,
  5. */
  6. // TODO: This file was created by bulk-decaffeinate.
  7. // Fix any style issues and re-enable lint.
  8. /*
  9. * decaffeinate suggestions:
  10. * DS102: Remove unnecessary code created because of implicit returns
  11. * DS103: Rewrite code to no longer use __guard__
  12. * DS207: Consider shorter variations of null checks
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. let ReferencesHandler
  16. const OError = require('@overleaf/o-error')
  17. const logger = require('@overleaf/logger')
  18. const request = require('request')
  19. const settings = require('@overleaf/settings')
  20. const Features = require('../../infrastructure/Features')
  21. const ProjectGetter = require('../Project/ProjectGetter')
  22. const UserGetter = require('../User/UserGetter')
  23. const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
  24. const _ = require('underscore')
  25. const Async = require('async')
  26. const Errors = require('../Errors/Errors')
  27. const oneMinInMs = 60 * 1000
  28. const fiveMinsInMs = oneMinInMs * 5
  29. if (!Features.hasFeature('references')) {
  30. logger.log('references search not enabled')
  31. }
  32. module.exports = ReferencesHandler = {
  33. _buildDocUrl(projectId, docId) {
  34. return `${settings.apis.docstore.url}/project/${projectId}/doc/${docId}/raw`
  35. },
  36. _buildFileUrl(projectId, fileId) {
  37. return `${settings.apis.filestore.url}/project/${projectId}/file/${fileId}`
  38. },
  39. _findBibFileIds(project) {
  40. const ids = []
  41. function _process(folder) {
  42. _.each(folder.fileRefs || [], function (file) {
  43. if (
  44. __guard__(file != null ? file.name : undefined, x1 =>
  45. x1.match(/^.*\.bib$/)
  46. )
  47. ) {
  48. return ids.push(file._id)
  49. }
  50. })
  51. return _.each(folder.folders || [], folder => _process(folder))
  52. }
  53. _.each(project.rootFolder || [], rootFolder => _process(rootFolder))
  54. return ids
  55. },
  56. _findBibDocIds(project) {
  57. const ids = []
  58. function _process(folder) {
  59. _.each(folder.docs || [], function (doc) {
  60. if (
  61. __guard__(doc != null ? doc.name : undefined, x1 =>
  62. x1.match(/^.*\.bib$/)
  63. )
  64. ) {
  65. return ids.push(doc._id)
  66. }
  67. })
  68. return _.each(folder.folders || [], folder => _process(folder))
  69. }
  70. _.each(project.rootFolder || [], rootFolder => _process(rootFolder))
  71. return ids
  72. },
  73. _isFullIndex(project, callback) {
  74. if (callback == null) {
  75. callback = function () {}
  76. }
  77. return UserGetter.getUser(
  78. project.owner_ref,
  79. { features: true },
  80. function (err, owner) {
  81. if (err != null) {
  82. return callback(err)
  83. }
  84. const features = owner != null ? owner.features : undefined
  85. return callback(
  86. null,
  87. (features != null ? features.references : undefined) === true ||
  88. (features != null ? features.referencesSearch : undefined) === true
  89. )
  90. }
  91. )
  92. },
  93. indexAll(projectId, callback) {
  94. if (callback == null) {
  95. callback = function () {}
  96. }
  97. return ProjectGetter.getProject(
  98. projectId,
  99. { rootFolder: true, owner_ref: 1 },
  100. function (err, project) {
  101. if (err) {
  102. OError.tag(err, 'error finding project', {
  103. projectId,
  104. })
  105. return callback(err)
  106. }
  107. if (!project) {
  108. return callback(
  109. new Errors.NotFoundError(`project does not exist: ${projectId}`)
  110. )
  111. }
  112. logger.log({ projectId }, 'indexing all bib files in project')
  113. const docIds = ReferencesHandler._findBibDocIds(project)
  114. const fileIds = ReferencesHandler._findBibFileIds(project)
  115. return ReferencesHandler._doIndexOperation(
  116. projectId,
  117. project,
  118. docIds,
  119. fileIds,
  120. callback
  121. )
  122. }
  123. )
  124. },
  125. index(projectId, docIds, callback) {
  126. if (callback == null) {
  127. callback = function () {}
  128. }
  129. return ProjectGetter.getProject(
  130. projectId,
  131. { rootFolder: true, owner_ref: 1 },
  132. function (err, project) {
  133. if (err) {
  134. OError.tag(err, 'error finding project', {
  135. projectId,
  136. })
  137. return callback(err)
  138. }
  139. if (!project) {
  140. return callback(
  141. new Errors.NotFoundError(`project does not exist: ${projectId}`)
  142. )
  143. }
  144. return ReferencesHandler._doIndexOperation(
  145. projectId,
  146. project,
  147. docIds,
  148. [],
  149. callback
  150. )
  151. }
  152. )
  153. },
  154. _doIndexOperation(projectId, project, docIds, fileIds, callback) {
  155. if (!Features.hasFeature('references')) {
  156. return callback()
  157. }
  158. return ReferencesHandler._isFullIndex(project, function (err, isFullIndex) {
  159. if (err) {
  160. OError.tag(err, 'error checking whether to do full index', {
  161. projectId,
  162. })
  163. return callback(err)
  164. }
  165. logger.log(
  166. { projectId, docIds },
  167. 'flushing docs to mongo before calling references service'
  168. )
  169. return Async.series(
  170. docIds.map(
  171. docId => cb =>
  172. DocumentUpdaterHandler.flushDocToMongo(projectId, docId, cb)
  173. ),
  174. function (err) {
  175. // continue
  176. if (err) {
  177. OError.tag(err, 'error flushing docs to mongo', {
  178. projectId,
  179. docIds,
  180. })
  181. return callback(err)
  182. }
  183. const bibDocUrls = docIds.map(docId =>
  184. ReferencesHandler._buildDocUrl(projectId, docId)
  185. )
  186. const bibFileUrls = fileIds.map(fileId =>
  187. ReferencesHandler._buildFileUrl(projectId, fileId)
  188. )
  189. const allUrls = bibDocUrls.concat(bibFileUrls)
  190. return request.post(
  191. {
  192. url: `${settings.apis.references.url}/project/${projectId}/index`,
  193. json: {
  194. docUrls: allUrls,
  195. fullIndex: isFullIndex,
  196. },
  197. },
  198. function (err, res, data) {
  199. if (err) {
  200. OError.tag(err, 'error communicating with references api', {
  201. projectId,
  202. })
  203. return callback(err)
  204. }
  205. if (res.statusCode >= 200 && res.statusCode < 300) {
  206. logger.log({ projectId }, 'got keys from references api')
  207. return callback(null, data)
  208. } else {
  209. err = new Error(
  210. `references api responded with non-success code: ${res.statusCode}`
  211. )
  212. return callback(err)
  213. }
  214. }
  215. )
  216. }
  217. )
  218. })
  219. },
  220. }
  221. function __guard__(value, transform) {
  222. return typeof value !== 'undefined' && value !== null
  223. ? transform(value)
  224. : undefined
  225. }