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:
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) {
|
||||
|
||||
+3
-3
@@ -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" },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ const moveSeatedBookingToNewTimeSlot = async (
|
||||
},
|
||||
});
|
||||
|
||||
evt = addVideoCallDataToEvent(newBooking.references, evt);
|
||||
evt = { ...addVideoCallDataToEvent(newBooking.references, evt), bookerUrl: evt.bookerUrl };
|
||||
|
||||
const copyEvent = cloneDeep(evt);
|
||||
|
||||
|
||||
+10
-8
@@ -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
@@ -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,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,
|
||||
|
||||
Reference in new issue
Block a user