fix: Broken team events if a user with the same name exists (#10724)

* fix: Broken team events if a user with the same name exists

* Fix tests + fix usernameList optionality
pull/10718/head^2
Alex van Andel 2023-08-11 22:06:24 +01:00 committed by GitHub
parent 4d5697f5a5
commit ceb18167cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 39 deletions

View File

@ -274,6 +274,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
// As per Google Calendar Availability, only 4PM(4-4:45PM) GMT slot would be available
@ -358,6 +359,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
// getSchedule returns timeslots in GMT
@ -387,6 +389,7 @@ describe("getSchedule", () => {
startTime: `${plus2DateString}T18:30:00.000Z`,
endTime: `${plus3DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(scheduleForDayWithOneBooking).toHaveTimeSlots(
@ -450,6 +453,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(scheduleForEventWith30Length).toHaveTimeSlots(
@ -483,6 +487,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
// `slotInterval` takes precedence over `length`
// 4:30 is utc so it is 10:00 in IST
@ -545,6 +550,7 @@ describe("getSchedule", () => {
startTime: `${minus1DateString}T18:30:00.000Z`,
endTime: `${todayDateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(scheduleForEventWithBookingNotice13Hrs).toHaveTimeSlots(
[
@ -563,6 +569,7 @@ describe("getSchedule", () => {
startTime: `${minus1DateString}T18:30:00.000Z`,
endTime: `${todayDateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(scheduleForEventWithBookingNotice10Hrs).toHaveTimeSlots(
[
@ -625,6 +632,7 @@ describe("getSchedule", () => {
startTime: `${plus2DateString}T18:30:00.000Z`,
endTime: `${plus3DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(scheduleForEventOnADayWithNonCalBooking).toHaveTimeSlots(
@ -697,6 +705,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(scheduleForEventOnADayWithCalBooking).toHaveTimeSlots(
@ -753,6 +762,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(schedule).toHaveTimeSlots(
@ -815,6 +825,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(scheduleForEventOnADayWithDateOverride).toHaveTimeSlots(
@ -899,6 +910,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: false,
});
expect(thisUserAvailability).toHaveTimeSlots(
@ -995,6 +1007,7 @@ describe("getSchedule", () => {
startTime: `${todayDateString}T18:30:00.000Z`,
endTime: `${plus1DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: true,
});
expect(scheduleForTeamEventOnADayWithNoBooking).toHaveTimeSlots(
@ -1022,6 +1035,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: true,
});
// A user with blocked time in another event, still affects Team Event availability
@ -1128,6 +1142,7 @@ describe("getSchedule", () => {
startTime: `${plus1DateString}T18:30:00.000Z`,
endTime: `${plus2DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: true,
});
// A user with blocked time in another event, still affects Team Event availability
expect(scheduleForTeamEventOnADayWithOneBookingForEachUserButOnDifferentTimeslots).toHaveTimeSlots(
@ -1153,6 +1168,7 @@ describe("getSchedule", () => {
startTime: `${plus2DateString}T18:30:00.000Z`,
endTime: `${plus3DateString}T18:29:59.999Z`,
timeZone: Timezones["+5:30"],
isTeamEvent: true,
});
// A user with blocked time in another event, still affects Team Event availability
expect(scheduleForTeamEventOnADayWithOneBookingForEachUserOnSameTimeSlot).toHaveTimeSlots(

View File

@ -1,4 +1,4 @@
import { useSearchParams } from "next/navigation";
import { useSearchParams, usePathname } from "next/navigation";
import { shallow } from "zustand/shallow";
import { useSchedule } from "@calcom/features/schedules";
@ -59,8 +59,10 @@ export const useScheduleForEvent = ({
(state) => [state.username, state.eventSlug, state.month, state.selectedDuration],
shallow
);
const serachParams = useSearchParams();
const rescheduleUid = serachParams.get("rescheduleUid");
const searchParams = useSearchParams();
const rescheduleUid = searchParams.get("rescheduleUid");
const pathname = usePathname();
return useSchedule({
username: usernameFromStore ?? username,
@ -71,5 +73,6 @@ export const useScheduleForEvent = ({
rescheduleUid,
month: monthFromStore ?? month,
duration: durationFromStore ?? duration,
isTeamEvent: pathname.indexOf("/team/") !== -1,
});
};

View File

@ -11,6 +11,7 @@ type UseScheduleWithCacheArgs = {
prefetchNextMonth?: boolean;
duration?: number | null;
rescheduleUid?: string | null;
isTeamEvent?: boolean;
};
export const useSchedule = ({
@ -22,6 +23,7 @@ export const useSchedule = ({
prefetchNextMonth,
duration,
rescheduleUid,
isTeamEvent,
}: UseScheduleWithCacheArgs) => {
const monthDayjs = month ? dayjs(month) : dayjs();
const nextMonthDayjs = monthDayjs.add(1, "month");
@ -30,6 +32,7 @@ export const useSchedule = ({
// no satisfy typscript.
return trpc.viewer.public.slots.getSchedule.useQuery(
{
isTeamEvent,
usernameList: getUsernameList(username ?? ""),
// Prioritize slug over id, since slug is the first value we get available.
// If we have a slug, we don't need to fetch the id.

View File

@ -13,7 +13,7 @@ export const getScheduleSchema = z
// invitee timezone
timeZone: z.string().optional(),
// or list of users (for dynamic events)
usernameList: z.union([z.string(), z.array(z.string())]).optional(),
usernameList: z.array(z.string()).min(1).optional(),
debug: z.boolean().optional(),
// to handle event types with multiple duration options
duration: z
@ -21,6 +21,8 @@ export const getScheduleSchema = z
.optional()
.transform((val) => val && parseInt(val)),
rescheduleUid: z.string().optional().nullable(),
// whether to do team event or user event
isTeamEvent: z.boolean().optional().default(false),
})
.transform((val) => {
// Need this so we can pass a single username in the query string form public API

View File

@ -69,42 +69,53 @@ export const checkIfIsAvailable = ({
});
};
async function getEventTypeId({
slug,
eventTypeSlug,
isTeamEvent,
}: {
slug?: string;
eventTypeSlug?: string;
isTeamEvent: boolean;
}) {
if (!eventTypeSlug || !slug) return null;
let teamId;
let userId;
if (isTeamEvent) {
teamId = await getTeamIdFromSlug(slug);
} else {
userId = await getUserIdFromUsername(slug);
}
const eventType = await prisma.eventType.findFirst({
where: {
slug: eventTypeSlug,
...(teamId ? { teamId } : {}),
...(userId ? { userId } : {}),
},
select: {
id: true,
},
});
if (!eventType) {
throw new TRPCError({ code: "NOT_FOUND" });
}
return eventType?.id;
}
export async function getEventType(input: TGetScheduleInputSchema) {
const { eventTypeSlug, usernameList } = input;
let eventTypeId = input.eventTypeId;
const { eventTypeSlug, usernameList, isTeamEvent } = input;
const eventTypeId =
input.eventTypeId ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(await getEventTypeId({
slug: usernameList?.[0],
eventTypeSlug: eventTypeSlug,
isTeamEvent,
}));
if (eventTypeId === undefined && eventTypeSlug && usernameList && usernameList.length === 1) {
// If we only have the slug and usernameList, we need to get the id first
const username = usernameList[0];
const userId = await getUserIdFromUsername(username);
let teamId;
if (!userId) {
teamId = await getTeamIdFromSlug(username);
if (!teamId) {
throw new TRPCError({
message: "User or team not found",
code: "NOT_FOUND",
});
}
}
const eventType = await prisma.eventType.findFirst({
where: {
slug: eventTypeSlug,
...(teamId ? { teamId } : {}),
...(userId ? { userId } : {}),
},
select: {
id: true,
},
});
if (!eventType) {
throw new TRPCError({ code: "NOT_FOUND" });
}
eventTypeId = eventType.id;
if (!eventTypeId) {
return null;
}
const eventType = await prisma.eventType.findUnique({
@ -165,7 +176,7 @@ export async function getEventType(input: TGetScheduleInputSchema) {
},
});
if (!eventType) {
return eventType;
return null;
}
return {