Skip to content

Commit

Permalink
Hotfix: Fix the events endpoint not working (#44)
Browse files Browse the repository at this point in the history
* Added hotfix logs

* Got event to work + added returns

* Fixed docs

* Fixed events endpoint

* Fixed linter issue

* Fixed API crashing
  • Loading branch information
AydanPirani committed Sep 26, 2023
1 parent ac8885e commit b682ddb
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 40 deletions.
5 changes: 2 additions & 3 deletions src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,10 @@ abstract class Constants {

static readonly DEFAULT_JWT_OFFSET: string = "48h";

// Conversions for datetimes
static readonly MILLISECONDS_PER_SECOND:number = 1000;

// Constants for general usage
static readonly ZERO: number = 0;
static readonly EVENT_ID_LENGTH: number = 16;
static readonly MILLISECONDS_PER_SECOND:number = 1000;

// Constants for database names
static readonly AUTH_DB: string = "auth";
Expand Down
2 changes: 0 additions & 2 deletions src/services/auth/auth-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ authRouter.get("/dev/", (req: Request, res: Response) => {

/**
* @api {get} /auth/login/github/ GET /auth/login/github/
* @apiName Github
* @apiGroup Auth
* @apiDescription Perform Github authentication for an attendee.
*
Expand Down Expand Up @@ -79,7 +78,6 @@ authRouter.get("/login/github/", (req: Request, res: Response, next: NextFunctio

/**
* @api {get} /auth/login/google/ GET /auth/login/google/
* @apiName Google
* @apiGroup Auth
* @apiDescription Perform Google authentication for a staff member.
*
Expand Down
7 changes: 6 additions & 1 deletion src/services/event/event-formats.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Constants from "../../constants.js";
import { Location, EVENT_TYPE } from "./event-models.js";


Expand Down Expand Up @@ -26,10 +27,13 @@ export interface EventFormat {
*
*/
export function isEventFormat(obj: EventFormat): boolean {
if (typeof obj.id !== "string" || obj.id.length != Constants.EVENT_ID_LENGTH) {
return false;
}

if (
typeof obj.isPrivate !== "boolean" ||
typeof obj.displayOnStaffCheckIn !== "boolean" ||
typeof obj.id !== "string" ||
typeof obj.name !== "string" ||
typeof obj.description !== "string" ||
typeof obj.startTime !== "number" ||
Expand All @@ -54,6 +58,7 @@ export function isEventFormat(obj: EventFormat): boolean {
}
}


if (
typeof obj.sponsor !== "string" ||
typeof obj.eventType !== "string" ||
Expand Down
57 changes: 25 additions & 32 deletions src/services/event/event-router.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import cors from "cors";
import { Request, Router } from "express";
import crypto from "crypto";
import { Response } from "express-serve-static-core";
import { Collection, Document, Filter, UpdateFilter } from "mongodb";

Expand All @@ -22,7 +23,6 @@ eventsRouter.use(cors({ origin: "*" }));

/**
* @api {get} /event/:EVENTID GET /event/:EVENTID
* @apiName Get Event by ID
* @apiGroup Event
* @apiDescription Get event details by its unique ID.
*
Expand Down Expand Up @@ -66,7 +66,7 @@ eventsRouter.get("/:EVENTID/", weakJwtVerification, async (req: Request, res: Re
const eventId: string | undefined = req.params.EVENTID;

if (!eventId) {
res.redirect("/");
return res.redirect("/");
}

try {
Expand All @@ -76,23 +76,22 @@ eventsRouter.get("/:EVENTID/", weakJwtVerification, async (req: Request, res: Re
if (event.isPrivate) {
// If event is private and we're elevated, return the event -> else, return forbidden
if (isElevated) {
res.status(Constants.SUCCESS).send({ event: event });
return res.status(Constants.SUCCESS).send({ event: event });
} else {
res.status(Constants.FORBIDDEN).send({ error: "PrivateEvent" });
return res.status(Constants.FORBIDDEN).send({ error: "PrivateEvent" });
}
} else {
// Not a private event -> convert to Public event and return
res.status(Constants.SUCCESS).send({ event: truncateToPublicEvent(event) });
return res.status(Constants.SUCCESS).send({ event: truncateToPublicEvent(event) });
}
} catch {
res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
}
});


/**
* @api {delete} /event/:EVENTID DELETE /event/:EVENTID
* @apiName Delete Event by ID
* @apiGroup Event
* @apiDescription Delete an event by its unique ID.
*
Expand All @@ -113,30 +112,29 @@ eventsRouter.delete("/:EVENTID/", strongJwtVerification, async (req: Request, re

// Check if request sender has permission to delete the event
if (!hasElevatedPerms(res.locals.payload as JwtPayload)) {
res.status(Constants.FORBIDDEN).send({ error: "InvalidPermission" });
return res.status(Constants.FORBIDDEN).send({ error: "InvalidPermission" });
}

// Check if event doesn't exist -> if not, returns error
if (!eventId) {
res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
}

const collection: Collection = databaseClient.db(Constants.EVENT_DB).collection(EventDB.EVENTS);

// Perform a lazy delete, and return true if not existent
try {
await collection.deleteOne({ id: eventId });
res.sendStatus(Constants.SUCCESS);
return res.sendStatus(Constants.SUCCESS);
} catch (error) {
console.error(error);
res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
}
});


/**
* @api {post} /event/staff/attendance/ POST /event/staff/attendance/
* @apiName Record Staff Attendance
* @apiGroup Event
* @apiDescription Record staff attendance for an event.
*
Expand Down Expand Up @@ -169,13 +167,11 @@ eventsRouter.post("/staff/attendance/", strongJwtVerification, async (req: Reque

// Only staff can mark themselves as attending these events
if (!hasElevatedPerms(token)) {
res.status(Constants.FORBIDDEN).send({ error: "Forbidden" });
return;
return res.status(Constants.FORBIDDEN).send({ error: "Forbidden" });
}

if (!eventId) {
res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
return;
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
}

const eventsCollection: Collection = databaseClient.db(Constants.EVENT_DB).collection(EventDB.STAFF_ATTENDANCE);
Expand All @@ -184,17 +180,16 @@ eventsRouter.post("/staff/attendance/", strongJwtVerification, async (req: Reque
try {
await eventsCollection.updateOne({ id: eventId }, { "$addToSet": { "attendees": token.id } }, { upsert: true });
await staffCollection.updateOne({ id: token.id }, { "$addToSet": { "attendance": eventId } }, { upsert: true });
res.sendStatus(Constants.SUCCESS);
return res.sendStatus(Constants.SUCCESS);
} catch (error) {
console.error(error);
res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
}
});


/**
* @api {get} /event/ GET /event/
* @apiName Event
* @apiGroup Event
* @apiDescription Get all the publicly-available events
* @apiSuccess (200: Success) {Json} events All publicly-facing events.
Expand Down Expand Up @@ -256,16 +251,15 @@ eventsRouter.get("/", weakJwtVerification, async (_: Request, res: Response) =>
// Get collection from the database, and return it as an array
const events: PrivateEventSchema[] = await collection.find(filter).toArray() as PrivateEventSchema[];
const cleanedEvents: PrivateEvent[] | PublicEvent[] = isElevated ? events : events.map(truncateToPublicEvent);
res.status(Constants.SUCCESS).send({ events: cleanedEvents });
return res.status(Constants.SUCCESS).send({ events: cleanedEvents });
} catch {
res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
}
});


/**
* @api {post} /event/ POST /event/
* @apiName Create or Update Event
* @apiGroup Event
* @apiDescription Create a new event or update an existing event.
*
Expand Down Expand Up @@ -297,7 +291,6 @@ eventsRouter.get("/", weakJwtVerification, async (_: Request, res: Response) =>
* @apiSuccessExample Example Success Response:
* HTTP/1.1 200 OK
* {
* "id": "52fdfc072182654f163f5f0f9a621d72",
* "name": "Example Event 10",
* "description": "This is a description",
* "startTime": 1532202702,
Expand Down Expand Up @@ -332,31 +325,31 @@ eventsRouter.post("/", strongJwtVerification, async (req: Request, res: Response

// Check if the token has elevated permissions
if (!hasElevatedPerms(token)) {
res.status(Constants.FORBIDDEN).send({ error: "InvalidPermission" });
return res.status(Constants.FORBIDDEN).send({ error: "InvalidPermission" });
}

// Verify that the input format is valid to create a new event or update it
const eventFormat: EventFormat = req.body as EventFormat;
eventFormat.id = crypto.randomBytes(Constants.EVENT_ID_LENGTH).toString("hex");
if (!isEventFormat(eventFormat)) {
res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
}

const collection: Collection<Document> = databaseClient.db(Constants.EVENT_DB).collection(EventDB.EVENTS);

// Try to update the database, if possivle
try {
await collection.insertOne(eventFormat);
res.status(Constants.SUCCESS).send({ ...eventFormat });
return res.status(Constants.SUCCESS).send({ ...eventFormat });
} catch (error) {
console.error(error);
res.status(Constants.INTERNAL_ERROR).send({ error: "DatabaseError" });
return res.status(Constants.INTERNAL_ERROR).send({ error: "DatabaseError" });
}
});


/**
* @api {put} /event/ PUT /event/
* @apiName Create or Update Event
* @apiGroup Event
* @apiDescription Create a new event or update an existing event.
*
Expand Down Expand Up @@ -415,13 +408,13 @@ eventsRouter.put("/", strongJwtVerification, async (req: Request, res: Response)

// Check if the token has elevated permissions
if (!hasElevatedPerms(token)) {
res.status(Constants.FORBIDDEN).send({ error: "InvalidPermission" });
return res.status(Constants.FORBIDDEN).send({ error: "InvalidPermission" });
}

// Verify that the input format is valid to create a new event or update it
const eventFormat: EventFormat = req.body as EventFormat;
if (!isEventFormat(eventFormat)) {
res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
}

const collection: Collection<Document> = databaseClient.db(Constants.EVENT_DB).collection(EventDB.EVENTS);
Expand All @@ -435,10 +428,10 @@ eventsRouter.put("/", strongJwtVerification, async (req: Request, res: Response)
// Try to update the database, if possivle
try {
await collection.updateOne(updateFilter, eventFormat, { upsert: true });
res.status(Constants.SUCCESS).send({ ...eventFormat });
return res.status(Constants.SUCCESS).send({ ...eventFormat });
} catch (error) {
console.error(error);
res.status(Constants.INTERNAL_ERROR).send({ error: "DatabaseError" });
return res.status(Constants.INTERNAL_ERROR).send({ error: "DatabaseError" });
}
});

Expand Down
1 change: 0 additions & 1 deletion src/services/newsletter/newsletter-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ newsletterRouter.use(cors(corsOptions));

/**
* @api {post} /newsletter/subscribe/ POST /newsletter/subscribe/
* @apiName Subscribe
* @apiGroup Newsletter
* @apiDescription Subscribe an email address to a newsletter. Will create a newsleter if it doesn't exist.
*
Expand Down
1 change: 0 additions & 1 deletion src/services/profile/profile-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ profileRouter.use(cors({ origin: "*" }));

/**
* @api {get} /profile/leaderboard/ GET /profile/leaderboard/
* @apiName Leaderboard
* @apiGroup Profile
* @apiDescription Get the top N profiles from the leaderboard, sorted by points.
*
Expand Down

0 comments on commit b682ddb

Please sign in to comment.