Skip to content

Commit 0f09f82

Browse files
committed
fix(plane): preserve cursor page sizes in capped connector syncs
1 parent 86021c7 commit 0f09f82

3 files changed

Lines changed: 66 additions & 7 deletions

File tree

‎apps/sim/connectors/plane/plane.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ export const planeConnector: ConnectorConfig = {
111111
}
112112
const response = await requestPlane(token, config, resourcePath(config), {
113113
cursor,
114-
per_page: Math.min(PAGE_SIZE, remaining),
114+
// Plane v1 cursor offsets depend on per_page, so it must stay fixed across pages.
115+
per_page: PAGE_SIZE,
115116
fields: config.contentType === 'pages' ? PAGE_FIELDS : WORK_ITEM_FIELDS,
116117
...(config.contentType === 'work_items'
117118
? { expand: 'state,assignees,labels', order_by: 'created_at' }
@@ -131,7 +132,9 @@ export const planeConnector: ConnectorConfig = {
131132
const total = fetched + documents.length
132133
if (syncContext) syncContext.totalDocsFetched = total
133134
const capped =
134-
config.maxDocuments > 0 && total >= config.maxDocuments && page.nextCursor !== undefined
135+
config.maxDocuments > 0 &&
136+
total >= config.maxDocuments &&
137+
(page.nextCursor !== undefined || page.results.length > remaining)
135138
if (capped && syncContext) syncContext.listingCapped = true
136139
logger.info('Listed Plane documents', {
137140
count: documents.length,

‎apps/sim/scripts/test-plane-e2e.ts‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,55 @@ try {
983983
}
984984
})
985985
}
986+
await check(
987+
'v1: knowledge connector preserves distinct capped cursor continuations',
988+
async () => {
989+
const projectId = id(
990+
(
991+
await call('plane_create_project', 'v1', {
992+
name: 'Disposable cursor verification',
993+
identifier: `P${generateId().replaceAll('-', '').slice(0, 7).toUpperCase()}`,
994+
})
995+
).result
996+
)
997+
const itemIds: string[] = []
998+
for (let index = 0; index < 102; index++) {
999+
itemIds.push(
1000+
id(
1001+
(
1002+
await call('plane_create_work_item', 'v1', {
1003+
project_id: projectId,
1004+
name: `Cursor verification ${index}`,
1005+
})
1006+
).result
1007+
)
1008+
)
1009+
}
1010+
for (const maxDocuments of [101, 102]) {
1011+
const config = { workspaceSlug, projectId, baseUrl, maxDocuments }
1012+
const context: Record<string, unknown> = { totalDocsFetched: 0 }
1013+
const externalIds: string[] = []
1014+
let cursor: string | undefined
1015+
let hasMore = true
1016+
for (let page = 0; page < itemIds.length && hasMore; page++) {
1017+
const listing = await planeConnector.listDocuments(apiKey, config, cursor, context)
1018+
externalIds.push(...listing.documents.map((document) => document.externalId))
1019+
cursor = listing.nextCursor
1020+
hasMore = listing.hasMore
1021+
if (hasMore) assert(cursor, 'A continuing listing must provide its next cursor')
1022+
}
1023+
assert.equal(hasMore, false)
1024+
assert.equal(cursor, undefined)
1025+
assert.deepEqual(
1026+
externalIds,
1027+
itemIds.slice(0, maxDocuments).map((itemId) => `work_item:${itemId}`)
1028+
)
1029+
assert.equal(context.totalDocsFetched, maxDocuments)
1030+
if (maxDocuments < itemIds.length) assert.equal(context.listingCapped, true)
1031+
else assert.notEqual(context.listingCapped, true)
1032+
}
1033+
}
1034+
)
9861035
if (!publicCallback)
9871036
checks.push({
9881037
name: 'Signed public webhook delivery',

‎apps/sim/tools/plane/coverage.json‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@
779779
"checked": 102,
780780
"failures": 0
781781
},
782-
"liveVerification": "The recorded Plane Cloud validation covers v2 and v1 compatibility and a real signed webhook delivery. These Cloud checks precede the connector cap fix and have not been rerun for this follow-up. Official Community Edition v1.4.2 was also tested over real HTTP with production Sim actions and the knowledge connector, reverse-proxy paths, signed attachment storage, and actual Celery webhook deliveries through the production signature and event handler.",
782+
"liveVerification": "The recorded Plane Cloud validation covers v2 and v1 compatibility and a real signed webhook delivery. These Cloud checks precede the connector cap fixes and have not been rerun for this follow-up. Official Community Edition v1.4.2 was also tested over real HTTP with production Sim actions and the knowledge connector, reverse-proxy paths, signed attachment storage, actual Celery webhook deliveries, and capped cursor continuations without duplicate source records.",
783783
"liveResponses": {
784784
"checked": 145,
785785
"failures": 0
@@ -795,16 +795,23 @@
795795
"communityChecks": {
796796
"release": "v1.4.2",
797797
"reference": "https://github.com/makeplane/plane/releases/tag/v1.4.2",
798-
"passed": 21,
798+
"passed": 23,
799799
"failed": 0,
800800
"skipped": 1,
801-
"requests": 76,
801+
"requests": 180,
802802
"successfulV1Operations": 51,
803803
"additionalReverseProxyChecksPassed": 8,
804-
"verifiedSignedDeliveries": 63,
804+
"verifiedSignedDeliveries": 354,
805805
"webhookProcessingErrors": 0,
806806
"cleanupVerified": true,
807-
"boundary": "Production action transport and knowledge connector over real provider HTTP; actual provider webhook deliveries through the production handler. Full Sim route/workflow execution is covered separately by synthetic HTTP and database integration checks."
807+
"boundary": "Production action transport and knowledge connector over real provider HTTP; actual provider webhook deliveries through the production handler. Full Sim route/workflow execution is covered separately by synthetic HTTP and database integration checks.",
808+
"cursorPagination": {
809+
"sourceWorkItems": 102,
810+
"verifiedCaps": [101, 102],
811+
"distinctSourceOrderPreserved": true,
812+
"exactExhaustionNotMarkedCapped": true,
813+
"independentGuardReversionsVerified": true
814+
}
808815
}
809816
},
810817
"documentedLimitations": [

0 commit comments

Comments
 (0)