Skip to content

Commit c5e65a0

Browse files
authored
fix(search): preserve source rows during approval updates (#7731)
1 parent 3c735d2 commit c5e65a0

2 files changed

Lines changed: 64 additions & 3 deletions

File tree

apps/sim/hooks/queries/utils/reset-organization-search-access.test.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,63 @@
11
/** @vitest-environment node */
2-
import { QueryClient } from '@tanstack/react-query'
2+
import { QueryClient, QueryObserver } from '@tanstack/react-query'
33
import { expect, it, vi } from 'vitest'
44
import type { WorkspaceKnowledgeSearchResult } from '@/lib/api/contracts/knowledge/search'
55
import { resourceScopeKey } from '@/lib/core/resource-scope'
66
import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys'
77
import { resetOrganizationSearchAccess } from '@/hooks/queries/utils/reset-organization-search-access'
8+
import { searchSourceKeys } from '@/hooks/queries/utils/search-source-keys'
9+
10+
it.each([true, false])(
11+
'keeps administrative rows visible while revalidating access, refresh success=%s',
12+
async (success) => {
13+
const client = new QueryClient({ defaultOptions: { queries: { retry: false } } })
14+
const scope = { kind: 'organization', organizationId: 'org-1' } as const
15+
const adminKey = searchSourceKeys.organizationOverview(scope.organizationId)
16+
const otherKey = searchSourceKeys.organizationOverview('org-2')
17+
const viewerKeys = [
18+
searchSourceKeys.list(scope),
19+
searchSourceKeys.overview(scope),
20+
searchSourceKeys.pages(scope, { search: '', mine: false }),
21+
]
22+
const before = { providers: [{ connectorType: 'gmail', approved: true }] }
23+
const after = { providers: [{ connectorType: 'gmail', approved: false }] }
24+
const response = Promise.withResolvers<typeof before>()
25+
const fetchOverview = vi.fn(() => response.promise)
26+
client.setQueryData(adminKey, before)
27+
client.setQueryData(otherKey, before)
28+
for (const key of viewerKeys) client.setQueryData(key, { privateContent: 'previous access' })
29+
const observer = new QueryObserver(client, {
30+
queryKey: adminKey,
31+
queryFn: fetchOverview,
32+
staleTime: Number.POSITIVE_INFINITY,
33+
})
34+
const observed = vi.fn()
35+
const unsubscribe = observer.subscribe(observed)
36+
try {
37+
const refreshing = resetOrganizationSearchAccess(client, scope.organizationId)
38+
expect(fetchOverview).toHaveBeenCalledOnce()
39+
expect(observer.getCurrentResult()).toMatchObject({ data: before, isPending: false })
40+
for (const key of viewerKeys) expect(client.getQueryData(key)).toBeUndefined()
41+
expect(client.getQueryState(otherKey)?.isInvalidated).toBe(false)
42+
43+
if (success) response.resolve(after)
44+
else response.reject(new Error('Could not refresh sources'))
45+
await refreshing
46+
47+
expect(observer.getCurrentResult()).toMatchObject({
48+
data: success ? after : before,
49+
isError: !success,
50+
isFetching: false,
51+
})
52+
expect(observed.mock.calls.every(([result]) => result.data && !result.isPending)).toBe(true)
53+
expect(client.getQueryData(otherKey)).toEqual(before)
54+
} finally {
55+
response.resolve(after)
56+
unsubscribe()
57+
client.clear()
58+
}
59+
}
60+
)
861

962
it.each([
1063
{ name: 'document', key: knowledgeKeys.document('kb-direct', 'document-direct') },

apps/sim/hooks/queries/utils/reset-organization-search-access.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { QueryClient } from '@tanstack/react-query'
1+
import { matchQuery, type QueryClient } from '@tanstack/react-query'
22
import { resourceScopeKey } from '@/lib/core/resource-scope'
33
import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys'
44
import { searchSourceKeys } from '@/hooks/queries/utils/search-source-keys'
@@ -9,12 +9,20 @@ export async function resetOrganizationSearchAccess(
99
organizationId: string
1010
) {
1111
const scope = { kind: 'organization', organizationId } as const
12+
const adminOverview = {
13+
queryKey: searchSourceKeys.organizationOverview(organizationId),
14+
exact: true,
15+
}
1216
await Promise.all([
1317
queryClient.resetQueries({
1418
queryKey: [...knowledgeKeys.searches(), resourceScopeKey(scope)],
1519
}),
1620
/** Document keys carry no resource scope and may exist without source or result caches. */
1721
queryClient.resetQueries({ queryKey: knowledgeKeys.details() }),
18-
queryClient.resetQueries({ queryKey: searchSourceKeys.list(scope) }),
22+
queryClient.resetQueries({
23+
queryKey: searchSourceKeys.list(scope),
24+
predicate: (query) => !matchQuery(adminOverview, query),
25+
}),
26+
queryClient.invalidateQueries(adminOverview),
1927
])
2028
}

0 commit comments

Comments
 (0)