ProjectEntityHandler.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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('../../util/promises')
  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. docs[path.join(folderPath, doc.name)] = {
  31. _id: doc._id,
  32. name: doc.name,
  33. lines: content.lines,
  34. rev: content.rev,
  35. folder,
  36. }
  37. }
  38. }
  39. }
  40. callback(null, docs)
  41. })
  42. })
  43. },
  44. getAllFiles(projectId, callback) {
  45. ProjectEntityHandler._getAllFolders(projectId, (err, folders) => {
  46. if (err != null) {
  47. return callback(err)
  48. }
  49. const files = {}
  50. for (const { path: folderPath, folder } of folders) {
  51. for (const file of iterablePaths(folder, 'fileRefs')) {
  52. if (file != null) {
  53. files[path.join(folderPath, file.name)] = { ...file, folder }
  54. }
  55. }
  56. }
  57. callback(null, files)
  58. })
  59. },
  60. getAllEntities(projectId, callback) {
  61. ProjectGetter.getProject(projectId, (err, project) => {
  62. if (err != null) {
  63. return callback(err)
  64. }
  65. if (project == null) {
  66. return callback(new Errors.NotFoundError('project not found'))
  67. }
  68. const entities = ProjectEntityHandler.getAllEntitiesFromProject(project)
  69. callback(null, entities)
  70. })
  71. },
  72. getAllEntitiesFromProject(project) {
  73. const folders = ProjectEntityHandler._getAllFoldersFromProject(project)
  74. const docs = []
  75. const files = []
  76. for (const { path: folderPath, folder } of folders) {
  77. for (const doc of iterablePaths(folder, 'docs')) {
  78. if (doc != null) {
  79. docs.push({ path: path.join(folderPath, doc.name), doc })
  80. }
  81. }
  82. for (const file of iterablePaths(folder, 'fileRefs')) {
  83. if (file != null) {
  84. files.push({ path: path.join(folderPath, file.name), file })
  85. }
  86. }
  87. }
  88. return { docs, files, folders }
  89. },
  90. getAllDocPathsFromProjectById(projectId, callback) {
  91. ProjectGetter.getProjectWithoutDocLines(projectId, (err, project) => {
  92. if (err != null) {
  93. return callback(err)
  94. }
  95. if (project == null) {
  96. return callback(Errors.NotFoundError('no project'))
  97. }
  98. const docPaths = ProjectEntityHandler.getAllDocPathsFromProject(project)
  99. callback(null, docPaths)
  100. })
  101. },
  102. getAllDocPathsFromProject(project) {
  103. const folders = ProjectEntityHandler._getAllFoldersFromProject(project)
  104. const docPath = {}
  105. for (const { path: folderPath, folder } of folders) {
  106. for (const doc of iterablePaths(folder, 'docs')) {
  107. docPath[doc._id] = path.join(folderPath, doc.name)
  108. }
  109. }
  110. return docPath
  111. },
  112. getDoc(projectId, docId, options, callback) {
  113. if (options == null) {
  114. options = {}
  115. }
  116. if (typeof options === 'function') {
  117. callback = options
  118. options = {}
  119. }
  120. DocstoreManager.getDoc(projectId, docId, options, callback)
  121. },
  122. /**
  123. * @param {ObjectID | string} projectId
  124. * @param {ObjectID | string} docId
  125. * @param {Function} callback
  126. */
  127. getDocPathByProjectIdAndDocId(projectId, docId, callback) {
  128. ProjectGetter.getProjectWithoutDocLines(projectId, (err, project) => {
  129. if (err != null) {
  130. return callback(err)
  131. }
  132. if (project == null) {
  133. return callback(new Errors.NotFoundError('no project'))
  134. }
  135. ProjectEntityHandler.getDocPathFromProjectByDocId(
  136. project,
  137. docId,
  138. (err, docPath) => {
  139. if (err) return callback(Errors.OError.tag(err))
  140. if (docPath == null) {
  141. return callback(new Errors.NotFoundError('no doc'))
  142. }
  143. callback(null, docPath)
  144. }
  145. )
  146. })
  147. },
  148. /**
  149. * @param {Project} project
  150. * @param {ObjectID | string} docId
  151. * @param {Function} callback
  152. */
  153. getDocPathFromProjectByDocId(project, docId, callback) {
  154. function recursivelyFindDocInFolder(basePath, docId, folder) {
  155. const docInCurrentFolder = (folder.docs || []).find(
  156. currentDoc => currentDoc._id.toString() === docId.toString()
  157. )
  158. if (docInCurrentFolder != null) {
  159. return path.join(basePath, docInCurrentFolder.name)
  160. } else {
  161. let docPath, childFolder
  162. for (childFolder of iterablePaths(folder, 'folders')) {
  163. docPath = recursivelyFindDocInFolder(
  164. path.join(basePath, childFolder.name),
  165. docId,
  166. childFolder
  167. )
  168. if (docPath != null) {
  169. return docPath
  170. }
  171. }
  172. return null
  173. }
  174. }
  175. const docPath = recursivelyFindDocInFolder(
  176. '/',
  177. docId,
  178. project.rootFolder[0]
  179. )
  180. callback(null, docPath)
  181. },
  182. _getAllFolders(projectId, callback) {
  183. ProjectGetter.getProjectWithoutDocLines(projectId, (err, project) => {
  184. if (err != null) {
  185. return callback(err)
  186. }
  187. if (project == null) {
  188. return callback(new Errors.NotFoundError('no project'))
  189. }
  190. const folders = ProjectEntityHandler._getAllFoldersFromProject(project)
  191. callback(null, folders)
  192. })
  193. },
  194. _getAllFoldersFromProject(project) {
  195. const folders = []
  196. try {
  197. const processFolder = (basePath, folder) => {
  198. folders.push({ path: basePath, folder })
  199. if (folder.folders) {
  200. for (const childFolder of iterablePaths(folder, 'folders')) {
  201. if (childFolder.name != null) {
  202. const childPath = path.join(basePath, childFolder.name)
  203. processFolder(childPath, childFolder)
  204. }
  205. }
  206. }
  207. }
  208. processFolder('/', project.rootFolder[0])
  209. return folders
  210. } catch (err) {
  211. throw OError.tag(err, 'Error getting folders', { projectId: project._id })
  212. }
  213. },
  214. }
  215. module.exports = ProjectEntityHandler
  216. module.exports.promises = promisifyAll(ProjectEntityHandler, {
  217. without: ['getAllEntitiesFromProject'],
  218. multiResult: {
  219. getDoc: ['lines', 'rev', 'version', 'ranges'],
  220. },
  221. })