Răsfoiți Sursa

Merge pull request #21989 from overleaf/em-dangling-comments-script

Script for deleting dangling comments in a project

GitOrigin-RevId: da947834d5a824bbf38ec8e98e212796faf66568
Eric Mc Sween 1 an în urmă
părinte
comite
7e42b1f3eb

+ 3 - 0
services/web/app/src/Features/Docstore/DocstoreManager.js

@@ -40,6 +40,9 @@ function deleteDoc(projectId, docId, name, deletedAt, callback) {
   })
 }
 
+/**
+ * @param {string} projectId
+ */
 function getAllDocs(projectId, callback) {
   const url = `${settings.apis.docstore.url}/project/${projectId}/doc`
   request.get(

+ 6 - 0
services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js

@@ -10,6 +10,9 @@ const { promisifyMultiResult } = require('@overleaf/promise-utils')
 const ProjectGetter = require('../Project/ProjectGetter')
 const FileStoreHandler = require('../FileStore/FileStoreHandler')
 
+/**
+ * @param {string} projectId
+ */
 function flushProjectToMongo(projectId, callback) {
   _makeRequest(
     {
@@ -29,6 +32,9 @@ function flushMultipleProjectsToMongo(projectIds, callback) {
   async.series(jobs, callback)
 }
 
+/**
+ * @param {string} projectId
+ */
 function flushProjectToMongoAndDelete(projectId, callback) {
   _makeRequest(
     {

+ 85 - 0
services/web/scripts/delete_dangling_comments.mjs

@@ -0,0 +1,85 @@
+// @ts-check
+
+import minimist from 'minimist'
+import ChatApiHandler from '../app/src/Features/Chat/ChatApiHandler.js'
+import DocumentUpdaterHandler from '../app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js'
+import DocstoreManager from '../app/src/Features/Docstore/DocstoreManager.js'
+import HistoryManager from '../app/src/Features/History/HistoryManager.js'
+import { db, ObjectId } from '../app/src/infrastructure/mongodb.js'
+
+const OPTS = parseArgs()
+
+function usage() {
+  console.error(
+    'Usage: node delete_dangling_comments.mjs [--commit] PROJECT_ID...'
+  )
+}
+
+function parseArgs() {
+  const args = minimist(process.argv.slice(2), {
+    boolean: ['commit'],
+  })
+  if (args._.length === 0) {
+    usage()
+    process.exit(0)
+  }
+  return {
+    projectIds: args._,
+    commit: args.commit,
+  }
+}
+
+async function processProject(projectId) {
+  console.log(`Processing project ${projectId}...`)
+  await DocumentUpdaterHandler.promises.flushProjectToMongoAndDelete(projectId)
+  const docRanges = await DocstoreManager.promises.getAllRanges(projectId)
+  const threads = await ChatApiHandler.promises.getThreads(projectId)
+  const threadIds = new Set(Object.keys(threads))
+  let commentsDeleted = 0
+  for (const doc of docRanges) {
+    const commentsDeletedInDoc = await processDoc(projectId, doc, threadIds)
+    commentsDeleted += commentsDeletedInDoc
+  }
+  if (OPTS.commit) {
+    console.log(`${commentsDeleted} comments deleted`)
+    if (commentsDeleted > 0) {
+      console.log(`Resyncing history for project ${projectId}`)
+      await HistoryManager.promises.resyncProject(projectId)
+    }
+  }
+}
+
+async function processDoc(projectId, doc, threadIds) {
+  let commentsDeleted = 0
+  for (const comment of doc.ranges.comments ?? []) {
+    const threadId = comment.op.t
+    if (!threadIds.has(threadId)) {
+      if (OPTS.commit) {
+        console.log(`Deleting dangling comment ${comment.op.t}...`)
+        await deleteComment(doc._id, threadId)
+        commentsDeleted += 1
+      } else {
+        console.log(`Would delete dangling comment ${comment.op.t}...`)
+      }
+    }
+  }
+  return commentsDeleted
+}
+
+async function deleteComment(docId, threadId) {
+  await db.docs.updateOne(
+    { _id: new ObjectId(docId) },
+    {
+      $pull: { 'ranges.comments': { 'op.t': new ObjectId(threadId) } },
+    }
+  )
+}
+
+// Main loop
+for (const projectId of OPTS.projectIds) {
+  await processProject(projectId)
+}
+if (!OPTS.commit) {
+  console.log('This was a dry run. Rerun with --commit to apply changes')
+}
+process.exit(0)