fix: Stop sending Reminders when a 'Seat' cancels the Event (#10405)

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
This commit is contained in:
Monto
2023-08-01 10:13:28 -04:00
committed by GitHub
co-authored by CarinaWolli Carina Wollendorfer
parent a0fc045852
commit 17855251ff
14 changed files with 68 additions and 22 deletions
+2 -1
View File
@@ -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) => {
+1 -1
View File
@@ -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";
@@ -48,4 +48,4 @@ ${this.verifyAccountInput.language("happy_scheduling")} ${this.verifyAccountInpu
)}
`.replace(/(<([^>]+)>)/gi, "");
}
}
}
@@ -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 };
}
@@ -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);
@@ -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,
},
});
}
@@ -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,
});
}
}
@@ -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,
@@ -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,
},
});
}
@@ -0,0 +1,5 @@
-- AlterTable
ALTER TABLE "WorkflowReminder" ADD COLUMN "seatReferenceId" TEXT;
-- CreateIndex
CREATE INDEX "WorkflowReminder_seatReferenceId_idx" ON "WorkflowReminder"("seatReferenceId");
+12 -10
View File
@@ -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 {
@@ -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;
@@ -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<typeof ZSendVerifyEmailCodeSchema>;
@@ -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";