From fe429d52e44be295b130d6e55d229c89a52324f2 Mon Sep 17 00:00:00 2001 From: Alyssa Zhang Date: Sat, 4 May 2024 12:02:38 -0400 Subject: [PATCH] initial commit --- backend/src/API/coffeeChatAPI.ts | 52 ++++++++++++++++ backend/src/api.ts | 6 ++ backend/src/dao/CoffeeChatDao.ts | 101 +++++++++++++++++++++++++++++++ frontend/next-env.d.ts | 1 - 4 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 backend/src/API/coffeeChatAPI.ts create mode 100644 backend/src/dao/CoffeeChatDao.ts diff --git a/backend/src/API/coffeeChatAPI.ts b/backend/src/API/coffeeChatAPI.ts new file mode 100644 index 000000000..b59975f03 --- /dev/null +++ b/backend/src/API/coffeeChatAPI.ts @@ -0,0 +1,52 @@ +import CoffeeChatDao from '../dao/CoffeeChatDao'; +import isEqual from 'lodash.isequal'; +import PermissionsManager from '../utils/permissionsManager'; +import { PermissionError } from '../utils/errors'; + +const coffeeChatDao = new CoffeeChatDao(); + +//get all coffee chat instances +export const getAllCoffeeChats = (): Promise => coffeeChatDao.getAllCoffeeChats(); + +//get coffee chat instances for user +export const getCoffeeChatsByUser = async (user: IdolMember): Promise => + coffeeChatDao.getCoffeeChatsByUser(user); + +// create new coffee chat instance +export const createCoffeeChat = async (coffeeChat: CoffeeChat): Promise => { + const [member1, member2] = coffeeChat.members; + + if (isEqual(member1, member2)) { + throw new Error('Cannot create coffee chat with yourself.'); + } + const prevChats1 = await coffeeChatDao.getCoffeeChatsByUser(member1); + const prevChats2 = await coffeeChatDao.getCoffeeChatsByUser(member2); + const prevChats = [...prevChats1, ...prevChats2]; + if ( + prevChats.some( + (c) => isEqual(c.members, [member1, member2]) || isEqual(c.members, [member2, member1]) + ) + ) { + throw new Error( + 'Cannot create coffee chat with member. Previous coffee chats from previous semesters exist.' + ); + } + await coffeeChatDao.createCoffeeChat(coffeeChat); + return coffeeChat; +}; + +// update coffee chat instance +export const updateCoffeeChat = async (coffeeChat: CoffeeChat): Promise => { + await coffeeChatDao.updateCoffeeChat(coffeeChat); + return coffeeChat; +}; + +//delete all coffee chats +export const clearAllCoffeeChats = async (user: IdolMember): Promise => { + const isLeadOrAdmin = await PermissionsManager.isLeadOrAdmin(user); + if (!isLeadOrAdmin) + throw new PermissionError( + 'User with email ${user.email} does not have sufficient permissions to delete all coffee chats.' + ); + await CoffeeChatDao.deleteAllCoffeeChat(); +}; diff --git a/backend/src/api.ts b/backend/src/api.ts index a44a36a17..7d6e12dd7 100644 --- a/backend/src/api.ts +++ b/backend/src/api.ts @@ -81,6 +81,7 @@ import { regradeSubmissions, updateSubmissions } from './API/devPortfolioAPI'; +import { getAllCoffeeChats } from './API/coffeeChatAPI'; import DPSubmissionRequestLogDao from './dao/DPSubmissionRequestLogDao'; import AdminsDao from './dao/AdminsDao'; import { sendMail } from './API/mailAPI'; @@ -345,6 +346,11 @@ loginCheckedGet('/event-proof-image/:name(*)', async (req, user) => ({ url: await getEventProofImage(req.params.name, user) })); +// Coffee Chat +loginCheckedGet('/coffee-chat', async () => ({ + coffeeChats: await getAllCoffeeChats() +})); + // TODO: Modify this endpoint to /event-proof-image/* to be more RESTful loginCheckedGet('/event-proof-image-signed-url/:name(*)', async (req, user) => ({ url: await setEventProofImage(req.params.name, user) diff --git a/backend/src/dao/CoffeeChatDao.ts b/backend/src/dao/CoffeeChatDao.ts new file mode 100644 index 000000000..c6881baa6 --- /dev/null +++ b/backend/src/dao/CoffeeChatDao.ts @@ -0,0 +1,101 @@ +import { v4 as uuidv4 } from 'uuid'; +import { db, memberCollection, coffeeChatsCollection } from '../firebase'; +import { DBCoffeeChat } from '../types/DataTypes'; +import { getMemberFromDocumentReference } from '../utils/memberUtil'; +import BaseDao from './BaseDao'; +import { deleteCollection } from '../utils/firebase-utils'; + +async function materializeCoffeeChat(dbCoffeeChat: DBCoffeeChat): Promise { + const member1 = await getMemberFromDocumentReference(dbCoffeeChat.members[0]); + const member2 = await getMemberFromDocumentReference(dbCoffeeChat.members[1]); + + return { + ...dbCoffeeChat, + members: [member1, member2] + }; +} + +async function serializeCoffeeChat(coffeeChat: CoffeeChat): Promise { + const member1Data = await memberCollection.doc(coffeeChat.members[0].email); + const member2Data = await memberCollection.doc(coffeeChat.members[1].email); + + return { + ...coffeeChat, + members: [member1Data, member2Data] + }; +} + +export default class CoffeeChatDao extends BaseDao { + constructor() { + super(coffeeChatsCollection, materializeCoffeeChat, serializeCoffeeChat); + } + + /** + * Creates a new coffee chat for member + * @param coffeeChat - Newly created CoffeeChat object. + * If provided, the object uuid will be used. If not, a new one will be generated. + * The pending field will be set to true by default. + * A member can not create a coffee chat the same person from previous semesters + */ + + async createCoffeeChat(coffeeChat: CoffeeChat): Promise { + const coffeeChatWithUUID = { + ...coffeeChat, + status: 'pending' as Status, + uuid: coffeeChat.uuid ? coffeeChat.uuid : uuidv4() + }; + return this.createDocument(coffeeChatWithUUID.uuid, coffeeChatWithUUID); + } + + /** + * Gets the coffee chats + * @param uuid - DB uuid of coffee chat + */ + async getCoffeeChat(uuid: string): Promise { + return this.getDocument(uuid); + } + + /** + * Gets all coffee chats + */ + async getAllCoffeeChats(): Promise { + return this.getDocuments(); + } + + /** + * Updates a coffee chat + * @param coffeeChat - updated Coffee Chat object + */ + async updateCoffeeChat(coffeeChat: CoffeeChat): Promise { + return this.updateDocument(coffeeChat.uuid, coffeeChat); + } + + /** + * Gets all coffee chat for a user + * @param user - user whose coffee chats should be fetched + */ + async getCoffeeChatsByUser(user: IdolMember): Promise { + return this.getDocuments([ + { + field: 'member', + comparisonOperator: '==', + value: memberCollection.doc(user.email) + } + ]); + } + + /** + * Deletes a coffee chat + * @param uuid - DB uuid of CoffeeChat + */ + async deleteCoffeeChat(uuisd: string): Promise { + await this.getDocuments(); + } + + /** + * Deletes all coffee chats for all users + */ + static async deleteAllCoffeeChat(): Promise { + await deleteCollection(db, 'coffee-chats', 500); + } +} diff --git a/frontend/next-env.d.ts b/frontend/next-env.d.ts index 9bc3dd46b..4f11a03dc 100644 --- a/frontend/next-env.d.ts +++ b/frontend/next-env.d.ts @@ -1,5 +1,4 @@ /// -/// /// // NOTE: This file should not be edited