ProjectEntityHandler.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. const path = require('path')
  2. const DocstoreManager = require('../Docstore/DocstoreManager')
  3. const Errors = require('../Errors/Errors')
  4. const ProjectGetter = require('./ProjectGetter')
  5. const { promisifyAll } = require('@overleaf/promise-utils')
  6. const OError = require('@overleaf/o-error')
  7. const { iterablePaths } = require('./IterablePath')
  8. const ProjectEntityHandler = {
  9. getAllDocs(projectId, callback) {
  10. // We get the path and name info from the project, and the lines and
  11. // version info from the doc store.
  12. DocstoreManager.getAllDocs(projectId, (error, docContentsArray) => {
  13. if (error != null) {
  14. return callback(error)
  15. }
  16. // Turn array from docstore into a dictionary based on doc id
  17. const docContents = {}
  18. for (const docContent of docContentsArray) {
  19. docContents[docContent._id] = docContent
  20. }
  21. ProjectEntityHandler._getAllFolders(projectId, (error, folders) => {
  22. if (error != null) {
  23. return callback(error)
  24. }
  25. const docs = {}
  26. for (const { path: folderPath, folder } of folders) {
  27. for (const doc of iterablePaths(folder, 'docs')) {
  28. const content = docContents[doc._id.toString()]
  29. if (content != null) {
  30. try {
  31. docs[path.join(folderPath, doc.name)] = {
  32. _id: doc._id,
  33. name: doc.name,
  34. lines: content.lines,
  35. rev: content.rev,
  36. folder,
  37. }
  38. } catch (err) {
  39. return callback(err)
  40. }
  41. }
  42. }
  43. }
  44. callback(null, docs)
  45. })
  46. })
  47. },
  48. getAllFiles(projectId, callback) {
  49. ProjectEntityHandler._getAllFolders(projectId, (err, folders) => {
  50. if (err != null) {
  51. return callback(err)
  52. }
  53. const files = {}
  54. for (const { path: folderPath, folder } of folders) {
  55. for (const file of iterablePaths(folder, 'fileRefs')) {
  56. if (file != null) {
  57. try {
  58. files[path.join(folderPath, file.name)] = { ...file, folder }
  59. } catch (err) {
  60. return callback(err)
  61. }
  62. }
  63. }
  64. }
  65. callback(null, files)
  66. })
  67. },
  68. getAllEntities(projectId, callback) {
  69. ProjectGetter.getProject(projectId, (err, project) => {
  70. if (err != null) {
  71. return callback(err)
  72. }
  73. if (project == null) {
  74. return callback(new Errors.NotFoundError('project not found'))
  75. }
  76. try {
  77. const entities = ProjectEntityHandler.getAllEntitiesFromProject(project)
  78. callback(null, entities)
  79. } catch (err) {
  80. callback(err)
  81. }
  82. })
  83. },
  84. getAllEntitiesFromProject(project) {
  85. const folders = ProjectEntityHandler._getAllFoldersFromProject(project)
  86. const docs = []
  87. const files = []
  88. for (const { path: folderPath, folder } of folders) {
  89. for (const doc of iterablePaths(folder, 'docs')) {
  90. if (doc != null) {
  91. docs.push({ path: path.join(folderPath, doc.name), doc })
  92. }
  93. }
  94. for (const file of iterablePaths(folder, 'fileRefs')) {
  95. if (file != null) {
  96. files.push({ path: path.join(folderPath, file.name), file })
  97. }
  98. }
  99. }
  100. return { docs, files, folders }
  101. },
  102. getAllDocPathsFromProjectById(projectId, callback) {
  103. ProjectGetter.getProjectWithoutDocLines(projectId, (err, project) => {
  104. if (err != null) {
  105. return callback(err)
  106. }
  107. if (project == null) {
  108. return callback(Errors.NotFoundError('no project'))
  109. }
  110. try {
  111. const docPaths = ProjectEntityHandler.getAllDocPathsFromProject(project)
  112. callback(null, docPaths)
  113. } catch (err) {
  114. callback(err)
  115. }
  116. })
  117. },
  118. getAllDocPathsFromProject(project) {
  119. const folders = ProjectEntityHandler._getAllFoldersFromProject(project)
  120. const docPath = {}
  121. for (const { path: folderPath, folder } of folders) {
  122. for (const doc of iterablePaths(folder, 'docs')) {
  123. docPath[doc._id] = path.join(folderPath, doc.name)
  124. }
  125. }
  126. return docPath
  127. },
  128. getDoc(projectId, docId, options, callback) {
  129. if (options == null) {
  130. options = {}
  131. }
  132. if (typeof options === 'function') {
  133. callback = options
  134. options = {}
  135. }
  136. DocstoreManager.getDoc(projectId, docId, options, callback)
  137. },
  138. /**
  139. * @param {ObjectId | string} projectId
  140. * @param {ObjectId | string} docId
  141. * @param {Function} callback
  142. */
  143. getDocPathByProjectIdAndDocId(projectId, docId, callback) {
  144. ProjectGetter.getProjectWithoutDocLines(projectId, (err, project) => {
  145. if (err != null) {
  146. return callback(err)
  147. }
  148. if (project == null) {
  149. return callback(new Errors.NotFoundError('no project'))
  150. }
  151. ProjectEntityHandler.getDocPathFromProjectByDocId(
  152. project,
  153. docId,
  154. (err, docPath) => {
  155. if (err) return callback(Errors.OError.tag(err))
  156. if (docPath == null) {
  157. return callback(new Errors.NotFoundError('no doc'))
  158. }
  159. callback(null, docPath)
  160. }
  161. )
  162. })
  163. },
  164. /**
  165. * @param {Project} project
  166. * @param {ObjectId | string} docId
  167. * @param {Function} callback
  168. */
  169. getDocPathFromProjectByDocId(project, docId, callback) {
  170. function recursivelyFindDocInFolder(basePath, docId, folder) {
  171. const docInCurrentFolder = (folder.docs || []).find(
  172. currentDoc => currentDoc._id.toString() === docId.toString()
  173. )
  174. if (docInCurrentFolder != null) {
  175. return path.join(basePath, docInCurrentFolder.name)
  176. } else {
  177. let docPath, childFolder
  178. for (childFolder of iterablePaths(folder, 'folders')) {
  179. docPath = recursivelyFindDocInFolder(
  180. path.join(basePath, childFolder.name),
  181. docId,
  182. childFolder
  183. )
  184. if (docPath != null) {
  185. return docPath
  186. }
  187. }
  188. return null
  189. }
  190. }
  191. try {
  192. const docPath = recursivelyFindDocInFolder(
  193. '/',
  194. docId,
  195. project.rootFolder[0]
  196. )
  197. callback(null, docPath)
  198. } catch (err) {
  199. callback(err)
  200. }
  201. },
  202. _getAllFolders(projectId, callback) {
  203. ProjectGetter.getProjectWithoutDocLines(projectId, (err, project) => {
  204. if (err != null) {
  205. return callback(err)
  206. }
  207. if (project == null) {
  208. return callback(new Errors.NotFoundError('no project'))
  209. }
  210. try {
  211. const folders = ProjectEntityHandler._getAllFoldersFromProject(project)
  212. callback(null, folders)
  213. } catch (err) {
  214. callback(err)
  215. }
  216. })
  217. },
  218. _getAllFoldersFromProject(project) {
  219. const folders = []
  220. try {
  221. const processFolder = (basePath, folder) => {
  222. folders.push({ path: basePath, folder })
  223. if (folder.folders) {
  224. for (const childFolder of iterablePaths(folder, 'folders')) {
  225. if (childFolder.name != null) {
  226. const childPath = path.join(basePath, childFolder.name)
  227. processFolder(childPath, childFolder)
  228. }
  229. }
  230. }
  231. }
  232. processFolder('/', project.rootFolder[0])
  233. return folders
  234. } catch (err) {
  235. throw OError.tag(err, 'Error getting folders', { projectId: project._id })
  236. }
  237. },
  238. }
  239. module.exports = ProjectEntityHandler
  240. module.exports.promises = promisifyAll(ProjectEntityHandler, {
  241. without: ['getAllEntitiesFromProject'],
  242. multiResult: {
  243. getDoc: ['lines', 'rev', 'version', 'ranges'],
  244. },
  245. })