diff --git a/src/backend/src/controllers/users.controllers.ts b/src/backend/src/controllers/users.controllers.ts index cc084c714e..b3fee931c2 100644 --- a/src/backend/src/controllers/users.controllers.ts +++ b/src/backend/src/controllers/users.controllers.ts @@ -194,10 +194,9 @@ export default class UsersController { static async getUserUnreadNotifications(req: Request, res: Response, next: NextFunction) { try { - const { userId } = req.params; - const { organization } = req; + const { organization, currentUser } = req; - const unreadNotifications = await UsersService.getUserUnreadNotifications(userId, organization); + const unreadNotifications = await UsersService.getUserUnreadNotifications(currentUser.userId, organization); res.status(200).json(unreadNotifications); } catch (error: unknown) { next(error); @@ -206,11 +205,14 @@ export default class UsersController { static async removeUserNotification(req: Request, res: Response, next: NextFunction) { try { - const { userId } = req.params; - const { notificationId } = req.body; - const { organization } = req; + const { notificationId } = req.params; + const { organization, currentUser } = req; - const unreadNotifications = await UsersService.removeUserNotification(userId, notificationId, organization); + const unreadNotifications = await UsersService.removeUserNotification( + currentUser.userId, + notificationId, + organization + ); res.status(200).json(unreadNotifications); } catch (error: unknown) { next(error); @@ -219,10 +221,9 @@ export default class UsersController { static async getUserUnreadAnnouncements(req: Request, res: Response, next: NextFunction) { try { - const { userId } = req.params; - const { organization } = req; + const { organization, currentUser } = req; - const unreadAnnouncements = await UsersService.getUserUnreadAnnouncements(userId, organization); + const unreadAnnouncements = await UsersService.getUserUnreadAnnouncements(currentUser.userId, organization); res.status(200).json(unreadAnnouncements); } catch (error: unknown) { next(error); diff --git a/src/backend/src/prisma/migrations/20241218160143_homepage_updates/migration.sql b/src/backend/src/prisma/migrations/20241220204035_homepage_updates/migration.sql similarity index 100% rename from src/backend/src/prisma/migrations/20241218160143_homepage_updates/migration.sql rename to src/backend/src/prisma/migrations/20241220204035_homepage_updates/migration.sql diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index fdb5ffefdb..ddd4e190cb 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -33,7 +33,7 @@ import { writeFileSync } from 'fs'; import WorkPackageTemplatesService from '../services/work-package-template.services'; import RecruitmentServices from '../services/recruitment.services'; import OrganizationsService from '../services/organizations.services'; -import NotificationsService from '../services/notifications.services'; +import AnnouncementService from '../services/announcement.service'; const prisma = new PrismaClient(); @@ -1894,6 +1894,16 @@ const performSeed: () => Promise = async () => { await RecruitmentServices.createFaq(batman, 'When was FinishLine created?', 'FinishLine was created in 2019', ner); await RecruitmentServices.createFaq(batman, 'How many developers are working on FinishLine?', '178 as of 2024', ner); + + await AnnouncementService.createAnnouncement( + 'Welcome to Finishline!', + [regina.userId], + new Date(), + 'Thomas Emrax', + '1', + 'software', + ner.organizationId + ); }; performSeed() diff --git a/src/backend/src/routes/users.routes.ts b/src/backend/src/routes/users.routes.ts index 802d586500..96be49828c 100644 --- a/src/backend/src/routes/users.routes.ts +++ b/src/backend/src/routes/users.routes.ts @@ -54,12 +54,8 @@ userRouter.post( validateInputs, UsersController.getManyUserTasks ); -userRouter.get('/:userId/notifications', UsersController.getUserUnreadNotifications); -userRouter.get('/:userId/announcements', UsersController.getUserUnreadAnnouncements); -userRouter.post( - '/:userId/notifications/remove', - nonEmptyString(body('notificationId')), - UsersController.removeUserNotification -); +userRouter.get('/notifications/current-user', UsersController.getUserUnreadNotifications); +userRouter.get('/announcements/current-user', UsersController.getUserUnreadAnnouncements); +userRouter.post('/notifications/:notificationId/remove', UsersController.removeUserNotification); export default userRouter; diff --git a/src/backend/src/services/announcement.service.ts b/src/backend/src/services/announcement.service.ts index 7be31175f0..342d5de296 100644 --- a/src/backend/src/services/announcement.service.ts +++ b/src/backend/src/services/announcement.service.ts @@ -5,6 +5,18 @@ import announcementTransformer from '../transformers/announcements.transformer'; import { NotFoundException } from '../utils/errors.utils'; export default class AnnouncementService { + /** + * Creates an announcement that is sent to users + * this data is populated from slack events + * @param text slack message text + * @param usersReceivedIds users to send announcements to + * @param dateCreated date created of slack message + * @param senderName name of user who sent slack message + * @param slackEventId id of slack event (provided by slack api) + * @param slackChannelName name of channel message was sent in + * @param organizationId id of organization of users + * @returns the created announcement + */ static async createAnnouncement( text: string, usersReceivedIds: string[], diff --git a/src/backend/src/services/users.services.ts b/src/backend/src/services/users.services.ts index fff25ef56b..b5b64e5f7f 100644 --- a/src/backend/src/services/users.services.ts +++ b/src/backend/src/services/users.services.ts @@ -578,13 +578,18 @@ export default class UsersService { * @returns the unread notifications of the user */ static async getUserUnreadNotifications(userId: string, organization: Organization) { - const requestedUser = await prisma.user.findUnique({ - where: { userId }, - include: { unreadNotifications: getNotificationQueryArgs(organization.organizationId) } + const unreadNotifications = await prisma.notification.findMany({ + where: { + users: { + some: { userId } + } + }, + ...getNotificationQueryArgs(organization.organizationId) }); - if (!requestedUser) throw new NotFoundException('User', userId); - return requestedUser.unreadNotifications.map(notificationTransformer); + if (!unreadNotifications) throw new HttpException(404, 'User Unread Notifications Not Found'); + + return unreadNotifications.map(notificationTransformer); } /** diff --git a/src/backend/tests/test-utils.ts b/src/backend/tests/test-utils.ts index fc6698ff06..5d3f3eea76 100644 --- a/src/backend/tests/test-utils.ts +++ b/src/backend/tests/test-utils.ts @@ -122,6 +122,7 @@ export const resetUsers = async () => { await prisma.organization.deleteMany(); await prisma.announcement.deleteMany(); await prisma.user.deleteMany(); + await prisma.announcement.deleteMany(); }; export const createFinanceTeamAndLead = async (organization?: Organization) => { diff --git a/src/backend/tests/unmocked/users.test.ts b/src/backend/tests/unmocked/users.test.ts index 512a651b90..bd0b934270 100644 --- a/src/backend/tests/unmocked/users.test.ts +++ b/src/backend/tests/unmocked/users.test.ts @@ -4,6 +4,7 @@ import { batmanAppAdmin } from '../test-data/users.test-data'; import UsersService from '../../src/services/users.services'; import { NotFoundException } from '../../src/utils/errors.utils'; import NotificationsService from '../../src/services/notifications.services'; +import AnnouncementService from '../../src/services/announcement.service'; describe('User Tests', () => { let orgId: string; @@ -51,12 +52,6 @@ describe('User Tests', () => { }); describe('Get Notifications', () => { - it('fails on invalid user id', async () => { - await expect(async () => await UsersService.getUserUnreadNotifications('1', organization)).rejects.toThrow( - new NotFoundException('User', '1') - ); - }); - it('Succeeds and gets user notifications', async () => { const testBatman = await createTestUser(batmanAppAdmin, orgId); await NotificationsService.sendNotifcationToUsers('test1', 'test1', [testBatman.userId], orgId); @@ -71,17 +66,7 @@ describe('User Tests', () => { }); describe('Remove Notifications', () => { - it('Fails with invalid user', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - await NotificationsService.sendNotifcationToUsers('test1', 'test1', [testBatman.userId], orgId); - const notifications = await UsersService.getUserUnreadNotifications(testBatman.userId, organization); - - await expect( - async () => await UsersService.removeUserNotification('1', notifications[0].notificationId, organization) - ).rejects.toThrow(new NotFoundException('User', '1')); - }); - - it('Succeeds and gets user notifications', async () => { + it('Succeeds and removes user notification', async () => { const testBatman = await createTestUser(batmanAppAdmin, orgId); await NotificationsService.sendNotifcationToUsers('test1', 'test1', [testBatman.userId], orgId); await NotificationsService.sendNotifcationToUsers('test2', 'test2', [testBatman.userId], orgId); @@ -102,4 +87,34 @@ describe('User Tests', () => { expect(updatedNotifications[0].text).toBe('test2'); }); }); + + describe('Get Announcements', () => { + it('Succeeds and gets user announcements', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + await AnnouncementService.createAnnouncement( + 'test1', + [testBatman.userId], + new Date(), + 'Thomas Emrax', + '1', + 'software', + organization.organizationId + ); + await AnnouncementService.createAnnouncement( + 'test2', + [testBatman.userId], + new Date(), + 'Superman', + '50', + 'mechanical', + organization.organizationId + ); + + const announcements = await UsersService.getUserUnreadAnnouncements(testBatman.userId, organization); + + expect(announcements).toHaveLength(2); + expect(announcements[0].text).toBe('test1'); + expect(announcements[1].text).toBe('test2'); + }); + }); });