fix: undefined in meeting url workflow variable (#16771)

* make bookerUrl mandatory

* add missing bookerUrl

* add fallback

* fix roundRObinReassignment

* clean up

* allow bookerUrl to be undefined

* code clean up

* make bookerUrl mandatory

* fix type errors

* fix type error

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2024-09-30 15:25:51 -04:00
1 parent 934cefecce
commit 2a734faf45
16 files changed
+98 -46

No files matched your search

@@ -15,6 +15,7 @@ import { deleteWebhookScheduledTriggers } from "@calcom/features/webhooks/lib/sc
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
import type { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
import { HttpError } from "@calcom/lib/http-error";
@@ -276,7 +277,18 @@ async function handler(req: CustomRequest) {
const teamMembers = await Promise.all(teamMembersPromises);
const tOrganizer = await getTranslation(organizer.locale ?? "en", "common");
const ownerProfile = await prisma.profile.findFirst({
where: {
userId: bookingToDelete.userId,
},
});
const bookerUrl = await getBookerBaseUrl(
bookingToDelete.eventType?.team?.parentId ?? ownerProfile?.organizationId ?? null
);
const evt: CalendarEvent = {
bookerUrl,
title: bookingToDelete?.title,
length: bookingToDelete?.eventType?.length,
type: bookingToDelete?.eventType?.slug as string,
@@ -371,6 +383,7 @@ async function handler(req: CustomRequest) {
evt: {
...evt,
metadata: { videoCallUrl: bookingMetadataSchema.parse(bookingToDelete.metadata || {})?.videoCallUrl },
bookerUrl,
...{
eventType: {
slug: bookingToDelete.eventType?.slug,
@@ -15,6 +15,7 @@ import { scheduleTrigger } from "@calcom/features/webhooks/lib/scheduleTrigger";
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
import type { EventPayloadType, EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser";
import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
import logger from "@calcom/lib/logger";
@@ -283,6 +284,21 @@ export async function handleConfirmation(args: {
updatedBookings.push(updatedBooking);
}
const teamId = await getTeamIdFromEventType({
eventType: {
team: { id: eventType?.teamId ?? null },
parentId: eventType?.parentId ?? null,
},
});
const triggerForUser = !teamId || (teamId && eventType?.parentId);
const userId = triggerForUser ? booking.userId : null;
const orgId = await getOrgIdFromMemberOrTeamId({ memberId: userId, teamId });
const bookerUrl = await getBookerBaseUrl(orgId ?? null);
//Workflows - set reminders for confirmed events
try {
for (let index = 0; index < updatedBookings.length; index++) {
@@ -295,6 +311,7 @@ export async function handleConfirmation(args: {
schedulingType: updatedBookings[index].eventType?.schedulingType,
hosts: updatedBookings[index].eventType?.hosts,
},
bookerUrl,
};
evtOfBooking.startTime = updatedBookings[index].startTime.toISOString();
evtOfBooking.endTime = updatedBookings[index].endTime.toISOString();
@@ -325,19 +342,6 @@ export async function handleConfirmation(args: {
}
try {
const teamId = await getTeamIdFromEventType({
eventType: {
team: { id: eventType?.teamId ?? null },
parentId: eventType?.parentId ?? null,
},
});
const triggerForUser = !teamId || (teamId && eventType?.parentId);
const userId = triggerForUser ? booking.userId : null;
const orgId = await getOrgIdFromMemberOrTeamId({ memberId: userId, teamId });
const subscribersBookingCreated = await getWebhooks({
userId,
eventTypeId: booking.eventTypeId,
@@ -987,7 +987,7 @@ async function handler(
rescheduleUid,
reqBookingUid: reqBody.bookingUid,
eventType,
evt,
evt: { ...evt, bookerUrl },
invitee,
allCredentials,
organizerUser,
@@ -1782,6 +1782,7 @@ async function handler(
...evt,
metadata,
eventType: { slug: eventType.slug, schedulingType: eventType.schedulingType, hosts: eventType.hosts },
bookerUrl,
};
if (!eventType.metadata?.disableStandardEmails?.all?.attendee) {
@@ -76,14 +76,14 @@ const attendeeRescheduleSeatedBooking = async (
}),
]);
}
// Add the new attendees to the new time slot booking attendees
for (const attendee of newTimeSlotBooking.attendees) {
const language = await getTranslation(attendee.locale ?? "en", "common");
const translate = await getTranslation(attendee.locale ?? "en", "common");
evt.attendees.push({
email: attendee.email,
name: attendee.name,
language,
timeZone: attendee.timeZone,
language: { translate, locale: attendee.locale ?? "en" },
});
}
@@ -119,7 +119,7 @@ const combineTwoSeatedBookings = async (
evt.attendees = updatedBookingAttendees;
evt = addVideoCallDataToEvent(updatedNewBooking.references, evt);
evt = { ...addVideoCallDataToEvent(updatedNewBooking.references, evt), bookerUrl: evt.bookerUrl };
const copyEvent = cloneDeep(evt);
@@ -48,7 +48,7 @@ const moveSeatedBookingToNewTimeSlot = async (
},
});
evt = addVideoCallDataToEvent(newBooking.references, evt);
evt = { ...addVideoCallDataToEvent(newBooking.references, evt), bookerUrl: evt.bookerUrl };
const copyEvent = cloneDeep(evt);
@@ -21,14 +21,16 @@ const ownerRescheduleSeatedBooking = async (
) => {
const { originalRescheduledBooking, tAttendees } = rescheduleSeatedBookingObject;
const { evt } = rescheduleSeatedBookingObject;
evt.attendees = originalRescheduledBooking?.attendees.map((attendee) => {
return {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
};
});
evt.attendees =
originalRescheduledBooking?.attendees.map((attendee) => {
return {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { translate: tAttendees, locale: attendee.locale ?? "en" },
};
}) ?? [];
// If there is no booking during the new time slot then update the current booking to the new date
if (!newTimeSlotBooking) {
+4 -2
View File
@@ -2,7 +2,7 @@ import type { Prisma } from "@prisma/client";
import type z from "zod";
import type { Workflow } from "@calcom/features/ee/workflows/lib/types";
import type { AppsStatus } from "@calcom/types/Calendar";
import type { AppsStatus, CalendarEvent } from "@calcom/types/Calendar";
import type { Booking, NewBookingEventType, OriginalRescheduledBooking } from "../handleNewBooking/types";
@@ -12,7 +12,9 @@ export type NewSeatedBookingObject = {
rescheduleUid: string | undefined;
reqBookingUid: string | undefined;
eventType: NewBookingEventType;
evt: CalendarEvent;
evt: Omit<CalendarEvent, "bookerUrl"> & {
bookerUrl: string;
};
invitee: Invitee;
allCredentials: Awaited<ReturnType<typeof getAllCredentials>>;
organizerUser: OrganizerUser;
@@ -18,6 +18,7 @@ import {
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import { isPrismaObjOrUndefined } from "@calcom/lib";
import { getVideoCallUrlFromCalEvent } from "@calcom/lib/CalEventParser";
import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server";
import logger from "@calcom/lib/logger";
import { getLuckyUser } from "@calcom/lib/server";
import { getTranslation } from "@calcom/lib/server/i18n";
@@ -50,7 +51,13 @@ const bookingSelect = {
references: true,
};
export const roundRobinReassignment = async ({ bookingId }: { bookingId: number }) => {
export const roundRobinReassignment = async ({
bookingId,
orgId,
}: {
bookingId: number;
orgId: number | null;
}) => {
const roundRobinReassignLogger = logger.getSubLogger({
prefix: ["roundRobinReassign", `${bookingId}`],
});
@@ -465,6 +472,8 @@ export const roundRobinReassignment = async ({ bookingId }: { bookingId: number
const workflowEventMetadata = { videoCallUrl: getVideoCallUrlFromCalEvent(evt) };
const bookerUrl = await getBookerBaseUrl(orgId);
for (const workflowReminder of workflowReminders) {
const workflowStep = workflowReminder?.workflowStep;
const workflow = workflowStep?.workflow;
@@ -475,6 +484,7 @@ export const roundRobinReassignment = async ({ bookingId }: { bookingId: number
...evt,
metadata: workflowEventMetadata,
eventType,
bookerUrl,
},
action: WorkflowActions.EMAIL_HOST,
triggerEvent: workflow.trigger,
@@ -538,7 +548,12 @@ export const roundRobinReassignment = async ({ bookingId }: { bookingId: number
await scheduleWorkflowReminders({
workflows: newEventWorkflows,
smsReminderNumber: null,
calendarEvent: { ...evt, metadata: workflowEventMetadata, eventType: { slug: eventType.slug } },
calendarEvent: {
...evt,
metadata: workflowEventMetadata,
eventType: { slug: eventType.slug },
bookerUrl,
},
hideBranding: !!eventType?.owner?.hideBranding,
});
}
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from "uuid";
import { guessEventLocationType } from "@calcom/app-store/locations";
import dayjs from "@calcom/dayjs";
import { preprocessNameFieldDataWithVariant } from "@calcom/features/form-builder/utils";
import { WEBSITE_URL } from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import prisma from "@calcom/prisma";
import type { TimeUnit } from "@calcom/prisma/enums";
@@ -189,6 +190,8 @@ export const scheduleEmailReminder = async (args: scheduleEmailReminderArgs) =>
emailSubject,
emailBody: `<body style="white-space: pre-wrap;">${emailBody}</body>`,
};
const bookerUrl = evt.bookerUrl ?? WEBSITE_URL;
if (emailBody) {
const variables: VariablesType = {
eventName: evt.title || "",
@@ -204,10 +207,10 @@ export const scheduleEmailReminder = async (args: scheduleEmailReminderArgs) =>
additionalNotes: evt.additionalNotes,
responses: evt.responses,
meetingUrl: bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl,
cancelLink: `${evt.bookerUrl}/booking/${evt.uid}?cancel=true`,
rescheduleLink: `${evt.bookerUrl}/reschedule/${evt.uid}`,
ratingUrl: `${evt.bookerUrl}/booking/${evt.uid}?rating`,
noShowUrl: `${evt.bookerUrl}/booking/${evt.uid}?noShow=true`,
cancelLink: `${bookerUrl}/booking/${evt.uid}?cancel=true`,
rescheduleLink: `${bookerUrl}/reschedule/${evt.uid}`,
ratingUrl: `${bookerUrl}/booking/${evt.uid}?rating`,
noShowUrl: `${bookerUrl}/booking/${evt.uid}?noShow=true`,
};
const locale =
@@ -247,8 +250,8 @@ export const scheduleEmailReminder = async (args: scheduleEmailReminderArgs) =>
timeZone,
organizer: evt.organizer.name,
name,
ratingUrl: `${evt.bookerUrl}/booking/${evt.uid}?rating`,
noShowUrl: `${evt.bookerUrl}/booking/${evt.uid}?noShow=true`,
ratingUrl: `${bookerUrl}/booking/${evt.uid}?rating`,
noShowUrl: `${bookerUrl}/booking/${evt.uid}?noShow=true`,
});
}
@@ -14,13 +14,14 @@ import type { ScheduleTextReminderAction } from "./smsReminderManager";
import { scheduleSMSReminder } from "./smsReminderManager";
import { scheduleWhatsappReminder } from "./whatsappReminderManager";
export type ExtendedCalendarEvent = CalendarEvent & {
export type ExtendedCalendarEvent = Omit<CalendarEvent, "bookerUrl"> & {
metadata?: { videoCallUrl: string | undefined };
eventType: {
slug?: string;
schedulingType?: SchedulingType | null;
hosts?: { user: { email: string; destinationCalendar?: { primaryEmail: string | null } | null } }[];
};
bookerUrl: string;
};
type ProcessWorkflowStepParams = {
@@ -1,5 +1,5 @@
import dayjs from "@calcom/dayjs";
import { SENDER_ID } from "@calcom/lib/constants";
import { SENDER_ID, WEBSITE_URL } from "@calcom/lib/constants";
import logger from "@calcom/lib/logger";
import type { TimeFormat } from "@calcom/lib/timeFormat";
import type { PrismaClient } from "@calcom/prisma";
@@ -36,7 +36,7 @@ export type AttendeeInBookingInfo = {
export type BookingInfo = {
uid?: string | null;
bookerUrl?: string;
bookerUrl: string;
attendees: AttendeeInBookingInfo[];
organizer: {
language: { locale: string };
@@ -158,8 +158,8 @@ export const scheduleSMSReminder = async (args: ScheduleTextReminderArgs) => {
additionalNotes: evt.additionalNotes,
responses: evt.responses,
meetingUrl: bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl,
cancelLink: `${evt.bookerUrl}/booking/${evt.uid}?cancel=true`,
rescheduleLink: `${evt.bookerUrl}/reschedule/${evt.uid}`,
cancelLink: `${evt.bookerUrl ?? WEBSITE_URL}/booking/${evt.uid}?cancel=true`,
rescheduleLink: `${evt.bookerUrl ?? WEBSITE_URL}/reschedule/${evt.uid}`,
};
const customMessage = customTemplate(smsMessage, variables, locale, evt.organizer.timeFormat);
smsMessage = customMessage.text;
+1 -1
View File
@@ -1,4 +1,4 @@
// This is your Prisma Schema file
// This is your Prisma Schema file
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
@@ -23,7 +23,7 @@ export const roundRobinReassignHandler = async ({ ctx, input }: RoundRobinReassi
throw new TRPCError({ code: "FORBIDDEN", message: "You do not have permission" });
}
await roundRobinReassignment({ bookingId });
await roundRobinReassignment({ bookingId, orgId: ctx.user.organizationId });
};
export default roundRobinReassignHandler;
@@ -1,6 +1,7 @@
import { scheduleEmailReminder } from "@calcom/features/ee/workflows/lib/reminders/emailReminderManager";
import { scheduleSMSReminder } from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager";
import { scheduleWhatsappReminder } from "@calcom/features/ee/workflows/lib/reminders/whatsappReminderManager";
import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server";
import { WorkflowRepository } from "@calcom/lib/server/repository/workflow";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
import { prisma } from "@calcom/prisma";
@@ -221,10 +222,13 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
},
});
const bookerUrl = await getBookerBaseUrl(ctx.user.organizationId ?? null);
for (const booking of bookingsForReminders) {
const defaultLocale = "en";
const bookingInfo = {
uid: booking.uid,
bookerUrl,
attendees: booking.attendees.map((attendee) => {
return {
name: attendee.name,
@@ -14,6 +14,7 @@ import {
} from "@calcom/features/bookings/lib/getBookingFields";
import { removeBookingField, upsertBookingField } from "@calcom/features/eventtypes/lib/bookingFieldsManager";
import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
import logger from "@calcom/lib/logger";
@@ -481,7 +482,8 @@ export async function scheduleWorkflowNotifications(
timeUnit,
trigger,
userId,
teamId
teamId,
isOrg
);
}
@@ -587,10 +589,14 @@ export async function scheduleBookingReminders(
timeUnit: TimeUnit | null,
trigger: WorkflowTriggerEvents,
userId: number,
teamId: number | null
teamId: number | null,
isOrg: boolean
) {
if (!bookings.length) return;
if (trigger !== WorkflowTriggerEvents.BEFORE_EVENT && trigger !== WorkflowTriggerEvents.AFTER_EVENT) return;
const bookerUrl = await getBookerBaseUrl(isOrg ? teamId : null);
//create reminders for all bookings for each workflow step
const promiseSteps = workflowSteps.map(async (step) => {
// we do not have attendees phone number (user is notified about that when setting this action)
@@ -601,6 +607,7 @@ export async function scheduleBookingReminders(
const defaultLocale = "en";
const bookingInfo = {
uid: booking.uid,
bookerUrl,
attendees: booking.attendees.map((attendee) => {
return {
name: attendee.name,