Skip to content

fix(alert): guard against null alert/dateCreated in email alert template - #6457

Open
wy471x wants to merge 20 commits into
apache:masterfrom
wy471x:fix_EmailAlertReportFail
Open

fix(alert): guard against null alert/dateCreated in email alert template#6457
wy471x wants to merge 20 commits into
apache:masterfrom
wy471x:fix_EmailAlertReportFail

Conversation

@wy471x

@wy471x wy471x commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Move null check before alert dereferencing in EmailAlertNotifyStrategy to prevent NullPointerException. Add @notblank validation on AlertReportRequest title and content fields. Add field by field mapping from AlertReportRequest to AlertContent in the AlertReportController. Add unit tests for these fixes.

Make sure that:

  • You have read the contribution guidelines.
  • You submit test cases (unit or integration tests) that back your changes.
  • Your local test passed ./mvnw clean install -Dmaven.javadoc.skip=true.

Summary of Changes

  1. AlertReportRequest.java (new, shenyu-admin) — Controller-layer request DTO with @notblank on title and content. Validation only applies to external HTTP requests to
    /alert/report, not to internal AlarmContent users.

  2. AlertReportController.java — Changed from @Valid @RequestBody AlarmContent to @Valid @RequestBody AlertReportRequest, with explicit field-by-field mapping into AlarmContent
    before dispatch.

  3. EmailAlertNotifyStrategy.java — Guards dateCreated == null with a fallback to new Date().

  4. AlarmContentTest.java (new) — Builder and getter/setter test for AlarmContent.

  5. EmailAlertNotifyStrategyTest.java (new) — Tests that buildAlertHtmlTemplate does not throw on null dateCreated or null content.

  6. AlertReportControllerTest.java (new) — MockMvc tests covering blank/null title → 400, blank/null content → 400, and valid request → 200 with correct field mapping to
    dispatchAlert.

close #6446

@wy471x wy471x changed the title fix(alert): guard against null alert/dateCreated in email alert template (#xxx) fix(alert): guard against null alert/dateCreated in email alert template Jul 26, 2026
@wy471x
wy471x force-pushed the fix_EmailAlertReportFail branch from bb7c523 to cbe08cf Compare July 26, 2026 11:53
@Aias00

Aias00 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

I found one regression risk outside the email-template null guard.

Adding @NotBlank to AlarmContent.title and AlarmContent.content changes runtime behavior because AlertReportController already receives @Valid @RequestBody AlarmContent. After this change, null/blank title or content is rejected with a validation error before alert dispatch.

That can drop existing alerts. For example, the gateway error flow can build an alert from an exception message; an IllegalArgumentException can have a null message, and AlarmSender copies that value into AlarmContent.content. Before this PR the alert could still reach dispatch and the email strategy could render it defensively. With the new shared DTO constraints, the request is rejected at /alert/report before any notify strategy sees it.

Could we keep the email alert == null / dateCreated == null guard, but remove the new @NotBlank constraints and the added jakarta.validation-api dependency from shenyu-common? If stricter validation is desired for external report requests, it would be safer to put it on a controller-layer request DTO and map into AlarmContent explicitly.

wy471x and others added 4 commits July 31, 2026 15:00
…yer DTO

Remove @notblank from AlarmContent (shared DTO used by gateway AlarmSender)
and add a separate AlertReportRequest DTO at the controller layer instead.
This prevents gateway-generated alerts with null content (e.g. from
IllegalArgumentException with no message) from being silently dropped by
Spring @Valid rejection before dispatch.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@wy471x

wy471x commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

I found one regression risk outside the email-template null guard.

Adding @NotBlank to AlarmContent.title and AlarmContent.content changes runtime behavior because AlertReportController already receives @Valid @RequestBody AlarmContent. After this change, null/blank title or content is rejected with a validation error before alert dispatch.

That can drop existing alerts. For example, the gateway error flow can build an alert from an exception message; an IllegalArgumentException can have a null message, and AlarmSender copies that value into AlarmContent.content. Before this PR the alert could still reach dispatch and the email strategy could render it defensively. With the new shared DTO constraints, the request is rejected at /alert/report before any notify strategy sees it.

Could we keep the email alert == null / dateCreated == null guard, but remove the new @NotBlank constraints and the added jakarta.validation-api dependency from shenyu-common? If stricter validation is desired for external report requests, it would be safer to put it on a controller-layer request DTO and map into AlarmContent explicitly.

@Aias00 Hi, I have fixed the issues you mentioned. Please take a look when you have time.

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed #6457. The core fix is sound: EmailAlertNotifyStrategy.buildAlertHtmlTemplate previously dereferenced alert.getContent()/alert.getDateCreated() before the dead Objects.isNull(alert) guard; the new ordering with dateCreated == null → new Date() is correct and is reachable via the HTTP report path (a caller omitting dateCreated). EmailAlertNotifyStrategyTest.testNullDateCreatedShouldNotThrowNpe backs that path. Moving @NotBlank from the shared AlarmContent DTO to a controller-layer AlertReportRequest is the right call — it avoids silently dropping gateway-generated alerts (which bypass the controller via AlarmSenderAlertDispatchService.dispatchAlert) when content is null.

Non-blocking issues worth addressing before/after merge:

  1. PR description is stale. The body still claims @NotBlank was added to AlarmContent, that jakarta.validation-api was added to shenyu-common/pom.xml, and that AlarmContentTest verifies @NotBlank. None of that is true in the final diff — AlarmContent is unmodified, the only pom change is a trailing-whitespace trim, and AlarmContentTest only tests builder/setters. Please rewrite the body to match the merged approach (controller-layer AlertReportRequest).

  2. No test backs the new validation or the request→AlarmContent mapping. AlertReportController.reportAlert now manually copies 7 fields and relies on @NotBlank, but there is no AlertReportControllerTest (the repo has many *ControllerTest examples). A MockMvc test asserting (a) blank title/body → 400 with the validation message and (b) a valid request maps through to dispatchAlert with the right fields would close the gap.

  3. shenyu-common/pom.xml whitespace-only hunk — drop it; it serves no purpose in the final approach.

  4. Minor: the alert == null branch in buildAlertHtmlTemplate is unreachable (AlertDispatchServiceImpl.DispatchTask already guards Objects.nonNull(alert) before invoking handlers), while DingTalkRobotAlertNotifyStrategy.send() still dereferences alert.getTitle() unguarded. Either apply the guard consistently or drop the unreachable alert == null part and keep only the real dateCreated == null fix.

CI note: it / shenyu-integrated-test-combination failed (other it jobs cancelled); ci and e2e are green. Please confirm the combination failure is unrelated/flaky before merging.

wy471x and others added 2 commits August 2, 2026 20:40
…ion test

Remove the dead alert==null check in EmailAlertNotifyStrategy since
DispatchTask.run() already guarantees non-null alerts upstream.
Add AlertReportControllerTest covering @notblank validation rejections
and correct AlertReportRequest→AlarmContent mapping.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@wy471x

wy471x commented Aug 2, 2026

Copy link
Copy Markdown
Contributor Author

Reviewed #6457. The core fix is sound: EmailAlertNotifyStrategy.buildAlertHtmlTemplate previously dereferenced alert.getContent()/alert.getDateCreated() before the dead Objects.isNull(alert) guard; the new ordering with dateCreated == null → new Date() is correct and is reachable via the HTTP report path (a caller omitting dateCreated). EmailAlertNotifyStrategyTest.testNullDateCreatedShouldNotThrowNpe backs that path. Moving @NotBlank from the shared AlarmContent DTO to a controller-layer AlertReportRequest is the right call — it avoids silently dropping gateway-generated alerts (which bypass the controller via AlarmSenderAlertDispatchService.dispatchAlert) when content is null.

Non-blocking issues worth addressing before/after merge:

  1. PR description is stale. The body still claims @NotBlank was added to AlarmContent, that jakarta.validation-api was added to shenyu-common/pom.xml, and that AlarmContentTest verifies @NotBlank. None of that is true in the final diff — AlarmContent is unmodified, the only pom change is a trailing-whitespace trim, and AlarmContentTest only tests builder/setters. Please rewrite the body to match the merged approach (controller-layer AlertReportRequest).
  2. No test backs the new validation or the request→AlarmContent mapping. AlertReportController.reportAlert now manually copies 7 fields and relies on @NotBlank, but there is no AlertReportControllerTest (the repo has many *ControllerTest examples). A MockMvc test asserting (a) blank title/body → 400 with the validation message and (b) a valid request maps through to dispatchAlert with the right fields would close the gap.
  3. shenyu-common/pom.xml whitespace-only hunk — drop it; it serves no purpose in the final approach.
  4. Minor: the alert == null branch in buildAlertHtmlTemplate is unreachable (AlertDispatchServiceImpl.DispatchTask already guards Objects.nonNull(alert) before invoking handlers), while DingTalkRobotAlertNotifyStrategy.send() still dereferences alert.getTitle() unguarded. Either apply the guard consistently or drop the unreachable alert == null part and keep only the real dateCreated == null fix.

CI note: it / shenyu-integrated-test-combination failed (other it jobs cancelled); ci and e2e are green. Please confirm the combination failure is unrelated/flaky before merging.

I have fixed all the issues you mentioned, as well as the CI problems. I have already verified that they pass in my own fork repository. Thank you for the code review.

@Aias00

Aias00 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Good fix — the old null guard was dead code: Date alertTime = alert.getDateCreated(); if (Objects.isNull(alert)) { alertTime = new Date(); } checked alert (the parameter) for null after already calling alert.getDateCreated(), so a null alert would NPE before reaching the check, and a null getDateCreated() on a non-null alert was never replaced. The replacement Objects.isNull(alert.getDateCreated()) ? new Date() : alert.getDateCreated() correctly guards the right thing.

Moving validation into a dedicated AlertReportRequest DTO with @NotBlank on title/content is the right call — it constrains the HTTP boundary without affecting internal callers that build AlarmContent directly. Tests cover the 400 paths (blank/null title + content) and the NPE guard for null dateCreated, plus a valid-dispatch assertion via ArgumentCaptor.

Two minor notes:

  • shenyu-common/pom.xml has a cosmetic whitespace-only change (a blank line removed before </dependencies>) that looks unintentional — fine to drop or keep, just flagging.
  • The /alert/report endpoint's auth posture is unchanged by this PR (validation improved, but anyone who could post before still can). Out of scope here, just noting it's a pre-existing consideration.

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.

[BUG] Email alert reporting can fail with NPE for null or partial AlarmContent

2 participants