refactor(core): shared paginateFiltered for client-side-filter listing - #2108
refactor(core): shared paginateFiltered for client-side-filter listing#2108jariy17 wants to merge 1 commit into
Conversation
|
Claude Security Review: no high-confidence findings. (run) |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## refactor #2108 +/- ##
============================================
- Coverage 97.38% 97.34% -0.04%
============================================
Files 440 447 +7
Lines 26626 27004 +378
============================================
+ Hits 25929 26287 +358
- Misses 697 717 +20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
AgentCore Harness Review
Verdict: Looks good
Nice consolidation — the three near-identical filter-and-fill pagination loops in eval.tsx (Batch Insights, Online Insights) and gateway.tsx (Gateway Connectors) collapse into paginateFiltered cleanly, and the tests exercise the tricky bits with a real in-memory source rather than mocks.
I walked the algorithm through the interesting cases and it holds up:
- Normal overshoot (matches from a later scan page push past
pageSize): trims topageSizeand returnsrequestToken— the token that fetched the current page — so the next call re-reads that page and picks up the tail. The boundary match repeats, matching the documented "accepted dup on boundary" contract that the rewrittenbatchInsights.test.tsxandgateway.test.tsassert. - Guard (a single scan page's matches on their own meet
pageSize): returns all matches accumulated so far withpage.nextToken, so the next call advances past the scan page instead of looping on its own token. Trace onrows("ABCDE")/servicePage=3/pageSize=2 confirms no loop and no lost items across the resumption. - Exhaustion returns
nextToken: undefinedcorrectly; scan cap at 101 throwsResultTruncationError;scanPageSizeundefined passes through asundefined(test at L638-647 verifies). - Token stability: fetches only ever happen at
scanPageSize(orundefined), never varying maxResults on a re-fetch, so APIs that bind maxResults into their continuation token won't reject the resume.
Behavior changes worth flagging (both look intentional and are captured in the tests):
- The guard branch can return more items than
maxResults(seepaginateFiltered.tsL746-747 and the rewrittenonline-insight.test.tsx"over-returns a scan page's insight configs when --max-results is smaller"). This is a real UX change from the old boundary-reread approach, which always trimmed exactly. It's the right trade to avoid the self-refilling-token loop, but any downstream consumer that strictly slices bymaxResultswill now silently drop items on the current page (the extras) rather than see them on the next call. Worth a quick sanity check that no caller does that. - The
ResultTruncationErrormessage loses the resource-specific phrasing ("Batch Evaluation scan requests" / "config pages") in favor of a generic "${resourceLabel} discovery exceeded N scans". Fine, just a minor telemetry/observability regression if anything greps those strings.
No new user-facing feature here, so no telemetry expectations. Mocking is appropriate — the helper's tests use a real fake source keyed by offset tokens, and the client tests still stub only the SDK command boundary.
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
|
Claude Security Review: no high-confidence findings. (run) |
…ine-insight, batch-insight, gateway-connector)
6b0570e to
f72cfd2
Compare
|
Claude Security Review: no high-confidence findings. (run) |
| throw new ResultTruncationError( | ||
| `Batch Insights discovery exceeded ${MAX_BATCH_INSIGHTS_SCAN_REQUESTS} Batch Evaluation scan requests; results are incomplete`, | ||
| ); | ||
| const page = await FilteredPaginator.paginate({ |
nborges-aws
left a comment
There was a problem hiding this comment.
Love that we're adding a shared utility for this. A few issues with our filtering algorithm that we should iron out first
| if (results.length >= pageSize) { | ||
| // Page holds >= pageSize matches by itself. Return every match found (the | ||
| // page may exceed maxResults) and advance past it: replaying its token would | ||
| // loop, and skipping the surplus would drop matches — so we over-return. |
There was a problem hiding this comment.
Doesn't over returning here just drop resources then? We only render up to pageSize rows, but we continue from where the service token leaves off. Everything beyond maxResults that was included by the service becomes permanently unreachable.
There was a problem hiding this comment.
This is best case scenario. It's not the best solution we can find. We can generate a composite token ({ serviceToken, skip }) so we can prevent this and the replay issue but its too complicated for a small inconvenience.
| if (matches.length >= pageSize) { | ||
| return { items: results, nextToken: page.nextToken }; | ||
| } | ||
| // Partial page: replaying its token is safe — the taken matches just repeat. |
There was a problem hiding this comment.
I get how replaying the service page here prevents dropping matched items. But for the inverse case, wouldn't this duplicate every match already consumed from the first partial page? If page A matches 2 items and page B matches 8, we're still using the token that returns page B. So all 8 from the second page would be duplicated and rendered twice.
I think this needs to change so that if A + B fills the max results, we return nextToken?
There was a problem hiding this comment.
This is best case scenario without introduce a composite token. I think its fine customers seen a duplicate. If this is an issue, I can introduce now.
One helper for every
listthat filters a broader API client-side.Why
Three
list*methods over-fetch a broad service API and filter client-side:What changed
FilteredPaginatorclass (src/core/filteredPaginator.ts) — scans full service pages, fills the requested page across them, bounded by 101 scans (ResultTruncationErrorpast that).listOnlineInsights/listBatchInsights/listGatewayConnectorseach delegate to the staticFilteredPaginator.paginate({ ... }), passing their predicate + page sizes inline.Behavior changes
maxResults, so list APIs that bindmaxResultsinto the token can't reject the continuation.--max-resultswhen one service page already holds a full page of matches (guards against an infinite loop).listGatewayConnectorsnow validatesmaxResults(rejects<1/ non-integer) — it didn't before.listBatchInsights+listGatewayConnectors: exact-size pages → dup-on-boundary.Tests
filteredPaginator.test.ts— 10 unit tests: validation, under-fill, overshoot+dup, guard over-return, truncation cap, token seeding, scan sizing.tscclean.