Skip to content

Integrate email channel into notifications#3658

Open
CarsonF wants to merge 2 commits intonotifications/configfrom
notifications/email
Open

Integrate email channel into notifications#3658
CarsonF wants to merge 2 commits intonotifications/configfrom
notifications/email

Conversation

@CarsonF
Copy link
Contributor

@CarsonF CarsonF commented Feb 20, 2026

No description provided.

@CarsonF CarsonF force-pushed the notifications/email branch from 1e97033 to 170f306 Compare February 20, 2026 20:43
@CarsonF CarsonF force-pushed the notifications/config branch from 76285c5 to 338ced1 Compare February 20, 2026 20:53
@CarsonF CarsonF force-pushed the notifications/email branch from 170f306 to d9168d2 Compare February 20, 2026 21:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR integrates email as a notification delivery channel alongside the existing in-app notification channel. The implementation adds email rendering capabilities to notification strategies, implements email delivery logic with user permission contexts, and updates the notification channel enumeration.

Changes:

  • Added Email as a new notification channel with configurable availability based on strategy implementation
  • Implemented email delivery logic that fetches users, renders emails per recipient, and sends via MailerService
  • Added UserModule dependency to NotificationModule to support user data fetching for email delivery

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/components/notifications/notification.strategy.ts Added EmailMessage type import, Email channel availability logic, and optional renderEmail interface method
src/components/notifications/notification.service.ts Added email delivery method with user fetching, asUser context for rendering, and asyncPool-based sending
src/components/notifications/notification.module.ts Added UserModule import to support user data access in notification service
src/components/notifications/dto/notification-channel.enum.ts Added 'Email' to NotificationChannel enum values

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +193 to +200
await asyncPool(Infinity, users, async (user) => {
const email = user.email.value;
if (!email) return;
await this.identity.asUser(user.id, async () => {
const msg: EmailMessage<any> = strategy.renderEmail!(notification);
await this.mailer.send(msg.withHeaders({ to: email }));
});
});
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

Email sending failures are not caught or logged. If mailer.send() or renderEmail() throws an error, the entire notification creation will fail silently for all users. Consider adding try-catch blocks around the email rendering and sending operations to log errors without failing the entire notification flow. Similar error handling patterns exist in webhook processing (webhook-processor.worker.ts lines 62-78) and other services (e.g., progress-report migration at line 25-36).

Copilot uses AI. Check for mistakes.
Comment on lines +196 to +197
await this.identity.asUser(user.id, async () => {
const msg: EmailMessage<any> = strategy.renderEmail!(notification);
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

Calling renderEmail() within the asUser context may limit access to notification data based on the recipient's permissions. If the notification contains data that the user might not have direct access to (but should see in the email), this could cause issues. Consider whether renderEmail() needs to be called within the Administrator context (like the user fetch), or if a different permission context is more appropriate. The webhook executor (webhook.executor.ts:63) uses a similar pattern for executing subscriptions.

Suggested change
await this.identity.asUser(user.id, async () => {
const msg: EmailMessage<any> = strategy.renderEmail!(notification);
const msg: EmailMessage<any> = await this.identity.asRole(
'Administrator',
async () => strategy.renderEmail!(notification),
);
await this.identity.asUser(user.id, async () => {

Copilot uses AI. Check for mistakes.
hydrateExtraForGel?(): Record<string, any>;

/**
* Send an email notification called for each recipient.
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

The comment contains a grammatical error. "Send an email notification called for each recipient" should be "Send an email notification for each recipient" (remove "called").

Suggested change
* Send an email notification called for each recipient.
* Send an email notification for each recipient.

Copilot uses AI. Check for mistakes.
'Administrator',
async () => await this.users.readMany(userIds),
);
await asyncPool(Infinity, users, async (user) => {
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

Using asyncPool(Infinity, ...) removes all concurrency limits, which could cause resource exhaustion if many users receive email notifications simultaneously. Other parts of the codebase use limited concurrency: webhook processing uses 3, product sync uses 5, migrations use 1-2. Consider setting a reasonable concurrency limit (e.g., 5-10) to prevent overwhelming the mail service and to handle errors more gracefully.

Copilot uses AI. Check for mistakes.
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.

2 participants