Skip to content

Commit 6ffc0e3

Browse files
waleedlatif1claude
andcommitted
fix(connectors): blogpost labels and capped listing deletion reconciliation
- Confluence: fetchLabelsForPages now tries both /pages/{id}/labels and /blogposts/{id}/labels, preventing label loss when getDocument hydrates blogpost content (previously returned empty labels on 404). - Sync engine: skip deletion reconciliation when listing was capped (maxFiles/maxThreads). Connectors signal this via syncContext.listingCapped. Prevents incorrect deletion of docs beyond the cap that still exist in source. fullSync override still forces deletion for explicit cleanup. - Google Drive & Gmail: set syncContext.listingCapped = true when cap is hit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bc73154 commit 6ffc0e3

File tree

4 files changed

+29
-16
lines changed

4 files changed

+29
-16
lines changed

apps/sim/connectors/confluence/confluence.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,34 @@ async function fetchLabelsForPages(
3131
const results = await Promise.all(
3232
batch.map(async (pageId) => {
3333
try {
34-
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/pages/${pageId}/labels`
35-
const response = await fetchWithRetry(url, {
36-
method: 'GET',
37-
headers: {
38-
Accept: 'application/json',
39-
Authorization: `Bearer ${accessToken}`,
40-
},
41-
})
34+
let data: Record<string, unknown> | null = null
35+
for (const contentType of ['pages', 'blogposts']) {
36+
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/api/v2/${contentType}/${pageId}/labels`
37+
const response = await fetchWithRetry(url, {
38+
method: 'GET',
39+
headers: {
40+
Accept: 'application/json',
41+
Authorization: `Bearer ${accessToken}`,
42+
},
43+
})
44+
45+
if (response.ok) {
46+
data = await response.json()
47+
break
48+
}
49+
if (response.status !== 404) {
50+
logger.warn(`Failed to fetch labels for ${contentType} ${pageId}`, {
51+
status: response.status,
52+
})
53+
}
54+
}
4255

43-
if (!response.ok) {
44-
logger.warn(`Failed to fetch labels for page ${pageId}`, { status: response.status })
56+
if (!data) {
4557
return { pageId, labels: [] as string[] }
4658
}
4759

48-
const data = await response.json()
49-
const labels = (data.results || []).map(
50-
(label: Record<string, unknown>) => label.name as string
60+
const labels = ((data.results as Record<string, unknown>[]) || []).map(
61+
(label) => label.name as string
5162
)
5263
return { pageId, labels }
5364
} catch (error) {

apps/sim/connectors/gmail/gmail.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,7 @@ export const gmailConnector: ConnectorConfig = {
454454

455455
const nextPageToken = data.nextPageToken as string | undefined
456456
const hitLimit = newTotal >= maxThreads
457+
if (hitLimit && syncContext) syncContext.listingCapped = true
457458

458459
return {
459460
documents,

apps/sim/connectors/google-drive/google-drive.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ export const googleDriveConnector: ConnectorConfig = {
274274
const totalFetched = previouslyFetched + documents.length
275275
if (syncContext) syncContext.totalDocsFetched = totalFetched
276276
const hitLimit = maxFiles > 0 && totalFetched >= maxFiles
277+
if (hitLimit && syncContext) syncContext.listingCapped = true
277278

278279
const nextPageToken = data.nextPageToken as string | undefined
279280

apps/sim/lib/knowledge/connectors/sync-engine.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -554,9 +554,9 @@ export async function executeSync(
554554
}
555555
}
556556

557-
// Reconcile deletions for non-incremental syncs (which return ALL docs).
558-
// Skip for incremental syncs since results only contain changed docs.
559-
if (!isIncremental) {
557+
// Reconcile deletions for non-incremental syncs that returned ALL docs.
558+
// Skip when listing was capped (maxFiles/maxThreads) — unseen docs may still exist in the source.
559+
if (!isIncremental && (!syncContext?.listingCapped || options?.fullSync)) {
560560
const removedIds = existingDocs
561561
.filter((d) => d.externalId && !seenExternalIds.has(d.externalId))
562562
.map((d) => d.id)

0 commit comments

Comments
 (0)