diff --git a/apps/sim/hooks/queries/utils/reset-organization-search-access.test.ts b/apps/sim/hooks/queries/utils/reset-organization-search-access.test.ts index 8e6a96e4a35..7040c040f72 100644 --- a/apps/sim/hooks/queries/utils/reset-organization-search-access.test.ts +++ b/apps/sim/hooks/queries/utils/reset-organization-search-access.test.ts @@ -1,10 +1,63 @@ /** @vitest-environment node */ -import { QueryClient } from '@tanstack/react-query' +import { QueryClient, QueryObserver } from '@tanstack/react-query' import { expect, it, vi } from 'vitest' import type { WorkspaceKnowledgeSearchResult } from '@/lib/api/contracts/knowledge/search' import { resourceScopeKey } from '@/lib/core/resource-scope' import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys' import { resetOrganizationSearchAccess } from '@/hooks/queries/utils/reset-organization-search-access' +import { searchSourceKeys } from '@/hooks/queries/utils/search-source-keys' + +it.each([true, false])( + 'keeps administrative rows visible while revalidating access, refresh success=%s', + async (success) => { + const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }) + const scope = { kind: 'organization', organizationId: 'org-1' } as const + const adminKey = searchSourceKeys.organizationOverview(scope.organizationId) + const otherKey = searchSourceKeys.organizationOverview('org-2') + const viewerKeys = [ + searchSourceKeys.list(scope), + searchSourceKeys.overview(scope), + searchSourceKeys.pages(scope, { search: '', mine: false }), + ] + const before = { providers: [{ connectorType: 'gmail', approved: true }] } + const after = { providers: [{ connectorType: 'gmail', approved: false }] } + const response = Promise.withResolvers() + const fetchOverview = vi.fn(() => response.promise) + client.setQueryData(adminKey, before) + client.setQueryData(otherKey, before) + for (const key of viewerKeys) client.setQueryData(key, { privateContent: 'previous access' }) + const observer = new QueryObserver(client, { + queryKey: adminKey, + queryFn: fetchOverview, + staleTime: Number.POSITIVE_INFINITY, + }) + const observed = vi.fn() + const unsubscribe = observer.subscribe(observed) + try { + const refreshing = resetOrganizationSearchAccess(client, scope.organizationId) + expect(fetchOverview).toHaveBeenCalledOnce() + expect(observer.getCurrentResult()).toMatchObject({ data: before, isPending: false }) + for (const key of viewerKeys) expect(client.getQueryData(key)).toBeUndefined() + expect(client.getQueryState(otherKey)?.isInvalidated).toBe(false) + + if (success) response.resolve(after) + else response.reject(new Error('Could not refresh sources')) + await refreshing + + expect(observer.getCurrentResult()).toMatchObject({ + data: success ? after : before, + isError: !success, + isFetching: false, + }) + expect(observed.mock.calls.every(([result]) => result.data && !result.isPending)).toBe(true) + expect(client.getQueryData(otherKey)).toEqual(before) + } finally { + response.resolve(after) + unsubscribe() + client.clear() + } + } +) it.each([ { name: 'document', key: knowledgeKeys.document('kb-direct', 'document-direct') }, diff --git a/apps/sim/hooks/queries/utils/reset-organization-search-access.ts b/apps/sim/hooks/queries/utils/reset-organization-search-access.ts index 00a2c91cb97..53f7b3a5cfb 100644 --- a/apps/sim/hooks/queries/utils/reset-organization-search-access.ts +++ b/apps/sim/hooks/queries/utils/reset-organization-search-access.ts @@ -1,4 +1,4 @@ -import type { QueryClient } from '@tanstack/react-query' +import { matchQuery, type QueryClient } from '@tanstack/react-query' import { resourceScopeKey } from '@/lib/core/resource-scope' import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys' import { searchSourceKeys } from '@/hooks/queries/utils/search-source-keys' @@ -9,12 +9,20 @@ export async function resetOrganizationSearchAccess( organizationId: string ) { const scope = { kind: 'organization', organizationId } as const + const adminOverview = { + queryKey: searchSourceKeys.organizationOverview(organizationId), + exact: true, + } await Promise.all([ queryClient.resetQueries({ queryKey: [...knowledgeKeys.searches(), resourceScopeKey(scope)], }), /** Document keys carry no resource scope and may exist without source or result caches. */ queryClient.resetQueries({ queryKey: knowledgeKeys.details() }), - queryClient.resetQueries({ queryKey: searchSourceKeys.list(scope) }), + queryClient.resetQueries({ + queryKey: searchSourceKeys.list(scope), + predicate: (query) => !matchQuery(adminOverview, query), + }), + queryClient.invalidateQueries(adminOverview), ]) }