Skip to content

feat: Push Notification API for Learning Events#55

Merged
3m1n3nc3 merged 4 commits into
learnault:mainfrom
prodbycorne:feat/issue-53-push-notification-api
May 1, 2026
Merged

feat: Push Notification API for Learning Events#55
3m1n3nc3 merged 4 commits into
learnault:mainfrom
prodbycorne:feat/issue-53-push-notification-api

Conversation

@prodbycorne
Copy link
Copy Markdown

This pull request implements the Push Notification API for real-time learner updates as described in issue #53.

Changes

Database Schema (prisma/schema.prisma)

  • Added DeviceToken model for per-user, per-platform FCM token storage
  • Added NotificationPreference model for controlling which event types a user receives (rewardReceipt, quizPassFail, streakReminders)
  • Added NotificationLog model for tracking push delivery attempts with retry and dead-letter handling

Notification Service (src/services/notification.service.ts)

  • Device token registration with upsert logic
  • Notification preference management (defaults to enabled for all types)
  • Queue-based notification dispatch with user preference checks
  • Firebase Cloud Messaging (FCM) integration via firebase-admin
  • Exponential backoff retry mechanism (1min, 5min, 25min...)
  • Dead-letter handling after 5 failed attempts
  • Graceful fallback when Firebase is not configured (dev/test environments)

Notification Controller and Routes

  • POST /v1/notifications/devices -- Register a device token (authenticated)
  • PATCH /v1/notifications/preferences -- Update notification preferences (authenticated)
  • GET /v1/notifications/delivery-status -- Query delivery logs for troubleshooting (authenticated)
  • Zod validation on all inputs
  • OpenAPI documentation annotations on all endpoints

Event Triggers

  • Quiz pass/fail notifications fired from module.controller.ts after module completion
  • Reward receipt notifications fired from reward.service.ts after successful reward payout
  • All triggers are non-blocking (fire-and-forget with error logging)

Tests (18 new tests, all passing)

  • tests/notification.controller.test.ts -- 10 tests covering auth guards, validation, and all endpoint paths
  • tests/notification.service.test.ts -- 8 tests covering token registration, preference checks, queue logic, retry behavior, and dead-letter handling

Closes #53

@drips-wave
Copy link
Copy Markdown

drips-wave Bot commented Apr 26, 2026

@prodbycorne Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Copy link
Copy Markdown
Contributor

@Kaylahray Kaylahray left a comment

Choose a reason for hiding this comment

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

Hi please resolve conflicts @prodbycorne

- Resolve prisma/schema and routes/index conflicts (User relations + all v1 routes)
- Instantiate PrismaClient with PrismaPg adapter for Prisma ORM 7
- Share prisma singleton in webhook.service; type-only Prisma imports
- Default DATABASE_URL in tests/setup for Vitest when .env.test is absent
- Align credential/employer flows with User.username (no name field)
- Module start: use in-progress score sentinel (-1) with non-null Float schema
- Express Router explicit annotations; referral getStats typing; user password catch param
- Update unit test mocks; remove unused firebase-admin import in notification tests

Made-with: Cursor
@Kaylahray
Copy link
Copy Markdown
Contributor

Hi @prodbycorne

Add the required blank line before the return statement in the Prisma client factory so the CI lint step passes.

Made-with: Cursor
@prodbycorne
Copy link
Copy Markdown
Author

Addressed the remaining CI failure on this PR by fixing the lint error in src/config/database.ts (newline-before-return).\n\nCurrent blocker is that the latest workflow run is in action_required state (no jobs started), so a maintainer needs to approve/re-run the workflow for forked PRs. Once that is done, checks should execute on commit 79e71f6.

@3m1n3nc3 3m1n3nc3 merged commit da2eaa5 into learnault:main May 1, 2026
1 check 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.

4 participants