fix: webhooks for events with seats (#10143)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>pull/10151/head
parent
4de1de3418
commit
1419c534c8
|
@ -1,4 +1,4 @@
|
|||
import type { Prisma, WebhookTriggerEvents, WorkflowReminder } from "@prisma/client";
|
||||
import type { Prisma, WorkflowReminder } from "@prisma/client";
|
||||
import type { NextApiRequest } from "next";
|
||||
|
||||
import appStore from "@calcom/app-store";
|
||||
|
@ -23,7 +23,7 @@ import logger from "@calcom/lib/logger";
|
|||
import { handleRefundError } from "@calcom/lib/payment/handleRefundError";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
import { BookingStatus, MembershipRole, WorkflowMethods } from "@calcom/prisma/enums";
|
||||
import { BookingStatus, MembershipRole, WorkflowMethods, WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
import { schemaBookingCancelParams } from "@calcom/prisma/zod-utils";
|
||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
import type { IAbstractPaymentService, PaymentApp } from "@calcom/types/PaymentService";
|
||||
|
@ -126,9 +126,25 @@ async function handler(req: CustomRequest) {
|
|||
throw new HttpError({ statusCode: 400, message: "User not found" });
|
||||
}
|
||||
|
||||
// If it's just an attendee of a booking then just remove them from that booking
|
||||
const result = await handleSeatedEventCancellation(req);
|
||||
if (result) return { success: true };
|
||||
// get webhooks
|
||||
const eventTrigger: WebhookTriggerEvents = "BOOKING_CANCELLED";
|
||||
|
||||
const subscriberOptions = {
|
||||
userId: bookingToDelete.userId,
|
||||
eventTypeId: bookingToDelete.eventTypeId as number,
|
||||
triggerEvent: eventTrigger,
|
||||
teamId: bookingToDelete.eventType?.teamId,
|
||||
};
|
||||
const eventTypeInfo: EventTypeInfo = {
|
||||
eventTitle: bookingToDelete?.eventType?.title || null,
|
||||
eventDescription: bookingToDelete?.eventType?.description || null,
|
||||
requiresConfirmation: bookingToDelete?.eventType?.requiresConfirmation || null,
|
||||
price: bookingToDelete?.eventType?.price || null,
|
||||
currency: bookingToDelete?.eventType?.currency || null,
|
||||
length: bookingToDelete?.eventType?.length || null,
|
||||
};
|
||||
|
||||
const webhooks = await getWebhooks(subscriberOptions);
|
||||
|
||||
const organizer = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
|
@ -207,6 +223,12 @@ async function handler(req: CustomRequest) {
|
|||
seatsShowAttendees: bookingToDelete.eventType?.seatsShowAttendees,
|
||||
};
|
||||
|
||||
const dataForWebhooks = { evt, webhooks, eventTypeInfo };
|
||||
|
||||
// If it's just an attendee of a booking then just remove them from that booking
|
||||
const result = await handleSeatedEventCancellation(req, dataForWebhooks);
|
||||
if (result) return { success: true };
|
||||
|
||||
// If it's just an attendee of a booking then just remove them from that booking
|
||||
if (seatReferenceUid && bookingToDelete.attendees.length > 1) {
|
||||
const seatReference = bookingToDelete.seatsReferences.find(
|
||||
|
@ -308,26 +330,6 @@ async function handler(req: CustomRequest) {
|
|||
return { message: "No longer attending event" };
|
||||
}
|
||||
|
||||
// Hook up the webhook logic here
|
||||
const eventTrigger: WebhookTriggerEvents = "BOOKING_CANCELLED";
|
||||
// Send Webhook call if hooked to BOOKING.CANCELLED
|
||||
const subscriberOptions = {
|
||||
userId: bookingToDelete.userId,
|
||||
eventTypeId: bookingToDelete.eventTypeId as number,
|
||||
triggerEvent: eventTrigger,
|
||||
teamId: bookingToDelete.eventType?.teamId,
|
||||
};
|
||||
|
||||
const eventTypeInfo: EventTypeInfo = {
|
||||
eventTitle: bookingToDelete?.eventType?.title || null,
|
||||
eventDescription: bookingToDelete?.eventType?.description || null,
|
||||
requiresConfirmation: bookingToDelete?.eventType?.requiresConfirmation || null,
|
||||
price: bookingToDelete?.eventType?.price || null,
|
||||
currency: bookingToDelete?.eventType?.currency || null,
|
||||
length: bookingToDelete?.eventType?.length || null,
|
||||
};
|
||||
|
||||
const webhooks = await getWebhooks(subscriberOptions);
|
||||
const promises = webhooks.map((webhook) =>
|
||||
sendPayload(webhook.secret, eventTrigger, new Date().toISOString(), webhook, {
|
||||
...evt,
|
||||
|
@ -677,12 +679,31 @@ async function handler(req: CustomRequest) {
|
|||
return { message: "Booking successfully cancelled." };
|
||||
}
|
||||
|
||||
async function handleSeatedEventCancellation(req: CustomRequest) {
|
||||
async function handleSeatedEventCancellation(
|
||||
req: CustomRequest,
|
||||
dataForWebhooks: {
|
||||
webhooks: {
|
||||
id: string;
|
||||
subscriberUrl: string;
|
||||
payloadTemplate: string | null;
|
||||
appId: string | null;
|
||||
secret: string | null;
|
||||
}[];
|
||||
evt: CalendarEvent;
|
||||
eventTypeInfo: EventTypeInfo;
|
||||
}
|
||||
) {
|
||||
const { seatReferenceUid } = schemaBookingCancelParams.parse(req.body);
|
||||
const { webhooks, evt, eventTypeInfo } = dataForWebhooks;
|
||||
if (!seatReferenceUid) return;
|
||||
if (!req.bookingToDelete?.attendees.length || req.bookingToDelete.attendees.length < 2) return;
|
||||
const bookingToDelete = req.bookingToDelete;
|
||||
if (!bookingToDelete?.attendees.length || bookingToDelete.attendees.length < 2) return;
|
||||
|
||||
const seatReference = req.bookingToDelete.seatsReferences.find(
|
||||
if (!bookingToDelete.userId) {
|
||||
throw new HttpError({ statusCode: 400, message: "User not found" });
|
||||
}
|
||||
|
||||
const seatReference = bookingToDelete.seatsReferences.find(
|
||||
(reference) => reference.referenceUid === seatReferenceUid
|
||||
);
|
||||
|
||||
|
@ -701,6 +722,36 @@ async function handleSeatedEventCancellation(req: CustomRequest) {
|
|||
}),
|
||||
]);
|
||||
req.statusCode = 200;
|
||||
|
||||
const attendee = bookingToDelete?.attendees.find((attendee) => attendee.id === seatReference.attendeeId);
|
||||
|
||||
evt.attendees = attendee
|
||||
? [
|
||||
{
|
||||
...attendee,
|
||||
language: {
|
||||
translate: await getTranslation(attendee.locale ?? "en", "common"),
|
||||
locale: attendee.locale ?? "en",
|
||||
},
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const promises = webhooks.map((webhook) =>
|
||||
sendPayload(webhook.secret, WebhookTriggerEvents.BOOKING_CANCELLED, new Date().toISOString(), webhook, {
|
||||
...evt,
|
||||
...eventTypeInfo,
|
||||
status: "CANCELLED",
|
||||
smsReminderNumber: bookingToDelete.smsReminderNumber || undefined,
|
||||
}).catch((e) => {
|
||||
console.error(
|
||||
`Error executing webhook for event: ${WebhookTriggerEvents.BOOKING_CANCELLED}, URL: ${webhook.subscriberUrl}`,
|
||||
e
|
||||
);
|
||||
})
|
||||
);
|
||||
await Promise.all(promises);
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
|
|
|
@ -1135,6 +1135,40 @@ async function handler(
|
|||
return deletedReferences;
|
||||
};
|
||||
|
||||
// data needed for triggering webhooks
|
||||
const eventTypeInfo: EventTypeInfo = {
|
||||
eventTitle: eventType.title,
|
||||
eventDescription: eventType.description,
|
||||
requiresConfirmation: requiresConfirmation || null,
|
||||
price: paymentAppData.price,
|
||||
currency: eventType.currency,
|
||||
length: eventType.length,
|
||||
};
|
||||
|
||||
const teamId = await getTeamId({ eventType });
|
||||
|
||||
const subscriberOptions: GetSubscriberOptions = {
|
||||
userId: organizerUser.id,
|
||||
eventTypeId,
|
||||
triggerEvent: WebhookTriggerEvents.BOOKING_CREATED,
|
||||
teamId,
|
||||
};
|
||||
|
||||
const eventTrigger: WebhookTriggerEvents = rescheduleUid
|
||||
? WebhookTriggerEvents.BOOKING_RESCHEDULED
|
||||
: WebhookTriggerEvents.BOOKING_CREATED;
|
||||
|
||||
subscriberOptions.triggerEvent = eventTrigger;
|
||||
|
||||
const subscriberOptionsMeetingEnded = {
|
||||
userId: organizerUser.id,
|
||||
eventTypeId,
|
||||
triggerEvent: WebhookTriggerEvents.MEETING_ENDED,
|
||||
teamId,
|
||||
};
|
||||
|
||||
const subscribersMeetingEnded = await getWebhooks(subscriberOptionsMeetingEnded);
|
||||
|
||||
const handleSeats = async () => {
|
||||
let resultBooking:
|
||||
| (Partial<Booking> & {
|
||||
|
@ -1167,6 +1201,9 @@ async function handler(
|
|||
startTime: true,
|
||||
user: true,
|
||||
status: true,
|
||||
smsReminderNumber: true,
|
||||
endTime: true,
|
||||
scheduledJobs: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1701,6 +1738,25 @@ async function handler(
|
|||
log.error("Error while scheduling workflow reminders", error);
|
||||
}
|
||||
|
||||
const webhookData = {
|
||||
...evt,
|
||||
...eventTypeInfo,
|
||||
bookingId: booking?.id,
|
||||
rescheduleUid,
|
||||
rescheduleStartTime: originalRescheduledBooking?.startTime
|
||||
? dayjs(originalRescheduledBooking?.startTime).utc().format()
|
||||
: undefined,
|
||||
rescheduleEndTime: originalRescheduledBooking?.endTime
|
||||
? dayjs(originalRescheduledBooking?.endTime).utc().format()
|
||||
: undefined,
|
||||
metadata: { ...metadata, ...reqBody.metadata },
|
||||
eventTypeId,
|
||||
status: "ACCEPTED",
|
||||
smsReminderNumber: booking?.smsReminderNumber || undefined,
|
||||
};
|
||||
|
||||
await handleWebhookTrigger({ subscriberOptions, eventTrigger, webhookData });
|
||||
|
||||
return resultBooking;
|
||||
};
|
||||
// For seats, if the booking already exists then we want to add the new attendee to the existing booking
|
||||
|
@ -2231,14 +2287,6 @@ async function handler(
|
|||
}
|
||||
: undefined;
|
||||
|
||||
const eventTypeInfo: EventTypeInfo = {
|
||||
eventTitle: eventType.title,
|
||||
eventDescription: eventType.description,
|
||||
requiresConfirmation: requiresConfirmation || null,
|
||||
price: paymentAppData.price,
|
||||
currency: eventType.currency,
|
||||
length: eventType.length,
|
||||
};
|
||||
const webhookData = {
|
||||
...evt,
|
||||
...eventTypeInfo,
|
||||
|
@ -2255,30 +2303,7 @@ async function handler(
|
|||
status: "ACCEPTED",
|
||||
smsReminderNumber: booking?.smsReminderNumber || undefined,
|
||||
};
|
||||
|
||||
const teamId = await getTeamId({ eventType });
|
||||
|
||||
const subscriberOptions: GetSubscriberOptions = {
|
||||
userId: organizerUser.id,
|
||||
eventTypeId,
|
||||
triggerEvent: WebhookTriggerEvents.BOOKING_CREATED,
|
||||
teamId,
|
||||
};
|
||||
|
||||
if (isConfirmedByDefault) {
|
||||
const eventTrigger: WebhookTriggerEvents = rescheduleUid
|
||||
? WebhookTriggerEvents.BOOKING_RESCHEDULED
|
||||
: WebhookTriggerEvents.BOOKING_CREATED;
|
||||
|
||||
subscriberOptions.triggerEvent = eventTrigger;
|
||||
|
||||
const subscriberOptionsMeetingEnded = {
|
||||
userId: organizerUser.id,
|
||||
eventTypeId,
|
||||
triggerEvent: WebhookTriggerEvents.MEETING_ENDED,
|
||||
teamId,
|
||||
};
|
||||
|
||||
try {
|
||||
const subscribersMeetingEnded = await getWebhooks(subscriberOptionsMeetingEnded);
|
||||
|
||||
|
|
Loading…
Reference in New Issue