Skip to content

Ks 026 sep 26 email notification 1959#2235

Merged
MuhammadKhalilzadeh merged 14 commits intodevelopfrom
ks-026-Sep-26-email-notification-1959
Sep 29, 2025
Merged

Ks 026 sep 26 email notification 1959#2235
MuhammadKhalilzadeh merged 14 commits intodevelopfrom
ks-026-Sep-26-email-notification-1959

Conversation

@solan117
Copy link
Copy Markdown
Contributor

Describe your changes

feat(notifications): add user-added-as-editor notification flow

  • Introduced sendUserAddedEditorNotification service to notify users when added as project editors.
  • Added new MJML email template user-added-project-editor.mjml for editor notification styling.
  • Integrated the service into updateProjectById to trigger notifications for added project editors.
  • Implemented email queuing with rate limiting in NotificationService to comply with sending limits.

feat(project): refine logic for sending notifications to added project members

  • Added getCurrentProjectMembers utility to fetch existing project members.
  • Updated updateProjectById controller to compare new and current members for targeted notifications.
  • Ensured notifications are triggered only for users newly added as project editors.
  • Updated notification flow to account for both newly added and re-added members.
  • Refactored logic to calculate added members post-update for accurate notifications.
  • Ensured changes do not block the response while sending notifications asynchronously.

For ProjectSettings/index.tsx
fix(project settings): clear field-specific errors and update initial values on successful save

  • Clear validation errors when adding/removing team members
  • Update initialValuesRef after successful save to reset form state
  • Ensure save button properly enables/disables based on actual changes

Write your issue number after "Fixes "

Fixes #1959

Please ensure all items are checked off before requesting a review:

  • I deployed the code locally.
  • I have performed a self-review of my code.
  • I have included the issue # in the PR.
  • I have labelled the PR correctly.
  • The issue I am working on is assigned to me.
  • I have avoided using hardcoded values to ensure scalability and maintain consistency across the application.
  • I have ensured that font sizes, color choices, and other UI elements are referenced from the theme.
  • My pull request is focused and addresses a single, specific feature.
  • If there are UI changes, I have attached a screenshot or video to this PR.
Screenshot 2025-09-27 004559

- Introduced `sendUserAddedEditorNotification` service to notify users when added as project editors.
- Added new MJML email template `user-added-project-editor.mjml` for editor notification styling.
- Integrated the service into `updateProjectById` to trigger notifications for added project editors.
- Implemented email queuing with rate limiting in `NotificationService` to comply with sending limits.
…for project editor flow

- Updated `sendUserAddedEditorNotification` log descriptions to reflect correct "project editor" role.
- Adjusted MJML template to replace "project admin" with "project editor."
- Ensured only users with the "Editor" role receive notifications in `updateProjectById`.
- Added `getCurrentProjectMembers` utility to fetch existing project members.
- Updated `updateProjectById` controller to compare new and current members for targeted notifications.
- Ensured notifications are triggered only for users newly added as project editors.
- Applied consistent indentation throughout `sendEmail` function.
- Improved readability of mail option object initialization.

fix(project): refine logic for sending notifications to added project members

- Updated notification flow to account for both newly added and re-added members.
- Refactored logic to calculate added members post-update for accurate notifications.
- Ensured changes do not block the response while sending notifications asynchronously.
- Updated `user-added-project-editor.mjml` and `user-added-project-admin.mjml` to include `{{project_name}}` in headings for clarity.
… values on successful save

  - Clear validation errors when adding/removing team members
  - Update initialValuesRef after successful save to reset form state
  - Ensure save button properly enables/disables based on actual changes
@solan117 solan117 added this to the 1.5 milestone Sep 27, 2025
@solan117 solan117 self-assigned this Sep 27, 2025
@solan117 solan117 added the backend Backend related tasks/issues label Sep 27, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 27, 2025

Walkthrough

Adds client-side per-field error clearing and initial-values sync; server diffs current project members to detect additions, sends editor-specific notifications, introduces a persistent rate-limited in-process email queue, adds project-member lookup util, new editor notification module and template, tweaks admin template, and updates .gitignore.

Changes

Cohort / File(s) Summary
Client: Project Settings
Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx
Clears per-field validation errors on select changes; on 202 save response synchronizes initialValuesRef.current and component values with saved values.
Server: Project Controller
Servers/controllers/project.ctrl.ts
Loads current project members, diffs to compute newly added members, and for each added editor (role_id = 3) looks up the user and triggers a fire-and-forget editor notification while logging lookup/send errors.
Notification Service (Queue & Rate-limit)
Servers/services/notificationService.ts
Adds queued-email model, persistent token-bucket rate-limit state file, token refill/backoff logic, in-process queue & processing loop, per-item promise resolution, filesystem template reads, sanitized error handling, and exports singleton notificationService.
Email Service (Formatting only)
Servers/services/emailService.ts
Whitespace/comment reformatting only; no functional or signature changes.
User Notification Modules
Servers/services/userNotification/userAddedAdminNotification.ts, Servers/services/userNotification/userAddedEditorNotification.ts
Admin module: switch template to user-added-project-admin.mjml. New module: sendUserAddedEditorNotification(...) added to validate inputs, compose template data, and invoke notificationService for editor emails.
Project Utilities
Servers/utils/project.utils.ts
Adds exported getCurrentProjectMembers(projectId, tenant, transaction?) => Promise<number[]> to read current project member user_ids from tenant-scoped projects_members.
Email Templates
Servers/templates/user-added-project-admin.mjml, Servers/templates/user-added-project-editor.mjml
Admin template updated for dynamic project context; new editor MJML template added with role-specific copy, CTA, and layout.
Repo Ignore
Servers/.gitignore
Removed ignore for README-Email-Notifications.md; added .rate-limit-state.json to ignore persisted rate-limit state.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant API as ProjectController
  participant U as ProjectUtils
  participant N as NotificationService (queue)
  participant E as EmailService

  C->>API: Update project (members/roles)
  API->>U: getCurrentProjectMembers(projectId, tenant)
  U-->>API: currentMemberIds[]
  API->>API: diff -> addedMembers[]
  loop each addedMember
    alt role == Editor (role_id = 3)
      API->>N: enqueue sendEmailWithTemplate(editor template, data)
      note right of N #EDF7FF: enqueue returns per-email Promise (fire-and-forget)
    else
      API->>API: no editor notification
    end
  end

  rect rgba(230,245,255,0.6)
  Note over N,E: processQueue() sequentially sends queued emails\nusing token-bucket rate limiting and persisted state
  N->>E: sendEmailDirectly(recipient, subject, template)
  E-->>N: success / error
  N-->>API: resolve/reject per-item Promise (controller used fire-and-forget)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • MuhammadKhalilzadeh

Poem

I thump the queue with careful cheer,
One hop per mail — no floods to fear.
New editors find a carroted door,
Admins now see their projects more.
Tiny paws, big ping — away it flies. 🥕✉️

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The pull request title “Ks 026 sep 26 email notification 1959” is vague and uses internal codes and a date rather than clearly summarizing the primary changes, so it does not provide a concise, meaningful description of the feature. Please rename the title to a concise, descriptive sentence that highlights the main change, for example “Add user-added-as-editor email notifications with rate limiting.”
Linked Issues Check ⚠️ Warning The PR fully implements the “user added as editor” notification specified in issue #1959, including service, template, and controller integration with queuing and rate limiting, but it omits other required notifications for project reviewers, auditors, admin role changes, new project registration, and policy due reminders. Implement or integrate the remaining notifications outlined in issue #1959—member.added.reviewer, member.added.auditor, member.role_changed.editor_to_admin, project.created.admin, and policy.due_soon—to achieve full compliance.
Out of Scope Changes Check ⚠️ Warning The PR includes frontend form validation and state-reset logic in ProjectSettings/index.tsx, which does not relate to the email notification objectives defined in issue #1959, making these UI-specific changes out of scope for this notification-focused task. Extract the UI updates into a separate pull request dedicated to frontend enhancements or adjust the linked issue to explicitly include these form behavior improvements if they are intended to be part of this feature.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed The pull request description adheres to the required template by clearly summarizing all functional changes, referencing “Fixes #1959,” and including a completed checklist with attached screenshot, fulfilling each section of the repository’s PR description guidelines.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ks-026-Sep-26-email-notification-1959

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b180a98 and 63fdee3.

📒 Files selected for processing (1)
  • Servers/services/notificationService.ts (4 hunks)

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
Servers/services/notificationService.ts (1)

155-172: Preserve PII sanitization when bubbling errors

Inside sendEmailDirectly we sanitize the error message to scrub email addresses, but we immediately rethrow the original error. Any caller that logs or surfaces that rejection will leak the unsanitized email, defeating the sanitization step and reopening the compliance gap. Please rethrow the sanitized error (optionally attaching the original as cause) so downstream logs stay redacted.

Apply this diff to propagate the sanitized error:

       await logFailure({
         eventType: "Create",
         description: `Failed to send email to ${maskEmail(recipientEmail)}`,
         functionName: "sendEmailWithTemplate",
         fileName: "NotificationService.ts",
         error: sanitized,
       });
-      throw error;
+      (sanitized as any).cause = error;
+      throw sanitized;
🧹 Nitpick comments (1)
Servers/utils/project.utils.ts (1)

645-658: Rely on Sequelize model mapping to avoid the any cast.

Line 657 casts to any[], which we can eliminate by letting Sequelize hydrate ProjectsMembersModel instances the same way the rest of this file already does. This keeps the return type honest and avoids surprises if user_id comes back as a string.

 export const getCurrentProjectMembers = async (
     projectId: number,
     tenant: string,
     transaction?: Transaction
 ): Promise<number[]> => {
     const currentMembersResult = await sequelize.query(
         `SELECT user_id FROM "${tenant}".projects_members WHERE project_id = :project_id`,
         {
             replacements: { project_id: projectId },
-            type: QueryTypes.SELECT,
+            mapToModel: true,
+            model: ProjectsMembersModel,
             transaction,
         }
     );
-    return (currentMembersResult as any[]).map((m) => m.user_id);
+    return currentMembersResult.map((m) => m.user_id);
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e75e65f and 8db608b.

📒 Files selected for processing (9)
  • Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx (2 hunks)
  • Servers/controllers/project.ctrl.ts (4 hunks)
  • Servers/services/emailService.ts (1 hunks)
  • Servers/services/notificationService.ts (3 hunks)
  • Servers/services/userNotification/userAddedAdminNotification.ts (1 hunks)
  • Servers/services/userNotification/userAddedEditorNotification.ts (1 hunks)
  • Servers/templates/user-added-project-admin.mjml (1 hunks)
  • Servers/templates/user-added-project-editor.mjml (1 hunks)
  • Servers/utils/project.utils.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
Servers/utils/project.utils.ts (1)
Servers/database/db.ts (1)
  • sequelize (141-141)
Servers/services/userNotification/userAddedEditorNotification.ts (3)
Servers/utils/logger/logHelper.ts (3)
  • logProcessing (22-30)
  • logSuccess (33-49)
  • logFailure (51-68)
Servers/utils/user.utils.ts (1)
  • getUserByIdQuery (137-144)
Servers/services/notificationService.ts (1)
  • notificationService (177-177)
Servers/controllers/project.ctrl.ts (4)
Servers/utils/project.utils.ts (2)
  • getProjectByIdQuery (114-159)
  • getCurrentProjectMembers (644-658)
Servers/utils/user.utils.ts (1)
  • getUserByIdQuery (137-144)
Servers/services/userNotification/userAddedEditorNotification.ts (1)
  • sendUserAddedEditorNotification (19-84)
Servers/utils/logger/logHelper.ts (1)
  • logFailure (51-68)
🔇 Additional comments (4)
Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx (1)

447-452: Clearing member-field errors on change is a nice touch

Resetting the error alongside the multi-select update keeps the validation UX tight.

Servers/templates/user-added-project-admin.mjml (1)

14-16: Copy update looks consistent with template data.

The new header and sentence reuse the existing project_name, actor_name, and user_name variables, so no additional data plumbing is required. Nicely aligned with the subject line.

Servers/services/userNotification/userAddedAdminNotification.ts (1)

62-63: Template rename matches the new asset.

The updated filename aligns with Servers/templates/user-added-project-admin.mjml, so the notification will keep rendering without further changes.

Servers/templates/user-added-project-editor.mjml (1)

14-22: Template wiring looks ready to ship.

All required placeholders (project_name, actor_name, user_name, project_url) are present, matching the data provided by the editor notification service, so rendering should work out of the box.

Comment thread Clients/src/presentation/pages/ProjectView/ProjectSettings/index.tsx Outdated
Comment thread Servers/services/userNotification/userAddedEditorNotification.ts
coderabbitai bot added a commit that referenced this pull request Sep 27, 2025
Docstrings generation was requested by @solan117.

* #2235 (comment)

The following files were modified:

* `Servers/controllers/project.ctrl.ts`
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Sep 27, 2025

Note

Generated docstrings for this pull request at #2238

…er successful save

- Introduce `newValues` to synchronize `initialValuesRef` and `setValues` for consistent state management.
@MuhammadKhalilzadeh
Copy link
Copy Markdown
Collaborator

@HarshP4585 and @Br0wnHammer , would you guys please do a test on this and get beck to me with your feedbacks?

… sanitized errors

- Assigned the original error as the `.cause` property of the sanitized error before re-throwing it for improved debugging context.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9ca0df8 and f086c39.

📒 Files selected for processing (1)
  • Servers/services/notificationService.ts (4 hunks)

Comment thread Servers/services/notificationService.ts
…iting

- Added token bucket algorithm to handle rate limiting with burst capacity and compliance to the email send limits.
- Introduced persistence for rate limiting state to maintain consistency across restarts.
- Updated queue processing to incorporate exponential backoff on failures.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f086c39 and 0ab4a70.

📒 Files selected for processing (2)
  • Servers/.gitignore (1 hunks)
  • Servers/services/notificationService.ts (4 hunks)

Comment thread Servers/services/notificationService.ts
Comment thread Servers/services/notificationService.ts
…essing actions

- Introduced an `initialization` promise to enforce asynchronous initialization before processing the email queue or sending emails.
- Adjusted token validation logic to handle edge cases with loaded rate limit state.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
Servers/services/notificationService.ts (4)

271-294: Correct logger metadata: wrong function name and file name casing.

These logs originate from sendEmailDirectly but report sendEmailWithTemplate; file name casing also differs from the actual file.

   logProcessing({
     description: `Sending email with template: ${templateFileName}`,
-    functionName: "sendEmailWithTemplate",
-    fileName: "NotificationService.ts",
+    functionName: "sendEmailDirectly",
+    fileName: "notificationService.ts",
   });
@@
   await logSuccess({
     eventType: "Create",
     description: `Email sent successfully to ${maskEmail(recipientEmail)}`,
-    functionName: "sendEmailWithTemplate",
-    fileName: "NotificationService.ts",
+    functionName: "sendEmailDirectly",
+    fileName: "notificationService.ts",
   });
@@
   await logFailure({
     eventType: "Create",
     description: `Failed to send email to ${maskEmail(recipientEmail)}`,
-    functionName: "sendEmailWithTemplate",
-    fileName: "NotificationService.ts",
+    functionName: "sendEmailDirectly",
+    fileName: "notificationService.ts",
     error: sanitized,
   });

Also applies to: 304-313


121-128: Persist state atomically to avoid partial/corrupted files.

Use a temp file + rename to make writes atomic.

   private async saveRateLimitState(): Promise<void> {
     try {
-      await fs.writeFile(rateLimitStateFile, JSON.stringify(this.rateLimitState), 'utf8');
+      const tmp = `${rateLimitStateFile}.tmp`;
+      await fs.writeFile(tmp, JSON.stringify(this.rateLimitState), 'utf8');
+      await fs.rename(tmp, rateLimitStateFile);
     } catch (error) {
       // Log but don't throw - persistence failure shouldn't break email sending
       console.warn('Failed to save rate limit state:', error);
     }
   }

297-313: Prefer native Error cause over manual mutation.

Construct the error with { cause } instead of assigning later.

-      const sanitized = new Error(
-        String((error as any)?.message ?? error).replace(
-          /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
-          "[redacted]"
-        )
-      );
+      const sanitized = new Error(
+        String((error as any)?.message ?? error).replace(
+          /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi,
+          "[redacted]"
+        ),
+        { cause: error as unknown }
+      );
@@
-      (sanitized as any).cause = error;
-      throw sanitized;
+      throw sanitized;

62-73: Operational note: per-process limiter and local JSON state.

If you run multiple app instances (pods/workers), each will send up to ~1.6 rps, exceeding provider caps system-wide. Consider a centralized queue/limiter (Redis/Cloud task queue) or provider-level throttle, and cap in-memory queue size to avoid unbounded growth.

Also applies to: 121-128

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab4a70 and b180a98.

📒 Files selected for processing (1)
  • Servers/services/notificationService.ts (4 hunks)
🔇 Additional comments (2)
Servers/services/notificationService.ts (2)

58-77: Nice: prior rate-limit gaps are addressed.

Initialization is awaited, 0 tokens are preserved on restore, and a min-gap/backoff is enforced before each send. This closes the burst-on-restart loopholes flagged earlier.

Also applies to: 94-103, 160-184, 201-216, 220-223


33-51: Good PII hygiene and path hardening.

Email masking in logs and path.basename to constrain template reads are solid.

Also applies to: 271-281

Comment thread Servers/services/notificationService.ts
Comment thread Servers/services/notificationService.ts
… during shutdown

- Released processing lock but ensured the queue resumes if pending items are added during the winding-down phase.
…ency

- Adjusted `templatesDir` to point to `Servers/templates` for streamlined directory structure and compatibility.
Copy link
Copy Markdown
Member

@Br0wnHammer Br0wnHammer left a comment

Choose a reason for hiding this comment

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

LGTM! Great work 🚀

@MuhammadKhalilzadeh MuhammadKhalilzadeh merged commit 52f6e94 into develop Sep 29, 2025
3 checks passed
@MuhammadKhalilzadeh MuhammadKhalilzadeh deleted the ks-026-Sep-26-email-notification-1959 branch September 29, 2025 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Backend related tasks/issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implementing email notifications

3 participants