ReferencesHandler.mjs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /* eslint-disable
  2. n/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. import OError from '@overleaf/o-error'
  16. import logger from '@overleaf/logger'
  17. import request from 'request'
  18. import settings from '@overleaf/settings'
  19. import Features from '../../infrastructure/Features.js'
  20. import ProjectGetter from '../Project/ProjectGetter.js'
  21. import UserGetter from '../User/UserGetter.js'
  22. import DocumentUpdaterHandler from '../DocumentUpdater/DocumentUpdaterHandler.js'
  23. import _ from 'lodash'
  24. import Async from 'async'
  25. import Errors from '../Errors/Errors.js'
  26. import { promisify } from '@overleaf/promise-utils'
  27. import HistoryURLHelper from '../History/HistoryURLHelper.js'
  28. let ReferencesHandler
  29. if (!Features.hasFeature('references')) {
  30. logger.debug('references search not enabled')
  31. }
  32. export default ReferencesHandler = {
  33. _buildDocUrl(projectId, docId) {
  34. return {
  35. url: `${settings.apis.docstore.url}/project/${projectId}/doc/${docId}/raw`,
  36. }
  37. },
  38. _findBibFileRefs(project) {
  39. const fileRefs = []
  40. function _process(folder) {
  41. _.forEach(folder.fileRefs || [], function (file) {
  42. if (
  43. __guard__(file != null ? file.name : undefined, x1 =>
  44. x1.match(/^.*\.bib$/)
  45. )
  46. ) {
  47. return fileRefs.push(file)
  48. }
  49. })
  50. return _.forEach(folder.folders || [], folder => _process(folder))
  51. }
  52. _.forEach(project.rootFolder || [], rootFolder => _process(rootFolder))
  53. return fileRefs
  54. },
  55. _findBibDocIds(project) {
  56. const ids = []
  57. function _process(folder) {
  58. _.forEach(folder.docs || [], function (doc) {
  59. if (
  60. __guard__(doc != null ? doc.name : undefined, x1 =>
  61. x1.match(/^.*\.bib$/)
  62. )
  63. ) {
  64. return ids.push(doc._id)
  65. }
  66. })
  67. return _.forEach(folder.folders || [], folder => _process(folder))
  68. }
  69. _.forEach(project.rootFolder || [], rootFolder => _process(rootFolder))
  70. return ids
  71. },
  72. _isFullIndex(project, callback) {
  73. if (callback == null) {
  74. callback = function () {}
  75. }
  76. return UserGetter.getUser(
  77. project.owner_ref,
  78. { features: true },
  79. function (err, owner) {
  80. if (err != null) {
  81. return callback(err)
  82. }
  83. const features = owner != null ? owner.features : undefined
  84. return callback(
  85. null,
  86. (features != null ? features.references : undefined) === true ||
  87. (features != null ? features.referencesSearch : undefined) === true
  88. )
  89. }
  90. )
  91. },
  92. indexAll(projectId, callback) {
  93. if (callback == null) {
  94. callback = function () {}
  95. }
  96. return ProjectGetter.getProject(
  97. projectId,
  98. { rootFolder: true, owner_ref: 1, 'overleaf.history.id': 1 },
  99. function (err, project) {
  100. if (err) {
  101. OError.tag(err, 'error finding project', {
  102. projectId,
  103. })
  104. return callback(err)
  105. }
  106. if (!project) {
  107. return callback(
  108. new Errors.NotFoundError(`project does not exist: ${projectId}`)
  109. )
  110. }
  111. logger.debug({ projectId }, 'indexing all bib files in project')
  112. const docIds = ReferencesHandler._findBibDocIds(project)
  113. const fileRefs = ReferencesHandler._findBibFileRefs(project)
  114. return ReferencesHandler._doIndexOperation(
  115. projectId,
  116. project,
  117. docIds,
  118. fileRefs,
  119. callback
  120. )
  121. }
  122. )
  123. },
  124. _doIndexOperation(projectId, project, docIds, fileRefs, callback) {
  125. if (!Features.hasFeature('references')) {
  126. return callback()
  127. }
  128. const historyId = project?.overleaf?.history?.id
  129. if (!historyId) {
  130. return callback(
  131. new OError('project does not have a history id', { projectId })
  132. )
  133. }
  134. return ReferencesHandler._isFullIndex(project, function (err, isFullIndex) {
  135. if (err) {
  136. OError.tag(err, 'error checking whether to do full index', {
  137. projectId,
  138. })
  139. return callback(err)
  140. }
  141. logger.debug(
  142. { projectId, docIds },
  143. 'flushing docs to mongo before calling references service'
  144. )
  145. return Async.series(
  146. docIds.map(
  147. docId => cb =>
  148. DocumentUpdaterHandler.flushDocToMongo(projectId, docId, cb)
  149. ),
  150. function (err) {
  151. // continue
  152. if (err) {
  153. OError.tag(err, 'error flushing docs to mongo', {
  154. projectId,
  155. docIds,
  156. })
  157. return callback(err)
  158. }
  159. const bibDocUrls = docIds.map(docId =>
  160. ReferencesHandler._buildDocUrl(projectId, docId)
  161. )
  162. const bibFileUrls = fileRefs.map(fileRef =>
  163. HistoryURLHelper.projectHistoryURLWithFilestoreFallback(
  164. settings,
  165. projectId,
  166. historyId,
  167. fileRef,
  168. 'bibFileUrls'
  169. )
  170. )
  171. const sourceURLs = bibDocUrls.concat(bibFileUrls)
  172. return request.post(
  173. {
  174. url: `${settings.apis.references.url}/project/${projectId}/index`,
  175. json: {
  176. docUrls: sourceURLs.map(item => item.fallbackURL || item.url),
  177. sourceURLs,
  178. fullIndex: isFullIndex,
  179. },
  180. },
  181. function (err, res, data) {
  182. if (err) {
  183. OError.tag(err, 'error communicating with references api', {
  184. projectId,
  185. })
  186. return callback(err)
  187. }
  188. if (res.statusCode >= 200 && res.statusCode < 300) {
  189. logger.debug({ projectId }, 'got keys from references api')
  190. return callback(null, data)
  191. } else {
  192. err = new Error(
  193. `references api responded with non-success code: ${res.statusCode}`
  194. )
  195. return callback(err)
  196. }
  197. }
  198. )
  199. }
  200. )
  201. })
  202. },
  203. }
  204. ReferencesHandler.promises = {
  205. indexAll: promisify(ReferencesHandler.indexAll),
  206. }
  207. function __guard__(value, transform) {
  208. return typeof value !== 'undefined' && value !== null
  209. ? transform(value)
  210. : undefined
  211. }