Skip to content

feat: add email notifications for issue events (#202) - #278

Merged
nazarli-shabnam merged 4 commits into
Devlaner:mainfrom
Jbansal2:feature/email-notifications-202
Jul 8, 2026
Merged

feat: add email notifications for issue events (#202)#278
nazarli-shabnam merged 4 commits into
Devlaner:mainfrom
Jbansal2:feature/email-notifications-202

Conversation

@Jbansal2

@Jbansal2 Jbansal2 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Adds email notifications for issue events by extending the existing notification pipeline.

Changes

  • Added EmailNotificationLog model/store
  • Enqueued notification emails after in-app notifications
  • Reused existing RabbitMQ email infrastructure
  • Logged all notification emails to email_notification_logs
  • Added unit tests for notification email rendering

Supported events

  • assigned
  • mentioned
  • commented
  • state_changed
  • subscribed

Verification

  • Notifications created
  • Email log entries created
  • RabbitMQ enqueue verified
  • Worker processed emails
  • sent_at updated after processing

Summary by CodeRabbit

  • New Features
    • Added email notifications for in-app alerts (assignments, mentions, comments, state changes, and subscriptions) with tailored subject lines and plaintext bodies.
    • When email is configured, messages are queued for delivery and recorded in an email audit log with queue-time timestamps.
  • Bug Fixes
    • Best-effort behavior: email failures won’t affect in-app notifications.
    • Improved template rendering when optional comment previews or old state values are missing.
  • Tests
    • Added unit tests covering subject/body formatting for each notification type and key edge cases.

Adds email notifications for issue events by extending the existing notification pipeline.

### Changes
- Added notification email subject/body templates
- Added EmailNotificationLog model/store
- Enqueued notification emails after in-app notifications
- Reused existing RabbitMQ email infrastructure
- Logged all notification emails to email_notification_logs
- Added unit tests for notification email rendering
@Jbansal2
Jbansal2 requested a review from a team as a code owner July 8, 2026 11:56
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ba180e7-ea86-44d7-a647-ca746b907c9f

📥 Commits

Reviewing files that changed from the base of the PR and between 4aac303 and b33ec6b.

📒 Files selected for processing (3)
  • apps/api/internal/mail/notification.go
  • apps/api/internal/router/router.go
  • apps/api/internal/service/notification.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/api/internal/router/router.go
  • apps/api/internal/mail/notification.go

📝 Walkthrough

Walkthrough

Adds plaintext email notifications for five notification types, persists email audit logs, enqueues email send jobs through existing RabbitMQ wiring, conditionally enables the flow from router setup, and documents the implementation and verification steps.

Changes

Email Notification Pipeline

Layer / File(s) Summary
Email templates and tests
apps/api/internal/mail/notification.go, apps/api/internal/mail/notification_test.go
NotificationEmailData and BuildNotificationEmail generate sender-specific subjects and bodies for assigned, mentioned, commented, state_changed, and subscribed events, with conditional comment preview and value formatting; tests cover all supported cases.
Email log model and store
apps/api/internal/model/email_notification_log.go, apps/api/internal/store/email_notification_log.go
EmailNotificationLog is added with UUID creation and table mapping, and a store provides Create and MarkSent operations for email_notification_logs.
Service enqueue flow and router wiring
apps/api/internal/service/notification.go, apps/api/internal/router/router.go
NotificationService gains optional email log, queue, and base URL fields plus setters; emit() conditionally calls enqueueNotificationEmails to build, log, publish, and mark sent per receiver; router wires the dependencies when cfg.Queue is configured.
Implementation summary documentation
IMPLEMENTATION_SUMMARY.md
Documents the flow, design decisions, configuration requirements, testing steps, and example output.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Poem

A rabbit hops with mail in paw,
Queuing notes without a flaw,
Assigned, mentioned, comments, too,
Logs stay neat and signals queue,
🐇✉️ off to inboxes in a row!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding email notifications for issue events.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
apps/api/internal/service/notification.go (1)

345-347: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Avoid recomputing actorName and projectIdent in enqueueNotificationEmails.

These values are already computed in emit() at lines 290–292. Recomputing them here makes two redundant DB calls (us.GetByID and ps.GetByID) per emit invocation. Pass them as parameters instead.

♻️ Proposed refactor: pass computed values as params
-	if s.queue != nil && s.emailLog != nil && s.appURL != "" {
-		s.enqueueNotificationEmails(ctx, allowed, params)
-	}
+	if s.queue != nil && s.emailLog != nil && s.appURL != "" {
+		s.enqueueNotificationEmails(ctx, allowed, params, actorName, issueRef)
+	}
 func (s *NotificationService) enqueueNotificationEmails(ctx context.Context, receivers []uuid.UUID, params emitParams, actorName, issueRef string) {
 	if params.issue == nil || len(receivers) == 0 {
 		return
 	}
 
-	actorName := s.actorDisplayName(ctx, params.actorID)
-	projectIdent := s.projectIdentifier(ctx, params.issue.ProjectID)
-	issueRef := fmt.Sprintf("%s-%d", projectIdent, params.issue.SequenceID)
 	issueURL := fmt.Sprintf("%s/issue/%s", strings.TrimSuffix(s.appURL, "/"), params.issue.ID)
🤖 Prompt for 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.

In `@apps/api/internal/service/notification.go` around lines 345 - 347, Avoid
recomputing actorName and projectIdent inside enqueueNotificationEmails; they
are already resolved in emit(). Update emit and enqueueNotificationEmails to
pass the precomputed values through as parameters, and use those parameters when
building issueRef so the helper no longer triggers redundant us.GetByID and
ps.GetByID calls.
apps/api/internal/mail/notification_test.go (1)

117-146: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for "subscribed" without FieldName.

The existing TestBuildNotificationEmail_Subscribed always sets FieldName. A test with empty FieldName would catch the double-space formatting bug in the body builder.

♻️ Suggested additional test
func TestBuildNotificationEmail_SubscribedWithoutFieldName(t *testing.T) {
	data := NotificationEmailData{
		ReceiverName:  "Oscar",
		ActorName:     "Patty",
		IssueRef:      "TASK-555",
		IssueTitle:    "Refactor module",
		IssueURL:      "https://app.devlane.io/issue/vwx-555",
		WorkspaceName: "Platform",
		OldValue:      "Low",
		NewValue:      "High",
	}

	subject, body := BuildNotificationEmail("subscribed", data)

	expectedSubject := "Patty updated TASK-555"
	if subject != expectedSubject {
		t.Errorf("subject mismatch: got %q, want %q", subject, expectedSubject)
	}

	if !strings.Contains(body, "Patty updated TASK-555") {
		t.Error("body should contain update message without field name")
	}
	if strings.Contains(body, "updated  on") {
		t.Error("body should not contain double space when FieldName is empty")
	}
}
🤖 Prompt for 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.

In `@apps/api/internal/mail/notification_test.go` around lines 117 - 146, Add a
test in TestBuildNotificationEmail coverage for the "subscribed" case when
NotificationEmailData.FieldName is empty. Create a new test alongside
TestBuildNotificationEmail_Subscribed that calls BuildNotificationEmail with
FieldName unset, then assert the subject/body use the no-field wording and do
not contain the double-space formatting bug. Use the existing
BuildNotificationEmail and NotificationEmailData symbols so the test targets the
body builder behavior directly.
🤖 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 `@apps/api/internal/mail/notification.go`:
- Around line 77-84: The subscribed notification body always injects
data.FieldName, which causes awkward double spacing when it is empty. Update the
"subscribed" branch in notification.go to build the message conditionally the
same way the subject logic does: include the field name only when it is
non-empty, while preserving the existing ActorName, IssueRef, IssueTitle, and
value change formatting in the body.

In `@apps/api/internal/router/router.go`:
- Line 165: Use the computed appBaseURL fallback instead of cfg.AppBaseURL when
wiring NotificationService. The router already derives appBaseURL with a
fallback to cfg.CORSAllowOrigin, so update the SetAppBaseURL call in router
initialization to pass that value, matching the auth handler behavior and
preventing s.appURL from being empty inside NotificationService.

In `@apps/api/internal/service/notification.go`:
- Line 420: The warning log in notification handling currently uses
receiver.Email, which can log a pointer value and expose PII; update the email
queue failure log in the notification flow to use receiverID consistently
instead. Locate the logging call in the same block that already references
emailLog.ID and s.logger().Warn, and replace the receiver field with receiverID
so the log matches the earlier usage in this function and avoids logging email
addresses.

In `@IMPLEMENTATION_SUMMARY.md`:
- Around line 31-48: The two fenced code blocks in the implementation summary
are missing language tags, which trips markdownlint’s fenced-code-language rule.
Update both markdown fences in IMPLEMENTATION_SUMMARY.md to include a language
identifier such as text so the diagram renders and lints consistently; keep the
content unchanged and make sure the same fix is applied to the other referenced
block as well.
- Around line 161-165: The verification step references a log string that
doesn’t match the implemented email flow, so update the checks in
IMPLEMENTATION_SUMMARY to use the actual symbols and messages from the email
pipeline. Point the API log check at the real publish/queue logging from the
send_email flow (for example the queue publish send_email message or the warning
strings) or at the email_notification_logs row, and keep the database and
RabbitMQ verification aligned with those actual outputs.
- Around line 70-80: The Go command examples are written as if the reader is
already inside apps/api, so they fail from the repository root. Update the
commands in IMPLEMENTATION_SUMMARY to use repo-root-relative paths or explicitly
change into the API directory first, so the go test and go build examples remain
runnable from the root. Refer to the existing unit test and build command
snippets in the summary and adjust them consistently.

---

Nitpick comments:
In `@apps/api/internal/mail/notification_test.go`:
- Around line 117-146: Add a test in TestBuildNotificationEmail coverage for the
"subscribed" case when NotificationEmailData.FieldName is empty. Create a new
test alongside TestBuildNotificationEmail_Subscribed that calls
BuildNotificationEmail with FieldName unset, then assert the subject/body use
the no-field wording and do not contain the double-space formatting bug. Use the
existing BuildNotificationEmail and NotificationEmailData symbols so the test
targets the body builder behavior directly.

In `@apps/api/internal/service/notification.go`:
- Around line 345-347: Avoid recomputing actorName and projectIdent inside
enqueueNotificationEmails; they are already resolved in emit(). Update emit and
enqueueNotificationEmails to pass the precomputed values through as parameters,
and use those parameters when building issueRef so the helper no longer triggers
redundant us.GetByID and ps.GetByID calls.
🪄 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: CHILL

Plan: Pro Plus

Run ID: 8277441e-714a-4b45-b907-51ae502614de

📥 Commits

Reviewing files that changed from the base of the PR and between d2a1d0e and 3b1b1d9.

📒 Files selected for processing (7)
  • IMPLEMENTATION_SUMMARY.md
  • apps/api/internal/mail/notification.go
  • apps/api/internal/mail/notification_test.go
  • apps/api/internal/model/email_notification_log.go
  • apps/api/internal/router/router.go
  • apps/api/internal/service/notification.go
  • apps/api/internal/store/email_notification_log.go

Comment thread apps/api/internal/mail/notification.go
Comment thread apps/api/internal/router/router.go Outdated
Comment thread apps/api/internal/service/notification.go Outdated
Comment thread IMPLEMENTATION_SUMMARY.md Outdated
Comment thread IMPLEMENTATION_SUMMARY.md Outdated
Comment thread IMPLEMENTATION_SUMMARY.md Outdated
- Use appBaseURL fallback in router (fixes email disable when APP_BASE_URL unset)
- Handle empty FieldName in subscribed notification body
- Log receiverID instead of email pointer (PII + correctness)
- Pass precomputed actorName and issueRef to avoid redundant DB calls
@martian56 martian56 added API improvement Enhancement to an existing, partial feature labels Jul 8, 2026

@nazarli-shabnam nazarli-shabnam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

remove newly created IMPLEMENTATION_SUMMARY.md file. Thats not needed in the codebase.

@nazarli-shabnam

Copy link
Copy Markdown
Member

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nazarli-shabnam nazarli-shabnam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the contribution.

@nazarli-shabnam
nazarli-shabnam merged commit 47153a3 into Devlaner:main Jul 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

API improvement Enhancement to an existing, partial feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants