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