Skip to content

fix(alerts): read issue alert rules from org-scoped /workflows/ - #1215

Merged
betegon merged 1 commit into
mainfrom
fix/cli-issue-alerts-workflows
Jul 9, 2026
Merged

betegon merged 1 commit into
mainfrom
fix/cli-issue-alerts-workflows

Conversation

@betegon

@betegon betegon commented Jul 9, 2026

Copy link
Copy Markdown
Member

What & why

The project-scoped /projects/{org}/{project}/rules/ endpoint was deprecated on 2026-05-14 and now returns HTTP 410 during recurring brownouts (every ~2h), breaking sentry alert issues list and view for users. Fixes the read path per getsentry/toolkit#1482.

Change

Migrate the read functions in src/lib/api/alerts.ts to the org-scoped /organizations/{org}/workflows/ endpoint, mirroring the proven sentry-mcp implementation:

  • listIssueAlertsPaginated → GET /organizations/{org}/workflows/?projectSlug={project}&sortBy=-id
  • getIssueAlertRule → GET /organizations/{org}/workflows/?projectSlug={project}&id={id}&per_page=1, throwing a 404 ApiError when no attached rule matches (keeps isNotFoundApiError resolution working).

The workflows endpoint returns rule-shaped payloads, so the existing IssueAlertRule type is preserved. Added detectorIds to the type and a filterAttachedIssueAlertRules helper to drop unattached org-level workflows (a projectSlug-filtered response can include them).

Scope

Reads only. Mutations (create/edit/delete) still use the legacy endpoint and are a tracked follow-up — the workflows create/update body shape differs and has no MCP reference yet. getReplayRecordingSegments / getTraceItemDetail are out of scope (not deprecated; blocked on getsentry/sentry#118963).

Test plan

  • tsc --noEmit + biome check: clean.
  • Unit: alert/api/command suites pass; added HTTP-level tests for the workflows list, single-get, and the 404 path; updated list.test.ts mocks to the workflows endpoint with detectorIds.
  • Live (real org, branch source): alert issues list returns attached rules; alert issues view <id> returns the rule; a bogus id yields Issue alert rule '…' not found — no 410 exposure.

Refs getsentry/toolkit#1482

The project-scoped `/projects/{org}/{project}/rules/` endpoint was deprecated on
2026-05-14 and now returns HTTP 410 during recurring brownouts, breaking `sentry
alert issues list` and `view` (getsentry/cli#1182).

Migrate the read path to the org-scoped `/organizations/{org}/workflows/` endpoint,
filtered by `projectSlug` (and `id` for single-rule reads), mirroring sentry-mcp.
The endpoint returns rule-shaped payloads, so the existing `IssueAlertRule` shape is
preserved; add `detectorIds` and drop unattached org-level workflows via
`filterAttachedIssueAlertRules`. `getIssueAlertRule` now throws a 404 ApiError when no
attached rule matches, keeping `isNotFoundApiError` resolution working.

Mutations (create/update/delete) still use the legacy endpoint pending a follow-up.

Verified: tsc + biome clean; alert/api/command suites pass (added HTTP-level tests for
the workflows list/get + 404); live `alert issues list` and `view` against a real org
return rules with no 410 exposure.

Refs GH-1182
Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-1215/

Built to branch gh-pages at 2026-07-09 14:52 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Codecov Results 📊

✅ Patch coverage is 100.00%. Project has 5337 uncovered lines.
✅ Project coverage is 81.81%. Comparing base (base) to head (head).

Files with missing lines (1)
File Patch % Lines
src/lib/api/alerts.ts 100.00% ⚠️ 1 partials
Coverage diff
@@            Coverage Diff             @@
##          main       #PR       +/-##
==========================================
+ Coverage    81.80%    81.81%    +0.01%
==========================================
  Files          423       423         —
  Lines        29338     29343        +5
  Branches     19113     19117        +4
==========================================
+ Hits         24000     24006        +6
- Misses        5338      5337        -1
- Partials      1987      1989        +2

Generated by Codecov Action

@betegon
betegon marked this pull request as ready for review July 9, 2026 15:21
@betegon
betegon merged commit 968a0bd into main Jul 9, 2026
31 checks passed
@betegon
betegon deleted the fix/cli-issue-alerts-workflows branch July 9, 2026 15:49
betegon added a commit that referenced this pull request Jul 9, 2026
## What & why

Second slice of migrating issue alerts off the deprecated (HTTP 410
brownout) project-scoped `/projects/{org}/{project}/rules/` endpoint
(#1182). The read path (`list`/`view`) landed in #1215; this migrates
**delete**.

`deleteIssueAlertRule` now issues `DELETE
/organizations/{org}/workflows/{id}/`.

- The id is the **workflow id** already surfaced by the migrated read
path (that's what `list`/`view` show and what `getIssueAlertRule`'s
`?id=` filter matches), and `alert issues delete` resolves the rule
before deleting, so the delete is correctly keyed by id.
- `projectSlug` is kept for signature parity with `getIssueAlertRule`
but is unused (workflow delete is by id; project scoping happens
upstream at resolve time).

## Scope

**Delete only.** Create and edit are a distinct, larger follow-up: the
workflows write endpoint expects a workflow-document body
(`triggers`/`action_filters`/`detector_ids`) with different
condition/action item schemas than the legacy rule identifiers users
pass via `--condition`/`--action` today. Faithfully translating that
client-side is impractical (the backend does it in
`issue_alert_dual_write.py`), and it would be a breaking
`--condition`/`--action` contract change plus a `detector_ids`
resolution step. Tracked on #1182.

## Test plan

- `tsc --noEmit` + `biome check`: clean.
- `vitest run test/lib/api/alerts.test.ts test/commands/alert`: green.
Updated the api-level delete test to assert the workflows DELETE URL;
the command test mocks the api-client function so it is transparent to
the endpoint change.
- Live delete is destructive (needs a real rule), so it's not run in CI;
the api test pins the exact `DELETE
/organizations/{org}/workflows/{id}/` request.

Refs #1182

---------

Co-authored-by: Claude <noreply@anthropic.com>
BYK pushed a commit that referenced this pull request Jul 24, 2026
Migrates the metric-alert **read** path (`list` / `view` / rule
resolution) off the legacy `/organizations/{org}/alert-rules/` endpoint
— which is being retired on **2026-08-17** and 410s during brownouts —
to the new org-scoped `/organizations/{org}/detectors/` endpoint
filtered to `type:metric_issue`. A `mapDetectorToMetricAlertRule` helper
flattens the nested detector payload into the existing `MetricAlertRule`
shape, so command formatters and resolvers work unchanged.

This mirrors the proven issue-alert read migration (#1215/#1216).
Metric-alert **mutations** (`create`/`edit`/`delete`) still use the
legacy endpoint pending the follow-up mutation migration tracked in
#1277 — same transitional state as issue alerts today.

## Testing
- `pnpm run typecheck` — clean
- `pnpm run lint` — clean
- `npx vitest run test/lib/api/alerts.test.ts test/commands/alert` — 150
passing (updated metric-list mocks to `/detectors/`; added list/get +
nested-`snubaQuery` mapping tests)

Refs #1274, #1182

<!--
## Plan
Scope: incremental slice of #1274, matching the established #1215/#1216
pattern
(migrate read path first, defer mutations).

Root cause: metric-alert list/view/get still hit the deprecated
/organizations/{org}/alert-rules/ endpoint, retired 2026-08-17.

Changes (src/lib/api/alerts.ts):
- Add MetricDetector type (only CLI-read fields typed;
dataSources/config opaque).
- Add mapDetectorToMetricAlertRule: id=String(detector.id),
status=enabled===false?1:0,
threshold fields from dataSources[0] with queryObj/snubaQuery fallback,
owner
  flattened, projects/dateCreated defaulted.
- listMetricAlertsPaginated -> GET
/organizations/{org}/detectors/?query=type:metric_issue&sortBy=-id then
map.
- getMetricAlertRule -> GET /organizations/{org}/detectors/{id}/ then
map.
- Leave
fetchMetricAlertRuleJson/getMetricAlertRuleDocument/put*/delete*/create*
on legacy /alert-rules/.

Tests updated: alerts.test.ts (fixture + list/get/snubaQuery mapping),
metrics/list.test.ts
(detector() builder; repoint mocks/regexes/endpoint assertions to
/detectors/).

Known transitional caveat (matches issue-alert precedent): edit resolves
via the migrated
detector read but still PUTs against legacy /alert-rules/{id}/; resolved
by the deferred
mutation migration.
-->

---------

Co-authored-by: jared-outpost[bot] <jared-outpost[bot]@users.noreply.github.com>
jared-outpost Bot added a commit that referenced this pull request Aug 1, 2026
…ows/

Final slice of migrating issue alerts off the deprecated (HTTP 410
brownout) project-scoped /projects/{org}/{project}/rules/ endpoint
(#1182), after read (#1215) and delete (#1216). This migrates create and
edit.

create/edit now go through the @sentry/api SDK (createOrganizationWorkflow
/ updateOrganizationWorkflow), assembling the workflow-document body:
triggers {logicType, conditions}, actionFilters[{logicType, conditions,
actions}], config.frequency, and detectorIds resolved from the project's
error detector. --action-match/--filter-match map to logic types the same
way the backend dual-write does (any -> any-short, all -> all).

The SDK types conditions/actions as unknown[] and its body fields as
snake_case, but the endpoint's CamelSnakeSerializer wants camelCase, so
the body is built as a record and cast into the typed SDK arg (same
double-cast pattern as getProject). No backend change needed.

BREAKING CHANGE: --condition/--action/--filter now take workflow-native
JSON ({type, comparison, conditionResult} / {type, data, config}) instead
of legacy rule identifiers ({id: 'sentry.rules...'}).

Refs #1182
jared-outpost Bot added a commit that referenced this pull request Aug 1, 2026
…ows/ (#1223)

## What & why

Final slice of migrating issue alerts off the deprecated (HTTP 410
brownout) project-scoped `/projects/{org}/{project}/rules/` endpoint
(#1182), after read (#1215) and delete (#1216). This migrates **create**
and **edit**.

`create`/`edit` now go through the `@sentry/api` SDK
(`createOrganizationWorkflow` / `updateOrganizationWorkflow`),
assembling the workflow-document body:

- `triggers: { logicType, conditions }` from `--condition` /
`--action-match`
- `actionFilters: [{ logicType, conditions, actions }]` from `--filter`
/ `--filter-match` / `--action`
- `config.frequency` from `--frequency`
- `detectorIds` resolved from the project's **error detector** (an issue
alert has to attach to it to fire) via
`listOrganizationDetectors(?project=…&query=type:error)`

`--action-match` / `--filter-match` map to `DataConditionGroup` logic
types the same way the backend dual-write does (`any` → `any-short`,
`all` → `all`). `edit` reads the workflow document
(`getOrganizationWorkflow`), applies the flag overrides in workflow
shape (`--status` → `enabled`), and PUTs it back.

The SDK types conditions/actions as `unknown[]` and its body fields as
snake_case, but the endpoint's `CamelSnakeSerializer` wants camelCase —
so the body is built as a record and cast into the typed SDK arg, the
same double-cast pattern as `getProject`. No backend change needed.

## Breaking change

`--condition` / `--action` / `--filter` now take **workflow-native
JSON** (`{type, comparison, conditionResult}` / `{type, data, config}`)
instead of the legacy rule identifiers (`{id: "sentry.rules..."}`). The
`--help` examples are updated accordingly.

## Scope

Read/delete (merged in #1215/#1216) use `apiRequestToRegion`;
create/edit here use the `@sentry/api` SDK + cast, matching the
projects-domain SDK adoption (#1213). Minor inconsistency within
`alerts.ts`, intentional — this is the SDK-adoption direction.

## Test plan

- `tsc --noEmit` + `biome check`: clean.
- `vitest run test/lib/api/alerts.test.ts test/commands/alert`: green
(19 tests). API-level tests pin the exact workflow
create/update/detector requests via `mockFetch`; command tests mock the
api-client so they are transparent to the mechanism.
- **Not yet run live.** The unit tests mock the API, so three things
still need a real create/edit to confirm: (1) the edit read-modify-write
round-trips through the workflows PUT, (2) `config.frequency` unit
(minutes, per the CLI flag), (3) `query=type:error` returns the project
error detector.

Refs #1182

---------

Co-authored-by: jared-outpost[bot] <jared-outpost[bot]@users.noreply.github.com>
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.

1 participant