From 17855251ffa4d4bda3fdbc4d400376e997017390 Mon Sep 17 00:00:00 2001 From: Monto <138862352+monto7926@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:13:28 +0300 Subject: [PATCH] fix: Stop sending Reminders when a 'Seat' cancels the Event (#10405) Co-authored-by: CarinaWolli Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> --- packages/emails/email-manager.ts | 3 ++- packages/emails/src/templates/index.ts | 2 +- .../emails/templates/attendee-verify-email.ts | 2 +- .../bookings/lib/handleCancelBooking.ts | 18 +++++++++++++++ .../features/bookings/lib/handleNewBooking.ts | 2 ++ .../lib/reminders/emailReminderManager.ts | 5 ++++- .../lib/reminders/reminderScheduler.ts | 14 +++++++++--- .../lib/reminders/smsReminderManager.ts | 6 ++++- .../lib/reminders/whatsappReminderManager.ts | 5 ++++- .../migration.sql | 5 +++++ packages/prisma/schema.prisma | 22 ++++++++++--------- .../auth/sendVerifyEmailCode.handler.ts | 2 +- .../viewer/auth/sendVerifyEmailCode.schema.ts | 2 +- .../organizations/verifyCode.handler.ts | 2 +- 14 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 packages/prisma/migrations/20230726083334_fix_workflow_reminders_not_canceled_for_canceled_seats/migration.sql diff --git a/packages/emails/email-manager.ts b/packages/emails/email-manager.ts index 541da301ff..91e381b2b0 100644 --- a/packages/emails/email-manager.ts +++ b/packages/emails/email-manager.ts @@ -17,6 +17,8 @@ import AttendeeLocationChangeEmail from "./templates/attendee-location-change-em import AttendeeRequestEmail from "./templates/attendee-request-email"; import AttendeeRescheduledEmail from "./templates/attendee-rescheduled-email"; import AttendeeScheduledEmail from "./templates/attendee-scheduled-email"; +import type { EmailVerifyCode } from "./templates/attendee-verify-email"; +import AttendeeVerifyEmail from "./templates/attendee-verify-email"; import AttendeeWasRequestedToRescheduleEmail from "./templates/attendee-was-requested-to-reschedule-email"; import BrokenIntegrationEmail from "./templates/broken-integration-email"; import DisabledAppEmail from "./templates/disabled-app-email"; @@ -42,7 +44,6 @@ import OrganizerScheduledEmail from "./templates/organizer-scheduled-email"; import SlugReplacementEmail from "./templates/slug-replacement-email"; import type { TeamInvite } from "./templates/team-invite-email"; import TeamInviteEmail from "./templates/team-invite-email"; -import AttendeeVerifyEmail, { EmailVerifyCode } from "./templates/attendee-verify-email"; const sendEmail = (prepare: () => BaseEmail) => { return new Promise((resolve, reject) => { diff --git a/packages/emails/src/templates/index.ts b/packages/emails/src/templates/index.ts index 83947fc9c6..c10514f3ec 100644 --- a/packages/emails/src/templates/index.ts +++ b/packages/emails/src/templates/index.ts @@ -24,7 +24,7 @@ export { BrokenIntegrationEmail } from "./BrokenIntegrationEmail"; export { OrganizerAttendeeCancelledSeatEmail } from "./OrganizerAttendeeCancelledSeatEmail"; export { NoShowFeeChargedEmail } from "./NoShowFeeChargedEmail"; export { VerifyAccountEmail } from "./VerifyAccountEmail"; -export { VerifyEmailByCode } from "./VerifyEmailByCode" +export { VerifyEmailByCode } from "./VerifyEmailByCode"; export * from "@calcom/app-store/routing-forms/emails/components"; export { DailyVideoDownloadRecordingEmail } from "./DailyVideoDownloadRecordingEmail"; export { OrganisationAccountVerifyEmail } from "./OrganizationAccountVerifyEmail"; diff --git a/packages/emails/templates/attendee-verify-email.ts b/packages/emails/templates/attendee-verify-email.ts index 26719fcb58..99e9e3e31f 100644 --- a/packages/emails/templates/attendee-verify-email.ts +++ b/packages/emails/templates/attendee-verify-email.ts @@ -48,4 +48,4 @@ ${this.verifyAccountInput.language("happy_scheduling")} ${this.verifyAccountInpu )} `.replace(/(<([^>]+)>)/gi, ""); } -} \ No newline at end of file +} diff --git a/packages/features/bookings/lib/handleCancelBooking.ts b/packages/features/bookings/lib/handleCancelBooking.ts index 73f2b874c7..b867368749 100644 --- a/packages/features/bookings/lib/handleCancelBooking.ts +++ b/packages/features/bookings/lib/handleCancelBooking.ts @@ -766,6 +766,24 @@ async function handleSeatedEventCancellation( ); await Promise.all(promises); + const workflowRemindersForAttendee = bookingToDelete?.workflowReminders.filter( + (reminder) => reminder.seatReferenceId === seatReferenceUid + ); + + if (workflowRemindersForAttendee && workflowRemindersForAttendee.length !== 0) { + const deletionPromises = workflowRemindersForAttendee.map((reminder) => { + if (reminder.method === WorkflowMethods.EMAIL) { + return deleteScheduledEmailReminder(reminder.id, reminder.referenceId); + } else if (reminder.method === WorkflowMethods.SMS) { + return deleteScheduledSMSReminder(reminder.id, reminder.referenceId); + } else if (reminder.method === WorkflowMethods.WHATSAPP) { + return deleteScheduledWhatsappReminder(reminder.id, reminder.referenceId); + } + }); + + await Promise.allSettled(deletionPromises); + } + return { success: true }; } diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index 255cf959f2..f65e203dd3 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -1664,6 +1664,7 @@ async function handler( isRescheduleEvent: !!rescheduleUid, isFirstRecurringEvent: true, emailAttendeeSendToOverride: bookerEmail, + seatReferenceUid: evt.attendeeSeatId, }); } catch (error) { log.error("Error while scheduling workflow reminders", error); @@ -2306,6 +2307,7 @@ async function handler( isRescheduleEvent: !!rescheduleUid, isFirstRecurringEvent: true, hideBranding: !!eventType.owner?.hideBranding, + seatReferenceUid: evt.attendeeSeatId, }); } catch (error) { log.error("Error while scheduling workflow reminders", error); diff --git a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts index 7911982dab..08060a413d 100644 --- a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts @@ -40,7 +40,8 @@ export const scheduleEmailReminder = async ( workflowStepId: number, template: WorkflowTemplates, sender: string, - hideBranding?: boolean + hideBranding?: boolean, + seatReferenceUid?: string ) => { if (action === WorkflowActions.EMAIL_ADDRESS) return; const { startTime, endTime } = evt; @@ -221,6 +222,7 @@ export const scheduleEmailReminder = async ( scheduledDate: scheduledDate.toDate(), scheduled: true, referenceId: batchIdResponse[1].batch_id, + seatReferenceId: seatReferenceUid, }, }); } catch (error) { @@ -235,6 +237,7 @@ export const scheduleEmailReminder = async ( method: WorkflowMethods.EMAIL, scheduledDate: scheduledDate.toDate(), scheduled: false, + seatReferenceId: seatReferenceUid, }, }); } diff --git a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts index 1b62fa55a4..3e531218cd 100644 --- a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts +++ b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts @@ -20,6 +20,7 @@ type ProcessWorkflowStepParams = { calendarEvent: ExtendedCalendarEvent; emailAttendeeSendToOverride?: string; hideBranding?: boolean; + seatReferenceUid?: string; }; export interface ScheduleWorkflowRemindersArgs extends ProcessWorkflowStepParams { @@ -41,6 +42,7 @@ const processWorkflowStep = async ( calendarEvent: evt, emailAttendeeSendToOverride, hideBranding, + seatReferenceUid, }: ProcessWorkflowStepParams ) => { if (step.action === WorkflowActions.SMS_ATTENDEE || step.action === WorkflowActions.SMS_NUMBER) { @@ -60,7 +62,8 @@ const processWorkflowStep = async ( step.sender || SENDER_ID, workflow.userId, workflow.teamId, - step.numberVerificationPending + step.numberVerificationPending, + seatReferenceUid ); } else if (step.action === WorkflowActions.EMAIL_ATTENDEE || step.action === WorkflowActions.EMAIL_HOST) { let sendTo: string[] = []; @@ -93,7 +96,8 @@ const processWorkflowStep = async ( step.id, step.template, step.sender || SENDER_NAME, - hideBranding + hideBranding, + seatReferenceUid ); } else if (isWhatsappAction(step.action)) { const sendTo = step.action === WorkflowActions.WHATSAPP_ATTENDEE ? smsReminderNumber : step.sendTo; @@ -111,7 +115,8 @@ const processWorkflowStep = async ( step.template, workflow.userId, workflow.teamId, - step.numberVerificationPending + step.numberVerificationPending, + seatReferenceUid ); } }; @@ -126,7 +131,9 @@ export const scheduleWorkflowReminders = async (args: ScheduleWorkflowRemindersA isFirstRecurringEvent = false, emailAttendeeSendToOverride = "", hideBranding, + seatReferenceUid, } = args; + if (requiresConfirmation || !workflows.length) return; for (const workflowReference of workflows) { @@ -157,6 +164,7 @@ export const scheduleWorkflowReminders = async (args: ScheduleWorkflowRemindersA emailAttendeeSendToOverride, smsReminderNumber, hideBranding, + seatReferenceUid, }); } } diff --git a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts index 47f70ff7fc..2b7944b6fc 100644 --- a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts @@ -60,7 +60,8 @@ export const scheduleSMSReminder = async ( sender: string, userId?: number | null, teamId?: number | null, - isVerificationPending = false + isVerificationPending = false, + seatReferenceUid?: string ) => { const { startTime, endTime } = evt; const uid = evt.uid as string; @@ -174,6 +175,7 @@ export const scheduleSMSReminder = async ( scheduledDate: scheduledDate.toDate(), scheduled: true, referenceId: scheduledSMS.sid, + seatReferenceId: seatReferenceUid, }, }); } catch (error) { @@ -188,6 +190,7 @@ export const scheduleSMSReminder = async ( method: WorkflowMethods.SMS, scheduledDate: scheduledDate.toDate(), scheduled: false, + seatReferenceId: seatReferenceUid, }, }); } @@ -200,6 +203,7 @@ export const deleteScheduledSMSReminder = async (reminderId: number, referenceId if (referenceId) { await twilio.cancelSMS(referenceId); } + await prisma.workflowReminder.delete({ where: { id: reminderId, diff --git a/packages/features/ee/workflows/lib/reminders/whatsappReminderManager.ts b/packages/features/ee/workflows/lib/reminders/whatsappReminderManager.ts index 39ade64e88..acb8f9f61f 100644 --- a/packages/features/ee/workflows/lib/reminders/whatsappReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/whatsappReminderManager.ts @@ -36,7 +36,8 @@ export const scheduleWhatsappReminder = async ( template: WorkflowTemplates, userId?: number | null, teamId?: number | null, - isVerificationPending = false + isVerificationPending = false, + seatReferenceUid?: string ) => { const { startTime, endTime } = evt; const uid = evt.uid as string; @@ -184,6 +185,7 @@ export const scheduleWhatsappReminder = async ( scheduledDate: scheduledDate.toDate(), scheduled: true, referenceId: scheduledWHATSAPP.sid, + seatReferenceId: seatReferenceUid, }, }); } catch (error) { @@ -198,6 +200,7 @@ export const scheduleWhatsappReminder = async ( method: WorkflowMethods.WHATSAPP, scheduledDate: scheduledDate.toDate(), scheduled: false, + seatReferenceId: seatReferenceUid, }, }); } diff --git a/packages/prisma/migrations/20230726083334_fix_workflow_reminders_not_canceled_for_canceled_seats/migration.sql b/packages/prisma/migrations/20230726083334_fix_workflow_reminders_not_canceled_for_canceled_seats/migration.sql new file mode 100644 index 0000000000..bb0edafc52 --- /dev/null +++ b/packages/prisma/migrations/20230726083334_fix_workflow_reminders_not_canceled_for_canceled_seats/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "WorkflowReminder" ADD COLUMN "seatReferenceId" TEXT; + +-- CreateIndex +CREATE INDEX "WorkflowReminder_seatReferenceId_idx" ON "WorkflowReminder"("seatReferenceId"); diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index c99686633e..da77e28995 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -782,19 +782,21 @@ enum TimeUnit { } model WorkflowReminder { - id Int @id @default(autoincrement()) - 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]) - cancelled Boolean? + id Int @id @default(autoincrement()) + 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]) + cancelled Boolean? + seatReferenceId String? @@index([bookingUid]) @@index([workflowStepId]) + @@index([seatReferenceId]) } enum WorkflowTemplates { diff --git a/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.handler.ts b/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.handler.ts index 6168b527a0..7380cc4d5c 100644 --- a/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.handler.ts +++ b/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.handler.ts @@ -1,7 +1,7 @@ import { sendEmailVerificationByCode } from "@calcom/features/auth/lib/verifyEmail"; import logger from "@calcom/lib/logger"; -import { TSendVerifyEmailCodeSchema } from "./sendVerifyEmailCode.schema" +import type { TSendVerifyEmailCodeSchema } from "./sendVerifyEmailCode.schema"; type SendVerifyEmailCode = { input: TSendVerifyEmailCodeSchema; diff --git a/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.schema.ts b/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.schema.ts index c744178036..f5cbcc60ae 100644 --- a/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.schema.ts +++ b/packages/trpc/server/routers/viewer/auth/sendVerifyEmailCode.schema.ts @@ -3,7 +3,7 @@ import { z } from "zod"; export const ZSendVerifyEmailCodeSchema = z.object({ email: z.string().min(1), username: z.string().optional(), - language: z.string().optional() + language: z.string().optional(), }); export type TSendVerifyEmailCodeSchema = z.infer; diff --git a/packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts b/packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts index e2f3a10f8c..eb2a814c31 100644 --- a/packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/verifyCode.handler.ts @@ -1,8 +1,8 @@ import { createHash } from "crypto"; import { totp } from "otplib"; -import { IS_PRODUCTION } from "@calcom/lib/constants"; import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError"; +import { IS_PRODUCTION } from "@calcom/lib/constants"; import type { ZVerifyCodeInputSchema } from "@calcom/prisma/zod-utils"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";