diff --git a/packages/app-store/routing-forms/trpc/deleteForm.handler.ts b/packages/app-store/routing-forms/trpc/deleteForm.handler.ts index 773703b23f..831f223d93 100644 --- a/packages/app-store/routing-forms/trpc/deleteForm.handler.ts +++ b/packages/app-store/routing-forms/trpc/deleteForm.handler.ts @@ -1,6 +1,5 @@ -import type { PrismaClient } from "@prisma/client"; - import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils"; +import type { PrismaClient } from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; diff --git a/packages/app-store/routing-forms/trpc/formMutation.handler.ts b/packages/app-store/routing-forms/trpc/formMutation.handler.ts index 2a24e08164..dffd2486bf 100644 --- a/packages/app-store/routing-forms/trpc/formMutation.handler.ts +++ b/packages/app-store/routing-forms/trpc/formMutation.handler.ts @@ -1,8 +1,8 @@ -import type { PrismaClient } from "@prisma/client"; import type { App_RoutingForms_Form } from "@prisma/client"; import { Prisma } from "@prisma/client"; import { entityPrismaWhereClause, canEditEntity } from "@calcom/lib/entityPermissionUtils"; +import type { PrismaClient } from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; diff --git a/packages/app-store/routing-forms/trpc/formQuery.handler.ts b/packages/app-store/routing-forms/trpc/formQuery.handler.ts index 8057d3ebad..94bc9e1929 100644 --- a/packages/app-store/routing-forms/trpc/formQuery.handler.ts +++ b/packages/app-store/routing-forms/trpc/formQuery.handler.ts @@ -1,6 +1,5 @@ -import type { PrismaClient } from "@prisma/client"; - import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils"; +import type { PrismaClient } from "@calcom/prisma"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { getSerializableForm } from "../lib/getSerializableForm"; diff --git a/packages/app-store/routing-forms/trpc/forms.handler.ts b/packages/app-store/routing-forms/trpc/forms.handler.ts index 1c3211a070..a01cdca8b0 100644 --- a/packages/app-store/routing-forms/trpc/forms.handler.ts +++ b/packages/app-store/routing-forms/trpc/forms.handler.ts @@ -1,7 +1,8 @@ import { hasFilter } from "@calcom/features/filters/lib/hasFilter"; import { entityPrismaWhereClause, canEditEntity } from "@calcom/lib/entityPermissionUtils"; import logger from "@calcom/lib/logger"; -import type { PrismaClient, Prisma } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; +import type { Prisma } from "@calcom/prisma/client"; import { entries } from "@calcom/prisma/zod-utils"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; diff --git a/packages/app-store/routing-forms/trpc/report.handler.ts b/packages/app-store/routing-forms/trpc/report.handler.ts index 8a14626501..cfb34167ac 100644 --- a/packages/app-store/routing-forms/trpc/report.handler.ts +++ b/packages/app-store/routing-forms/trpc/report.handler.ts @@ -1,6 +1,5 @@ -import type { PrismaClient } from "@prisma/client"; - import logger from "@calcom/lib/logger"; +import type { PrismaClient } from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; import { jsonLogicToPrisma } from "../jsonLogicToPrisma"; diff --git a/packages/app-store/routing-forms/trpc/response.handler.ts b/packages/app-store/routing-forms/trpc/response.handler.ts index b28623b12c..8078c7107a 100644 --- a/packages/app-store/routing-forms/trpc/response.handler.ts +++ b/packages/app-store/routing-forms/trpc/response.handler.ts @@ -1,7 +1,7 @@ -import type { PrismaClient } from "@prisma/client"; import { Prisma } from "@prisma/client"; import { z } from "zod"; +import type { PrismaClient } from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; import { getSerializableForm } from "../lib/getSerializableForm"; diff --git a/packages/features/bookings/lib/get-booking.ts b/packages/features/bookings/lib/get-booking.ts index f6058a1aa5..350e6cfca6 100644 --- a/packages/features/bookings/lib/get-booking.ts +++ b/packages/features/bookings/lib/get-booking.ts @@ -1,8 +1,9 @@ -import type { Prisma, PrismaClient } from "@prisma/client"; +import type { Prisma } from "@prisma/client"; import type { z } from "zod"; import { bookingResponsesDbSchema } from "@calcom/features/bookings/lib/getBookingResponsesSchema"; import slugify from "@calcom/lib/slugify"; +import type { PrismaClient } from "@calcom/prisma"; import prisma from "@calcom/prisma"; type BookingSelect = { diff --git a/packages/features/bookings/lib/handleConfirmation.ts b/packages/features/bookings/lib/handleConfirmation.ts index 5e384a9c42..cbfc3c3fa3 100644 --- a/packages/features/bookings/lib/handleConfirmation.ts +++ b/packages/features/bookings/lib/handleConfirmation.ts @@ -1,4 +1,4 @@ -import type { Prisma, PrismaClient, Workflow, WorkflowsOnEventTypes, WorkflowStep } from "@prisma/client"; +import type { Prisma, Workflow, WorkflowsOnEventTypes, WorkflowStep } from "@prisma/client"; import { scheduleTrigger } from "@calcom/app-store/zapier/lib/nodeScheduler"; import type { EventManagerUser } from "@calcom/core/EventManager"; @@ -11,6 +11,7 @@ import type { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload"; import sendPayload from "@calcom/features/webhooks/lib/sendPayload"; import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType"; import logger from "@calcom/lib/logger"; +import type { PrismaClient } from "@calcom/prisma"; import { BookingStatus, WebhookTriggerEvents } from "@calcom/prisma/enums"; import { bookingMetadataSchema } from "@calcom/prisma/zod-utils"; import type { AdditionalInformation, CalendarEvent } from "@calcom/types/Calendar"; diff --git a/packages/features/ee/common/server/checkLicense.ts b/packages/features/ee/common/server/checkLicense.ts index 82501b43a9..e3a5755caf 100644 --- a/packages/features/ee/common/server/checkLicense.ts +++ b/packages/features/ee/common/server/checkLicense.ts @@ -1,8 +1,8 @@ -import type { PrismaClient } from "@prisma/client"; import cache from "memory-cache"; import { z } from "zod"; import { CONSOLE_URL } from "@calcom/lib/constants"; +import type { PrismaClient } from "@calcom/prisma"; const CACHING_TIME = 86400000; // 24 hours in milliseconds diff --git a/packages/features/ee/deployment/lib/getDeploymentKey.ts b/packages/features/ee/deployment/lib/getDeploymentKey.ts index 3c25255a52..9210ba0631 100644 --- a/packages/features/ee/deployment/lib/getDeploymentKey.ts +++ b/packages/features/ee/deployment/lib/getDeploymentKey.ts @@ -1,4 +1,4 @@ -import type { PrismaClient } from "@prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; export async function getDeploymentKey(prisma: PrismaClient) { const deployment = await prisma.deployment.findUnique({ diff --git a/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts b/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts index a058ae1b4d..80eb738093 100644 --- a/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts +++ b/packages/features/ee/managed-event-types/lib/handleChildrenEventTypes.ts @@ -1,9 +1,10 @@ -import type { PrismaClient, Prisma } from "@prisma/client"; +import type { Prisma } from "@prisma/client"; import short from "short-uuid"; import { v5 as uuidv5 } from "uuid"; import { sendSlugReplacementEmail } from "@calcom/emails/email-manager"; import { getTranslation } from "@calcom/lib/server/i18n"; +import type { PrismaClient } from "@calcom/prisma"; import { SchedulingType } from "@calcom/prisma/enums"; import { _EventTypeModel } from "@calcom/prisma/zod"; import { allManagedEventTypeProps, unlockedManagedEventTypeProps } from "@calcom/prisma/zod-utils"; diff --git a/packages/features/ee/sso/lib/saml.ts b/packages/features/ee/sso/lib/saml.ts index c4b0975e9b..553d1f83e9 100644 --- a/packages/features/ee/sso/lib/saml.ts +++ b/packages/features/ee/sso/lib/saml.ts @@ -1,8 +1,8 @@ import type { SAMLSSORecord, OIDCSSORecord } from "@boxyhq/saml-jackson"; -import type { PrismaClient } from "@prisma/client"; import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants"; import { isTeamAdmin } from "@calcom/lib/server/queries/teams"; +import type { PrismaClient } from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; export const samlDatabaseUrl = process.env.SAML_DATABASE_URL || ""; diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index 0a1c9ac67c..2883e03435 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -9,7 +9,7 @@ import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/or import { isRecurringEvent, parseRecurringEvent } from "@calcom/lib"; import { getDefaultEvent, getUsernameList } from "@calcom/lib/defaultEvents"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import type { BookerLayoutSettings } from "@calcom/prisma/zod-utils"; import { bookerLayoutOptions, diff --git a/packages/features/flags/server/utils.ts b/packages/features/flags/server/utils.ts index 0686381925..6ffc090d55 100644 --- a/packages/features/flags/server/utils.ts +++ b/packages/features/flags/server/utils.ts @@ -1,4 +1,4 @@ -import type { PrismaClient } from "@prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import type { AppFlags } from "../config"; diff --git a/packages/features/webhooks/lib/getWebhooks.ts b/packages/features/webhooks/lib/getWebhooks.ts index c933b30343..0966fc1600 100644 --- a/packages/features/webhooks/lib/getWebhooks.ts +++ b/packages/features/webhooks/lib/getWebhooks.ts @@ -1,6 +1,5 @@ -import type { PrismaClient } from "@prisma/client"; - import defaultPrisma from "@calcom/prisma"; +import type { PrismaClient } from "@calcom/prisma"; import type { WebhookTriggerEvents } from "@calcom/prisma/enums"; export type GetSubscriberOptions = { diff --git a/packages/lib/getBooking.tsx b/packages/lib/getBooking.tsx index e67125dfd4..ed456e79b5 100644 --- a/packages/lib/getBooking.tsx +++ b/packages/lib/getBooking.tsx @@ -1,8 +1,9 @@ -import type { Prisma, PrismaClient } from "@prisma/client"; +import type { Prisma } from "@prisma/client"; import type { z } from "zod"; import { bookingResponsesDbSchema } from "@calcom/features/bookings/lib/getBookingResponsesSchema"; import slugify from "@calcom/lib/slugify"; +import type { PrismaClient } from "@calcom/prisma"; type BookingSelect = { description: true; diff --git a/packages/lib/getEventTypeById.ts b/packages/lib/getEventTypeById.ts index b9a232c410..32989b1711 100644 --- a/packages/lib/getEventTypeById.ts +++ b/packages/lib/getEventTypeById.ts @@ -1,4 +1,3 @@ -import type { PrismaClient } from "@prisma/client"; import { Prisma } from "@prisma/client"; import { getLocationGroupedOptions } from "@calcom/app-store/server"; @@ -10,6 +9,7 @@ import { parseBookingLimit, parseDurationLimit, parseRecurringEvent } from "@cal import { CAL_URL } from "@calcom/lib/constants"; import getPaymentAppData from "@calcom/lib/getPaymentAppData"; import { getTranslation } from "@calcom/lib/server/i18n"; +import type { PrismaClient } from "@calcom/prisma"; import { SchedulingType, MembershipRole, AppCategories } from "@calcom/prisma/enums"; import { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; diff --git a/packages/lib/server/maybeGetBookingUidFromSeat.ts b/packages/lib/server/maybeGetBookingUidFromSeat.ts index 20d6e72a31..c211da5f4c 100644 --- a/packages/lib/server/maybeGetBookingUidFromSeat.ts +++ b/packages/lib/server/maybeGetBookingUidFromSeat.ts @@ -1,4 +1,4 @@ -import type { PrismaClient } from "@prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; export async function maybeGetBookingUidFromSeat(prisma: PrismaClient, uid: string) { // Look bookingUid in bookingSeat diff --git a/packages/prisma/index.ts b/packages/prisma/index.ts index 2f91849dfe..f078f3ac8f 100644 --- a/packages/prisma/index.ts +++ b/packages/prisma/index.ts @@ -1,28 +1,65 @@ import type { Prisma } from "@prisma/client"; -import { PrismaClient } from "@prisma/client"; +import { PrismaClient as PrismaClientWithoutExtension } from "@prisma/client"; import { bookingReferenceMiddleware } from "./middleware"; declare global { // eslint-disable-next-line no-var - var prisma: PrismaClient | undefined; + var prisma: typeof prismaWithClientExtensions; } const prismaOptions: Prisma.PrismaClientOptions = {}; if (!!process.env.NEXT_PUBLIC_DEBUG) prismaOptions.log = ["query", "error", "warn"]; -export const prisma = globalThis.prisma || new PrismaClient(prismaOptions); +const prismaWithoutClientExtensions = new PrismaClientWithoutExtension(prismaOptions); export const customPrisma = (options: Prisma.PrismaClientOptions) => - new PrismaClient({ ...prismaOptions, ...options }); + new PrismaClientWithoutExtension({ ...prismaOptions, ...options }); + +// If any changed on middleware server restart is required +// TODO: Migrate it to $extends +bookingReferenceMiddleware(prismaWithoutClientExtensions); + +// FIXME: Due to some reason, there are types failing in certain places due to the $extends. Fix it and then enable it +// Specifically we get errors like `Type 'string | Date | null | undefined' is not assignable to type 'Exact'` +// const prismaWithClientExtensions = prismaWithoutClientExtensions.$extends({ +// query: { +// $allModels: { +// async $allOperations({ model, operation, args, query }) { +// const start = performance.now(); +// /* your custom logic here */ +// const res = await query(args); +// const end = performance.now(); +// logger.debug("Query Perf: ", `${model}.${operation} took ${(end - start).toFixed(2)}ms\n`); +// return res; +// }, +// }, +// }, +// }); +// .$extends({ +// name: "teamUpdateWithMetadata", +// query: { +// team: { +// async update({ model, operation, args, query }) { +// if (args.data.metadata) { +// // Prepare args.data with merged metadata +// } +// return query(args); +// }, +// }, +// }, +// }) + +const prismaWithClientExtensions = prismaWithoutClientExtensions; + +export const prisma = (globalThis.prisma as typeof prismaWithClientExtensions) || prismaWithClientExtensions; if (process.env.NODE_ENV !== "production") { globalThis.prisma = prisma; } -// If any changed on middleware server restart is required -bookingReferenceMiddleware(prisma); +export type PrismaClient = typeof prisma; export default prisma; export * from "./selects"; diff --git a/packages/prisma/middleware/bookingReference.ts b/packages/prisma/middleware/bookingReference.ts index 8141e999a0..8f81b7a98a 100644 --- a/packages/prisma/middleware/bookingReference.ts +++ b/packages/prisma/middleware/bookingReference.ts @@ -1,4 +1,4 @@ -import { PrismaClient } from "@prisma/client"; +import type { PrismaClient } from "@prisma/client"; function middleware(prisma: PrismaClient) { /***********************************/ diff --git a/packages/prisma/seed-performance-testing.ts b/packages/prisma/seed-performance-testing.ts new file mode 100644 index 0000000000..255aae462c --- /dev/null +++ b/packages/prisma/seed-performance-testing.ts @@ -0,0 +1,325 @@ +/** + * This script can be used to seed the database with a lot of data for performance testing. + * TODO: Make it more structured and configurable from CLI + * Run it as `npx ts-node --transpile-only ./seed-performance-testing.ts` + */ +import { uuid } from "short-uuid"; + +import dailyMeta from "@calcom/app-store/dailyvideo/_metadata"; +import googleMeetMeta from "@calcom/app-store/googlevideo/_metadata"; +import zoomMeta from "@calcom/app-store/zoomvideo/_metadata"; +import dayjs from "@calcom/dayjs"; +import { BookingStatus } from "@calcom/prisma/enums"; + +import { createUserAndEventType } from "./seed-utils"; + +async function createManyDifferentUsersWithDifferentEventTypesAndBookings({ + tillUser, + startFrom = 0, +}: { + tillUser: number; + startFrom?: number; +}) { + for (let i = startFrom; i < tillUser; i++) { + await createUserAndEventType({ + user: { + email: `pro${i}@example.com`, + name: "Pro Example", + password: "1111", + username: `pro${i}`, + theme: "light", + }, + eventTypes: [ + { + title: "30min", + slug: "30min", + length: 30, + _bookings: [ + { + uid: uuid(), + title: "30min", + startTime: dayjs().add(1, "day").toDate(), + endTime: dayjs().add(1, "day").add(30, "minutes").toDate(), + }, + { + uid: uuid(), + title: "30min", + startTime: dayjs().add(2, "day").toDate(), + endTime: dayjs().add(2, "day").add(30, "minutes").toDate(), + status: BookingStatus.PENDING, + }, + ], + }, + { + title: "60min", + slug: "60min", + length: 60, + }, + { + title: "Multiple duration", + slug: "multiple-duration", + length: 75, + metadata: { + multipleDuration: [30, 75, 90], + }, + }, + { + title: "paid", + slug: "paid", + length: 60, + price: 100, + }, + { + title: "In person meeting", + slug: "in-person", + length: 60, + locations: [{ type: "inPerson", address: "London" }], + }, + { + title: "Zoom Event", + slug: "zoom", + length: 60, + locations: [{ type: zoomMeta.appData?.location?.type }], + }, + { + title: "Daily Event", + slug: "daily", + length: 60, + locations: [{ type: dailyMeta.appData?.location?.type }], + }, + { + title: "Google Meet", + slug: "google-meet", + length: 60, + locations: [{ type: googleMeetMeta.appData?.location?.type }], + }, + { + title: "Yoga class", + slug: "yoga-class", + length: 30, + recurringEvent: { freq: 2, count: 12, interval: 1 }, + _bookings: [ + { + uid: uuid(), + title: "Yoga class", + recurringEventId: Buffer.from("yoga-class").toString("base64"), + startTime: dayjs().add(1, "day").toDate(), + endTime: dayjs().add(1, "day").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Yoga class", + recurringEventId: Buffer.from("yoga-class").toString("base64"), + startTime: dayjs().add(1, "day").add(1, "week").toDate(), + endTime: dayjs().add(1, "day").add(1, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Yoga class", + recurringEventId: Buffer.from("yoga-class").toString("base64"), + startTime: dayjs().add(1, "day").add(2, "week").toDate(), + endTime: dayjs().add(1, "day").add(2, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Yoga class", + recurringEventId: Buffer.from("yoga-class").toString("base64"), + startTime: dayjs().add(1, "day").add(3, "week").toDate(), + endTime: dayjs().add(1, "day").add(3, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Yoga class", + recurringEventId: Buffer.from("yoga-class").toString("base64"), + startTime: dayjs().add(1, "day").add(4, "week").toDate(), + endTime: dayjs().add(1, "day").add(4, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Yoga class", + recurringEventId: Buffer.from("yoga-class").toString("base64"), + startTime: dayjs().add(1, "day").add(5, "week").toDate(), + endTime: dayjs().add(1, "day").add(5, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Seeded Yoga class", + description: "seeded", + recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"), + startTime: dayjs().subtract(4, "day").toDate(), + endTime: dayjs().subtract(4, "day").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Seeded Yoga class", + description: "seeded", + recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"), + startTime: dayjs().subtract(4, "day").add(1, "week").toDate(), + endTime: dayjs().subtract(4, "day").add(1, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Seeded Yoga class", + description: "seeded", + recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"), + startTime: dayjs().subtract(4, "day").add(2, "week").toDate(), + endTime: dayjs().subtract(4, "day").add(2, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + { + uid: uuid(), + title: "Seeded Yoga class", + description: "seeded", + recurringEventId: Buffer.from("seeded-yoga-class").toString("base64"), + startTime: dayjs().subtract(4, "day").add(3, "week").toDate(), + endTime: dayjs().subtract(4, "day").add(3, "week").add(30, "minutes").toDate(), + status: BookingStatus.ACCEPTED, + }, + ], + }, + { + title: "Tennis class", + slug: "tennis-class", + length: 60, + recurringEvent: { freq: 2, count: 10, interval: 2 }, + requiresConfirmation: true, + _bookings: [ + { + uid: uuid(), + title: "Tennis class", + recurringEventId: Buffer.from("tennis-class").toString("base64"), + startTime: dayjs().add(2, "day").toDate(), + endTime: dayjs().add(2, "day").add(60, "minutes").toDate(), + status: BookingStatus.PENDING, + }, + { + uid: uuid(), + title: "Tennis class", + recurringEventId: Buffer.from("tennis-class").toString("base64"), + startTime: dayjs().add(2, "day").add(2, "week").toDate(), + endTime: dayjs().add(2, "day").add(2, "week").add(60, "minutes").toDate(), + status: BookingStatus.PENDING, + }, + { + uid: uuid(), + title: "Tennis class", + recurringEventId: Buffer.from("tennis-class").toString("base64"), + startTime: dayjs().add(2, "day").add(4, "week").toDate(), + endTime: dayjs().add(2, "day").add(4, "week").add(60, "minutes").toDate(), + status: BookingStatus.PENDING, + }, + { + uid: uuid(), + title: "Tennis class", + recurringEventId: Buffer.from("tennis-class").toString("base64"), + startTime: dayjs().add(2, "day").add(8, "week").toDate(), + endTime: dayjs().add(2, "day").add(8, "week").add(60, "minutes").toDate(), + status: BookingStatus.PENDING, + }, + { + uid: uuid(), + title: "Tennis class", + recurringEventId: Buffer.from("tennis-class").toString("base64"), + startTime: dayjs().add(2, "day").add(10, "week").toDate(), + endTime: dayjs().add(2, "day").add(10, "week").add(60, "minutes").toDate(), + status: BookingStatus.PENDING, + }, + ], + }, + ], + }); + } +} + +async function createAUserWithManyBookings() { + const random = Math.random(); + await createUserAndEventType({ + user: { + email: `pro-${random}@example.com`, + name: "Pro Example", + password: "1111", + username: `pro-${random}`, + theme: "light", + }, + eventTypes: [ + { + title: "30min", + slug: "30min", + length: 30, + _numBookings: 100, + }, + { + title: "60min", + slug: "60min", + length: 60, + _numBookings: 100, + }, + { + title: "Multiple duration", + slug: "multiple-duration", + length: 75, + metadata: { + multipleDuration: [30, 75, 90], + }, + _numBookings: 100, + }, + { + title: "paid", + slug: "paid", + length: 60, + price: 100, + _numBookings: 100, + }, + { + title: "Zoom Event", + slug: "zoom", + length: 60, + locations: [{ type: zoomMeta.appData?.location?.type }], + _numBookings: 100, + }, + { + title: "Daily Event", + slug: "daily", + length: 60, + locations: [{ type: dailyMeta.appData?.location?.type }], + _numBookings: 100, + }, + { + title: "Google Meet", + slug: "google-meet", + length: 60, + locations: [{ type: googleMeetMeta.appData?.location?.type }], + _numBookings: 100, + }, + { + title: "Yoga class", + slug: "yoga-class", + length: 30, + _numBookings: 100, + }, + { + title: "Tennis class", + slug: "tennis-class", + length: 60, + recurringEvent: { freq: 2, count: 10, interval: 2 }, + requiresConfirmation: true, + _numBookings: 100, + }, + ], + }); +} + +// createManyDifferentUsersWithDifferentEventTypesAndBookings({ +// tillUser: 20000, +// startFrom: 10000, +// }); + +createAUserWithManyBookings(); diff --git a/packages/prisma/seed-utils.ts b/packages/prisma/seed-utils.ts new file mode 100644 index 0000000000..31f5768429 --- /dev/null +++ b/packages/prisma/seed-utils.ts @@ -0,0 +1,147 @@ +import type { Prisma, UserPermissionRole } from "@prisma/client"; +import { uuid } from "short-uuid"; + +import dayjs from "@calcom/dayjs"; +import { hashPassword } from "@calcom/features/auth/lib/hashPassword"; +import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability"; + +import prisma from "."; + +export async function createUserAndEventType({ + user, + eventTypes = [], +}: { + user: { + email: string; + password: string; + username: string; + name: string; + completedOnboarding?: boolean; + timeZone?: string; + role?: UserPermissionRole; + theme?: "dark" | "light"; + }; + eventTypes?: Array< + Prisma.EventTypeUncheckedCreateInput & { + _bookings?: Prisma.BookingCreateInput[]; + _numBookings?: number; + } + >; +}) { + const userData = { + ...user, + password: await hashPassword(user.password), + emailVerified: new Date(), + completedOnboarding: user.completedOnboarding ?? true, + locale: "en", + schedules: + user.completedOnboarding ?? true + ? { + create: { + name: "Working Hours", + availability: { + createMany: { + data: getAvailabilityFromSchedule(DEFAULT_SCHEDULE), + }, + }, + }, + } + : undefined, + }; + + const theUser = await prisma.user.upsert({ + where: { email_username: { email: user.email, username: user.username } }, + update: userData, + create: userData, + }); + + console.log( + `👤 Upserted '${user.username}' with email "${user.email}" & password "${user.password}". Booking page 👉 ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}` + ); + + for (const eventTypeInput of eventTypes) { + const { _bookings, _numBookings, ...eventTypeData } = eventTypeInput; + let bookingFields; + if (_bookings && _numBookings) { + throw new Error("You can't set both _bookings and _numBookings"); + } else if (_numBookings) { + bookingFields = [...Array(_numBookings).keys()].map((i) => ({ + startTime: dayjs() + .add(1, "day") + .add(i * 5 + 0, "minutes") + .toDate(), + endTime: dayjs() + .add(1, "day") + .add(i * 5 + 30, "minutes") + .toDate(), + title: `${eventTypeInput.title}:${i + 1}`, + uid: uuid(), + })); + } else { + bookingFields = _bookings || []; + } + eventTypeData.userId = theUser.id; + eventTypeData.users = { connect: { id: theUser.id } }; + + const eventType = await prisma.eventType.findFirst({ + where: { + slug: eventTypeData.slug, + users: { + some: { + id: eventTypeData.userId, + }, + }, + }, + select: { + id: true, + }, + }); + + if (eventType) { + console.log( + `\t📆 Event type ${eventTypeData.slug} already seems seeded - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}/${eventTypeData.slug}` + ); + continue; + } + const { id } = await prisma.eventType.create({ + data: eventTypeData, + }); + + console.log( + `\t📆 Event type ${eventTypeData.slug} with id ${id}, length ${eventTypeData.length}min - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}/${eventTypeData.slug}` + ); + + for (const bookingInput of bookingFields) { + await prisma.booking.create({ + data: { + ...bookingInput, + user: { + connect: { + email: user.email, + }, + }, + attendees: { + create: { + email: user.email, + name: user.name, + timeZone: "Europe/London", + }, + }, + eventType: { + connect: { + id, + }, + }, + status: bookingInput.status, + }, + }); + console.log( + `\t\t☎️ Created booking ${bookingInput.title} at ${new Date( + bookingInput.startTime + ).toLocaleDateString()}` + ); + } + } + console.log("👤 User with it's event-types and bookings created", theUser.email); + return theUser; +} diff --git a/packages/prisma/seed.ts b/packages/prisma/seed.ts index 406da7b001..29d981a3a7 100644 --- a/packages/prisma/seed.ts +++ b/packages/prisma/seed.ts @@ -1,4 +1,4 @@ -import type { Prisma, UserPermissionRole } from "@prisma/client"; +import type { Prisma } from "@prisma/client"; import { uuid } from "short-uuid"; import type z from "zod"; @@ -6,132 +6,13 @@ import dailyMeta from "@calcom/app-store/dailyvideo/_metadata"; import googleMeetMeta from "@calcom/app-store/googlevideo/_metadata"; import zoomMeta from "@calcom/app-store/zoomvideo/_metadata"; import dayjs from "@calcom/dayjs"; -import { hashPassword } from "@calcom/features/auth/lib/hashPassword"; -import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability"; import { BookingStatus, MembershipRole } from "@calcom/prisma/enums"; import prisma from "."; import mainAppStore from "./seed-app-store"; +import { createUserAndEventType } from "./seed-utils"; import type { teamMetadataSchema } from "./zod-utils"; -async function createUserAndEventType({ - user, - eventTypes = [], -}: { - user: { - email: string; - password: string; - username: string; - name: string; - completedOnboarding?: boolean; - timeZone?: string; - role?: UserPermissionRole; - theme?: "dark" | "light"; - }; - eventTypes?: Array< - Prisma.EventTypeUncheckedCreateInput & { - _bookings?: Prisma.BookingCreateInput[]; - } - >; -}) { - const userData = { - ...user, - password: await hashPassword(user.password), - emailVerified: new Date(), - completedOnboarding: user.completedOnboarding ?? true, - locale: "en", - schedules: - user.completedOnboarding ?? true - ? { - create: { - name: "Working Hours", - availability: { - createMany: { - data: getAvailabilityFromSchedule(DEFAULT_SCHEDULE), - }, - }, - }, - } - : undefined, - }; - - const theUser = await prisma.user.upsert({ - where: { email_username: { email: user.email, username: user.username } }, - update: userData, - create: userData, - }); - - console.log( - `👤 Upserted '${user.username}' with email "${user.email}" & password "${user.password}". Booking page 👉 ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}` - ); - - for (const eventTypeInput of eventTypes) { - const { _bookings: bookingFields = [], ...eventTypeData } = eventTypeInput; - eventTypeData.userId = theUser.id; - eventTypeData.users = { connect: { id: theUser.id } }; - - const eventType = await prisma.eventType.findFirst({ - where: { - slug: eventTypeData.slug, - users: { - some: { - id: eventTypeData.userId, - }, - }, - }, - select: { - id: true, - }, - }); - - if (eventType) { - console.log( - `\t📆 Event type ${eventTypeData.slug} already seems seeded - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}/${eventTypeData.slug}` - ); - continue; - } - const { id } = await prisma.eventType.create({ - data: eventTypeData, - }); - - console.log( - `\t📆 Event type ${eventTypeData.slug} with id ${id}, length ${eventTypeData.length}min - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}/${eventTypeData.slug}` - ); - for (const bookingInput of bookingFields) { - await prisma.booking.create({ - data: { - ...bookingInput, - user: { - connect: { - email: user.email, - }, - }, - attendees: { - create: { - email: user.email, - name: user.name, - timeZone: "Europe/London", - }, - }, - eventType: { - connect: { - id, - }, - }, - status: bookingInput.status, - }, - }); - console.log( - `\t\t☎️ Created booking ${bookingInput.title} at ${new Date( - bookingInput.startTime - ).toLocaleDateString()}` - ); - } - } - - return theUser; -} - async function createTeamAndAddUsers( teamInput: Prisma.TeamCreateInput, users: { id: number; username: string; role?: MembershipRole }[] = [] diff --git a/packages/trpc/server/middlewares/sessionMiddleware.ts b/packages/trpc/server/middlewares/sessionMiddleware.ts index c77a6fe97f..9b14f37c66 100644 --- a/packages/trpc/server/middlewares/sessionMiddleware.ts +++ b/packages/trpc/server/middlewares/sessionMiddleware.ts @@ -1,6 +1,7 @@ import type { Session } from "next-auth"; import { WEBAPP_URL } from "@calcom/lib/constants"; +import logger from "@calcom/lib/logger"; import { MembershipRole } from "@calcom/prisma/enums"; import { teamMetadataSchema, userMetadata } from "@calcom/prisma/zod-utils"; @@ -131,17 +132,25 @@ const getUserSession = async (ctx: TRPCContextInner) => { return { user, session }; }; -const sessionMiddleware = middleware(async ({ ctx, next }) => { - const { user, session } = await getUserSession(ctx); +const sessionMiddleware = middleware(async ({ ctx, next }) => { + const middlewareStart = performance.now(); + const { user, session } = await getUserSession(ctx); + const middlewareEnd = performance.now(); + logger.debug("Perf:t.sessionMiddleware", middlewareEnd - middlewareStart); return next({ ctx: { user, session }, }); }); export const isAuthed = middleware(async ({ ctx, next }) => { + const middlewareStart = performance.now(); + const { user, session } = await getUserSession(ctx); + const middlewareEnd = performance.now(); + logger.debug("Perf:t.isAuthed", middlewareEnd - middlewareStart); + if (!user || !session) { throw new TRPCError({ code: "UNAUTHORIZED" }); } diff --git a/packages/trpc/server/routers/loggedInViewer/teamsAndUserProfilesQuery.handler.ts b/packages/trpc/server/routers/loggedInViewer/teamsAndUserProfilesQuery.handler.ts index 64d2aed7e6..d8e1f85b24 100644 --- a/packages/trpc/server/routers/loggedInViewer/teamsAndUserProfilesQuery.handler.ts +++ b/packages/trpc/server/routers/loggedInViewer/teamsAndUserProfilesQuery.handler.ts @@ -1,7 +1,6 @@ -import { type PrismaClient } from "@prisma/client"; - import { isOrganization, withRoleCanCreateEntity } from "@calcom/lib/entityPermissionUtils"; import { getBookerUrl } from "@calcom/lib/server/getBookerUrl"; +import type { PrismaClient } from "@calcom/prisma"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { TRPCError } from "@trpc/server"; diff --git a/packages/trpc/server/routers/publicViewer/event.handler.ts b/packages/trpc/server/routers/publicViewer/event.handler.ts index c1a20e5a77..58c0752fa2 100644 --- a/packages/trpc/server/routers/publicViewer/event.handler.ts +++ b/packages/trpc/server/routers/publicViewer/event.handler.ts @@ -1,6 +1,5 @@ -import type { PrismaClient } from "@prisma/client"; - import { getPublicEvent } from "@calcom/features/eventtypes/lib/getPublicEvent"; +import type { PrismaClient } from "@calcom/prisma"; import type { TEventInputSchema } from "./event.schema"; diff --git a/packages/trpc/server/routers/publicViewer/samlTenantProduct.handler.ts b/packages/trpc/server/routers/publicViewer/samlTenantProduct.handler.ts index 1bdfb8af5b..23f58730df 100644 --- a/packages/trpc/server/routers/publicViewer/samlTenantProduct.handler.ts +++ b/packages/trpc/server/routers/publicViewer/samlTenantProduct.handler.ts @@ -1,6 +1,5 @@ -import type { PrismaClient } from "@prisma/client"; - import { samlTenantProduct } from "@calcom/features/ee/sso/lib/saml"; +import type { PrismaClient } from "@calcom/prisma"; import type { TSamlTenantProductInputSchema } from "./samlTenantProduct.schema"; diff --git a/packages/trpc/server/routers/viewer/apps/listLocal.handler.ts b/packages/trpc/server/routers/viewer/apps/listLocal.handler.ts index 5dea5e51d1..6ab4755817 100644 --- a/packages/trpc/server/routers/viewer/apps/listLocal.handler.ts +++ b/packages/trpc/server/routers/viewer/apps/listLocal.handler.ts @@ -1,7 +1,8 @@ -import type { Prisma, PrismaClient } from "@prisma/client"; +import type { Prisma } from "@prisma/client"; import { appKeysSchemas } from "@calcom/app-store/apps.keys-schemas.generated"; import { getLocalAppMetadata } from "@calcom/app-store/utils"; +import type { PrismaClient } from "@calcom/prisma"; import { AppCategories } from "@calcom/prisma/enums"; import type { TrpcSessionUser } from "../../../trpc"; diff --git a/packages/trpc/server/routers/viewer/apps/saveKeys.handler.ts b/packages/trpc/server/routers/viewer/apps/saveKeys.handler.ts index 004992cf28..54fda2e1b9 100644 --- a/packages/trpc/server/routers/viewer/apps/saveKeys.handler.ts +++ b/packages/trpc/server/routers/viewer/apps/saveKeys.handler.ts @@ -1,8 +1,8 @@ import type { Prisma } from "@prisma/client"; -import type { PrismaClient } from "@prisma/client"; import { appKeysSchemas } from "@calcom/app-store/apps.keys-schemas.generated"; import { getLocalAppMetadata } from "@calcom/app-store/utils"; +import type { PrismaClient } from "@calcom/prisma"; import type { AppCategories } from "@calcom/prisma/enums"; // import prisma from "@calcom/prisma"; diff --git a/packages/trpc/server/routers/viewer/apps/toggle.handler.ts b/packages/trpc/server/routers/viewer/apps/toggle.handler.ts index 5fec389685..d71f85f980 100644 --- a/packages/trpc/server/routers/viewer/apps/toggle.handler.ts +++ b/packages/trpc/server/routers/viewer/apps/toggle.handler.ts @@ -1,8 +1,7 @@ -import type { PrismaClient } from "@prisma/client"; - import { getLocalAppMetadata } from "@calcom/app-store/utils"; import { sendDisabledAppEmail } from "@calcom/emails"; import { getTranslation } from "@calcom/lib/server"; +import type { PrismaClient } from "@calcom/prisma"; import { AppCategories } from "@calcom/prisma/enums"; import { TRPCError } from "@trpc/server"; diff --git a/packages/trpc/server/routers/viewer/availability/util.ts b/packages/trpc/server/routers/viewer/availability/util.ts index 74ce058b82..4f93efdd8a 100644 --- a/packages/trpc/server/routers/viewer/availability/util.ts +++ b/packages/trpc/server/routers/viewer/availability/util.ts @@ -1,6 +1,6 @@ import type { Availability as AvailabilityModel, Schedule as ScheduleModel, User } from "@prisma/client"; -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import type { Schedule } from "@calcom/types/schedule"; export const getDefaultScheduleId = async (userId: number, prisma: PrismaClient) => { diff --git a/packages/trpc/server/routers/viewer/bookings/get.handler.ts b/packages/trpc/server/routers/viewer/bookings/get.handler.ts index 1bdb98ea3c..7cfe9d617f 100644 --- a/packages/trpc/server/routers/viewer/bookings/get.handler.ts +++ b/packages/trpc/server/routers/viewer/bookings/get.handler.ts @@ -1,6 +1,7 @@ import { parseRecurringEvent } from "@calcom/lib"; +import type { PrismaClient } from "@calcom/prisma"; import { bookingMinimalSelect } from "@calcom/prisma"; -import type { Prisma, PrismaClient } from "@calcom/prisma/client"; +import type { Prisma } from "@calcom/prisma/client"; import { BookingStatus } from "@calcom/prisma/enums"; import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils"; @@ -72,6 +73,62 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { unconfirmed: { startTime: "asc" }, }; + const passedBookingsStatusFilter = bookingListingFilters[bookingListingByStatus]; + const orderBy = bookingListingOrderby[bookingListingByStatus]; + + const { bookings, recurringInfo } = await getBookings({ + user, + prisma, + passedBookingsStatusFilter, + filters: input.filters, + orderBy, + take, + skip, + }); + + const bookingsFetched = bookings.length; + let nextCursor: typeof skip | null = skip; + if (bookingsFetched > take) { + nextCursor += bookingsFetched; + } else { + nextCursor = null; + } + + return { + bookings, + recurringInfo, + nextCursor, + }; +}; + +const set = new Set(); +const getUniqueBookings = (arr: T[]) => { + const unique = arr.filter((booking) => { + const duplicate = set.has(booking.uid); + set.add(booking.uid); + return !duplicate; + }); + set.clear(); + return unique; +}; + +async function getBookings({ + user, + prisma, + passedBookingsStatusFilter, + filters, + orderBy, + take, + skip, +}: { + user: { id: number; email: string }; + filters: TGetInputSchema["filters"]; + prisma: PrismaClient; + passedBookingsStatusFilter: Prisma.BookingWhereInput; + orderBy: Prisma.BookingOrderByWithAggregationInput; + take: number; + skip: number; +}) { // TODO: Fix record typing const bookingWhereInputFilters: Record = { teamIds: { @@ -80,7 +137,7 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { eventType: { team: { id: { - in: input.filters?.teamIds, + in: filters?.teamIds, }, }, }, @@ -94,7 +151,7 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { users: { some: { id: { - in: input.filters?.userIds, + in: filters?.userIds, }, }, }, @@ -106,7 +163,7 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { AND: [ { eventTypeId: { - in: input.filters?.eventTypeIds, + in: filters?.eventTypeIds, }, }, ], @@ -114,21 +171,100 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { }; const filtersCombined: Prisma.BookingWhereInput[] = - input.filters && - Object.keys(input.filters).map((key) => { + filters && + Object.keys(filters).map((key) => { return bookingWhereInputFilters[key]; }); - const passedBookingsStatusFilter = bookingListingFilters[bookingListingByStatus]; - const orderBy = bookingListingOrderby[bookingListingByStatus]; + const bookingSelect = { + ...bookingMinimalSelect, + uid: true, + recurringEventId: true, + location: true, + eventType: { + select: { + slug: true, + id: true, + eventName: true, + price: true, + recurringEvent: true, + currency: true, + metadata: true, + seatsShowAttendees: true, + team: { + select: { + id: true, + name: true, + }, + }, + }, + }, + status: true, + paid: true, + payment: { + select: { + paymentOption: true, + amount: true, + currency: true, + success: true, + }, + }, + user: { + select: { + id: true, + name: true, + email: true, + }, + }, + rescheduled: true, + references: true, + isRecorded: true, + seatsReferences: { + where: { + attendee: { + email: user.email, + }, + }, + select: { + referenceUid: true, + attendee: { + select: { + email: true, + }, + }, + }, + }, + }; - const [bookingsQuery, recurringInfoBasic, recurringInfoExtended] = await Promise.all([ + const [ + // Quering these in parallel to save time. + // Note that because we are applying `take` to individual queries, we will usually get more bookings then we need. It is okay to have more bookings faster than having what we need slower + bookingsQueryUserId, + bookingsQueryAttendees, + bookingsQueryTeamMember, + bookingsQuerySeatReference, + ////////////////////////// + + recurringInfoBasic, + recurringInfoExtended, + // We need all promises to be successful, so we are not using Promise.allSettled + ] = await Promise.all([ prisma.booking.findMany({ where: { OR: [ { userId: user.id, }, + ], + AND: [passedBookingsStatusFilter, ...(filtersCombined ?? [])], + }, + orderBy, + take: take + 1, + skip, + }), + prisma.booking.findMany({ + where: { + OR: [ { attendees: { some: { @@ -136,6 +272,16 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { }, }, }, + ], + AND: [passedBookingsStatusFilter, ...(filtersCombined ?? [])], + }, + orderBy, + take: take + 1, + skip, + }), + prisma.booking.findMany({ + where: { + OR: [ { eventType: { team: { @@ -150,6 +296,16 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { }, }, }, + ], + AND: [passedBookingsStatusFilter, ...(filtersCombined ?? [])], + }, + orderBy, + take: take + 1, + skip, + }), + prisma.booking.findMany({ + where: { + OR: [ { seatsReferences: { some: { @@ -162,65 +318,6 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { ], AND: [passedBookingsStatusFilter, ...(filtersCombined ?? [])], }, - select: { - ...bookingMinimalSelect, - uid: true, - recurringEventId: true, - location: true, - eventType: { - select: { - slug: true, - id: true, - eventName: true, - price: true, - recurringEvent: true, - currency: true, - metadata: true, - seatsShowAttendees: true, - team: { - select: { - id: true, - name: true, - }, - }, - }, - }, - status: true, - paid: true, - payment: { - select: { - paymentOption: true, - amount: true, - currency: true, - success: true, - }, - }, - user: { - select: { - id: true, - name: true, - email: true, - }, - }, - rescheduled: true, - references: true, - isRecorded: true, - seatsReferences: { - where: { - attendee: { - email: user.email, - }, - }, - select: { - referenceUid: true, - attendee: { - select: { - email: true, - }, - }, - }, - }, - }, orderBy, take: take + 1, skip, @@ -285,7 +382,25 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { } ); - const bookings = bookingsQuery.map((booking) => { + const plainBookings = getUniqueBookings( + bookingsQueryUserId + .concat(bookingsQueryAttendees) + .concat(bookingsQueryTeamMember) + .concat(bookingsQuerySeatReference) + ); + + // Now enrich bookings with relation data. We could have queried the relation data along with the bookings, but that would cause unnecessary queries to the database. + // Because Prisma is also going to query the select relation data sequentially, we are fine querying it separately here as it would be just 1 query instead of 4 + const bookings = ( + await prisma.booking.findMany({ + where: { + id: { + in: plainBookings.map((booking) => booking.id), + }, + }, + select: bookingSelect, + }) + ).map((booking) => { // If seats are enabled and the event is not set to show attendees, filter out attendees that are not the current user if (booking.seatsReferences.length && !booking.eventType?.seatsShowAttendees) { booking.attendees = booking.attendees.filter((attendee) => attendee.email === user.email); @@ -303,18 +418,5 @@ export const getHandler = async ({ ctx, input }: GetOptions) => { endTime: booking.endTime.toISOString(), }; }); - - const bookingsFetched = bookings.length; - let nextCursor: typeof skip | null = skip; - if (bookingsFetched > take) { - nextCursor += bookingsFetched; - } else { - nextCursor = null; - } - - return { - bookings, - recurringInfo, - nextCursor, - }; -}; + return { bookings, recurringInfo }; +} diff --git a/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts index 87de226fd9..3c432cda94 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/create.handler.ts @@ -5,7 +5,7 @@ import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug"; import { DailyLocationType } from "@calcom/app-store/locations"; import getApps from "@calcom/app-store/utils"; import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials"; -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import { SchedulingType } from "@calcom/prisma/enums"; import { userMetadata as userMetadataSchema } from "@calcom/prisma/zod-utils"; diff --git a/packages/trpc/server/routers/viewer/eventTypes/get.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/get.handler.ts index cfa97c76c0..bf818039cd 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/get.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/get.handler.ts @@ -1,6 +1,5 @@ -import type { PrismaClient } from "@prisma/client"; - import getEventTypeById from "@calcom/lib/getEventTypeById"; +import type { PrismaClient } from "@calcom/prisma"; import type { TrpcSessionUser } from "../../../trpc"; import type { TGetInputSchema } from "./get.schema"; diff --git a/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts index ed97669408..f6e2791299 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/getByViewer.handler.ts @@ -1,4 +1,4 @@ -import { type PrismaClient, Prisma } from "@prisma/client"; +import { Prisma } from "@prisma/client"; // eslint-disable-next-line no-restricted-imports import { orderBy } from "lodash"; @@ -7,6 +7,7 @@ import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowE import { CAL_URL } from "@calcom/lib/constants"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { getBookerUrl } from "@calcom/lib/server/getBookerUrl"; +import type { PrismaClient } from "@calcom/prisma"; import { baseEventTypeSelect } from "@calcom/prisma"; import { MembershipRole, SchedulingType } from "@calcom/prisma/enums"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; diff --git a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts index ad3fdc75e1..da8109abdb 100644 --- a/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts +++ b/packages/trpc/server/routers/viewer/eventTypes/update.handler.ts @@ -1,4 +1,3 @@ -import type { PrismaClient } from "@prisma/client"; import { Prisma } from "@prisma/client"; import type { NextApiResponse, GetServerSidePropsContext } from "next"; @@ -8,6 +7,7 @@ import { validateIntervalLimitOrder } from "@calcom/lib"; import logger from "@calcom/lib/logger"; import { getTranslation } from "@calcom/lib/server"; import { validateBookerLayouts } from "@calcom/lib/validateBookerLayouts"; +import type { PrismaClient } from "@calcom/prisma"; import { WorkflowActions, WorkflowTriggerEvents } from "@calcom/prisma/client"; import { SchedulingType } from "@calcom/prisma/enums"; diff --git a/packages/trpc/server/routers/viewer/organizations/list.handler.ts b/packages/trpc/server/routers/viewer/organizations/list.handler.ts index 6fc4c15b29..d8f5bacd4d 100644 --- a/packages/trpc/server/routers/viewer/organizations/list.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/list.handler.ts @@ -1,4 +1,4 @@ -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; diff --git a/packages/trpc/server/routers/viewer/payments/chargeCard.handler.ts b/packages/trpc/server/routers/viewer/payments/chargeCard.handler.ts index 8d5e654d6d..5bde7bfed5 100644 --- a/packages/trpc/server/routers/viewer/payments/chargeCard.handler.ts +++ b/packages/trpc/server/routers/viewer/payments/chargeCard.handler.ts @@ -2,7 +2,7 @@ import appStore from "@calcom/app-store"; import dayjs from "@calcom/dayjs"; import { sendNoShowFeeChargedEmail } from "@calcom/emails"; import { getTranslation } from "@calcom/lib/server/i18n"; -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import type { CalendarEvent } from "@calcom/types/Calendar"; import type { IAbstractPaymentService, PaymentApp } from "@calcom/types/PaymentService"; diff --git a/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts b/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts index 5ec95b1866..330789f975 100644 --- a/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts +++ b/packages/trpc/server/routers/viewer/slots/reserveSlot.handler.ts @@ -4,7 +4,7 @@ import { v4 as uuid } from "uuid"; import dayjs from "@calcom/dayjs"; import { MINUTES_TO_BOOK } from "@calcom/lib/constants"; -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import { TRPCError } from "@trpc/server"; diff --git a/packages/trpc/server/routers/viewer/teams/listMembers.handler.ts b/packages/trpc/server/routers/viewer/teams/listMembers.handler.ts index d507250745..29291c5723 100644 --- a/packages/trpc/server/routers/viewer/teams/listMembers.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/listMembers.handler.ts @@ -1,5 +1,4 @@ -import type { PrismaClient } from "@prisma/client"; - +import type { PrismaClient } from "@calcom/prisma"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import type { TListMembersInputSchema } from "./listMembers.schema"; diff --git a/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts b/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts index 2a486d7177..4797f6561d 100644 --- a/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/removeMember.handler.ts @@ -1,9 +1,8 @@ -import type { PrismaClient } from "@prisma/client"; - import { updateQuantitySubscriptionFromStripe } from "@calcom/features/ee/teams/lib/payments"; import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants"; import { isTeamAdmin, isTeamOwner } from "@calcom/lib/server/queries/teams"; import { closeComDeleteTeamMembership } from "@calcom/lib/sync/SyncServiceManager"; +import type { PrismaClient } from "@calcom/prisma"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { TRPCError } from "@trpc/server"; diff --git a/packages/trpc/server/routers/viewer/workflows/create.handler.ts b/packages/trpc/server/routers/viewer/workflows/create.handler.ts index 6159389ffc..93c28e1402 100644 --- a/packages/trpc/server/routers/viewer/workflows/create.handler.ts +++ b/packages/trpc/server/routers/viewer/workflows/create.handler.ts @@ -3,8 +3,8 @@ import type { Workflow } from "@prisma/client"; import emailReminderTemplate from "@calcom/ee/workflows/lib/reminders/templates/emailReminderTemplate"; import { SENDER_NAME } from "@calcom/lib/constants"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; +import type { PrismaClient } from "@calcom/prisma"; import { prisma } from "@calcom/prisma"; -import type { PrismaClient } from "@calcom/prisma/client"; import { MembershipRole, TimeUnit, diff --git a/packages/trpc/server/routers/viewer/workflows/filteredList.handler.tsx b/packages/trpc/server/routers/viewer/workflows/filteredList.handler.tsx index db2e5b2ec6..fc8197ed71 100644 --- a/packages/trpc/server/routers/viewer/workflows/filteredList.handler.tsx +++ b/packages/trpc/server/routers/viewer/workflows/filteredList.handler.tsx @@ -1,6 +1,7 @@ import type { WorkflowType } from "@calcom/ee/workflows/components/WorkflowListPage"; import { hasFilter } from "@calcom/features/filters/lib/hasFilter"; -import type { Prisma, PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; +import type { Prisma } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/client"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; diff --git a/packages/trpc/server/routers/viewer/workflows/update.handler.ts b/packages/trpc/server/routers/viewer/workflows/update.handler.ts index 0ab20781e7..a138030317 100644 --- a/packages/trpc/server/routers/viewer/workflows/update.handler.ts +++ b/packages/trpc/server/routers/viewer/workflows/update.handler.ts @@ -19,7 +19,7 @@ import { import { IS_SELF_HOSTED, SENDER_ID, SENDER_NAME } from "@calcom/lib/constants"; import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import { BookingStatus, WorkflowActions, WorkflowMethods, WorkflowTriggerEvents } from "@calcom/prisma/enums"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; diff --git a/packages/trpc/server/routers/viewer/workflows/util.ts b/packages/trpc/server/routers/viewer/workflows/util.ts index ec76fea3a4..f312718f23 100644 --- a/packages/trpc/server/routers/viewer/workflows/util.ts +++ b/packages/trpc/server/routers/viewer/workflows/util.ts @@ -8,7 +8,7 @@ import { } from "@calcom/features/bookings/lib/getBookingFields"; import { removeBookingField, upsertBookingField } from "@calcom/features/eventtypes/lib/bookingFieldsManager"; import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants"; -import type PrismaType from "@calcom/prisma"; +import type { PrismaClient } from "@calcom/prisma"; import type { WorkflowStep } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/enums"; @@ -20,7 +20,7 @@ export function getSender( export async function isAuthorized( workflow: Pick | null, - prisma: typeof PrismaType, + prisma: PrismaClient, currentUserId: number, readOnly?: boolean ) { diff --git a/packages/types/next.d.ts b/packages/types/next.d.ts index 6e13587cc8..2807620616 100644 --- a/packages/types/next.d.ts +++ b/packages/types/next.d.ts @@ -1,7 +1,7 @@ import type { IncomingMessage } from "http"; import type { Session } from "next-auth"; -import type { PrismaClient } from "@calcom/prisma/client"; +import type { PrismaClient } from "@calcom/prisma"; import "./next-auth"; diff --git a/tests/libs/__mocks__/prisma.ts b/tests/libs/__mocks__/prisma.ts index a510d2abd1..d285b349a7 100644 --- a/tests/libs/__mocks__/prisma.ts +++ b/tests/libs/__mocks__/prisma.ts @@ -1,7 +1,8 @@ -import type { PrismaClient } from "@prisma/client"; import { beforeEach, vi } from "vitest"; import { mockDeep, mockReset } from "vitest-mock-extended"; +import type { PrismaClient } from "@calcom/prisma"; + vi.mock("@calcom/prisma", () => ({ default: prisma, availabilityUserSelect: vi.fn(),