-
-
Notifications
You must be signed in to change notification settings - Fork 963
/
Copy pathgenerateRecurringEventInstances.ts
159 lines (142 loc) · 5.07 KB
/
generateRecurringEventInstances.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import type mongoose from "mongoose";
import type { InterfaceEvent } from "../../../models";
import { AppUserProfile, Event, EventAttendee, User } from "../../../models";
import type { EventInput } from "../../../types/generatedGraphQLTypes";
import { cacheEvents } from "../../../services/EventCache/cacheEvents";
import { addDays, differenceInDays } from "date-fns";
/**
* This function generates the recurring event instances.
* @param data - the EventInput data provided in the args.
* @param baseRecurringEventId - _id of the baseRecurringEvent.
* @param recurrenceRuleId - _id of the recurrenceRule document containing the recurrence rule that the instances follow.
* @param recurringInstanceDates - the dates of the recurring instances.
* @param creatorId - _id of the creator.
* @param organizationId - _id of the current organization.
* @remarks The following steps are followed:
* 1. Gets the start and end dates for instances.
* 2. Generate the instances.
* 3. Insert the documents in the database.
* 4. Associate the instances with the user.
* 5. Cache the instances.
* @returns A recurring instance generated during this operation.
*/
interface InterfaceGenerateRecurringInstances {
data: InterfaceRecurringEvent;
baseRecurringEventId: string;
recurrenceRuleId: string;
recurringInstanceDates: Date[];
creatorId: string;
organizationId: string;
session: mongoose.ClientSession;
}
export interface InterfaceRecurringEvent extends EventInput {
isBaseRecurringEvent?: boolean;
recurrenceRuleId?: string;
baseRecurringEventId?: string;
creatorId?: string;
admins?: string[];
organization?: string;
}
export const generateRecurringEventInstances = async ({
data,
baseRecurringEventId,
recurrenceRuleId,
recurringInstanceDates,
creatorId,
organizationId,
session,
}: InterfaceGenerateRecurringInstances): Promise<InterfaceEvent> => {
// get the start and end dates from the data
const { startDate, endDate } = data;
// this is the difference between the start and end dates of the event
// it will be used for creating events that last multiple days
// e.g if an event has startDate: "2024-04-15" & endDate: "2024-04-17", i.e. event lasts 2 days
// then, all the new instances generated would also have this 2 day gap between their start and end dates
let eventDurationInDays = 0;
// during createEventMutation, startDate & endDate would exist, so the difference would
// be calculated with these dates
if (endDate) {
eventDurationInDays = differenceInDays(endDate, startDate);
}
// during queries, while dynamically generating new instances,
// we would find this difference with the start and end dates of the latestGeneratedInstance
const latestGeneratedInstance = await Event.findOne({
recurrenceRuleId,
baseRecurringEventId,
isRecurringEventException: false,
}).sort({ startDate: -1 });
if (latestGeneratedInstance) {
// it would exist during queries (i.e. during dynamic generation)
eventDurationInDays = differenceInDays(
latestGeneratedInstance.endDate as string,
latestGeneratedInstance.startDate,
);
}
// get the recurring event instances
const recurringInstances: InterfaceRecurringEvent[] = [];
recurringInstanceDates.map((date): void => {
// get the start date for the instance
const instanceStartDate = date;
// get the end date of the instance
const instanceEndDate = addDays(date, eventDurationInDays);
const createdEventInstance = {
...data,
startDate: instanceStartDate,
endDate: instanceEndDate,
recurring: true,
isBaseRecurringEvent: false,
recurrenceRuleId,
baseRecurringEventId,
creatorId,
admins: data.admins && data.admins.length ? data.admins : [creatorId],
organization: organizationId,
};
recurringInstances.push(createdEventInstance);
});
// Bulk insertion in database
const recurringEventInstances = await Event.insertMany(recurringInstances, {
session,
});
// add eventattendee for each instance
const eventAttendees = recurringEventInstances.map(
(recurringEventInstance) => ({
userId: creatorId,
eventId: recurringEventInstance?._id.toString(),
}),
);
// get event instances ids for updating user event fields to include generated instances
const eventInstanceIds = recurringEventInstances.map((instance) =>
instance._id.toString(),
);
// perform database operations
await Promise.all([
EventAttendee.insertMany(eventAttendees, { session }),
User.updateOne(
{ _id: creatorId },
{
$push: {
registeredEvents: { $each: eventInstanceIds },
},
},
{ session },
),
AppUserProfile.updateOne(
{
userId: creatorId,
},
{
$push: {
eventAdmin: { $each: eventInstanceIds },
createdEvents: { $each: eventInstanceIds },
},
},
),
]);
// cache the instances
await Promise.all(
recurringEventInstances.map((recurringEventInstance) =>
cacheEvents([recurringEventInstance]),
),
);
return recurringEventInstances[0];
};