Merge pull request #9892 from overleaf/em-bad-filetrees

Add more ways to fix bad filetrees

GitOrigin-RevId: a60ab819fbd7c0547cf2d00022df71d4cba5cf9d
This commit is contained in:
Eric Mc Sween 2022-10-11 10:26:09 -04:00 committed by Copybot
parent 82f0792e68
commit a177872179

View file

@ -1,19 +1,34 @@
/**
* This script fixes problems found by the find_malformed_filetrees.js script.
*
* The script takes two arguments: the project id and the problemtatic path.
* This is the output format of each line in the find_malformed_filetrees.js
* script.
*/
const { ObjectId } = require('mongodb')
const { db, waitForDb } = require('../app/src/infrastructure/mongodb')
const ProjectLocator = require('../app/src/Features/Project/ProjectLocator')
async function main() {
const { projectId, mongoPath } = parseArgs()
await waitForDb()
const pathSegments = mongoPath.split('.')
const lastPathSegment = pathSegments[pathSegments.length - 1]
let modifiedCount
if (mongoPath === 'rootFolder.0') {
if (isRootFolder(mongoPath)) {
modifiedCount = await fixRootFolder(projectId)
} else if (endsWithNumber(mongoPath)) {
modifiedCount = await removeNullFolders(projectId, parentPath(mongoPath))
} else if (['docs', 'folders', 'fileRefs'].includes(lastPathSegment)) {
modifiedCount = await ensureElementIsArray(projectId, mongoPath)
} else if (isArrayElement(mongoPath)) {
modifiedCount = await removeNulls(projectId, parentPath(mongoPath))
} else if (isArray(mongoPath)) {
modifiedCount = await fixArray(projectId, mongoPath)
} else if (isFolderId(mongoPath)) {
modifiedCount = await fixFolderId(projectId, mongoPath)
} else if (isDocOrFileId(mongoPath)) {
modifiedCount = await removeElementsWithoutIds(
projectId,
parentPath(parentPath(mongoPath))
)
} else if (isName(mongoPath)) {
modifiedCount = await fixName(projectId, mongoPath)
} else {
console.error(`Unexpected mongo path: ${mongoPath}`)
process.exit(1)
@ -33,10 +48,30 @@ function parseArgs() {
return { projectId: ObjectId(projectId), mongoPath }
}
function endsWithNumber(path) {
function isRootFolder(path) {
return path === 'rootFolder.0'
}
function isArray(path) {
return /\.(docs|folders|fileRefs)$/.test(path)
}
function isArrayElement(path) {
return /\.\d+$/.test(path)
}
function isFolderId(path) {
return /\.folders\.\d+\._id$/.test(path)
}
function isDocOrFileId(path) {
return /\.(docs|fileRefs)\.\d+\._id$/.test(path)
}
function isName(path) {
return /\.name$/.test(path)
}
function parentPath(path) {
return path.slice(0, path.lastIndexOf('.'))
}
@ -65,12 +100,12 @@ async function fixRootFolder(projectId) {
}
/**
* Remove all null entries from the given folders array
* Remove all nulls from the given docs/files/folders array
*/
async function removeNullFolders(projectId, foldersPath) {
async function removeNulls(projectId, path) {
const result = await db.projects.updateOne(
{ _id: projectId, [foldersPath]: { $exists: true } },
{ $pull: { [foldersPath]: null } }
{ _id: projectId, [path]: { $type: 'array' } },
{ $pull: { [path]: null } }
)
return result.modifiedCount
}
@ -78,7 +113,7 @@ async function removeNullFolders(projectId, foldersPath) {
/**
* If the element at the given path is not an array, set it to an empty array
*/
async function ensureElementIsArray(projectId, path) {
async function fixArray(projectId, path) {
const result = await db.projects.updateOne(
{ _id: projectId, [path]: { $not: { $type: 'array' } } },
{ $set: { [path]: [] } }
@ -86,6 +121,57 @@ async function ensureElementIsArray(projectId, path) {
return result.modifiedCount
}
/**
* Generate a missing id for a folder
*/
async function fixFolderId(projectId, path) {
const result = await db.projects.updateOne(
{ _id: projectId, [path]: { $exists: false } },
{ $set: { [path]: ObjectId() } }
)
return result.modifiedCount
}
/**
* Remove elements that don't have ids in the array at the given path
*/
async function removeElementsWithoutIds(projectId, path) {
const result = await db.projects.updateOne(
{ _id: projectId, [path]: { $type: 'array' } },
{ $pull: { [path]: { _id: null } } }
)
return result.modifiedCount
}
/**
* Give a name to a file/doc/folder that doesn't have one
*/
async function fixName(projectId, path) {
const project = await db.projects.findOne(
{ _id: projectId },
{ projection: { rootFolder: 1 } }
)
const arrayPath = parentPath(parentPath(path))
const array = ProjectLocator.findElementByMongoPath(project, arrayPath)
const existingNames = new Set(array.map(x => x.name))
const name = findUniqueName(existingNames)
const result = await db.projects.updateOne(
{ _id: projectId, [path]: { $exists: false } },
{ $set: { [path]: name } }
)
return result.modifiedCount
}
function findUniqueName(existingFilenames) {
let index = 0
let filename = 'untitled'
while (existingFilenames.has(filename)) {
index += 1
filename = `untitled-${index}`
}
return filename
}
main()
.then(() => {
process.exit(0)