fix: /bookings get handler (#19369)
* fix: sorting on /bookings * fix type erorr * add filters to logging * fix type error * filter with attendee emails * clean up filters * add test from #19376 * fix e2e tests * split ORs into smaller queries * add missing imports
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
import { expect } from "@playwright/test";
|
||||
|
||||
import prisma from "@calcom/prisma";
|
||||
import { BookingStatus } from "@calcom/prisma/client";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
|
||||
import { createTeamEventType } from "./fixtures/users";
|
||||
import type { Fixtures } from "./lib/fixtures";
|
||||
import { test } from "./lib/fixtures";
|
||||
import { setupManagedEvent } from "./lib/testUtils";
|
||||
@@ -207,6 +210,129 @@ test.describe("Bookings", () => {
|
||||
webhookReceiver.close();
|
||||
});
|
||||
});
|
||||
|
||||
test("People filter includes bookings where filtered person is attendee", async ({
|
||||
page,
|
||||
users,
|
||||
bookings,
|
||||
}) => {
|
||||
const firstUser = await users.create(
|
||||
{ name: "First" },
|
||||
{
|
||||
hasTeam: true,
|
||||
teamRole: MembershipRole.ADMIN,
|
||||
}
|
||||
);
|
||||
const teamId = (await firstUser.getFirstTeamMembership()).teamId;
|
||||
const secondUser = await users.create({ name: "Second" });
|
||||
const thirdUser = await users.create({ name: "Third" });
|
||||
// Add teammates to the team
|
||||
await prisma.membership.createMany({
|
||||
data: [
|
||||
{
|
||||
teamId: teamId,
|
||||
userId: secondUser.id,
|
||||
role: MembershipRole.MEMBER,
|
||||
accepted: true,
|
||||
},
|
||||
{
|
||||
teamId: teamId,
|
||||
userId: thirdUser.id,
|
||||
role: MembershipRole.MEMBER,
|
||||
accepted: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
const teamEvent = await createTeamEventType(
|
||||
{ id: firstUser.id },
|
||||
{ id: teamId },
|
||||
{ teamEventSlug: "team-event-slug" }
|
||||
);
|
||||
|
||||
//Create a TeamEventType booking where ThirdUser is attendee
|
||||
const thirdUserAttendeeTeamEventBookingFixture = await createBooking({
|
||||
title: "ThirdUser is Attendee for TeamEvent",
|
||||
bookingsFixture: bookings,
|
||||
relativeDate: 6,
|
||||
organizer: firstUser,
|
||||
organizerEventType: teamEvent,
|
||||
attendees: [{ name: "Third", email: thirdUser.email, timeZone: "Europe/Berlin" }],
|
||||
});
|
||||
const thirdUserAttendeeTeamEvent = await thirdUserAttendeeTeamEventBookingFixture.self();
|
||||
|
||||
//Create a IndividualEventType booking where ThirdUser,SecondUser are attendees and FirstUser is organizer
|
||||
const thirdUserAttendeeIndividualBookingFixture = await createBooking({
|
||||
title: "ThirdUser is Attendee and FirstUser is Organizer",
|
||||
bookingsFixture: bookings,
|
||||
relativeDate: 3,
|
||||
organizer: firstUser,
|
||||
organizerEventType: firstUser.eventTypes[0],
|
||||
attendees: [
|
||||
{ name: "Third", email: thirdUser.email, timeZone: "Europe/Berlin" },
|
||||
{ name: "Second", email: secondUser.email, timeZone: "Europe/Berlin" },
|
||||
],
|
||||
});
|
||||
const thirdUserAttendeeIndividualBooking = await thirdUserAttendeeIndividualBookingFixture.self();
|
||||
|
||||
//Create a IndividualEventType booking where ThirdUser is organizer and FirstUser,SecondUser are attendees
|
||||
const thirdUserOrganizerBookingFixture = await createBooking({
|
||||
title: "ThirdUser is Organizer and FirstUser is Attendee",
|
||||
bookingsFixture: bookings,
|
||||
organizer: thirdUser,
|
||||
relativeDate: 2,
|
||||
organizerEventType: thirdUser.eventTypes[0],
|
||||
attendees: [
|
||||
{ name: "First", email: firstUser.email, timeZone: "Europe/Berlin" },
|
||||
{ name: "Second", email: secondUser.email, timeZone: "Europe/Berlin" },
|
||||
],
|
||||
});
|
||||
const thirdUserOrganizerBooking = await thirdUserOrganizerBookingFixture.self();
|
||||
|
||||
//Create a booking where FirstUser is organizer and SecondUser is attendee
|
||||
await createBooking({
|
||||
title: "FirstUser is Organizer and SecondUser is Attendee",
|
||||
bookingsFixture: bookings,
|
||||
organizer: firstUser,
|
||||
relativeDate: 4,
|
||||
organizerEventType: firstUser.eventTypes[0],
|
||||
attendees: [{ name: "Second", email: secondUser.email, timeZone: "Europe/Berlin" }],
|
||||
});
|
||||
|
||||
//admin login
|
||||
//Select 'ThirdUser' in people filter
|
||||
await firstUser.apiLogin();
|
||||
await Promise.all([
|
||||
page.waitForResponse((response) => /\/api\/trpc\/bookings\/get.*/.test(response.url())),
|
||||
page.waitForResponse((response) => /\/api\/trpc\/bookings\/get.*/.test(response.url())),
|
||||
page.goto(`/bookings/upcoming?status=upcoming&userIds=${thirdUser.id}`),
|
||||
]);
|
||||
|
||||
//expect only 3 bookings (out of 4 total) to be shown in list.
|
||||
//where ThirdUser is either organizer or attendee
|
||||
const upcomingBookingsTable = page.locator('[data-testid="upcoming-bookings"]');
|
||||
const bookingListItems = upcomingBookingsTable.locator('[data-testid="booking-item"]');
|
||||
const bookingListCount = await bookingListItems.count();
|
||||
expect(bookingListCount).toBe(3);
|
||||
|
||||
//verify with the booking titles
|
||||
const firstUpcomingBooking = bookingListItems.nth(0);
|
||||
await expect(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
firstUpcomingBooking.locator(`text=${thirdUserOrganizerBooking!.title}`)
|
||||
).toBeVisible();
|
||||
|
||||
const secondUpcomingBooking = bookingListItems.nth(1);
|
||||
await expect(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
secondUpcomingBooking.locator(`text=${thirdUserAttendeeIndividualBooking!.title}`)
|
||||
).toBeVisible();
|
||||
|
||||
const thirdUpcomingBooking = bookingListItems.nth(2);
|
||||
await expect(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
thirdUpcomingBooking.locator(`text=${thirdUserAttendeeTeamEvent!.title}`)
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
async function createBooking({
|
||||
|
||||
@@ -86,7 +86,7 @@ const createTeamWorkflow = async (user: { id: number }, team: { id: number }) =>
|
||||
});
|
||||
};
|
||||
|
||||
const createTeamEventType = async (
|
||||
export const createTeamEventType = async (
|
||||
user: { id: number },
|
||||
team: { id: number },
|
||||
scenario?: {
|
||||
|
||||
@@ -48,17 +48,6 @@ export const getHandler = async ({ ctx, input }: GetOptions) => {
|
||||
};
|
||||
};
|
||||
|
||||
const set = new Set();
|
||||
const getUniqueBookings = <T extends { uid: string }>(arr: T[]) => {
|
||||
const unique = arr.filter((booking) => {
|
||||
const duplicate = set.has(booking.uid);
|
||||
set.add(booking.uid);
|
||||
return !duplicate;
|
||||
});
|
||||
set.clear();
|
||||
return unique;
|
||||
};
|
||||
|
||||
export async function getBookings({
|
||||
user,
|
||||
prisma,
|
||||
@@ -76,154 +65,6 @@ export async function getBookings({
|
||||
take: number;
|
||||
skip: number;
|
||||
}) {
|
||||
const bookingWhereInputFilters: Record<string, Prisma.BookingWhereInput> = {};
|
||||
|
||||
if (filters?.teamIds && filters.teamIds.length > 0) {
|
||||
bookingWhereInputFilters.teamIds = {
|
||||
AND: [
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
eventType: {
|
||||
teamId: { in: filters.teamIds },
|
||||
},
|
||||
},
|
||||
{
|
||||
eventType: {
|
||||
parent: {
|
||||
teamId: { in: filters.teamIds },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.userIds && filters.userIds.length > 0) {
|
||||
bookingWhereInputFilters.userIds = {
|
||||
AND: [
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
eventType: {
|
||||
hosts: {
|
||||
some: {
|
||||
userId: {
|
||||
in: filters.userIds,
|
||||
},
|
||||
isFixed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
userId: {
|
||||
in: filters.userIds,
|
||||
},
|
||||
},
|
||||
{
|
||||
eventType: {
|
||||
users: {
|
||||
some: {
|
||||
id: {
|
||||
in: filters.userIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.eventTypeIds && filters.eventTypeIds.length > 0) {
|
||||
bookingWhereInputFilters.eventTypeIds = {
|
||||
AND: [
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
eventTypeId: {
|
||||
in: filters.eventTypeIds,
|
||||
},
|
||||
},
|
||||
{
|
||||
eventType: {
|
||||
parent: {
|
||||
id: {
|
||||
in: filters.eventTypeIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.attendeeEmail) {
|
||||
bookingWhereInputFilters.attendeeEmail = {
|
||||
attendees: {
|
||||
some: {
|
||||
email: filters.attendeeEmail.trim(),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.attendeeName) {
|
||||
bookingWhereInputFilters.attendeeName = {
|
||||
attendees: {
|
||||
some: {
|
||||
name: filters.attendeeName.trim(),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.afterStartDate) {
|
||||
bookingWhereInputFilters.afterStartDate = {
|
||||
startTime: {
|
||||
gte: dayjs.utc(filters.afterStartDate).toDate(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.beforeEndDate) {
|
||||
bookingWhereInputFilters.beforeEndDate = {
|
||||
endTime: {
|
||||
lte: dayjs.utc(filters.beforeEndDate).toDate(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.afterUpdatedDate) {
|
||||
bookingWhereInputFilters.afterUpdatedDate = {
|
||||
updatedAt: {
|
||||
gte: dayjs.utc(filters.afterUpdatedDate).toDate(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (filters?.beforeUpdatedDate) {
|
||||
bookingWhereInputFilters.beforeUpdatedDate = {
|
||||
updatedAt: {
|
||||
lte: dayjs.utc(filters.beforeUpdatedDate).toDate(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const filtersCombined: Prisma.BookingWhereInput[] = !filters
|
||||
? []
|
||||
: Object.keys(filters)
|
||||
.map((key) => bookingWhereInputFilters[key])
|
||||
// On prisma 5.4.2 passing undefined to where "AND" causes an error
|
||||
.filter(Boolean);
|
||||
|
||||
const bookingSelect = {
|
||||
...bookingMinimalSelect,
|
||||
uid: true,
|
||||
@@ -326,123 +167,168 @@ export async function getBookings({
|
||||
};
|
||||
|
||||
const [
|
||||
// Querying 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,
|
||||
bookingsQueryManagedEvents,
|
||||
bookingsQueryOrganizationMembers,
|
||||
bookingsQuerySeatReference,
|
||||
//////////////////////////
|
||||
|
||||
recurringInfoBasic,
|
||||
recurringInfoExtended,
|
||||
// We need all promises to be successful, so we are not using Promise.allSettled
|
||||
eventTypeIdsFromTeamIdsFilter,
|
||||
{ attendeeEmailsFromUserIdsFilter, eventTypeIdsFromUserIdsFilter },
|
||||
eventTypeIdsFromEventTypeIdsFilter,
|
||||
eventTypeIdsWhereUserIsAdminOrOwener,
|
||||
userIdsWhereUserIsOrgAdminOrOwener,
|
||||
] = await Promise.all([
|
||||
prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
userId: user.id,
|
||||
getEventTypeIdsFromTeamIdsFilter(prisma, filters?.teamIds),
|
||||
getIdsFromUserIdsFilter(prisma, user.email, filters?.userIds),
|
||||
getEventTypeIdsFromEventTypeIdsFilter(prisma, filters?.eventTypeIds),
|
||||
getEventTypeIdsWhereUserIsAdminOrOwner(prisma, membershipConditionWhereUserIsAdminOwner),
|
||||
getUserIdsWhereUserIsOrgAdminOrOwner(prisma, membershipConditionWhereUserIsAdminOwner),
|
||||
]);
|
||||
|
||||
const plainBookings = await prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
userId: user.id,
|
||||
},
|
||||
{
|
||||
attendees: {
|
||||
some: {
|
||||
email: user.email,
|
||||
},
|
||||
},
|
||||
],
|
||||
AND: [passedBookingsStatusFilter, ...filtersCombined],
|
||||
},
|
||||
orderBy,
|
||||
take: take + 1,
|
||||
skip,
|
||||
}),
|
||||
prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
attendees: {
|
||||
some: {
|
||||
},
|
||||
{
|
||||
eventTypeId: {
|
||||
in: eventTypeIdsWhereUserIsAdminOrOwener,
|
||||
},
|
||||
},
|
||||
{
|
||||
userId: {
|
||||
in: userIdsWhereUserIsOrgAdminOrOwener,
|
||||
},
|
||||
},
|
||||
{
|
||||
seatsReferences: {
|
||||
some: {
|
||||
attendee: {
|
||||
email: user.email,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
AND: [passedBookingsStatusFilter, ...filtersCombined],
|
||||
},
|
||||
orderBy,
|
||||
take: take + 1,
|
||||
skip,
|
||||
}),
|
||||
prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
eventType: {
|
||||
team: {
|
||||
members: membershipConditionWhereUserIsAdminOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
AND: [passedBookingsStatusFilter, ...filtersCombined],
|
||||
},
|
||||
orderBy,
|
||||
take: take + 1,
|
||||
skip,
|
||||
}),
|
||||
prisma.booking.findMany({
|
||||
where: {
|
||||
eventType: {
|
||||
parent: {
|
||||
team: {
|
||||
members: membershipConditionWhereUserIsAdminOwner,
|
||||
},
|
||||
},
|
||||
},
|
||||
AND: [passedBookingsStatusFilter, ...filtersCombined],
|
||||
},
|
||||
orderBy,
|
||||
take: take + 1,
|
||||
skip,
|
||||
}),
|
||||
prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
user: {
|
||||
teams: {
|
||||
some: {
|
||||
team: {
|
||||
isOrganization: true,
|
||||
members: membershipConditionWhereUserIsAdminOwner,
|
||||
],
|
||||
AND: [
|
||||
passedBookingsStatusFilter,
|
||||
...(eventTypeIdsFromTeamIdsFilter
|
||||
? [
|
||||
{
|
||||
eventTypeId: {
|
||||
in: eventTypeIdsFromTeamIdsFilter,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(filters?.userIds && filters.userIds.length > 0 && eventTypeIdsFromUserIdsFilter
|
||||
? [
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
userId: {
|
||||
in: filters.userIds,
|
||||
},
|
||||
},
|
||||
...(eventTypeIdsFromUserIdsFilter?.length
|
||||
? [
|
||||
{
|
||||
eventTypeId: {
|
||||
in: eventTypeIdsFromUserIdsFilter,
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(attendeeEmailsFromUserIdsFilter?.length
|
||||
? [
|
||||
{
|
||||
attendees: {
|
||||
some: {
|
||||
email: {
|
||||
in: attendeeEmailsFromUserIdsFilter,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(eventTypeIdsFromEventTypeIdsFilter
|
||||
? [
|
||||
{
|
||||
eventTypeId: { in: eventTypeIdsFromEventTypeIdsFilter },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(filters?.attendeeEmail
|
||||
? [
|
||||
{
|
||||
attendees: { some: { email: filters.attendeeEmail.trim() } },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(filters?.attendeeName
|
||||
? [
|
||||
{
|
||||
attendees: { some: { name: filters.attendeeName.trim() } },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(filters?.afterStartDate
|
||||
? [
|
||||
{
|
||||
startTime: {
|
||||
gte: dayjs.utc(filters.afterStartDate).toDate(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
AND: [passedBookingsStatusFilter, ...filtersCombined],
|
||||
},
|
||||
orderBy,
|
||||
take: take + 1,
|
||||
skip,
|
||||
}),
|
||||
prisma.booking.findMany({
|
||||
where: {
|
||||
OR: [
|
||||
{
|
||||
seatsReferences: {
|
||||
some: {
|
||||
attendee: {
|
||||
email: user.email,
|
||||
]
|
||||
: []),
|
||||
...(filters?.beforeEndDate
|
||||
? [
|
||||
{
|
||||
endTime: {
|
||||
lte: dayjs.utc(filters.beforeEndDate).toDate(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
AND: [passedBookingsStatusFilter, ...filtersCombined],
|
||||
},
|
||||
orderBy,
|
||||
take: take + 1,
|
||||
skip,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...(filters?.afterUpdatedDate
|
||||
? [
|
||||
{
|
||||
updatedAt: {
|
||||
gte: dayjs.utc(filters.afterUpdatedDate).toDate(),
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(filters?.beforeUpdatedDate
|
||||
? [
|
||||
{
|
||||
updatedAt: {
|
||||
lte: dayjs.utc(filters.beforeUpdatedDate).toDate(),
|
||||
},
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
select: bookingSelect,
|
||||
orderBy,
|
||||
take: take + 1,
|
||||
skip,
|
||||
});
|
||||
|
||||
const [
|
||||
recurringInfoBasic,
|
||||
recurringInfoExtended,
|
||||
// We need all promises to be successful, so we are not using Promise.allSettled
|
||||
] = await Promise.all([
|
||||
prisma.booking.groupBy({
|
||||
by: ["recurringEventId"],
|
||||
_min: {
|
||||
@@ -503,16 +389,6 @@ export async function getBookings({
|
||||
}
|
||||
);
|
||||
|
||||
const plainBookings = getUniqueBookings(
|
||||
// It's going to mess up the orderBy as we are concatenating independent queries results
|
||||
bookingsQueryUserId
|
||||
.concat(bookingsQueryAttendees)
|
||||
.concat(bookingsQueryTeamMember)
|
||||
.concat(bookingsQueryManagedEvents)
|
||||
.concat(bookingsQueryOrganizationMembers)
|
||||
.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
|
||||
|
||||
@@ -520,28 +396,15 @@ export async function getBookings({
|
||||
`fetching all bookings for ${user.id}`,
|
||||
safeStringify({
|
||||
ids: plainBookings.map((booking) => booking.id),
|
||||
filters,
|
||||
orderBy,
|
||||
filtersCombined,
|
||||
take,
|
||||
skip,
|
||||
})
|
||||
);
|
||||
|
||||
const bookings = await Promise.all(
|
||||
(
|
||||
await prisma.booking.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: plainBookings
|
||||
.sort((a, b) => a.startTime.getTime() - b.startTime.getTime())
|
||||
.map((booking) => booking.id),
|
||||
},
|
||||
},
|
||||
select: bookingSelect,
|
||||
// We need to get the sorted bookings here as well because plainBookings array is not correctly sorted
|
||||
orderBy,
|
||||
})
|
||||
).map(async (booking) => {
|
||||
plainBookings.map(async (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);
|
||||
@@ -564,3 +427,199 @@ export async function getBookings({
|
||||
);
|
||||
return { bookings, recurringInfo };
|
||||
}
|
||||
|
||||
async function getEventTypeIdsFromTeamIdsFilter(prisma: PrismaClient, teamIds?: number[]) {
|
||||
if (!teamIds || teamIds.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [directTeamEventTypeIds, parentTeamEventTypeIds] = await Promise.all([
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
teamId: { in: teamIds },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
parent: {
|
||||
teamId: { in: teamIds },
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
]);
|
||||
|
||||
return Array.from(new Set([...directTeamEventTypeIds, ...parentTeamEventTypeIds]));
|
||||
}
|
||||
|
||||
async function getIdsFromUserIdsFilter(prisma: PrismaClient, userEmail: string, userIds?: number[]) {
|
||||
if (!userIds || userIds.length === 0) {
|
||||
return {
|
||||
eventTypeIdsFromUserIdsFilter: undefined,
|
||||
attendeeEmailsFromUserIdsFilter: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const [attendeeEmailsFromUserIdsFilter, eventTypeIdsFromHostsFilter, eventTypeIdsFromUsersFilter] =
|
||||
await Promise.all([
|
||||
prisma.user
|
||||
.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: userIds,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
email: true,
|
||||
},
|
||||
})
|
||||
// Include booking if current user is an attendee, regardless of user ID filter
|
||||
.then((users) => users.map((user) => user.email).concat([userEmail])),
|
||||
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
hosts: {
|
||||
some: {
|
||||
userId: {
|
||||
in: userIds,
|
||||
},
|
||||
isFixed: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
users: {
|
||||
some: {
|
||||
id: {
|
||||
in: userIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
]);
|
||||
|
||||
const eventTypeIdsFromUserIdsFilter = Array.from(
|
||||
new Set([...eventTypeIdsFromHostsFilter, ...eventTypeIdsFromUsersFilter])
|
||||
);
|
||||
|
||||
return { attendeeEmailsFromUserIdsFilter, eventTypeIdsFromUserIdsFilter };
|
||||
}
|
||||
|
||||
async function getEventTypeIdsFromEventTypeIdsFilter(prisma: PrismaClient, eventTypeIds?: number[]) {
|
||||
if (!eventTypeIds || eventTypeIds.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const [directEventTypeIds, parentEventTypeIds] = await Promise.all([
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
id: { in: eventTypeIds },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
parent: {
|
||||
id: {
|
||||
in: eventTypeIds,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
]);
|
||||
|
||||
return Array.from(new Set([...directEventTypeIds, ...parentEventTypeIds]));
|
||||
}
|
||||
|
||||
async function getEventTypeIdsWhereUserIsAdminOrOwner(
|
||||
prisma: PrismaClient,
|
||||
membershipCondition: PrismaClientType.MembershipListRelationFilter
|
||||
) {
|
||||
const [directTeamEventTypeIds, parentTeamEventTypeIds] = await Promise.all([
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
team: {
|
||||
members: membershipCondition,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
|
||||
prisma.eventType
|
||||
.findMany({
|
||||
where: {
|
||||
parent: {
|
||||
team: {
|
||||
members: membershipCondition,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
.then((eventTypes) => eventTypes.map((eventType) => eventType.id)),
|
||||
]);
|
||||
|
||||
return Array.from(new Set([...directTeamEventTypeIds, ...parentTeamEventTypeIds]));
|
||||
}
|
||||
|
||||
async function getUserIdsWhereUserIsOrgAdminOrOwner(
|
||||
prisma: PrismaClient,
|
||||
membershipCondition: PrismaClientType.MembershipListRelationFilter
|
||||
) {
|
||||
return (
|
||||
await prisma.user.findMany({
|
||||
where: {
|
||||
teams: {
|
||||
some: {
|
||||
team: {
|
||||
isOrganization: true,
|
||||
members: membershipCondition,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
})
|
||||
).map((user) => user.id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user