-
-
Notifications
You must be signed in to change notification settings - Fork 995
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Dynamic Recurring Event Instances Generation during Queries (#1853
) * create recurring event helpers and refactor * remove unnecessary file * add documentation and comments * add tests * fix comments * update generateRecurrenceRuleString function * speed up code * fix formatting * format fix * restore package.json * fix failing test * better implementation and faster code * minor refactoring * minor correction * fix imports * return single recurring instance * fix failing test * Revert "return single recurring instance" This reverts commit 60353fd. * Reapply "return single recurring instance" This reverts commit fc12a99. * test commit * update test * fix test * convert to UTC dates * test commit with minor change * limit instance generation date according to the recurrence frequency * adjust for different timezones * remove unnecessary code * remove unnecessary formatting * update inputs * add tests * correct test * format files * update variable name * add dynamic recurring event instances generation during query * account for how many instances to generate based on recurrenceRule's specified count * add constant for query limit date
- Loading branch information
Showing
6 changed files
with
487 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
154 changes: 154 additions & 0 deletions
154
src/helpers/event/createEventHelpers/createRecurringEventInstancesDuringQuery.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { addDays, addYears } from "date-fns"; | ||
import { RecurrenceRule } from "../../../models/RecurrenceRule"; | ||
import { convertToUTCDate } from "../../../utilities/recurrenceDatesUtil"; | ||
import { Event } from "../../../models"; | ||
import { | ||
generateRecurringEventInstances, | ||
getRecurringInstanceDates, | ||
} from "../recurringEventHelpers"; | ||
import { session } from "../../../db"; | ||
import type { Recurrance } from "../../../types/generatedGraphQLTypes"; | ||
import type { InterfaceRecurringEvent } from "../recurringEventHelpers/generateRecurringEventInstances"; | ||
import { RECURRING_EVENT_INSTANCES_QUERY_LIMIT } from "../../../constants"; | ||
|
||
/** | ||
* This function creates the instances of a recurring event upto a certain date during queries. | ||
* @param organizationId - _id of the organization the events belong to | ||
* @remarks The following steps are followed: | ||
* 1. Get the limit date upto which we would want to query the recurrenceRules and generate new instances. | ||
* 2. Get the recurrence rules to be used for instance generation during this query. | ||
* 3. For every recurrence rule found: | ||
* - find the base recurring event to get the data to be used for new instance generation. | ||
* - get the number of existing instances and how many more to generate based on the recurrenceRule's count (if specified). | ||
* - generate new instances after their latestInstanceDates. | ||
* - update the latestInstanceDate. | ||
*/ | ||
|
||
export const createRecurringEventInstancesDuringQuery = async ( | ||
organizationId: string | undefined | null, | ||
): Promise<void> => { | ||
if (!organizationId) { | ||
return; | ||
} | ||
|
||
// get the current calendar date in UTC midnight | ||
const calendarDate = convertToUTCDate(new Date()); | ||
const queryUptoDate = addYears( | ||
calendarDate, | ||
RECURRING_EVENT_INSTANCES_QUERY_LIMIT, | ||
); | ||
|
||
// get the recurrenceRules | ||
const recurrenceRules = await RecurrenceRule.find({ | ||
organizationId, | ||
latestInstanceDate: { $lt: queryUptoDate }, | ||
}).lean(); | ||
|
||
await Promise.all( | ||
recurrenceRules.map(async (recurrenceRule) => { | ||
// find the baseRecurringEvent for the recurrenceRule | ||
const baseRecurringEvent = await Event.find({ | ||
_id: recurrenceRule.baseRecurringEventId, | ||
}).lean(); | ||
|
||
// get the data from the baseRecurringEvent | ||
const { | ||
_id: baseRecurringEventId, | ||
recurrance, | ||
...data | ||
} = baseRecurringEvent[0]; | ||
|
||
// get the input data for the generateRecurringEventInstances function | ||
const currentInputData: InterfaceRecurringEvent = { | ||
...data, | ||
organizationId: recurrenceRule.organizationId.toString(), | ||
recurrance: recurrance as Recurrance, | ||
}; | ||
|
||
// get the properties from recurrenceRule | ||
const { | ||
_id: recurrenceRuleId, | ||
latestInstanceDate, | ||
recurrenceRuleString, | ||
endDate: recurrenceEndDate, | ||
count: totalInstancesCount, | ||
} = recurrenceRule; | ||
|
||
// get the date from which new instances would be generated | ||
const currentRecurrenceStartDate = addDays(latestInstanceDate, 1); | ||
|
||
// get the dates for recurrence | ||
let recurringInstanceDates = getRecurringInstanceDates( | ||
recurrenceRuleString, | ||
currentRecurrenceStartDate, | ||
recurrenceEndDate, | ||
queryUptoDate, | ||
); | ||
|
||
// find out how many instances following the recurrence rule already exist and how many more to generate | ||
if (totalInstancesCount) { | ||
const totalExistingInstances = await Event.countDocuments({ | ||
recurrenceRuleId, | ||
}); | ||
|
||
const remainingInstances = totalInstancesCount - totalExistingInstances; | ||
|
||
recurringInstanceDates = recurringInstanceDates.slice( | ||
0, | ||
Math.min(recurringInstanceDates.length, remainingInstances), | ||
); | ||
} | ||
|
||
/* c8 ignore start */ | ||
if (session) { | ||
// start a transaction | ||
session.startTransaction(); | ||
} | ||
|
||
/* c8 ignore stop */ | ||
try { | ||
if (recurringInstanceDates && recurringInstanceDates.length) { | ||
const updatedLatestRecurringInstanceDate = | ||
recurringInstanceDates[recurringInstanceDates.length - 1]; | ||
|
||
// update the latestInstanceDate of the recurrenceRule | ||
await RecurrenceRule.updateOne( | ||
{ | ||
_id: recurrenceRuleId, | ||
}, | ||
{ | ||
latestInstanceDate: updatedLatestRecurringInstanceDate, | ||
}, | ||
{ session }, | ||
); | ||
|
||
// generate recurring event instances | ||
await generateRecurringEventInstances({ | ||
data: currentInputData, | ||
baseRecurringEventId: baseRecurringEventId.toString(), | ||
recurrenceRuleId: recurrenceRuleId.toString(), | ||
recurringInstanceDates, | ||
creatorId: baseRecurringEvent[0].creatorId.toString(), | ||
organizationId, | ||
session, | ||
}); | ||
} | ||
|
||
/* c8 ignore start */ | ||
if (session) { | ||
// commit transaction if everything's successful | ||
await session.commitTransaction(); | ||
} | ||
} catch (error) { | ||
if (session) { | ||
// abort transaction if something fails | ||
await session.abortTransaction(); | ||
} | ||
|
||
throw error; | ||
} | ||
|
||
/* c8 ignore stop */ | ||
}), | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export { createSingleEvent } from "./createSingleEvent"; | ||
export { createRecurringEvent } from "./createRecurringEvent"; | ||
export { createRecurringEventInstancesDuringQuery } from "./createRecurringEventInstancesDuringQuery"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.