Skip to content

Commit

Permalink
Create weekly event recurring instance (#1658)
Browse files Browse the repository at this point in the history
* create weekly recurring events

* Commented out some test

* Added testcase to support other tests

* Added a single mongo session for whole application

* Optimized the code and added tests

* Fixed Failing Test case

* Fixed  Test case

* Fixed linting and merge conflicts

* Refactored file and added date-fns module
  • Loading branch information
Community-Programmer authored Jan 17, 2024
1 parent ad3e16e commit 663a08b
Show file tree
Hide file tree
Showing 8 changed files with 466 additions and 94 deletions.
53 changes: 34 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"copy-paste": "^1.5.3",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"date-fns": "^3.2.0",
"dotenv": "^8.6.0",
"express": "^4.18.2",
"express-mongo-sanitize": "^2.2.0",
Expand Down
6 changes: 6 additions & 0 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import mongoose from "mongoose";
import { MONGO_DB_URL } from "./constants";
import { logger } from "./libraries";

let session!: mongoose.ClientSession;

export const connect = async (): Promise<void> => {
try {
await mongoose.connect(MONGO_DB_URL as string, {
Expand All @@ -10,6 +12,7 @@ export const connect = async (): Promise<void> => {
useFindAndModify: false,
useNewUrlParser: true,
});
session = await mongoose.startSession();
} catch (error: unknown) {
if (error instanceof Error) {
const errorMessage = error.toString();
Expand Down Expand Up @@ -45,5 +48,8 @@ export const connect = async (): Promise<void> => {
};

export const disconnect = async (): Promise<void> => {
session?.endSession();
await mongoose.connection.close();
};

export { session };
4 changes: 4 additions & 0 deletions src/helpers/eventInstances/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import * as Once from "./once";
import * as Weekly from "./weekly";

export { Once, Weekly };
29 changes: 29 additions & 0 deletions src/helpers/eventInstances/once.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import type mongoose from "mongoose";
import type {
InterfaceEvent,
InterfaceOrganization,
InterfaceUser,
} from "../../models";
import { Event } from "../../models";
import type { MutationCreateEventArgs } from "../../types/generatedGraphQLTypes";

export async function generateEvent(
args: Partial<MutationCreateEventArgs>,
currentUser: InterfaceUser,
organization: InterfaceOrganization,
session: mongoose.ClientSession
): Promise<Promise<InterfaceEvent[]>> {
const createdEvent = await Event.create(
[
{
...args.data,
creatorId: currentUser._id,
admins: [currentUser._id],
organization: organization._id,
},
],
{ session }
);

return createdEvent;
}
53 changes: 53 additions & 0 deletions src/helpers/eventInstances/weekly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type mongoose from "mongoose";
import type {
InterfaceEvent,
InterfaceOrganization,
InterfaceUser,
} from "../../models";
import { Event } from "../../models";
import type { MutationCreateEventArgs } from "../../types/generatedGraphQLTypes";
import { eachDayOfInterval, format } from "date-fns";

interface InterfaceRecurringEvent extends MutationCreateEventArgs {
startDate: Date;
creatorId: mongoose.Types.ObjectId;
admins: mongoose.Types.ObjectId[];
organization: mongoose.Types.ObjectId;
}

export async function generateEvents(
args: Partial<MutationCreateEventArgs>,
currentUser: InterfaceUser,
organization: InterfaceOrganization,
session: mongoose.ClientSession
): Promise<InterfaceEvent[]> {
const recurringEvents: InterfaceRecurringEvent[] = [];
const { data } = args;

const startDate = new Date(data?.startDate);
const endDate = new Date(data?.endDate);

const allDays = eachDayOfInterval({ start: startDate, end: endDate });
const occurrences = allDays.filter(
(date) => date.getDay() === startDate.getDay()
);

occurrences.map((date) => {
const formattedDate = format(date, "yyyy-MM-dd");

const createdEvent = {
...data,
startDate: new Date(formattedDate),
creatorId: currentUser._id,
admins: [currentUser._id],
organization: organization._id,
};

recurringEvents.push(createdEvent);
});

//Bulk insertion in database
const weeklyEvents = await Event.insertMany(recurringEvents, { session });

return Array.isArray(weeklyEvents) ? weeklyEvents : [weeklyEvents];
}
131 changes: 86 additions & 45 deletions src/resolvers/Mutation/createEvent.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { MutationResolvers } from "../../types/generatedGraphQLTypes";
import { errors, requestContext } from "../../libraries";
import { User, Organization, Event } from "../../models";
import type { InterfaceEvent, InterfaceUser } from "../../models";
import { User, Organization } from "../../models";
import {
USER_NOT_FOUND_ERROR,
ORGANIZATION_NOT_FOUND_ERROR,
Expand All @@ -11,6 +12,9 @@ import { isValidString } from "../../libraries/validators/validateString";
import { compareDates } from "../../libraries/validators/compareDates";
import { EventAttendee } from "../../models/EventAttendee";
import { cacheEvents } from "../../services/EventCache/cacheEvents";
import type mongoose from "mongoose";
import { session } from "../../db";
import { Weekly, Once } from "../../helpers/eventInstances";

/**
* This function enables to create an event.
Expand Down Expand Up @@ -119,27 +123,88 @@ export const createEvent: MutationResolvers["createEvent"] = async (
);
}

// Creates new event.
const createdEvent = await Event.create({
...args.data,
creatorId: currentUser._id,
admins: [currentUser._id],
organization: organization._id,
});
if (session) {
session.startTransaction();
}

try {
let createdEvent!: InterfaceEvent[];

if (args.data?.recurring) {
switch (args.data?.recurrance) {
case "ONCE":
createdEvent = await Once.generateEvent(
args,
currentUser,
organization,
session
);

for (const event of createdEvent) {
await associateEventWithUser(currentUser, event, session);
await cacheEvents([event]);
}

break;

case "WEEKLY":
createdEvent = await Weekly.generateEvents(
args,
currentUser,
organization,
session
);

for (const event of createdEvent) {
await associateEventWithUser(currentUser, event, session);
await cacheEvents([event]);
}

break;
}
} else {
createdEvent = await Once.generateEvent(
args,
currentUser,
organization,
session
);

for (const event of createdEvent) {
await associateEventWithUser(currentUser, event, session);
await cacheEvents([event]);
}
}

if (session) {
await session.commitTransaction();
}

if (createdEvent !== null) {
await cacheEvents([createdEvent]);
// Returns the createdEvent.
return createdEvent[0];
} catch (error) {
if (session) {
await session.abortTransaction();
}
throw error;
}
};

await EventAttendee.create({
userId: currentUser._id.toString(),
eventId: createdEvent._id,
});
async function associateEventWithUser(
currentUser: InterfaceUser,
createdEvent: InterfaceEvent,
session: mongoose.ClientSession
): Promise<void> {
await EventAttendee.create(
[
{
userId: currentUser._id.toString(),
eventId: createdEvent._id,
},
],
{ session }
);

/*
Adds createdEvent._id to eventAdmin, createdEvents and registeredEvents lists
on currentUser's document.
*/
await User.updateOne(
{
_id: currentUser._id,
Expand All @@ -150,31 +215,7 @@ export const createEvent: MutationResolvers["createEvent"] = async (
createdEvents: createdEvent._id,
registeredEvents: createdEvent._id,
},
}
},
{ session }
);

/* Commenting out this notification code coz we don't use firebase anymore.
for (let i = 0; i < organization.members.length; i++) {
const user = await User.findOne({
_id: organization.members[i],
}).lean();
// Checks whether both user and user.token exist.
if (user && user.token) {
await admin.messaging().send({
token: user.token,
notification: {
title: "New Event",
body: `${currentUser.firstName} has created a new event in ${organization.name}`,
},
});
}
}
*/

// Returns the createdEvent.
return createdEvent.toObject();
};
}
Loading

0 comments on commit 663a08b

Please sign in to comment.