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

Create weekly event recurring instance #1658

Merged
merged 10 commits into from
Jan 17, 2024
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
Loading