Skip to content

Commit 0b5e01b

Browse files
committed
fix(search): reconcile sync progress and finish source guides
1 parent e41a1c9 commit 0b5e01b

16 files changed

Lines changed: 190 additions & 100 deletions

File tree

‎apps/docs/content/docs/search/confluence.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,6 @@ Configure one shared Confluence OAuth integration for your deployment. This powe
196196
2. Under **Authorization → OAuth 2.0 (3LO)**, add `https://<your-sim-domain>/api/auth/oauth2/callback/confluence` to **Callback URLs**, keep existing callbacks used by the deployment, and save.
197197
3. Under **Permissions**, add the Confluence API and configure the full `confluence` scope list for your release in [Sim's OAuth configuration](https://github.com/simstudioai/sim/blob/staging/apps/sim/lib/oauth/oauth.ts), including `read:group:confluence`. Also add **User Identity API** with `read:me`. Sim requests `offline_access` for refresh tokens. The service-account read scopes above do not replace the broader shared OAuth scope set.
198198
4. Enable sharing under **Distribution**. Set `CONFLUENCE_CLIENT_ID` and `CONFLUENCE_CLIENT_SECRET` from the app's **Settings**, verify `NEXT_PUBLIC_APP_URL`, and restart Sim.
199-
5. Start authorization from Search and select the configured site. Reconnect old accounts after adding scopes so the new permission grant takes effect.
199+
5. Start authorization from **Integrations** and select the configured site. Reconnect old accounts after adding scopes so the new permission grant takes effect.
200200

201201
A callback mismatch needs a corrected callback URL; a connection that works only for the app owner needs sharing enabled. See Atlassian's [OAuth configuration guide](https://developer.atlassian.com/cloud/confluence/oauth-2-3lo-apps/) and Sim's [deployment reference](/platform/self-hosting/integrations-oauth).

‎apps/docs/content/docs/search/connect-your-account.mdx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ Use **Add source** beside **Add another [provider] source** when you need anothe
3535

3636
In the new tab, select **Connect** and complete the provider's authorization. Choose the account associated with your verified Sim email. The provider may require your organization's SSO or app approval.
3737

38-
Return to Integrations when the connection completes, or use **Return to Search** on the connection page. If the popup was blocked or closed, allow popups and select **Connect account** again. While authorization is pending, use **Open again**.
38+
Return to Integrations when the connection completes, or select **Return to Search** to open your organization’s Search page. Your account is saved when authorization completes; there is no separate submit step. If the popup was blocked or closed, allow popups and select **Connect account** again. While authorization is pending, use **Open again**.
39+
40+
<Image src="/static/search/member-enrollment.jpg" alt="Gmail account connection with Connect and Return to Search actions" width={680} height={305} />
3941

4042
</Step>
4143
<Step>

‎apps/docs/content/docs/search/index.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ Open **Settings → Sources → [integration]**, then select a source. Providers
9595
| Source tab | What you can do |
9696
| --- | --- |
9797
| **Documents** | Find indexed documents, inspect processing status, retry failed indexing, or exclude and restore documents. |
98-
| **Settings** | Edit the source's scope, filters, metadata, and supported connection settings. Save your changes before leaving. |
98+
| **Settings** | Edit the source's scope, filters, and supported connection settings. Save your changes before leaving. |
9999
| **Sync history** | Review recent sync runs and errors. |
100100

101101
Use the source header to sync, pause, resume, or remove that source. The back link returns to its integration. **Deactivate** on the integration page makes its content unavailable in Search, Assistant, and MCP while preserving its sources and connected accounts; **Activate** enables it again.

‎apps/docs/content/docs/search/jira.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,6 @@ The deployment operator configures one shared Jira OAuth integration. Teammates
138138

139139
3. Under **Permissions**, add **Jira API**, then **Configure** its classic and granular scopes for Jira, Jira Service Management, and Assets. Separately add **User Identity API** with `read:me`. Sim requests `offline_access` in the authorization URL for refresh tokens. Configure the full `jira` scope list for your release in [Sim's OAuth configuration](https://github.com/simstudioai/sim/blob/staging/apps/sim/lib/oauth/oauth.ts); the Search read scopes above are only a subset of this shared integration's permissions.
140140
4. Under **Distribution**, enable sharing so teammates can authorize the app. Copy the client ID and secret from **Settings** into `JIRA_CLIENT_ID` and `JIRA_CLIENT_SECRET`, set the correct `NEXT_PUBLIC_APP_URL`, and restart Sim.
141-
5. Start a connection from Search. Confirm that Atlassian lists the intended site, then return to Sim. After changing requested scopes, reconnect previously authorized accounts.
141+
5. Start a connection from **Integrations**. Confirm that Atlassian lists the intended site, then return to Sim. After changing requested scopes, reconnect previously authorized accounts.
142142

143-
For a local instance using `NEXT_PUBLIC_APP_URL=http://localhost:3000`, register `http://localhost:3000/api/auth/oauth2/callback/jira`. Use a separate development OAuth app when production callbacks must remain unchanged. After updating local client credentials or the app URL, restart Sim and begin a new connection from Search. If only the app owner can connect, check **Distribution**. See Atlassian's [OAuth configuration and sharing guide](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/) and Sim's [deployment reference](/platform/self-hosting/integrations-oauth).
143+
For a local instance using `NEXT_PUBLIC_APP_URL=http://localhost:3000`, register `http://localhost:3000/api/auth/oauth2/callback/jira`. Use a separate development OAuth app when production callbacks must remain unchanged. After updating local client credentials or the app URL, restart Sim and begin a new connection from **Integrations**. If only the app owner can connect, check **Distribution**. See Atlassian's [OAuth configuration and sharing guide](https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/) and Sim's [deployment reference](/platform/self-hosting/integrations-oauth).
13.5 KB
Loading

‎apps/sim/app/api/knowledge/sim-search/sources/route.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ describe('organization administration overview boundary', () => {
266266
sourceCount: 1,
267267
approved: true,
268268
status: 'waiting_for_connections',
269+
isSyncing: false,
269270
}
270271
mocks.adminOverview.mockResolvedValue({
271272
providers: [{ ...provider, privateAccount: 'private' }],

‎apps/sim/app/o/[organizationId]/settings/components/integrations/organization-integrations-setup.test.tsx‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,13 @@ import { SettingsHeaderProvider, SettingsHeaderShell } from '@/components/settin
4848
import { OrganizationIntegrationsSetup } from '@/app/o/[organizationId]/settings/components/integrations/organization-integrations-setup'
4949

5050
const providers: OrganizationSearchProviderSummary[] = [
51-
{ connectorType: 'gmail', approved: true, sourceCount: 0, status: 'waiting_for_connections' },
51+
{
52+
connectorType: 'gmail',
53+
approved: true,
54+
sourceCount: 0,
55+
status: 'waiting_for_connections',
56+
isSyncing: false,
57+
},
5258
{ connectorType: 'google_drive', approved: false, sourceCount: 2, status: 'paused' },
5359
]
5460
let root: Root

‎apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/edit-connector-modal/use-connector-settings-form.ts‎

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -294,24 +294,13 @@ export function useConnectorSettingsForm({
294294
[connector.sourceConfig]
295295
)
296296

297-
const hasChanges = useMemo(() => {
298-
if (syncInterval !== connector.syncIntervalMinutes) return true
299-
if (didCanonicalModesChange(canonicalModes, persistedCanonicalModes)) return true
300-
const resolved = resolveSourceConfig()
301-
for (const [key, value] of Object.entries(resolved)) {
302-
if (hiddenCapFieldIds.has(key)) continue
303-
if (!valuesEqual(connector.sourceConfig[key], value)) return true
304-
}
305-
return false
306-
}, [
307-
resolveSourceConfig,
308-
syncInterval,
309-
connector.syncIntervalMinutes,
310-
connector.sourceConfig,
311-
canonicalModes,
312-
persistedCanonicalModes,
313-
hiddenCapFieldIds,
314-
])
297+
const hasChanges =
298+
syncInterval !== connector.syncIntervalMinutes ||
299+
didCanonicalModesChange(canonicalModes, persistedCanonicalModes) ||
300+
Object.entries(resolveSourceConfig()).some(
301+
([key, value]) =>
302+
!hiddenCapFieldIds.has(key) && !valuesEqual(connector.sourceConfig[key], value)
303+
)
315304

316305
const handleSave = useCallback(() => {
317306
if (!searchSettingsAllowed || !settingsComplete || accessDirty) return

‎apps/sim/hooks/queries/kb/connectors.test.ts‎

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ describe('useTriggerSync optimistic state', () => {
211211
function capturedMutationOptions() {
212212
return mocks.useMutation.mock.calls.at(-1)?.[0] as {
213213
onMutate: (vars: { knowledgeBaseId: string; connectorId: string }) => Promise<unknown>
214+
onSettled: () => Promise<unknown>
214215
onSuccess: (data: undefined, vars: { knowledgeBaseId: string; connectorId: string }) => void
215216
onError: (
216217
error: unknown,
@@ -294,37 +295,10 @@ describe('useTriggerSync optimistic state', () => {
294295
expect(rolledBack?.find((c) => c.id === 'connector-2')?.status).toBe('pending')
295296
})
296297

297-
/**
298-
* The Search surface reads the member sync status from the workspace
299-
* member-connector list, which has no poll of its own, so a members-mode
300-
* trigger patches that cache too and a refused trigger refetches it.
301-
*/
302-
it('patches all loaded source pages without changing cursors or overview cache data', async () => {
303-
mocks.getQueryData.mockReturnValue([makeConnector({ status: 'active' })])
298+
it('reconciles server source summaries after either sync outcome', async () => {
304299
useTriggerSync()
305-
await capturedMutationOptions().onMutate({ knowledgeBaseId: KB_ID, connectorId: 'connector-1' })
306-
const [filter, patch] = mocks.setQueriesData.mock.calls.find(
307-
([filter]) => filter.queryKey[0] === 'search-sources'
308-
)!
309-
const overviewKey = searchSourceKeys.overview('workspace-a')
310-
const pagesKey = searchSourceKeys.pages('workspace-a', { search: '', mine: false })
311-
expect(filter.predicate({ queryKey: overviewKey })).toBe(false)
312-
expect(filter.predicate({ queryKey: pagesKey })).toBe(true)
313-
const cached = {
314-
pageParams: [null, 'cursor'],
315-
pages: [
316-
{ sources: [{ connectorId: 'other', isSyncing: false }], nextCursor: 'cursor' },
317-
{ sources: [{ connectorId: 'connector-1', isSyncing: false }], nextCursor: null },
318-
],
319-
}
320-
expect(patch(cached)).toEqual({
321-
...cached,
322-
pages: [
323-
cached.pages[0],
324-
{ ...cached.pages[1], sources: [{ connectorId: 'connector-1', isSyncing: true }] },
325-
],
326-
})
327-
expect(patch(undefined)).toBeUndefined()
300+
await capturedMutationOptions().onSettled()
301+
expect(mocks.invalidateQueries).toHaveBeenCalledWith({ queryKey: searchSourceKeys.lists() })
328302
})
329303

330304
it('queues a members connector in the workspace member-connector list as well', async () => {

‎apps/sim/hooks/queries/kb/connectors.ts‎

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { useEffect } from 'react'
22
import {
3-
type InfiniteData,
43
keepPreviousData,
54
type QueryClient,
65
useInfiniteQuery,
@@ -521,7 +520,7 @@ export function useOrganizationSearchOverview(
521520
enabled: Boolean(organizationId) && (options?.enabled ?? true),
522521
staleTime: CONNECTOR_LIST_STALE_TIME,
523522
refetchInterval: (query) =>
524-
query.state.data?.providers.some((provider) => provider.status === 'indexing')
523+
query.state.data?.providers.some((provider) => provider.isSyncing)
525524
? SEARCH_SOURCE_SUMMARY_POLL_MS
526525
: false,
527526
})
@@ -807,31 +806,14 @@ export function useTriggerSync() {
807806
await Promise.all([
808807
queryClient.cancelQueries({ queryKey: connectorKeys.all(knowledgeBaseId) }),
809808
queryClient.cancelQueries({ queryKey: memberConnectorKeys.lists() }),
810-
queryClient.cancelQueries({ queryKey: searchSourceKeys.lists() }),
811809
])
812-
queryClient.setQueriesData<InfiniteData<SearchSourcePage>>(
813-
{ queryKey: searchSourceKeys.lists(), predicate: (query) => query.queryKey[3] === 'pages' },
814-
(data) =>
815-
data
816-
? {
817-
...data,
818-
pages: data.pages.map((page) => ({
819-
...page,
820-
sources: page.sources.map((source) =>
821-
source.connectorId === connectorId ? { ...source, isSyncing: true } : source
822-
),
823-
})),
824-
}
825-
: data
826-
)
827810
return optimisticallyQueueSync(queryClient, knowledgeBaseId, connectorId)
828811
},
829812
/**
830813
* Rolling back also stops the poll the optimistic `pending` started, so a
831814
* refused sync does not leave the row spinning.
832815
*/
833816
onError: (_error, { knowledgeBaseId, connectorId }, previous) => {
834-
queryClient.invalidateQueries({ queryKey: searchSourceKeys.lists() })
835817
queryClient.invalidateQueries({ queryKey: searchIntegrationKeys.lists() })
836818
if (previous) {
837819
setCachedConnectorStatus(queryClient, knowledgeBaseId, connectorId, previous)
@@ -852,6 +834,7 @@ export function useTriggerSync() {
852834
queryKey: connectorKeys.progresses(knowledgeBaseId, connectorId),
853835
})
854836
},
837+
onSettled: () => queryClient.invalidateQueries({ queryKey: searchSourceKeys.lists() }),
855838
})
856839
}
857840

0 commit comments

Comments
 (0)