fix: unhandled promise error in scheduleEmailReminders endpoint (#12183)

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
Carina Wollendorfer
2023-11-06 09:14:35 -05:00
committed by GitHub
co-authored by CarinaWolli
parent 809abd2f4f
commit e11cb9e3a5
2 changed files with 86 additions and 83 deletions
@@ -1,23 +1,18 @@
/* Schedule any workflow reminder that falls within 72 hours for email */
import type { Prisma } from "@prisma/client";
import client from "@sendgrid/client";
import sgMail from "@sendgrid/mail";
import { createEvent } from "ics";
import type { DateArray } from "ics";
import type { NextApiRequest, NextApiResponse } from "next";
import { RRule } from "rrule";
import { v4 as uuidv4 } from "uuid";
import dayjs from "@calcom/dayjs";
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
import { parseRecurringEvent } from "@calcom/lib";
import { defaultHandler } from "@calcom/lib/server";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
import prisma from "@calcom/prisma";
import type { User } from "@calcom/prisma/client";
import { WorkflowActions, WorkflowMethods, WorkflowTemplates } from "@calcom/prisma/enums";
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
import { getiCalEventAsString } from "../lib/getiCalEventAsString";
import type { VariablesType } from "../lib/reminders/templates/customTemplate";
import customTemplate from "../lib/reminders/templates/customTemplate";
import emailReminderTemplate from "../lib/reminders/templates/emailReminderTemplate";
@@ -28,69 +23,6 @@ const senderEmail = process.env.SENDGRID_EMAIL as string;
sgMail.setApiKey(sendgridAPIKey);
client.setApiKey(sendgridAPIKey);
type Booking = Prisma.BookingGetPayload<{
include: {
eventType: true;
attendees: true;
};
}>;
function getiCalEventAsString(
booking: Pick<Booking, "startTime" | "endTime" | "description" | "location" | "attendees"> & {
eventType: { recurringEvent?: Prisma.JsonValue; title?: string } | null;
user: Partial<User> | null;
}
) {
let recurrenceRule: string | undefined = undefined;
const recurringEvent = parseRecurringEvent(booking.eventType?.recurringEvent);
if (recurringEvent?.count) {
recurrenceRule = new RRule(recurringEvent).toString().replace("RRULE:", "");
}
const uid = uuidv4();
const icsEvent = createEvent({
uid,
startInputType: "utc",
start: dayjs(booking.startTime.toISOString() || "")
.utc()
.toArray()
.slice(0, 6)
.map((v, i) => (i === 1 ? v + 1 : v)) as DateArray,
duration: {
minutes: dayjs(booking.endTime.toISOString() || "").diff(
dayjs(booking.startTime.toISOString() || ""),
"minute"
),
},
title: booking.eventType?.title || "",
description: booking.description || "",
location: booking.location || "",
organizer: {
email: booking.user?.email || "",
name: booking.user?.name || "",
},
attendees: [
{
name: booking.attendees[0].name,
email: booking.attendees[0].email,
partstat: "ACCEPTED",
role: "REQ-PARTICIPANT",
rsvp: true,
},
],
method: "REQUEST",
...{ recurrenceRule },
status: "CONFIRMED",
});
if (icsEvent.error) {
throw icsEvent.error;
}
return icsEvent.value;
}
async function handler(req: NextApiRequest, res: NextApiResponse) {
const apiKey = req.headers.authorization || req.query.apiKey;
if (process.env.CRON_API_KEY !== apiKey) {
@@ -105,11 +37,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
const sandboxMode = process.env.NEXT_PUBLIC_IS_E2E ? true : false;
// delete batch_ids with already past scheduled date from scheduled_sends
const pageSize = 90;
let pageNumber = 0;
const deletePromises = [];
const deletePromises: Promise<any>[] = [];
//delete batch_ids with already past scheduled date from scheduled_sends
while (true) {
const remindersToDelete = await prisma.workflowReminder.findMany({
where: {
@@ -130,14 +62,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
break;
}
deletePromises.push(
remindersToDelete.map((reminder) =>
client.request({
url: `/v3/user/scheduled_sends/${reminder.referenceId}`,
method: "DELETE",
})
)
);
for (const reminder of remindersToDelete) {
const deletePromise = client.request({
url: `/v3/user/scheduled_sends/${reminder.referenceId}`,
method: "DELETE",
});
deletePromises.push(deletePromise);
}
pageNumber++;
}
@@ -149,6 +81,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
});
});
//delete workflow reminders with past scheduled date
await prisma.workflowReminder.deleteMany({
where: {
method: WorkflowMethods.EMAIL,
@@ -159,10 +92,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
});
//cancel reminders for cancelled/rescheduled bookings that are scheduled within the next hour
pageNumber = 0;
const allPromisesCancelReminders = [];
const allPromisesCancelReminders: Promise<any>[] = [];
while (true) {
const remindersToCancel = await prisma.workflowReminder.findMany({
@@ -217,11 +149,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
});
});
// schedule all unscheduled reminders within the next 72 hours
pageNumber = 0;
const sendEmailPromises = [];
const sendEmailPromises: Promise<any>[] = [];
while (true) {
//find all unscheduled Email reminders
const unscheduledReminders = await prisma.workflowReminder.findMany({
where: {
method: WorkflowMethods.EMAIL,
@@ -0,0 +1,71 @@
import { createEvent } from "ics";
import type { DateArray } from "ics";
import { RRule } from "rrule";
import { v4 as uuidv4 } from "uuid";
import dayjs from "@calcom/dayjs";
import { parseRecurringEvent } from "@calcom/lib";
import type { Prisma, User } from "@calcom/prisma/client";
type Booking = Prisma.BookingGetPayload<{
include: {
eventType: true;
attendees: true;
};
}>;
export function getiCalEventAsString(
booking: Pick<Booking, "startTime" | "endTime" | "description" | "location" | "attendees"> & {
eventType: { recurringEvent?: Prisma.JsonValue; title?: string } | null;
user: Partial<User> | null;
}
) {
let recurrenceRule: string | undefined = undefined;
const recurringEvent = parseRecurringEvent(booking.eventType?.recurringEvent);
if (recurringEvent?.count) {
recurrenceRule = new RRule(recurringEvent).toString().replace("RRULE:", "");
}
const uid = uuidv4();
const icsEvent = createEvent({
uid,
startInputType: "utc",
start: dayjs(booking.startTime.toISOString() || "")
.utc()
.toArray()
.slice(0, 6)
.map((v, i) => (i === 1 ? v + 1 : v)) as DateArray,
duration: {
minutes: dayjs(booking.endTime.toISOString() || "").diff(
dayjs(booking.startTime.toISOString() || ""),
"minute"
),
},
title: booking.eventType?.title || "",
description: booking.description || "",
location: booking.location || "",
organizer: {
email: booking.user?.email || "",
name: booking.user?.name || "",
},
attendees: [
{
name: booking.attendees[0].name,
email: booking.attendees[0].email,
partstat: "ACCEPTED",
role: "REQ-PARTICIPANT",
rsvp: true,
},
],
method: "REQUEST",
...{ recurrenceRule },
status: "CONFIRMED",
});
if (icsEvent.error) {
throw icsEvent.error;
}
return icsEvent.value;
}