Skip to content

Commit

Permalink
Fix Events Following/Unfollowing (#137)
Browse files Browse the repository at this point in the history
* event-favorites

* revert yarn.lock and package.json

* revert package.json + yarn.lock

* fix format

* added event-favorites test

* change /event/favorites/ to /event/followers/

* change from EventFollowing to EventFollowers

* Finished up event favorites

* Fixed build issues

* Made code more consistent

* Update user-router.ts

---------

Co-authored-by: npunati27 <nikhitapunati@gmail.com>
  • Loading branch information
AydanPirani and npunati27 authored Jan 7, 2024
1 parent e1817b0 commit 630f942
Show file tree
Hide file tree
Showing 8 changed files with 7,233 additions and 6,792 deletions.
13 changes: 13 additions & 0 deletions src/database/attendee-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ export class AttendeeProfile {
@prop({ required: true })
public points: number;
}

export class AttendeeFollowing {
@prop({ required: true })
public userId: string;

@prop({
required: true,
type: () => {
return String;
},
})
public events: string[];
}
13 changes: 13 additions & 0 deletions src/database/event-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,16 @@ export class EventAttendance {
})
public attendees: string[];
}

export class EventFollowers {
@prop({ required: true })
public eventId: string;

@prop({
required: true,
type: () => {
return String;
},
})
public followers: string[];
}
10 changes: 8 additions & 2 deletions src/database/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { getModelForClass } from "@typegoose/typegoose";
import { Database, generateConfig } from "../database.js";

import { AuthInfo } from "./auth-db.js";
import { AttendeeMetadata, AttendeeProfile } from "./attendee-db.js";
import { AttendeeFollowing, AttendeeMetadata, AttendeeProfile } from "./attendee-db.js";
import { AdmissionDecision } from "./admission-db.js";
import { EventAttendance, EventMetadata, PublicEvent, StaffEvent } from "./event-db.js";
import { EventAttendance, EventMetadata, PublicEvent, StaffEvent, EventFollowers } from "./event-db.js";
import { NewsletterSubscription } from "./newsletter-db.js";
import { RegistrationApplication, RegistrationInfo } from "./registration-db.js";
import { ShopItem, ShopQuantity } from "./shop-db.js";
Expand All @@ -17,6 +17,7 @@ import { AnyParamConstructor } from "@typegoose/typegoose/lib/types.js";
enum AttendeeCollection {
METADATA = "metadata",
PROFILE = "profile",
FOLLOWING = "following",
}

enum AuthCollection {
Expand All @@ -37,6 +38,7 @@ enum EventCollection {
ATTENDANCE = "attendance",
STAFF_EVENTS = "staffevents",
PUBLIC_EVENTS = "publicevents",
FOLLOWERS = "followers",
}

enum NewsletterCollection {
Expand Down Expand Up @@ -64,6 +66,7 @@ export default class Models {
// Attendee
static AttendeeMetadata: mongoose.Model<AttendeeMetadata> = undefined!;
static AttendeeProfile: mongoose.Model<AttendeeProfile> = undefined!;
static AttendeeFollowing: mongoose.Model<AttendeeFollowing> = undefined!;
// Auth
static AuthInfo: mongoose.Model<AuthInfo> = undefined!;
// Admission
Expand All @@ -73,6 +76,7 @@ export default class Models {
static PublicEvent: mongoose.Model<PublicEvent> = undefined!;
static EventMetadata: mongoose.Model<EventMetadata> = undefined!;
static EventAttendance: mongoose.Model<EventAttendance> = undefined!;
static EventFollowers: mongoose.Model<EventFollowers> = undefined!;
// Newsletter
static NewsletterSubscription: mongoose.Model<NewsletterSubscription> = undefined!;
// Registration
Expand All @@ -88,12 +92,14 @@ export default class Models {
static initialize(): void {
this.AttendeeMetadata = getModel(AttendeeMetadata, Database.ATTENDEE, AttendeeCollection.METADATA);
this.AttendeeProfile = getModel(AttendeeProfile, Database.ATTENDEE, AttendeeCollection.PROFILE);
this.AttendeeFollowing = getModel(AttendeeFollowing, Database.ATTENDEE, AttendeeCollection.FOLLOWING);
this.AuthInfo = getModel(AuthInfo, Database.AUTH, AuthCollection.INFO);
this.AdmissionDecision = getModel(AdmissionDecision, Database.ADMISSION, AdmissionCollection.DECISION);
this.StaffEvent = getModel(StaffEvent, Database.EVENT, EventCollection.STAFF_EVENTS);
this.PublicEvent = getModel(PublicEvent, Database.EVENT, EventCollection.PUBLIC_EVENTS);
this.EventMetadata = getModel(EventMetadata, Database.EVENT, EventCollection.METADATA);
this.EventAttendance = getModel(EventAttendance, Database.EVENT, EventCollection.ATTENDANCE);
this.EventFollowers = getModel(EventFollowers, Database.EVENT, EventCollection.FOLLOWERS);
this.NewsletterSubscription = getModel(NewsletterSubscription, Database.NEWSLETTER, NewsletterCollection.SUBSCRIPTIONS);
this.RegistrationInfo = getModel(RegistrationInfo, Database.REGISTRATION, RegistrationCollection.INFO);
this.RegistrationApplication = getModel(
Expand Down
54 changes: 54 additions & 0 deletions src/services/event/event-router.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expect, it, beforeEach } from "@jest/globals";
import { EventFollowers } from "database/event-db.js";
import { AttendeeFollowing } from "database/attendee-db.js";
import Models from "../../database/models.js";
import { StatusCode } from "status-code-enum";
import { TESTER, getAsAttendee, getAsStaff } from "../../testTools.js";

const TESTER_EVENT_FOLLOWERS = {
eventId: "other-event",
followers: ["user5", "user8"],
} satisfies EventFollowers;

const TESTER_ATTENDEE_FOLLOWING = {
userId: TESTER.id,
events: ["event3", "event9"],
} satisfies AttendeeFollowing;

// Before each test, initialize database with tester & other users
beforeEach(async () => {
Models.initialize();
await Models.EventFollowers.create(TESTER_EVENT_FOLLOWERS);
await Models.AttendeeFollowing.create(TESTER_ATTENDEE_FOLLOWING);
});

describe("GET /event/followers/:EVENTID", () => {
it("gives an forbidden error for a non-staff user", async () => {
const response = await getAsAttendee(`/event/followers/${TESTER_EVENT_FOLLOWERS.eventId}/`).expect(
StatusCode.ClientErrorForbidden,
);

expect(JSON.parse(response.text)).toHaveProperty("error", "Forbidden");
});

it("gives an not found error for a non-existent event", async () => {
await Models.EventFollowers.deleteOne({
eventId: TESTER_EVENT_FOLLOWERS.eventId,
});

const response = await getAsStaff(`/event/followers/${TESTER_EVENT_FOLLOWERS.eventId}/`).expect(
StatusCode.ClientErrorNotFound,
);

expect(JSON.parse(response.text)).toHaveProperty("error", "EventNotFound");
});

it("works for a staff user", async () => {
const response = await getAsStaff(`/event/followers/${TESTER_EVENT_FOLLOWERS.eventId}/`).expect(StatusCode.SuccessOK);

expect(JSON.parse(response.text)).toMatchObject({
eventId: TESTER_EVENT_FOLLOWERS.eventId,
followers: TESTER_EVENT_FOLLOWERS.followers,
});
});
});
42 changes: 35 additions & 7 deletions src/services/event/event-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from "./event-formats.js";
import { FilteredEventView } from "./event-models.js";

import { EventMetadata, PublicEvent, StaffEvent } from "../../database/event-db.js";
import { EventFollowers, EventMetadata, PublicEvent, StaffEvent } from "../../database/event-db.js";
import Models from "../../database/models.js";
import { ObjectId } from "mongodb";
import { StatusCode } from "status-code-enum";
Expand Down Expand Up @@ -637,14 +637,42 @@ eventsRouter.put("/", strongJwtVerification, async (req: Request, res: Response,
}
});

// Prototype error handler
eventsRouter.use((err: Error, req: Request, res: Response) => {
if (!err) {
return res.status(StatusCode.SuccessOK).send({ status: "OK" });
/**
* @api {get} /event/favorites/:EVENTID/ GET /event/favorites/:EVENTID/
* @apiGroup Event
* @apiDescription Get users that favorite an event for a specific event by its unique ID.
*
* @apiHeader {String} Authorization User's JWT Token with staff permissions.
*
* @apiParam {String} EVENTID The unique identifier of the event.
*
* @apiSuccess (200: Success) {JSON} followers The followers of an event.
* @apiSuccessExample Example Success Response
* HTTP/1.1 200 OK
* [
* "user1",
* "user2",
* "user3"
* ]
* @apiUse strongVerifyErrors
* @apiError (400: Bad Request) {String} EventNotFound Event with the given ID not found.
* @apiError (403: Forbidden) {String} InvalidPermission User does not have staff permissions.
*/
eventsRouter.get("/followers/:EVENTID", strongJwtVerification, async (req: Request, res: Response, next: NextFunction) => {
const payload: JwtPayload = res.locals.payload as JwtPayload;

if (!hasStaffPerms(payload)) {
return next(new RouterError(StatusCode.ClientErrorForbidden, "Forbidden"));
}

const eventId: string | undefined = req.params.EVENTID;
const followers: EventFollowers | null = await Models.EventFollowers.findOne({ eventId: eventId });

if (!followers) {
return next(new RouterError(StatusCode.ClientErrorNotFound, "EventNotFound"));
}

console.error(err.stack, req.body);
return res.status(StatusCode.ServerErrorInternal).send({ error: err.message });
return res.status(StatusCode.SuccessOK).send({ eventId: eventId, followers: followers.followers });
});

export default eventsRouter;
Loading

0 comments on commit 630f942

Please sign in to comment.