Skip to content

Commit

Permalink
add update all recurring event instances functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
meetulr committed Feb 21, 2024
1 parent 99875fc commit 8dde560
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 6 deletions.
3 changes: 3 additions & 0 deletions src/helpers/event/createEventHelpers/createRecurringEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
/**
* This function creates the instances of a recurring event upto a certain date.
* @param args - payload of the createEvent mutation
* @param generateAhead - whether we want to generate recurring events ahead of the startDate (in case a single event is made recurring)
* @param creatorId - _id of the creator
* @param organizationId - _id of the organization the events belongs to
* @remarks The following steps are followed:
Expand All @@ -29,6 +30,7 @@ export const createRecurringEvent = async (
creatorId: string,
organizationId: string,
session: mongoose.ClientSession,
generateAhead: boolean = false,
): Promise<InterfaceEvent> => {
const { data } = args;
let { recurrenceRuleData } = args;
Expand Down Expand Up @@ -70,6 +72,7 @@ export const createRecurringEvent = async (
recurrenceRuleString,
data.startDate,
data.endDate,
generateAhead,
);

// get the date for the latest created instance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export const createRecurringEventInstancesDuringQuery = async (
recurrenceRuleString,
currentRecurrenceStartDate,
recurrenceEndDate,
false,
queryUptoDate,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { addYears } from "date-fns";
import { addDays, addYears } from "date-fns";
import { Frequency, rrulestr } from "rrule";
import type { RRule } from "rrule";
import {
Expand All @@ -13,6 +13,7 @@ import {
* @param recurrenceRuleString - the rrule string for the recurrenceRule.
* @param recurrenceStartDate - the starting date from which we want to generate instances.
* @param eventEndDate - the end date of the event
* @param generateAhead - if we want to generate instances ahead of the recurrenceStartDate
* @param queryUptoDate - the limit date to query recurrenceRules (To be used for dynamic instance generation during queries).
* @remarks The following steps are followed:
* 1. Get the date limit for instance generation based on its recurrence frequency.
Expand All @@ -24,6 +25,7 @@ export function getRecurringInstanceDates(
recurrenceRuleString: string,
recurrenceStartDate: Date,
eventEndDate: Date | null,
generateAhead: boolean,
queryUptoDate: Date = recurrenceStartDate,
): Date[] {
// get the rrule object
Expand Down Expand Up @@ -65,6 +67,11 @@ export function getRecurringInstanceDates(
Math.min(eventEndDate.getTime(), limitEndDate.getTime()),
);

// generate ahead in case a single event is made recurring to avoid ovarlap
if (generateAhead) {
recurrenceStartDate = addDays(recurrenceStartDate, 1);
}

// get the dates of recurrence
const recurringInstanceDates = recurrenceRuleObject.between(
recurrenceStartDate,
Expand Down
1 change: 1 addition & 0 deletions src/helpers/event/updateEventHelpers/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { updateSingleEvent } from "./updateSingleEvent";
export { updateRecurringEvent } from "./updateRecurringEvent";
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import type mongoose from "mongoose";
import { Event, InterfaceEvent } from "../../../models";
import { MutationUpdateEventArgs } from "../../../types/generatedGraphQLTypes";
import { RecurrenceRule } from "../../../models/RecurrenceRule";

export const updateAllRecurringInstances = async (
args: MutationUpdateEventArgs,
event: InterfaceEvent,
session: mongoose.ClientSession,
): Promise<InterfaceEvent> => {
const { _id: eventId, recurrenceRuleId, baseRecurringEventId } = event;

const recurrenceRule = await RecurrenceRule.findOne({
_id: recurrenceRuleId,
});

const baseRecurringEvent = await Event.findOne({
_id: baseRecurringEventId,
});

if (!recurrenceRule || !baseRecurringEvent) {
return event;
}

if (
(recurrenceRule.endDate === null && baseRecurringEvent.endDate === null) ||
(recurrenceRule.endDate &&
baseRecurringEvent.endDate &&
recurrenceRule.endDate.toISOString() === baseRecurringEvent.endDate)
) {
await Event.updateOne(
{
_id: baseRecurringEventId,
},
{
...(args.data as Partial<InterfaceEvent>),
},
{
session,
},
);
}

console.log("here");

await Event.updateMany(
{
recurrenceRuleId,
},
{
...(args.data as Partial<InterfaceEvent>),
},
{
session,
},
);

const updatedEvent = await Event.findOne({
_id: eventId,
}).lean();

return updatedEvent as InterfaceEvent;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// import type mongoose from "mongoose";
import { InterfaceEvent } from "../../../models";
// import { MutationUpdateEventArgs } from "../../../types/generatedGraphQLTypes";

export const updateCurrentAndFollowingInstances = async (
// args: MutationUpdateEventArgs,
event: InterfaceEvent,
// session: mongoose.ClientSession,
): Promise<InterfaceEvent> => {
return event;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type mongoose from "mongoose";
import { MutationUpdateEventArgs } from "../../../types/generatedGraphQLTypes";
import { Event, InterfaceEvent } from "../../../models";
import { cacheEvents } from "../../../services/EventCache/cacheEvents";
import { RecurrenceRule } from "../../../models/RecurrenceRule";
import { generateRecurrenceRuleString } from "../recurringEventHelpers";

export const updateCurrentRecurringInstance = async (
args: MutationUpdateEventArgs,
event: InterfaceEvent,
session: mongoose.ClientSession,
): Promise<InterfaceEvent> => {
let updatedEvent: InterfaceEvent = event;

const recurrenceRule = await RecurrenceRule.findOne({
_id: event.recurrenceRuleId,
});

if (!recurrenceRule) {
return event;
}

let newRecurrenceRuleString = recurrenceRule.recurrenceRuleString;
let isRecurrenceRuleChanged = false;

const startDate = args.data?.startDate || event.startDate;
const endDate = args.data?.endDate || event.endDate;

if (args.recurrenceRuleData) {
newRecurrenceRuleString = generateRecurrenceRuleString(
args.recurrenceRuleData,
startDate,
endDate,
);

isRecurrenceRuleChanged =
recurrenceRule.recurrenceRuleString !== newRecurrenceRuleString;
}

if (
event.isRecurringEventException !== args.data?.isRecurringEventException ||
!isRecurrenceRuleChanged
) {
updatedEvent = await Event.findOneAndUpdate(
{
_id: args.id,
},
{
...(args.data as Partial<InterfaceEvent>),
},
{
new: true,
session,
},
).lean();

if (updatedEvent !== null) {
await cacheEvents([updatedEvent]);
}
} else {
// if recurrenceRule has changed
}

return updatedEvent;
};
35 changes: 35 additions & 0 deletions src/helpers/event/updateEventHelpers/updateRecurringEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import type mongoose from "mongoose";
import { InterfaceEvent } from "../../../models";
import { MutationUpdateEventArgs } from "../../../types/generatedGraphQLTypes";
import { updateCurrentRecurringInstance } from "./updateCurrentRecurringInstance";
import { updateAllRecurringInstances } from "./updateAllRecurringInstances";
import { updateCurrentAndFollowingInstances } from "./updateCurrentAndFollowingInstances";

export const updateRecurringEvent = async (
args: MutationUpdateEventArgs,
event: InterfaceEvent,
session: mongoose.ClientSession,
): Promise<InterfaceEvent> => {
let updatedEvent: InterfaceEvent = event;

if (
(args.data?.isRecurringEventException &&
args.data?.isRecurringEventException !==
event.isRecurringEventException) ||
args.recurringEventUpdateType === "ThisEvent"
) {
updatedEvent = await updateCurrentRecurringInstance(args, event, session);
} else if (args.recurringEventUpdateType === "AllEvents") {
// perform a regular bulk update on all the instances
updatedEvent = await updateAllRecurringInstances(args, event, session);
} else {
// update current and following events
updatedEvent = await updateCurrentAndFollowingInstances(
event,
// args,
// session,
);
}

return updatedEvent;
};
3 changes: 3 additions & 0 deletions src/helpers/event/updateEventHelpers/updateSingleEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const updateSingleEvent = async (
creatorId,
organizationId,
session,
true,
);

// add the baseRecurringEventId to the current event to make it a part of the recurrence
Expand All @@ -79,6 +80,8 @@ export const updateSingleEvent = async (
_id: eventId,
},
{
...(args.data as Partial<InterfaceEvent>),
recurrenceRuleId: updatedEvent.recurrenceRuleId,
baseRecurringEventId: updatedEvent.baseRecurringEventId,
},
{ session },
Expand Down
10 changes: 5 additions & 5 deletions src/resolvers/Mutation/updateEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ import { findEventsInCache } from "../../services/EventCache/findEventInCache";
import { cacheEvents } from "../../services/EventCache/cacheEvents";
import { Types } from "mongoose";
import { session } from "../../db";
import { updateSingleEvent } from "../../helpers/event/updateEventHelpers";
import {
updateRecurringEvent,
updateSingleEvent,
} from "../../helpers/event/updateEventHelpers";
/**
* This function enables to update an event.
* @param _parent - parent of current request
Expand Down Expand Up @@ -130,10 +133,7 @@ export const updateEvent: MutationResolvers["updateEvent"] = async (

if (event.recurring) {
// update recurring event
// updatedEvent = await updateRecurringEvent(
// args,
// session,
// );
updatedEvent = await updateRecurringEvent(args, event, session);
} else {
// update single event
updatedEvent = await updateSingleEvent(args, event, session);
Expand Down
6 changes: 6 additions & 0 deletions src/typeDefs/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ export const enums = gql`
location_DESC
}
enum RecurringEventUpdateType {
AllEvents
ThisEvent
ThisAndFollowingEvents
}
enum Frequency {
YEARLY
MONTHLY
Expand Down
1 change: 1 addition & 0 deletions src/typeDefs/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export const mutations = gql`
id: ID!
data: UpdateEventInput
recurrenceRuleData: RecurrenceRuleInput
recurringEventUpdateType: RecurringEventUpdateType
): Event! @auth
updateEventVolunteer(
Expand Down
7 changes: 7 additions & 0 deletions src/types/generatedGraphQLTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,7 @@ export type MutationUpdateEventArgs = {
data?: InputMaybe<UpdateEventInput>;
id: Scalars['ID']['input'];
recurrenceRuleData?: InputMaybe<RecurrenceRuleInput>;
recurringEventUpdateType?: InputMaybe<RecurringEventUpdateType>;
};


Expand Down Expand Up @@ -1834,6 +1835,11 @@ export type RecurrenceRuleInput = {
weekDays?: InputMaybe<Array<InputMaybe<WeekDays>>>;
};

export type RecurringEventUpdateType =
| 'AllEvents'
| 'ThisAndFollowingEvents'
| 'ThisEvent';

export type Status =
| 'ACTIVE'
| 'BLOCKED'
Expand Down Expand Up @@ -2386,6 +2392,7 @@ export type ResolversTypes = {
RecaptchaVerification: RecaptchaVerification;
Recurrance: Recurrance;
RecurrenceRuleInput: RecurrenceRuleInput;
RecurringEventUpdateType: RecurringEventUpdateType;
Status: Status;
String: ResolverTypeWrapper<Scalars['String']['output']>;
Subscription: ResolverTypeWrapper<{}>;
Expand Down

0 comments on commit 8dde560

Please sign in to comment.