diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index 6470b94ee8..8ee7959077 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -1147,7 +1147,7 @@ async function handler( // cancel workflow reminders from previous rescheduled booking originalRescheduledBooking.workflowReminders.forEach((reminder) => { if (reminder.method === WorkflowMethods.EMAIL) { - deleteScheduledEmailReminder(reminder.id, reminder.referenceId, true); + deleteScheduledEmailReminder(reminder.id, reminder.referenceId); } else if (reminder.method === WorkflowMethods.SMS) { deleteScheduledSMSReminder(reminder.id, reminder.referenceId); } diff --git a/packages/features/ee/workflows/api/scheduleEmailReminders.ts b/packages/features/ee/workflows/api/scheduleEmailReminders.ts index 5125c9fb1b..5480e11eee 100644 --- a/packages/features/ee/workflows/api/scheduleEmailReminders.ts +++ b/packages/features/ee/workflows/api/scheduleEmailReminders.ts @@ -7,10 +7,11 @@ import type { NextApiRequest, NextApiResponse } from "next"; import dayjs from "@calcom/dayjs"; import { defaultHandler } from "@calcom/lib/server"; import prisma from "@calcom/prisma"; -import { Prisma, WorkflowReminder } from "@calcom/prisma/client"; +import type { Prisma, WorkflowReminder } from "@calcom/prisma/client"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; -import customTemplate, { VariablesType } from "../lib/reminders/templates/customTemplate"; +import type { VariablesType } from "../lib/reminders/templates/customTemplate"; +import customTemplate from "../lib/reminders/templates/customTemplate"; import emailReminderTemplate from "../lib/reminders/templates/emailReminderTemplate"; const sendgridAPIKey = process.env.SENDGRID_API_KEY as string; @@ -103,15 +104,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { } for (const reminder of unscheduledReminders) { + if (!reminder.workflowStep || !reminder.booking) { + continue; + } try { let sendTo; switch (reminder.workflowStep.action) { case WorkflowActions.EMAIL_HOST: - sendTo = reminder.booking?.user?.email; + sendTo = reminder.booking.user?.email; break; case WorkflowActions.EMAIL_ATTENDEE: - sendTo = reminder.booking?.attendees[0].email; + sendTo = reminder.booking.attendees[0].email; break; case WorkflowActions.EMAIL_ADDRESS: sendTo = reminder.workflowStep.sendTo; @@ -119,24 +123,24 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const name = reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE - ? reminder.booking?.attendees[0].name - : reminder.booking?.user?.name; + ? reminder.booking.attendees[0].name + : reminder.booking.user?.name; const attendeeName = reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE - ? reminder.booking?.user?.name - : reminder.booking?.attendees[0].name; + ? reminder.booking.user?.name + : reminder.booking.attendees[0].name; const timeZone = reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE - ? reminder.booking?.attendees[0].timeZone - : reminder.booking?.user?.timeZone; + ? reminder.booking.attendees[0].timeZone + : reminder.booking.user?.timeZone; const locale = reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE || reminder.workflowStep.action === WorkflowActions.SMS_ATTENDEE - ? reminder.booking?.attendees[0].locale - : reminder.booking?.user?.locale; + ? reminder.booking.attendees[0].locale + : reminder.booking.user?.locale; let emailContent = { emailSubject: reminder.workflowStep.emailSubject || "", @@ -149,9 +153,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { switch (reminder.workflowStep.template) { case WorkflowTemplates.REMINDER: emailContent = emailReminderTemplate( - reminder.booking?.startTime.toISOString() || "", - reminder.booking?.endTime.toISOString() || "", - reminder.booking?.eventType?.title || "", + reminder.booking.startTime.toISOString() || "", + reminder.booking.endTime.toISOString() || "", + reminder.booking.eventType?.title || "", timeZone || "", attendeeName || "", name || "" @@ -160,16 +164,16 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { case WorkflowTemplates.CUSTOM: const variables: VariablesType = { eventName: reminder.booking?.eventType?.title || "", - organizerName: reminder.booking?.user?.name || "", - attendeeName: reminder.booking?.attendees[0].name, - attendeeEmail: reminder.booking?.attendees[0].email, - eventDate: dayjs(reminder.booking?.startTime).tz(timeZone), - eventTime: dayjs(reminder.booking?.startTime).tz(timeZone), + organizerName: reminder.booking.user?.name || "", + attendeeName: reminder.booking.attendees[0].name, + attendeeEmail: reminder.booking.attendees[0].email, + eventDate: dayjs(reminder.booking.startTime).tz(timeZone), + eventTime: dayjs(reminder.booking.startTime).tz(timeZone), timeZone: timeZone, - location: reminder.booking?.location || "", - additionalNotes: reminder.booking?.description, - customInputs: reminder.booking?.customInputs, - meetingUrl: bookingMetadataSchema.parse(reminder.booking?.metadata || {})?.videoCallUrl, + location: reminder.booking.location || "", + additionalNotes: reminder.booking.description, + customInputs: reminder.booking.customInputs, + meetingUrl: bookingMetadataSchema.parse(reminder.booking.metadata || {})?.videoCallUrl, }; const emailSubject = await customTemplate( reminder.workflowStep.emailSubject || "", @@ -204,7 +208,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { html: emailContent.emailBody.html, batchId: batchId, sendAt: dayjs(reminder.scheduledDate).unix(), - replyTo: reminder.booking?.user?.email || senderEmail, + replyTo: reminder.booking.user?.email || senderEmail, }); } diff --git a/packages/features/ee/workflows/api/scheduleSMSReminders.ts b/packages/features/ee/workflows/api/scheduleSMSReminders.ts index 97b135dab4..ce4f84e630 100644 --- a/packages/features/ee/workflows/api/scheduleSMSReminders.ts +++ b/packages/features/ee/workflows/api/scheduleSMSReminders.ts @@ -9,7 +9,8 @@ import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; import { getSenderId } from "../lib/alphanumericSenderIdSupport"; import * as twilio from "../lib/reminders/smsProviders/twilioProvider"; -import customTemplate, { VariablesType } from "../lib/reminders/templates/customTemplate"; +import type { VariablesType } from "../lib/reminders/templates/customTemplate"; +import customTemplate from "../lib/reminders/templates/customTemplate"; import smsReminderTemplate from "../lib/reminders/templates/smsReminderTemplate"; async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -53,6 +54,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { if (!unscheduledReminders.length) res.json({ ok: true }); for (const reminder of unscheduledReminders) { + if (!reminder.workflowStep || !reminder.booking) { + continue; + } try { const sendTo = reminder.workflowStep.action === WorkflowActions.SMS_NUMBER diff --git a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts index e84373b690..384b1b3715 100644 --- a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts @@ -1,10 +1,5 @@ -import { - TimeUnit, - WorkflowTriggerEvents, - WorkflowTemplates, - WorkflowActions, - WorkflowMethods, -} from "@prisma/client"; +import type { TimeUnit } from "@prisma/client"; +import { WorkflowTriggerEvents, WorkflowTemplates, WorkflowActions, WorkflowMethods } from "@prisma/client"; import client from "@sendgrid/client"; import sgMail from "@sendgrid/mail"; @@ -12,8 +7,9 @@ import dayjs from "@calcom/dayjs"; import prisma from "@calcom/prisma"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; -import { BookingInfo, timeUnitLowerCase } from "./smsReminderManager"; -import customTemplate, { VariablesType } from "./templates/customTemplate"; +import type { BookingInfo, timeUnitLowerCase } from "./smsReminderManager"; +import type { VariablesType } from "./templates/customTemplate"; +import customTemplate from "./templates/customTemplate"; import emailReminderTemplate from "./templates/emailReminderTemplate"; let sendgridAPIKey, senderEmail: string; @@ -194,11 +190,7 @@ export const scheduleEmailReminder = async ( } }; -export const deleteScheduledEmailReminder = async ( - reminderId: number, - referenceId: string | null, - immediateDelete?: boolean -) => { +export const deleteScheduledEmailReminder = async (reminderId: number, referenceId: string | null) => { try { if (!referenceId) { await prisma.workflowReminder.delete({ @@ -210,18 +202,6 @@ export const deleteScheduledEmailReminder = async ( return; } - if (immediateDelete) { - await client.request({ - url: "/v3/user/scheduled_sends", - method: "POST", - body: { - batch_id: referenceId, - status: "cancel", - }, - }); - return; - } - await prisma.workflowReminder.update({ where: { id: reminderId, diff --git a/packages/prisma/migrations/20230309203435_make_booking_and_workflow_step_optional_for_workflow_reminder/migration.sql b/packages/prisma/migrations/20230309203435_make_booking_and_workflow_step_optional_for_workflow_reminder/migration.sql new file mode 100644 index 0000000000..8059967e9c --- /dev/null +++ b/packages/prisma/migrations/20230309203435_make_booking_and_workflow_step_optional_for_workflow_reminder/migration.sql @@ -0,0 +1,15 @@ +-- DropForeignKey +ALTER TABLE "WorkflowReminder" DROP CONSTRAINT "WorkflowReminder_bookingUid_fkey"; + +-- DropForeignKey +ALTER TABLE "WorkflowReminder" DROP CONSTRAINT "WorkflowReminder_workflowStepId_fkey"; + +-- AlterTable +ALTER TABLE "WorkflowReminder" ALTER COLUMN "bookingUid" DROP NOT NULL, +ALTER COLUMN "workflowStepId" DROP NOT NULL; + +-- AddForeignKey +ALTER TABLE "WorkflowReminder" ADD CONSTRAINT "WorkflowReminder_bookingUid_fkey" FOREIGN KEY ("bookingUid") REFERENCES "Booking"("uid") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "WorkflowReminder" ADD CONSTRAINT "WorkflowReminder_workflowStepId_fkey" FOREIGN KEY ("workflowStepId") REFERENCES "WorkflowStep"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 1173cbaadd..af2ccafbe7 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -642,14 +642,14 @@ enum TimeUnit { model WorkflowReminder { id Int @id @default(autoincrement()) - bookingUid String - booking Booking? @relation(fields: [bookingUid], references: [uid], onDelete: Cascade) + bookingUid String? + booking Booking? @relation(fields: [bookingUid], references: [uid]) method WorkflowMethods scheduledDate DateTime referenceId String? @unique scheduled Boolean - workflowStepId Int - workflowStep WorkflowStep @relation(fields: [workflowStepId], references: [id], onDelete: Cascade) + workflowStepId Int? + workflowStep WorkflowStep? @relation(fields: [workflowStepId], references: [id]) cancelled Boolean? } diff --git a/packages/trpc/server/routers/viewer/workflows.tsx b/packages/trpc/server/routers/viewer/workflows.tsx index 46ae210777..15827580c0 100644 --- a/packages/trpc/server/routers/viewer/workflows.tsx +++ b/packages/trpc/server/routers/viewer/workflows.tsx @@ -410,7 +410,7 @@ export const workflowsRouter = router({ //cancel workflow reminders of deleted workflow scheduledReminders.forEach((reminder) => { if (reminder.method === WorkflowMethods.EMAIL) { - deleteScheduledEmailReminder(reminder.id, reminder.referenceId, true); + deleteScheduledEmailReminder(reminder.id, reminder.referenceId); } else if (reminder.method === WorkflowMethods.SMS) { deleteScheduledSMSReminder(reminder.id, reminder.referenceId); } @@ -736,7 +736,7 @@ export const workflowsRouter = router({ if (remindersFromStep.length > 0) { remindersFromStep.forEach((reminder) => { if (reminder.method === WorkflowMethods.EMAIL) { - deleteScheduledEmailReminder(reminder.id, reminder.referenceId, true); + deleteScheduledEmailReminder(reminder.id, reminder.referenceId); } else if (reminder.method === WorkflowMethods.SMS) { deleteScheduledSMSReminder(reminder.id, reminder.referenceId); }