From 1b7591847e85ac93da2f7c7a8c03de858a61b38b Mon Sep 17 00:00:00 2001 From: Udit Takkar Date: Sun, 22 Dec 2024 01:40:07 +0530 Subject: [PATCH 1/8] perf: don't fetch all the hosts --- .../team/[slug]/[type]/page.tsx | 1 + .../org/[orgSlug]/team/[slug]/[type]/page.tsx | 1 + .../future/team/[slug]/[type]/embed/page.tsx | 1 + .../app/future/team/[slug]/[type]/page.tsx | 1 + .../BookEventForm/BookFormAsModal.tsx | 2 +- .../features/bookings/Booker/utils/event.ts | 3 +- packages/features/embed/Embed.tsx | 2 +- .../features/eventtypes/lib/getPublicEvent.ts | 195 +++++++++--------- packages/lib/server/repository/event.ts | 3 +- .../atoms/booker/BookerWebWrapper.tsx | 5 +- .../routers/publicViewer/event.schema.ts | 1 + 11 files changed, 115 insertions(+), 100 deletions(-) diff --git a/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx b/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx index 2786759be38759..220026afb8b66f 100644 --- a/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx @@ -27,6 +27,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent: true, org, fromRedirectOfNonOrgLink: context.query.orgRedirection === "true", + includeOnlyOneHost: true, }); const profileName = event?.profile.name ?? ""; diff --git a/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx b/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx index ea51f5cd9b771b..8090b11b4ae4b4 100644 --- a/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx @@ -25,6 +25,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent: true, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", + includeOnlyOneHost: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/apps/web/app/future/team/[slug]/[type]/embed/page.tsx b/apps/web/app/future/team/[slug]/[type]/embed/page.tsx index 378a528ff517dc..fe4f757da2fe11 100644 --- a/apps/web/app/future/team/[slug]/[type]/embed/page.tsx +++ b/apps/web/app/future/team/[slug]/[type]/embed/page.tsx @@ -26,6 +26,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent: true, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", + includeOnlyOneHost: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/apps/web/app/future/team/[slug]/[type]/page.tsx b/apps/web/app/future/team/[slug]/[type]/page.tsx index debf7575b85c76..2a27cdc8f632f2 100644 --- a/apps/web/app/future/team/[slug]/[type]/page.tsx +++ b/apps/web/app/future/team/[slug]/[type]/page.tsx @@ -24,6 +24,7 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => { isTeamEvent: true, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", + includeOnlyOneHost: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx b/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx index 888e15625331af..84331c9c2f2db3 100644 --- a/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx +++ b/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx @@ -12,7 +12,7 @@ import { FromTime } from "../../utils/dates"; import { useEvent } from "../../utils/event"; const BookEventFormWrapper = ({ children, onCancel }: { onCancel: () => void; children: ReactNode }) => { - const { data } = useEvent(); + const { data } = useEvent({ includeOnlyOneHost: true }); return ; }; diff --git a/packages/features/bookings/Booker/utils/event.ts b/packages/features/bookings/Booker/utils/event.ts index fd04b3eebe9e8b..e83baa65515963 100644 --- a/packages/features/bookings/Booker/utils/event.ts +++ b/packages/features/bookings/Booker/utils/event.ts @@ -19,7 +19,7 @@ export type useScheduleForEventReturnType = ReturnType { +export const useEvent = (props?: { fromRedirectOfNonOrgLink?: boolean; includeOnlyOneHost?: boolean }) => { const [username, eventSlug, isTeamEvent, org] = useBookerStore( (state) => [state.username, state.eventSlug, state.isTeamEvent, state.org], shallow @@ -32,6 +32,7 @@ export const useEvent = (props?: { fromRedirectOfNonOrgLink?: boolean }) => { isTeamEvent, org: org ?? null, fromRedirectOfNonOrgLink: props?.fromRedirectOfNonOrgLink, + includeOnlyOneHost: props?.includeOnlyOneHost, }, { refetchOnWindowFocus: false, diff --git a/packages/features/embed/Embed.tsx b/packages/features/embed/Embed.tsx index f136c9e1af9a5d..52f4a2918e3283 100644 --- a/packages/features/embed/Embed.tsx +++ b/packages/features/embed/Embed.tsx @@ -178,7 +178,7 @@ const EmailEmbed = ({ ], shallow ); - const event = useEvent(); + const event = useEvent({ includeOnlyOneHost: true }); const schedule = useScheduleForEvent({ orgSlug, eventId: eventType?.id, isTeamEvent }); const nonEmptyScheduleDays = useNonEmptyScheduleDays(schedule?.data?.slots); diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index 14ecd04dd106ef..c075c57e41836f 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -50,99 +50,6 @@ const userSelect = Prisma.validator()({ defaultScheduleId: true, }); -const publicEventSelect = Prisma.validator()({ - id: true, - title: true, - description: true, - eventName: true, - slug: true, - isInstantEvent: true, - instantMeetingParameters: true, - aiPhoneCallConfig: true, - schedulingType: true, - length: true, - locations: true, - customInputs: true, - disableGuests: true, - metadata: true, - lockTimeZoneToggleOnBookingPage: true, - requiresConfirmation: true, - autoTranslateDescriptionEnabled: true, - fieldTranslations: { - select: { - translatedText: true, - targetLocale: true, - field: true, - }, - }, - requiresBookerEmailVerification: true, - recurringEvent: true, - price: true, - currency: true, - seatsPerTimeSlot: true, - seatsShowAvailabilityCount: true, - bookingFields: true, - teamId: true, - team: { - select: { - parentId: true, - metadata: true, - brandColor: true, - darkBrandColor: true, - slug: true, - name: true, - logoUrl: true, - theme: true, - parent: { - select: { - slug: true, - name: true, - bannerUrl: true, - logoUrl: true, - }, - }, - isPrivate: true, - }, - }, - successRedirectUrl: true, - forwardParamsSuccessRedirect: true, - workflows: { - include: { - workflow: { - include: { - steps: true, - }, - }, - }, - }, - hosts: { - select: { - user: { - select: userSelect, - }, - }, - }, - owner: { - select: userSelect, - }, - schedule: { - select: { - id: true, - timeZone: true, - }, - }, - instantMeetingSchedule: { - select: { - id: true, - timeZone: true, - }, - }, - - hidden: true, - assignAllTeamMembers: true, - rescheduleWithSameRoundRobinHost: true, -}); - export async function isCurrentlyAvailable({ prisma, instantMeetingScheduleId, @@ -209,6 +116,102 @@ function isAvailableInTimeSlot( return isWithinPeriod; } +const getPublicEventSelect = (includeOnlyOneHost?: boolean) => { + return Prisma.validator()({ + id: true, + title: true, + description: true, + eventName: true, + slug: true, + isInstantEvent: true, + instantMeetingParameters: true, + aiPhoneCallConfig: true, + schedulingType: true, + length: true, + locations: true, + customInputs: true, + disableGuests: true, + metadata: true, + lockTimeZoneToggleOnBookingPage: true, + requiresConfirmation: true, + autoTranslateDescriptionEnabled: true, + fieldTranslations: { + select: { + translatedText: true, + targetLocale: true, + field: true, + }, + }, + requiresBookerEmailVerification: true, + recurringEvent: true, + price: true, + currency: true, + seatsPerTimeSlot: true, + seatsShowAvailabilityCount: true, + bookingFields: true, + teamId: true, + team: { + select: { + parentId: true, + metadata: true, + brandColor: true, + darkBrandColor: true, + slug: true, + name: true, + logoUrl: true, + theme: true, + parent: { + select: { + slug: true, + name: true, + bannerUrl: true, + logoUrl: true, + }, + }, + isPrivate: true, + }, + }, + successRedirectUrl: true, + forwardParamsSuccessRedirect: true, + workflows: { + include: { + workflow: { + include: { + steps: true, + }, + }, + }, + }, + hosts: { + select: { + user: { + select: userSelect, + }, + }, + ...(includeOnlyOneHost ? { take: 3 } : {}), + }, + owner: { + select: userSelect, + }, + schedule: { + select: { + id: true, + timeZone: true, + }, + }, + instantMeetingSchedule: { + select: { + id: true, + timeZone: true, + }, + }, + + hidden: true, + assignAllTeamMembers: true, + rescheduleWithSameRoundRobinHost: true, + }); +}; + // TODO: Convert it to accept a single parameter with structured data export const getPublicEvent = async ( username: string, @@ -217,8 +220,10 @@ export const getPublicEvent = async ( org: string | null, prisma: PrismaClient, fromRedirectOfNonOrgLink: boolean, - currentUserId?: number + currentUserId?: number, + includeOnlyOneHost?: boolean ) => { + const publicEventSelect = getPublicEventSelect(includeOnlyOneHost); const usernameList = getUsernameList(username); const orgQuery = org ? getSlugOrRequestedSlug(org) : null; // In case of dynamic group event, we fetch user's data and use the default event. @@ -504,7 +509,7 @@ export const getPublicEvent = async ( }; const eventData = Prisma.validator()({ - select: publicEventSelect, + select: getPublicEventSelect(false), }); type Event = Prisma.EventTypeGetPayload; diff --git a/packages/lib/server/repository/event.ts b/packages/lib/server/repository/event.ts index e1d2fa222391d5..3b79183ab5631b 100644 --- a/packages/lib/server/repository/event.ts +++ b/packages/lib/server/repository/event.ts @@ -11,7 +11,8 @@ export class EventRepository { input.org, prisma, input.fromRedirectOfNonOrgLink, - userId + userId, + input.includeOnlyOneHost ); return event; } diff --git a/packages/platform/atoms/booker/BookerWebWrapper.tsx b/packages/platform/atoms/booker/BookerWebWrapper.tsx index 88eb7acc686bfc..9fd4a5ec275959 100644 --- a/packages/platform/atoms/booker/BookerWebWrapper.tsx +++ b/packages/platform/atoms/booker/BookerWebWrapper.tsx @@ -31,7 +31,10 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => { const router = useRouter(); const pathname = usePathname(); const searchParams = useSearchParams(); - const event = useEvent({ fromRedirectOfNonOrgLink: props.entity.fromRedirectOfNonOrgLink }); + const event = useEvent({ + fromRedirectOfNonOrgLink: props.entity.fromRedirectOfNonOrgLink, + includeOnlyOneHost: true, + }); const bookerLayout = useBookerLayout(event.data); const selectedDate = searchParams?.get("date"); diff --git a/packages/trpc/server/routers/publicViewer/event.schema.ts b/packages/trpc/server/routers/publicViewer/event.schema.ts index d10a5965cccbef..708d883e0456bb 100644 --- a/packages/trpc/server/routers/publicViewer/event.schema.ts +++ b/packages/trpc/server/routers/publicViewer/event.schema.ts @@ -10,6 +10,7 @@ export const ZEventInputSchema = z.object({ * Based on this decision like whether to allow unpublished organization's event to be served or not can be made. */ fromRedirectOfNonOrgLink: z.boolean().optional().default(false), + includeOnlyOneHost: z.boolean().optional().default(false), }); export type TEventInputSchema = z.infer; From de03a0f61dc8345ab44aba33c30be9e6012d6d9b Mon Sep 17 00:00:00 2001 From: Udit Takkar Date: Sun, 22 Dec 2024 01:49:52 +0530 Subject: [PATCH 2/8] fix: type err --- apps/web/app/d/[link]/[slug]/page.tsx | 1 + apps/web/app/future/[user]/[type]/page.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/web/app/d/[link]/[slug]/page.tsx b/apps/web/app/d/[link]/[slug]/page.tsx index 0d194c685322de..c9e725ed3b3ae8 100644 --- a/apps/web/app/d/[link]/[slug]/page.tsx +++ b/apps/web/app/d/[link]/[slug]/page.tsx @@ -28,6 +28,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent, org, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", + includeOnlyOneHost: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/apps/web/app/future/[user]/[type]/page.tsx b/apps/web/app/future/[user]/[type]/page.tsx index 3a61c20e5badb9..81e11ff88c2c08 100644 --- a/apps/web/app/future/[user]/[type]/page.tsx +++ b/apps/web/app/future/[user]/[type]/page.tsx @@ -28,6 +28,7 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => { isTeamEvent: false, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", + includeOnlyOneHost: true, }); const profileName = event?.profile?.name ?? ""; From 9eaa3f610cda6ef4e9887730309c9e2d3c77d113 Mon Sep 17 00:00:00 2001 From: Hariom Date: Mon, 23 Dec 2024 12:02:58 +0530 Subject: [PATCH 3/8] use appschema only when necessary --- .../app-store/_utils/getEventTypeAppData.ts | 6 +- .../payments/checkForMultiplePaymentApps.ts | 9 +- packages/app-store/salesforce/zod.ts | 5 +- .../getAllCredentials.ts | 4 +- .../bookings/lib/handleConfirmation.ts | 5 +- .../features/bookings/lib/handleNewBooking.ts | 20 +++- .../lib/handleSeats/create/createNewSeat.ts | 4 +- .../credentials/handleDeleteCredential.ts | 20 ++-- .../components/EventTypeDescription.tsx | 6 +- .../tabs/instant/EventInstantTab.tsx | 6 +- .../tabs/recurring/EventRecurringTab.tsx | 6 +- .../features/eventtypes/lib/getPublicEvent.ts | 6 +- packages/features/eventtypes/lib/types.ts | 4 +- .../handleSendingAttendeeNoShowDataToApps.ts | 4 +- packages/lib/defaultEvents.ts | 3 +- packages/lib/event-types/getEventTypeById.ts | 4 +- .../lib/event-types/getEventTypesByViewer.ts | 4 +- packages/lib/payment/handlePayment.ts | 13 +-- packages/lib/payment/handlePaymentSuccess.ts | 4 +- packages/lib/piiFreeData.ts | 4 +- packages/lib/server/service/eventType.ts | 6 +- .../event-types/hooks/useEventTypeForm.ts | 4 +- .../event-types/hooks/useTabsNavigations.tsx | 6 +- packages/prisma/zod-utils.ts | 98 +++++++++++-------- .../viewer/eventTypes/update.handler.ts | 6 +- .../getAllActiveWorkflows.handler.ts | 9 +- 26 files changed, 169 insertions(+), 97 deletions(-) diff --git a/packages/app-store/_utils/getEventTypeAppData.ts b/packages/app-store/_utils/getEventTypeAppData.ts index 08c60d3d184a48..3b7aa78ecd497b 100644 --- a/packages/app-store/_utils/getEventTypeAppData.ts +++ b/packages/app-store/_utils/getEventTypeAppData.ts @@ -1,9 +1,11 @@ import type { z } from "zod"; import type { BookerEvent } from "@calcom/features/bookings/types"; -import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import type { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; -export type EventTypeApps = NonNullable>["apps"]>; +export type EventTypeApps = NonNullable< + NonNullable>["apps"] +>; export type EventTypeAppsList = keyof EventTypeApps; export const getEventTypeAppData = ( diff --git a/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts b/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts index e1186b4a1ec6a0..49dcccde58ded2 100644 --- a/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts +++ b/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts @@ -1,6 +1,6 @@ import type z from "zod"; -import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import type { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import type { appDataSchemas } from "../../apps.schemas.generated"; @@ -11,7 +11,12 @@ import type { appDataSchemas } from "../../apps.schemas.generated"; * @returns boolean */ const checkForMultiplePaymentApps = ( - metadata: z.infer, + metadata: + | { + apps?: z.infer; + } + | null + | undefined, inclusive = false ) => { let enabledPaymentApps = 0; diff --git a/packages/app-store/salesforce/zod.ts b/packages/app-store/salesforce/zod.ts index cf21fa02f0ea29..e5e3a59e884433 100644 --- a/packages/app-store/salesforce/zod.ts +++ b/packages/app-store/salesforce/zod.ts @@ -27,10 +27,7 @@ export const appDataSchema = eventTypeAppCardZod.extend({ .nativeEnum(SalesforceRecordEnum) .default(SalesforceRecordEnum.CONTACT) .optional(), - // Another way to define z.boolean().optional() through z.any() - // Required to prevent tsc crash. I have no clue why it started crashing suddenly. - // But we need to improve the schemas of all apps that are combined into apps.schemas.generated.ts - ifFreeEmailDomainSkipOwnerCheck: optionalBooleanOnlyRunTimeValidation, + ifFreeEmailDomainSkipOwnerCheck: z.boolean().optional(), skipContactCreation: z.boolean().optional(), createEventOn: z.nativeEnum(SalesforceRecordEnum).default(SalesforceRecordEnum.CONTACT).optional(), createNewContactUnderAccount: z.boolean().optional(), diff --git a/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts b/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts index fc697f154eafd2..9578818c0ce0fc 100644 --- a/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts +++ b/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts @@ -3,6 +3,7 @@ import type z from "zod"; import { UserRepository } from "@calcom/lib/server/repository/user"; import prisma from "@calcom/prisma"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; +import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { CredentialPayload } from "@calcom/types/Credential"; @@ -76,8 +77,7 @@ export const getAllCredentials = async ( } // Only return CRM credentials that are enabled on the event type - const eventTypeAppMetadata = eventType?.metadata?.apps; - console.log(eventTypeAppMetadata); + const eventTypeAppMetadata = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); // Will be [credentialId]: { enabled: boolean }] const eventTypeCrmCredentials: Record = {}; diff --git a/packages/features/bookings/lib/handleConfirmation.ts b/packages/features/bookings/lib/handleConfirmation.ts index 2f28d265f92c65..cb11060bfa7707 100644 --- a/packages/features/bookings/lib/handleConfirmation.ts +++ b/packages/features/bookings/lib/handleConfirmation.ts @@ -24,7 +24,7 @@ import type { PrismaClient } from "@calcom/prisma"; import type { SchedulingType } from "@calcom/prisma/enums"; import { BookingStatus, WebhookTriggerEvents } from "@calcom/prisma/enums"; import type { PlatformClientParams } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchema, EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util"; import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar"; @@ -84,7 +84,8 @@ export async function handleConfirmation(args: { } = args; const eventType = booking.eventType; const eventTypeMetadata = EventTypeMetaDataSchema.parse(eventType?.metadata || {}); - const eventManager = new EventManager(user, eventTypeMetadata?.apps); + const apps = EventTypeAppMetadataOptionalSchema.parse(eventTypeMetadata?.apps); + const eventManager = new EventManager(user, apps); const scheduleResult = await eventManager.create(evt); const results = scheduleResult.results; const metadata: AdditionalInformation = {}; diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index ec06c494850db4..0abd690fdfce38 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -68,6 +68,10 @@ import { WorkflowRepository } from "@calcom/lib/server/repository/workflow"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; import prisma from "@calcom/prisma"; import { BookingStatus, SchedulingType, WebhookTriggerEvents } from "@calcom/prisma/enums"; +import { + EventTypeAppMetadataOptionalSchema, + EventTypeMetaDataSchemaWithTypedApps, +} from "@calcom/prisma/zod-utils"; import type { PlatformClientParams } from "@calcom/prisma/zod-utils"; import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils"; import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util"; @@ -421,7 +425,10 @@ async function handler( const isTeamEventType = !!eventType.schedulingType && ["COLLECTIVE", "ROUND_ROBIN"].includes(eventType.schedulingType); - const paymentAppData = getPaymentAppData(eventType); + const paymentAppData = getPaymentAppData({ + ...eventType, + metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }); loggerWithEventDetails.info( `Booking eventType ${eventTypeId} started`, safeStringify({ @@ -1051,7 +1058,13 @@ async function handler( oAuthClientId: platformClientId, }; - const workflows = await getAllWorkflowsFromEventType(eventType, organizerUser.id); + const workflows = await getAllWorkflowsFromEventType( + { + ...eventType, + metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }, + organizerUser.id + ); if (isTeamEventType) { evt.team = { @@ -1275,8 +1288,9 @@ async function handler( // After polling videoBusyTimes, credentials might have been changed due to refreshment, so query them again. const credentials = await monitorCallbackAsync(refreshCredentials, allCredentials); + const apps = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); const eventManager = !isDryRun - ? new EventManager({ ...organizerUser, credentials }, eventType?.metadata?.apps) + ? new EventManager({ ...organizerUser, credentials }, apps) : buildDryRunEventManager(); let videoCallUrl; diff --git a/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts b/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts index 12ef741a4bda84..303080653f1a94 100644 --- a/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts +++ b/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts @@ -14,6 +14,7 @@ import { HttpError } from "@calcom/lib/http-error"; import { handlePayment } from "@calcom/lib/payment/handlePayment"; import prisma from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/enums"; +import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import { findBookingQuery } from "../../handleNewBooking/findBookingQuery"; import type { IEventTypePaymentCredentialType } from "../../handleNewBooking/types"; @@ -147,7 +148,8 @@ const createNewSeat = async ( ); } const credentials = await refreshCredentials(allCredentials); - const eventManager = new EventManager({ ...organizerUser, credentials }, eventType?.metadata?.apps); + const apps = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); + const eventManager = new EventManager({ ...organizerUser, credentials }, apps); await eventManager.updateCalendarAttendees(evt, seatedBooking); const foundBooking = await findBookingQuery(seatedBooking.id); diff --git a/packages/features/credentials/handleDeleteCredential.ts b/packages/features/credentials/handleDeleteCredential.ts index dfc45f32994403..49ed51a15abacc 100644 --- a/packages/features/credentials/handleDeleteCredential.ts +++ b/packages/features/credentials/handleDeleteCredential.ts @@ -14,7 +14,8 @@ import { bookingMinimalSelect, prisma } from "@calcom/prisma"; import { AppCategories, BookingStatus } from "@calcom/prisma/enums"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; import type { EventTypeAppMetadataSchema, EventTypeMetadata } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchema, EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils"; type App = { @@ -151,9 +152,11 @@ const handleDeleteCredential = async ({ if (credential.app?.categories.includes(AppCategories.crm)) { const metadata = EventTypeMetaDataSchema.parse(eventType.metadata); const appSlugToDelete = credential.app?.slug; - + const apps = EventTypeAppMetadataOptionalSchema.parse(metadata?.apps); if (appSlugToDelete) { - const appMetadata = removeAppFromEventTypeMetadata(appSlugToDelete, metadata); + const appMetadata = removeAppFromEventTypeMetadata(appSlugToDelete, { + apps, + }); await prisma.$transaction(async () => { await prisma.eventType.update({ @@ -179,7 +182,10 @@ const handleDeleteCredential = async ({ const metadata = EventTypeMetaDataSchema.parse(eventType.metadata); const appSlug = credential.app?.slug; if (appSlug) { - const appMetadata = removeAppFromEventTypeMetadata(appSlug, metadata); + const apps = EventTypeAppMetadataOptionalSchema.parse(metadata?.apps); + const appMetadata = removeAppFromEventTypeMetadata(appSlug, { + apps, + }); await prisma.$transaction(async () => { await prisma.eventType.update({ @@ -358,7 +364,7 @@ const handleDeleteCredential = async ({ } else if ( appStoreMetadata[credential.app?.slug as keyof typeof appStoreMetadata]?.extendsFeature === "EventType" ) { - const metadata = EventTypeMetaDataSchema.parse(eventType.metadata); + const metadata = EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata); const appSlug = credential.app?.slug; if (appSlug) { await prisma.eventType.update({ @@ -456,7 +462,9 @@ const handleDeleteCredential = async ({ const removeAppFromEventTypeMetadata = ( appSlugToDelete: string, - eventTypeMetadata: z.infer + eventTypeMetadata: { + apps: z.infer; + } ) => { const appMetadata = eventTypeMetadata?.apps ? Object.entries(eventTypeMetadata.apps).reduce((filteredApps, [appName, appData]) => { diff --git a/packages/features/eventtypes/components/EventTypeDescription.tsx b/packages/features/eventtypes/components/EventTypeDescription.tsx index 0afecba82992af..bc0d7a8280884f 100644 --- a/packages/features/eventtypes/components/EventTypeDescription.tsx +++ b/packages/features/eventtypes/components/EventTypeDescription.tsx @@ -11,6 +11,7 @@ import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import type { baseEventTypeSelect } from "@calcom/prisma"; import { SchedulingType } from "@calcom/prisma/enums"; import type { EventTypeModel } from "@calcom/prisma/zod"; +import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { Badge } from "@calcom/ui"; export type EventTypeDescriptionProps = { @@ -39,7 +40,10 @@ export const EventTypeDescription = ({ [eventType.recurringEvent] ); - const paymentAppData = getPaymentAppData(eventType); + const paymentAppData = getPaymentAppData({ + ...eventType, + metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }); return ( <> diff --git a/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx b/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx index 1aa13d5ab997a8..f72d17ce28d892 100644 --- a/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx +++ b/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx @@ -1,5 +1,6 @@ import type { EventTypeSetupProps } from "@calcom/features/eventtypes/lib/types"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; +import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import InstantEventController from "./InstantEventController"; @@ -7,7 +8,10 @@ export const EventInstantTab = ({ eventType, isTeamEvent, }: Pick & { isTeamEvent: boolean }) => { - const paymentAppData = getPaymentAppData(eventType); + const paymentAppData = getPaymentAppData({ + ...eventType, + metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }); const requirePayment = paymentAppData.price > 0; diff --git a/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx b/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx index ed62fa493ef3a4..3afdc755459530 100644 --- a/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx +++ b/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx @@ -1,4 +1,5 @@ import getPaymentAppData from "@calcom/lib/getPaymentAppData"; +import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { RecurringEventControllerProps } from "./RecurringEventController"; import RecurringEventController from "./RecurringEventController"; @@ -6,7 +7,10 @@ import RecurringEventController from "./RecurringEventController"; export type EventRecurringTabProps = Omit; export const EventRecurringTab = ({ eventType, customClassNames }: EventRecurringTabProps) => { - const paymentAppData = getPaymentAppData(eventType); + const paymentAppData = getPaymentAppData({ + ...eventType, + metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }); const requirePayment = paymentAppData.price > 0; diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index 14ecd04dd106ef..fb19d2b2c05eb9 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -20,7 +20,7 @@ import type { Team } from "@calcom/prisma/client"; import type { BookerLayoutSettings } from "@calcom/prisma/zod-utils"; import { BookerLayouts, - EventTypeMetaDataSchema, + EventTypeMetaDataSchemaWithTypedApps, bookerLayoutOptions, bookerLayouts as bookerLayoutsSchema, customInputSchema, @@ -368,7 +368,7 @@ export const getPublicEvent = async ( if (!event) return null; - const eventMetaData = EventTypeMetaDataSchema.parse(event.metadata || {}); + const eventMetaData = EventTypeMetaDataSchemaWithTypedApps.parse(event.metadata || {}); const teamMetadata = teamMetadataSchema.parse(event.team?.metadata || {}); const usersAsHosts = event.hosts.map((host) => host.user); @@ -517,7 +517,7 @@ function getProfileFromEvent(event: Event) { const username = "username" in profile ? profile.username : team?.slug; const weekStart = hosts?.[0]?.user?.weekStart || owner?.weekStart || "Monday"; - const eventMetaData = EventTypeMetaDataSchema.parse(event.metadata || {}); + const eventMetaData = EventTypeMetaDataSchemaWithTypedApps.parse(event.metadata || {}); const userMetaData = userMetadataSchema.parse(profile.metadata || {}); return { diff --git a/packages/features/eventtypes/lib/types.ts b/packages/features/eventtypes/lib/types.ts index 64d710c03f0a29..d9ce7722f22021 100644 --- a/packages/features/eventtypes/lib/types.ts +++ b/packages/features/eventtypes/lib/types.ts @@ -5,7 +5,7 @@ import type { ChildrenEventType } from "@calcom/features/eventtypes/components/C import type { AttributesQueryValue } from "@calcom/lib/raqb/types"; import type { EventTypeTranslation } from "@calcom/prisma/client"; import type { PeriodType, SchedulingType } from "@calcom/prisma/enums"; -import type { BookerLayoutSettings, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import type { BookerLayoutSettings, EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { customInputSchema } from "@calcom/prisma/zod-utils"; import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils"; import type { eventTypeColor } from "@calcom/prisma/zod-utils"; @@ -122,7 +122,7 @@ export type FormValues = { beforeEventBuffer: number; afterEventBuffer: number; slotInterval: number | null; - metadata: z.infer; + metadata: z.infer; destinationCalendar: { integration: string; externalId: string; diff --git a/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts b/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts index f46de7b0416ccc..9c542858cae426 100644 --- a/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts +++ b/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts @@ -4,7 +4,7 @@ import type { eventTypeAppCardZod } from "@calcom/app-store/eventTypeAppCardZod" import CrmManager from "@calcom/core/crmManager/crmManager"; import logger from "@calcom/lib/logger"; import prisma from "@calcom/prisma"; -import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeAppMetadataOptionalSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { NoShowAttendees } from "../handleMarkNoShow"; import { noShowEnabledApps } from "./noShowEnabledApps"; @@ -38,7 +38,7 @@ export default async function handleSendingAttendeeNoShowDataToApps( log.error(`Malformed event type metadata for bookingUid ${bookingUid}`); return; } - const eventTypeAppMetadata = eventTypeMetadataParse.data?.apps; + const eventTypeAppMetadata = EventTypeAppMetadataOptionalSchema.parse(eventTypeMetadataParse.data?.apps); for (const slug in eventTypeAppMetadata) { if (noShowEnabledApps.includes(slug)) { diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts index ed47ae8475884f..e1761d86992be6 100644 --- a/packages/lib/defaultEvents.ts +++ b/packages/lib/defaultEvents.ts @@ -5,6 +5,7 @@ import slugify from "@calcom/lib/slugify"; import { PeriodType, SchedulingType } from "@calcom/prisma/enums"; import type { userSelect } from "@calcom/prisma/selects"; import type { CustomInputSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { CredentialPayload } from "@calcom/types/Credential"; @@ -132,7 +133,7 @@ export const dynamicEvent = { descriptionAsSafeHTML: "", position: 0, ...commons, - metadata: EventTypeMetaDataSchema.parse({ multipleDuration: [15, 30, 45, 60, 90] }), + metadata: EventTypeMetaDataSchemaWithTypedApps.parse({ multipleDuration: [15, 30, 45, 60, 90] }), }; export const defaultEvents = [dynamicEvent]; diff --git a/packages/lib/event-types/getEventTypeById.ts b/packages/lib/event-types/getEventTypeById.ts index 738934cd07d503..864da19e909e7a 100644 --- a/packages/lib/event-types/getEventTypeById.ts +++ b/packages/lib/event-types/getEventTypeById.ts @@ -11,7 +11,7 @@ import { EventTypeRepository } from "@calcom/lib/server/repository/eventType"; import { UserRepository } from "@calcom/lib/server/repository/user"; import type { PrismaClient } from "@calcom/prisma"; import { SchedulingType, MembershipRole } from "@calcom/prisma/enums"; -import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { customInputSchema, EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -58,7 +58,7 @@ export const getEventTypeById = async ({ } const { locations, metadata, ...restEventType } = rawEventType; - const newMetadata = EventTypeMetaDataSchema.parse(metadata || {}) || {}; + const newMetadata = EventTypeMetaDataSchemaWithTypedApps.parse(metadata || {}) || {}; const apps = newMetadata?.apps || {}; const eventTypeWithParsedMetadata = { ...rawEventType, metadata: newMetadata }; const eventTeamMembershipsWithUserProfile = []; diff --git a/packages/lib/event-types/getEventTypesByViewer.ts b/packages/lib/event-types/getEventTypesByViewer.ts index 189f262102585a..1e3265b2b6b9fe 100644 --- a/packages/lib/event-types/getEventTypesByViewer.ts +++ b/packages/lib/event-types/getEventTypesByViewer.ts @@ -15,7 +15,7 @@ import { ProfileRepository } from "@calcom/lib/server/repository/profile"; import { UserRepository } from "@calcom/lib/server/repository/user"; import { MembershipRole, SchedulingType } from "@calcom/prisma/enums"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchemaWithoutApps } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -116,7 +116,7 @@ export const getEventTypesByViewer = async (user: User, filters?: Filters, forRo }) ) ), - metadata: eventType.metadata ? EventTypeMetaDataSchema.parse(eventType.metadata) : null, + metadata: eventType.metadata ? EventTypeMetaDataSchemaWithoutApps.parse(eventType.metadata) : null, children: await Promise.all( (eventType.children || []).map(async (c) => ({ ...c, diff --git a/packages/lib/payment/handlePayment.ts b/packages/lib/payment/handlePayment.ts index a2093051e0d7d9..ce4ad4a30ce2e8 100644 --- a/packages/lib/payment/handlePayment.ts +++ b/packages/lib/payment/handlePayment.ts @@ -3,6 +3,7 @@ import type { AppCategories, Prisma } from "@prisma/client"; import appStore from "@calcom/app-store"; import type { EventTypeAppsList } from "@calcom/app-store/utils"; import type { CompleteEventType } from "@calcom/prisma/zod"; +import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import type { CalendarEvent } from "@calcom/types/Calendar"; import type { IAbstractPaymentService, PaymentApp } from "@calcom/types/PaymentService"; @@ -40,15 +41,15 @@ const handlePayment = async ( const paymentInstance = new PaymentService(paymentAppCredentials) as IAbstractPaymentService; - const paymentOption = - selectedEventType?.metadata?.apps?.[paymentAppCredentials.appId].paymentOption || "ON_BOOKING"; + const apps = EventTypeAppMetadataOptionalSchema.parse(selectedEventType?.metadata?.apps); + const paymentOption = apps?.[paymentAppCredentials.appId].paymentOption || "ON_BOOKING"; let paymentData; if (paymentOption === "HOLD") { paymentData = await paymentInstance.collectCard( { - amount: selectedEventType?.metadata?.apps?.[paymentAppCredentials.appId].price, - currency: selectedEventType?.metadata?.apps?.[paymentAppCredentials.appId].currency, + amount: apps?.[paymentAppCredentials.appId].price, + currency: apps?.[paymentAppCredentials.appId].currency, }, booking.id, paymentOption, @@ -58,8 +59,8 @@ const handlePayment = async ( } else { paymentData = await paymentInstance.create( { - amount: selectedEventType?.metadata?.apps?.[paymentAppCredentials.appId].price, - currency: selectedEventType?.metadata?.apps?.[paymentAppCredentials.appId].currency, + amount: apps?.[paymentAppCredentials.appId].price, + currency: apps?.[paymentAppCredentials.appId].currency, }, booking.id, booking.userId, diff --git a/packages/lib/payment/handlePaymentSuccess.ts b/packages/lib/payment/handlePaymentSuccess.ts index 84db4bd57bc1ec..7bcf29b9c04b4c 100644 --- a/packages/lib/payment/handlePaymentSuccess.ts +++ b/packages/lib/payment/handlePaymentSuccess.ts @@ -9,6 +9,7 @@ import { HttpError as HttpCode } from "@calcom/lib/http-error"; import { getBooking } from "@calcom/lib/payment/getBooking"; import prisma from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/enums"; +import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import logger from "../logger"; @@ -26,7 +27,8 @@ export async function handlePaymentSuccess(paymentId: number, bookingId: number) const isConfirmed = booking.status === BookingStatus.ACCEPTED; if (isConfirmed) { - const eventManager = new EventManager(userWithCredentials, eventType?.metadata?.apps); + const apps = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); + const eventManager = new EventManager(userWithCredentials, apps); const scheduleResult = await eventManager.create(evt); bookingData.references = { create: scheduleResult.referencesToCreate }; } diff --git a/packages/lib/piiFreeData.ts b/packages/lib/piiFreeData.ts index dfa346f63f6f91..01aa8088865cf6 100644 --- a/packages/lib/piiFreeData.ts +++ b/packages/lib/piiFreeData.ts @@ -89,7 +89,9 @@ export function getPiiFreeDestinationCalendar(destinationCalendar: Partial>) { +export function getPiiFreeEventType( + eventType: Partial> +) { return { id: eventType.id, schedulingType: eventType.schedulingType, diff --git a/packages/lib/server/service/eventType.ts b/packages/lib/server/service/eventType.ts index 28f472e21eb9dd..2091cd4f9acf05 100644 --- a/packages/lib/server/service/eventType.ts +++ b/packages/lib/server/service/eventType.ts @@ -2,7 +2,7 @@ import type { Prisma } from "@prisma/client"; import type { appDataSchemas } from "@calcom/app-store/apps.schemas.generated"; import { prisma } from "@calcom/prisma"; -import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchema, EventTypeAppMetadataSchema } from "@calcom/prisma/zod-utils"; export class EventTypeService { static async getEventTypeAppDataFromId(eventTypeId: number, appSlug: keyof typeof appDataSchemas) { @@ -26,8 +26,8 @@ export class EventTypeService { if (!parseEventTypeAppMetadata.success || !parseEventTypeAppMetadata.data?.apps) return null; const eventTypeAppMetadata = parseEventTypeAppMetadata.data.apps; - - const appMetadata = eventTypeAppMetadata[appSlug]; + const apps = EventTypeAppMetadataSchema.parse(eventTypeAppMetadata); + const appMetadata = apps[appSlug]; return appMetadata; } diff --git a/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts b/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts index 8b5b354a10f8ac..8aa7d04755bccb 100644 --- a/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts +++ b/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts @@ -20,7 +20,6 @@ import { validateIntervalLimitOrder } from "@calcom/lib"; import { locationsResolver } from "@calcom/lib/event-types/utils/locationsResolver"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { validateBookerLayouts } from "@calcom/lib/validateBookerLayouts"; -import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import { eventTypeBookingFields as eventTypeBookingFieldsSchema } from "@calcom/prisma/zod-utils"; type Fields = z.infer; @@ -325,8 +324,7 @@ export const useEventTypeForm = ({ // Prevent two payment apps to be enabled // Ok to cast type here because this metadata will be updated as the event type metadata - if (checkForMultiplePaymentApps(metadata as z.infer)) - throw new Error(t("event_setup_multiple_payment_apps_error")); + if (checkForMultiplePaymentApps(metadata)) throw new Error(t("event_setup_multiple_payment_apps_error")); if (metadata?.apps?.stripe?.paymentOption === "HOLD" && seatsPerTimeSlot) { throw new Error(t("seats_and_no_show_fee_error")); diff --git a/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx b/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx index c70406965ea94c..615504765f766c 100644 --- a/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx +++ b/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx @@ -15,6 +15,7 @@ import type { } from "@calcom/features/eventtypes/lib/types"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { VerticalTabItemProps } from "@calcom/ui"; type Props = { @@ -55,7 +56,10 @@ export const useTabsNavigations = ({ eventTypeApps?.items.find((app) => app.slug === appId)?.isInstalled && appData.enabled ).length; } - const paymentAppData = getPaymentAppData(eventType); + const paymentAppData = getPaymentAppData({ + ...eventType, + metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }); const requirePayment = paymentAppData.price > 0; diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index a861d18a078bbe..90bd938ac2ea65 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -71,50 +71,64 @@ export type BookerLayoutSettings = z.infer; export const RequiresConfirmationThresholdUnits: z.ZodType = z.enum(["hours", "minutes"]); export const EventTypeAppMetadataSchema = z.object(appDataSchemas).partial(); +export const EventTypeAppMetadataOptionalSchema = EventTypeAppMetadataSchema.optional(); + +const _EventTypeMetaDataSchemaWithoutApps = z.object({ + smartContractAddress: z.string().optional(), + blockchainId: z.number().optional(), + multipleDuration: z.number().array().optional(), + giphyThankYouPage: z.string().optional(), + additionalNotesRequired: z.boolean().optional(), + disableSuccessPage: z.boolean().optional(), + disableStandardEmails: z + .object({ + all: z + .object({ + host: z.boolean().optional(), + attendee: z.boolean().optional(), + }) + .optional(), + confirmation: z + .object({ + host: z.boolean().optional(), + attendee: z.boolean().optional(), + }) + .optional(), + }) + .optional(), + managedEventConfig: z + .object({ + unlockedFields: z.custom<{ [k in keyof Omit]: true }>().optional(), + }) + .optional(), + requiresConfirmationThreshold: z + .object({ + time: z.number(), + unit: RequiresConfirmationThresholdUnits, + }) + .optional(), + config: z + .object({ + useHostSchedulesForTeamEvent: z.boolean().optional(), + }) + .optional(), + bookerLayouts: bookerLayouts.optional(), +}); -export const EventTypeMetaDataSchema = z - .object({ - smartContractAddress: z.string().optional(), - blockchainId: z.number().optional(), - multipleDuration: z.number().array().optional(), - giphyThankYouPage: z.string().optional(), - apps: EventTypeAppMetadataSchema.optional(), - additionalNotesRequired: z.boolean().optional(), - disableSuccessPage: z.boolean().optional(), - disableStandardEmails: z - .object({ - all: z - .object({ - host: z.boolean().optional(), - attendee: z.boolean().optional(), - }) - .optional(), - confirmation: z - .object({ - host: z.boolean().optional(), - attendee: z.boolean().optional(), - }) - .optional(), - }) - .optional(), - managedEventConfig: z - .object({ - unlockedFields: z.custom<{ [k in keyof Omit]: true }>().optional(), - }) - .optional(), - requiresConfirmationThreshold: z - .object({ - time: z.number(), - unit: RequiresConfirmationThresholdUnits, - }) - .optional(), - config: z - .object({ - useHostSchedulesForTeamEvent: z.boolean().optional(), - }) - .optional(), - bookerLayouts: bookerLayouts.optional(), +export const EventTypeMetaDataSchemaWithUntypedApps = _EventTypeMetaDataSchemaWithoutApps.merge( + z.object({ + apps: z.unknown().optional(), }) +); + +export const EventTypeMetaDataSchema = EventTypeMetaDataSchemaWithUntypedApps.nullable(); +export const EventTypeMetaDataSchemaWithoutApps = _EventTypeMetaDataSchemaWithoutApps.nullable(); +export const EventTypeMetaDataSchemaWithTypedApps = _EventTypeMetaDataSchemaWithoutApps + .merge( + z.object({ + apps: EventTypeAppMetadataOptionalSchema, + }) + ) .nullable(); export type EventTypeMetadata = z.infer; diff --git a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts index 21b1ff9284b86b..c136ec33dcb629 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts @@ -27,6 +27,7 @@ import { handleCustomInputs, handlePeriodType, } from "./util"; +import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; type SessionUser = NonNullable; type User = { @@ -382,8 +383,9 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { } } - for (const appKey in input.metadata?.apps) { - const app = input.metadata?.apps[appKey as keyof typeof appDataSchemas]; + const apps = EventTypeAppMetadataOptionalSchema.parse(input.metadata?.apps); + for (const appKey in apps) { + const app = apps[appKey as keyof typeof appDataSchemas]; // There should only be one enabled payment app in the metadata if (app.enabled && app.price && app.currency) { data.price = app.price; diff --git a/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts b/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts index c55d13f3d32aa8..95ffa0a4b61218 100644 --- a/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts +++ b/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts @@ -1,4 +1,5 @@ import { isTeamMember } from "@calcom/lib/server/queries"; +import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { TRPCError } from "@trpc/server"; @@ -40,7 +41,13 @@ export const getAllActiveWorkflowsHandler = async ({ input, ctx }: GetAllActiveW if (!team) throw new TRPCError({ code: "UNAUTHORIZED" }); } - const allActiveWorkflows = await getAllWorkflowsFromEventType(completeEventType, eventType.userId); + const allActiveWorkflows = await getAllWorkflowsFromEventType( + { + ...completeEventType, + metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }, + eventType.userId + ); return allActiveWorkflows; }; From cd127e8771960445c629b9c72cbe458da7b7907e Mon Sep 17 00:00:00 2001 From: Hariom Date: Mon, 23 Dec 2024 14:36:01 +0530 Subject: [PATCH 4/8] Variable casing change and type fixes --- .../apps/installation/[[...step]]/step-view.tsx | 6 +++--- .../bookings/views/bookings-single-view.tsx | 14 +++++++++++--- apps/web/playwright/integrations-stripe.e2e.ts | 10 +++++----- packages/app-store/_utils/getEventTypeAppData.ts | 4 ++-- .../_utils/payments/checkForMultiplePaymentApps.ts | 4 ++-- .../getAllCredentials.ts | 4 ++-- .../features/bookings/lib/handleConfirmation.ts | 4 ++-- packages/features/bookings/lib/handleNewBooking.ts | 10 +++++----- .../lib/handleSeats/create/createNewSeat.ts | 4 ++-- .../features/credentials/handleDeleteCredential.ts | 12 ++++++------ packages/features/ee/payments/api/webhook.ts | 4 +++- .../eventtypes/components/EventTypeDescription.tsx | 4 ++-- .../components/tabs/instant/EventInstantTab.tsx | 4 ++-- .../tabs/recurring/EventRecurringTab.tsx | 4 ++-- packages/features/eventtypes/lib/getPublicEvent.ts | 6 +++--- packages/features/eventtypes/lib/types.ts | 4 ++-- .../handleSendingAttendeeNoShowDataToApps.ts | 4 ++-- packages/lib/defaultEvents.ts | 4 ++-- packages/lib/event-types/getEventTypeById.ts | 4 ++-- packages/lib/event-types/getEventTypesByViewer.ts | 4 ++-- packages/lib/getPaymentAppData.ts | 13 ++++++++++--- packages/lib/payment/handlePayment.ts | 4 ++-- packages/lib/payment/handlePaymentSuccess.ts | 4 ++-- .../atoms/event-types/hooks/useTabsNavigations.tsx | 4 ++-- packages/prisma/zod-utils.ts | 14 +++++++------- .../routers/viewer/eventTypes/update.handler.ts | 4 ++-- .../workflows/getAllActiveWorkflows.handler.ts | 4 ++-- 27 files changed, 89 insertions(+), 72 deletions(-) diff --git a/apps/web/modules/apps/installation/[[...step]]/step-view.tsx b/apps/web/modules/apps/installation/[[...step]]/step-view.tsx index 6691621cbc7243..26014f669cef95 100644 --- a/apps/web/modules/apps/installation/[[...step]]/step-view.tsx +++ b/apps/web/modules/apps/installation/[[...step]]/step-view.tsx @@ -18,6 +18,7 @@ import { WEBAPP_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { Team } from "@calcom/prisma/client"; import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import { trpc } from "@calcom/trpc/react"; import type { AppMeta } from "@calcom/types/App"; @@ -243,11 +244,10 @@ const OnboardingPage = ({ const promises = group.eventTypes .filter((eventType) => eventType.selected) .map((value: TEventType) => { + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(value.metadata); // Prevent two payment apps to be enabled // Ok to cast type here because this metadata will be updated as the event type metadata - if ( - checkForMultiplePaymentApps(value.metadata as z.infer) - ) + if (checkForMultiplePaymentApps(metadata)) throw new Error(t("event_setup_multiple_payment_apps_error")); if (value.metadata?.apps?.stripe?.paymentOption === "HOLD" && value.seatsPerTimeSlot) { throw new Error(t("seats_and_no_show_fee_error")); diff --git a/apps/web/modules/bookings/views/bookings-single-view.tsx b/apps/web/modules/bookings/views/bookings-single-view.tsx index e541afd761890b..222152d9415fc5 100644 --- a/apps/web/modules/bookings/views/bookings-single-view.tsx +++ b/apps/web/modules/bookings/views/bookings-single-view.tsx @@ -48,7 +48,7 @@ import { getEveryFreqFor } from "@calcom/lib/recurringStrings"; import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/timeFormat"; import { localStorage } from "@calcom/lib/webstorage"; import { BookingStatus, SchedulingType } from "@calcom/prisma/enums"; -import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; +import { bookingMetadataSchema, eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { trpc } from "@calcom/trpc/react"; import { Alert, @@ -230,7 +230,13 @@ export default function Success(props: PageProps) { t, }; - const giphyAppData = getEventTypeAppData(eventType, "giphy"); + const giphyAppData = getEventTypeAppData( + { + ...eventType, + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + }, + "giphy" + ); const giphyImage = giphyAppData?.thankYouPage; const isRoundRobin = eventType.schedulingType === SchedulingType.ROUND_ROBIN; @@ -435,7 +441,9 @@ export default function Success(props: PageProps) { )} - +
{ }, }); - const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata); + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata); const stripeAppMetadata = metadata?.apps?.stripe; @@ -93,7 +93,7 @@ test.describe("Stripe integration skip true", () => { }, }); - const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata); + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata); const stripeAppMetadata = metadata?.apps?.stripe; @@ -291,7 +291,7 @@ test.describe("Stripe integration with the new app install flow skip false", () }); for (const eventTypeMetadata of eventTypeMetadatas) { - const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata); + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata); const stripeAppMetadata = metadata?.apps?.stripe; expect(stripeAppMetadata).toHaveProperty("credentialId"); expect(typeof stripeAppMetadata?.credentialId).toBe("number"); @@ -328,7 +328,7 @@ test.describe("Stripe integration with the new app install flow skip false", () }, }); - const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata); + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata); const stripeAppMetadata = metadata?.apps?.stripe; diff --git a/packages/app-store/_utils/getEventTypeAppData.ts b/packages/app-store/_utils/getEventTypeAppData.ts index 3b7aa78ecd497b..299bcdf5bbfcd5 100644 --- a/packages/app-store/_utils/getEventTypeAppData.ts +++ b/packages/app-store/_utils/getEventTypeAppData.ts @@ -1,10 +1,10 @@ import type { z } from "zod"; import type { BookerEvent } from "@calcom/features/bookings/types"; -import type { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import type { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; export type EventTypeApps = NonNullable< - NonNullable>["apps"] + NonNullable>["apps"] >; export type EventTypeAppsList = keyof EventTypeApps; diff --git a/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts b/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts index 49dcccde58ded2..aed2b8385b3a9c 100644 --- a/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts +++ b/packages/app-store/_utils/payments/checkForMultiplePaymentApps.ts @@ -1,6 +1,6 @@ import type z from "zod"; -import type { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import type { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import type { appDataSchemas } from "../../apps.schemas.generated"; @@ -13,7 +13,7 @@ import type { appDataSchemas } from "../../apps.schemas.generated"; const checkForMultiplePaymentApps = ( metadata: | { - apps?: z.infer; + apps?: z.infer; } | null | undefined, diff --git a/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts b/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts index 9578818c0ce0fc..7e65d55193ad18 100644 --- a/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts +++ b/packages/features/bookings/lib/getAllCredentialsForUsersOnEvent/getAllCredentials.ts @@ -3,7 +3,7 @@ import type z from "zod"; import { UserRepository } from "@calcom/lib/server/repository/user"; import prisma from "@calcom/prisma"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; -import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { CredentialPayload } from "@calcom/types/Credential"; @@ -77,7 +77,7 @@ export const getAllCredentials = async ( } // Only return CRM credentials that are enabled on the event type - const eventTypeAppMetadata = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); + const eventTypeAppMetadata = eventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); // Will be [credentialId]: { enabled: boolean }] const eventTypeCrmCredentials: Record = {}; diff --git a/packages/features/bookings/lib/handleConfirmation.ts b/packages/features/bookings/lib/handleConfirmation.ts index cb11060bfa7707..2254bba6edf67e 100644 --- a/packages/features/bookings/lib/handleConfirmation.ts +++ b/packages/features/bookings/lib/handleConfirmation.ts @@ -24,7 +24,7 @@ import type { PrismaClient } from "@calcom/prisma"; import type { SchedulingType } from "@calcom/prisma/enums"; import { BookingStatus, WebhookTriggerEvents } from "@calcom/prisma/enums"; import type { PlatformClientParams } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchema, EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchema, eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util"; import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar"; @@ -84,7 +84,7 @@ export async function handleConfirmation(args: { } = args; const eventType = booking.eventType; const eventTypeMetadata = EventTypeMetaDataSchema.parse(eventType?.metadata || {}); - const apps = EventTypeAppMetadataOptionalSchema.parse(eventTypeMetadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(eventTypeMetadata?.apps); const eventManager = new EventManager(user, apps); const scheduleResult = await eventManager.create(evt); const results = scheduleResult.results; diff --git a/packages/features/bookings/lib/handleNewBooking.ts b/packages/features/bookings/lib/handleNewBooking.ts index 0abd690fdfce38..e378ad231b9541 100644 --- a/packages/features/bookings/lib/handleNewBooking.ts +++ b/packages/features/bookings/lib/handleNewBooking.ts @@ -69,8 +69,8 @@ import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; import prisma from "@calcom/prisma"; import { BookingStatus, SchedulingType, WebhookTriggerEvents } from "@calcom/prisma/enums"; import { - EventTypeAppMetadataOptionalSchema, - EventTypeMetaDataSchemaWithTypedApps, + eventTypeAppMetadataOptionalSchema, + eventTypeMetaDataSchemaWithTypedApps, } from "@calcom/prisma/zod-utils"; import type { PlatformClientParams } from "@calcom/prisma/zod-utils"; import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils"; @@ -427,7 +427,7 @@ async function handler( const paymentAppData = getPaymentAppData({ ...eventType, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), }); loggerWithEventDetails.info( `Booking eventType ${eventTypeId} started`, @@ -1061,7 +1061,7 @@ async function handler( const workflows = await getAllWorkflowsFromEventType( { ...eventType, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), }, organizerUser.id ); @@ -1288,7 +1288,7 @@ async function handler( // After polling videoBusyTimes, credentials might have been changed due to refreshment, so query them again. const credentials = await monitorCallbackAsync(refreshCredentials, allCredentials); - const apps = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); const eventManager = !isDryRun ? new EventManager({ ...organizerUser, credentials }, apps) : buildDryRunEventManager(); diff --git a/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts b/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts index 303080653f1a94..2de6dd853ce417 100644 --- a/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts +++ b/packages/features/bookings/lib/handleSeats/create/createNewSeat.ts @@ -14,7 +14,7 @@ import { HttpError } from "@calcom/lib/http-error"; import { handlePayment } from "@calcom/lib/payment/handlePayment"; import prisma from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/enums"; -import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import { findBookingQuery } from "../../handleNewBooking/findBookingQuery"; import type { IEventTypePaymentCredentialType } from "../../handleNewBooking/types"; @@ -148,7 +148,7 @@ const createNewSeat = async ( ); } const credentials = await refreshCredentials(allCredentials); - const apps = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); const eventManager = new EventManager({ ...organizerUser, credentials }, apps); await eventManager.updateCalendarAttendees(evt, seatedBooking); diff --git a/packages/features/credentials/handleDeleteCredential.ts b/packages/features/credentials/handleDeleteCredential.ts index 49ed51a15abacc..97bf17763f0847 100644 --- a/packages/features/credentials/handleDeleteCredential.ts +++ b/packages/features/credentials/handleDeleteCredential.ts @@ -14,8 +14,8 @@ import { bookingMinimalSelect, prisma } from "@calcom/prisma"; import { AppCategories, BookingStatus } from "@calcom/prisma/enums"; import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential"; import type { EventTypeAppMetadataSchema, EventTypeMetadata } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchema, EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { EventTypeMetaDataSchema, eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils"; type App = { @@ -152,7 +152,7 @@ const handleDeleteCredential = async ({ if (credential.app?.categories.includes(AppCategories.crm)) { const metadata = EventTypeMetaDataSchema.parse(eventType.metadata); const appSlugToDelete = credential.app?.slug; - const apps = EventTypeAppMetadataOptionalSchema.parse(metadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(metadata?.apps); if (appSlugToDelete) { const appMetadata = removeAppFromEventTypeMetadata(appSlugToDelete, { apps, @@ -182,7 +182,7 @@ const handleDeleteCredential = async ({ const metadata = EventTypeMetaDataSchema.parse(eventType.metadata); const appSlug = credential.app?.slug; if (appSlug) { - const apps = EventTypeAppMetadataOptionalSchema.parse(metadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(metadata?.apps); const appMetadata = removeAppFromEventTypeMetadata(appSlug, { apps, }); @@ -364,7 +364,7 @@ const handleDeleteCredential = async ({ } else if ( appStoreMetadata[credential.app?.slug as keyof typeof appStoreMetadata]?.extendsFeature === "EventType" ) { - const metadata = EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata); + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata); const appSlug = credential.app?.slug; if (appSlug) { await prisma.eventType.update({ @@ -463,7 +463,7 @@ const handleDeleteCredential = async ({ const removeAppFromEventTypeMetadata = ( appSlugToDelete: string, eventTypeMetadata: { - apps: z.infer; + apps: z.infer; } ) => { const appMetadata = eventTypeMetadata?.apps diff --git a/packages/features/ee/payments/api/webhook.ts b/packages/features/ee/payments/api/webhook.ts index 2474b010dd64f4..02910c013e1942 100644 --- a/packages/features/ee/payments/api/webhook.ts +++ b/packages/features/ee/payments/api/webhook.ts @@ -17,6 +17,7 @@ import { handlePaymentSuccess } from "@calcom/lib/payment/handlePaymentSuccess"; import { safeStringify } from "@calcom/lib/safeStringify"; import { prisma } from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/enums"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; const log = logger.getSubLogger({ prefix: ["[paymentWebhook]"] }); @@ -72,7 +73,8 @@ const handleSetupSuccess = async (event: Stripe.Event) => { }, }); if (!requiresConfirmation) { - const eventManager = new EventManager(user, eventType?.metadata?.apps); + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventType?.metadata); + const eventManager = new EventManager(user, metadata?.apps); const scheduleResult = await eventManager.create(evt); bookingData.references = { create: scheduleResult.referencesToCreate }; bookingData.status = BookingStatus.ACCEPTED; diff --git a/packages/features/eventtypes/components/EventTypeDescription.tsx b/packages/features/eventtypes/components/EventTypeDescription.tsx index bc0d7a8280884f..bae4378a2ab63d 100644 --- a/packages/features/eventtypes/components/EventTypeDescription.tsx +++ b/packages/features/eventtypes/components/EventTypeDescription.tsx @@ -11,7 +11,7 @@ import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import type { baseEventTypeSelect } from "@calcom/prisma"; import { SchedulingType } from "@calcom/prisma/enums"; import type { EventTypeModel } from "@calcom/prisma/zod"; -import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { Badge } from "@calcom/ui"; export type EventTypeDescriptionProps = { @@ -42,7 +42,7 @@ export const EventTypeDescription = ({ const paymentAppData = getPaymentAppData({ ...eventType, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), }); return ( diff --git a/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx b/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx index f72d17ce28d892..b4b69d6ea246f6 100644 --- a/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx +++ b/packages/features/eventtypes/components/tabs/instant/EventInstantTab.tsx @@ -1,6 +1,6 @@ import type { EventTypeSetupProps } from "@calcom/features/eventtypes/lib/types"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; -import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import InstantEventController from "./InstantEventController"; @@ -10,7 +10,7 @@ export const EventInstantTab = ({ }: Pick & { isTeamEvent: boolean }) => { const paymentAppData = getPaymentAppData({ ...eventType, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), }); const requirePayment = paymentAppData.price > 0; diff --git a/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx b/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx index 3afdc755459530..8349487b6e47b6 100644 --- a/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx +++ b/packages/features/eventtypes/components/tabs/recurring/EventRecurringTab.tsx @@ -1,5 +1,5 @@ import getPaymentAppData from "@calcom/lib/getPaymentAppData"; -import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { RecurringEventControllerProps } from "./RecurringEventController"; import RecurringEventController from "./RecurringEventController"; @@ -9,7 +9,7 @@ export type EventRecurringTabProps = Omit { const paymentAppData = getPaymentAppData({ ...eventType, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), }); const requirePayment = paymentAppData.price > 0; diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index fb19d2b2c05eb9..d3fa3c5f9278f5 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -20,7 +20,7 @@ import type { Team } from "@calcom/prisma/client"; import type { BookerLayoutSettings } from "@calcom/prisma/zod-utils"; import { BookerLayouts, - EventTypeMetaDataSchemaWithTypedApps, + eventTypeMetaDataSchemaWithTypedApps, bookerLayoutOptions, bookerLayouts as bookerLayoutsSchema, customInputSchema, @@ -368,7 +368,7 @@ export const getPublicEvent = async ( if (!event) return null; - const eventMetaData = EventTypeMetaDataSchemaWithTypedApps.parse(event.metadata || {}); + const eventMetaData = eventTypeMetaDataSchemaWithTypedApps.parse(event.metadata || {}); const teamMetadata = teamMetadataSchema.parse(event.team?.metadata || {}); const usersAsHosts = event.hosts.map((host) => host.user); @@ -517,7 +517,7 @@ function getProfileFromEvent(event: Event) { const username = "username" in profile ? profile.username : team?.slug; const weekStart = hosts?.[0]?.user?.weekStart || owner?.weekStart || "Monday"; - const eventMetaData = EventTypeMetaDataSchemaWithTypedApps.parse(event.metadata || {}); + const eventMetaData = eventTypeMetaDataSchemaWithTypedApps.parse(event.metadata || {}); const userMetaData = userMetadataSchema.parse(profile.metadata || {}); return { diff --git a/packages/features/eventtypes/lib/types.ts b/packages/features/eventtypes/lib/types.ts index d9ce7722f22021..2490eafd603e36 100644 --- a/packages/features/eventtypes/lib/types.ts +++ b/packages/features/eventtypes/lib/types.ts @@ -5,7 +5,7 @@ import type { ChildrenEventType } from "@calcom/features/eventtypes/components/C import type { AttributesQueryValue } from "@calcom/lib/raqb/types"; import type { EventTypeTranslation } from "@calcom/prisma/client"; import type { PeriodType, SchedulingType } from "@calcom/prisma/enums"; -import type { BookerLayoutSettings, EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import type { BookerLayoutSettings, eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { customInputSchema } from "@calcom/prisma/zod-utils"; import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils"; import type { eventTypeColor } from "@calcom/prisma/zod-utils"; @@ -122,7 +122,7 @@ export type FormValues = { beforeEventBuffer: number; afterEventBuffer: number; slotInterval: number | null; - metadata: z.infer; + metadata: z.infer; destinationCalendar: { integration: string; externalId: string; diff --git a/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts b/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts index 9c542858cae426..b30faebb3319aa 100644 --- a/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts +++ b/packages/features/noShow/handleSendingAttendeeNoShowDataToApps.ts @@ -4,7 +4,7 @@ import type { eventTypeAppCardZod } from "@calcom/app-store/eventTypeAppCardZod" import CrmManager from "@calcom/core/crmManager/crmManager"; import logger from "@calcom/lib/logger"; import prisma from "@calcom/prisma"; -import { EventTypeAppMetadataOptionalSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeAppMetadataOptionalSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { NoShowAttendees } from "../handleMarkNoShow"; import { noShowEnabledApps } from "./noShowEnabledApps"; @@ -38,7 +38,7 @@ export default async function handleSendingAttendeeNoShowDataToApps( log.error(`Malformed event type metadata for bookingUid ${bookingUid}`); return; } - const eventTypeAppMetadata = EventTypeAppMetadataOptionalSchema.parse(eventTypeMetadataParse.data?.apps); + const eventTypeAppMetadata = eventTypeAppMetadataOptionalSchema.parse(eventTypeMetadataParse.data?.apps); for (const slug in eventTypeAppMetadata) { if (noShowEnabledApps.includes(slug)) { diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts index e1761d86992be6..81f7422252aa73 100644 --- a/packages/lib/defaultEvents.ts +++ b/packages/lib/defaultEvents.ts @@ -5,7 +5,7 @@ import slugify from "@calcom/lib/slugify"; import { PeriodType, SchedulingType } from "@calcom/prisma/enums"; import type { userSelect } from "@calcom/prisma/selects"; import type { CustomInputSchema } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; import type { CredentialPayload } from "@calcom/types/Credential"; @@ -133,7 +133,7 @@ export const dynamicEvent = { descriptionAsSafeHTML: "", position: 0, ...commons, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse({ multipleDuration: [15, 30, 45, 60, 90] }), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse({ multipleDuration: [15, 30, 45, 60, 90] }), }; export const defaultEvents = [dynamicEvent]; diff --git a/packages/lib/event-types/getEventTypeById.ts b/packages/lib/event-types/getEventTypeById.ts index 864da19e909e7a..00f239653efad1 100644 --- a/packages/lib/event-types/getEventTypeById.ts +++ b/packages/lib/event-types/getEventTypeById.ts @@ -11,7 +11,7 @@ import { EventTypeRepository } from "@calcom/lib/server/repository/eventType"; import { UserRepository } from "@calcom/lib/server/repository/user"; import type { PrismaClient } from "@calcom/prisma"; import { SchedulingType, MembershipRole } from "@calcom/prisma/enums"; -import { customInputSchema, EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { customInputSchema, eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -58,7 +58,7 @@ export const getEventTypeById = async ({ } const { locations, metadata, ...restEventType } = rawEventType; - const newMetadata = EventTypeMetaDataSchemaWithTypedApps.parse(metadata || {}) || {}; + const newMetadata = eventTypeMetaDataSchemaWithTypedApps.parse(metadata || {}) || {}; const apps = newMetadata?.apps || {}; const eventTypeWithParsedMetadata = { ...rawEventType, metadata: newMetadata }; const eventTeamMembershipsWithUserProfile = []; diff --git a/packages/lib/event-types/getEventTypesByViewer.ts b/packages/lib/event-types/getEventTypesByViewer.ts index 1e3265b2b6b9fe..003964e8111051 100644 --- a/packages/lib/event-types/getEventTypesByViewer.ts +++ b/packages/lib/event-types/getEventTypesByViewer.ts @@ -15,7 +15,7 @@ import { ProfileRepository } from "@calcom/lib/server/repository/profile"; import { UserRepository } from "@calcom/lib/server/repository/user"; import { MembershipRole, SchedulingType } from "@calcom/prisma/enums"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; -import { EventTypeMetaDataSchemaWithoutApps } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithoutApps } from "@calcom/prisma/zod-utils"; import { TRPCError } from "@trpc/server"; @@ -116,7 +116,7 @@ export const getEventTypesByViewer = async (user: User, filters?: Filters, forRo }) ) ), - metadata: eventType.metadata ? EventTypeMetaDataSchemaWithoutApps.parse(eventType.metadata) : null, + metadata: eventType.metadata ? eventTypeMetaDataSchemaWithoutApps.parse(eventType.metadata) : null, children: await Promise.all( (eventType.children || []).map(async (c) => ({ ...c, diff --git a/packages/lib/getPaymentAppData.ts b/packages/lib/getPaymentAppData.ts index 0c7de5402e7bf7..6c9ef6f473f7b9 100644 --- a/packages/lib/getPaymentAppData.ts +++ b/packages/lib/getPaymentAppData.ts @@ -5,16 +5,23 @@ import type { appDataSchemas } from "@calcom/app-store/apps.schemas.generated"; import type { appDataSchema, paymentOptionEnum } from "@calcom/app-store/stripepayment/zod"; import type { EventTypeAppsList } from "@calcom/app-store/utils"; import type { BookerEvent } from "@calcom/features/bookings/types"; +import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; export default function getPaymentAppData( - eventType: Pick, + _eventType: Pick & { + metadata: z.infer; + }, forcedGet?: boolean ) { - const metadataApps = eventType?.metadata?.apps as unknown as EventTypeAppsList; + const eventType = { + ..._eventType, + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(_eventType.metadata), + }; + const metadataApps = eventType.metadata?.apps; if (!metadataApps) { return { enabled: false, price: 0, currency: "usd", appId: null }; } - type appId = keyof typeof metadataApps; + type appId = keyof typeof eventType.metadata.apps; // @TODO: a lot of unknowns types here can be improved later const paymentAppIds = (Object.keys(metadataApps) as Array).filter( (app) => diff --git a/packages/lib/payment/handlePayment.ts b/packages/lib/payment/handlePayment.ts index ce4ad4a30ce2e8..ae4b8b27726575 100644 --- a/packages/lib/payment/handlePayment.ts +++ b/packages/lib/payment/handlePayment.ts @@ -3,7 +3,7 @@ import type { AppCategories, Prisma } from "@prisma/client"; import appStore from "@calcom/app-store"; import type { EventTypeAppsList } from "@calcom/app-store/utils"; import type { CompleteEventType } from "@calcom/prisma/zod"; -import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import type { CalendarEvent } from "@calcom/types/Calendar"; import type { IAbstractPaymentService, PaymentApp } from "@calcom/types/PaymentService"; @@ -41,7 +41,7 @@ const handlePayment = async ( const paymentInstance = new PaymentService(paymentAppCredentials) as IAbstractPaymentService; - const apps = EventTypeAppMetadataOptionalSchema.parse(selectedEventType?.metadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(selectedEventType?.metadata?.apps); const paymentOption = apps?.[paymentAppCredentials.appId].paymentOption || "ON_BOOKING"; let paymentData; diff --git a/packages/lib/payment/handlePaymentSuccess.ts b/packages/lib/payment/handlePaymentSuccess.ts index 7bcf29b9c04b4c..2defd49f5cf118 100644 --- a/packages/lib/payment/handlePaymentSuccess.ts +++ b/packages/lib/payment/handlePaymentSuccess.ts @@ -9,7 +9,7 @@ import { HttpError as HttpCode } from "@calcom/lib/http-error"; import { getBooking } from "@calcom/lib/payment/getBooking"; import prisma from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/enums"; -import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; import logger from "../logger"; @@ -27,7 +27,7 @@ export async function handlePaymentSuccess(paymentId: number, bookingId: number) const isConfirmed = booking.status === BookingStatus.ACCEPTED; if (isConfirmed) { - const apps = EventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps); const eventManager = new EventManager(userWithCredentials, apps); const scheduleResult = await eventManager.create(evt); bookingData.references = { create: scheduleResult.referencesToCreate }; diff --git a/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx b/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx index 615504765f766c..a5303b21cff48e 100644 --- a/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx +++ b/packages/platform/atoms/event-types/hooks/useTabsNavigations.tsx @@ -15,7 +15,7 @@ import type { } from "@calcom/features/eventtypes/lib/types"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { VerticalTabItemProps } from "@calcom/ui"; type Props = { @@ -58,7 +58,7 @@ export const useTabsNavigations = ({ } const paymentAppData = getPaymentAppData({ ...eventType, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), }); const requirePayment = paymentAppData.price > 0; diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 90bd938ac2ea65..b5baa1948a3844 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -71,9 +71,9 @@ export type BookerLayoutSettings = z.infer; export const RequiresConfirmationThresholdUnits: z.ZodType = z.enum(["hours", "minutes"]); export const EventTypeAppMetadataSchema = z.object(appDataSchemas).partial(); -export const EventTypeAppMetadataOptionalSchema = EventTypeAppMetadataSchema.optional(); +export const eventTypeAppMetadataOptionalSchema = EventTypeAppMetadataSchema.optional(); -const _EventTypeMetaDataSchemaWithoutApps = z.object({ +const _eventTypeMetaDataSchemaWithoutApps = z.object({ smartContractAddress: z.string().optional(), blockchainId: z.number().optional(), multipleDuration: z.number().array().optional(), @@ -115,18 +115,18 @@ const _EventTypeMetaDataSchemaWithoutApps = z.object({ bookerLayouts: bookerLayouts.optional(), }); -export const EventTypeMetaDataSchemaWithUntypedApps = _EventTypeMetaDataSchemaWithoutApps.merge( +export const eventTypeMetaDataSchemaWithUntypedApps = _eventTypeMetaDataSchemaWithoutApps.merge( z.object({ apps: z.unknown().optional(), }) ); -export const EventTypeMetaDataSchema = EventTypeMetaDataSchemaWithUntypedApps.nullable(); -export const EventTypeMetaDataSchemaWithoutApps = _EventTypeMetaDataSchemaWithoutApps.nullable(); -export const EventTypeMetaDataSchemaWithTypedApps = _EventTypeMetaDataSchemaWithoutApps +export const EventTypeMetaDataSchema = eventTypeMetaDataSchemaWithUntypedApps.nullable(); +export const eventTypeMetaDataSchemaWithoutApps = _eventTypeMetaDataSchemaWithoutApps.nullable(); +export const eventTypeMetaDataSchemaWithTypedApps = _eventTypeMetaDataSchemaWithoutApps .merge( z.object({ - apps: EventTypeAppMetadataOptionalSchema, + apps: eventTypeAppMetadataOptionalSchema, }) ) .nullable(); diff --git a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts index c136ec33dcb629..ab978caac14faf 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts @@ -27,7 +27,7 @@ import { handleCustomInputs, handlePeriodType, } from "./util"; -import { EventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeAppMetadataOptionalSchema } from "@calcom/prisma/zod-utils"; type SessionUser = NonNullable; type User = { @@ -383,7 +383,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => { } } - const apps = EventTypeAppMetadataOptionalSchema.parse(input.metadata?.apps); + const apps = eventTypeAppMetadataOptionalSchema.parse(input.metadata?.apps); for (const appKey in apps) { const app = apps[appKey as keyof typeof appDataSchemas]; // There should only be one enabled payment app in the metadata diff --git a/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts b/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts index 95ffa0a4b61218..69edacb5408a05 100644 --- a/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts +++ b/packages/trpc/server/routers/viewer/workflows/getAllActiveWorkflows.handler.ts @@ -1,5 +1,5 @@ import { isTeamMember } from "@calcom/lib/server/queries"; -import { EventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { TRPCError } from "@trpc/server"; @@ -44,7 +44,7 @@ export const getAllActiveWorkflowsHandler = async ({ input, ctx }: GetAllActiveW const allActiveWorkflows = await getAllWorkflowsFromEventType( { ...completeEventType, - metadata: EventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), + metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata), }, eventType.userId ); From 564cec05bd88298574f6c43a1a85652fd1ab4872 Mon Sep 17 00:00:00 2001 From: Hariom Date: Mon, 23 Dec 2024 14:38:38 +0530 Subject: [PATCH 5/8] Another fix --- packages/lib/getPaymentAppData.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/lib/getPaymentAppData.ts b/packages/lib/getPaymentAppData.ts index 6c9ef6f473f7b9..d9ce8790709251 100644 --- a/packages/lib/getPaymentAppData.ts +++ b/packages/lib/getPaymentAppData.ts @@ -6,6 +6,7 @@ import type { appDataSchema, paymentOptionEnum } from "@calcom/app-store/stripep import type { EventTypeAppsList } from "@calcom/app-store/utils"; import type { BookerEvent } from "@calcom/features/bookings/types"; import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; +import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils"; export default function getPaymentAppData( _eventType: Pick & { From 4819bad937ecc834cfcf5609ea9a981059977732 Mon Sep 17 00:00:00 2001 From: Hariom Date: Mon, 23 Dec 2024 16:43:34 +0530 Subject: [PATCH 6/8] Another missing ts fix --- apps/web/modules/test-setup.ts | 3 +++ packages/lib/getPaymentAppData.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/web/modules/test-setup.ts b/apps/web/modules/test-setup.ts index df683202a6ea33..32524c982e8a82 100644 --- a/apps/web/modules/test-setup.ts +++ b/apps/web/modules/test-setup.ts @@ -128,6 +128,9 @@ vi.mock("@calcom/prisma/zod-utils", () => ({ EventTypeMetaDataSchema: { parse: vi.fn(), }, + eventTypeMetaDataSchemaWithTypedApps: { + parse: vi.fn(), + }, bookingMetadataSchema: { parse: vi.fn(), }, diff --git a/packages/lib/getPaymentAppData.ts b/packages/lib/getPaymentAppData.ts index d9ce8790709251..2b0fe867a6308c 100644 --- a/packages/lib/getPaymentAppData.ts +++ b/packages/lib/getPaymentAppData.ts @@ -22,7 +22,7 @@ export default function getPaymentAppData( if (!metadataApps) { return { enabled: false, price: 0, currency: "usd", appId: null }; } - type appId = keyof typeof eventType.metadata.apps; + type appId = keyof typeof metadataApps; // @TODO: a lot of unknowns types here can be improved later const paymentAppIds = (Object.keys(metadataApps) as Array).filter( (app) => From ead0cecb44ade23ee809d64d04c0fc34b1d8018c Mon Sep 17 00:00:00 2001 From: Udit Takkar Date: Mon, 23 Dec 2024 19:41:45 +0530 Subject: [PATCH 7/8] chore: update variable name --- apps/web/app/d/[link]/[slug]/page.tsx | 2 +- apps/web/app/future/[user]/[type]/page.tsx | 2 +- .../[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx | 2 +- .../app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx | 2 +- apps/web/app/future/team/[slug]/[type]/embed/page.tsx | 2 +- apps/web/app/future/team/[slug]/[type]/page.tsx | 2 +- .../Booker/components/BookEventForm/BookFormAsModal.tsx | 2 +- packages/features/bookings/Booker/utils/event.ts | 4 ++-- packages/features/embed/Embed.tsx | 2 +- packages/features/eventtypes/lib/getPublicEvent.ts | 8 ++++---- packages/lib/server/repository/event.ts | 2 +- packages/platform/atoms/booker/BookerWebWrapper.tsx | 2 +- packages/trpc/server/routers/publicViewer/event.schema.ts | 2 +- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/web/app/d/[link]/[slug]/page.tsx b/apps/web/app/d/[link]/[slug]/page.tsx index c9e725ed3b3ae8..50984b836a0f02 100644 --- a/apps/web/app/d/[link]/[slug]/page.tsx +++ b/apps/web/app/d/[link]/[slug]/page.tsx @@ -28,7 +28,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent, org, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", - includeOnlyOneHost: true, + limitHostsToThree: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/apps/web/app/future/[user]/[type]/page.tsx b/apps/web/app/future/[user]/[type]/page.tsx index 81e11ff88c2c08..0fce87470a134d 100644 --- a/apps/web/app/future/[user]/[type]/page.tsx +++ b/apps/web/app/future/[user]/[type]/page.tsx @@ -28,7 +28,7 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => { isTeamEvent: false, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", - includeOnlyOneHost: true, + limitHostsToThree: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx b/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx index 220026afb8b66f..5f27b9b94abe17 100644 --- a/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/instant-meeting/team/[slug]/[type]/page.tsx @@ -27,7 +27,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent: true, org, fromRedirectOfNonOrgLink: context.query.orgRedirection === "true", - includeOnlyOneHost: true, + limitHostsToThree: true, }); const profileName = event?.profile.name ?? ""; diff --git a/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx b/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx index 8090b11b4ae4b4..45a259e684b808 100644 --- a/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx +++ b/apps/web/app/future/org/[orgSlug]/team/[slug]/[type]/page.tsx @@ -25,7 +25,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent: true, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", - includeOnlyOneHost: true, + limitHostsToThree: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/apps/web/app/future/team/[slug]/[type]/embed/page.tsx b/apps/web/app/future/team/[slug]/[type]/embed/page.tsx index fe4f757da2fe11..d41adfde432625 100644 --- a/apps/web/app/future/team/[slug]/[type]/embed/page.tsx +++ b/apps/web/app/future/team/[slug]/[type]/embed/page.tsx @@ -26,7 +26,7 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) => isTeamEvent: true, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", - includeOnlyOneHost: true, + limitHostsToThree: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/apps/web/app/future/team/[slug]/[type]/page.tsx b/apps/web/app/future/team/[slug]/[type]/page.tsx index 2a27cdc8f632f2..abca995aeb267c 100644 --- a/apps/web/app/future/team/[slug]/[type]/page.tsx +++ b/apps/web/app/future/team/[slug]/[type]/page.tsx @@ -24,7 +24,7 @@ export const generateMetadata = async ({ params, searchParams }: PageProps) => { isTeamEvent: true, org: isValidOrgDomain ? currentOrgDomain : null, fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true", - includeOnlyOneHost: true, + limitHostsToThree: true, }); const profileName = event?.profile?.name ?? ""; diff --git a/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx b/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx index 84331c9c2f2db3..9881f9a437f9f0 100644 --- a/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx +++ b/packages/features/bookings/Booker/components/BookEventForm/BookFormAsModal.tsx @@ -12,7 +12,7 @@ import { FromTime } from "../../utils/dates"; import { useEvent } from "../../utils/event"; const BookEventFormWrapper = ({ children, onCancel }: { onCancel: () => void; children: ReactNode }) => { - const { data } = useEvent({ includeOnlyOneHost: true }); + const { data } = useEvent({ limitHostsToThree: true }); return ; }; diff --git a/packages/features/bookings/Booker/utils/event.ts b/packages/features/bookings/Booker/utils/event.ts index e83baa65515963..6809e9d38c4916 100644 --- a/packages/features/bookings/Booker/utils/event.ts +++ b/packages/features/bookings/Booker/utils/event.ts @@ -19,7 +19,7 @@ export type useScheduleForEventReturnType = ReturnType { +export const useEvent = (props?: { fromRedirectOfNonOrgLink?: boolean; limitHostsToThree?: boolean }) => { const [username, eventSlug, isTeamEvent, org] = useBookerStore( (state) => [state.username, state.eventSlug, state.isTeamEvent, state.org], shallow @@ -32,7 +32,7 @@ export const useEvent = (props?: { fromRedirectOfNonOrgLink?: boolean; includeOn isTeamEvent, org: org ?? null, fromRedirectOfNonOrgLink: props?.fromRedirectOfNonOrgLink, - includeOnlyOneHost: props?.includeOnlyOneHost, + limitHostsToThree: props?.limitHostsToThree, }, { refetchOnWindowFocus: false, diff --git a/packages/features/embed/Embed.tsx b/packages/features/embed/Embed.tsx index 52f4a2918e3283..e6b92be6dcd9e9 100644 --- a/packages/features/embed/Embed.tsx +++ b/packages/features/embed/Embed.tsx @@ -178,7 +178,7 @@ const EmailEmbed = ({ ], shallow ); - const event = useEvent({ includeOnlyOneHost: true }); + const event = useEvent({ limitHostsToThree: true }); const schedule = useScheduleForEvent({ orgSlug, eventId: eventType?.id, isTeamEvent }); const nonEmptyScheduleDays = useNonEmptyScheduleDays(schedule?.data?.slots); diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index 4170c63fde3be6..050a7fbeac3eee 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -116,7 +116,7 @@ function isAvailableInTimeSlot( return isWithinPeriod; } -const getPublicEventSelect = (includeOnlyOneHost?: boolean) => { +const getPublicEventSelect = (limitHostsToThree?: boolean) => { return Prisma.validator()({ id: true, title: true, @@ -188,7 +188,7 @@ const getPublicEventSelect = (includeOnlyOneHost?: boolean) => { select: userSelect, }, }, - ...(includeOnlyOneHost ? { take: 3 } : {}), + ...(limitHostsToThree ? { take: 3 } : {}), }, owner: { select: userSelect, @@ -221,9 +221,9 @@ export const getPublicEvent = async ( prisma: PrismaClient, fromRedirectOfNonOrgLink: boolean, currentUserId?: number, - includeOnlyOneHost?: boolean + limitHostsToThree?: boolean ) => { - const publicEventSelect = getPublicEventSelect(includeOnlyOneHost); + const publicEventSelect = getPublicEventSelect(limitHostsToThree); const usernameList = getUsernameList(username); const orgQuery = org ? getSlugOrRequestedSlug(org) : null; // In case of dynamic group event, we fetch user's data and use the default event. diff --git a/packages/lib/server/repository/event.ts b/packages/lib/server/repository/event.ts index 3b79183ab5631b..09d6e33b19dfeb 100644 --- a/packages/lib/server/repository/event.ts +++ b/packages/lib/server/repository/event.ts @@ -12,7 +12,7 @@ export class EventRepository { prisma, input.fromRedirectOfNonOrgLink, userId, - input.includeOnlyOneHost + input.limitHostsToThree ); return event; } diff --git a/packages/platform/atoms/booker/BookerWebWrapper.tsx b/packages/platform/atoms/booker/BookerWebWrapper.tsx index 9fd4a5ec275959..4a7312b1b31dd3 100644 --- a/packages/platform/atoms/booker/BookerWebWrapper.tsx +++ b/packages/platform/atoms/booker/BookerWebWrapper.tsx @@ -33,7 +33,7 @@ export const BookerWebWrapper = (props: BookerWebWrapperAtomProps) => { const searchParams = useSearchParams(); const event = useEvent({ fromRedirectOfNonOrgLink: props.entity.fromRedirectOfNonOrgLink, - includeOnlyOneHost: true, + limitHostsToThree: true, }); const bookerLayout = useBookerLayout(event.data); diff --git a/packages/trpc/server/routers/publicViewer/event.schema.ts b/packages/trpc/server/routers/publicViewer/event.schema.ts index 708d883e0456bb..fc52c3c2587ae2 100644 --- a/packages/trpc/server/routers/publicViewer/event.schema.ts +++ b/packages/trpc/server/routers/publicViewer/event.schema.ts @@ -10,7 +10,7 @@ export const ZEventInputSchema = z.object({ * Based on this decision like whether to allow unpublished organization's event to be served or not can be made. */ fromRedirectOfNonOrgLink: z.boolean().optional().default(false), - includeOnlyOneHost: z.boolean().optional().default(false), + limitHostsToThree: z.boolean().optional().default(false), }); export type TEventInputSchema = z.infer; From 39e64ffe48891019c785d4641039ca6a1dbc184c Mon Sep 17 00:00:00 2001 From: CarinaWolli Date: Mon, 23 Dec 2024 13:01:43 -0600 Subject: [PATCH 8/8] check for undefined metadata --- .../installation/[[...step]]/step-view.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/web/modules/apps/installation/[[...step]]/step-view.tsx b/apps/web/modules/apps/installation/[[...step]]/step-view.tsx index 26014f669cef95..56501c005bc57c 100644 --- a/apps/web/modules/apps/installation/[[...step]]/step-view.tsx +++ b/apps/web/modules/apps/installation/[[...step]]/step-view.tsx @@ -244,13 +244,18 @@ const OnboardingPage = ({ const promises = group.eventTypes .filter((eventType) => eventType.selected) .map((value: TEventType) => { - const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(value.metadata); - // Prevent two payment apps to be enabled - // Ok to cast type here because this metadata will be updated as the event type metadata - if (checkForMultiplePaymentApps(metadata)) - throw new Error(t("event_setup_multiple_payment_apps_error")); - if (value.metadata?.apps?.stripe?.paymentOption === "HOLD" && value.seatsPerTimeSlot) { - throw new Error(t("seats_and_no_show_fee_error")); + if (value.metadata) { + const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(value.metadata); + // Prevent two payment apps to be enabled + // Ok to cast type here because this metadata will be updated as the event type metadata + if (checkForMultiplePaymentApps(metadata)) + throw new Error(t("event_setup_multiple_payment_apps_error")); + if ( + value.metadata?.apps?.stripe?.paymentOption === "HOLD" && + value.seatsPerTimeSlot + ) { + throw new Error(t("seats_and_no_show_fee_error")); + } } let updateObject: TUpdateObject = { id: value.id }; if (isConferencing) {