Skip to content

Commit a8d6b0c

Browse files
committed
add repeating event exclusion support to api endpoints
1 parent b15deff commit a8d6b0c

File tree

2 files changed

+28
-8
lines changed

2 files changed

+28
-8
lines changed

src/api/routes/events.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ const createProjectionParams = (includeMetadata: boolean = false) => {
5757
id: "#id",
5858
repeats: "#repeats",
5959
repeatEnds: "#repeatEnds",
60+
excludeDates: "#excludeDates",
6061
...(includeMetadata ? { metadata: "#metadata" } : {}),
6162
};
6263

@@ -123,14 +124,19 @@ const baseSchema = z.object({
123124
const requestSchema = baseSchema.extend({
124125
repeats: z.optional(z.enum(repeatOptions)),
125126
repeatEnds: z.string().optional(),
127+
repeatExcludes: z.array(z.string().date()).optional().openapi({
128+
description:
129+
"Dates to exclude from recurrence rules (in the America/Chicago timezone).",
130+
}),
126131
});
127132

128-
const postRequestSchema = requestSchema.refine(
129-
(data) => (data.repeatEnds ? data.repeats !== undefined : true),
130-
{
133+
const postRequestSchema = requestSchema
134+
.refine((data) => (data.repeatEnds ? data.repeats !== undefined : true), {
131135
message: "repeats is required when repeatEnds is defined",
132-
},
133-
);
136+
})
137+
.refine((data) => (data.repeatExcludes ? data.repeats !== undefined : true), {
138+
message: "repeats is required when repeatExcludes is defined",
139+
});
134140
export type EventPostRequest = z.infer<typeof postRequestSchema>;
135141

136142
const getEventSchema = requestSchema.extend({

src/api/routes/ics.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,15 +155,29 @@ const icalPlugin: FastifyPluginAsync = async (fastify, _options) => {
155155
});
156156

157157
if (rawEvent.repeats) {
158+
const startTime = moment.tz(rawEvent.start, "America/Chicago");
159+
const hours = startTime.hours();
160+
const minutes = startTime.minutes();
161+
const seconds = startTime.seconds();
162+
const milliseconds = startTime.milliseconds();
163+
const exclusions = ((rawEvent.repeatExcludes as string[]) || []).map(
164+
(x) =>
165+
moment
166+
.tz(x, "America/Chicago")
167+
.set({ hours, minutes, seconds, milliseconds }),
168+
);
169+
158170
if (rawEvent.repeatEnds) {
159171
event = event.repeating({
160172
...repeatingIcalMap[rawEvent.repeats as EventRepeatOptions],
161173
until: moment.tz(rawEvent.repeatEnds, "America/Chicago"),
174+
exclude: exclusions,
162175
});
163176
} else {
164-
event.repeating(
165-
repeatingIcalMap[rawEvent.repeats as EventRepeatOptions],
166-
);
177+
event.repeating({
178+
...repeatingIcalMap[rawEvent.repeats as EventRepeatOptions],
179+
exclude: exclusions,
180+
});
167181
}
168182
}
169183
if (rawEvent.location) {

0 commit comments

Comments
 (0)