[web] Avoid double indexing of client side referencing (#28235)

* [web] Move chat client id to shared module

* [web] Avoid double indexing of client references

GitOrigin-RevId: 993930e66fdc9952649e3e8d345c70dd37516121
This commit is contained in:
Mathias Jakobsen
2025-09-05 11:02:33 +01:00
committed by Copybot
parent 644db1722e
commit 442c1952ab
7 changed files with 37 additions and 17 deletions

View File

@@ -84,6 +84,7 @@ async function createLinkedFile(req, res, next) {
async function refreshLinkedFile(req, res, next) {
const { project_id: projectId, file_id: fileId } = req.params
const { clientId } = req.body
const userId = SessionManager.getLoggedInUserId(req.session)
const { file, parentFolder } = await LinkedFilesHandler.promises.getFileById(
@@ -138,7 +139,8 @@ async function refreshLinkedFile(req, res, next) {
projectId,
'references:keys:updated',
data.keys,
true
true,
clientId
)
res.json({ new_file_id: newFileId })
} else {

View File

@@ -19,7 +19,7 @@ let ReferencesController
export default ReferencesController = {
indexAll(req, res, next) {
const projectId = req.params.Project_id
const { shouldBroadcast } = req.body
const { shouldBroadcast, clientId } = req.body
return ReferencesHandler.indexAll(projectId, function (error, data) {
if (error) {
OError.tag(error, 'failed to index references', { projectId })
@@ -31,12 +31,21 @@ export default ReferencesController = {
projectId,
shouldBroadcast,
true,
data
data,
clientId
)
})
},
_handleIndexResponse(req, res, projectId, shouldBroadcast, isAllDocs, data) {
_handleIndexResponse(
req,
res,
projectId,
shouldBroadcast,
isAllDocs,
data,
clientId
) {
if (data == null || data.keys == null) {
return res.json({ projectId, keys: [] })
}
@@ -45,7 +54,8 @@ export default ReferencesController = {
projectId,
'references:keys:updated',
data.keys,
isAllDocs
isAllDocs,
clientId
)
}
return res.json(data)

View File

@@ -8,8 +8,7 @@ import {
useRef,
FC,
} from 'react'
import { v4 as uuid } from 'uuid'
import clientIdGenerator from '@/utils/client-id'
import { useUserContext } from '../../../shared/context/user-context'
import { useProjectContext } from '../../../shared/context/project-context'
import { getJSON, postJSON } from '../../../infrastructure/fetch-json'
@@ -78,11 +77,6 @@ type Action =
error: any
}
// Wrap uuid in an object method so that it can be stubbed
export const chatClientIdGenerator = {
generate: () => uuid(),
}
let nextChatMessageId = 1
function generateChatMessageId() {
@@ -201,7 +195,7 @@ export const ChatProvider: FC<React.PropsWithChildren> = ({ children }) => {
const clientId = useRef<string>()
if (clientId.current === undefined) {
clientId.current = chatClientIdGenerator.generate()
clientId.current = clientIdGenerator.get()
}
const user = useUserContext()
const { projectId } = useProjectContext()

View File

@@ -14,6 +14,7 @@ import importOverleafModules from '../../../../macros/import-overleaf-module.mac
import OLButton from '@/shared/components/ol/ol-button'
import { sendMB } from '@/infrastructure/event-tracking'
import useIsMounted from '@/shared/hooks/use-is-mounted'
import clientId from '@/utils/client-id'
type FileViewRefreshButtonProps = {
setRefreshError: Dispatch<SetStateAction<Nullable<string>>>
@@ -42,6 +43,7 @@ export default function FileViewRefreshButton({
window.expectingLinkedFileRefreshedSocketFor = file.name
const body = {
shouldReindexReferences: isTPR || /\.bib$/.test(file.name),
clientId: clientId.get(),
}
postJSON(`/project/${projectId}/linked_file/${file.id}/refresh`, {
body,

View File

@@ -24,6 +24,7 @@ import { debugConsole } from '@/utils/debugging'
import { useFeatureFlag } from '@/shared/context/split-test-context'
import type { ReferenceIndexer } from '../references/reference-indexer'
import { AdvancedReferenceSearchResult } from '@/features/ide-react/references/types'
import clientId from '@/utils/client-id'
export const ReferencesContext = createContext<
| {
@@ -105,7 +106,7 @@ export const ReferencesProvider: FC<React.PropsWithChildren> = ({
if (shouldBroadcast) {
// Inform other clients about change in keys
await postJSON(`/project/${projectId}/references/indexAll`, {
body: { shouldBroadcast: true },
body: { shouldBroadcast: true, clientId: clientId.get() },
}).catch(error => {
// allow the request to fail
debugConsole.error(error)
@@ -176,8 +177,12 @@ export const ReferencesProvider: FC<React.PropsWithChildren> = ({
const handleProjectJoined = () => {
// We only need to grab the references when the editor first loads,
// not on every reconnect
socket.on('references:keys:updated', (keys, allDocs) => {
socket.on('references:keys:updated', (keys, allDocs, refresherId) => {
if (clientSideReferences) {
if (refresherId === clientId.get()) {
// We asked for this broadcast, so we must have already done the indexing
return
}
indexAllReferences(false)
} else {
setReferenceKeys(oldDocs =>

View File

@@ -0,0 +1,7 @@
import { v4 as uuid } from 'uuid'
// Wrap uuid in an object method so that it can be stubbed
const clientId = uuid()
export default {
get: () => clientId,
}

View File

@@ -5,9 +5,9 @@ import { renderHook, act, waitFor } from '@testing-library/react'
import { expect } from 'chai'
import sinon from 'sinon'
import fetchMock from 'fetch-mock'
import clientIdGenerator from '@/utils/client-id'
import {
useChatContext,
chatClientIdGenerator,
ServerMessageEntry,
} from '@/features/chat/context/chat-context'
import { stubMathJax, tearDownMathJaxStubs } from '../components/stubs'
@@ -35,7 +35,7 @@ describe('ChatContext', function () {
window.metaAttributesCache.set('ol-user', user)
window.metaAttributesCache.set('ol-preventCompileOnLoad', true)
this.stub = sinon.stub(chatClientIdGenerator, 'generate').returns(uuidValue)
this.stub = sinon.stub(clientIdGenerator, 'get').returns(uuidValue)
})
afterEach(function () {