From e44f1370901dea2c7fd514e861fb2bb5f97d2a64 Mon Sep 17 00:00:00 2001 From: Eric Mc Sween <5454374+emcsween@users.noreply.github.com> Date: Fri, 12 Sep 2025 09:44:33 -0400 Subject: [PATCH] Merge pull request #28431 from overleaf/em-docupdater-get-ranges Look in docupdater when obtaining comments for DSMP GitOrigin-RevId: acaead280f725da8e0c438d5b429fa983c3cad86 --- services/document-updater/app.js | 2 ++ .../document-updater/app/js/HttpController.js | 7 +++++++ .../document-updater/app/js/ProjectManager.js | 11 ++++++++++ .../document-updater/app/js/RedisManager.js | 6 ++++++ .../DocumentUpdater/DocumentUpdaterHandler.js | 8 +++++++ .../src/mocks/MockDocUpdaterApi.mjs | 21 +++++++++++++++++++ 6 files changed, 55 insertions(+) diff --git a/services/document-updater/app.js b/services/document-updater/app.js index 22a1421c02..8e400950b0 100644 --- a/services/document-updater/app.js +++ b/services/document-updater/app.js @@ -140,6 +140,8 @@ app.get( HttpController.getComment ) app.get('/project/:project_id/doc/:doc_id/peek', HttpController.peekDoc) +app.get('/project/:project_id/ranges', HttpController.getProjectRanges) + // temporarily keep the GET method for backwards compatibility app.get('/project/:project_id/doc', HttpController.getProjectDocsAndFlushIfOld) // will migrate to the POST method of get_and_flush_if_old instead diff --git a/services/document-updater/app/js/HttpController.js b/services/document-updater/app/js/HttpController.js index 7c1e5d9de2..497c0e5c35 100644 --- a/services/document-updater/app/js/HttpController.js +++ b/services/document-updater/app/js/HttpController.js @@ -181,6 +181,12 @@ async function getProjectLastUpdatedAt(req, res) { res.json({ lastUpdatedAt: timestamps.pop() }) } +async function getProjectRanges(req, res) { + const projectId = req.params.project_id + const docs = await ProjectManager.promises.getProjectRanges(projectId) + res.json({ docs }) +} + async function clearProjectState(req, res) { const projectId = req.params.project_id const timer = new Metrics.Timer('http.clearProjectState') @@ -537,6 +543,7 @@ module.exports = { peekDoc: expressify(peekDoc), getProjectDocsAndFlushIfOld: expressify(getProjectDocsAndFlushIfOld), getProjectLastUpdatedAt: expressify(getProjectLastUpdatedAt), + getProjectRanges: expressify(getProjectRanges), clearProjectState: expressify(clearProjectState), appendToDoc: expressify(appendToDoc), setDoc: expressify(setDoc), diff --git a/services/document-updater/app/js/ProjectManager.js b/services/document-updater/app/js/ProjectManager.js index dc755d56e0..1eecf1f7b4 100644 --- a/services/document-updater/app/js/ProjectManager.js +++ b/services/document-updater/app/js/ProjectManager.js @@ -118,6 +118,16 @@ async function getProjectDocsAndFlushIfOld( return docs } +async function getProjectRanges(projectId) { + const docIds = await RedisManager.promises.getDocIdsInProject(projectId) + const docs = [] + for (const docId of docIds) { + const ranges = await RedisManager.promises.getDocRanges(docId) + docs.push({ id: docId, ranges }) + } + return docs +} + async function clearProjectState(projectId) { await RedisManager.promises.clearProjectState(projectId) } @@ -234,6 +244,7 @@ const ProjectManager = { queueFlushAndDeleteProject, getProjectDocsTimestamps, getProjectDocsAndFlushIfOld, + getProjectRanges, clearProjectState, updateProjectWithLocks, } diff --git a/services/document-updater/app/js/RedisManager.js b/services/document-updater/app/js/RedisManager.js index 2c9ca4934f..e4eedf8fdd 100644 --- a/services/document-updater/app/js/RedisManager.js +++ b/services/document-updater/app/js/RedisManager.js @@ -296,6 +296,12 @@ const RedisManager = { } }, + async getDocRanges(docId) { + const json = await rclient.get(keys.ranges({ doc_id: docId })) + const ranges = RedisManager._deserializeRanges(json) + return ranges + }, + async getDocVersion(docId) { const result = await rclient.mget(keys.docVersion({ doc_id: docId })) let [version] = result || [] diff --git a/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js b/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js index 0b19e8681b..9fc58cd529 100644 --- a/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js +++ b/services/web/app/src/Features/DocumentUpdater/DocumentUpdaterHandler.js @@ -24,6 +24,13 @@ async function getProjectLastUpdatedAt(projectId) { return body.lastUpdatedAt != null ? new Date(body.lastUpdatedAt) : null } +async function getProjectRanges(projectId) { + const { docs } = await fetchJson(`${BASE_URL}/project/${projectId}/ranges`, { + signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), + }) + return docs +} + /** * @param {string} projectId */ @@ -518,6 +525,7 @@ const DocumentUpdaterHandler = { getComment, getDocument, getProjectLastUpdatedAt, + getProjectRanges, setDocument, appendToDocument, getProjectDocsIfMatch, diff --git a/services/web/test/acceptance/src/mocks/MockDocUpdaterApi.mjs b/services/web/test/acceptance/src/mocks/MockDocUpdaterApi.mjs index fd045a48c7..9ae253311e 100644 --- a/services/web/test/acceptance/src/mocks/MockDocUpdaterApi.mjs +++ b/services/web/test/acceptance/src/mocks/MockDocUpdaterApi.mjs @@ -3,6 +3,7 @@ import AbstractMockApi from './AbstractMockApi.mjs' class MockDocUpdaterApi extends AbstractMockApi { reset() { this.updates = {} + this.docsByProject = new Map() } getProjectStructureUpdates(projectId) { @@ -22,6 +23,15 @@ class MockDocUpdaterApi extends AbstractMockApi { this.updates[projectId].version = version } + setDoc(projectId, docId, lines, ranges) { + let docsById = this.docsByProject.get(projectId) + if (docsById == null) { + docsById = new Map() + this.docsByProject.set(projectId, docsById) + } + docsById.set(docId, { id: docId, lines, ranges }) + } + applyRoutes() { this.app.post('/project/:projectId/flush', (req, res) => { res.sendStatus(204) @@ -68,6 +78,17 @@ class MockDocUpdaterApi extends AbstractMockApi { this.app.post('/project/:projectId/history/resync', (req, res) => { res.sendStatus(204) }) + + this.app.get('/project/:projectId/ranges', (req, res) => { + const docsById = this.docsByProject.get(req.params.projectId) + const docs = docsById == null ? [] : Array.from(docsById.values()) + res.json({ + docs: docs.map(doc => ({ + id: doc.id, + ranges: doc.ranges, + })), + }) + }) } }