feat: add RECORDING_READY webhook (#8277)

* feat: add RECORDING_READY webhook

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

* fix: type errors and improvements

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>

---------

Signed-off-by: Udit Takkar <udit.07814802719@cse.mait.ac.in>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
This commit is contained in:
Udit Takkar
2023-05-10 14:56:31 +00:00
committed by GitHub
co-authored by Keith Williams Hariom Balhara
parent 203a36e2b5
commit d067b0782a
8 changed files with 70 additions and 22 deletions
@@ -170,7 +170,7 @@ export const EventTeamWebhooksTab = ({
</Dialog>
{/* Edit webhook dialog */}
<Dialog open={editModalOpen} onOpenChange={(isOpen) => !isOpen && setEditModalOpen(false)}>
<DialogContent title={t("edit_webhook")}>
<DialogContent enableOverflow title={t("edit_webhook")}>
<WebhookForm
webhook={webhookToEdit}
apps={installedApps?.items.map((app) => app.slug)}
+62 -21
View File
@@ -1,3 +1,4 @@
import type { WebhookTriggerEvents } from "@prisma/client";
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
@@ -5,6 +6,8 @@ import { DailyLocationType } from "@calcom/app-store/locations";
import { getDownloadLinkOfCalVideoByRecordingId } from "@calcom/core/videoClient";
import { sendDailyVideoRecordingEmails } from "@calcom/emails";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import sendPayload from "@calcom/features/webhooks/lib/sendPayload";
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
import { defaultHandler } from "@calcom/lib/server";
import { getTranslation } from "@calcom/lib/server/i18n";
@@ -20,6 +23,38 @@ const downloadLinkSchema = z.object({
download_link: z.string(),
});
const triggerWebhook = async ({
evt,
downloadLink,
booking,
}: {
evt: CalendarEvent;
downloadLink: string;
booking: {
userId: number | undefined;
eventTypeId: number | null;
};
}) => {
const eventTrigger: WebhookTriggerEvents = "RECORDING_READY";
// Send Webhook call if hooked to BOOKING.RECORDING_READY
const subscriberOptions = {
userId: booking.userId ?? 0,
eventTypeId: booking.eventTypeId ?? 0,
triggerEvent: eventTrigger,
};
const webhooks = await getWebhooks(subscriberOptions);
const promises = webhooks.map((webhook) =>
sendPayload(webhook.secret, eventTrigger, new Date().toISOString(), webhook, {
...evt,
downloadLink,
}).catch((e) => {
console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${webhook.subscriberUrl}`, e);
})
);
await Promise.all(promises);
};
async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) {
return res.status(405).json({ message: "No SendGrid API key or email" });
@@ -51,6 +86,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
uid: true,
location: true,
isRecorded: true,
eventTypeId: true,
user: {
select: {
id: true,
@@ -105,31 +141,36 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
},
});
const response = await getDownloadLinkOfCalVideoByRecordingId(recordingId);
const downloadLinkResponse = downloadLinkSchema.parse(response);
const downloadLink = downloadLinkResponse.download_link;
const evt: CalendarEvent = {
type: booking.title,
title: booking.title,
description: booking.description || undefined,
startTime: booking.startTime.toISOString(),
endTime: booking.endTime.toISOString(),
organizer: {
email: booking.user?.email || "Email-less",
name: booking.user?.name || "Nameless",
timeZone: booking.user?.timeZone || "Europe/London",
language: { translate: t, locale: booking?.user?.locale ?? "en" },
},
attendees: attendeesList,
uid: booking.uid,
};
await triggerWebhook({
evt,
downloadLink,
booking: { userId: booking?.user?.id, eventTypeId: booking.eventTypeId },
});
const isSendingEmailsAllowed = IS_SELF_HOSTED || session?.user?.belongsToActiveTeam;
// send emails to all attendees only when user has team plan
if (isSendingEmailsAllowed) {
const response = await getDownloadLinkOfCalVideoByRecordingId(recordingId);
const downloadLinkResponse = downloadLinkSchema.parse(response);
const downloadLink = downloadLinkResponse.download_link;
const evt: CalendarEvent = {
type: booking.title,
title: booking.title,
description: booking.description || undefined,
startTime: booking.startTime.toISOString(),
endTime: booking.endTime.toISOString(),
organizer: {
email: booking.user?.email || "Email-less",
name: booking.user?.name || "Nameless",
timeZone: booking.user?.timeZone || "Europe/London",
language: { translate: t, locale: booking?.user?.locale ?? "en" },
},
attendees: attendeesList,
uid: booking.uid,
};
await sendDailyVideoRecordingEmails(evt, downloadLink);
return res.status(200).json({ message: "Success" });
}
@@ -367,6 +367,7 @@
"create_webhook": "Create Webhook",
"booking_cancelled": "Booking Cancelled",
"booking_rescheduled": "Booking Rescheduled",
"recording_ready":"Recording Download Link Ready",
"booking_created": "Booking Created",
"meeting_ended": "Meeting Ended",
"form_submitted": "Form Submitted",
@@ -34,6 +34,7 @@ const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2: Record<string, WebhookTriggerEve
{ value: WebhookTriggerEvents.BOOKING_CREATED, label: "booking_created" },
{ value: WebhookTriggerEvents.BOOKING_RESCHEDULED, label: "booking_rescheduled" },
{ value: WebhookTriggerEvents.MEETING_ENDED, label: "meeting_ended" },
{ value: WebhookTriggerEvents.RECORDING_READY, label: "recording_ready" },
],
"routing-forms": [{ value: WebhookTriggerEvents.FORM_SUBMITTED, label: "form_submitted" }],
} as const;
@@ -8,6 +8,7 @@ export const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP = {
WebhookTriggerEvents.BOOKING_CREATED,
WebhookTriggerEvents.BOOKING_RESCHEDULED,
WebhookTriggerEvents.MEETING_ENDED,
WebhookTriggerEvents.RECORDING_READY,
] as const,
"routing-forms": [WebhookTriggerEvents.FORM_SUBMITTED] as const,
};
@@ -27,6 +27,7 @@ type WebhookDataType = CalendarEvent &
rescheduleEndTime?: string;
triggerEvent: string;
createdAt: string;
downloadLink?: string;
};
function getZapierPayload(data: CalendarEvent & EventTypeInfo & { status?: string }): string {
@@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "WebhookTriggerEvents" ADD VALUE 'RECORDING_READY';
+1
View File
@@ -496,6 +496,7 @@ enum WebhookTriggerEvents {
BOOKING_CANCELLED
FORM_SUBMITTED
MEETING_ENDED
RECORDING_READY
}
model Webhook {