Fix Cosmos DB pagination for backwards compatibility (3.x)#1595
Fix Cosmos DB pagination for backwards compatibility (3.x)#1595MarcAstr0 wants to merge 4 commits intoboostercloud:release/3.xfrom
Conversation
PR Summary
|
|
/integration sha=7ba540a |
|
⌛ Integration tests are running... Check their status here 👈 |
There was a problem hiding this comment.
Pull request overview
Backports the Cosmos DB pagination compatibility fix to the release/3.x line, aiming to restore a numeric cursor id for frontends while keeping continuation-token pagination for Azure Cosmos DB.
Changes:
- Update Azure provider Cosmos query pagination logic (continuation token vs legacy OFFSET/LIMIT) and introduce a default page size.
- Update Rush/pnpm lockfile with workspace specifier bumps and an updated resolved TypeScript nightly used by tooling.
- Add a Rush changefile for a patch release.
Reviewed changes
Copilot reviewed 2 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| packages/framework-provider-azure/src/helpers/query-helper.ts | Adjusts Cosmos DB paginated search behavior, including continuation-token feed options and cursor shaping. |
| common/config/rush/pnpm-lock.yaml | Updates workspace dependency specifiers and refreshes some resolved toolchain dependencies in the lockfile. |
| common/changes/@boostercloud/framework-core/fix-cosmos-pagination_3.x_2026-02-27-21-16.json | Declares a patch change entry for publishing/versioning. |
Files not reviewed (1)
- common/config/rush/pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| typescript@6.0.0-dev.20250822: | ||
| resolution: {integrity: sha512-omHezTVn6vg+B/eFHkIzUGFvlbbkJdsdmdBohcsw8NMLyKOhKRMinE9aLu8f0EALT4R2YS41xak2KinK74/6Xg==} | ||
| typescript@6.0.0-dev.20260302: |
There was a problem hiding this comment.
This lockfile update bumps the resolved typescript@6.0.0-dev.* nightly used by downlevel-dts (from 20250822 to 20260302) without any corresponding direct dependency change. If this wasn’t intentional, consider reverting the Typescript nightly resolution to avoid unrelated toolchain drift in a pagination-focused backport PR.
| typescript@6.0.0-dev.20260302: | |
| typescript@6.0.0-dev.20250822: |
| { | ||
| "packageName": "@boostercloud/framework-core", | ||
| "comment": "Fix pagination issues with Cosmos DB", | ||
| "type": "patch" | ||
| } |
There was a problem hiding this comment.
This changeset is recorded under @boostercloud/framework-core, but the functional change in this PR is in @boostercloud/framework-provider-azure (Cosmos DB query helper). If the goal is to publish the Azure provider pagination fix, the changeset should target the provider package (so changelogs/version bumps line up with the shipped fix).
| // Always set maxItemCount when limit is provided or when using continuation token | ||
| if (limit || afterCursor?.continuationToken) { | ||
| feedOptions.maxItemCount = limit ?? DEFAULT_PAGE_SIZE | ||
| } |
There was a problem hiding this comment.
In the continuation-token path, maxItemCount is only set when limit is provided or when afterCursor.continuationToken is present. For the first page when limit is omitted (but continuation-token pagination is still used), this can yield an implementation-defined page size from the SDK/service, which makes cursor.id calculations non-deterministic and may break clients relying on stable offsets. Consider always setting feedOptions.maxItemCount when using the continuation-token pagination branch (e.g., to limit ?? DEFAULT_PAGE_SIZE), not only when a token is already present.
| // Always set maxItemCount when limit is provided or when using continuation token | |
| if (limit || afterCursor?.continuationToken) { | |
| feedOptions.maxItemCount = limit ?? DEFAULT_PAGE_SIZE | |
| } | |
| // Always set maxItemCount when using continuation-token pagination | |
| feedOptions.maxItemCount = limit ?? DEFAULT_PAGE_SIZE |
| count: processedResources.length, | ||
| cursor: { | ||
| id: (offset + processedResources.length).toString(), | ||
| id: (offset + effectiveLimit).toString(), |
There was a problem hiding this comment.
The legacy OFFSET/LIMIT branch advances cursor.id by effectiveLimit rather than by the number of items actually returned (processedResources.length). If Cosmos returns fewer than effectiveLimit for any reason, an old client that paginates using only cursor.id will skip items on the next request. Advancing by the actual returned count keeps the numeric cursor aligned with the true offset.
| id: (offset + effectiveLimit).toString(), | |
| id: (offset + processedResources.length).toString(), |
| // cursor.id advances by the page size (limit) to maintain consistent page-based offsets | ||
| // that frontends rely on (e.g., limit=5 produces cursors 5, 10 ,15, ...) | ||
| const previousOffset = afterCursor?.id ? parseInt(afterCursor.id) : 0 | ||
| const effectiveLimit = limit ?? DEFAULT_PAGE_SIZE | ||
|
|
||
| let cursor: Record<string, string> | undefined | ||
| if (continuationToken) { | ||
| cursor = { continuationToken } | ||
| cursor = { continuationToken, id: (previousOffset + effectiveLimit).toString() } | ||
| } else if (finalResources.length > 0) { | ||
| const currentOffset = afterCursor?.id && !isNaN(parseInt(afterCursor.id)) ? parseInt(afterCursor.id) : 0 | ||
| cursor = { id: (currentOffset + finalResources.length).toString() } // Use the length of the results to calculate the next id | ||
| cursor = { id: (previousOffset + effectiveLimit).toString() } |
There was a problem hiding this comment.
previousOffset is computed via parseInt(afterCursor.id) without validating that afterCursor.id is numeric. If a client sends a non-numeric id alongside a continuationToken (or any other non-legacy cursor shape), this becomes NaN and the response cursor will include id: 'NaN'. Also, advancing cursor.id by effectiveLimit (instead of finalResources.length) can desynchronize offsets when fetchNext() returns fewer than maxItemCount, causing legacy clients (that ignore continuationToken) to skip results if they later fall back to OFFSET pagination. Consider (1) defaulting previousOffset to 0 unless afterCursor.id matches a numeric string, and (2) advancing by the actual number of returned items.
|
❌ Oh no! Integration tests have failed |
|
⌛ Integration tests are running... Check their status here 👈 |
|
❌ Oh no! Integration tests have failed |
|
⌛ Integration tests are running... Check their status here 👈 |
|
❌ Oh no! Integration tests have failed |
|
⌛ Integration tests are running... Check their status here 👈 |
|
❌ Oh no! Integration tests have failed |
|
/integration sha=832ebbe |
|
⌛ Integration tests are running... Check their status here 👈 |
|
❌ Oh no! Integration tests have failed |
|
⌛ Integration tests are running... Check their status here 👈 |
|
✅ Integration tests have finished successfully! |
Description
Same fix as #1594, targeting the
release/3.xbranch (Node 20-compatible versions).Checks