Skip to content

feat: Push notification system#123

Merged
MPSxDev merged 5 commits into
PayStell:mainfrom
Bosun-Josh121:feat/push-notification
Aug 7, 2025
Merged

feat: Push notification system#123
MPSxDev merged 5 commits into
PayStell:mainfrom
Bosun-Josh121:feat/push-notification

Conversation

@Bosun-Josh121

@Bosun-Josh121 Bosun-Josh121 commented Aug 4, 2025

Copy link
Copy Markdown
Contributor

Pull Request Overview

📝 Summary

This pull request implements a complete, real-time in-app push notification system.
It introduces the necessary services, controllers, and routes to create, manage, and deliver notifications for critical events like payments, fraud alerts, and system updates.
This work builds upon the existing InAppNotification entity to deliver a full-featured and highly requested user-facing feature.

🔗 Related Issues

Closes #115

🔄 Changes Made

This PR introduces a comprehensive set of components to support the in-app notification system:

  • inAppNotificationService.ts:
    A robust service class to handle all business logic, including:

    • Notification creation
    • Retrieval with advanced filtering (by category, status, and date range)
    • Marking notifications as read
    • Soft deletion
  • NotificationController.ts:
    Manages the request/response lifecycle for all notification-related API endpoints.

  • notification.routes.ts:
    Defines the REST API endpoints, including:

    • GET /
    • GET /unread-count
    • PUT /:id/read
    • PUT /mark-all-read
    • DELETE /:id
      Swagger documentation is included.
  • notificationEvent.middleware.ts:
    Middleware to automatically trigger notifications for system events like completed payments and detected fraud alerts.

  • NotificationDTO.ts:
    Provides DTOs for request validation.

  • Integration:
    The new notification routes are registered in app.ts under the /api/notifications base path.

🖼️ Current Output

The primary output is a series of fully functional API endpoints.
For example, a GET request to /api/notifications with query parameters for filtering and pagination will now return a structured JSON response containing:

  • The list of notifications
  • Metadata about the pagination

🧪 Testing

result 14

💬 Comments

This implementation provides a solid foundation for the notification system.
The template-based methods in the service make it straightforward to add new automatic notification types in the future as the application grows.

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive in-app notifications system with endpoints to view, filter, mark as read, and delete notifications.
    • Added support for notification event triggers, including payment completions, fraud alerts, and system updates.
    • Users can now view unread notification counts and filter notifications by category, status, and date range.
  • Tests

    • Added extensive automated tests to ensure notification service reliability and correct behavior.
  • Documentation

    • Enhanced API documentation for all notification-related endpoints, including usage, parameters, and security details.

@coderabbitai

coderabbitai Bot commented Aug 4, 2025

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@Bosun-Josh121 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between ce34979 and 43b8e9b.

📒 Files selected for processing (2)
  • src/dtos/NotificationDto.ts (1 hunks)
  • src/services/merchantWebhookQueue.service.ts (1 hunks)

Walkthrough

A comprehensive in-app notification system has been implemented. This includes new API endpoints, a controller, DTOs, middleware for event-driven notifications, a fully-featured notification service with templates, filtering, and pagination, and extensive unit tests. Swagger documentation and validation are integrated into the new routes.

Changes

Cohort / File(s) Change Summary
App Route Registration
src/app.ts
Registers new notification routes at /api/notifications by importing and mounting the notification router.
Notification Controller
src/controllers/NotificationController.ts
Introduces NotificationController with methods for listing, marking as read, counting, and deleting notifications, using the notification service and handling authentication and errors.
Notification DTOs
src/dtos/NotificationDto.ts
Adds DTO classes for notification creation and querying, with validation rules for all fields, supporting type safety and input validation.
Notification Middleware
src/middlewares/notificationEvent.middleware.ts
Adds middleware to process notification events from responses, trigger notifications for payments, fraud, and system updates, and provide static helpers for event attachment.
Notification Routes
src/routes/notification.routes.ts
Adds a router with endpoints for notification CRUD, read/unread management, filtering, pagination, and Swagger documentation. Integrates authentication and validation middlewares.
Notification Service
src/services/inAppNotificationService.ts
Expands the notification service with methods for querying, marking as read, counting, soft deleting, handling expiration, and creating templated notifications for payments, fraud alerts, and system updates.
Notification Service Tests
src/tests/services/notificationService.test.ts
Adds a comprehensive Jest test suite covering notification creation, querying, marking as read, counting, soft deletion, expiration handling, template methods, and error handling, using mocked repositories for isolation.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Router
    participant AuthMiddleware
    participant NotificationController
    participant NotificationService
    participant DB

    Client->>Router: HTTP request (/api/notifications)
    Router->>AuthMiddleware: Authenticate user
    AuthMiddleware-->>Router: Pass if authenticated
    Router->>NotificationController: Call method (e.g., getNotifications)
    NotificationController->>NotificationService: Query notifications (with filters)
    NotificationService->>DB: Fetch notifications
    DB-->>NotificationService: Return results
    NotificationService-->>NotificationController: Return notifications
    NotificationController-->>Router: Send JSON response
    Router-->>Client: Return notifications data
Loading
sequenceDiagram
    participant Controller
    participant NotificationEventMiddleware
    participant NotificationService
    participant DB

    Controller->>NotificationEventMiddleware: Attach notification event to response
    NotificationEventMiddleware->>NotificationService: Process event (e.g., create notification)
    NotificationService->>DB: Persist notification
    DB-->>NotificationService: Confirmation
    NotificationService-->>NotificationEventMiddleware: Done
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
Complete API endpoints for notification management (/api/notifications) (#115)
Template system for automatic notifications (#115)
Automatic notifications for payment and fraud events (#115)
Filtering by category, read/unread status, date; Pagination for notification listing (#115)
Soft delete for notifications (#115)
Tests with >80% coverage (#115)
Complete Swagger documentation (#115)

Poem

A message arrives with a gentle ping,
Payments, alerts, and updates it brings.
With routes and filters, all neat and tight,
Notifications sorted, day and night.
The rabbit hops—so proud, so spry—
"Your inbox is ready!" it winks with an eye.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@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: 4

🧹 Nitpick comments (6)
src/tests/services/notificationService.test.ts (2)

15-22: Replace any type with proper TypeScript typing.

The mock repository should use proper typing instead of any to maintain type safety in tests.

  beforeEach(() => {
    // Before each test, create a mock repository with jest functions for each method
    mockRepository = {
      save: jest.fn(),
      findAndCount: jest.fn(),
      findOne: jest.fn(),
      update: jest.fn(),
      count: jest.fn(),
      delete: jest.fn(),
-   } as any;
+   } as jest.Mocked<Repository<InAppNotificationEntity>>;

251-251: Replace any type casts with proper TypeScript typing.

The mock return values should use proper typing instead of any for better type safety.

-     mockRepository.update.mockResolvedValue({ affected: 3 } as any);
+     mockRepository.update.mockResolvedValue({ affected: 3, generatedMaps: [], raw: [] });

-     mockRepository.update.mockResolvedValue({ affected: 1 } as any);
+     mockRepository.update.mockResolvedValue({ affected: 1, generatedMaps: [], raw: [] });

-       mockRepository.delete.mockResolvedValue({ affected: 2 } as any);
+       mockRepository.delete.mockResolvedValue({ affected: 2, raw: [] });

Also applies to: 282-282, 296-296

src/controllers/NotificationController.ts (1)

15-17: Consider extracting repeated authentication logic.

The authentication check pattern is repeated in every method. Consider extracting this into a private method or relying on middleware for cleaner code.

+ private validateAuthentication(req: Request): string {
+   const userId = req.user?.id.toString();
+   if (!userId) {
+     throw new AppError("User not authenticated", 401);
+   }
+   return userId;
+ }

  async getNotifications(req: Request, res: Response, next: NextFunction): Promise<void> {
    try {
-     const userId = req.user?.id.toString();
-     if (!userId) {
-       throw new AppError("User not authenticated", 401);
-     }
+     const userId = this.validateAuthentication(req);

Also applies to: 49-53, 69-72, 87-90, 108-110

src/middlewares/notificationEvent.middleware.ts (1)

88-134: Consider improving the notification trigger pattern

The static helper methods work but have some limitations:

  1. They assume controllers will use res.json(res.locals.responseBody)
  2. Only one notification event can be triggered per response
  3. No parameter validation

Consider this alternative approach that supports multiple notifications:

+  static attachNotificationEvent(
+    res: Response,
+    event: NotificationEvent
+  ): void {
+    if (!res.locals.notificationEvents) {
+      res.locals.notificationEvents = [];
+    }
+    res.locals.notificationEvents.push(event);
+  }
+
   static triggerPaymentNotification(
     res: Response,
     recipientId: string,
     amount: number,
     paymentId: string
   ): void {
-    const currentBody = res.locals.responseBody || {};
-    currentBody.notificationEvent = {
+    this.attachNotificationEvent(res, {
       type: "payment_completed" as const,
       recipientId,
       amount,
       paymentId,
-    };
-    res.locals.responseBody = currentBody;
+    });
   }

Then update the middleware to process multiple events:

res.json = (body: unknown) => {
  // Process any queued notification events
  if (res.locals.notificationEvents && Array.isArray(res.locals.notificationEvents)) {
    for (const event of res.locals.notificationEvents) {
      this.processNotificationEvent(event)
        .catch(error => logger.error('Failed to process notification event', { error }));
    }
  }
  
  // Also check body for backward compatibility
  if (body && typeof body === 'object' && 'notificationEvent' in body) {
    this.processNotificationEvent((body as { notificationEvent: NotificationEvent }).notificationEvent)
      .catch(error => logger.error('Failed to process notification event', { error }));
  }
  
  return originalJson(body);
};
src/services/inAppNotificationService.ts (2)

158-163: Consider using soft delete for expired notifications

The method uses hard delete which permanently removes expired notifications. For audit trail and compliance purposes, consider using soft delete instead.

   async deleteExpiredNotifications(): Promise<void> {
     const now = new Date();
-    await this.notificationRepository.delete({
-      expiresAt: LessThan(now),
-    });
+    await this.notificationRepository.update(
+      { expiresAt: LessThan(now), status: Not(NotificationStatus.ARCHIVED) },
+      { status: NotificationStatus.ARCHIVED }
+    );
   }

165-212: Well-designed template methods for common notifications

The template methods provide consistent notification creation with appropriate priorities and metadata.

Consider using locale-specific currency formatting for the payment notification:

-      message: `You received a payment of $${amount}`,
+      message: `You received a payment of ${new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount)}`,
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0171fd2 and e4a8886.

📒 Files selected for processing (7)
  • src/app.ts (2 hunks)
  • src/controllers/NotificationController.ts (1 hunks)
  • src/dtos/NotificationDto.ts (1 hunks)
  • src/middlewares/notificationEvent.middleware.ts (1 hunks)
  • src/routes/notification.routes.ts (1 hunks)
  • src/services/inAppNotificationService.ts (4 hunks)
  • src/tests/services/notificationService.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/middlewares/notificationEvent.middleware.ts (1)
src/services/inAppNotificationService.ts (1)
  • NotificationService (36-213)
src/controllers/NotificationController.ts (1)
src/services/inAppNotificationService.ts (2)
  • NotificationService (36-213)
  • NotificationFilters (26-34)
src/routes/notification.routes.ts (2)
src/controllers/NotificationController.ts (1)
  • NotificationController (6-122)
src/middlewares/validationErrorHandler.ts (1)
  • handleValidationErrors (5-42)
🪛 GitHub Check: build-and-test
src/tests/services/notificationService.test.ts

[failure] 296-296:
Unexpected any. Specify a different type


[failure] 282-282:
Unexpected any. Specify a different type


[failure] 251-251:
Unexpected any. Specify a different type


[failure] 22-22:
Unexpected any. Specify a different type

src/dtos/NotificationDto.ts

[failure] 26-26:
Unexpected any. Specify a different type

src/services/inAppNotificationService.ts

[failure] 149-149:
Unexpected any. Specify a different type


[failure] 113-113:
Unexpected any. Specify a different type


[failure] 82-82:
Unexpected any. Specify a different type

src/middlewares/notificationEvent.middleware.ts

[failure] 41-41:
Unexpected any. Specify a different type

🪛 GitHub Actions: CI Pipeline
src/dtos/NotificationDto.ts

[error] 26-26: ESLint: Unexpected any. Specify a different type (@typescript-eslint/no-explicit-any)

🔇 Additional comments (13)
src/app.ts (1)

28-28: LGTM! Clean integration of notification routes.

The notification routes are properly imported and registered under the appropriate /api/notifications path prefix, following the existing routing patterns in the application.

Also applies to: 153-153

src/routes/notification.routes.ts (3)

10-11: LGTM! Proper security implementation.

Global authentication middleware is correctly applied to all notification routes, ensuring that only authenticated users can access notification endpoints.


85-97: LGTM! Comprehensive validation and documentation.

The route implementation follows best practices with:

  • Proper query parameter validation using express-validator
  • Comprehensive Swagger documentation
  • Appropriate constraints (e.g., limit max 100, UUID validation)
  • Error handling middleware integration

159-164: LGTM! Proper controller method binding.

The use of .bind(notificationController) ensures the correct this context is maintained when the controller methods are called, preventing potential runtime errors.

src/tests/services/notificationService.test.ts (2)

34-88: LGTM! Excellent test coverage for notification creation.

The test suite properly covers both successful creation and error handling scenarios, with appropriate assertions to verify the repository methods are called with correct parameters.


307-371: LGTM! Comprehensive template method testing.

The template method tests properly validate the structured creation of different notification types (payment, fraud alert, system update) with correct metadata, priorities, and categorization.

src/controllers/NotificationController.ts (3)

6-11: LGTM! Proper dependency injection pattern.

The controller correctly initializes the NotificationService in the constructor, following good dependency injection practices.


13-44: LGTM! Comprehensive query parameter handling.

The getNotifications method properly:

  • Validates user authentication
  • Parses and type-converts query parameters
  • Handles optional date filtering
  • Provides sensible defaults for pagination
  • Uses consistent response format

46-65: LGTM! Consistent error handling and response format.

All controller methods follow consistent patterns with:

  • Proper try-catch error handling
  • Standardized JSON response format
  • Appropriate HTTP status codes
  • Error delegation to middleware via next()

Also applies to: 67-83, 85-101, 103-121

src/middlewares/notificationEvent.middleware.ts (2)

1-26: Well-structured event interfaces with proper discriminated unions

The event interfaces follow TypeScript best practices with clear property definitions and discriminated union pattern for type safety.


54-86: Robust event processing with proper error handling

The event processing method correctly handles all event types with appropriate service calls and includes comprehensive error handling.

src/services/inAppNotificationService.ts (2)

1-1: Good addition of TypeORM query operators and filtering interface

The new imports and NotificationFilters interface provide comprehensive filtering capabilities needed for the notification system.

Also applies to: 26-34


61-63: Correct implementation of notification expiration

The optional expiresAt property is properly handled with appropriate conditional assignment.

Comment thread src/dtos/NotificationDto.ts Outdated
Comment thread src/middlewares/notificationEvent.middleware.ts
Comment thread src/services/inAppNotificationService.ts
Comment thread src/services/inAppNotificationService.ts Outdated

@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: 1

🧹 Nitpick comments (2)
src/services/inAppNotificationService.ts (2)

180-184: Hard-deleting records may break audit expectations

deleteExpiredNotifications issues a physical delete, removing rows permanently.
If the domain requires auditability or GDPR “erasure vs. archive” distinction, consider switching to a soft-delete (update status = ARCHIVED or use TypeORM’s soft-delete API) and schedule a real purge only when fully safe.


148-156: Return count from bulk markAllAsRead

The method currently returns void, so callers can’t confirm how many rows were affected.
Returning UpdateResult (or at least affected count) provides feedback for logging and troubleshooting at negligible cost.

-  async markAllAsRead(recipientId: string): Promise<void> {
-    await this.notificationRepository.update(
+  async markAllAsRead(recipientId: string): Promise<number> {
+    const res = await this.notificationRepository.update(
       { recipientId, isRead: false },
       { isRead: true, status: NotificationStatus.READ, readAt: new Date() },
     );
+    return res.affected ?? 0;
   }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4a8886 and ce34979.

📒 Files selected for processing (6)
  • src/controllers/NotificationController.ts (1 hunks)
  • src/dtos/NotificationDto.ts (1 hunks)
  • src/middlewares/notificationEvent.middleware.ts (1 hunks)
  • src/routes/notification.routes.ts (1 hunks)
  • src/services/inAppNotificationService.ts (5 hunks)
  • src/tests/services/notificationService.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/dtos/NotificationDto.ts
  • src/controllers/NotificationController.ts
  • src/tests/services/notificationService.test.ts
  • src/middlewares/notificationEvent.middleware.ts
  • src/routes/notification.routes.ts

Comment thread src/services/inAppNotificationService.ts

@MPSxDev MPSxDev 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.

Thanks for contributing to Paystell, excellent work.

@MPSxDev MPSxDev merged commit 8a5e8ec into PayStell:main Aug 7, 2025
2 checks passed
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.

Push Notification

2 participants