fix(backend): prevent request loss in large team collections (#6072)

Co-authored-by: “mirarifhasan” <arif.ishan05@gmail.com>
This commit is contained in:
Yuri Gerasimov
2026-07-29 21:43:47 +05:00
committed by GitHub
parent 342d0cbdf0
commit edd3713edd
2 changed files with 88 additions and 11 deletions
@@ -328,15 +328,74 @@ describe('getRequestsInCollection', () => {
});
test('resolves with the correct info for the collection id and a valid cursor', async () => {
mockPrisma.teamRequest.findFirst.mockResolvedValue({
orderIndex: dbTeamRequests[0].orderIndex,
} as DbTeamRequest);
mockPrisma.teamRequest.findMany.mockResolvedValue([dbTeamRequests[1]]);
const response = teamRequestService.getRequestsInCollection(
const response = await teamRequestService.getRequestsInCollection(
dbTeamRequests[1].collectionID,
dbTeamRequests[0].id,
1,
);
expect(response).resolves.toEqual([teamRequests[1]]);
expect(response).toEqual([teamRequests[1]]);
});
test('paginates on the orderIndex of the cursor item, scoped to the collection', async () => {
mockPrisma.teamRequest.findFirst.mockResolvedValue({
orderIndex: dbTeamRequests[0].orderIndex,
} as DbTeamRequest);
mockPrisma.teamRequest.findMany.mockResolvedValue([dbTeamRequests[1]]);
await teamRequestService.getRequestsInCollection(
teamCollection.id,
dbTeamRequests[0].id,
1,
);
expect(mockPrisma.teamRequest.findFirst).toHaveBeenCalledWith({
where: { id: dbTeamRequests[0].id, collectionID: teamCollection.id },
select: { orderIndex: true },
});
expect(mockPrisma.teamRequest.findMany).toHaveBeenCalledWith({
take: 1,
where: {
collectionID: teamCollection.id,
orderIndex: { gt: dbTeamRequests[0].orderIndex },
},
orderBy: { orderIndex: 'asc' },
});
});
test('resolves with an empty array when cursor is provided but cursor item is not found', async () => {
mockPrisma.teamRequest.findFirst.mockResolvedValue(null);
const result = await teamRequestService.getRequestsInCollection(
'testcoll',
'nonexistent-cursor',
10,
);
expect(result).toEqual([]);
expect(mockPrisma.teamRequest.findMany).not.toHaveBeenCalled();
});
test('does not look up a cursor item when no cursor is provided', async () => {
mockPrisma.teamRequest.findMany.mockResolvedValue(dbTeamRequests);
await teamRequestService.getRequestsInCollection(
teamCollection.id,
null,
10,
);
expect(mockPrisma.teamRequest.findFirst).not.toHaveBeenCalled();
expect(mockPrisma.teamRequest.findMany).toHaveBeenCalledWith({
take: 10,
where: { collectionID: teamCollection.id },
orderBy: { orderIndex: 'asc' },
});
});
});
@@ -226,8 +226,15 @@ export class TeamRequestService {
/**
* Fetch team requests by Collection ID
*
* Pagination is keyed on `orderIndex` (unique per collection, see the
* `TeamRequest_teamID_collectionID_orderIndex_key` constraint) instead of
* Prisma's `cursor` + `skip`, so a page never depends on the offset of the
* cursor row within the result set.
*
* @param collectionID Collection ID to fetch requests in
* @param cursor Cursor for pagination
* @param cursor ID of the last request of the previous page. Must belong to
* `collectionID`; an unknown cursor resolves to an empty page
* @param take Take number of requests
* @returns
*/
@@ -236,20 +243,31 @@ export class TeamRequestService {
cursor: string,
take = 10,
) {
let whereClause: Prisma.TeamRequestWhereInput = { collectionID };
if (cursor) {
const cursorItem = await this.prisma.teamRequest.findFirst({
where: { id: cursor, collectionID },
select: { orderIndex: true },
});
if (!cursorItem) return [];
whereClause = {
collectionID,
orderIndex: { gt: cursorItem.orderIndex },
};
}
const dbTeamRequests = await this.prisma.teamRequest.findMany({
cursor: cursor ? { id: cursor } : undefined,
take: take,
skip: cursor ? 1 : 0,
where: {
collectionID: collectionID,
},
take,
where: whereClause,
orderBy: {
orderIndex: 'asc',
},
});
const teamRequests = dbTeamRequests.map((tr) => this.cast(tr));
return teamRequests;
return dbTeamRequests.map((tr) => this.cast(tr));
}
/**