From 3339b57603066b3730d04e78f5e06ab02c8bfc39 Mon Sep 17 00:00:00 2001 From: Josemar Luedke Date: Mon, 29 Dec 2025 12:19:34 -0800 Subject: [PATCH] fix(offset): correct StartCursor and EndCursor to reference current page items MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, StartCursor always returned offset 0 and EndCursor returned the offset to the last page of the entire dataset. This caused pagination to jump incorrectly when using cursors to navigate between pages. Now both cursors correctly point to items on the current page: - StartCursor: first item on current page - EndCursor: last item on current page (accounting for partial pages) This fixes the issue where requesting a second page would jump to an incorrect offset (e.g., offset 420 instead of offset 10). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- offset/paginator.go | 38 +++++++++++++++++++++++++------------- offset/paginator_test.go | 8 ++++++-- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/offset/paginator.go b/offset/paginator.go index 0a58f36..6c2fc98 100644 --- a/offset/paginator.go +++ b/offset/paginator.go @@ -131,27 +131,39 @@ func buildOrderBy(args *paging.PageArgs) []paging.Sort { // buildOffsetPageInfo creates PageInfo for offset-based pagination. // It calculates page boundaries and provides functions to query pagination state. // -// The endOffset calculation ensures the last page cursor points to the start -// of the final complete page of results. +// StartCursor and EndCursor point to the first and last items on the current page, +// not the first or last page of the entire dataset. func buildOffsetPageInfo( pageSize int, totalCount int64, currentOffset int, ) paging.PageInfo { count := int(totalCount) - endOffset := count - (count % pageSize) - - if endOffset == count { - endOffset = count - pageSize - } - if endOffset < 0 { - endOffset = 0 - } return paging.PageInfo{ - TotalCount: func() (*int, error) { return &count, nil }, - StartCursor: func() (*string, error) { return EncodeCursor(0), nil }, - EndCursor: func() (*string, error) { return EncodeCursor(endOffset), nil }, + TotalCount: func() (*int, error) { return &count, nil }, + StartCursor: func() (*string, error) { + if count == 0 { + return nil, nil + } + // Cursor of the first item on current page + return EncodeCursor(currentOffset + 1), nil + }, + EndCursor: func() (*string, error) { + if count == 0 { + return nil, nil + } + // Calculate the offset of the last item on this page + lastItemOffset := currentOffset + pageSize - 1 + if lastItemOffset >= count { + lastItemOffset = count - 1 + } + if lastItemOffset < currentOffset { + return nil, nil + } + // Cursor encodes position after the item (lastItemOffset + 1) + return EncodeCursor(lastItemOffset + 1), nil + }, HasNextPage: func() (bool, error) { return (currentOffset + pageSize) < count, nil }, HasPreviousPage: func() (bool, error) { return currentOffset > 0, nil }, } diff --git a/offset/paginator_test.go b/offset/paginator_test.go index e8cfede..33c58cf 100644 --- a/offset/paginator_test.go +++ b/offset/paginator_test.go @@ -116,11 +116,15 @@ var _ = Describe("Paginator", func() { hasPreviousPage, _ := page.PageInfo.HasPreviousPage() Expect(hasPreviousPage).To(Equal(true)) + // StartCursor should point to first item on current page (offset 20) + // Cursor encoding is offset + 1, so cursor = 21 startCursor, _ := page.PageInfo.StartCursor() - Expect(startCursor).To(Equal(offset.EncodeCursor(0))) + Expect(startCursor).To(Equal(offset.EncodeCursor(21))) + // EndCursor should point to last item on current page (offset 29) + // Cursor encoding is offset + 1, so cursor = 30 endCursor, _ := page.PageInfo.EndCursor() - Expect(endCursor).To(Equal(offset.EncodeCursor(90))) + Expect(endCursor).To(Equal(offset.EncodeCursor(30))) }) })