mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-01 14:59:19 +08:00
fix(connectors): only flag a Bitbucket listing capped when the cap withheld something
Review round 2. takeIndexableWithinCap reports capReached as soon as the running total equals maxItems, which is also true of a listing that ended at exactly that count. Setting listingCapped there suppressed deletion reconciliation for a complete listing, so upstream-deleted files and pull requests could stay in the knowledge base indefinitely. applyMaxItemsCap now takes whether Bitbucket had more content beyond the page -- a next link, or directories still queued on the frontier -- and flags the listing only when the cap actually withheld something, matching the Databricks, Google Chat, and Workday connectors.
This commit is contained in:
@@ -357,6 +357,44 @@ describe('bitbucket maxItems cap', () => {
|
|||||||
expect(syncContext.listingCapped).toBe(true)
|
expect(syncContext.listingCapped).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('leaves the listing reconcilable when a complete listing ends exactly on the cap', async () => {
|
||||||
|
mockApi([[/\/src\//, () => jsonResponse({ values: [fileEntry('a.md'), fileEntry('b.md')] })]])
|
||||||
|
|
||||||
|
const syncContext: Record<string, unknown> = {}
|
||||||
|
const result = await bitbucketConnector.listDocuments(
|
||||||
|
ACCESS_TOKEN,
|
||||||
|
{ ...CONFIG, maxItems: '2' },
|
||||||
|
undefined,
|
||||||
|
syncContext
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.documents).toHaveLength(2)
|
||||||
|
expect(syncContext.listingCapped).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('flags the listing capped when the cap lands on a page boundary with more to come', async () => {
|
||||||
|
mockApi([
|
||||||
|
[
|
||||||
|
/\/src\//,
|
||||||
|
() =>
|
||||||
|
jsonResponse({
|
||||||
|
values: [fileEntry('a.md'), fileEntry('b.md')],
|
||||||
|
next: 'https://api.bitbucket.org/2.0/repositories/acme/widgets/src/abc/?page=2',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
|
||||||
|
const syncContext: Record<string, unknown> = {}
|
||||||
|
await bitbucketConnector.listDocuments(
|
||||||
|
ACCESS_TOKEN,
|
||||||
|
{ ...CONFIG, maxItems: '2' },
|
||||||
|
undefined,
|
||||||
|
syncContext
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(syncContext.listingCapped).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
it('leaves the listing reconcilable when no cap is configured', async () => {
|
it('leaves the listing reconcilable when no cap is configured', async () => {
|
||||||
mockApi([[/\/src\//, () => jsonResponse({ values: [fileEntry('a.md'), fileEntry('b.md')] })]])
|
mockApi([[/\/src\//, () => jsonResponse({ values: [fileEntry('a.md'), fileEntry('b.md')] })]])
|
||||||
|
|
||||||
|
|||||||
@@ -714,11 +714,20 @@ function pendingDirectories(syncContext: Record<string, unknown> | undefined): s
|
|||||||
* Applies the optional maxItems cap to a page, tracking the running total in
|
* Applies the optional maxItems cap to a page, tracking the running total in
|
||||||
* syncContext and flagging `listingCapped` when the cap truncates the listing.
|
* syncContext and flagging `listingCapped` when the cap truncates the listing.
|
||||||
* Skipped (oversized) documents ride along without consuming the cap.
|
* Skipped (oversized) documents ride along without consuming the cap.
|
||||||
|
*
|
||||||
|
* `sourceHasMore` is whether Bitbucket still had content beyond this page — a
|
||||||
|
* `next` link, or directories still queued on the frontier. It is required because
|
||||||
|
* `takeIndexableWithinCap` reports `capReached` as soon as the running total
|
||||||
|
* *equals* maxItems, which is also true of a listing that ended at exactly that
|
||||||
|
* count. Setting `listingCapped` there would suppress deletion reconciliation for
|
||||||
|
* a listing that was in fact complete, leaving upstream-deleted files indexed
|
||||||
|
* indefinitely, so the flag is set only when the cap actually withheld something.
|
||||||
*/
|
*/
|
||||||
function applyMaxItemsCap(
|
function applyMaxItemsCap(
|
||||||
documents: ExternalDocument[],
|
documents: ExternalDocument[],
|
||||||
maxItems: number,
|
maxItems: number,
|
||||||
syncContext: Record<string, unknown> | undefined
|
syncContext: Record<string, unknown> | undefined,
|
||||||
|
sourceHasMore: boolean
|
||||||
): { documents: ExternalDocument[]; capped: boolean } {
|
): { documents: ExternalDocument[]; capped: boolean } {
|
||||||
if (maxItems <= 0) return { documents, capped: false }
|
if (maxItems <= 0) return { documents, capped: false }
|
||||||
const alreadyIndexed = (syncContext?.totalDocsFetched as number) ?? 0
|
const alreadyIndexed = (syncContext?.totalDocsFetched as number) ?? 0
|
||||||
@@ -734,7 +743,8 @@ function applyMaxItemsCap(
|
|||||||
)
|
)
|
||||||
if (syncContext) {
|
if (syncContext) {
|
||||||
syncContext.totalDocsFetched = alreadyIndexed + indexableCount
|
syncContext.totalDocsFetched = alreadyIndexed + indexableCount
|
||||||
if (capReached) syncContext.listingCapped = true
|
const withheld = taken.length < documents.length || sourceHasMore
|
||||||
|
if (capReached && withheld) syncContext.listingCapped = true
|
||||||
}
|
}
|
||||||
return { documents: taken, capped: capReached }
|
return { documents: taken, capped: capReached }
|
||||||
}
|
}
|
||||||
@@ -922,7 +932,8 @@ export const bitbucketConnector: ConnectorConfig = {
|
|||||||
const { documents: capped, capped: hitLimit } = applyMaxItemsCap(
|
const { documents: capped, capped: hitLimit } = applyMaxItemsCap(
|
||||||
documents,
|
documents,
|
||||||
maxItems,
|
maxItems,
|
||||||
syncContext
|
syncContext,
|
||||||
|
Boolean(page.next) || frontier.length > 0
|
||||||
)
|
)
|
||||||
if (hitLimit) return { documents: capped, hasMore: false }
|
if (hitLimit) return { documents: capped, hasMore: false }
|
||||||
|
|
||||||
@@ -990,7 +1001,8 @@ export const bitbucketConnector: ConnectorConfig = {
|
|||||||
const { documents: capped, capped: hitLimit } = applyMaxItemsCap(
|
const { documents: capped, capped: hitLimit } = applyMaxItemsCap(
|
||||||
documents,
|
documents,
|
||||||
maxItems,
|
maxItems,
|
||||||
syncContext
|
syncContext,
|
||||||
|
Boolean(page.next)
|
||||||
)
|
)
|
||||||
if (hitLimit) return { documents: capped, hasMore: false }
|
if (hitLimit) return { documents: capped, hasMore: false }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user