Skip to content

feat(alerts): implement Opsgenie delivery channel - #550

Closed
DevSolex wants to merge 5 commits into
TegoLabs:mainfrom
DevSolex:feat/alerts-opsgenie-channel
Closed

DevSolex wants to merge 5 commits into
TegoLabs:mainfrom
DevSolex:feat/alerts-opsgenie-channel

Conversation

@DevSolex

Copy link
Copy Markdown
Contributor

Closes #314

Summary

Adds Opsgenie as a sixth built-in alert channel, following the plugin recipe in docs/adding-an-alert-channel.md exactly.

Changes

Created:

  • src/alerts/opsgenie.ts — OpsgenieChannel class + sendOpsgenieAlert function implementing the AlertChannel interface
  • tests/alerts/opsgenie.test.ts — 32 tests, written before the implementation (TDD)

Edited:

  • src/alerts/builtins.ts — one registerAlertChannel call added for opsgenie, nothing else
  • tests/alerts/builtins.test.ts — updated hardcoded channel count (5→6), added opsgenie to the targetOption table, added delegate test, added vi.mock for opsgenie.js

Implementation Notes

  • Create alerts: threshold_crossed, resource_alert, and state_changed events POST to https://api.opsgenie.com/v2/alerts with a GenieKey <apiKey> authorization header
  • Close alerts: alert_resolved events POST to /v2/alerts/{alias}/close instead
  • Dedup alias: buildAlias() mirrors pagerduty.ts's buildDedupKey() exactly — same three-branch logic — so repeated threshold crossings for the same entry map to one Opsgenie alert rather than duplicates
  • API key validation: checked before any network call; throws a clear, actionable "Opsgenie API key is required..." error if empty or whitespace
  • Timeout: uses the same AbortController/10 s timeout pattern as the other built-in channels
  • Registry: targetOption: "routingKey" — users pass their Opsgenie API key via --routing-key

Acceptance Criteria

  • A threshold_crossed event creates an Opsgenie alert via a mocked fetch, asserting the GenieKey header and payload shape
  • An alert_resolved event calls the close-alert endpoint instead of create
  • Missing API key throws a clear, actionable error before the network call

Testing

Test Files  78 passed (78)
     Tests  1029 passed | 1 skipped (1030)

All 32 new tests pass. Full suite clean — no regressions.

Files Not Touched

dispatcher.ts, registry.ts, commands/alerts.ts, db/schema.sql, webhook.ts, slack.ts, discord.ts, telegram.ts, pagerduty.ts — as required by the issue scope.

@drips-wave

drips-wave Bot commented Jul 29, 2026

Copy link
Copy Markdown

@DevSolex Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@coderabbitai

coderabbitai Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an Opsgenie alert channel with authenticated creation and resolution requests, event-specific payloads and deduplication aliases, built-in registration using a routing key, and comprehensive tests.

Changes

Opsgenie delivery channel

Layer / File(s) Summary
Payload construction and HTTP transport
src/alerts/opsgenie.ts
Maps alert types and severities to Opsgenie messages, details, priorities, aliases, and authenticated HTTP requests.
Create and resolve flow
src/alerts/opsgenie.ts
Validates API keys, creates alerts for active events, closes resolved alerts, and exposes the sender wrapper.
Registration and test coverage
src/alerts/builtins.ts, tests/alerts/*
Registers the opsgenie channel with a routingKey target and tests registration, delegation, payloads, aliases, resolution, timeout, and errors.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AlertDispatcher
  participant OpsgenieChannel
  participant OpsgenieAPI
  AlertDispatcher->>OpsgenieChannel: send(AlertEvent)
  OpsgenieChannel->>OpsgenieAPI: POST create or close request
  OpsgenieAPI-->>OpsgenieChannel: HTTP response
  OpsgenieChannel-->>AlertDispatcher: delivery result
Loading

Possibly related PRs

Suggested reviewers: abdulmalikalayande

Poem

I’m a rabbit routing alerts through the night,
With aliases tidy and priorities right.
Thresholds hop in, resolved ones close,
Secure keys guide each message that goes.
Opsgenie joins the alerting parade!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an Opsgenie delivery channel.
Description check ✅ Passed The description is directly about the Opsgenie alert channel and its tests, so it matches the changeset.
Linked Issues check ✅ Passed The changes align with #314 by adding the new channel, registering it, and covering create/close flows plus API-key validation.
Out of Scope Changes check ✅ Passed The summary shows only the expected Opsgenie implementation and related tests, with no unrelated file changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/alerts/opsgenie.ts`:
- Around line 107-129: Update postJson to read the Opsgenie response body when
response.ok is false and include its diagnostic message or errors in the thrown
error alongside the HTTP status. Preserve the existing successful-response
behavior and timeout cleanup.
- Around line 151-159: Update the close URL in the alert_resolved branch to
include Opsgenie’s identifierType=alias query parameter while retaining the
encoded alias route segment, and adjust the close endpoint test to expect the
query-param URL.

In `@tests/alerts/builtins.test.ts`:
- Around line 100-104: Extend the existing “each missingTargetError message
matches the historical CLI wording” test in builtins.test.ts to include the
Opsgenie channel’s exact missingTargetError string. Locate the Opsgenie
definition via getAlertChannel("opsgenie") or the related test setup, and assert
the wording requiring --routing-key and referencing the Opsgenie API key.

In `@tests/alerts/opsgenie.test.ts`:
- Around line 426-458: Add a test in the “error handling” suite for
sendOpsgenieAlert that uses fake timers and a pending mockFetch observing
options.signal, advances time by 10 seconds, and verifies the request aborts and
the promise rejects. Restore real timers reliably after the assertion so the
test does not affect other cases.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 1f8a40ea-3ce2-4b06-9c62-585f8aa6270e

📥 Commits

Reviewing files that changed from the base of the PR and between 35d9237 and 0505bf5.

📒 Files selected for processing (4)
  • src/alerts/builtins.ts
  • src/alerts/opsgenie.ts
  • tests/alerts/builtins.test.ts
  • tests/alerts/opsgenie.test.ts
📜 Review details
🔇 Additional comments (7)
src/alerts/opsgenie.ts (4)

13-17: LGTM on priority mapping.

Straightforward severity → priority mapping with a safe P3 default; matches the tested behavior for critical, warning, and info.


27-37: LGTM!


41-103: LGTM!


133-180: LGTM!

src/alerts/builtins.ts (1)

5-5: LGTM!

Also applies to: 68-74

tests/alerts/builtins.test.ts (1)

10-10: LGTM!

Also applies to: 32-34, 47-55, 70-70

tests/alerts/opsgenie.test.ts (1)

1-425: LGTM!

Comment thread src/alerts/opsgenie.ts
Comment thread tests/alerts/builtins.test.ts
Comment thread tests/alerts/opsgenie.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/alerts/opsgenie.test.ts`:
- Around line 480-498: Update the “aborts the request after the 10-second
timeout” test to wrap its fake-timer setup, request, timer advancement, and
assertion in try/finally, and call vi.useRealTimers() in the finally block so
real timers are restored even when the test fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: dd9d2280-57d6-4909-a311-8abd86d280d6

📥 Commits

Reviewing files that changed from the base of the PR and between 0505bf5 and 252091d.

📒 Files selected for processing (3)
  • src/alerts/opsgenie.ts
  • tests/alerts/builtins.test.ts
  • tests/alerts/opsgenie.test.ts
📜 Review details
🔇 Additional comments (3)
src/alerts/opsgenie.ts (1)

127-134: LGTM!

Also applies to: 159-162

tests/alerts/builtins.test.ts (1)

47-55: LGTM!

Also applies to: 100-103, 121-123

tests/alerts/opsgenie.test.ts (1)

275-284: LGTM!

Also applies to: 446-456

Comment thread tests/alerts/opsgenie.test.ts Outdated
@DevSolex
DevSolex force-pushed the feat/alerts-opsgenie-channel branch from 252091d to 68a6543 Compare July 29, 2026 07:05
@coderabbitai coderabbitai Bot mentioned this pull request Jul 29, 2026
8 of 11 tasks
DevSolex added 3 commits July 29, 2026 09:39
Closes TegoLabs#314

- Add src/alerts/opsgenie.ts with OpsgenieChannel class and
  sendOpsgenieAlert function following the AlertChannel interface
- POST to /v2/alerts with GenieKey authorization header for
  threshold_crossed, resource_alert, and state_changed events
- Map alert_resolved to POST /v2/alerts/{alias}/close endpoint
- buildAlias() mirrors pagerduty.ts buildDedupKey() exactly so
  repeated threshold crossings for the same entry deduplicate
- Validate API key before any network call with a clear error message
- Use AbortController/timeout pattern consistent with other channels
- Register in builtins.ts with targetOption: routingKey
- Add 32 tests in tests/alerts/opsgenie.test.ts (TDD, written first)
- Update tests/alerts/builtins.test.ts for the new 6-channel count
- Add ?identifierType=alias to the close URL so Opsgenie looks up the
  alert by alias rather than ID (critical: without this, close silently
  finds nothing since the default identifierType is 'id')
- Surface Opsgenie response body in error messages for faster debugging
- Test that identifierType=alias is present in the close URL
- Test that error body message is included in the thrown error
- Test AbortController timeout fires after 10 seconds
- Assert opsgenie missingTargetError wording in builtins test
Ensures vi.useRealTimers() is always called even if the test assertion
fails, preventing timer pollution affecting subsequent tests.
@DevSolex
DevSolex force-pushed the feat/alerts-opsgenie-channel branch from 690866f to b20431a Compare July 29, 2026 08:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/alerts/opsgenie.ts`:
- Around line 107-136: Update postJson to distinguish AbortError caused by the
request timeout from other fetch failures, rethrowing a clear message that
includes the configured timeout duration (10 seconds). Preserve propagation of
non-timeout errors, and update the timeout assertion in the Opsgenie tests to
match the new message.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7eaaec91-59ff-48c2-a09b-f3d1400c10ac

📥 Commits

Reviewing files that changed from the base of the PR and between 252091d and 690866f.

📒 Files selected for processing (4)
  • src/alerts/builtins.ts
  • src/alerts/opsgenie.ts
  • tests/alerts/builtins.test.ts
  • tests/alerts/opsgenie.test.ts
📜 Review details
🔇 Additional comments (6)
src/alerts/opsgenie.ts (3)

1-103: LGTM!


140-184: LGTM!


186-189: LGTM!

src/alerts/builtins.ts (1)

5-5: LGTM!

Also applies to: 68-74

tests/alerts/builtins.test.ts (1)

10-10: LGTM!

Also applies to: 32-34, 47-55, 70-70, 100-104, 121-123

tests/alerts/opsgenie.test.ts (1)

1-503: LGTM!

Comment thread src/alerts/opsgenie.ts
Distinguish AbortError (our own 10-second timeout) from other fetch
failures by catching it explicitly and rethrowing with a message that
names the configured timeout duration:

  'Opsgenie API request timed out after 10 seconds'

Other network errors (ECONNREFUSED etc.) are rethrown unchanged.
Update the timeout test assertion to match the new message.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/alerts/opsgenie.ts`:
- Around line 150-158: Update the Opsgenie client constructor to store the
trimmed API key after validation, using the same normalized value for the
Authorization header and preserving rejection of empty keys.

In `@tests/alerts/opsgenie.test.ts`:
- Around line 248-261: Remove the initial alert_resolved sendOpsgenieAlert call
and its associated mock reset from the “sets priority P3 for info severity”
test; keep only the resource-event invocation, response setup, and priority
assertion using sendOpsgenieAlert.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9f2c2131-dd7f-404e-abe2-9a8174945629

📥 Commits

Reviewing files that changed from the base of the PR and between 690866f and 2111819.

📒 Files selected for processing (4)
  • src/alerts/builtins.ts
  • src/alerts/opsgenie.ts
  • tests/alerts/builtins.test.ts
  • tests/alerts/opsgenie.test.ts
📜 Review details
🔇 Additional comments (7)
src/alerts/opsgenie.ts (4)

13-17: LGTM!


160-190: LGTM!


107-131: 🩺 Stability & Availability

Run this review in a checkout where src/alerts/opsgenie.ts is available.

The repository could not be cloned in this environment, so the code path cannot be inspected or the proposed clearTimeout/AbortError changes validated here.


27-37: 🩺 Stability & Availability

No change needed.

alert_resolved events include the threshold.configuredLedgers field, so buildAlias can reuse the same close-time alias as the create-time threshold alias.

src/alerts/builtins.ts (1)

5-5: LGTM!

Also applies to: 68-74

tests/alerts/builtins.test.ts (1)

10-10: LGTM!

Also applies to: 32-34, 47-55, 70-70, 100-103, 121-123

tests/alerts/opsgenie.test.ts (1)

480-501: LGTM!

Comment thread src/alerts/opsgenie.ts
Comment thread tests/alerts/opsgenie.test.ts
@gitguardian

gitguardian Bot commented Jul 29, 2026 •

Copy link
Copy Markdown

️✅ There are no secrets present in this pull request anymore.

If these secrets were true positive and are still valid, we highly recommend you to revoke them.
While these secrets were previously flagged, we no longer have a reference to the
specific commits where they were detected. Once a secret has been leaked into a git
repository, you should consider it compromised, even if it was deleted immediately.
Find here more information about risks.


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

- Store trimmed API key in constructor so leading/trailing whitespace
  is not forwarded in the GenieKey Authorization header
- Simplify 'sets priority P3 for info severity' test: remove the
  redundant alert_resolved call and mock reset; test directly with
  a resource event at info severity
AbdulmalikAlayande added a commit that referenced this pull request Jul 30, 2026
)

Small maintainer fixup on top of #550's Opsgenie channel: the timeout
error was thrown without a cause chain, tripping preserve-caught-error.
@AbdulmalikAlayande

Copy link
Copy Markdown
Collaborator

Merged manually as d3f16f2 on main — your Opsgenie channel implementation, plus a one-line lint fixup on top (attaching cause to the timeout error to satisfy preserve-caught-error). All verified locally: lint, typecheck, full test suite (1032/1032), build, and npm audit --omit=dev all clean. Thanks for the clean implementation and for handling the CodeRabbit review rounds properly. Closing #314 as shipped.

AbdulmalikAlayande added a commit that referenced this pull request Aug 2, 2026
)

Small maintainer fixup on top of #550's Opsgenie channel: the timeout
error was thrown without a cause chain, tripping preserve-caught-error.
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.

feat(alerts): implement Opsgenie delivery channel

2 participants