fix: fetch next batch if event types are empty and cursor exists (#17109)
* fix: fetch next batch if event types are empty and cursor exists * refactor: improve * fix: add isFetchingForFirstTime * chore: add number type * fix: type err * chore: add logs * fix: type err * fix: conflict * fix: bug * fix: type err * chore: try changing type * chore: add nul * Update packages/trpc/server/routers/viewer/eventTypes/getEventTypesFromGroup.handler.ts Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * chore: use log.info --------- Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Bailey Pumfleet <bailey@pumfleet.co.uk>
This commit is contained in:
co-authored by
sean-brydon
Bailey Pumfleet
parent
18c7c3c940
commit
5c6e60b726
@@ -81,7 +81,7 @@ interface InfiniteEventTypeListProps {
|
||||
group: InfiniteEventTypeGroup;
|
||||
readOnly: boolean;
|
||||
bookerUrl: string | null;
|
||||
pages: { nextCursor: number | undefined; eventTypes: InfiniteEventType[] }[] | undefined;
|
||||
pages: { nextCursor: number | null | undefined; eventTypes: InfiniteEventType[] }[] | undefined;
|
||||
lockedByOrg?: boolean;
|
||||
isPending?: boolean;
|
||||
debouncedSearchTerm?: string;
|
||||
@@ -349,9 +349,14 @@ export const InfiniteEventTypeList = ({
|
||||
group: { teamId: group?.teamId, parentId: group?.parentId },
|
||||
},
|
||||
(data) => {
|
||||
if (!data) return { pages: [], pageParams: [] };
|
||||
|
||||
return {
|
||||
pageParams: data?.pageParams ?? [],
|
||||
pages: newOrder,
|
||||
...data,
|
||||
pages: newOrder.map((page) => ({
|
||||
...page,
|
||||
nextCursor: page.nextCursor ?? undefined,
|
||||
})),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { EventTypeRepository } from "@calcom/lib/server/repository/eventType";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
|
||||
// import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import type { TrpcSessionUser } from "../../../trpc";
|
||||
import type { TGetEventTypesFromGroupSchema } from "./getByViewer.schema";
|
||||
import { mapEventType } from "./util";
|
||||
@@ -22,8 +21,15 @@ type GetByViewerOptions = {
|
||||
};
|
||||
|
||||
type EventType = Awaited<ReturnType<typeof EventTypeRepository.findAllByUpId>>[number];
|
||||
type MappedEventType = Awaited<ReturnType<typeof mapEventType>>;
|
||||
|
||||
export const getEventTypesFromGroup = async ({ ctx, input }: GetByViewerOptions) => {
|
||||
export const getEventTypesFromGroup = async ({
|
||||
ctx,
|
||||
input,
|
||||
}: GetByViewerOptions): Promise<{
|
||||
eventTypes: MappedEventType[];
|
||||
nextCursor: number | null | undefined;
|
||||
}> => {
|
||||
await checkRateLimitAndThrowError({
|
||||
identifier: `eventTypes:getEventTypesFromGroup:${ctx.user.id}`,
|
||||
rateLimitingType: "common",
|
||||
@@ -31,7 +37,7 @@ export const getEventTypesFromGroup = async ({ ctx, input }: GetByViewerOptions)
|
||||
|
||||
const userProfile = ctx.user.profile;
|
||||
const { group, limit, cursor, filters, searchQuery } = input;
|
||||
const { teamId, parentId } = group;
|
||||
const { teamId } = group;
|
||||
|
||||
const isFilterSet = (filters && hasFilter(filters)) || !!teamId;
|
||||
const isUpIdInFilter = filters?.upIds?.includes(userProfile.upId);
|
||||
@@ -39,6 +45,41 @@ export const getEventTypesFromGroup = async ({ ctx, input }: GetByViewerOptions)
|
||||
const shouldListUserEvents =
|
||||
!isFilterSet || isUpIdInFilter || (isFilterSet && filters?.upIds && !isUpIdInFilter);
|
||||
|
||||
const eventTypes: MappedEventType[] = [];
|
||||
const currentCursor = cursor;
|
||||
let nextCursor: number | null | undefined = undefined;
|
||||
let isFetchingForFirstTime = true;
|
||||
|
||||
const fetchAndFilterEventTypes = async () => {
|
||||
const batch = await fetchEventTypesBatch(ctx, input, shouldListUserEvents, currentCursor, searchQuery);
|
||||
const filteredBatch = await filterEventTypes(batch.eventTypes, ctx.user.id, shouldListUserEvents, teamId);
|
||||
eventTypes.push(...filteredBatch);
|
||||
nextCursor = batch.nextCursor;
|
||||
};
|
||||
|
||||
while (eventTypes.length < limit && (nextCursor || isFetchingForFirstTime)) {
|
||||
await fetchAndFilterEventTypes();
|
||||
isFetchingForFirstTime = false;
|
||||
}
|
||||
|
||||
return {
|
||||
eventTypes,
|
||||
nextCursor: nextCursor ?? undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const fetchEventTypesBatch = async (
|
||||
ctx: GetByViewerOptions["ctx"],
|
||||
input: GetByViewerOptions["input"],
|
||||
shouldListUserEvents: boolean | undefined,
|
||||
cursor: TGetEventTypesFromGroupSchema["cursor"],
|
||||
searchQuery: TGetEventTypesFromGroupSchema["searchQuery"]
|
||||
) => {
|
||||
const userProfile = ctx.user.profile;
|
||||
const { group, limit, filters } = input;
|
||||
const { teamId, parentId } = group;
|
||||
const isFilterSet = (filters && hasFilter(filters)) || !!teamId;
|
||||
|
||||
const eventTypes: EventType[] = [];
|
||||
|
||||
if (shouldListUserEvents || !teamId) {
|
||||
@@ -99,8 +140,8 @@ export const getEventTypesFromGroup = async ({ ctx, input }: GetByViewerOptions)
|
||||
eventTypes.push(...teamEventTypes);
|
||||
}
|
||||
|
||||
let nextCursor: typeof cursor | undefined = undefined;
|
||||
if (eventTypes && eventTypes.length > limit) {
|
||||
let nextCursor: number | null | undefined = undefined;
|
||||
if (eventTypes.length > limit) {
|
||||
const nextItem = eventTypes.pop();
|
||||
nextCursor = nextItem?.id;
|
||||
}
|
||||
@@ -108,37 +149,45 @@ export const getEventTypesFromGroup = async ({ ctx, input }: GetByViewerOptions)
|
||||
const mappedEventTypes = await Promise.all(eventTypes.map(mapEventType));
|
||||
|
||||
log.info(
|
||||
"mappedEventTypes before filtering",
|
||||
"fetchEventTypesBatch",
|
||||
safeStringify({
|
||||
input,
|
||||
mappedEventTypes,
|
||||
})
|
||||
);
|
||||
|
||||
const filteredEventTypes = mappedEventTypes.filter((eventType) => {
|
||||
const isAChildEvent = eventType.parentId;
|
||||
if (!isAChildEvent) {
|
||||
return { eventTypes: mappedEventTypes, nextCursor: nextCursor ?? undefined };
|
||||
};
|
||||
|
||||
const filterEventTypes = async (
|
||||
eventTypes: MappedEventType[],
|
||||
userId: number,
|
||||
shouldListUserEvents: boolean | undefined,
|
||||
teamId: number | null | undefined
|
||||
) => {
|
||||
const filteredEventTypes = eventTypes.filter((eventType) => {
|
||||
if (!eventType.parentId) {
|
||||
return true;
|
||||
}
|
||||
// A child event only has one user
|
||||
const childEventAssignee = eventType.users[0];
|
||||
if (!childEventAssignee || childEventAssignee.id != ctx.user.id) {
|
||||
|
||||
if (!childEventAssignee || childEventAssignee.id !== userId) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
log.info(
|
||||
"mappedEventTypes after filtering",
|
||||
"mappedEventTypes before and after filtering",
|
||||
safeStringify({
|
||||
input,
|
||||
filteredEventTypes,
|
||||
beforeFiltering: eventTypes,
|
||||
afterFiltering: filteredEventTypes,
|
||||
})
|
||||
);
|
||||
|
||||
const membership = await prisma.membership.findFirst({
|
||||
where: {
|
||||
userId: ctx.user.id,
|
||||
userId,
|
||||
teamId: teamId ?? 0,
|
||||
accepted: true,
|
||||
role: "MEMBER",
|
||||
@@ -158,8 +207,12 @@ export const getEventTypesFromGroup = async ({ ctx, input }: GetByViewerOptions)
|
||||
evType.hosts = [];
|
||||
});
|
||||
|
||||
return {
|
||||
eventTypes: filteredEventTypes || [],
|
||||
nextCursor,
|
||||
};
|
||||
log.info(
|
||||
"filteredEventTypes",
|
||||
safeStringify({
|
||||
filteredEventTypes,
|
||||
})
|
||||
);
|
||||
|
||||
return filteredEventTypes;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user