Skip to content

ref(cli): Move API calls with generated operations onto @sentry/api - #2073

Merged
BYK merged 1 commit into
mainfrom
ref/cli-sentry-api-operations
Nov 7, 2026
Merged

BYK merged 1 commit into
mainfrom
ref/cli-sentry-api-operations

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

This is step 1 of moving the CLI onto @sentry/api generated operations. The goal is an Oxlint rule that bans the apiRequest* helpers with no allowlist. @BYK chose that scope ("Also ban apiRequest* helpers") when the lint follow-up to #2062 was scoped. Since the rule can't land until every call site has moved, it isn't in this PR.

#2062 bumps @sentry/api to 1.44.0 too and moves lib/objectstore.ts. This PR leaves objectstore.ts alone, so whichever PR merges second only needs a lockfile rebase.

What changed

  • @sentry/api 1.26.0 → 1.44.0 in the catalog and in minimumReleaseAgeExclude. The regenerated alert.md and event.md skill references are included.
  • 18 apiRequest* call sites now use generated operations:
    • alerts: deleteIssueAlertRule → deleteOrganizationWorkflow
    • chunk-upload:
      • getChunkUploadOptions → getOrganizationChunkUploadOptions. ChunkServerOptionsSchema still validates the response because the SDK type doesn't include maxWait.
      • New assembleDebugFiles → assembleProjectDebugFiles
      • New assembleArtifactBundle → assembleOrganizationArtifactBundle
    • sourcemaps, dart-symbols, proguard, debug-files: their assemble and poll calls now go through the chunk-upload wrappers above (7 sites). pollAssembly/pollDifAssembly take an assemble callback.
    • conversations: getConversationSpans → retrieveOrganizationAiConversation
    • issues: getIssueByShortId → resolveOrganizationShortId
    • preprod-artifacts:
      • getBuildInstallDetails → getOrganizationPreprodArtifactInstallDetails
      • Latest base snapshot → getOrganizationPreprodArtifactSnapshotLatestBase (a 404 still returns null)
    • releases: setCommitsWithRefs → updateOrganizationRelease. The ./.. path-segment checks stay because the SDK's path serializer lets them through.
    • replays: getReplay → getOrganizationReplay
    • traces:
      • getTraceMeta → getOrganizationTraceMeta
      • fetchTransactionsPage/fetchSpansPage → listOrganizationEvents
  • infrastructure.ts gets validateResponse and unwrapValidatedResult, so call sites that already validated responses with Zod still do that on SDK results. No new as casts on query or body types.

Behavior notes

  • HTTP error messages for the moved calls now read <context>: <status> (from unwrapResult/throwApiError) instead of API request failed: …. The 401/403 enrichment is unchanged.
  • throwApiError doesn't attach the api_response_headers Sentry context that the helper path set.
  • Pagination, region routing (getOrgSdkConfig), caching and idempotency are unchanged.

Tests

  • chunk-upload.test.ts and proguard.test.ts now test at the fetch level (mockFetch + setOrgRegion). New cases cover maxWait, schema mismatch, 404, and both assemble endpoints.
  • debug-files.test.ts mocks the new assembleDebugFiles.
  • preprod-artifacts.test.ts mocks getOrgSdkConfig, with new cases for a null body, a 500, and a schema mismatch.
  • The existing fetch-level tests for alerts, short IDs, conversations, releases and traces pass unchanged.

Gate

  • pnpm run tsc and pnpm run lint: pass.
  • pnpm run test: fails only on sandbox environment problems that also fail on main. CI is the real check.
    • The root run aborts in codemod-cli3to4 because the sandbox lacks GLIBC_2.35.
    • pnpm -r --no-bail run test: every package passes except the CLI, which has 5 failures (12881 passed). All 5 are file-permission tests that fail on main in the same sandbox: completions (2), upgrade acquireLock, db/resilient readonly, and dsn/code-scanner unreadable files.

Remaining call sites (39 + 2 raw fetches)

(a) Missing from the public schema: needs getsentry/sentry API docs

  • auth.ts GET /api/0/ (root). Used for the token check; dynamic.
  • dashboards.ts GET /organizations/{org}/dashboards/{id}/revisions/
  • dashboards.ts POST /organizations/{org}/dashboards/{id}/revisions/{rev}/restore/
  • issues.ts GET /issues/{id}/ (unscoped issue lookup)
  • issues.ts PUT /issues/{id}/ (unscoped update fallback)
  • issues.ts raw customFetch GET /shared/issues/{shareId}/ (getSharedIssue)
  • logs.ts GET /organizations/{org}/trace-logs/
  • org-auth-tokens.ts GET, POST /organizations/{org}/org-auth-tokens/, DELETE …/{tokenId}/
  • organizations.ts GET /users/me/regions/
  • preprod-artifacts.ts:
    • POST /projects/{org}/{project}/files/preprodartifacts/assemble/ (build assemble)
    • GET …/preprodartifacts/snapshots/upload-options/
    • Snapshot archive status and trigger at …/snapshots/{id}/archive/
    • Raw customFetch openSnapshotArchive
  • projects.ts GET /organizations/{org}/projects/?query=dsn:{key} (×2). The list op has no query param.
  • releases.ts GET /organizations/{org}/releases/{version}/previous-with-commits/
  • traces.ts GET /projects/{org}/{project}/trace-items/{itemId}/
  • trials.ts GET /customers/{org}/ and PUT /customers/{org}/product-trial/
  • users.ts GET /auth/

These look intentionally private, so publishing them needs a call from the API owners: /customers/* (billing, getsentry), /users/me/regions/, the /api/0/ root, /auth/, previous-with-commits, trace-logs, trace-items, and the preprod/snapshot internals.

(b) In the schema, but the generated types don't match what the CLI sends or reads

  • alerts.ts ×2, GET /organizations/{org}/workflows/: no projectSlug filter, and the CLI sends string ids.
  • code-mappings.ts POST bulk code mappings: the CLI sends a camelCase body; the schema is snake_case.
  • conversations.ts list: no sort query param.
  • dashboards.ts events-timeseries: yAxis is an array and dataset is optional in the CLI.
  • dashboards.ts events: dataset can be null or a value outside the enum.
  • explore.ts events-timeseries: array yAxis, string interval.
  • explore.ts events: free-form dataset.
  • issues.ts GET org issue: the collapse enum is only release|stats|tags.
  • issues.ts PUT org issue: every update body field is required.
  • issues.ts PUT /organizations/{org}/issues/ (merge): updateOrganizationIssues also requires every body field.
  • replays.ts list: the field enum lacks info_ids/warning_ids.
  • replays.ts replay-count: no data_source=events.
  • seer.ts ×3, /organizations/{org}/issues/{id}/autofix/: no mode=explorer.
  • traces.ts GET /organizations/{org}/trace/{id}/: no timestamp/limit/project params.
  • preprod-artifacts.ts POST …/preprodartifacts/snapshots/: the body type is never, and the CLI sends a zstd body.

(c) Dynamic or upload transport, not Sentry API operations

  • Chunk uploads POST to the server-provided chunk URL.
  • Preprod build download fetches the external installUrl.
  • rawApiRequest/prepareRawApiRequest back the sentry api passthrough command.

via @BYK.

--

View Session [Sentry]

Bump @sentry/api to 1.44.0 and move the apiRequest* call sites that have
a matching generated operation onto it: alert rule delete, chunk upload
options and assemble (debug files, sourcemaps, dart symbols, proguard),
AI conversation detail, short ID resolve, preprod install details and
snapshot latest base, release commit refs, replay detail, trace meta,
and the transactions/spans event pages.

Add validateResponse/unwrapValidatedResult so call sites that already
check responses with a Zod schema keep doing that on SDK results.

This is step 1 of moving the CLI onto generated operations. The
remaining sites are blocked on missing public schema entries or type
mismatches and stay on the helpers for now.

Co-Authored-By: BYK <[email protected]>
@vercel

vercel Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
sentry-local Ready Ready Preview Nov 7, 2026 12:57am UTC

Request Review

@github-actions github-actions Bot added the risk: medium PR risk score: medium label Nov 7, 2026
BYK added a commit to getsentry/sentry that referenced this pull request Nov 7, 2026
The OpenAPI schema for `PUT /organizations/{org}/issues/{issue_id}/`
(`updateOrganizationIssue`) and `PUT /organizations/{org}/issues/`
(`updateOrganizationIssues`) listed every `GroupValidator` field, and
every `statusDetails` field, as required. The endpoints always validate
with `partial=True` (`validate_request` in
`api/helpers/group_index/update.py`), so a request only sends the fields
it changes, for example `{"status": "resolved"}`.

This marks those fields `required=False`. Validation doesn't change,
because `partial=True` already skipped the required check. Only the two
operations above change in the generated schema. Their request body
schemas lose the `required` lists, and the body itself is no longer
marked required.

The Sentry CLI needs this to move its issue update calls onto the
generated `@sentry/api` operations (getsentry/toolkit#2073). Today the
generated body type makes every field mandatory, so a partial update
doesn't typecheck.

Checked locally with `make build-api-docs`, `pnpm run
validate-api-examples`, `tests/apidocs`,
`tests/sentry/issues/endpoints/test_group_details.py`, `GroupUpdateTest`
in `test_organization_group_index.py`,
`tests/sentry/api/helpers/test_group_index.py`, prek and mypy. Under
xdist, three snuba-backed `GroupUpdateTest` cases failed:
`test_resolve_with_integration`, `test_snooze_user_count` and
`test_bulk_resolve`. Run serially, they pass on this branch and on
master.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <[email protected]>
BYK added a commit to getsentry/sentry that referenced this pull request Nov 7, 2026
…estamp (#130349)

Three query parameters on public Explore endpoints were documented
differently from what the code accepts:

- **`yAxis`** on `listOrganizationEventsTimeseries` was a single string.
The endpoint reads `request.GET.getlist("yAxis")` and returns one
timeseries per aggregate, so it's now an array (repeated
`?yAxis=count()&yAxis=p95(span.duration)`).
- **`interval`** (`VisibilityParams.INTERVAL`, used by
`listOrganizationEventsTimeseries` and `listOrganizationEventsDropped`)
was an integer. `get_rollup_from_request` parses it with
`parse_stats_period`, which also takes a unit suffix such as `5m` or
`1h`. It's now `anyOf: [integer, string matching ^\d+[smhdw]?$]`, so
callers that pass a number keep working.
- **`timestamp`** on `getOrganizationTrace` and
`getOrganizationTraceMeta` wasn't listed. Both endpoints pass it to
`update_snuba_params_with_timestamp`, which accepts ISO 8601 or Unix
seconds and narrows the query window. I added it as
`TraceParams.TIMESTAMP`.

Only those four operations change in the generated schema. There's no
runtime change.

The Sentry CLI needs these to move its timeseries and trace calls onto
the generated `@sentry/api` operations (getsentry/toolkit#2073). Right
now the generated types reject repeated `yAxis`, `interval=1h` and
`timestamp`. One thing to note for SDK consumers: the generated `yAxis`
type changes from `string` to `string[]`.

Checked locally with `make build-api-docs`, `pnpm run
validate-api-examples`, `tests/apidocs`, prek and mypy. I also ran
`tests/snuba/api/endpoints/test_organization_trace_meta.py` and
`test_organization_events_timeseries.py::test_simple`. Under xdist those
snuba tests miss rows from shared state, but run serially they pass.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <[email protected]>
BYK added a commit to getsentry/sentry that referenced this pull request Nov 7, 2026
…0350)

`GET /organizations/{org}/agents/conversations/`
(`listOrganizationAIConversations`) accepts a `sort` query param.
`OrganizationAIConversationsSerializer` validates it against
`AI_CONVERSATIONS_FIELDS`, and it defaults to `-conversation.age`, but
the OpenAPI docs didn't list it. This adds the parameter with an enum of
the canonical fields, each with an optional `-` prefix. Only this
operation changes in the generated schema, and there's no runtime
change.

The enum comes from a new `AI_CONVERSATIONS_SORT_FIELDS` tuple. It takes
the keys of `AI_CONVERSATIONS_FIELDS` before the legacy spellings are
added, so `messages`, the bare field names and the function aliases stay
accepted but undocumented.

The Sentry CLI's `agent-conversation list --sort` needs this to move
onto the generated `@sentry/api` operation (getsentry/toolkit#2073). The
generated query type has no `sort` today.

Checked locally with `make build-api-docs`, `pnpm run
validate-api-examples`,
`tests/sentry/api/endpoints/test_organization_ai_conversations.py`,
`tests/apidocs`, prek and mypy.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <[email protected]>
@sentry-junior
sentry-junior Bot added this pull request to stack #2076 November 7, 2026 01:59
@BYK
BYK enabled stacked auto-merge (squash) November 7, 2026 02:11
@BYK
BYK merged commit 2b34b73 into main Nov 7, 2026
46 checks passed
@BYK
BYK deleted the ref/cli-sentry-api-operations branch November 7, 2026 02:12
BYK added a commit that referenced this pull request Nov 7, 2026
Stacked on #2073. That PR moves the CLI onto the generated `@sentry/api`
operations. This one fixes the cases where the CLI, not the API docs,
was wrong. The full list is in getsentry/sentry#130351. Where the 1.44.0
operation now matches, the call moves onto it here too.

Each fix was checked against the getsentry/sentry endpoint code.

### Moved onto generated operations
| Area | Fix | Operation |
| --- | --- | --- |
| `alert issues list/view` | Filter workflows by `project` (the endpoint
ignores `projectSlug`) and send `id` as a number |
`listOrganizationWorkflows` (×2) |
| `code-mappings upload` | Send the bulk body in snake_case:
`default_branch`, `stack_root`, `source_root` |
`bulkUpsertOrganizationCodeMappings` |
| `explore`, `dashboard view` tables | Type `dataset` as a public Events
dataset. Explore still falls back to `errors`, and unmapped widget types
still render as unsupported | `listOrganizationEvents` (×2) |
| `issue view` and other org-scoped issue lookups | Send only the
`collapse` values the details endpoint reads (`release`, `stats`,
`tags`). It ignored `lifetime`/`filtered`/`unhandled`, and the detail
response keeps those fields either way. The short-ID endpoint still gets
every value | `getOrganizationIssue` |
| `issue view` replay lookup | Send `data_source=errors`. `events` isn't
a value the endpoint accepts | `getOrganizationReplayCount` |
| `issue explain/plan` | Drop `mode=explorer`, which the autofix
endpoint never reads | `startOrganizationIssueAutofix` (×2),
`getOrganizationIssueAutofixState` |

### Fixed, but still on the helper
- **`issue merge`**: now sends `{merge: true}` with numeric IDs.
`updateOrganizationIssues` marks every body field required until
getsentry/sentry#130348 lands.
- **`trace view`**: no longer sends `project` or `limit`, since the
endpoint reads neither. It stays on the helper until
getsentry/sentry#130349 documents `timestamp`. An unknown project in
`trace view org/project/<id>` still fails as before.
- **Dashboard and explore time series**: get the same typed `dataset`,
but stay on the helper until #130349 fixes `yAxis`/`interval`.
- **AI conversations list**: untouched, because it's waiting on
getsentry/sentry#130350 (`sort`).

### Kept on purpose
- **`replay list`** keeps requesting `info_ids` and `warning_ids`. The
server computes them and `replay list --json` prints them, so dropping
them would change the output. `fetchReplayPage` stays on the helper, and
a comment says why.

### Behavior notes
- On the moved calls, errors use the SDK wording already used by the
moved calls in #2073 and by the metric alert path, e.g. `Failed to list
issue alert rules: 403 Forbidden`. They no longer start with `API
request failed:`, and they no longer have an `Endpoint:` line. Status
codes and the 401/403 enrichment are unchanged.
- Existing bug, not fixed here: `trace view org/project/<id>` prints
"Filtered to project", but the trace endpoint has returned spans from
every project since getsentry/sentry#87314.
- Docs gap: with `returnIds=true`, the replay-count response maps each
issue ID to a list of replay IDs. The documented schema describes
counts. The CLI keeps validating that response with its own schema.

### Verification
- `pnpm run tsc` and `pnpm run lint` pass.
- CLI tests: 12884 pass and 5 fail. All 5 are the sandbox-only
permission tests that fail on `main` too (`completions` ×2, `upgrade`
acquireLock, `db/resilient` readonly, `dsn/code-scanner`).
- Unit tests were updated or added for each changed request shape,
including the new `test/lib/api/code-mappings.test.ts`.
- API discovery no longer lists the moved call sites.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
Co-authored-by: BYK <[email protected]>
BYK pushed a commit that referenced this pull request Nov 7, 2026
…2078)

Next step after #2073 and #2075, which moved the CLI onto the generated
`@sentry/api` operations. `@sentry/api` 1.46.0 includes the API docs
fixes from getsentry/sentry#130348, #130349 and #130350, so six more
call sites now match their generated operation. This PR bumps the
package and moves them.

### Moved onto generated operations
| Command(s) | Call | Operation | Unblocked by |
| --- | --- | --- | --- |
| `issue resolve/unresolve/archive`, `feedback resolve/spam/…`
(org-scoped) | `updateIssueStatus` | `updateOrganizationIssue` |
sentry#130348 (body fields no longer all required) |
| `issue merge` | `mergeIssues` | `updateOrganizationIssues` with
numeric `id`s and `{merge: true}` | sentry#130348 |
| `dashboard view` chart widgets | `queryWidgetTimeseries` |
`listOrganizationEventsTimeseries` | sentry#130349 (`yAxis` array,
`interval` int or duration) |
| `explore` time series | `queryEventsTimeSeries` |
`listOrganizationEventsTimeseries` | sentry#130349 |
| `agent-conversation list` | `listConversations` |
`listOrganizationAiConversations` with `sort` | sentry#130350 |
| `trace view`, `span view`, `log view` | `getDetailedTrace` |
`getOrganizationTrace` with `timestamp` | sentry#130349 |

Trace meta already used `getOrganizationTraceMeta` and doesn't send
`timestamp`, so nothing changes there.

### Behavior notes
- Responses are validated exactly as before:
`EventsTimeseriesResponseSchema` for widgets and
`ConversationListItemSchema` for conversations. The explore time series
and trace detail responses were not validated before and still aren't.
- `issue merge` still turns a 204 (no matching issues) into the same
friendly error, and still clears each affected issue's cross-origin
cache.
- `agent-conversation list --sort` is now typed with the endpoint's sort
enum. The command's sort values are a subset of it.
- Errors on the moved calls use the SDK wording, as in #2073 and #2075,
e.g. `Failed to update issue: 403 Forbidden`. Status codes and the
401/403 enrichment are unchanged.
- The 1.46.0 regen also changes `uniqueValues`/`key`/`name` descriptions
in the generated `issue` skill reference, which is committed here.
Typecheck is otherwise unaffected.

### Remaining `apiRequest*` call sites and raw fetches
Details for each are in getsentry/sentry#130351.

**Not in the public schema** (needs a getsentry/sentry API docs
decision):
- `organizations.ts:66`, `projects.ts:576/591`: region lookups
- `users.ts:21`: current user
- `trials.ts:30/70`: trials list/start
- `org-auth-tokens.ts:77`: delete org auth token
- `dashboards.ts:201/235`: dashboard revisions/restore
- `issues.ts:329`: unscoped `GET /issues/{id}/`
- `issues.ts:632`: legacy unscoped `PUT /issues/{id}/`
- `logs.ts:486`: logs query
- `traces.ts:225`: trace-items detail (experimental)
- `releases.ts:425`: release set-commits
- `preprod-artifacts.ts:283/449`: build/snapshot upload
- Raw fetches: `getSharedIssue` (`issues.ts`) and `openSnapshotArchive`
(`preprod-artifacts.ts`)

**Dynamic request identity / transport:**
- `auth.ts:17`
- `org-auth-tokens.ts:37/59`
- `preprod-artifacts.ts:508/526`: snapshot downloads

**Kept on purpose:**
- `replays.ts:201`: `replay list` keeps requesting
`info_ids`/`warning_ids` because `--json` prints them (see #2075).

The Oxlint rule that bans the helpers comes last, after these are
resolved.

### Verification
- `pnpm install --frozen-lockfile` passes with the committed lockfile.
- `pnpm run tsc` and `pnpm run lint` pass.
- CLI tests: 12919 pass and 5 fail. All 5 are the sandbox-only
permission tests that also fail on `main` (`completions` ×2, `upgrade`
acquireLock, `db/resilient` readonly, `dsn/code-scanner`).
- The dashboard time series tests now mock
`listOrganizationEventsTimeseries`. I added an org-scoped
`updateIssueStatus` test. The conversation, explore and trace tests run
at the fetch layer and pass unchanged.
- API discovery no longer lists the moved call sites.

<!-- junior-request-attribution:start -->
via @BYK.
<!-- junior-request-attribution:end -->

<!-- junior-session-footer:start -->
<!-- junior-conversation-id:agent%3A80220b9d4c297e2a971f05dcb03af515 -->

--

[View
Session](https://junior-prod.sentry.dev/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515)
[[Sentry]](https://sentry.sentry.io/explore/conversations/agent%3A80220b9d4c297e2a971f05dcb03af515/?project=4510944073809921)

<!-- junior-session-footer:end -->

Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview — d2dad465 Deployed Nov 7, 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.

1 participant