Skip to content

Conversation

@zdennis
Copy link

@zdennis zdennis commented Dec 21, 2025

Fixes #898

Problem

After the migration to the new Jira v3 JQL search endpoint in PR #892, pagination using the --paginate flag is broken for Cloud installations. The from (offset) parameter in --paginate <from>:<limit> is silently ignored, causing all pagination queries to return the same results regardless of the specified offset.

For example, these commands all return identical results:

jira issue list --jql 'project in (PROJ)' --paginate 0:2
jira issue list --jql 'project in (PROJ)' --paginate 1:2
jira issue list --jql 'project in (PROJ)' --paginate 2:2

This happens because the new /rest/api/3/search/jql endpoint does not support the startAt parameter that the v2 API used. Instead, it uses cursor-based pagination via nextPageToken and isLast fields.

Solution

Implemented cursor-based pagination in the Search() function to support the from offset parameter:

  1. Modified Search() to accept a from parameter
  2. Uses nextPageToken to iterate through result pages
  3. Skips items until reaching the requested offset
  4. Collects items up to the requested limit
  5. Uses max page size (100) when skipping for efficiency

This maintains backward compatibility with the existing --paginate from:limit interface while working with the new Jira v3 API.

Changes

  • pkg/jira/search.go - Rewrote Search() to implement cursor-based pagination with offset support
    • Added maxSearchPageSize constant (100) for clarity
    • Properly sets IsLast field based on whether all results were fetched
  • pkg/jira/search_test.go - Added comprehensive pagination test coverage (11 tests)
  • api/client.go - Updated ProxySearch() to pass from parameter to Search()
  • internal/cmd/epic/list/list.go - Updated two Search() call sites to include from parameter

Testing

Unit Tests

Added 11 unit tests in pkg/jira/search_test.go covering:

  • Offset skipping within single page
  • Multi-page fetching with nextPageToken
  • Limit stops fetching early
  • Offset spanning multiple pages
  • Offset beyond available items returns empty
  • Empty result set handling
  • Error mid-pagination returns error (not partial results)
  • nextPageToken is correctly passed in subsequent requests
  • IsLast is true when all pages exhausted
  • IsLast is false when limit reached before end

Run with: go test ./pkg/jira/... -run TestSearchPagination -v

Manual Testing

test-pagination.sh
#!/bin/bash

# Test script to reproduce pagination issue #898

set -e

PROJECT=""

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -p|--project)
            PROJECT="$2"
            shift 2
            ;;
        *)
            echo "Unknown option: $1"
            echo "Usage: $0 -p|--project PROJECT_KEY"
            exit 1
            ;;
    esac
done

if [[ -z "$PROJECT" ]]; then
    echo "Error: --project is required"
    echo "Usage: $0 -p|--project PROJECT_KEY"
    exit 1
fi

JIRA_CLI="./jira-cli"

echo "=== Pagination Bug Test (Issue #898) ==="
echo "Project: $PROJECT"
echo ""

# Capture output from each pagination command
echo "Fetching page 1 (--paginate 0:2)..."
PAGE1=$($JIRA_CLI issue list --jql "project in ($PROJECT)" --paginate 0:2 --plain --no-headers --columns key,summary 2>&1)

echo "Fetching page 2 (--paginate 1:2)..."
PAGE2=$($JIRA_CLI issue list --jql "project in ($PROJECT)" --paginate 1:2 --plain --no-headers --columns key,summary 2>&1)

echo "Fetching page 3 (--paginate 2:2)..."
PAGE3=$($JIRA_CLI issue list --jql "project in ($PROJECT)" --paginate 2:2 --plain --no-headers --columns key,summary 2>&1)

echo ""
echo "--- Page 1 (offset 0, limit 2) ---"
echo "$PAGE1"
echo ""
echo "--- Page 2 (offset 1, limit 2) ---"
echo "$PAGE2"
echo ""
echo "--- Page 3 (offset 2, limit 2) ---"
echo "$PAGE3"
echo ""

# Compare results
echo "=== RESULT ==="

BUG_DETECTED=false

if [[ "$PAGE1" == "$PAGE2" ]]; then
    echo "FAIL: Page 1 and Page 2 are identical"
    BUG_DETECTED=true
else
    echo "PASS: Page 1 and Page 2 are different"
fi

if [[ "$PAGE2" == "$PAGE3" ]]; then
    echo "FAIL: Page 2 and Page 3 are identical"
    BUG_DETECTED=true
else
    echo "PASS: Page 2 and Page 3 are different"
fi

if [[ "$PAGE1" == "$PAGE3" ]]; then
    echo "FAIL: Page 1 and Page 3 are identical"
    BUG_DETECTED=true
else
    echo "PASS: Page 1 and Page 3 are different"
fi

echo ""
if [[ "$BUG_DETECTED" == "true" ]]; then
    echo "❌ PAGINATION BUG CONFIRMED - Different offsets return identical results"
    exit 1
else
    echo "✅ PAGINATION WORKING - Different offsets return different results"
    exit 0
fi

Notes

  • Local/on-premise installations continue to use the v2 API with startAt and are unaffected by this change
  • The implementation fetches pages sequentially to reach the requested offset, which may result in multiple API calls for large offsets

Breaking Changes

The Search() function signature in pkg/jira changed from:

func (c *Client) Search(jql string, limit uint) (*SearchResult, error)

to:

func (c *Client) Search(jql string, from, limit uint) (*SearchResult, error)

This is necessary to support pagination offsets. External code importing pkg/jira directly (uncommon) would need to add the from parameter. The CLI interface (--paginate) is unchanged.

Copilot AI review requested due to automatic review settings December 21, 2025 02:16
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restores pagination support for the Jira v3 JQL search API that was broken after the migration from v2 to v3. The v3 API uses cursor-based pagination (via nextPageToken) instead of the offset-based approach (startAt parameter) used by v2, which caused the --paginate flag's from offset parameter to be silently ignored.

Key Changes:

  • Implemented cursor-based pagination in Search() function that emulates offset behavior by fetching and skipping pages sequentially
  • Added comprehensive test coverage with 11 new test cases covering pagination edge cases
  • Updated all call sites to pass the from parameter to the Search() function

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
pkg/jira/search.go Rewrote Search() function to implement cursor-based pagination with offset support by iterating through pages using nextPageToken and skipping items until reaching the requested offset
pkg/jira/search_test.go Added 11 comprehensive pagination test cases covering offset skipping, multi-page fetching, early limit termination, error handling, and IsLast field behavior
api/client.go Updated ProxySearch() to pass the from parameter to the v3 Search() function
internal/cmd/epic/list/list.go Updated two call sites to include the from parameter when calling Search()

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Implement cursor-based pagination in Search() to support the 'from'
offset parameter using nextPageToken from the API response.

The v3 /search/jql endpoint doesn't support the 'startAt' parameter
that v2 used. Instead, it uses cursor-based pagination via
'nextPageToken' and 'isLast' fields. This change iterates through
pages using nextPageToken until reaching the requested offset.

Changes:
- Modified Search() signature to accept 'from' parameter
- Iterate through pages using nextPageToken to reach offset
- Use max page size (100) when skipping for efficiency
- Updated ProxySearch() to pass 'from' parameter
- Updated epic list command call sites

Tests:
- Offset skipping within single page
- Multi-page fetching with nextPageToken
- Limit enforcement stops fetching early
- Offset spanning multiple pages
- Offset beyond available items
- Empty result sets
- Error mid-pagination returns error
- IsLast correctness in various scenarios

Fixes ankitpokhrel#898
@zdennis zdennis force-pushed the issue-898-pagination-bug branch from a01afa5 to a8765c6 Compare December 21, 2025 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pagination with the new JQL endpoint

1 participant