Skip to content

Commit 60d2b80

Browse files
committed
refactor(connectors): tidy rehydrate flag + gate Full resync to supported connectors
Cleanup/simplify pass over the connector changes: - use shared booleanQueryFlagSchema for the rehydrate query param (typed boolean at the boundary instead of a hand-rolled 'true'/'false' string enum) - move rehydrateOnFullSync onto the client-safe ConnectorMeta so the UI can gate on it - only Confluence (rehydrateOnFullSync) shows the Sync now / Full resync dropdown; every other connector keeps its original one-click sync button (Full resync is a no-op for them, and this restores the pre-change one-click UX) - wrap the sync trigger in a span so its tooltip still shows while disabled (cooldown)
1 parent fe48d74 commit 60d2b80

6 files changed

Lines changed: 62 additions & 32 deletions

File tree

apps/sim/app/api/knowledge/[id]/connectors/[connectorId]/sync/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const POST = withRouteHandler(async (request: NextRequest, context: Route
2929
const parsed = await parseRequest(triggerKnowledgeConnectorSyncContract, request, context)
3030
if (!parsed.success) return parsed.response
3131
const { id: knowledgeBaseId, connectorId } = parsed.data.params
32-
const rehydrate = parsed.data.query?.rehydrate === 'true'
32+
const { rehydrate } = parsed.data.query
3333

3434
try {
3535
const auth = await checkSessionOrInternalAuth(request, { requireWorkflowId: false })

apps/sim/app/workspace/[workspaceId]/knowledge/[id]/components/connectors-section/connectors-section.tsx

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,14 @@ function ConnectorCard({
339339
)
340340
const syncLogs = detail?.syncLogs ?? []
341341

342+
const canFullResync = Boolean(connectorDef?.rehydrateOnFullSync)
343+
const syncDisabled =
344+
connector.status === 'syncing' ||
345+
connector.status === 'disabled' ||
346+
isSyncPending ||
347+
syncCooldown
348+
const syncTooltip = syncCooldown ? 'Sync recently triggered' : canFullResync ? 'Sync' : 'Sync now'
349+
342350
return (
343351
<div
344352
className={cn(
@@ -421,20 +429,47 @@ function ConnectorCard({
421429
<div className='flex flex-shrink-0 items-center gap-0.5'>
422430
{canEdit && (
423431
<>
424-
<DropdownMenu>
432+
{canFullResync ? (
433+
<DropdownMenu>
434+
<Tooltip.Root>
435+
<Tooltip.Trigger asChild>
436+
{/* span keeps the tooltip hoverable while the trigger button is disabled */}
437+
<span className='inline-flex'>
438+
<DropdownMenuTrigger asChild>
439+
<Button
440+
variant='ghost'
441+
aria-label='Sync options'
442+
className={CONNECTOR_ACTION_BUTTON_CLASSES}
443+
disabled={syncDisabled}
444+
>
445+
<RefreshCw
446+
className={cn(
447+
'size-3.5',
448+
connector.status === 'syncing' && 'animate-spin'
449+
)}
450+
/>
451+
</Button>
452+
</DropdownMenuTrigger>
453+
</span>
454+
</Tooltip.Trigger>
455+
<Tooltip.Content>{syncTooltip}</Tooltip.Content>
456+
</Tooltip.Root>
457+
<DropdownMenuContent align='end'>
458+
<DropdownMenuItem onSelect={() => onSync(false)}>Sync now</DropdownMenuItem>
459+
<DropdownMenuItem onSelect={() => onSync(true)}>Full resync</DropdownMenuItem>
460+
</DropdownMenuContent>
461+
</DropdownMenu>
462+
) : (
425463
<Tooltip.Root>
426464
<Tooltip.Trigger asChild>
427-
<DropdownMenuTrigger asChild>
465+
{/* span keeps the tooltip hoverable while the button is disabled */}
466+
<span className='inline-flex'>
428467
<Button
429468
variant='ghost'
430-
aria-label='Sync options'
469+
aria-label='Sync now'
431470
className={CONNECTOR_ACTION_BUTTON_CLASSES}
432-
disabled={
433-
connector.status === 'syncing' ||
434-
connector.status === 'disabled' ||
435-
isSyncPending ||
436-
syncCooldown
437-
}
471+
disabled={syncDisabled}
472+
onClick={() => onSync(false)}
438473
>
439474
<RefreshCw
440475
className={cn(
@@ -443,17 +478,11 @@ function ConnectorCard({
443478
)}
444479
/>
445480
</Button>
446-
</DropdownMenuTrigger>
481+
</span>
447482
</Tooltip.Trigger>
448-
<Tooltip.Content>
449-
{syncCooldown ? 'Sync recently triggered' : 'Sync'}
450-
</Tooltip.Content>
483+
<Tooltip.Content>{syncTooltip}</Tooltip.Content>
451484
</Tooltip.Root>
452-
<DropdownMenuContent align='end'>
453-
<DropdownMenuItem onSelect={() => onSync(false)}>Sync now</DropdownMenuItem>
454-
<DropdownMenuItem onSelect={() => onSync(true)}>Full resync</DropdownMenuItem>
455-
</DropdownMenuContent>
456-
</DropdownMenu>
485+
)}
457486

458487
<Tooltip.Root>
459488
<Tooltip.Trigger asChild>

apps/sim/connectors/confluence/confluence.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,6 @@ function cqlResultToStub(item: Record<string, unknown>, domain: string): Externa
156156
export const confluenceConnector: ConnectorConfig = {
157157
...confluenceConnectorMeta,
158158

159-
/**
160-
* Confluence pages can transclude other pages (Include Page / Excerpt macros).
161-
* Editing an included page changes a container page's rendered `view` without
162-
* bumping the container's version, so its version-based hash can't detect the
163-
* change. A full resync re-hydrates and re-indexes to pick up that drift.
164-
*/
165-
rehydrateOnFullSync: true,
166-
167159
listDocuments: async (
168160
accessToken: string,
169161
sourceConfig: Record<string, unknown>,

apps/sim/connectors/confluence/meta.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@ export const confluenceConnectorMeta: ConnectorMeta = {
2222
],
2323
},
2424

25+
/**
26+
* Confluence pages can transclude other pages (Include Page / Excerpt macros).
27+
* Editing an included page changes a container page's rendered `view` without
28+
* bumping the container's version, so its version-based hash can't detect the
29+
* change. A full resync re-hydrates and re-indexes to pick up that drift. This
30+
* lives on the meta so the client can offer "Full resync" only where it applies.
31+
*/
32+
rehydrateOnFullSync: true,
33+
2534
configFields: [
2635
{
2736
id: 'domain',

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ async function triggerSync({
215215
}: TriggerSyncParams): Promise<void> {
216216
await requestJson(triggerKnowledgeConnectorSyncContract, {
217217
params: { id: knowledgeBaseId, connectorId },
218-
query: rehydrate ? { rehydrate: 'true' } : {},
218+
query: rehydrate ? { rehydrate: true } : {},
219219
})
220220
}
221221

apps/sim/lib/api/contracts/knowledge/connectors.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
knowledgeConnectorParamsSchema,
55
successResponseSchema,
66
} from '@/lib/api/contracts/knowledge/shared'
7+
import { booleanQueryFlagSchema } from '@/lib/api/contracts/primitives'
78
import { defineRouteContract } from '@/lib/api/contracts/types'
89

910
export const createConnectorBodySchema = z.object({
@@ -155,11 +156,10 @@ export const triggerKnowledgeConnectorSyncQuerySchema = z.object({
155156
* Force re-hydration: for connectors whose rendered content can drift without a
156157
* hash change (e.g. Confluence transclusions), re-fetch and re-index every
157158
* already-synced document rather than only hash-changed ones. Listing and
158-
* deletion reconciliation are unchanged from a normal sync. Omitted performs the
159-
* normal hash-gated sync. Modeled as a string literal (not a boolean) so it
160-
* round-trips cleanly through the query string.
159+
* deletion reconciliation are unchanged from a normal sync. Defaults to the
160+
* normal hash-gated sync.
161161
*/
162-
rehydrate: z.enum(['true', 'false']).optional(),
162+
rehydrate: booleanQueryFlagSchema.optional().default(false),
163163
})
164164

165165
export const triggerKnowledgeConnectorSyncContract = defineRouteContract({

0 commit comments

Comments
 (0)