Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rsvp service #85

Merged
merged 38 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8e0cc84
yes
aletya Oct 8, 2023
900a0a1
Merge branch 'main' of https://github.com/HackIllinois/adonix
aletya Oct 8, 2023
3b76d14
added GET /rsvp/ and GET /rsvp/:USERID/
aletya Oct 8, 2023
d0e598d
bug fix
aletya Oct 8, 2023
52d15aa
added rsvp-router.ts
aletya Oct 15, 2023
187c65d
formating
aletya Oct 15, 2023
7529cb3
fixed JWT token processing
aletya Oct 15, 2023
8c54885
formatting
aletya Oct 15, 2023
1be07cf
resolved comments
aletya Oct 16, 2023
d0421d0
formatting
aletya Oct 16, 2023
bfa26a5
merge
aletya Oct 17, 2023
e1d800f
merge
aletya Oct 17, 2023
b0c97e8
merge conflicts
aletya Oct 17, 2023
d10b9b3
pr changes
aletya Oct 18, 2023
a680fdd
everything done except return stuffs
aletya Oct 18, 2023
d11633b
everything done except return stuffs
aletya Oct 18, 2023
1eeb6a4
return format stuffs
aletya Oct 19, 2023
e806ac5
Merge branch 'main' into dev/alex/rsvp
aletya Oct 19, 2023
d9d77f6
started working on testing
aletya Oct 20, 2023
3bacadd
Merge branch 'main' of https://github.com/HackIllinois/adonix
aletya Oct 20, 2023
34b90a0
Merge branch 'dev/alex/rsvp' of https://github.com/HackIllinois/adoni…
aletya Oct 20, 2023
9d9ec71
2/3 endpoints testing done
aletya Oct 20, 2023
c644b62
testing done
aletya Oct 22, 2023
6c56a15
formatting
aletya Oct 22, 2023
4acb35c
workaround
aletya Oct 22, 2023
0368699
pr comments resolved
aletya Oct 24, 2023
73e88a7
pr comments resolved
aletya Oct 24, 2023
be216d6
Merge branch 'main' of https://github.com/HackIllinois/adonix
aletya Oct 26, 2023
3996a4f
comments
aletya Oct 26, 2023
20f3f8e
comments
aletya Oct 26, 2023
b0f6441
Merge branch 'main' into dev/alex/rsvp
aletya Oct 26, 2023
7c9e27c
new status codes
aletya Oct 26, 2023
8df6db6
fixed tests
aletya Oct 29, 2023
8bb7851
comments
aletya Nov 1, 2023
1effbba
comments
aletya Nov 1, 2023
39d6182
changed redirect to forbidden
aletya Nov 2, 2023
8c4a4cb
changed redirect to forbidden
aletya Nov 2, 2023
b0c2000
comment
aletya Nov 2, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,5 @@ dist
docs/
apidocs/
devdocs/

.vercel
aletya marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import userRouter from "./services/user/user-router.js";
import eventRouter from "./services/event/event-router.js";
import profileRouter from "./services/profile/profile-router.js";
import newsletterRouter from "./services/newsletter/newsletter-router.js";
import rsvpRouter from "./services/rsvp/rsvp-router.js";
import versionRouter from "./services/version/version-router.js";

import { InitializeConfigReader } from "./middleware/config-reader.js";
Expand All @@ -35,6 +36,7 @@ app.use("/user/", userRouter);
app.use("/newsletter/", newsletterRouter);
app.use("/event/", eventRouter);
app.use("/profile/", profileRouter);
app.use("/rsvp/", rsvpRouter);
app.use("/version/", versionRouter);

// Ensure that API is running
Expand Down
4 changes: 2 additions & 2 deletions src/database/decision-db.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { prop } from "@typegoose/typegoose";

enum DecisionStatus {
export enum DecisionStatus {
TBD = "TBD",
ACCEPTED = "ACCEPTED",
REJECTED = "REJECTED",
WAITLISTED = "WAITLISTED",
}

enum DecisionResponse {
export enum DecisionResponse {
PENDING = "PENDING",
ACCEPTED = "ACCEPTED",
DECLINED = "DECLINED",
Expand Down
154 changes: 154 additions & 0 deletions src/services/rsvp/rsvp-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import express, { Request, Response, Router } from "express";
import Constants from "../../constants.js";
import { strongJwtVerification } from "../../middleware/verify-jwt.js";
import { JwtPayload } from "../auth/auth-models.js";
import { hasElevatedPerms } from "../auth/auth-lib.js";
import { DecisionStatus, DecisionResponse, DecisionInfo } from "../../database/decision-db.js";
import Models from "../../database/models.js";

const rsvpRouter: Router = Router();
rsvpRouter.use(express.urlencoded({ extended: false }));
aletya marked this conversation as resolved.
Show resolved Hide resolved

rsvpRouter.get("/test/", (_: Request, res: Response) => {
res.end("RSVP endpoint is working!");
});
aletya marked this conversation as resolved.
Show resolved Hide resolved

/**
* @api {get} /rsvp/:USERID/ GET /rsvp/:USERID/
* @apiGroup rsvp
* @apiDescription Check RSVP decision for a given userId, provided that the current user has elevated perms
*
* @apiSuccess (200: Success) { string, boolean } usedId and whether they are/aren't attending
aletya marked this conversation as resolved.
Show resolved Hide resolved
aletya marked this conversation as resolved.
Show resolved Hide resolved
* @apiSuccessExample Example Success Response:
* HTTP/1.1 200 OK
* {
* "userId": "github0000001",
* "isAttending": true
aletya marked this conversation as resolved.
Show resolved Hide resolved
* }
*
* @apiUse strongVerifyErrors
*/
rsvpRouter.get("/:USERID", strongJwtVerification, async (req: Request, res: Response) => {
const userId: string | undefined = req.params.USERID;

//Returns error if userid parameter is empty
if (!userId) {
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
}

const payload: JwtPayload = res.locals.payload as JwtPayload;
//Returns error if caller doesn't have elevated perms
if (!hasElevatedPerms(payload)) {
return res.redirect("/");
}
aletya marked this conversation as resolved.
Show resolved Hide resolved

const queryResult: DecisionInfo | null = await Models.DecisionInfo.findOne({ userId: userId });

//Returns error if query is empty
if (!queryResult) {
return res.status(Constants.BAD_REQUEST).send({ error: "User not found!" });
aletya marked this conversation as resolved.
Show resolved Hide resolved
}

const rsvpDecision: boolean =
queryResult.status === DecisionStatus.ACCEPTED && queryResult.response === DecisionResponse.ACCEPTED;
return res.status(Constants.SUCCESS).send({ userId: userId, isAttending: rsvpDecision });
aletya marked this conversation as resolved.
Show resolved Hide resolved
});

/**
* @api {get} /rsvp/ GET /rsvp/
* @apiGroup rsvp
* @apiDescription Check RSVP decision for current user
*
* @apiSuccess (200: Success) { string, boolean } usedid and whether they are/aren't attending
* @apiSuccessExample Example Success Response:
* HTTP/1.1 200 OK
* {
* "isAttending": true
* }
*
* @apiUse strongVerifyErrors
*/
rsvpRouter.get("/", strongJwtVerification, async (_: Request, res: Response) => {
const payload: JwtPayload = res.locals.payload as JwtPayload;

const userid: string = payload.id;
aletya marked this conversation as resolved.
Show resolved Hide resolved

const queryResult: DecisionInfo | null = await Models.DecisionInfo.findOne({ userId: userid });

//Returns error if query is empty
if (!queryResult) {
return res.status(Constants.BAD_REQUEST).send({ error: "User not found!" });
}

const rsvpDecision: boolean =
queryResult.status === DecisionStatus.ACCEPTED && queryResult.response === DecisionResponse.ACCEPTED;
return res.status(Constants.SUCCESS).send({ isAttending: rsvpDecision });
aletya marked this conversation as resolved.
Show resolved Hide resolved
});

/**
* @api {put} /rsvp/ Put /rsvp/
* @apiGroup rsvp
* @apiDescription Updates an rsvp for the currently authenticated user (determined by the JWT in the Authorization header).
*
* @apiBody {boolean} isAttending Whether or whether not the currently authenticated user is attending
* @apiParamExample {json} Example Request:
* {
* "isAttending": true
* }
*
* @apiSuccess (200: Success) { string, string, string, string, string, boolean } Updated decision info for currently authenticated user
aletya marked this conversation as resolved.
Show resolved Hide resolved
* @apiSuccessExample Example Success Response:
* HTTP/1.1 200 OK
* {
* "_id": "652c311b6e283244d2ef4c29",
aletya marked this conversation as resolved.
Show resolved Hide resolved
* "userId": "github0000001",
* "status": "ACCEPTED",
* "response": "ACCEPTED",
* "reviewer": "reviewer1",
* "emailSent": true
* }
*
* @apiUse strongVerifyErrors
*/
rsvpRouter.put("/", strongJwtVerification, async (req: Request, res: Response) => {
const rsvp: boolean | undefined = req.body.isAttending;

//Returns error if request body has no isAttending parameter
if (rsvp === undefined) {
return res.status(Constants.BAD_REQUEST).send({ error: "InvalidParams" });
}

const payload: JwtPayload = res.locals.payload as JwtPayload;

const userid: string = payload.id;

const queryResult: DecisionInfo | null = await Models.DecisionInfo.findOne({ userId: userid });

//Returns error if query is empty
if (!queryResult) {
return res.status(Constants.BAD_REQUEST).send({ error: "Unknown Error" });
aletya marked this conversation as resolved.
Show resolved Hide resolved
}

//If the current user has not been accepted, send an error
if (queryResult.status != DecisionStatus.ACCEPTED) {
return res.status(Constants.FORBIDDEN).send({ error: "Forbidden" });
}

//If current user has been accepted, update their RSVP decision to "ACCEPTED"/"DECLINED" acoordingly
const updatedDecision: DecisionInfo | null = await Models.DecisionInfo.findOneAndUpdate(
{ userId: queryResult.userId },
{
status: queryResult.status,
response: rsvp ? DecisionResponse.ACCEPTED : DecisionResponse.DECLINED,
},
{ new: true },
);

if (updatedDecision) {
return res.status(Constants.SUCCESS).send(updatedDecision);
} else {
return res.status(Constants.INTERNAL_ERROR).send({ error: "InternalError" });
}
});

export default rsvpRouter;
Loading
Loading