Kaynağa Gözat

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

Add more ways to fix bad filetrees

GitOrigin-RevId: a60ab819fbd7c0547cf2d00022df71d4cba5cf9d
Eric Mc Sween 4 yıl önce
ebeveyn
işleme
a177872179
1 değiştirilmiş dosya ile 99 ekleme ve 13 silme
  1. 99 13
      services/web/scripts/fix_malformed_filetree.js

+ 99 - 13
services/web/scripts/fix_malformed_filetree.js

@@ -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)