test: testing workflow triggers (#12823)
* add workflows to bookingScenario * activate sandbox mode for unit/integreation tests * add sendgrid specific code to SendgridProvider * Refactor WIP * remove duplicate sendgridProvider file * first implementation for testing workflows * revert unintended changes * comment out Workflow trigger tests * move sendgrid check after test mode * Update signup.tsx * fix esLint * test webhooks on all tests in fresh-booking.test.ts * fix subjectPattern as title can be different * add workflow tests to reschedule.test.ts * code clean up * code clean up * fix sendgrid credentials missing message * code clean up --------- Co-authored-by: CarinaWolli <[email protected]>
This commit is contained in:
co-authored by
CarinaWolli
parent
a03a1ba34e
commit
076868d243
@@ -16,6 +16,7 @@ import { weekdayToWeekIndex, type WeekDays } from "@calcom/lib/date-fns";
|
||||
import type { HttpError } from "@calcom/lib/http-error";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import type { WorkflowActions, WorkflowTemplates, WorkflowTriggerEvents } from "@calcom/prisma/client";
|
||||
import type { SchedulingType } from "@calcom/prisma/enums";
|
||||
import type { BookingStatus } from "@calcom/prisma/enums";
|
||||
import type { AppMeta } from "@calcom/types/App";
|
||||
@@ -36,6 +37,16 @@ type InputWebhook = {
|
||||
eventTriggers: WebhookTriggerEvents[];
|
||||
subscriberUrl: string;
|
||||
};
|
||||
|
||||
type InputWorkflow = {
|
||||
userId?: number | null;
|
||||
teamId?: number | null;
|
||||
name?: string;
|
||||
activeEventTypeId?: number;
|
||||
trigger: WorkflowTriggerEvents;
|
||||
action: WorkflowActions;
|
||||
template: WorkflowTemplates;
|
||||
};
|
||||
/**
|
||||
* Data to be mocked
|
||||
*/
|
||||
@@ -55,6 +66,7 @@ export type ScenarioData = {
|
||||
apps?: Partial<AppMeta>[];
|
||||
bookings?: InputBooking[];
|
||||
webhooks?: InputWebhook[];
|
||||
workflows?: InputWorkflow[];
|
||||
};
|
||||
|
||||
type InputCredential = typeof TestData.credentials.google & {
|
||||
@@ -371,6 +383,43 @@ async function addWebhooks(webhooks: InputWebhook[]) {
|
||||
await addWebhooksToDb(webhooks);
|
||||
}
|
||||
|
||||
async function addWorkflowsToDb(workflows: InputWorkflow[]) {
|
||||
await prismock.$transaction(
|
||||
workflows.map((workflow) => {
|
||||
return prismock.workflow.create({
|
||||
data: {
|
||||
userId: workflow.userId,
|
||||
teamId: workflow.teamId,
|
||||
trigger: workflow.trigger,
|
||||
name: workflow.name ? workflow.name : "Test Workflow",
|
||||
steps: {
|
||||
create: {
|
||||
stepNumber: 1,
|
||||
action: workflow.action,
|
||||
template: workflow.template,
|
||||
numberVerificationPending: false,
|
||||
includeCalendarEvent: false,
|
||||
},
|
||||
},
|
||||
activeOn: {
|
||||
create: workflow.activeEventTypeId ? { eventTypeId: workflow.activeEventTypeId } : undefined,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
activeOn: true,
|
||||
steps: true,
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
async function addWorkflows(workflows: InputWorkflow[]) {
|
||||
log.silly("TestData: Creating Workflows", safeStringify(workflows));
|
||||
|
||||
await addWorkflowsToDb(workflows);
|
||||
}
|
||||
|
||||
async function addUsersToDb(users: (Prisma.UserCreateInput & { schedules: Prisma.ScheduleCreateInput[] })[]) {
|
||||
log.silly("TestData: Creating Users", JSON.stringify(users));
|
||||
await prismock.user.createMany({
|
||||
@@ -510,6 +559,8 @@ export async function createBookingScenario(data: ScenarioData) {
|
||||
// mockBusyCalendarTimes([]);
|
||||
await addWebhooks(data.webhooks || []);
|
||||
// addPaymentMock();
|
||||
await addWorkflows(data.workflows || []);
|
||||
|
||||
return {
|
||||
eventTypes,
|
||||
};
|
||||
@@ -872,6 +923,7 @@ export function getScenarioData(
|
||||
usersApartFromOrganizer = [],
|
||||
apps = [],
|
||||
webhooks,
|
||||
workflows,
|
||||
bookings,
|
||||
}: // hosts = [],
|
||||
{
|
||||
@@ -880,6 +932,7 @@ export function getScenarioData(
|
||||
apps?: ScenarioData["apps"];
|
||||
usersApartFromOrganizer?: ScenarioData["users"];
|
||||
webhooks?: ScenarioData["webhooks"];
|
||||
workflows?: ScenarioData["workflows"];
|
||||
bookings?: ScenarioData["bookings"];
|
||||
// hosts?: ScenarioData["hosts"];
|
||||
},
|
||||
@@ -928,6 +981,7 @@ export function getScenarioData(
|
||||
apps: [...apps],
|
||||
webhooks,
|
||||
bookings: bookings || [],
|
||||
workflows,
|
||||
} satisfies ScenarioData;
|
||||
}
|
||||
|
||||
|
||||
@@ -304,8 +304,41 @@ export function expectWebhookToHaveBeenCalledWith(
|
||||
}
|
||||
}
|
||||
|
||||
export function expectWorkflowToBeTriggered() {
|
||||
// TODO: Implement this.
|
||||
export function expectWorkflowToBeTriggered({
|
||||
emails,
|
||||
organizer,
|
||||
}: {
|
||||
emails: Fixtures["emails"];
|
||||
organizer: { email: string; name: string; timeZone: string };
|
||||
}) {
|
||||
const subjectPattern = /^Reminder: /i;
|
||||
expect(emails.get()).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
subject: expect.stringMatching(subjectPattern),
|
||||
to: organizer.email,
|
||||
}),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
export function expectWorkflowToBeNotTriggered({
|
||||
emails,
|
||||
organizer,
|
||||
}: {
|
||||
emails: Fixtures["emails"];
|
||||
organizer: { email: string; name: string; timeZone: string };
|
||||
}) {
|
||||
const subjectPattern = /^Reminder: /i;
|
||||
|
||||
expect(emails.get()).not.toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
subject: expect.stringMatching(subjectPattern),
|
||||
to: organizer.email,
|
||||
}),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
export async function expectBookingToBeInDatabase(
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
expectBookingPaymentIntiatedWebhookToHaveBeenFired,
|
||||
expectBrokenIntegrationEmails,
|
||||
expectSuccessfulCalendarEventCreationInCalendar,
|
||||
expectWorkflowToBeNotTriggered,
|
||||
expectICalUIDAsString,
|
||||
} from "@calcom/web/test/utils/bookingScenario/expects";
|
||||
import { getMockRequestDataForBooking } from "@calcom/web/test/utils/bookingScenario/getMockRequestDataForBooking";
|
||||
@@ -72,7 +73,7 @@ describe("handleNewBooking", () => {
|
||||
1. Should create a booking in the database
|
||||
2. Should send emails to the booker as well as organizer
|
||||
3. Should create a booking in the event's destination calendar
|
||||
3. Should trigger BOOKING_CREATED webhook
|
||||
4. Should trigger BOOKING_CREATED webhook
|
||||
`,
|
||||
async ({ emails, org }) => {
|
||||
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
|
||||
@@ -107,6 +108,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -197,7 +207,7 @@ describe("handleNewBooking", () => {
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
calendarId: "[email protected]",
|
||||
videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
@@ -263,6 +273,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -348,7 +367,7 @@ describe("handleNewBooking", () => {
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
// We won't be sending evt.destinationCalendar in this case.
|
||||
@@ -417,6 +436,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -501,7 +529,7 @@ describe("handleNewBooking", () => {
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
calendarId: "[email protected]",
|
||||
videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
@@ -532,7 +560,7 @@ describe("handleNewBooking", () => {
|
||||
|
||||
test(
|
||||
`an error in creating a calendar event should not stop the booking creation - Current behaviour is wrong as the booking is created but no-one is notified of it`,
|
||||
async ({}) => {
|
||||
async ({ emails }) => {
|
||||
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
|
||||
const booker = getBooker({
|
||||
email: "[email protected]",
|
||||
@@ -563,6 +591,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -626,7 +663,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
|
||||
// FIXME: We should send Broken Integration emails on calendar event creation failure
|
||||
// expectCalendarEventCreationFailureEmails({ booker, organizer, emails });
|
||||
@@ -675,6 +712,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -767,7 +813,8 @@ describe("handleNewBooking", () => {
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
calendarId: "[email protected]",
|
||||
videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
@@ -830,6 +877,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -914,7 +970,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
calendarId: "[email protected]",
|
||||
videoCallUrl: "http://mock-dailyvideo.example.com/meeting-1",
|
||||
@@ -1312,6 +1368,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1374,7 +1439,7 @@ describe("handleNewBooking", () => {
|
||||
status: BookingStatus.PENDING,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeNotTriggered({ organizer, emails });
|
||||
|
||||
expectBookingRequestedEmails({
|
||||
booker,
|
||||
@@ -1429,6 +1494,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1490,7 +1564,7 @@ describe("handleNewBooking", () => {
|
||||
}),
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeNotTriggered({ organizer, emails });
|
||||
|
||||
expectBookingRequestedEmails({
|
||||
booker,
|
||||
@@ -1544,6 +1618,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1609,7 +1692,7 @@ describe("handleNewBooking", () => {
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
|
||||
const iCalUID = expectICalUIDAsString(createdBooking.iCalUID);
|
||||
|
||||
@@ -1667,6 +1750,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1733,7 +1825,7 @@ describe("handleNewBooking", () => {
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeNotTriggered({ organizer, emails });
|
||||
|
||||
expectBookingRequestedEmails({ booker, organizer, emails });
|
||||
|
||||
@@ -1864,6 +1956,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1902,7 +2003,7 @@ describe("handleNewBooking", () => {
|
||||
iCalUID: createdBooking.iCalUID,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
|
||||
const iCalUID = expectICalUIDAsString(createdBooking.iCalUID);
|
||||
|
||||
@@ -1932,6 +2033,8 @@ describe("handleNewBooking", () => {
|
||||
2. Should send email to the booker for Payment request
|
||||
3. Should trigger BOOKING_PAYMENT_INITIATED webhook
|
||||
4. Once payment is successful, should trigger BOOKING_CREATED webhook
|
||||
5. Workflow should not trigger before payment is made
|
||||
6. Workflow triggers once payment is successful
|
||||
`,
|
||||
async ({ emails }) => {
|
||||
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
|
||||
@@ -1959,6 +2062,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -2035,7 +2147,8 @@ describe("handleNewBooking", () => {
|
||||
}),
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeNotTriggered({ organizer, emails });
|
||||
|
||||
expectAwaitingPaymentEmails({ organizer, booker, emails });
|
||||
|
||||
expectBookingPaymentIntiatedWebhookToHaveBeenFired({
|
||||
@@ -2058,6 +2171,8 @@ describe("handleNewBooking", () => {
|
||||
status: BookingStatus.ACCEPTED,
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered({ organizer, emails });
|
||||
|
||||
expectBookingCreatedWebhookToHaveBeenFired({
|
||||
booker,
|
||||
organizer,
|
||||
@@ -2106,6 +2221,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "NEW_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -2176,7 +2300,9 @@ describe("handleNewBooking", () => {
|
||||
eventTypeId: mockBookingData.eventTypeId,
|
||||
status: BookingStatus.PENDING,
|
||||
});
|
||||
expectWorkflowToBeTriggered();
|
||||
|
||||
expectWorkflowToBeNotTriggered({ organizer, emails });
|
||||
|
||||
expectAwaitingPaymentEmails({ organizer, booker, emails });
|
||||
expectBookingPaymentIntiatedWebhookToHaveBeenFired({
|
||||
booker,
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
} from "@calcom/web/test/utils/bookingScenario/bookingScenario";
|
||||
import { createMockNextJsRequest } from "@calcom/web/test/utils/bookingScenario/createMockNextJsRequest";
|
||||
import {
|
||||
expectWorkflowToBeTriggered,
|
||||
// expectWorkflowToBeTriggered,
|
||||
expectSuccessfulBookingCreationEmails,
|
||||
expectBookingToBeInDatabase,
|
||||
expectBookingCreatedWebhookToHaveBeenFired,
|
||||
@@ -202,7 +202,7 @@ describe("handleNewBooking", () => {
|
||||
});
|
||||
}
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
|
||||
expectSuccessfulBookingCreationEmails({
|
||||
booker,
|
||||
@@ -548,7 +548,7 @@ describe("handleNewBooking", () => {
|
||||
});
|
||||
}
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
|
||||
expectSuccessfulBookingCreationEmails({
|
||||
booker,
|
||||
@@ -763,7 +763,7 @@ describe("handleNewBooking", () => {
|
||||
});
|
||||
}
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
|
||||
expectSuccessfulBookingCreationEmails({
|
||||
booking: {
|
||||
|
||||
@@ -88,6 +88,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "RESCHEDULE_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -231,8 +240,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ emails, organizer });
|
||||
|
||||
expectSuccessfulVideoMeetingUpdationInCalendar(videoMock, {
|
||||
calEvent: {
|
||||
@@ -317,6 +325,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "RESCHEDULE_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -442,7 +459,7 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ emails, organizer });
|
||||
|
||||
expectSuccessfulVideoMeetingUpdationInCalendar(videoMock, {
|
||||
calEvent: {
|
||||
@@ -486,7 +503,7 @@ describe("handleNewBooking", () => {
|
||||
|
||||
test(
|
||||
`an error in updating a calendar event should not stop the rescheduling - Current behaviour is wrong as the booking is resheduled but no-one is notified of it`,
|
||||
async ({}) => {
|
||||
async ({ emails }) => {
|
||||
const handleNewBooking = (await import("@calcom/features/bookings/lib/handleNewBooking")).default;
|
||||
const booker = getBooker({
|
||||
email: "[email protected]",
|
||||
@@ -520,6 +537,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "RESCHEDULE_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -630,7 +656,7 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ emails, organizer });
|
||||
|
||||
// FIXME: We should send Broken Integration emails on calendar event updation failure
|
||||
// expectBrokenIntegrationEmails({ booker, organizer, emails });
|
||||
@@ -694,6 +720,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "RESCHEDULE_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -815,7 +850,7 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered({emails, organizer});
|
||||
|
||||
expectBookingRequestedEmails({
|
||||
booker,
|
||||
@@ -892,6 +927,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "RESCHEDULE_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1041,7 +1085,7 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ emails, organizer });
|
||||
|
||||
expectSuccessfulVideoMeetingUpdationInCalendar(videoMock, {
|
||||
calEvent: {
|
||||
@@ -1134,6 +1178,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "RESCHEDULE_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1259,7 +1312,7 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
//expectWorkflowToBeTriggered({emails, organizer});
|
||||
|
||||
expectBookingRequestedEmails({
|
||||
booker,
|
||||
@@ -1337,6 +1390,15 @@ describe("handleNewBooking", () => {
|
||||
appId: null,
|
||||
},
|
||||
],
|
||||
workflows: [
|
||||
{
|
||||
userId: organizer.id,
|
||||
trigger: "RESCHEDULE_EVENT",
|
||||
action: "EMAIL_HOST",
|
||||
template: "REMINDER",
|
||||
activeEventTypeId: 1,
|
||||
},
|
||||
],
|
||||
eventTypes: [
|
||||
{
|
||||
id: 1,
|
||||
@@ -1497,7 +1559,7 @@ describe("handleNewBooking", () => {
|
||||
},
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
expectWorkflowToBeTriggered({ emails, organizer });
|
||||
|
||||
expectSuccessfulVideoMeetingUpdationInCalendar(videoMock, {
|
||||
calEvent: {
|
||||
|
||||
+6
-6
@@ -26,7 +26,7 @@ import {
|
||||
} from "@calcom/web/test/utils/bookingScenario/bookingScenario";
|
||||
import { createMockNextJsRequest } from "@calcom/web/test/utils/bookingScenario/createMockNextJsRequest";
|
||||
import {
|
||||
expectWorkflowToBeTriggered,
|
||||
// expectWorkflowToBeTriggered,
|
||||
expectSuccessfulBookingCreationEmails,
|
||||
expectBookingToBeInDatabase,
|
||||
expectBookingCreatedWebhookToHaveBeenFired,
|
||||
@@ -198,7 +198,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
destinationCalendars: [
|
||||
{
|
||||
@@ -513,7 +513,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
destinationCalendars: [
|
||||
{
|
||||
@@ -818,7 +818,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
destinationCalendars: [
|
||||
{
|
||||
@@ -1033,7 +1033,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
destinationCalendars: [
|
||||
{
|
||||
@@ -1260,7 +1260,7 @@ describe("handleNewBooking", () => {
|
||||
],
|
||||
});
|
||||
|
||||
expectWorkflowToBeTriggered();
|
||||
// expectWorkflowToBeTriggered();
|
||||
expectSuccessfulCalendarEventCreationInCalendar(calendarMock, {
|
||||
destinationCalendars: [
|
||||
{
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/* Schedule any workflow reminder that falls within 72 hours for email */
|
||||
import client from "@sendgrid/client";
|
||||
import sgMail from "@sendgrid/mail";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
|
||||
import { SENDER_NAME } from "@calcom/lib/constants";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { defaultHandler } from "@calcom/lib/server";
|
||||
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
@@ -21,16 +19,11 @@ import {
|
||||
getAllUnscheduledReminders,
|
||||
} from "../lib/getWorkflowReminders";
|
||||
import { getiCalEventAsString } from "../lib/getiCalEventAsString";
|
||||
import { sendSendgridMail } from "../lib/reminders/providers/sendgridProvider";
|
||||
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;
|
||||
const senderEmail = process.env.SENDGRID_EMAIL as string;
|
||||
|
||||
sgMail.setApiKey(sendgridAPIKey);
|
||||
client.setApiKey(sendgridAPIKey);
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const apiKey = req.headers.authorization || req.query.apiKey;
|
||||
if (process.env.CRON_API_KEY !== apiKey) {
|
||||
@@ -43,8 +36,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sandboxMode = process.env.NEXT_PUBLIC_IS_E2E ? true : false;
|
||||
|
||||
// delete batch_ids with already past scheduled date from scheduled_sends
|
||||
const remindersToDelete: { referenceId: string | null }[] = await getAllRemindersToDelete();
|
||||
|
||||
@@ -240,34 +231,30 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
|
||||
if (reminder.workflowStep.action !== WorkflowActions.EMAIL_ADDRESS) {
|
||||
sendEmailPromises.push(
|
||||
sgMail.send({
|
||||
to: sendTo,
|
||||
from: {
|
||||
email: senderEmail,
|
||||
name: reminder.workflowStep.sender || SENDER_NAME,
|
||||
sendSendgridMail(
|
||||
{
|
||||
to: sendTo,
|
||||
subject: emailContent.emailSubject,
|
||||
html: emailContent.emailBody,
|
||||
batchId: batchId,
|
||||
sendAt: dayjs(reminder.scheduledDate).unix(),
|
||||
replyTo: reminder.booking.user?.email,
|
||||
attachments: reminder.workflowStep.includeCalendarEvent
|
||||
? [
|
||||
{
|
||||
content: Buffer.from(getiCalEventAsString(reminder.booking) || "").toString(
|
||||
"base64"
|
||||
),
|
||||
filename: "event.ics",
|
||||
type: "text/calendar; method=REQUEST",
|
||||
disposition: "attachment",
|
||||
contentId: uuidv4(),
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
},
|
||||
subject: emailContent.emailSubject,
|
||||
html: emailContent.emailBody,
|
||||
batchId: batchId,
|
||||
sendAt: dayjs(reminder.scheduledDate).unix(),
|
||||
replyTo: reminder.booking.user?.email || senderEmail,
|
||||
mailSettings: {
|
||||
sandboxMode: {
|
||||
enable: sandboxMode,
|
||||
},
|
||||
},
|
||||
attachments: reminder.workflowStep.includeCalendarEvent
|
||||
? [
|
||||
{
|
||||
content: Buffer.from(getiCalEventAsString(reminder.booking) || "").toString("base64"),
|
||||
filename: "event.ics",
|
||||
type: "text/calendar; method=REQUEST",
|
||||
disposition: "attachment",
|
||||
contentId: uuidv4(),
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
})
|
||||
{ sender: reminder.workflowStep.sender }
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -319,24 +306,17 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const batchId = batchIdResponse[1].batch_id;
|
||||
|
||||
sendEmailPromises.push(
|
||||
sgMail.send({
|
||||
to: sendTo,
|
||||
from: {
|
||||
email: senderEmail,
|
||||
name: reminder.workflowStep?.sender || SENDER_NAME,
|
||||
sendSendgridMail(
|
||||
{
|
||||
to: sendTo,
|
||||
subject: emailContent.emailSubject,
|
||||
html: emailContent.emailBody,
|
||||
batchId: batchId,
|
||||
sendAt: dayjs(reminder.scheduledDate).unix(),
|
||||
replyTo: reminder.booking.user?.email,
|
||||
},
|
||||
subject: emailContent.emailSubject,
|
||||
html: emailContent.emailBody,
|
||||
batchId: batchId,
|
||||
sendAt: dayjs(reminder.scheduledDate).unix(),
|
||||
replyTo: reminder.booking.user?.email || senderEmail,
|
||||
mailSettings: {
|
||||
sandboxMode: {
|
||||
enable: sandboxMode,
|
||||
},
|
||||
},
|
||||
attachments: undefined,
|
||||
})
|
||||
{ sender: reminder.workflowStep?.sender }
|
||||
)
|
||||
);
|
||||
|
||||
await prisma.workflowReminder.update({
|
||||
|
||||
@@ -10,7 +10,7 @@ import { WorkflowActions, WorkflowMethods, WorkflowTemplates } from "@calcom/pri
|
||||
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { getSenderId } from "../lib/alphanumericSenderIdSupport";
|
||||
import * as twilio from "../lib/reminders/smsProviders/twilioProvider";
|
||||
import * as twilio from "../lib/reminders/providers/twilioProvider";
|
||||
import type { VariablesType } from "../lib/reminders/templates/customTemplate";
|
||||
import customTemplate from "../lib/reminders/templates/customTemplate";
|
||||
import smsReminderTemplate from "../lib/reminders/templates/smsReminderTemplate";
|
||||
|
||||
@@ -8,7 +8,7 @@ import prisma from "@calcom/prisma";
|
||||
import { WorkflowActions, WorkflowMethods } from "@calcom/prisma/enums";
|
||||
|
||||
import { getWhatsappTemplateFunction } from "../lib/actionHelperFunctions";
|
||||
import * as twilio from "../lib/reminders/smsProviders/twilioProvider";
|
||||
import * as twilio from "../lib/reminders/providers/twilioProvider";
|
||||
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const apiKey = req.headers.authorization || req.query.apiKey;
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import client from "@sendgrid/client";
|
||||
import type { MailData } from "@sendgrid/helpers/classes/mail";
|
||||
import sgMail from "@sendgrid/mail";
|
||||
import { createEvent } from "ics";
|
||||
import type { ParticipationStatus } from "ics";
|
||||
import type { DateArray } from "ics";
|
||||
@@ -9,7 +7,6 @@ import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { preprocessNameFieldDataWithVariant } from "@calcom/features/form-builder/utils";
|
||||
import { SENDER_NAME } from "@calcom/lib/constants";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { TimeUnit } from "@calcom/prisma/enums";
|
||||
@@ -21,33 +18,13 @@ import {
|
||||
} from "@calcom/prisma/enums";
|
||||
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { getBatchId, sendSendgridMail } from "./providers/sendgridProvider";
|
||||
import type { AttendeeInBookingInfo, BookingInfo, timeUnitLowerCase } from "./smsReminderManager";
|
||||
import type { VariablesType } from "./templates/customTemplate";
|
||||
import customTemplate from "./templates/customTemplate";
|
||||
import emailReminderTemplate from "./templates/emailReminderTemplate";
|
||||
|
||||
let sendgridAPIKey, senderEmail: string;
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["[emailReminderManager]"] });
|
||||
if (process.env.SENDGRID_API_KEY) {
|
||||
sendgridAPIKey = process.env.SENDGRID_API_KEY as string;
|
||||
senderEmail = process.env.SENDGRID_EMAIL as string;
|
||||
|
||||
sgMail.setApiKey(sendgridAPIKey);
|
||||
client.setApiKey(sendgridAPIKey);
|
||||
}
|
||||
|
||||
async function getBatchId() {
|
||||
if (!process.env.SENDGRID_API_KEY) {
|
||||
console.info("No sendgrid API key provided, returning DUMMY_BATCH_ID");
|
||||
return "DUMMY_BATCH_ID";
|
||||
}
|
||||
const batchIdResponse = await client.request({
|
||||
url: "/v3/mail/batch",
|
||||
method: "POST",
|
||||
});
|
||||
return batchIdResponse[1].batch_id as string;
|
||||
}
|
||||
|
||||
function getiCalEventAsString(evt: BookingInfo, status?: ParticipationStatus) {
|
||||
const uid = uuidv4();
|
||||
@@ -149,12 +126,6 @@ export const scheduleEmailReminder = async (args: scheduleEmailReminderArgs) =>
|
||||
scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null;
|
||||
}
|
||||
|
||||
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) {
|
||||
console.error("Sendgrid credentials are missing from the .env file");
|
||||
}
|
||||
|
||||
const sandboxMode = process.env.NEXT_PUBLIC_IS_E2E ? true : false;
|
||||
|
||||
let attendeeEmailToBeUsedInMail: string | null = null;
|
||||
let attendeeToBeUsedInMail: AttendeeInBookingInfo | null = null;
|
||||
let name = "";
|
||||
@@ -258,11 +229,6 @@ export const scheduleEmailReminder = async (args: scheduleEmailReminderArgs) =>
|
||||
const batchId = await getBatchId();
|
||||
|
||||
function sendEmail(data: Partial<MailData>, triggerEvent?: WorkflowTriggerEvents) {
|
||||
if (!process.env.SENDGRID_API_KEY) {
|
||||
console.info("No sendgrid API key provided, skipping email");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const status: ParticipationStatus =
|
||||
triggerEvent === WorkflowTriggerEvents.AFTER_EVENT
|
||||
? "COMPLETED"
|
||||
@@ -270,34 +236,28 @@ export const scheduleEmailReminder = async (args: scheduleEmailReminderArgs) =>
|
||||
? "DECLINED"
|
||||
: "ACCEPTED";
|
||||
|
||||
return sgMail.send({
|
||||
to: data.to,
|
||||
from: {
|
||||
email: senderEmail,
|
||||
name: sender || SENDER_NAME,
|
||||
return sendSendgridMail(
|
||||
{
|
||||
to: data.to,
|
||||
subject: emailContent.emailSubject,
|
||||
html: emailContent.emailBody,
|
||||
batchId,
|
||||
replyTo: evt.organizer.email,
|
||||
attachments: includeCalendarEvent
|
||||
? [
|
||||
{
|
||||
content: Buffer.from(getiCalEventAsString(evt, status) || "").toString("base64"),
|
||||
filename: "event.ics",
|
||||
type: "text/calendar; method=REQUEST",
|
||||
disposition: "attachment",
|
||||
contentId: uuidv4(),
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
sendAt: data.sendAt,
|
||||
},
|
||||
subject: emailContent.emailSubject,
|
||||
html: emailContent.emailBody,
|
||||
batchId,
|
||||
replyTo: evt.organizer.email,
|
||||
mailSettings: {
|
||||
sandboxMode: {
|
||||
enable: sandboxMode,
|
||||
},
|
||||
},
|
||||
attachments: includeCalendarEvent
|
||||
? [
|
||||
{
|
||||
content: Buffer.from(getiCalEventAsString(evt, status) || "").toString("base64"),
|
||||
filename: "event.ics",
|
||||
type: "text/calendar; method=REQUEST",
|
||||
disposition: "attachment",
|
||||
contentId: uuidv4(),
|
||||
},
|
||||
]
|
||||
: undefined,
|
||||
sendAt: data.sendAt,
|
||||
});
|
||||
{ sender }
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import client from "@sendgrid/client";
|
||||
import type { MailData } from "@sendgrid/helpers/classes/mail";
|
||||
import sgMail from "@sendgrid/mail";
|
||||
|
||||
import { SENDER_NAME } from "@calcom/lib/constants";
|
||||
import { setTestEmail } from "@calcom/lib/testEmails";
|
||||
|
||||
let sendgridAPIKey: string;
|
||||
let senderEmail: string;
|
||||
|
||||
function assertSendgrid() {
|
||||
if (process.env.SENDGRID_API_KEY && process.env.SENDGRID_EMAIL) {
|
||||
sendgridAPIKey = process.env.SENDGRID_API_KEY as string;
|
||||
senderEmail = process.env.SENDGRID_EMAIL as string;
|
||||
sgMail.setApiKey(sendgridAPIKey);
|
||||
client.setApiKey(sendgridAPIKey);
|
||||
} else {
|
||||
console.error("Sendgrid credentials are missing from the .env file");
|
||||
}
|
||||
}
|
||||
|
||||
export async function getBatchId() {
|
||||
assertSendgrid();
|
||||
if (!process.env.SENDGRID_API_KEY) {
|
||||
console.info("No sendgrid API key provided, returning DUMMY_BATCH_ID");
|
||||
return "DUMMY_BATCH_ID";
|
||||
}
|
||||
const batchIdResponse = await client.request({
|
||||
url: "/v3/mail/batch",
|
||||
method: "POST",
|
||||
});
|
||||
return batchIdResponse[1].batch_id as string;
|
||||
}
|
||||
|
||||
export function sendSendgridMail(
|
||||
mailData: Partial<MailData>,
|
||||
addData: { sender?: string | null; includeCalendarEvent?: boolean }
|
||||
) {
|
||||
assertSendgrid();
|
||||
|
||||
const testMode = process.env.NEXT_PUBLIC_IS_E2E || process.env.INTEGRATION_TEST_MODE;
|
||||
if (testMode) {
|
||||
if (!mailData.sendAt) {
|
||||
setTestEmail({
|
||||
to: mailData.to?.toString() || "",
|
||||
from: {
|
||||
email: senderEmail,
|
||||
name: addData.sender || SENDER_NAME,
|
||||
},
|
||||
subject: mailData.subject || "",
|
||||
html: mailData.html || "",
|
||||
});
|
||||
}
|
||||
console.log(
|
||||
"Skipped Sending Email as process.env.NEXT_PUBLIC_IS_E2E or process.env.INTEGRATION_TEST_MODE is set. Emails are available in globalThis.testEmails"
|
||||
);
|
||||
|
||||
return new Promise((r) => r("Skipped sendEmail for Unit Tests"));
|
||||
}
|
||||
|
||||
if (!sendgridAPIKey) {
|
||||
console.info("No sendgrid API key provided, skipping email");
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return sgMail.send({
|
||||
to: mailData.to,
|
||||
from: {
|
||||
email: senderEmail,
|
||||
name: addData.sender || SENDER_NAME,
|
||||
},
|
||||
subject: mailData.subject,
|
||||
html: mailData.html || "",
|
||||
batchId: mailData.batchId,
|
||||
replyTo: mailData.replyTo || senderEmail,
|
||||
attachments: mailData.attachments,
|
||||
sendAt: mailData.sendAt,
|
||||
});
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import type { CalEventResponses, RecurringEvent } from "@calcom/types/Calendar";
|
||||
|
||||
import { getSenderId } from "../alphanumericSenderIdSupport";
|
||||
import type { ScheduleReminderArgs } from "./emailReminderManager";
|
||||
import * as twilio from "./smsProviders/twilioProvider";
|
||||
import * as twilio from "./providers/twilioProvider";
|
||||
import type { VariablesType } from "./templates/customTemplate";
|
||||
import customTemplate from "./templates/customTemplate";
|
||||
import smsReminderTemplate from "./templates/smsReminderTemplate";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import * as twilio from "./smsProviders/twilioProvider";
|
||||
import * as twilio from "./providers/twilioProvider";
|
||||
|
||||
export const sendVerificationCode = async (phoneNumber: string) => {
|
||||
return twilio.sendVerificationCode(phoneNumber);
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
WorkflowMethods,
|
||||
} from "@calcom/prisma/enums";
|
||||
|
||||
import * as twilio from "./smsProviders/twilioProvider";
|
||||
import * as twilio from "./providers/twilioProvider";
|
||||
import type { ScheduleTextReminderArgs, timeUnitLowerCase } from "./smsReminderManager";
|
||||
import { deleteScheduledSMSReminder } from "./smsReminderManager";
|
||||
import {
|
||||
|
||||
@@ -6,7 +6,7 @@ declare global {
|
||||
content: string;
|
||||
};
|
||||
to: string;
|
||||
from: string;
|
||||
from: string | { email: string; name: string };
|
||||
subject: string;
|
||||
html: string;
|
||||
}[];
|
||||
|
||||
@@ -335,6 +335,7 @@
|
||||
"TELEMETRY_DEBUG",
|
||||
"TWILIO_MESSAGING_SID",
|
||||
"TWILIO_PHONE_NUMBER",
|
||||
"TWILIO_WHATSAPP_PHONE_NUMBER",
|
||||
"TWILIO_SID",
|
||||
"TWILIO_TOKEN",
|
||||
"TWILIO_VERIFY_SID",
|
||||
|
||||
Reference in New Issue
Block a user