mirror of
https://github.com/hoppscotch/hoppscotch.git
synced 2026-08-28 17:45:03 +08:00
fix(backend): prevent request loss in large team collections (#6072)
Co-authored-by: “mirarifhasan” <arif.ishan05@gmail.com>
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user