Skip to content

Commit 35a979c

Browse files
committed
fix(connectors): keep confluence CQL page size constant; unify monday API version
The CQL search endpoint paginates by opaque cursor, and Atlassian does not document that a cursor issued against one limit survives a request asking for a different one. Narrowing limit to the remaining budget was the same pattern reverted on airtable, asana, and ashby. The page size is now constant and the cap is applied by trimming the returned page, which keeps the cap exact without varying the request. Monday OAuth getUserInfo hardcoded API-Version 2024-10 while every other monday surface reads MONDAY_API_VERSION, defeating the single-source pin.
1 parent 5ce97d7 commit 35a979c

2 files changed

Lines changed: 32 additions & 8 deletions

File tree

apps/sim/connectors/confluence/confluence.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -708,12 +708,18 @@ async function listDocumentsViaCql(
708708
}
709709

710710
const fetchedSoFar = (syncContext?.totalDocsFetched as number) ?? 0
711-
const remaining = maxPages > 0 ? maxPages - fetchedSoFar : CQL_PAGE_SIZE
712-
const limit = Math.max(Math.min(CQL_PAGE_SIZE, remaining), 1)
711+
const remaining = maxPages > 0 ? maxPages - fetchedSoFar : Number.POSITIVE_INFINITY
713712

713+
/**
714+
* The page size stays constant for every request of a run. This endpoint
715+
* paginates by opaque cursor, and Atlassian does not document that a cursor
716+
* issued against one `limit` stays valid when the following request asks for a
717+
* different one, so narrowing `limit` to the remaining budget risks skipping or
718+
* repeating results. The cap is applied by trimming the returned page instead.
719+
*/
714720
const queryParams = new URLSearchParams()
715721
queryParams.append('cql', cql)
716-
queryParams.append('limit', String(limit))
722+
queryParams.append('limit', String(CQL_PAGE_SIZE))
717723
queryParams.append('expand', 'version,metadata.labels')
718724
/**
719725
* `/wiki/rest/api/content/search` paginates by opaque cursor only — it has no
@@ -724,7 +730,10 @@ async function listDocumentsViaCql(
724730

725731
const url = `https://api.atlassian.com/ex/confluence/${cloudId}/wiki/rest/api/content/search?${queryParams.toString()}`
726732

727-
logger.info(`Searching Confluence via CQL: ${cql}`, { limit, hasCursor: Boolean(cursor) })
733+
logger.info(`Searching Confluence via CQL: ${cql}`, {
734+
limit: CQL_PAGE_SIZE,
735+
hasCursor: Boolean(cursor),
736+
})
728737

729738
const response = await fetchWithRetry(url, {
730739
method: 'GET',
@@ -746,16 +755,30 @@ async function listDocumentsViaCql(
746755
const data = await response.json()
747756
const results = data.results || []
748757

749-
const documents: ExternalDocument[] = (results as Record<string, unknown>[])
758+
const allDocuments: ExternalDocument[] = (results as Record<string, unknown>[])
750759
.filter(isCurrentContent)
751760
.map((item) => cqlResultToStub(item, domain))
752761

762+
/**
763+
* Trim to the remaining budget. Trimming stops the walk (`hitLimit` below is
764+
* then true), so the discarded tail is never skipped over — the run simply
765+
* ends here.
766+
*/
767+
const documents =
768+
allDocuments.length > remaining ? allDocuments.slice(0, remaining) : allDocuments
769+
const trimmedByCap = documents.length < allDocuments.length
770+
753771
const nextCursor = extractCursor((data._links as Record<string, unknown> | undefined)?.next)
754772

755773
const totalFetched = fetchedSoFar + documents.length
756774
if (syncContext) syncContext.totalDocsFetched = totalFetched
757775
const hitLimit = maxPages > 0 && totalFetched >= maxPages
758-
if (hitLimit && nextCursor && syncContext) syncContext.listingCapped = true
776+
/**
777+
* Both truncation shapes count: pages this run trimmed off, and a page left
778+
* unread behind a live cursor. A cap that lands exactly on source exhaustion
779+
* is a complete listing and must still reconcile deletions.
780+
*/
781+
if (hitLimit && (trimmedByCap || nextCursor) && syncContext) syncContext.listingCapped = true
759782

760783
const hasMore = !hitLimit && Boolean(nextCursor)
761784

apps/sim/lib/auth/connectors/providers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { getDocusignOAuthUrl } from '@/lib/oauth/docusign'
1717
import { getMicrosoftUserInfoFromIdToken } from '@/lib/oauth/microsoft'
1818
import { SALESFORCE_LOGIN_HOSTS } from '@/lib/oauth/salesforce'
1919
import { getCanonicalScopesForProvider } from '@/lib/oauth/utils'
20+
import { MONDAY_API_URL, MONDAY_API_VERSION } from '@/tools/monday/utils'
2021
import { REDDIT_USER_AGENT } from '@/tools/reddit/constants'
2122
import { deriveZohoDeskBaseFromApiDomain } from '@/tools/zoho_desk/host-allowlist'
2223

@@ -1532,11 +1533,11 @@ export function buildConnectorProviders(): GenericOAuthConfig[] {
15321533
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/monday`,
15331534
getUserInfo: async (tokens) => {
15341535
try {
1535-
const response = await fetch('https://api.monday.com/v2', {
1536+
const response = await fetch(MONDAY_API_URL, {
15361537
method: 'POST',
15371538
headers: {
15381539
'Content-Type': 'application/json',
1539-
'API-Version': '2024-10',
1540+
'API-Version': MONDAY_API_VERSION,
15401541
Authorization: tokens.accessToken ?? '',
15411542
},
15421543
body: JSON.stringify({ query: '{ me { id name email } }' }),

0 commit comments

Comments
 (0)