Merge pull request #8701 from overleaf/bg-simple-iterable-paths

simple iterable paths

GitOrigin-RevId: f6906016888ccfc95c88858bdac4d2633fc1c5f4
This commit is contained in:
Brian Gough 2022-07-04 11:12:01 +01:00 committed by Copybot
parent be71ea690d
commit 5a3318f5b3
7 changed files with 53 additions and 15 deletions

View file

@ -0,0 +1,15 @@
/**
* Handles malformed filetrees - when fields such as `folder.docs`,
* `folder.folders` or `folder.fileRefs` are missing it returns an
* empty array.
*/
function iterablePaths(folder, field) {
if (!folder) {
return []
}
return folder[field] || []
}
module.exports = {
iterablePaths,
}

View file

@ -4,6 +4,7 @@ const Errors = require('../Errors/Errors')
const ProjectGetter = require('./ProjectGetter') const ProjectGetter = require('./ProjectGetter')
const { promisifyAll } = require('../../util/promises') const { promisifyAll } = require('../../util/promises')
const OError = require('@overleaf/o-error') const OError = require('@overleaf/o-error')
const { iterablePaths } = require('./IterablePath')
const ProjectEntityHandler = { const ProjectEntityHandler = {
getAllDocs(projectId, callback) { getAllDocs(projectId, callback) {
@ -26,7 +27,7 @@ const ProjectEntityHandler = {
} }
const docs = {} const docs = {}
for (const { path: folderPath, folder } of folders) { for (const { path: folderPath, folder } of folders) {
for (const doc of folder.docs || []) { for (const doc of iterablePaths(folder, 'docs')) {
const content = docContents[doc._id.toString()] const content = docContents[doc._id.toString()]
if (content != null) { if (content != null) {
docs[path.join(folderPath, doc.name)] = { docs[path.join(folderPath, doc.name)] = {
@ -51,7 +52,7 @@ const ProjectEntityHandler = {
} }
const files = {} const files = {}
for (const { path: folderPath, folder } of folders) { for (const { path: folderPath, folder } of folders) {
for (const file of folder.fileRefs || []) { for (const file of iterablePaths(folder, 'fileRefs')) {
if (file != null) { if (file != null) {
files[path.join(folderPath, file.name)] = file files[path.join(folderPath, file.name)] = file
} }
@ -80,12 +81,12 @@ const ProjectEntityHandler = {
const docs = [] const docs = []
const files = [] const files = []
for (const { path: folderPath, folder } of folders) { for (const { path: folderPath, folder } of folders) {
for (const doc of folder.docs || []) { for (const doc of iterablePaths(folder, 'docs')) {
if (doc != null) { if (doc != null) {
docs.push({ path: path.join(folderPath, doc.name), doc }) docs.push({ path: path.join(folderPath, doc.name), doc })
} }
} }
for (const file of folder.fileRefs || []) { for (const file of iterablePaths(folder, 'fileRefs')) {
if (file != null) { if (file != null) {
files.push({ path: path.join(folderPath, file.name), file }) files.push({ path: path.join(folderPath, file.name), file })
} }
@ -111,7 +112,7 @@ const ProjectEntityHandler = {
const folders = ProjectEntityHandler._getAllFoldersFromProject(project) const folders = ProjectEntityHandler._getAllFoldersFromProject(project)
const docPath = {} const docPath = {}
for (const { path: folderPath, folder } of folders) { for (const { path: folderPath, folder } of folders) {
for (const doc of folder.docs || []) { for (const doc of iterablePaths(folder, 'docs')) {
docPath[doc._id] = path.join(folderPath, doc.name) docPath[doc._id] = path.join(folderPath, doc.name)
} }
} }
@ -171,7 +172,7 @@ const ProjectEntityHandler = {
return path.join(basePath, docInCurrentFolder.name) return path.join(basePath, docInCurrentFolder.name)
} else { } else {
let docPath, childFolder let docPath, childFolder
for (childFolder of folder.folders || []) { for (childFolder of iterablePaths(folder, 'folders')) {
docPath = recursivelyFindDocInFolder( docPath = recursivelyFindDocInFolder(
path.join(basePath, childFolder.name), path.join(basePath, childFolder.name),
docId, docId,
@ -211,7 +212,7 @@ const ProjectEntityHandler = {
const processFolder = (basePath, folder) => { const processFolder = (basePath, folder) => {
folders.push({ path: basePath, folder }) folders.push({ path: basePath, folder })
if (folder.folders) { if (folder.folders) {
for (const childFolder of folder.folders) { for (const childFolder of iterablePaths(folder, 'folders')) {
if (childFolder.name != null) { if (childFolder.name != null) {
const childPath = path.join(basePath, childFolder.name) const childPath = path.join(basePath, childFolder.name)
processFolder(childPath, childFolder) processFolder(childPath, childFolder)

View file

@ -17,6 +17,7 @@ const ProjectLocator = require('./ProjectLocator')
const FolderStructureBuilder = require('./FolderStructureBuilder') const FolderStructureBuilder = require('./FolderStructureBuilder')
const SafePath = require('./SafePath') const SafePath = require('./SafePath')
const { DeletedFile } = require('../../models/DeletedFile') const { DeletedFile } = require('../../models/DeletedFile')
const { iterablePaths } = require('./IterablePath')
const LOCK_NAMESPACE = 'mongoTransaction' const LOCK_NAMESPACE = 'mongoTransaction'
const ENTITY_TYPE_TO_MONGO_PATH_SEGMENT = { const ENTITY_TYPE_TO_MONGO_PATH_SEGMENT = {
@ -494,7 +495,7 @@ function _countElements(project) {
let total = 0 let total = 0
if (folder.folders) { if (folder.folders) {
total += folder.folders.length total += folder.folders.length
for (const subfolder of folder.folders) { for (const subfolder of iterablePaths(folder, 'folders')) {
total += countFolder(subfolder) total += countFolder(subfolder)
} }
} }

View file

@ -22,6 +22,7 @@ const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
const FileWriter = require('../../infrastructure/FileWriter') const FileWriter = require('../../infrastructure/FileWriter')
const EditorRealTimeController = require('../Editor/EditorRealTimeController') const EditorRealTimeController = require('../Editor/EditorRealTimeController')
const { promisifyAll } = require('../../util/promises') const { promisifyAll } = require('../../util/promises')
const { iterablePaths } = require('./IterablePath')
const LOCK_NAMESPACE = 'sequentialProjectStructureUpdateLock' const LOCK_NAMESPACE = 'sequentialProjectStructureUpdateLock'
const VALID_ROOT_DOC_EXTENSIONS = Settings.validRootDocExtensions const VALID_ROOT_DOC_EXTENSIONS = Settings.validRootDocExtensions
@ -1608,16 +1609,16 @@ const ProjectEntityUpdateHandler = {
} else if (entityType.indexOf('folder') !== -1) { } else if (entityType.indexOf('folder') !== -1) {
changes = { oldDocs: [], oldFiles: [] } changes = { oldDocs: [], oldFiles: [] }
const _recurseFolder = (folder, folderPath) => { const _recurseFolder = (folder, folderPath) => {
for (const doc of folder.docs) { for (const doc of iterablePaths(folder, 'docs')) {
changes.oldDocs.push({ doc, path: Path.join(folderPath, doc.name) }) changes.oldDocs.push({ doc, path: Path.join(folderPath, doc.name) })
} }
for (const file of folder.fileRefs) { for (const file of iterablePaths(folder, 'fileRefs')) {
changes.oldFiles.push({ changes.oldFiles.push({
file, file,
path: Path.join(folderPath, file.name), path: Path.join(folderPath, file.name),
}) })
} }
for (const childFolder of folder.folders) { for (const childFolder of iterablePaths(folder, 'folders')) {
_recurseFolder(childFolder, Path.join(folderPath, childFolder.name)) _recurseFolder(childFolder, Path.join(folderPath, childFolder.name))
} }
} }

View file

@ -4,6 +4,7 @@ const async = require('async')
const ProjectGetter = require('./ProjectGetter') const ProjectGetter = require('./ProjectGetter')
const Errors = require('../Errors/Errors') const Errors = require('../Errors/Errors')
const { promisifyMultiResult } = require('../../util/promises') const { promisifyMultiResult } = require('../../util/promises')
const { iterablePaths } = require('./IterablePath')
function findElement(options, _callback) { function findElement(options, _callback) {
// The search algorithm below potentially invokes the callback multiple // The search algorithm below potentially invokes the callback multiple
@ -203,19 +204,19 @@ function _findElementByPathWithProject(
if (entityName == null) { if (entityName == null) {
return cb(null, folder, 'folder') return cb(null, folder, 'folder')
} }
for (const file of folder.fileRefs || []) { for (const file of iterablePaths(folder, 'fileRefs')) {
if (matchFn(file != null ? file.name : undefined, entityName)) { if (matchFn(file != null ? file.name : undefined, entityName)) {
result = file result = file
type = 'file' type = 'file'
} }
} }
for (const doc of folder.docs || []) { for (const doc of iterablePaths(folder, 'docs')) {
if (matchFn(doc != null ? doc.name : undefined, entityName)) { if (matchFn(doc != null ? doc.name : undefined, entityName)) {
result = doc result = doc
type = 'doc' type = 'doc'
} }
} }
for (const childFolder of folder.folders || []) { for (const childFolder of iterablePaths(folder, 'folders')) {
if ( if (
matchFn(childFolder != null ? childFolder.name : undefined, entityName) matchFn(childFolder != null ? childFolder.name : undefined, entityName)
) { ) {

View file

@ -9,6 +9,7 @@ const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
const Errors = require('../app/src/Features/Errors/Errors') const Errors = require('../app/src/Features/Errors/Errors')
const FileStoreHandler = require('../app/src/Features/FileStore/FileStoreHandler') const FileStoreHandler = require('../app/src/Features/FileStore/FileStoreHandler')
const ProjectEntityMongoUpdateHandler = require('../app/src/Features/Project/ProjectEntityMongoUpdateHandler') const ProjectEntityMongoUpdateHandler = require('../app/src/Features/Project/ProjectEntityMongoUpdateHandler')
const { iterablePaths } = require('../app/src/Features/Project/IterablePath')
const OPTIONS = parseArgs() const OPTIONS = parseArgs()
@ -70,7 +71,7 @@ async function processProject(project) {
function findRefsInFolder(folder) { function findRefsInFolder(folder) {
let docIds = folder.docs.map(doc => doc._id) let docIds = folder.docs.map(doc => doc._id)
let fileIds = folder.fileRefs.map(file => file._id) let fileIds = folder.fileRefs.map(file => file._id)
for (const subfolder of folder.folders) { for (const subfolder of iterablePaths(folder, 'folders')) {
const subrefs = findRefsInFolder(subfolder) const subrefs = findRefsInFolder(subfolder)
docIds = docIds.concat(subrefs.docIds) docIds = docIds.concat(subrefs.docIds)
fileIds = fileIds.concat(subrefs.fileIds) fileIds = fileIds.concat(subrefs.fileIds)

View file

@ -0,0 +1,18 @@
const { expect } = require('chai')
const {
iterablePaths,
} = require('../../../../app/src/Features/Project/IterablePath')
describe('iterablePaths', function () {
it('returns an empty array for empty folders', function () {
expect(iterablePaths(null, 'docs')).to.deep.equal([])
expect(iterablePaths({}, 'docs')).to.deep.equal([])
})
it('returns the `docs` object when it is iterable', function () {
const folder = {
docs: [{ _id: 1 }, { _id: 2 }],
}
expect(iterablePaths(folder, 'docs')).to.equal(folder.docs)
})
})