Skip to content

feat(cli): expose api pagination and shared cursor navigation - #1659

Merged
betegon merged 6 commits into
mainfrom
bt/api-pagination-cursor
Oct 30, 2026
Merged

betegon merged 6 commits into
mainfrom
bt/api-pagination-cursor

Conversation

@betegon

@betegon betegon commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

sentry api --json discarded pagination metadata, leaving agents without a way to continue a successful query. It now exposes continuation metadata and uses the CLI's shared cursor history for next, prev, and first.

Before

sentry api organizations/ -f per_page=1 --json

Even when another page existed, the output had no continuation (bodies abbreviated):

{"status":200,"statusText":"OK","body":[{"slug":"example-org"}]}

After

The same command includes navigation metadata:

{"status":200,"statusText":"OK","body":[{"slug":"example-org"}],"pagination":{"nextCursor":"1:1:0","hasPrev":false}}

Repeat the endpoint and filters to navigate:

sentry api organizations/ -f per_page=1 --cursor next --json
sentry api organizations/ -f per_page=1 --cursor prev --json
sentry api organizations/ -f per_page=1 --cursor first --json

Raw cursors and shared custom bookmarks are accepted, including legacy cli1~ bookmarks. Resuming restores the required page size and skips rows already read; forward/back navigation retains that exact bookmark. Arrays and Events {data, meta} envelopes are supported, preserving envelope metadata.

Shared SDK ownership

Uses published @sentry/api@0.428.0, including getsentry/sentry-api-schema#116 and getsentry/sentry-api-schema#117. The SDK owns generic URL, header, and body preparation through client.prepareRequest(), plus partial-page consumption and bounded traversal through consumePage and paginateUpTo. The API command uses the same prepared Request for preview and dispatch, removing its duplicate URL/header builders.

Toolkit keeps authentication and trusted-host checks, raw response decoding, request/identity fingerprints, bookmark encoding, page-sizing policy, and navigation history. The SDK accepts decoded server cursors and offsets without depending on CLI storage or cursor formats. Existing MCP and frontend consumers do not need to opt into request preparation.

Each api invocation still fetches one server page, and the endpoint remains required. History is scoped to the request and identity; dry-runs and failed requests do not advance it. hasPrev describes local navigation, while prevCursor is server-provided. --fields filters only the body. Raw text, binary downloads, explicit Content-Type headers, and non-2xx response bodies retain their behavior. URL fragments are excluded without losing encoded # characters. Command help, docs, and the generated skill reference are updated.

Continuing without an endpoint is stacked in #1691. Native logs pagination remains a separate follow-up.

Generic request preparation is tested in @sentry/api. Additional coverage for continuation across short and empty pages is in getsentry/sentry-api-schema#118, a tests-only follow-up that requires no SDK release. The consumer tests concentrate on CLI navigation/history, identity isolation, raw response policy, and bookmark adaptation.

Validation

  • Workspace typecheck and lint pass with published @sentry/api@0.428.0.
  • API and library E2E pass against the bundled CLI. Consolidated flows retain navigation, dry-run/error recovery, raw-file preview and dispatch, shared/legacy bookmarks, Events metadata, and request/identity isolation. The silent request starts without JSON mode, then resumes through the same saved history.
  • Full workspace pnpm run test passes on Linux x64 on the final source: 12,669 CLI tests, 337 shared tests, and all MCP, Cloudflare, frontend, and remaining workspace suites. Existing skips remain unchanged.
  • Review and cleanup completed with no remaining findings. Production code is unchanged by the test consolidation.

@vercel

vercel Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
sentry-local Skipped Skipped Oct 30, 2026 10:02am UTC

Request Review

@betegon
betegon marked this pull request as ready for review October 26, 2026 15:13
@github-actions github-actions Bot added the risk: medium PR risk score: medium label Oct 26, 2026
@betegon
betegon force-pushed the bt/api-pagination-cursor branch from b4cb377 to 4fe80fe Compare October 26, 2026 15:16

@BYK BYK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should use the common pagination system in the repo now. We even have some custom cursors now. I'm surprised the lint rule did not catch this.

Otherwise, great change thanks a lot!

@BYK

BYK commented Oct 26, 2026

Copy link
Copy Markdown
Member

I'd even argue we should let omitting the endpoint and other args or something so one can do sentry api --cursor next to keep paging through? Or something closer to this?

@betegon betegon changed the title feat(cli): expose api pagination and cursor continuation feat(cli): expose api pagination and shared cursor navigation Oct 27, 2026
@betegon
betegon force-pushed the bt/api-pagination-cursor branch from b754e53 to f2d8d61 Compare October 28, 2026 10:28
@betegon
betegon requested a review from BYK October 28, 2026 10:41
Comment thread packages/cli/src/commands/api.ts Resolved
@betegon
betegon force-pushed the bt/api-pagination-cursor branch from f2d8d61 to dae73bb Compare October 29, 2026 16:29
@betegon
betegon force-pushed the bt/api-pagination-cursor branch from dae73bb to 58fdcac Compare October 29, 2026 16:32
betegon added a commit to getsentry/sentry-api-schema that referenced this pull request Oct 29, 2026
Consumers that inspect a raw request before sending it currently have to
repeat URL, header and body preparation. Expose
`sentry.client.prepareRequest(options): Promise<Request>` from the
concrete client, and extract the same preparation stage used by ordinary
SDK requests. This lets consumers inspect and dispatch one native
request through their existing transport.

## Before / after

Before, the CLI prepares URLs and effective headers for pagination
identity and previews, then rebuilds them during raw dispatch. The SDK
cannot expose its prepared request without sending it. Passing `query`
alongside a URL that already has query parameters also appends a second
`?`.

After:

```ts
const sentry = createSentryClient({ baseUrl: "https://example.test/sentry" });
const request = await sentry.client.prepareRequest({
  method: "GET",
  url: "/api/0/organizations/example/events/?field=id&field=title&cursor=old",
  query: { cursor: "opaque-next", per_page: 50 },
});
// No fetch yet. Both field values remain and the old cursor is replaced:
// https://example.test/sentry/api/0/organizations/example/events/?field=id&field=title&cursor=opaque-next&per_page=50
const response = await fetch(request);
// The consumer retains raw status, headers and body handling.
```

Preparation runs configured auth, validation, serialization and request
interceptors once. It preserves the native signal and does not invoke
fetch or response/error processing. Transports can still attach browser
CSRF, credentials or routing when dispatching. Existing custom `Client`
implementations accepted by generated operations need no new method.

The shared URL builder replaces explicitly serialized query keys while
retaining unrelated repeated values and encoding. With the default
serializer, `null` clears a parameter and `undefined` leaves it
unchanged. Custom serializers and opaque cursor strings remain
supported. Ordinary requests use the same behavior.

## Validation and scope

Build, typecheck, 253 SDK tests and 2 Node cancellation tests pass.
Coverage compares preparation with ordinary dispatch, exercises raw and
browser transports, custom cursors/serializers, body handling,
cancellation, concurrent clients and reconfiguration. The packed npm
artifact is exercised in a clean Node consumer, including public types
and raw non-2xx responses. Review and cleanup completed.

Refs #93 and #99. This is the generic preparation slice for [Toolkit
#1659](getsentry/toolkit#1659); CLI adoption
follows publication. Request identity, navigation history, bookmark
codecs, output and endpoint recovery remain consumer policy. No
production or browser-login validation is claimed, and the remaining v1
adoption gates stay open.
@betegon
betegon force-pushed the bt/api-pagination-cursor branch from 58fdcac to 76ea3f5 Compare October 29, 2026 21:06
@betegon
betegon force-pushed the bt/api-pagination-cursor branch from 76ea3f5 to 880d622 Compare October 29, 2026 21:50
@betegon
betegon merged commit f18d4c3 into main Oct 30, 2026
42 of 43 checks passed
@betegon
betegon deleted the bt/api-pagination-cursor branch October 30, 2026 12:28
betegon added a commit to getsentry/sentry-api-schema that referenced this pull request Oct 30, 2026
A saved continuation can cross pages shorter than its remaining offset,
including an empty page. Cover `consumePage` and `paginateUpTo` carrying
an offset of five through `[1, 2]`, `[]`, and `[3, 4, 5, 6]`, returning
only `[6]` while following opaque cursors.

This keeps the generic continuation contract alongside its SDK
implementation as consumer test duplication is reduced in [Toolkit
#1659](getsentry/toolkit#1659). Only test
coverage changes; no runtime, public API, or version changes.

Build, typecheck, all 254 Bun tests, and both Node cancellation tests
pass.

This branch was previously deployed

1 inactive deployment
Preview — 0bd68a84 Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: medium PR risk score: medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants