From 6d89ab0377227d4097f6cca6f48a1fa0e6439ccf Mon Sep 17 00:00:00 2001 From: Alasdair Smith Date: Thu, 4 Aug 2022 10:42:14 +0100 Subject: [PATCH] Add script to delete orphaned doc comment ranges (#9086) Add script to delete orphaned doc comment ranges GitOrigin-RevId: c9ee92f791f8657ba0157cc843445c3cba144312 --- .../app/src/Features/Chat/ChatApiHandler.js | 30 +++++----- .../delete_orphaned_doc_comment_ranges.js | 57 +++++++++++++++++++ 2 files changed, 73 insertions(+), 14 deletions(-) create mode 100644 services/web/scripts/delete_orphaned_doc_comment_ranges.js diff --git a/services/web/app/src/Features/Chat/ChatApiHandler.js b/services/web/app/src/Features/Chat/ChatApiHandler.js index fa765a8379..6e1ee966ee 100644 --- a/services/web/app/src/Features/Chat/ChatApiHandler.js +++ b/services/web/app/src/Features/Chat/ChatApiHandler.js @@ -17,6 +17,20 @@ const request = require('request') const settings = require('@overleaf/settings') const { promisify } = require('util') +function getThreads(project_id, callback) { + if (callback == null) { + callback = function () {} + } + return ChatApiHandler._apiRequest( + { + url: `${settings.apis.chat.internal_url}/project/${project_id}/threads`, + method: 'GET', + json: true, + }, + callback + ) +} + function destroyProject(project_id, callback) { if (callback == null) { callback = function () {} @@ -97,20 +111,6 @@ module.exports = ChatApiHandler = { ) }, - getThreads(project_id, callback) { - if (callback == null) { - callback = function () {} - } - return ChatApiHandler._apiRequest( - { - url: `${settings.apis.chat.internal_url}/project/${project_id}/threads`, - method: 'GET', - json: true, - }, - callback - ) - }, - resolveThread(project_id, thread_id, user_id, callback) { if (callback == null) { callback = function () {} @@ -181,9 +181,11 @@ module.exports = ChatApiHandler = { ) }, + getThreads, destroyProject, promises: { + getThreads: promisify(getThreads), destroyProject: promisify(destroyProject), }, } diff --git a/services/web/scripts/delete_orphaned_doc_comment_ranges.js b/services/web/scripts/delete_orphaned_doc_comment_ranges.js new file mode 100644 index 0000000000..eec4ecc3a7 --- /dev/null +++ b/services/web/scripts/delete_orphaned_doc_comment_ranges.js @@ -0,0 +1,57 @@ +const WRITE_CONCURRENCY = parseInt(process.env.WRITE_CONCURRENCY, 10) || 10 + +const minimist = require('minimist') + +const { waitForDb } = require('../app/src/infrastructure/mongodb') +const ChatApiHandler = require('../app/src/Features/Chat/ChatApiHandler') +const DocstoreManager = require('../app/src/Features/Docstore/DocstoreManager') +const DocumentUpdaterHandler = require('../app/src/Features/DocumentUpdater/DocumentUpdaterHandler') +const { promiseMapWithLimit } = require('../app/src/util/promises') + +/** + * Remove doc comment ranges that are "orphaned" as they do have matching chat + * threads. This can happen when adding comments and the HTTP request fails, but + * the ShareJS op succeeded (eventually). See https://github.com/overleaf/internal/issues/3425 + * for more detail. + */ +async function main() { + await waitForDb() + + const argv = minimist(process.argv.slice(2)) + const { projectId, docId } = argv + + const threads = await ChatApiHandler.promises.getThreads(projectId) + const threadIds = Object.keys(threads) + + const doc = await DocstoreManager.promises.getDoc(projectId, docId) + const comments = doc.ranges.comments + + const orphanedCommentIds = comments.filter(comment => { + const commentThreadId = comment.op.t + return !threadIds.includes(commentThreadId) + }) + + await promiseMapWithLimit( + WRITE_CONCURRENCY, + orphanedCommentIds, + async comment => { + await DocumentUpdaterHandler.promises.deleteThread( + projectId, + docId, + comment.op.t + ) + } + ) + + await DocumentUpdaterHandler.promises.flushDocToMongo(projectId, docId) +} + +main() + .then(() => { + console.log('Done.') + process.exit(0) + }) + .catch(error => { + console.error({ error }) + process.exit(1) + })