-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature/notification crud logic (#14)
* fix: fix target user on course to user not userentity * feat: add the notification entity * wip: update the appdata source to sync the notification to database * wip: add notification service * wip: add notification controller * wip: add notification route * wip: protect notification route with middleware * wip: use notification route on server * feat: Implement notification CRUD with type filtering and duplicate prevention * feat: update the notification to be used on other dashbaords
- Loading branch information
1 parent
f48c2f8
commit 1ec587f
Showing
7 changed files
with
264 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import NotificationService from "../services/notificationService.js"; | ||
|
||
class NotificationController { | ||
constructor() { | ||
this.notificationService = NotificationService; | ||
} | ||
|
||
async create(req, res) { | ||
try { | ||
const notification = await this.notificationService.create(req.body); | ||
res.status(201).json(notification); | ||
} catch (error) { | ||
res.status(400).json({ error: error.message }); | ||
} | ||
} | ||
|
||
async findAll(req, res) { | ||
try { | ||
const notifications = await this.notificationService.findAll(); | ||
res.json(notifications); | ||
} catch (error) { | ||
res.status(500).json({ error: error.message }); | ||
} | ||
} | ||
|
||
async findById(req, res) { | ||
try { | ||
const notification = await this.notificationService.findById(req.params.id); | ||
if (!notification) { | ||
return res.status(404).json({ error: "Notification not found" }); | ||
} | ||
res.json(notification); | ||
} catch (error) { | ||
res.status(500).json({ error: error.message }); | ||
} | ||
} | ||
|
||
async findBySender(req, res) { | ||
try { | ||
const notifications = await this.notificationService.findBySender(req.params.senderId); | ||
res.json(notifications); | ||
} catch (error) { | ||
res.status(500).json({ error: error.message }); | ||
} | ||
} | ||
|
||
async findByReceiver(req, res) { | ||
try { | ||
const notifications = await this.notificationService.findByReceiver(req.params.receiverId); | ||
res.json(notifications); | ||
} catch (error) { | ||
res.status(500).json({ error: error.message }); | ||
} | ||
} | ||
|
||
async findByType(req, res) { | ||
try { | ||
const notifications = await this.notificationService.findByType(req.params.type); | ||
res.json(notifications); | ||
} catch (error) { | ||
res.status(400).json({ error: error.message }); | ||
} | ||
} | ||
|
||
async update(req, res) { | ||
try { | ||
const notification = await this.notificationService.update(req.params.id, req.body); | ||
res.json(notification); | ||
} catch (error) { | ||
res.status(400).json({ error: error.message }); | ||
} | ||
} | ||
|
||
async delete(req, res) { | ||
try { | ||
await this.notificationService.delete(req.params.id); | ||
res.status(204).send(); | ||
} catch (error) { | ||
res.status(500).json({ error: error.message }); | ||
} | ||
} | ||
} | ||
|
||
export default new NotificationController(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { EntitySchema } from "typeorm"; | ||
|
||
const Notification = new EntitySchema({ | ||
name: "Notification", | ||
tableName: "notifications", | ||
columns: { | ||
id: { | ||
primary: true, | ||
type: "int", | ||
generated: true, | ||
}, | ||
senderId: { | ||
type: "int", | ||
nullable: false, | ||
}, | ||
receiverId: { | ||
type: "int", | ||
nullable: false, | ||
}, | ||
notificationType: { | ||
type: "varchar", //represents the type of notification for which dashboard it will be displayed | ||
nullable: true | ||
}, | ||
type: { | ||
type: "enum", | ||
enum: ["exam", "certification", "candidate", "new-student", "question", "payment", "announcement", "message", "new-request"], | ||
nullable: false, | ||
}, | ||
message: { | ||
type: "text", | ||
nullable: false, | ||
}, | ||
read: { | ||
type: "boolean", | ||
default: false, | ||
}, | ||
createdAt: { | ||
type: "timestamp", | ||
createDate: true, | ||
}, | ||
}, | ||
relations: { | ||
sender: { | ||
type: "many-to-one", | ||
target: "User", | ||
joinColumn: { name: "senderId" }, | ||
onDelete: "CASCADE", | ||
}, | ||
receiver: { | ||
type: "many-to-one", | ||
target: "User", | ||
joinColumn: { name: "receiverId" }, | ||
onDelete: "CASCADE", | ||
}, | ||
}, | ||
}); | ||
|
||
export default Notification; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import express from "express"; | ||
import NotificationController from "../controllers/notification.controller.js"; | ||
import { authenticateToken } from "../middlewares/auth.middleware.js" | ||
|
||
const router = express.Router(); | ||
router.use(authenticateToken); | ||
|
||
router.post("/", (req, res) => NotificationController.create(req, res)); | ||
router.get("/", (req, res) => NotificationController.findAll(req, res)); | ||
router.get("/:id", (req, res) => NotificationController.findById(req, res)); | ||
router.get("/sender/:senderId", (req, res) => NotificationController.findBySender(req, res)); | ||
router.get("/receiver/:receiverId", (req, res) => NotificationController.findByReceiver(req, res)); | ||
router.get("/type/:type", (req, res) => NotificationController.findByType(req, res)); | ||
router.put("/:id", (req, res) => NotificationController.update(req, res)); | ||
router.delete("/:id", (req, res) => NotificationController.delete(req, res)); | ||
|
||
export default router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import AppDataSource from "../config/config.js"; | ||
import Notification from "../entities/notification.entity.js"; | ||
|
||
class NotificationService { | ||
constructor() { | ||
this.notificationRepository = AppDataSource.getRepository(Notification); | ||
} | ||
|
||
async create(notificationData) { | ||
const { senderId, receiverId, notificationType , type, message } = notificationData; | ||
|
||
const VALID_TYPES = [ | ||
"exam", | ||
"certification", | ||
"candidate", | ||
"new-student", | ||
"question", | ||
"payment", | ||
"announcement", | ||
"message", | ||
"new-request", | ||
]; | ||
if (!VALID_TYPES.includes(type)) { | ||
throw new Error( | ||
"Invalid notification type. Must be one of 'exam', 'certification', 'candidate', 'new-student', 'question', 'payment', 'announcement', 'message', or 'new-request'." | ||
); | ||
} | ||
|
||
// Check for duplicate notifications with the same sender, receiver, type, and message | ||
const existingNotification = await this.notificationRepository.findOne({ | ||
where: { senderId, receiverId, notificationType, type, message }, | ||
}); | ||
|
||
if (existingNotification) { | ||
throw new Error("Duplicate notification detected."); | ||
} | ||
|
||
// Create and save the notification | ||
const notification = this.notificationRepository.create(notificationData); | ||
return await this.notificationRepository.save(notification); | ||
} | ||
|
||
async findAll() { | ||
return await this.notificationRepository.find(); | ||
} | ||
|
||
async findById(id) { | ||
return await this.notificationRepository.findOne({ where: { id } }); | ||
} | ||
|
||
async findBySender(senderId) { | ||
return await this.notificationRepository.find({ where: { senderId } }); | ||
} | ||
|
||
async findByReceiver(receiverId) { | ||
return await this.notificationRepository.find({ where: { receiverId } }); | ||
} | ||
|
||
async findByType(type) { | ||
const VALID_TYPES = [ | ||
"exam", | ||
"certification", | ||
"candidate", | ||
"new-student", | ||
"question", | ||
"payment", | ||
"announcement", | ||
"message", | ||
"new-request", | ||
]; | ||
if (!VALID_TYPES.includes(type)) { | ||
throw new Error( | ||
"Invalid notification type. Must be one of 'exam', 'certification', 'candidate', 'new-student', 'question', 'payment', 'announcement', 'message', or 'new-request'." | ||
); | ||
} | ||
|
||
return await this.notificationRepository.find({ where: { type } }); | ||
} | ||
|
||
async update(id, notificationData) { | ||
const existingNotification = await this.findById(id); | ||
if (!existingNotification) { | ||
throw new Error("Notification not found"); | ||
} | ||
|
||
await this.notificationRepository.update(id, notificationData); | ||
return this.findById(id); | ||
} | ||
|
||
async delete(id) { | ||
const notification = await this.findById(id); | ||
if (!notification) { | ||
throw new Error("Notification not found"); | ||
} | ||
|
||
return await this.notificationRepository.delete(id); | ||
} | ||
} | ||
|
||
export default new NotificationService(); |