diff --git a/packages/app-store/zapier/pages/setup/index.tsx b/packages/app-store/zapier/pages/setup/index.tsx index 701a40c595..ba46da54fe 100644 --- a/packages/app-store/zapier/pages/setup/index.tsx +++ b/packages/app-store/zapier/pages/setup/index.tsx @@ -64,7 +64,7 @@ export default function ZapierSetup(props: IZapierSetupProps) { <>
+
{newApiKey}
diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts
index 6bbbac3388..703bbfedfb 100644
--- a/packages/features/bookings/lib/handleNewBooking.ts
+++ b/packages/features/bookings/lib/handleNewBooking.ts
@@ -635,6 +635,12 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
if (rescheduleUid) {
originalRescheduledBooking = await getOriginalRescheduledBooking(rescheduleUid);
}
+ // If the user is not the owner of the event, new booking should be always pending.
+ // Otherwise, an owner rescheduling should be always accepted.
+ // Before comparing make sure that userId is set, otherwise undefined === undefined
+ const userReschedulingIsOwner = userId && originalRescheduledBooking?.user?.id === userId;
+ const isConfirmedByDefault =
+ (!eventType.requiresConfirmation && !stripeAppData.price) || userReschedulingIsOwner;
async function createBooking() {
if (originalRescheduledBooking) {
@@ -654,12 +660,6 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
const dynamicEventSlugRef = !eventTypeId ? eventTypeSlug : null;
const dynamicGroupSlugRef = !eventTypeId ? (reqBody.user as string).toLowerCase() : null;
- // If the user is not the owner of the event, new booking should be always pending.
- // Otherwise, an owner rescheduling should be always accepted.
- // Before comparing make sure that userId is set, otherwise undefined === undefined
- const userReschedulingIsOwner = userId && originalRescheduledBooking?.user?.id === userId;
- const isConfirmedByDefault =
- (!eventType.requiresConfirmation && !stripeAppData.price) || userReschedulingIsOwner;
const newBookingData: Prisma.BookingCreateInput = {
uid,
title: evt.title,
@@ -901,8 +901,7 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
}
}
- // FIXME: instead of requiresConfirmation, we should check if isConfirmedByDefault is set or not.
- if (eventType.requiresConfirmation && !rescheduleUid && noEmail !== true) {
+ if (!isConfirmedByDefault && noEmail !== true) {
await sendOrganizerRequestEmail({ ...evt, additionalNotes });
await sendAttendeeRequestEmail({ ...evt, additionalNotes }, attendeesList[0]);
}
@@ -927,72 +926,74 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
log.debug(`Booking ${organizerUser.username} completed`);
- const eventTrigger: WebhookTriggerEvents = rescheduleUid
- ? WebhookTriggerEvents.BOOKING_RESCHEDULED
- : WebhookTriggerEvents.BOOKING_CREATED;
- const subscriberOptions = {
- userId: organizerUser.id,
- eventTypeId,
- triggerEvent: eventTrigger,
- };
-
- const subscriberOptionsMeetingEnded = {
- userId: organizerUser.id,
- eventTypeId,
- triggerEvent: WebhookTriggerEvents.MEETING_ENDED,
- };
-
- try {
- const subscribersMeetingEnded = await getWebhooks(subscriberOptionsMeetingEnded);
-
- subscribersMeetingEnded.forEach((subscriber) => {
- if (rescheduleUid && originalRescheduledBooking) {
- cancelScheduledJobs(originalRescheduledBooking, undefined, true);
- }
- if (booking && booking.status === BookingStatus.ACCEPTED) {
- scheduleTrigger(booking, subscriber.subscriberUrl, subscriber);
- }
- });
- } catch (error) {
- log.error("Error while running scheduledJobs for booking", error);
- }
-
- try {
- // Send Webhook call if hooked to BOOKING_CREATED & BOOKING_RESCHEDULED
- const subscribers = await getWebhooks(subscriberOptions);
- console.log("evt:", {
- ...evt,
- metadata: reqBody.metadata,
- });
- const bookingId = booking?.id;
-
- const eventTypeInfo: EventTypeInfo = {
- eventTitle: eventType.title,
- eventDescription: eventType.description,
- requiresConfirmation: eventType.requiresConfirmation || null,
- price: stripeAppData.price,
- currency: eventType.currency,
- length: eventType.length,
+ if (isConfirmedByDefault) {
+ const eventTrigger: WebhookTriggerEvents = rescheduleUid
+ ? WebhookTriggerEvents.BOOKING_RESCHEDULED
+ : WebhookTriggerEvents.BOOKING_CREATED;
+ const subscriberOptions = {
+ userId: organizerUser.id,
+ eventTypeId,
+ triggerEvent: eventTrigger,
};
- const promises = subscribers.map((sub) =>
- sendPayload(sub.secret, eventTrigger, new Date().toISOString(), sub, {
+ const subscriberOptionsMeetingEnded = {
+ userId: organizerUser.id,
+ eventTypeId,
+ triggerEvent: WebhookTriggerEvents.MEETING_ENDED,
+ };
+
+ try {
+ const subscribersMeetingEnded = await getWebhooks(subscriberOptionsMeetingEnded);
+
+ subscribersMeetingEnded.forEach((subscriber) => {
+ if (rescheduleUid && originalRescheduledBooking) {
+ cancelScheduledJobs(originalRescheduledBooking, undefined, true);
+ }
+ if (booking && booking.status === BookingStatus.ACCEPTED) {
+ scheduleTrigger(booking, subscriber.subscriberUrl, subscriber);
+ }
+ });
+ } catch (error) {
+ log.error("Error while running scheduledJobs for booking", error);
+ }
+
+ try {
+ // Send Webhook call if hooked to BOOKING_CREATED & BOOKING_RESCHEDULED
+ const subscribers = await getWebhooks(subscriberOptions);
+ console.log("evt:", {
...evt,
- ...eventTypeInfo,
- bookingId,
- rescheduleUid,
metadata: reqBody.metadata,
- eventTypeId,
- //FIXME: FOr consistency b/w webhook and actual DB state, it should use isConfirmedByDefault here
- status: eventType.requiresConfirmation ? "PENDING" : "ACCEPTED",
- }).catch((e) => {
- console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${sub.subscriberUrl}`, e);
- })
- );
- await Promise.all(promises);
- } catch (error) {
- log.error("Error while sending webhook", error);
+ });
+ const bookingId = booking?.id;
+
+ const eventTypeInfo: EventTypeInfo = {
+ eventTitle: eventType.title,
+ eventDescription: eventType.description,
+ requiresConfirmation: eventType.requiresConfirmation || null,
+ price: stripeAppData.price,
+ currency: eventType.currency,
+ length: eventType.length,
+ };
+
+ const promises = subscribers.map((sub) =>
+ sendPayload(sub.secret, eventTrigger, new Date().toISOString(), sub, {
+ ...evt,
+ ...eventTypeInfo,
+ bookingId,
+ rescheduleUid,
+ metadata: reqBody.metadata,
+ eventTypeId,
+ status: "ACCEPTED",
+ }).catch((e) => {
+ console.error(`Error executing webhook for event: ${eventTrigger}, URL: ${sub.subscriberUrl}`, e);
+ })
+ );
+ await Promise.all(promises);
+ } catch (error) {
+ log.error("Error while sending webhook", error);
+ }
}
+
// Avoid passing referencesToCreate with id unique constrain values
// refresh hashed link if used
const urlSeed = `${organizerUser.username}:${dayjs(reqBody.start).utc().format()}`;
@@ -1038,7 +1039,8 @@ async function handler(req: NextApiRequest & { userId?: number | undefined }) {
reqBody.smsReminderNumber as string | null,
evt,
evt.requiresConfirmation || false,
- rescheduleUid ? true : false
+ rescheduleUid ? true : false,
+ true
);
} catch (error) {
log.error("Error while scheduling workflow reminders", error);
diff --git a/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx b/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx
index 968912d4d2..f8f25d9137 100644
--- a/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx
+++ b/packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx
@@ -71,7 +71,8 @@ export default function ApiKeyDialogForm({
+
+ {" "}
{apiKey}
diff --git a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts
index 49d26f6ff2..5990830bb8 100644
--- a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts
+++ b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts
@@ -20,7 +20,8 @@ export const scheduleWorkflowReminders = async (
smsReminderNumber: string | null,
evt: CalendarEvent,
needsConfirmation: boolean,
- isRescheduleEvent: boolean
+ isRescheduleEvent: boolean,
+ isFirstRecurringEvent: boolean
) => {
if (workflows.length > 0 && !needsConfirmation) {
workflows.forEach((workflowReference) => {
@@ -29,7 +30,9 @@ export const scheduleWorkflowReminders = async (
const workflow = workflowReference.workflow;
if (
workflow.trigger === WorkflowTriggerEvents.BEFORE_EVENT ||
- (workflow.trigger === WorkflowTriggerEvents.NEW_EVENT && !isRescheduleEvent) ||
+ (workflow.trigger === WorkflowTriggerEvents.NEW_EVENT &&
+ !isRescheduleEvent &&
+ isFirstRecurringEvent) ||
(workflow.trigger === WorkflowTriggerEvents.RESCHEDULE_EVENT && isRescheduleEvent) ||
workflow.trigger === WorkflowTriggerEvents.AFTER_EVENT
) {
diff --git a/packages/prisma/seed-app-store.config.json b/packages/prisma/seed-app-store.config.json
index e6944d49ce..13bddc7dea 100644
--- a/packages/prisma/seed-app-store.config.json
+++ b/packages/prisma/seed-app-store.config.json
@@ -125,7 +125,6 @@
"categories": ["video"],
"slug": "telegram",
"type": "telegram_video"
-
},
{
"dirName": "signal",
diff --git a/packages/trpc/server/routers/viewer/bookings.tsx b/packages/trpc/server/routers/viewer/bookings.tsx
index f1abd3eecb..3a4457a62e 100644
--- a/packages/trpc/server/routers/viewer/bookings.tsx
+++ b/packages/trpc/server/routers/viewer/bookings.tsx
@@ -30,7 +30,7 @@ import {
} from "@calcom/emails";
import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
-import sendPayload from "@calcom/features/webhooks/lib/sendPayload";
+import sendPayload, { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
import logger from "@calcom/lib/logger";
import { getTranslation } from "@calcom/lib/server";
@@ -665,6 +665,10 @@ export const bookingsRouter = router({
recurringEvent: true,
title: true,
requiresConfirmation: true,
+ currency: true,
+ length: true,
+ description: true,
+ price: true,
workflows: {
include: {
workflow: {
@@ -913,21 +917,23 @@ export const bookingsRouter = router({
}
//Workflows - set reminders for confirmed events
-
try {
- for (const updatedBooking of updatedBookings) {
- if (updatedBooking.eventType?.workflows) {
+ for (let index = 0; index < updatedBookings.length; index++) {
+ if (updatedBookings[index].eventType?.workflows) {
const evtOfBooking = evt;
- evtOfBooking.startTime = updatedBooking.startTime.toISOString();
- evtOfBooking.endTime = updatedBooking.endTime.toISOString();
- evtOfBooking.uid = updatedBooking.uid;
+ evtOfBooking.startTime = updatedBookings[index].startTime.toISOString();
+ evtOfBooking.endTime = updatedBookings[index].endTime.toISOString();
+ evtOfBooking.uid = updatedBookings[index].uid;
+
+ const isFirstBooking = index === 0;
await scheduleWorkflowReminders(
- updatedBooking.eventType.workflows,
- updatedBooking.smsReminderNumber,
+ updatedBookings[index]?.eventType?.workflows || [],
+ updatedBookings[index].smsReminderNumber,
evtOfBooking,
false,
- false
+ false,
+ isFirstBooking
);
}
}
@@ -944,6 +950,14 @@ export const bookingsRouter = router({
triggerEvent: WebhookTriggerEvents.MEETING_ENDED,
};
+ const subscriberOptionsBookingCreated = {
+ userId: booking.userId || 0,
+ eventTypeId: booking.eventTypeId || 0,
+ triggerEvent: WebhookTriggerEvents.BOOKING_CREATED,
+ };
+
+ const subscribersBookingCreated = await getWebhooks(subscriberOptionsBookingCreated);
+
const subscribersMeetingEnded = await getWebhooks(subscriberOptionsMeetingEnded);
subscribersMeetingEnded.forEach((subscriber) => {
@@ -951,6 +965,31 @@ export const bookingsRouter = router({
scheduleTrigger(booking, subscriber.subscriberUrl, subscriber);
});
});
+
+ const eventTypeInfo: EventTypeInfo = {
+ eventTitle: booking.eventType?.title,
+ eventDescription: booking.eventType?.description,
+ requiresConfirmation: booking.eventType?.requiresConfirmation || null,
+ price: booking.eventType?.price,
+ currency: booking.eventType?.currency,
+ length: booking.eventType?.length,
+ };
+
+ const promises = subscribersBookingCreated.map((sub) =>
+ sendPayload(sub.secret, WebhookTriggerEvents.BOOKING_CREATED, new Date().toISOString(), sub, {
+ ...evt,
+ ...eventTypeInfo,
+ bookingId,
+ eventTypeId: booking.eventType?.id,
+ status: "ACCEPTED",
+ }).catch((e) => {
+ console.error(
+ `Error executing webhook for event: ${WebhookTriggerEvents.BOOKING_CREATED}, URL: ${sub.subscriberUrl}`,
+ e
+ );
+ })
+ );
+ await Promise.all(promises);
} catch (error) {
// Silently fail
console.error(error);