Przeglądaj źródła

[web] let docstore determine user ids of tracked changes (#26333)

* [docstore] add endpoint for getting user ids of tracked changes

* [web] let docstore determine user ids of tracked changes

GitOrigin-RevId: 8d0a131555aa827f7ff80690fedc1aca26cf0817
Jakob Ackermann 1 rok temu
rodzic
commit
3862826589

+ 4 - 0
services/docstore/app.js

@@ -50,6 +50,10 @@ app.param('doc_id', function (req, res, next, docId) {
 app.get('/project/:project_id/doc-deleted', HttpController.getAllDeletedDocs)
 app.get('/project/:project_id/doc', HttpController.getAllDocs)
 app.get('/project/:project_id/ranges', HttpController.getAllRanges)
+app.get(
+  '/project/:project_id/tracked-changes-user-ids',
+  HttpController.getTrackedChangesUserIds
+)
 app.get('/project/:project_id/has-ranges', HttpController.projectHasRanges)
 app.get('/project/:project_id/doc/:doc_id', HttpController.getDoc)
 app.get('/project/:project_id/doc/:doc_id/deleted', HttpController.isDocDeleted)

+ 14 - 0
services/docstore/app/js/DocManager.js

@@ -132,6 +132,20 @@ const DocManager = {
     return docs
   },
 
+  async getTrackedChangesUserIds(projectId) {
+    const docs = await DocManager.getAllNonDeletedDocs(projectId, {
+      ranges: true,
+    })
+    const userIds = new Set()
+    for (const doc of docs) {
+      for (const change of doc.ranges?.changes || []) {
+        if (change.metadata.user_id === 'anonymous-user') continue
+        userIds.add(change.metadata.user_id)
+      }
+    }
+    return Array.from(userIds)
+  },
+
   async projectHasRanges(projectId) {
     const docs = await MongoManager.getProjectsDocs(projectId, {}, { _id: 1 })
     const docIds = docs.map(doc => doc._id)

+ 7 - 0
services/docstore/app/js/HttpController.js

@@ -83,6 +83,12 @@ async function getAllRanges(req, res) {
   res.json(_buildDocsArrayView(projectId, docs))
 }
 
+async function getTrackedChangesUserIds(req, res) {
+  const { project_id: projectId } = req.params
+  const userIds = await DocManager.getTrackedChangesUserIds(projectId)
+  res.json(userIds)
+}
+
 async function projectHasRanges(req, res) {
   const { project_id: projectId } = req.params
   const projectHasRanges = await DocManager.projectHasRanges(projectId)
@@ -232,6 +238,7 @@ module.exports = {
   getAllDocs: expressify(getAllDocs),
   getAllDeletedDocs: expressify(getAllDeletedDocs),
   getAllRanges: expressify(getAllRanges),
+  getTrackedChangesUserIds: expressify(getTrackedChangesUserIds),
   projectHasRanges: expressify(projectHasRanges),
   updateDoc: expressify(updateDoc),
   patchDoc: expressify(patchDoc),

+ 43 - 5
services/docstore/test/acceptance/js/GettingAllDocsTests.js

@@ -24,26 +24,51 @@ describe('Getting all docs', function () {
       {
         _id: new ObjectId(),
         lines: ['one', 'two', 'three'],
-        ranges: { mock: 'one' },
+        ranges: {
+          changes: [
+            {
+              id: new ObjectId().toString(),
+              metadata: { user_id: 'user-id-1' },
+            },
+          ],
+        },
         rev: 2,
       },
       {
         _id: new ObjectId(),
         lines: ['aaa', 'bbb', 'ccc'],
-        ranges: { mock: 'two' },
+        ranges: {
+          changes: [
+            {
+              id: new ObjectId().toString(),
+              metadata: { user_id: 'user-id-2' },
+            },
+          ],
+        },
         rev: 4,
       },
       {
         _id: new ObjectId(),
         lines: ['111', '222', '333'],
-        ranges: { mock: 'three' },
+        ranges: {
+          changes: [
+            {
+              id: new ObjectId().toString(),
+              metadata: { user_id: 'anonymous-user' },
+            },
+          ],
+        },
         rev: 6,
       },
     ]
     this.deleted_doc = {
       _id: new ObjectId(),
       lines: ['deleted'],
-      ranges: { mock: 'four' },
+      ranges: {
+        changes: [
+          { id: new ObjectId().toString(), metadata: { user_id: 'user-id-3' } },
+        ],
+      },
       rev: 8,
     }
     const version = 42
@@ -96,7 +121,7 @@ describe('Getting all docs', function () {
     })
   })
 
-  return it('getAllRanges should return all the (non-deleted) doc ranges', function (done) {
+  it('getAllRanges should return all the (non-deleted) doc ranges', function (done) {
     return DocstoreClient.getAllRanges(this.project_id, (error, res, docs) => {
       if (error != null) {
         throw error
@@ -109,4 +134,17 @@ describe('Getting all docs', function () {
       return done()
     })
   })
+
+  it('getTrackedChangesUserIds should return all the user ids from (non-deleted) ranges', function (done) {
+    DocstoreClient.getTrackedChangesUserIds(
+      this.project_id,
+      (error, res, userIds) => {
+        if (error != null) {
+          throw error
+        }
+        userIds.should.deep.equal(['user-id-1', 'user-id-2'])
+        done()
+      }
+    )
+  })
 })

+ 10 - 0
services/docstore/test/acceptance/js/helpers/DocstoreClient.js

@@ -100,6 +100,16 @@ module.exports = DocstoreClient = {
     )
   },
 
+  getTrackedChangesUserIds(projectId, callback) {
+    request.get(
+      {
+        url: `http://127.0.0.1:${settings.internal.docstore.port}/project/${projectId}/tracked-changes-user-ids`,
+        json: true,
+      },
+      callback
+    )
+  },
+
   updateDoc(projectId, docId, lines, version, ranges, callback) {
     return request.post(
       {

+ 12 - 1
services/web/app/src/Features/Docstore/DocstoreManager.js

@@ -1,10 +1,11 @@
 const { promisify } = require('util')
-const { promisifyMultiResult } = require('@overleaf/promise-utils')
+const { promisifyMultiResult, callbackify } = require('@overleaf/promise-utils')
 const request = require('request').defaults({ jar: false })
 const OError = require('@overleaf/o-error')
 const logger = require('@overleaf/logger')
 const settings = require('@overleaf/settings')
 const Errors = require('../Errors/Errors')
+const { fetchJson } = require('@overleaf/fetch-utils')
 
 const TIMEOUT = 30 * 1000 // request timeout
 
@@ -86,6 +87,14 @@ function getAllDeletedDocs(projectId, callback) {
   })
 }
 
+/**
+ * @param {string} projectId
+ */
+async function getTrackedChangesUserIds(projectId) {
+  const url = `${settings.apis.docstore.url}/project/${projectId}/tracked-changes-user-ids`
+  return fetchJson(url, { signal: AbortSignal.timeout(TIMEOUT) })
+}
+
 /**
  * @param {string} projectId
  * @param {Callback} callback
@@ -292,6 +301,7 @@ module.exports = {
   getAllDeletedDocs,
   getAllRanges,
   getDoc,
+  getTrackedChangesUserIds: callbackify(getTrackedChangesUserIds),
   isDocDeleted,
   updateDoc,
   projectHasRanges,
@@ -304,6 +314,7 @@ module.exports = {
     getAllDeletedDocs: promisify(getAllDeletedDocs),
     getAllRanges: promisify(getAllRanges),
     getDoc: promisifyMultiResult(getDoc, ['lines', 'rev', 'version', 'ranges']),
+    getTrackedChangesUserIds,
     isDocDeleted: promisify(isDocDeleted),
     updateDoc: promisifyMultiResult(updateDoc, ['modified', 'rev']),
     projectHasRanges: promisify(projectHasRanges),