Skip to content

feat: add comments on alerts - #4533

Open
JRemitz wants to merge 1 commit into
target:masterfrom
Radico:upstream/alert-comments
Open

JRemitz wants to merge 1 commit into
target:masterfrom
Radico:upstream/alert-comments

Conversation

@JRemitz

@JRemitz JRemitz commented Jul 29, 2026

Copy link
Copy Markdown
image
  • Identified the issue which this PR solves.
  • Read the CONTRIBUTING document.
  • Code builds clean without any errors or warnings.
  • Added appropriate tests for any new functionality.
  • All new and existing tests passed.
  • Added comments in the code, where necessary.
  • Ran make check to catch common errors. Fixed any that came up.

Description:

Adds free-form comments on alerts, recording who wrote what and when. Any signed-in user can comment on any alert.

The motivation is that triage context currently has nowhere to live. The alert log is a record of what GoAlert did — created, escalated, notified — so there is no place for a person to note what they tried, what they found, or who they handed it to. That context ends up in chat and is lost once the alert closes.

Comments are deliberately inert with respect to alerting: commenting requires no authorization beyond being a user, and never changes alert state, escalation, or notification routing.

Design notes

Cleanup is handled by a foreign key rather than a new job:

alert_id BIGINT NOT NULL REFERENCES alerts(id) ON DELETE CASCADE

Alert cleanup is a plain DELETE FROM alerts, so comments are removed along with their alert and cannot accumulate. This is deliberately unlike alert_logs, which has no foreign key to alerts and therefore needs its own separate cleanup pass to avoid orphans.

The author FK is ON DELETE SET NULL rather than CASCADE, so deleting a user does not erase an alert's comment history — the text and timestamp survive, and only the attribution is lost (rendered as "Deleted user").

Bodies render through the existing Markdown component, the same one alert details use, so links are clickable and formatting works without adding a dependency.

Which issue(s) this PR fixes:

Fixes #3549

Out of Scope:

  • Editing an existing comment. Users can delete their own comments (admins can delete any), but there is no edit flow.
  • Comments are not included in outgoing webhook payloads (POSTDataAlert / POSTDataAlertStatus). Happy to add this if wanted — it seemed better raised separately, since that payload is a public contract and a comment does not currently trigger a status notification.
  • No notification is sent when someone comments.

Describe any introduced user-facing changes:

A "Comments" card on the alert details page, between the escalation policy and the event log. It lists existing comments — author, relative timestamp, body — and provides a box to add one. It carries the same "Full Timestamps" toggle as the event log, sharing that preference with it.

Comments are capped at 4096 characters, enforced both in Go and by a CHECK constraint.

Describe any introduced API changes:

All additive.

type AlertComment {
  id: ID!
  user: User        # null if the author's account was deleted
  body: String!
  createdAt: ISOTimestamp!
}

type Alert {
  comments: [AlertComment!]!   # new field
}

input AddAlertCommentInput {
  alertID: Int!
  body: String!
}

type Mutation {
  addAlertComment(input: AddAlertCommentInput!): AlertComment!
  deleteAlertComment(id: ID!): Boolean!
}

Alert.comments is batched through a dataloader to avoid an N+1 when listing alerts.

New migration 20260728115032-add-alert-comments.sql creates the alert_comments table. No engine_processing_versions change — no engine behavior is affected, so old and new nodes coexist during a rolling deploy. The Down migration drops only what it added.

Additional Info:

Tests — test/smoke/alertcomments_test.go (5 smoke tests):

  • create and list, asserting author, timestamp and body
  • validation: empty, whitespace-only and oversized bodies are rejected; surrounding whitespace is trimmed
  • delete permissions: authors can delete their own comments, other users cannot
  • cascade on cleanup: runs the real cleanup job and then asserts against the alert_comments table directly via SQL, since once the alert is deleted the comments are unreachable through the API whether or not they were actually removed
  • author deletion: the comment survives, attribution does not

Plus 5 Cypress cases in web/src/cypress/e2e/alerts.cy.ts covering add, markdown rendering, link rendering, the timestamp toggle, and empty-comment rejection.

On sanitization: comment bodies are untrusted input, so to be explicit about why rendering Markdown is safe here — React escapes interpolated text, the Markdown component does not enable rehype-raw (so raw HTML is displayed rather than mounted), and its allowElement hook runs hrefs through safeURL. One of the Cypress cases posts an <img onerror> payload and asserts no img element is created.

Note on make check: golangci-lint has no darwin/arm64 release asset, so I ran the pinned version (v2.12.2) in a container against this branch — 0 issues. make check-js passes. Three pre-existing Jest failures in web/src/app/util and web/src/app/schedules are locale-formatting assertions in files this branch does not touch.

Fixes target#3549

Adds free-form, user-authored comments to alerts, recording who said what and
when. Any signed-in user may comment on any alert.

Comments capture triage context that does not belong in the system-generated
event log: what someone tried, what they found, who they handed it to. The
event log stays a record of what GoAlert did.

Cleanup is handled by the database rather than by another job. Alert cleanup is
a plain `DELETE FROM alerts`, so a foreign key with ON DELETE CASCADE removes an
alert's comments along with it. This is deliberately unlike alert_logs, which
has no foreign key to alerts and therefore needs its own separate cleanup pass
to avoid orphans. TestAlertCommentsCleanupCascade runs the real cleanup job and
then asserts against the alert_comments table directly, since after the alert is
gone the comments are unreachable through the API either way.

The author FK is ON DELETE SET NULL rather than CASCADE: deleting a user must
not erase the comment history on an alert. The text and timestamp survive and
only the attribution is lost, which the UI renders as "Deleted user".

Bodies render through the existing Markdown component, the same one alert
details use, so links are clickable and formatting works without adding a
dependency. That does not weaken sanitization: React escapes interpolated text,
the component never enables rehype-raw so raw HTML is displayed rather than
mounted, and its allowElement hook runs hrefs through safeURL. A Cypress case
posts an <img onerror> payload and asserts no img element is created.

Users may delete their own comments; admins may delete any. A delete that
matches nothing reports "comment not found" rather than distinguishing missing
from not-yours, so comment IDs cannot be probed.

Tests: 5 smoke tests in test/smoke/alertcomments_test.go covering create/list,
validation, delete permissions, cascade-on-cleanup and author-deletion
retention, plus 5 Cypress cases.

Signed-off-by: Jake Remitz <jremitz@simondata.com>
@bing-goodnotes

Copy link
Copy Markdown

Very useful feature, hope we can have this in a near future :prey:

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.

Comment box for each Alert

2 participants