From 56cd9cef8bb686964ff08ffe67d8d648de44782b Mon Sep 17 00:00:00 2001 From: Somay Chauhan Date: Fri, 31 Jan 2025 15:49:21 +0530 Subject: [PATCH] perf: don't fetch all the hosts #18319 followup (#19021) * perf: don't fetch all the hosts #18319 followup * undo for user events * undo for regular user events * fix: limitHostsToThree -> hostsLimit * accepting hostLimit prop in * Update booking.tsx * hostLimit -> hostsLimit --- .../organizations-event-types.controller.ts | 8 +- .../organizations-event-types.service.ts | 8 +- .../services/teams-event-types.service.ts | 12 +- .../teams-event-types.repository.ts | 8 +- apps/api/v2/swagger/documentation.json | 18 +++ .../atoms/booker/BookerPlatformWrapper.tsx | 13 +- .../transformApiEventTypeForAtom.ts | 117 ++++++++++-------- .../event-types/public/useTeamEventType.ts | 9 +- .../examples/base/src/pages/booking.tsx | 2 + .../inputs/get-event-types-query.input.ts | 9 ++ 10 files changed, 138 insertions(+), 66 deletions(-) diff --git a/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.controller.ts b/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.controller.ts index 57170c7a64..433dcc88df 100644 --- a/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.controller.ts +++ b/apps/api/v2/src/modules/organizations/controllers/event-types/organizations-event-types.controller.ts @@ -149,10 +149,14 @@ export class OrganizationsEventTypesController { @Param("teamId", ParseIntPipe) teamId: number, @Query() queryParams: GetTeamEventTypesQuery_2024_06_14 ): Promise { - const { eventSlug } = queryParams; + const { eventSlug, hostsLimit } = queryParams; if (eventSlug) { - const eventType = await this.organizationsEventTypesService.getTeamEventTypeBySlug(teamId, eventSlug); + const eventType = await this.organizationsEventTypesService.getTeamEventTypeBySlug( + teamId, + eventSlug, + hostsLimit + ); return { status: SUCCESS_STATUS, diff --git a/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts b/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts index 7a62972ff9..e6af4fa48a 100644 --- a/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts +++ b/apps/api/v2/src/modules/organizations/services/event-types/organizations-event-types.service.ts @@ -64,8 +64,12 @@ export class OrganizationsEventTypesService { return this.teamsEventTypesService.getTeamEventType(teamId, eventTypeId); } - async getTeamEventTypeBySlug(teamId: number, eventTypeSlug: string): Promise { - return this.teamsEventTypesService.getTeamEventTypeBySlug(teamId, eventTypeSlug); + async getTeamEventTypeBySlug( + teamId: number, + eventTypeSlug: string, + hostsLimit?: number + ): Promise { + return this.teamsEventTypesService.getTeamEventTypeBySlug(teamId, eventTypeSlug, hostsLimit); } async getTeamEventTypes(teamId: number): Promise { diff --git a/apps/api/v2/src/modules/teams/event-types/services/teams-event-types.service.ts b/apps/api/v2/src/modules/teams/event-types/services/teams-event-types.service.ts index 5694599d7e..41158b1357 100644 --- a/apps/api/v2/src/modules/teams/event-types/services/teams-event-types.service.ts +++ b/apps/api/v2/src/modules/teams/event-types/services/teams-event-types.service.ts @@ -75,8 +75,16 @@ export class TeamsEventTypesService { return eventType; } - async getTeamEventTypeBySlug(teamId: number, eventTypeSlug: string): Promise { - const eventType = await this.teamsEventTypesRepository.getTeamEventTypeBySlug(teamId, eventTypeSlug); + async getTeamEventTypeBySlug( + teamId: number, + eventTypeSlug: string, + hostsLimit?: number + ): Promise { + const eventType = await this.teamsEventTypesRepository.getTeamEventTypeBySlug( + teamId, + eventTypeSlug, + hostsLimit + ); if (!eventType) { return null; diff --git a/apps/api/v2/src/modules/teams/event-types/teams-event-types.repository.ts b/apps/api/v2/src/modules/teams/event-types/teams-event-types.repository.ts index 3b438df5d2..d72deb62bc 100644 --- a/apps/api/v2/src/modules/teams/event-types/teams-event-types.repository.ts +++ b/apps/api/v2/src/modules/teams/event-types/teams-event-types.repository.ts @@ -16,7 +16,7 @@ export class TeamsEventTypesRepository { }); } - async getTeamEventTypeBySlug(teamId: number, eventTypeSlug: string) { + async getTeamEventTypeBySlug(teamId: number, eventTypeSlug: string, hostsLimit?: number) { return this.dbRead.prisma.eventType.findUnique({ where: { teamId_slug: { @@ -27,7 +27,11 @@ export class TeamsEventTypesRepository { include: { users: true, schedule: true, - hosts: true, + hosts: hostsLimit + ? { + take: hostsLimit, + } + : true, destinationCalendar: true, team: { select: { diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index e02b0cc369..571dc3049f 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -1251,6 +1251,15 @@ "schema": { "type": "string" } + }, + { + "name": "hostsLimit", + "required": false, + "in": "query", + "description": "Specifies the maximum number of hosts to include in the response. This limit helps optimize performance. If not provided, all Hosts will be fetched.", + "schema": { + "type": "number" + } } ], "responses": { @@ -5889,6 +5898,15 @@ "schema": { "type": "string" } + }, + { + "name": "hostsLimit", + "required": false, + "in": "query", + "description": "Specifies the maximum number of hosts to include in the response. This limit helps optimize performance. If not provided, all Hosts will be fetched.", + "schema": { + "type": "number" + } } ], "responses": { diff --git a/packages/platform/atoms/booker/BookerPlatformWrapper.tsx b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx index a699475f37..6dbd602bb9 100644 --- a/packages/platform/atoms/booker/BookerPlatformWrapper.tsx +++ b/packages/platform/atoms/booker/BookerPlatformWrapper.tsx @@ -81,6 +81,7 @@ export type BookerPlatformWrapperAtomProps = Omit< metadata?: Record; bannerUrl?: string; onDryRunSuccess?: () => void; + hostsLimit?: number; }; type VIEW_TYPE = keyof typeof BookerLayouts; @@ -151,7 +152,7 @@ export const BookerPlatformWrapper = ( isError: isTeamError, isPending: isTeamPending, data: teamEventTypeData, - } = useTeamEventType(teamId, props.eventSlug, props.isTeamEvent); + } = useTeamEventType(teamId, props.eventSlug, props.isTeamEvent, props.hostsLimit); const event = useMemo(() => { if (props.isTeamEvent && !isTeamPending && teamId && teamEventTypeData && teamEventTypeData.length > 0) { @@ -161,7 +162,12 @@ export const BookerPlatformWrapper = ( isPending: isTeamPending, data: teamEventTypeData && teamEventTypeData.length > 0 - ? transformApiTeamEventTypeForAtom(teamEventTypeData[0], props.entity, props.defaultFormValues) + ? transformApiTeamEventTypeForAtom( + teamEventTypeData[0], + props.entity, + props.defaultFormValues, + !!props.hostsLimit + ) : undefined, }; } @@ -172,7 +178,7 @@ export const BookerPlatformWrapper = ( isPending, data: data && data.length > 0 - ? transformApiEventTypeForAtom(data[0], props.entity, props.defaultFormValues) + ? transformApiEventTypeForAtom(data[0], props.entity, props.defaultFormValues, !!props.hostsLimit) : undefined, }; }, [ @@ -187,6 +193,7 @@ export const BookerPlatformWrapper = ( isTeamPending, isTeamSuccess, isTeamError, + props.hostsLimit, ]); if (isDynamic && props.duration && event.data) { diff --git a/packages/platform/atoms/event-types/atom-api-transformers/transformApiEventTypeForAtom.ts b/packages/platform/atoms/event-types/atom-api-transformers/transformApiEventTypeForAtom.ts index bc7f7165d9..0d9d003cff 100644 --- a/packages/platform/atoms/event-types/atom-api-transformers/transformApiEventTypeForAtom.ts +++ b/packages/platform/atoms/event-types/atom-api-transformers/transformApiEventTypeForAtom.ts @@ -37,7 +37,8 @@ import type { BookerPlatformWrapperAtomProps } from "../../booker/BookerPlatform export function transformApiEventTypeForAtom( eventType: Omit & { bannerUrl?: string }, entity: BookerPlatformWrapperAtomProps["entity"] | undefined, - defaultFormValues: BookerPlatformWrapperAtomProps["defaultFormValues"] | undefined + defaultFormValues: BookerPlatformWrapperAtomProps["defaultFormValues"] | undefined, + limitHosts = false ) { const { lengthInMinutes, @@ -70,6 +71,25 @@ export function transformApiEventTypeForAtom( firstUsersMetadata?.defaultBookerLayouts || defaultEventBookerLayouts ); const metadata = EventTypeMetaDataSchema.parse(eventType.metadata); + const usersTransformed = users.map((user) => ({ + ...user, + metadata: undefined, + bookerUrl: getBookerBaseUrlSync(null), + profile: { + username: user.username || "", + name: user.name, + weekStart: user.weekStart, + image: "", + brandColor: user.brandColor, + darkBrandColor: user.darkBrandColor, + theme: null, + organization: null, + id: user.id, + organizationId: null, + userId: user.id, + upId: `usr-${user.id}`, + }, + })); return { ...rest, @@ -106,25 +126,9 @@ export function transformApiEventTypeForAtom( logoUrl: undefined, }, hosts: [], - users: users.map((user) => ({ - ...user, - metadata: undefined, - bookerUrl: getBookerBaseUrlSync(null), - profile: { - username: user.username || "", - name: user.name, - weekStart: user.weekStart, - image: "", - brandColor: user.brandColor, - darkBrandColor: user.darkBrandColor, - theme: null, - organization: null, - id: user.id, - organizationId: null, - userId: user.id, - upId: `usr-${user.id}`, - }, - })), + subsetOfHosts: [], + users: !limitHosts ? usersTransformed : undefined, + subsetOfUsers: usersTransformed, bookingLimits: bookingLimitsCount ? transformIntervalLimitsApiToInternal(bookingLimitsCount) : undefined, durationLimits: bookingLimitsDuration ? transformIntervalLimitsApiToInternal(bookingLimitsDuration) @@ -153,7 +157,8 @@ export function transformApiEventTypeForAtom( export function transformApiTeamEventTypeForAtom( eventType: TeamEventTypeOutput_2024_06_14, entity: BookerPlatformWrapperAtomProps["entity"] | undefined, - defaultFormValues: BookerPlatformWrapperAtomProps["defaultFormValues"] | undefined + defaultFormValues: BookerPlatformWrapperAtomProps["defaultFormValues"] | undefined, + limitHosts = false ) { const { lengthInMinutes, @@ -188,6 +193,40 @@ export function transformApiTeamEventTypeForAtom( firstUsersMetadata?.defaultBookerLayouts || defaultEventBookerLayouts ); + const hostTransformed = hosts.map((host) => ({ + user: { + id: host.userId, + avatarUrl: null, + name: host.name, + username: "", + metadata: {}, + darkBrandColor: null, + brandColor: null, + theme: null, + weekStart: "Sunday", + }, + })); + + const usersTransformed = hosts.map((host) => ({ + ...host, + metadata: undefined, + bookerUrl: getBookerBaseUrlSync(null), + profile: { + username: "", + name: host.name, + weekStart: "Sunday", + image: "", + brandColor: null, + darkBrandColor: null, + theme: null, + organization: null, + id: host.userId, + organizationId: null, + userId: host.userId, + upId: `usr-${host.userId}`, + }, + })); + return { ...rest, length: lengthInMinutes, @@ -223,38 +262,10 @@ export function transformApiTeamEventTypeForAtom( name: team?.name, logoUrl: team?.logoUrl, }, - hosts: hosts.map((host) => ({ - user: { - id: host.userId, - avatarUrl: null, - name: host.name, - username: "", - metadata: {}, - darkBrandColor: null, - brandColor: null, - theme: null, - weekStart: "Sunday", - }, - })), - users: hosts.map((host) => ({ - ...host, - metadata: undefined, - bookerUrl: getBookerBaseUrlSync(null), - profile: { - username: "", - name: host.name, - weekStart: "Sunday", - image: "", - brandColor: null, - darkBrandColor: null, - theme: null, - organization: null, - id: host.userId, - organizationId: null, - userId: host.userId, - upId: `usr-${host.userId}`, - }, - })), + hosts: !limitHosts ? hostTransformed : undefined, + subsetOfHosts: hostTransformed, + users: !limitHosts ? usersTransformed : undefined, + subsetOfUsers: usersTransformed, recurringEvent: recurrence ? transformRecurrenceApiToInternal(recurrence) : null, bookingLimits: bookingLimitsCount ? transformIntervalLimitsApiToInternal(bookingLimitsCount) : undefined, durationLimits: bookingLimitsDuration diff --git a/packages/platform/atoms/hooks/event-types/public/useTeamEventType.ts b/packages/platform/atoms/hooks/event-types/public/useTeamEventType.ts index f5b7947cbe..08b3b28198 100644 --- a/packages/platform/atoms/hooks/event-types/public/useTeamEventType.ts +++ b/packages/platform/atoms/hooks/event-types/public/useTeamEventType.ts @@ -9,13 +9,18 @@ import { useAtomsContext } from "../../useAtomsContext"; export const QUERY_KEY = "use-team-event-type"; -export const useTeamEventType = (teamId: number | undefined, eventSlug: string, isTeamEvent: boolean | undefined) => { +export const useTeamEventType = (teamId: number | undefined, eventSlug: string, isTeamEvent: boolean | undefined, hostsLimit?: number) => { const { organizationId } = useAtomsContext(); const requestEventSlug = eventSlug; - const pathname = `/organizations/${organizationId}/teams/${teamId}/event-types?eventSlug=${requestEventSlug}`; +let pathname = `/organizations/${organizationId}/teams/${teamId}/event-types?eventSlug=${requestEventSlug}`; + + if (hostsLimit !== undefined) { + pathname += `&hostsLimit=${hostsLimit}`; + } + const event = useQuery({ queryKey: [QUERY_KEY, eventSlug, organizationId, teamId], diff --git a/packages/platform/examples/base/src/pages/booking.tsx b/packages/platform/examples/base/src/pages/booking.tsx index a6b03c591e..a230974ab3 100644 --- a/packages/platform/examples/base/src/pages/booking.tsx +++ b/packages/platform/examples/base/src/pages/booking.tsx @@ -118,6 +118,7 @@ export default function Bookings(props: { calUsername: string; calEmail: string {...(isTeamEvent ? { isTeamEvent: true, teamId: teams?.[0]?.id || 0 } : { username: props.calUsername })} + hostsLimit={3} /> )} @@ -132,6 +133,7 @@ export default function Bookings(props: { calUsername: string; calEmail: string }} duration={eventTypeDuration} bannerUrl="https://i0.wp.com/mahala.co.uk/wp-content/uploads/2014/12/img_banner-thin_mountains.jpg?fit=800%2C258&ssl=1" + hostsLimit={3} /> )} {bookingTitle &&

Booking created: {bookingTitle}

} diff --git a/packages/platform/types/event-types/event-types_2024_06_14/inputs/get-event-types-query.input.ts b/packages/platform/types/event-types/event-types_2024_06_14/inputs/get-event-types-query.input.ts index e6c7930f42..dbf0d8d1a3 100644 --- a/packages/platform/types/event-types/event-types_2024_06_14/inputs/get-event-types-query.input.ts +++ b/packages/platform/types/event-types/event-types_2024_06_14/inputs/get-event-types-query.input.ts @@ -55,6 +55,15 @@ export class GetTeamEventTypesQuery_2024_06_14 { description: "Slug of team event type to return.", }) eventSlug?: string; + + @Transform(({ value }) => Number(value)) + @IsOptional() + @IsNumber() + @ApiPropertyOptional({ + description: + "Specifies the maximum number of hosts to include in the response. This limit helps optimize performance. If not provided, all Hosts will be fetched.", + }) + hostsLimit?: number; } function TransformUsernames() {