Skip to content

Commit 3dbccf5

Browse files
saskliutasgrigasp
andauthored
Presentation: Fix paged request result accumulation (#3329)
* Avoid infinite loop when building paged requests response * Rush change * Update presentation/frontend/src/test/PresentationManager.test.ts Co-authored-by: Grigas <[email protected]> Co-authored-by: Grigas <[email protected]>
1 parent bc750e8 commit 3dbccf5

File tree

3 files changed

+28
-10
lines changed

3 files changed

+28
-10
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@itwin/presentation-frontend",
5+
"comment": "Fix paged requests result accumulation resulting in infinite loop.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@itwin/presentation-frontend"
10+
}

presentation/frontend/src/presentation-frontend/PresentationManager.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -481,11 +481,8 @@ interface PagedGeneratorCreateProps<TPagedResponseItem> {
481481
get: (pageStart: Required<PageOptions>, requestIndex: number) => Promise<{ total: number, items: TPagedResponseItem[] }>;
482482
}
483483
async function createPagedGeneratorResponse<TPagedResponseItem>(props: PagedGeneratorCreateProps<TPagedResponseItem>) {
484-
const requestedPageStart = props.page?.start ?? 0;
485-
const requestedPageSize = props.page?.size ?? 0;
486-
let pageStart = requestedPageStart;
487-
let pageSize = requestedPageSize;
488-
let receivedItemsCount = 0;
484+
let pageStart = props.page?.start ?? 0;
485+
let pageSize = props.page?.size ?? 0;
489486
let requestIndex = 0;
490487

491488
const firstPage = await props.get({ start: pageStart, size: pageSize }, requestIndex++);
@@ -496,21 +493,21 @@ async function createPagedGeneratorResponse<TPagedResponseItem>(props: PagedGene
496493
while (true) {
497494
for (const item of partialResult.items) {
498495
yield item;
499-
++receivedItemsCount;
500496
}
501497

498+
const receivedItemsCount = partialResult.items.length;
502499
if (partialResult.total !== 0 && receivedItemsCount === 0) {
503500
if (pageStart >= partialResult.total)
504501
throw new Error(`Requested page with start index ${pageStart} is out of bounds. Total number of items: ${partialResult.total}`);
505502
throw new Error("Paged request returned non zero total count but no items");
506503
}
507504

508-
if (requestedPageSize !== 0 && receivedItemsCount >= requestedPageSize || receivedItemsCount >= (partialResult.total - requestedPageStart))
505+
if (pageSize !== 0 && receivedItemsCount >= pageSize || receivedItemsCount >= (partialResult.total - pageStart))
509506
break;
510507

511-
if (requestedPageSize !== 0)
512-
pageSize = requestedPageSize - receivedItemsCount;
513-
pageStart = requestedPageStart + receivedItemsCount;
508+
if (pageSize !== 0)
509+
pageSize -= receivedItemsCount;
510+
pageStart += receivedItemsCount;
514511

515512
partialResult = await props.get({ start: pageStart, size: pageSize }, requestIndex++);
516513
}

presentation/frontend/src/test/PresentationManager.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,17 @@ describe("PresentationManager", () => {
13431343
expect(result).to.deep.eq({ total: 0, items: [] });
13441344
});
13451345

1346+
it("returns zero response when partial request returns less items than requested", async () => {
1347+
const getter = sinon.stub();
1348+
getter.onFirstCall().resolves({ total: 5, items: [2, 3] });
1349+
getter.onSecondCall().resolves({ total: 5, items: [] });
1350+
const result = await buildPagedArrayResponse({ start: 1 }, getter);
1351+
expect(getter).to.be.calledTwice;
1352+
expect(getter.firstCall).to.be.calledWith({ start: 1, size: 0 });
1353+
expect(getter.secondCall).to.be.calledWith({ start: 3, size: 0 });
1354+
expect(result).to.deep.eq({ total: 0, items: [] });
1355+
});
1356+
13461357
});
13471358

13481359
});

0 commit comments

Comments
 (0)