diff --git a/client/src/models/__generated__/schema.d.ts b/client/src/models/__generated__/schema.d.ts index bd43d15dc..6959f9622 100644 --- a/client/src/models/__generated__/schema.d.ts +++ b/client/src/models/__generated__/schema.d.ts @@ -52,10 +52,6 @@ export interface paths { */ post: operations["GetBookingPayment"]; }; - "/events/signup": { - /** @description Signs up for an event */ - post: operations["EventSignup"]; - }; "/events": { /** * @description Fetches latest events starting from the event with the latest starting date @@ -63,13 +59,6 @@ export interface paths { */ get: operations["GetAllEvents"]; }; - "/events/reservations/stream": { - /** - * @description Streams the signup count for active events signups. - * Note that when testing this on swagger, the connection will remain open. - */ - get: operations["StreamSignupCounts"]; - }; "/events/{id}": { get: operations["GetEventById"]; }; @@ -314,35 +303,6 @@ export interface components { /** @description Firestore timestamp, should represent a UTC date that is set to exactly midnight */ endDate?: components["schemas"]["FirebaseFirestore.Timestamp"]; }; - EventSignupResponse: { - error?: string; - message?: string; - data?: { - email: string; - last_name: string; - first_name: string; - }; - }; - /** @description From T, pick a set of properties whose keys are in the union K */ - "Pick_EventReservation.Exclude_keyofEventReservation.timestamp__": { - /** @description The first name of the user who made this event reservation */ - first_name: string; - /** @description The last name of the user who made this event reservation */ - last_name: string; - /** @description The email of the user who made this even reservation */ - email: string; - /** - * @description Boolean to check if the user is a member - * @example true - */ - is_member: boolean; - }; - /** @description Construct a type with the properties of T except for those in type K. */ - "Omit_EventReservation.timestamp_": components["schemas"]["Pick_EventReservation.Exclude_keyofEventReservation.timestamp__"]; - EventSignupBody: { - event_id: string; - reservation: components["schemas"]["Omit_EventReservation.timestamp_"]; - }; Event: { /** @description The title of this event */ title: string; @@ -972,22 +932,6 @@ export interface operations { }; }; }; - /** @description Signs up for an event */ - EventSignup: { - requestBody: { - content: { - "application/json": components["schemas"]["EventSignupBody"]; - }; - }; - responses: { - /** @description Successfully signed up for Event */ - 200: { - content: { - "application/json": components["schemas"]["EventSignupResponse"]; - }; - }; - }; - }; /** * @description Fetches latest events starting from the event with the latest starting date * (**NOT** the signup open date) based on limit. Is paginated with a cursor @@ -1008,18 +952,6 @@ export interface operations { }; }; }; - /** - * @description Streams the signup count for active events signups. - * Note that when testing this on swagger, the connection will remain open. - */ - StreamSignupCounts: { - responses: { - /** @description No content */ - 204: { - content: never; - }; - }; - }; GetEventById: { parameters: { path: { diff --git a/server/src/data-layer/adapters/FirestoreSubcollections.ts b/server/src/data-layer/adapters/FirestoreSubcollections.ts index e72587390..e16d44df8 100644 --- a/server/src/data-layer/adapters/FirestoreSubcollections.ts +++ b/server/src/data-layer/adapters/FirestoreSubcollections.ts @@ -1,11 +1,6 @@ // credit https://plainenglish.io/blog/using-firestore-with-typescript-in-the-v9-sdk-cf36851bb099 import "dotenv/config" -import firestore from "./Firestore" -import { EventReservation } from "data-layer/models/firebase" -const FirestoreSubcollections = { - reservations: (eventId: string) => - firestore.subcollection("events", eventId, "reservations") -} as const +const FirestoreSubcollections = {} as const export default FirestoreSubcollections diff --git a/server/src/data-layer/models/firebase.ts b/server/src/data-layer/models/firebase.ts index 8a712c718..67a80ed32 100644 --- a/server/src/data-layer/models/firebase.ts +++ b/server/src/data-layer/models/firebase.ts @@ -130,30 +130,6 @@ export interface BookingChange { new_check_out: Timestamp // New check-out timestamp } -export interface EventReservation { - /** - * The first name of the user who made this event reservation - */ - first_name: string - /** - * The last name of the user who made this event reservation - */ - last_name: string - /** - * The email of the user who made this even reservation - */ - email: string - /** - * Boolean to check if the user is a member - * @example true - */ - is_member: boolean - /** - * This is the timestamp of when the reservation was made - */ - timestamp: Timestamp -} - export interface Event { /** * The title of this event diff --git a/server/src/data-layer/services/EventService.test.ts b/server/src/data-layer/services/EventService.test.ts index 75789c86b..dcc097a6b 100644 --- a/server/src/data-layer/services/EventService.test.ts +++ b/server/src/data-layer/services/EventService.test.ts @@ -4,7 +4,7 @@ import { dateToFirestoreTimeStamp, removeUnderscoresFromTimestamp } from "data-layer/adapters/DateUtils" -import { Event, EventReservation } from "data-layer/models/firebase" +import { Event } from "data-layer/models/firebase" import FirestoreCollections from "data-layer/adapters/FirestoreCollections" import { Timestamp } from "firebase-admin/firestore" @@ -46,21 +46,6 @@ const futureEvent: Event = { sign_up_end_date: Timestamp.fromDate(new Date(now.getUTCFullYear() + 1, 1, 1)) } -const reservation1: EventReservation = { - first_name: "John", - last_name: "Appleseed", - email: "test@gmail.com", - is_member: true, - timestamp: Timestamp.fromDate(startDate) -} -const reservation2: EventReservation = { - first_name: "Jane", - last_name: "Pearseed", - email: "test2@gmail.com", - is_member: false, - timestamp: Timestamp.fromDate(startDate) -} - describe("EventService integration tests", () => { afterEach(async () => { await cleanFirestore() @@ -160,151 +145,4 @@ describe("EventService integration tests", () => { expect(fetchedEvent).toBe(undefined) }) - - it("Should delete an event and also all reservations", async () => { - const newEvent = await eventService.createEvent(event1) - const newReservation1 = await eventService.addReservation( - newEvent.id, - reservation1 - ) - const newReservation2 = await eventService.addReservation( - newEvent.id, - reservation2 - ) - - await eventService.deleteEvent(newEvent.id) - - const fetchedReservation1 = await eventService.getReservationById( - newEvent.id, - newReservation1.id - ) - expect(fetchedReservation1).toBe(undefined) - const fetchedReservation2 = await eventService.getReservationById( - newEvent.id, - newReservation2.id - ) - expect(fetchedReservation2).toBe(undefined) - }) - - it("Should not delete other reservations when deleting an event document", async () => { - const newEvent = await eventService.createEvent(event1) - await eventService.addReservation(newEvent.id, reservation1) - await eventService.addReservation(newEvent.id, reservation2) - const newEvent2 = await eventService.createEvent(event2) - const newReservation3 = await eventService.addReservation( - newEvent2.id, - reservation1 - ) - const newReservation4 = await eventService.addReservation( - newEvent2.id, - reservation2 - ) - - await eventService.deleteEvent(newEvent.id) - const fetchedReservation3 = await eventService.getReservationById( - newEvent2.id, - newReservation3.id - ) - expect(fetchedReservation3).toEqual(reservation1) - const fetchedReservation4 = await eventService.getReservationById( - newEvent2.id, - newReservation4.id - ) - expect(fetchedReservation4).toEqual(reservation2) - }) - - /** - * Event reservation nested collection methods - */ - describe("EventReservation integration tests", () => { - it("Should be able to add a event reservation", async () => { - const newEvent = await eventService.createEvent(event1) - - const reservation = await eventService.addReservation( - newEvent.id, - reservation1 - ) - const fetchedReservation = await FirestoreCollections.events - .doc(newEvent.id) - .collection("reservations") // subject to place as a constant somewhere - .doc(reservation.id) - .get() - expect(fetchedReservation.data()).toEqual(reservation1) - }) - - it("Should be able to get an event reservation", async () => { - const newEvent = await eventService.createEvent(event1) - const reservation = await eventService.addReservation( - newEvent.id, - reservation1 - ) - const fetchedReservation = await eventService.getReservationById( - newEvent.id, - reservation.id - ) - expect(fetchedReservation).toEqual(reservation1) - }) - - it("Should get the total count of active event reservations", async () => { - // An older event shouldn't be counted. - const oldEvent = await eventService.createEvent(event1) - await eventService.addReservation(oldEvent.id, reservation1) - // Should only count reservations for future events - const newEvent = await eventService.createEvent(futureEvent) - await eventService.addReservation(newEvent.id, reservation1) - await eventService.addReservation(newEvent.id, reservation2) - - const eventCounts = await eventService.getActiveReservationsCount() - expect(eventCounts).toStrictEqual({ [newEvent.id]: 2 }) - }) - - it("Should get all event reservations", async () => { - const newEvent = await eventService.createEvent(event1) - await eventService.addReservation(newEvent.id, reservation1) - await eventService.addReservation(newEvent.id, reservation2) - const reservations = await eventService.getAllReservations(newEvent.id) - expect(reservations.length).toBe(2) - expect(reservations).toContainEqual(reservation1) - expect(reservations).toContainEqual(reservation2) - }) - - it("Should be able to update an event reservation", async () => { - const newEvent = await eventService.createEvent(event1) - - const reservation = await eventService.addReservation( - newEvent.id, - reservation1 - ) - - await eventService.updateReservation(newEvent.id, reservation.id, { - first_name: "Jan" - }) - - const fetchedReservation = await FirestoreCollections.events - .doc(newEvent.id) - .collection("reservations") // subject to place as a constant somewhere - .doc(reservation.id) - .get() - - expect(fetchedReservation.data().first_name).toBe("Jan") - }) - - it("Should be able to delete an event reservation", async () => { - const newEvent = await eventService.createEvent(event1) - - const reservation = await eventService.addReservation( - newEvent.id, - reservation1 - ) - - await eventService.deleteReservation(newEvent.id, reservation.id) - - const fetchedReservation = await FirestoreCollections.events - .doc(newEvent.id) - .collection("reservations") // subject to place as a constant somewhere - .doc(reservation.id) - .get() - expect(fetchedReservation.data()).toBe(undefined) - }) - }) }) diff --git a/server/src/data-layer/services/EventService.ts b/server/src/data-layer/services/EventService.ts index b8b29dd5d..35efea276 100644 --- a/server/src/data-layer/services/EventService.ts +++ b/server/src/data-layer/services/EventService.ts @@ -1,7 +1,6 @@ import FirestoreCollections from "data-layer/adapters/FirestoreCollections" -import FirestoreSubcollections from "data-layer/adapters/FirestoreSubcollections" import { DocumentDataWithUid } from "data-layer/models/common" -import { Event, EventReservation } from "data-layer/models/firebase" +import { Event } from "data-layer/models/firebase" class EventService { /** @@ -56,109 +55,13 @@ class EventService { /** * Deletes an existing event document by ID. - * Also deletes all reservation docs when deleting an event. * * @param eventId the ID of the event document */ public async deleteEvent(eventId: string) { - // Need to delete subcollections under this first - const snapshot = await FirestoreSubcollections.reservations(eventId).get() - const deletePromises = snapshot.docs.map((doc) => doc.ref.delete()) - await Promise.all(deletePromises) - // Delete main collection doc after deleting reservations return await FirestoreCollections.events.doc(eventId).delete() } - /** - * Event reservation collection methods - */ - - /** - * Adds a reservation to an event. - * - * @param eventId the ID of the event document - * @param reservation the new EventReservation to add - * @returns the created reservation document reference - */ - public async addReservation(eventId: string, reservation: EventReservation) { - return await FirestoreSubcollections.reservations(eventId).add(reservation) - } - - /** - * Gets an existing reservation document by ID. - * - * @param eventId the ID of the event document - * @param reservationId the ID of the reservation document - * @returns the reservation document - */ - public async getReservationById( - eventId: string, - reservationId: string - ): Promise { - const result = await FirestoreSubcollections.reservations(eventId) - .doc(reservationId) - .get() - - return result.data() - } - - /** - * Gets all reservations for an event. - * @param eventId the ID of the event document - * @returns an array of all the event reservation documents - */ - public async getAllReservations( - eventId: string - ): Promise { - const result = await FirestoreSubcollections.reservations(eventId).get() - return result.docs.map((doc) => doc.data()) - } - - /** - * Used for the SSE feature to display the total number of active event reservations. - * @returns a record of all the event ids and their event count - */ - public async getActiveReservationsCount(): Promise> { - const currentEvents = await this.getActiveEvents() - const output: Record = {} - await Promise.all( - currentEvents.map(async (event) => { - const eventReservations = await this.getAllReservations(event.id) - output[`${event.id}`] = eventReservations.length - }) - ) - return output - } - - /** - * Updates an existing reservation document by ID with new EventReservation data. - * - * @param eventId the ID of the event document - * @param reservationId the ID of the reservation document - * @param updatedReservation the new EventReservation data to update - */ - public async updateReservation( - eventId: string, - reservationId: string, - updatedReservation: Partial - ) { - return await FirestoreSubcollections.reservations(eventId) - .doc(reservationId) - .update(updatedReservation) - } - - /** - * Deletes a reservation from an event. - * - * @param eventId the ID of the event document - * @param reservationId the ID of the reservation document - */ - public async deleteReservation(eventId: string, reservationId: string) { - return await FirestoreSubcollections.reservations(eventId) - .doc(reservationId) - .delete() - } - /** * Utility method for getting the snapshot of a document * diff --git a/server/src/middleware/__generated__/routes.ts b/server/src/middleware/__generated__/routes.ts index e946a33f6..5dc2290e0 100644 --- a/server/src/middleware/__generated__/routes.ts +++ b/server/src/middleware/__generated__/routes.ts @@ -180,35 +180,6 @@ const models: TsoaRoute.Models = { "additionalProperties": false, }, // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "EventSignupResponse": { - "dataType": "refObject", - "properties": { - "error": {"dataType":"string"}, - "message": {"dataType":"string"}, - "data": {"dataType":"nestedObjectLiteral","nestedProperties":{"email":{"dataType":"string","required":true},"last_name":{"dataType":"string","required":true},"first_name":{"dataType":"string","required":true}}}, - }, - "additionalProperties": false, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "Pick_EventReservation.Exclude_keyofEventReservation.timestamp__": { - "dataType": "refAlias", - "type": {"dataType":"nestedObjectLiteral","nestedProperties":{"first_name":{"dataType":"string","required":true},"last_name":{"dataType":"string","required":true},"email":{"dataType":"string","required":true},"is_member":{"dataType":"boolean","required":true}},"validators":{}}, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "Omit_EventReservation.timestamp_": { - "dataType": "refAlias", - "type": {"ref":"Pick_EventReservation.Exclude_keyofEventReservation.timestamp__","validators":{}}, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - "EventSignupBody": { - "dataType": "refObject", - "properties": { - "event_id": {"dataType":"string","required":true}, - "reservation": {"ref":"Omit_EventReservation.timestamp_","required":true}, - }, - "additionalProperties": false, - }, - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa "Event": { "dataType": "refObject", "properties": { @@ -899,36 +870,6 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - app.post('/events/signup', - ...(fetchMiddlewares(EventController)), - ...(fetchMiddlewares(EventController.prototype.eventSignup)), - - function EventController_eventSignup(request: ExRequest, response: ExResponse, next: any) { - const args: Record = { - requestBody: {"in":"body","name":"requestBody","required":true,"ref":"EventSignupBody"}, - }; - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - let validatedArgs: any[] = []; - try { - validatedArgs = templateService.getValidatedArgs({ args, request, response }); - - const controller = new EventController(); - - templateService.apiHandler({ - methodName: 'eventSignup', - controller, - response, - next, - validatedArgs, - successStatus: 200, - }); - } catch (err) { - return next(err); - } - }); - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.get('/events', ...(fetchMiddlewares(EventController)), ...(fetchMiddlewares(EventController.prototype.getAllEvents)), @@ -960,36 +901,6 @@ export function RegisterRoutes(app: Router) { } }); // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - app.get('/events/reservations/stream', - ...(fetchMiddlewares(EventController)), - ...(fetchMiddlewares(EventController.prototype.streamSignupCounts)), - - function EventController_streamSignupCounts(request: ExRequest, response: ExResponse, next: any) { - const args: Record = { - req: {"in":"request","name":"req","required":true,"dataType":"object"}, - }; - - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa - - let validatedArgs: any[] = []; - try { - validatedArgs = templateService.getValidatedArgs({ args, request, response }); - - const controller = new EventController(); - - templateService.apiHandler({ - methodName: 'streamSignupCounts', - controller, - response, - next, - validatedArgs, - successStatus: undefined, - }); - } catch (err) { - return next(err); - } - }); - // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa app.get('/events/:id', ...(fetchMiddlewares(EventController)), ...(fetchMiddlewares(EventController.prototype.getEventById)), diff --git a/server/src/middleware/__generated__/swagger.json b/server/src/middleware/__generated__/swagger.json index 5bccffd1a..1e4812ef9 100644 --- a/server/src/middleware/__generated__/swagger.json +++ b/server/src/middleware/__generated__/swagger.json @@ -395,86 +395,6 @@ "type": "object", "additionalProperties": false }, - "EventSignupResponse": { - "properties": { - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "data": { - "properties": { - "email": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "first_name": { - "type": "string" - } - }, - "required": [ - "email", - "last_name", - "first_name" - ], - "type": "object" - } - }, - "type": "object", - "additionalProperties": false - }, - "Pick_EventReservation.Exclude_keyofEventReservation.timestamp__": { - "properties": { - "first_name": { - "type": "string", - "description": "The first name of the user who made this event reservation" - }, - "last_name": { - "type": "string", - "description": "The last name of the user who made this event reservation" - }, - "email": { - "type": "string", - "description": "The email of the user who made this even reservation" - }, - "is_member": { - "type": "boolean", - "description": "Boolean to check if the user is a member", - "example": true - } - }, - "required": [ - "first_name", - "last_name", - "email", - "is_member" - ], - "type": "object", - "description": "From T, pick a set of properties whose keys are in the union K" - }, - "Omit_EventReservation.timestamp_": { - "$ref": "#/components/schemas/Pick_EventReservation.Exclude_keyofEventReservation.timestamp__", - "description": "Construct a type with the properties of T except for those in type K." - }, - "EventSignupBody": { - "properties": { - "event_id": { - "type": "string" - }, - "reservation": { - "$ref": "#/components/schemas/Omit_EventReservation.timestamp_" - } - }, - "required": [ - "event_id", - "reservation" - ], - "type": "object", - "additionalProperties": false - }, "Event": { "properties": { "title": { @@ -1870,36 +1790,6 @@ } } }, - "/events/signup": { - "post": { - "operationId": "EventSignup", - "responses": { - "200": { - "description": "Successfully signed up for Event", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EventSignupResponse" - } - } - } - } - }, - "description": "Signs up for an event", - "security": [], - "parameters": [], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EventSignupBody" - } - } - } - } - } - }, "/events": { "get": { "operationId": "GetAllEvents", @@ -1939,19 +1829,6 @@ ] } }, - "/events/reservations/stream": { - "get": { - "operationId": "StreamSignupCounts", - "responses": { - "204": { - "description": "No content" - } - }, - "description": "Streams the signup count for active events signups.\nNote that when testing this on swagger, the connection will remain open.", - "security": [], - "parameters": [] - } - }, "/events/{id}": { "get": { "operationId": "GetEventById", diff --git a/server/src/middleware/tests/EventController.test.ts b/server/src/middleware/tests/EventController.test.ts index 38d8836d5..663c3a8e3 100644 --- a/server/src/middleware/tests/EventController.test.ts +++ b/server/src/middleware/tests/EventController.test.ts @@ -1,6 +1,6 @@ import EventService from "data-layer/services/EventService" import { request } from "../routes.setup" -import { Event, EventReservation } from "../../data-layer/models/firebase" +import { Event } from "../../data-layer/models/firebase" import { Timestamp } from "firebase-admin/firestore" const earlierStartDate = Timestamp.fromDate(new Date(2023, 1, 1)) @@ -27,12 +27,6 @@ const event2: Event = { sign_up_start_date: startDate, sign_up_end_date: endDate } -const reservation1: Omit = { - first_name: "John", - last_name: "Doe", - email: "test@email.com", - is_member: true -} describe("EventController endpoint tests", () => { const eventService = new EventService() @@ -64,60 +58,6 @@ describe("EventController endpoint tests", () => { }) }) - describe("/events/signup", () => { - it("should return 404 if the event does not exist", async () => { - const res = await request.post("/events/signup").send({ - event_id: "non-existent-event", - reservation: reservation1 - }) - expect(res.status).toEqual(404) - }) - - it("should return 400 if the event is full", async () => { - const event = await eventService.createEvent({ - ...event1, - max_occupancy: 0 - }) - const res = await request.post("/events/signup").send({ - event_id: event.id, - reservation: reservation1 - }) - expect(res.status).toEqual(400) - expect(res.body.error).toEqual("Maximum event occupancy reached.") - }) - - it("should return 400 if already signed up to event", async () => { - const event = await eventService.createEvent(event1) - await eventService.addReservation(event.id, { - ...reservation1, - timestamp: Timestamp.now() - }) - const res = await request.post("/events/signup").send({ - event_id: event.id, - reservation: reservation1 - }) - expect(res.status).toEqual(400) - expect(res.body.error).toEqual( - "You have already signed up for this event." - ) - }) - - it("should allow user to signup to an event", async () => { - const event = await eventService.createEvent(event1) - const res = await request.post("/events/signup").send({ - event_id: event.id, - reservation: reservation1 - }) - expect(res.status).toEqual(200) - expect(res.body.message).toEqual("Successfully signed up for event.") - expect(res.body.data).toEqual({ - first_name: reservation1.first_name, - last_name: reservation1.last_name, - email: reservation1.email - }) - }) - }) - describe("GET /events/:id", () => { it("should return the event details for a valid event ID", async () => { const { id: id1 } = await eventService.createEvent(event1) diff --git a/server/src/service-layer/controllers/EventController.ts b/server/src/service-layer/controllers/EventController.ts index d3ebc88a0..cd5ea777c 100644 --- a/server/src/service-layer/controllers/EventController.ts +++ b/server/src/service-layer/controllers/EventController.ts @@ -1,83 +1,12 @@ import EventService from "data-layer/services/EventService" -import { EventSignupBody } from "service-layer/request-models/EventRequests" import { - EventSignupResponse, GetAllEventsResponse, GetEventResponse } from "service-layer/response-models/EventResponse" -import { - Body, - Controller, - Get, - Path, - Post, - Query, - Request, - Route, - SuccessResponse -} from "tsoa" -import express from "express" -import { Timestamp } from "firebase-admin/firestore" +import { Controller, Get, Path, Query, Route, SuccessResponse } from "tsoa" @Route("events") export class EventController extends Controller { - /** - * Signs up for an event - */ - @SuccessResponse("200", "Successfully signed up for Event") - @Post("signup") - public async eventSignup( - @Body() requestBody: EventSignupBody - ): Promise { - const { event_id, reservation } = requestBody - const eventService = new EventService() - // Check if the event exists - const fetchedEvent = await eventService.getEventById(event_id) - if (!fetchedEvent) { - this.setStatus(404) - return { error: "Event not found." } - } - // Check if the event is full - const reservations = await eventService.getAllReservations(event_id) - if ( - fetchedEvent.max_occupancy !== undefined && - reservations.length >= fetchedEvent.max_occupancy - ) { - this.setStatus(400) - return { error: "Maximum event occupancy reached." } - } - // Check if the user is already signed up - if ( - reservations.some( - (r) => - r.email.trim().toLowerCase() === - reservation.email.trim().toLowerCase() - ) - ) { - this.setStatus(400) - return { error: "You have already signed up for this event." } - } - // Sign up the user - try { - await eventService.addReservation(event_id, { - ...reservation, - timestamp: Timestamp.now() - }) - this.setStatus(200) - return { - message: "Successfully signed up for event.", - data: { - first_name: reservation.first_name, - last_name: reservation.last_name, - email: reservation.email - } - } - } catch (e) { - this.setStatus(500) - return { error: "Failed to sign up for event." } - } - } - /** * Fetches latest events starting from the event with the latest starting date * (**NOT** the signup open date) based on limit. Is paginated with a cursor @@ -105,41 +34,6 @@ export class EventController extends Controller { } } - /** - * Streams the signup count for active events signups. - * Note that when testing this on swagger, the connection will remain open. - */ - @Get("/reservations/stream") - public async streamSignupCounts( - @Request() req: express.Request - ): Promise { - // Set the required headers for SSE - req.res.setHeader("Cache-Control", "no-cache") - req.res.setHeader("Content-Type", "text/event-stream") - req.res.setHeader("Access-Control-Allow-Origin", "*") - req.res.setHeader("Connection", "keep-alive") - req.res.flushHeaders() - const eventService = new EventService() - - const signupRecord: Record = - await eventService.getActiveReservationsCount() // Fetch the current signup count - req.res.write(`data: ${JSON.stringify(signupRecord)}\n\n`) - - const interValID = setInterval(async () => { - const signupRecord: Record = - await eventService.getActiveReservationsCount() - // NOTE: We use double new line because SSE requires this to indicate we're ready for the next event - // We also need the data: to indicate data payload - req.res.write(`data: ${JSON.stringify(signupRecord)}\n\n`) // res.write() instead of res.send() - }, 5000) - - // If the connection drops, stop sending events - req.res?.on("close", () => { - clearInterval(interValID) // Clear the loop - req.res?.end() - }) - } - @Get("{id}") @SuccessResponse("200", "Successfully fetched the event") public async getEventById(@Path() id: string): Promise { diff --git a/server/src/service-layer/request-models/EventRequests.ts b/server/src/service-layer/request-models/EventRequests.ts index 889cdc5b4..393a1e9bd 100644 --- a/server/src/service-layer/request-models/EventRequests.ts +++ b/server/src/service-layer/request-models/EventRequests.ts @@ -1,9 +1,4 @@ -import { EventReservation, Event } from "data-layer/models/firebase" - -export interface EventSignupBody { - event_id: string - reservation: Omit -} +import { Event } from "data-layer/models/firebase" export interface CreateEventBody { data: Event diff --git a/server/src/service-layer/response-models/EventResponse.ts b/server/src/service-layer/response-models/EventResponse.ts index 444f98d41..86dc8d221 100644 --- a/server/src/service-layer/response-models/EventResponse.ts +++ b/server/src/service-layer/response-models/EventResponse.ts @@ -1,14 +1,6 @@ import { CommonResponse, CursorPaginatedResponse } from "./CommonResponse" import { Event } from "data-layer/models/firebase" -export interface EventSignupResponse extends CommonResponse { - data?: { - first_name: string - last_name: string - email: string - } -} - export interface GetAllEventsResponse extends CommonResponse, CursorPaginatedResponse {