Files
calendar/packages/features/tasker/tasks/sendWorkflowEmails.ts
T
MorganGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
592cb4fb7b feat: add platform URL support for reschedule and cancel links in workflow emails (#27132)
* feat: add platform URL support for reschedule and cancel links in workflow emails

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* feat: pass platform URL data to CalendarEventBuilder in workflow emails

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* Revert "feat: pass platform URL data to CalendarEventBuilder in workflow emails"

This reverts commit 1d4d3623c93cd4eeeef18ffdad0597fe583b6a55.

* chore: provide platform metadat to workflow email task

* fixup! chore: provide platform metadat to workflow email task

* test: add unit tests for platform URL handling in EmailWorkflowService

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* test: update WorkflowService tests to include platform params in tasker payload

Co-Authored-By: morgan@cal.com <morgan@cal.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-03-13 15:48:51 +04:00

100 lines
3.3 KiB
TypeScript

import { sendCustomWorkflowEmail } from "@calcom/emails/workflow-email-service";
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
import { BookingSeatRepository } from "@calcom/features/bookings/repositories/BookingSeatRepository";
import { CalendarEventBuilder } from "@calcom/features/CalendarEventBuilder";
import { EmailWorkflowService } from "@calcom/features/ee/workflows/lib/service/EmailWorkflowService";
import { WorkflowReminderRepository } from "@calcom/features/ee/workflows/repositories/WorkflowReminderRepository";
import { prisma } from "@calcom/prisma";
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
import { z } from "zod";
export const ZSendWorkflowEmailsSchemaEager = z.object({
to: z.array(z.string()),
subject: z.string(),
html: z.string(),
replyTo: z.string().optional(),
sender: z.string().nullable().optional(),
attachments: z
.array(
z
.object({
content: z.string(),
filename: z.string(),
})
.passthrough()
)
.optional(),
});
const ZSendWorkflowEmailsSchemaLazy = z.object({
bookingUid: z.string(),
workflowReminderId: z.number(),
platformClientId: z.string().optional(),
platformRescheduleUrl: z.string().optional(),
platformCancelUrl: z.string().optional(),
platformBookingUrl: z.string().optional(),
});
export const ZSendWorkflowEmailsSchema = z.union([
ZSendWorkflowEmailsSchemaEager,
ZSendWorkflowEmailsSchemaLazy,
]);
export async function sendWorkflowEmails(payload: string): Promise<void> {
const mailData = ZSendWorkflowEmailsSchema.parse(JSON.parse(payload));
// Generate workflow body to send
if ("bookingUid" in mailData && "workflowReminderId" in mailData) {
const bookingRepository = new BookingRepository(prisma);
const booking = await bookingRepository.getBookingForCalEventBuilderFromUid(mailData.bookingUid);
if (!booking) {
throw new Error("Booking not found");
}
const bookingMetadata = bookingMetadataSchema.parse(booking.metadata || {});
const calendarEvent = (
await CalendarEventBuilder.fromBooking(booking, {
platformClientId: mailData.platformClientId ?? bookingMetadata?.platformClientId,
platformRescheduleUrl: mailData.platformRescheduleUrl,
platformCancelUrl: mailData.platformCancelUrl,
platformBookingUrl: mailData.platformBookingUrl,
})
).build();
if (!calendarEvent) {
throw new Error("Calendar event could not be built");
}
// Check if videoCallUrl exists in booking metadata and add it to evt.metadata
const metadata = bookingMetadata?.videoCallUrl
? {
videoCallUrl: bookingMetadata.videoCallUrl,
}
: undefined;
const evtWithMetadata = { ...calendarEvent, metadata };
const workflowReminderRepository = new WorkflowReminderRepository(prisma);
const bookingSeatRepository = new BookingSeatRepository(prisma);
const emailWorkflowService = new EmailWorkflowService(workflowReminderRepository, bookingSeatRepository);
await emailWorkflowService.handleSendEmailWorkflowTask({
evt: evtWithMetadata,
workflowReminderId: mailData.workflowReminderId,
});
return;
}
await Promise.all(
mailData.to.map((to) =>
sendCustomWorkflowEmail({
...mailData,
to,
})
)
);
}