revert: "fix: view team bookings (#14079)" (#14561)

This reverts commit f86c448359.
This commit is contained in:
Keith Williams
2024-04-13 00:53:05 +00:00
committed by GitHub
parent becad5ee67
commit fd193e651d
5 changed files with 82 additions and 828 deletions
-209
View File
@@ -1,15 +1,9 @@
import { expect } from "@playwright/test";
import { randomString } from "@calcom/lib/random";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/client";
import { SchedulingType } from "@calcom/prisma/enums";
import { createTeamEventType } from "./fixtures/users";
import type { Fixtures } from "./lib/fixtures";
import { test } from "./lib/fixtures";
import { assertBookingVisibleFor, bookTeamEvent, bookUserEvent } from "./lib/testUtils";
test.afterEach(({ users }) => users.deleteAll());
@@ -67,137 +61,6 @@ test.describe("Bookings", () => {
secondUpcomingBooking.locator(`text=${bookingWhereFirstUserIsOrganizer!.title}`)
).toBeVisible();
});
test("collective eventType booking should be visible to team admin", async ({ page, users }) => {
const { owner1, owner2, commonUser, team1_teammate1 } = await createTeams(users);
const { team: team1 } = await owner1.getFirstTeamMembership();
const scenario = {
schedulingType: SchedulingType.COLLECTIVE,
teamEventTitle: `collective-team-event`,
teamEventSlug: slugify(`collective-team-event-${randomString(5)}`),
};
const eventType = await createTeamEventType(owner1, team1, scenario);
const { id: eventId, slug: evnetSlug } = eventType;
await prisma.host.createMany({
data: [
{
userId: commonUser.id,
eventTypeId: eventId,
isFixed: true,
},
{
userId: team1_teammate1.id,
eventTypeId: eventId,
isFixed: true,
},
],
});
// to test if booking is visible to team admin/owner even if he is not part of the booking
await prisma.host.deleteMany({
where: {
userId: owner1.id,
eventTypeId: eventId,
},
});
await bookTeamEvent(page, team1, eventType);
// booking should be visible for the team host even though he is not part of the booking
await assertBookingVisibleFor(owner1, page, eventType, true);
// booking should not be visible for other team host
await assertBookingVisibleFor(owner2, page, eventType, false);
// booking should be visible for commonUser as he is part of the booking
await assertBookingVisibleFor(commonUser, page, eventType, true);
// booking should be visible for team1_teammate1 as he is part of the booking
await assertBookingVisibleFor(team1_teammate1, page, eventType, true);
});
test("round-robin eventType booking should be visible to team admin", async ({ page, users }) => {
const { owner1, owner2, commonUser, team1_teammate1 } = await createTeams(users);
const { team: team1 } = await owner1.getFirstTeamMembership();
const scenario = {
schedulingType: SchedulingType.ROUND_ROBIN,
teamEventTitle: `round-robin-team-event`,
teamEventSlug: slugify(`round-robin-team-event-${randomString(5)}`),
};
const eventType = await createTeamEventType(owner1, team1, scenario);
const { id: eventId } = eventType;
await prisma.host.createMany({
data: [
{
userId: commonUser.id,
eventTypeId: eventId,
},
{
userId: team1_teammate1.id,
eventTypeId: eventId,
},
],
});
await prisma.host.deleteMany({
where: {
userId: owner1.id,
eventTypeId: eventId,
},
});
await bookTeamEvent(page, team1, eventType);
// booking should be visible for the team host even though he is not part of the booking
await assertBookingVisibleFor(owner1, page, eventType, true);
// bookings should not be visible for other team host
await assertBookingVisibleFor(owner2, page, eventType, false);
});
test("managed eventType booking should be visible to team admin", async ({ page, users }) => {
const { owner1, owner2, commonUser, team1_teammate1 } = await createTeams(users);
const { team: team1 } = await owner1.getFirstTeamMembership();
await owner1.apiLogin();
await page.goto(`/event-types?dialog=new&eventPage=team%2F${team1.slug}&teamId=${team1.id}`);
await page.getByTestId("managed-event-type").click();
await page.getByTestId("event-type-quick-chat").click();
await page.getByTestId("event-type-quick-chat").fill("managed-event");
await page.getByRole("button", { name: "Continue" }).click();
await page.getByTestId("vertical-tab-assignment").click();
await page.getByRole("combobox", { name: "assignment-dropdown" }).fill("commonUser");
await page.getByRole("combobox", { name: "assignment-dropdown" }).press("Enter");
await page.getByRole("combobox", { name: "assignment-dropdown" }).fill("team1_teammate1");
await page.getByRole("combobox", { name: "assignment-dropdown" }).press("Enter");
await page.getByTestId("update-eventtype").click();
const eventType = await owner1.getFirstTeamEvent(team1.id);
await bookUserEvent(page, commonUser, eventType);
// booking should be visible for the team host even though he is not part of the booking
await assertBookingVisibleFor(owner1, page, eventType, true);
// booking should not be visible for other team host
await assertBookingVisibleFor(owner2, page, eventType, false);
// booking should be visible for commonUser as he is part of the booking
await assertBookingVisibleFor(commonUser, page, eventType, true);
// booking should not be visible for team1_teammate1 as we booked for commonUser
await assertBookingVisibleFor(team1_teammate1, page, eventType, false);
});
test("individual team members booking should not be visible to team admin", async ({ page, users }) => {
const { owner1, owner2, commonUser, team1_teammate1 } = await createTeams(users);
const eventType = await commonUser.getFirstEventAsOwner();
await bookUserEvent(page, commonUser, eventType);
// non-team bookings should not be visible for the team host
await assertBookingVisibleFor(owner1, page, eventType, false);
// non-team bookings should not be visible for other team host
await assertBookingVisibleFor(owner2, page, eventType, false);
// booking should be visible for commonUser as he is part of the booking
await assertBookingVisibleFor(commonUser, page, eventType, true);
});
});
});
@@ -248,75 +111,3 @@ async function createBooking({
},
});
}
const createTeams = async (userFixture: Fixtures["users"]) => {
const owner1 = await userFixture.create({ name: "team-owner-1" });
const owner2 = await userFixture.create({ name: "team-owner-2" });
const commonUser = await userFixture.create({ name: "commonUser" });
const team1_teammate1 = await userFixture.create({ name: "team1_teammate1" });
const team2_teammate1 = await userFixture.create({ name: "team2_teammate1" });
const teamOne = await prisma.team.create({
data: {
name: "bookings-test-team-1",
slug: slugify(`bookings-test-team-2-${randomString(5)}`),
},
});
const teamTwo = await prisma.team.create({
data: {
name: "bookings-test-team-2",
slug: slugify(`bookings-test-team-2-${randomString(5)}`),
},
});
// create memberships
await prisma.membership.createMany({
data: [
{
userId: owner1.id,
teamId: teamOne.id,
accepted: true,
role: "OWNER",
},
{
userId: commonUser.id,
teamId: teamOne.id,
accepted: true,
role: "MEMBER",
},
{
userId: team1_teammate1.id,
teamId: teamOne.id,
accepted: true,
role: "MEMBER",
},
{
userId: owner2.id,
teamId: teamTwo.id,
accepted: true,
role: "OWNER",
},
{
userId: commonUser.id,
teamId: teamTwo.id,
accepted: true,
role: "MEMBER",
},
{
userId: team2_teammate1.id,
teamId: teamTwo.id,
accepted: true,
role: "MEMBER",
},
],
});
return {
owner1,
owner2,
commonUser,
team1_teammate1,
team2_teammate1,
teamOne,
teamTwo,
};
};
+1 -1
View File
@@ -58,7 +58,7 @@ const createTeamWorkflow = async (user: { id: number }, team: { id: number }) =>
});
};
export const createTeamEventType = async (
const createTeamEventType = async (
user: { id: number },
team: { id: number },
scenario?: {
-51
View File
@@ -1,5 +1,4 @@
import type { Frame, Page } from "@playwright/test";
import type { APIResponse } from "@playwright/test";
import { expect } from "@playwright/test";
import { createHash } from "crypto";
import EventEmitter from "events";
@@ -372,53 +371,3 @@ export async function doOnOrgDomain(
// When App directory is there, this is the 404 page text. We should work on fixing the 404 page as it changed due to app directory.
export const NotFoundPageTextAppDir = "This page does not exist.";
// export const NotFoundPageText = "ERROR 404";
export type Team = {
id: number;
slug: string | null;
name: string;
};
export type EventType = {
id: number;
title: string;
slug: string;
};
export type User = {
id: number;
name: string | null;
username: string | null;
};
export async function bookTeamEvent(page: Page, team: Team, eventType: EventType) {
await page.goto(`/team/${team.slug}/${eventType.slug}/`);
await bookEventOnThisPage(page);
const bookingTitle = `${eventType.title} between ${team.name} and ${testName}`;
await assertBookingIsCorrect(page, bookingTitle);
}
export async function bookUserEvent(page: Page, user: User, eventType: EventType) {
await page.goto(`/${user.username}/${eventType.slug}/`);
await bookEventOnThisPage(page);
const bookingTitle = `${eventType.title} between ${user.name} and ${testName}`;
await assertBookingIsCorrect(page, bookingTitle);
}
export async function assertBookingIsCorrect(page: Page, bookingTitle: string) {
await expect(page.locator("[data-testid=booking-title]")).toHaveText(bookingTitle);
// The booker should be in the attendee list
await expect(page.locator(`[data-testid="attendee-name-${testName}"]`)).toHaveText(testName);
}
export const assertBookingVisibleFor = async (
user: { apiLogin: () => Promise<APIResponse> },
pageFixture: Fixtures["page"],
eventType: EventType,
shouldBeVisible: boolean
) => {
await user.apiLogin();
await pageFixture.goto(`/bookings/upcoming`);
const upcomingBookingList = pageFixture.locator('[data-testid="booking-item"]');
shouldBeVisible
? await expect(upcomingBookingList.locator(`text=${eventType.title}`)).toBeVisible()
: await expect(upcomingBookingList.locator(`text=${eventType.title}`)).toBeHidden();
};
@@ -1,391 +0,0 @@
import type { Page } from "@playwright/test";
import { expect } from "@playwright/test";
import { randomString } from "@calcom/lib/random";
import slugify from "@calcom/lib/slugify";
import prisma from "@calcom/prisma";
import { MembershipRole, SchedulingType } from "@calcom/prisma/enums";
import { createTeamEventType } from "../fixtures/users";
import { test } from "../lib/fixtures";
import type { Fixtures } from "../lib/fixtures";
import {
assertBookingVisibleFor,
bookTimeSlot,
doOnOrgDomain,
NotFoundPageTextAppDir,
selectFirstAvailableTimeSlotNextMonth,
testName,
} from "../lib/testUtils";
test.describe("Bookings list for organizations", () => {
test.afterEach(({ orgs, users }) => {
orgs.deleteAll();
users.deleteAll();
});
test.describe("Team Event", () => {
test("Collective EventType booking list", async ({ page, users, orgs }) => {
const { org, orgOwner, orgAdmin, team1owner, team2owner, commonUser, team1_teammate1 } =
await createOrg(orgs, users);
const { team: team1 } = await team1owner.getFirstTeamMembership();
const scenario = {
schedulingType: SchedulingType.COLLECTIVE,
teamEventTitle: `collective-team-event`,
teamEventSlug: slugify(`collective-team-event-${randomString(5)}`),
};
const eventType = await createTeamEventType(team1owner, team1, scenario);
const { id: eventId, slug: evnetSlug } = eventType;
await prisma.host.createMany({
data: [
{
userId: commonUser.id,
eventTypeId: eventId,
isFixed: true,
},
{
userId: team1_teammate1.id,
eventTypeId: eventId,
isFixed: true,
},
],
});
// to test if booking is visible to team admin/owner even if he is not part of the booking
await prisma.host.deleteMany({
where: {
userId: team1owner.id,
eventTypeId: eventId,
},
});
await expectPageToBeNotFound({ page, url: `/team/${team1.slug}/${eventType.slug}` });
await doOnOrgDomain(
{
orgSlug: org.slug,
page,
},
async () => {
await bookTeamEvent({ page, team: team1, event: eventType });
}
);
// booking should be visible for the Org OWNER even though he is not part of the booking
await assertBookingVisibleFor(orgOwner, page, eventType, true);
// booking should be visible for the Org ADMIN even though he is not part of the booking
await assertBookingVisibleFor(orgAdmin, page, eventType, true);
// booking should be visible for the team host even though he is not part of the booking
await assertBookingVisibleFor(team1owner, page, eventType, true);
// booking should not be visible for other team host, because its a team1 team-event
await assertBookingVisibleFor(team2owner, page, eventType, false);
// booking should be visible for commonUser as he is part of the booking
await assertBookingVisibleFor(commonUser, page, eventType, true);
// booking should be visible for team1_teammate1 as he is part of the booking
await assertBookingVisibleFor(team1_teammate1, page, eventType, true);
});
test("Round Robin EventType booking list", async ({ page, users, orgs }) => {
const { org, orgOwner, orgAdmin, team1owner, team2owner, commonUser, team1_teammate1 } =
await createOrg(orgs, users);
const { team: team1 } = await team1owner.getFirstTeamMembership();
const scenario = {
schedulingType: SchedulingType.ROUND_ROBIN,
teamEventTitle: `round-robin-team-event`,
teamEventSlug: slugify(`round-robin-team-event-${randomString(5)}`),
};
const eventType = await createTeamEventType(team1owner, team1, scenario);
const { id: eventId } = eventType;
await prisma.host.createMany({
data: [
{
userId: commonUser.id,
eventTypeId: eventId,
},
{
userId: team1_teammate1.id,
eventTypeId: eventId,
},
],
});
await prisma.host.deleteMany({
where: {
userId: team1owner.id,
eventTypeId: eventId,
},
});
await expectPageToBeNotFound({ page, url: `/team/${team1.slug}/${eventType.slug}` });
await doOnOrgDomain(
{
orgSlug: org.slug,
page,
},
async () => {
await bookTeamEvent({ page, team: team1, event: eventType });
}
);
// booking should be visible for the Org OWNER even though he is not part of the booking
await assertBookingVisibleFor(orgOwner, page, eventType, true);
// booking should be visible for the Org ADMIN even though he is not part of the booking
await assertBookingVisibleFor(orgAdmin, page, eventType, true);
// booking should be visible for the team host even though he is not part of the booking
await assertBookingVisibleFor(team1owner, page, eventType, true);
// booking should not be visible for other team host
await assertBookingVisibleFor(team2owner, page, eventType, false);
});
test("Managed EventType booking list", async ({ page, users, orgs }) => {
const { org, orgOwner, orgAdmin, team1owner, team2owner, commonUser, team1_teammate1 } =
await createOrg(orgs, users);
const { team: team1 } = await team1owner.getFirstTeamMembership();
await team1owner.apiLogin();
await page.goto(`/event-types?dialog=new&eventPage=team%2F${team1.slug}&teamId=${team1.id}`);
await page.getByTestId("managed-event-type").click();
await page.getByTestId("event-type-quick-chat").click();
await page.getByTestId("event-type-quick-chat").fill("managed-event");
await page.getByRole("button", { name: "Continue" }).click();
await page.getByTestId("vertical-tab-assignment").click();
await page.getByRole("combobox", { name: "assignment-dropdown" }).fill("commonUser");
await page.getByRole("combobox", { name: "assignment-dropdown" }).press("Enter");
await page.getByRole("combobox", { name: "assignment-dropdown" }).fill("team1_teammate1");
await page.getByRole("combobox", { name: "assignment-dropdown" }).press("Enter");
await page.getByTestId("update-eventtype").click();
const eventType = await team1owner.getFirstTeamEvent(team1.id);
await expectPageToBeNotFound({ page, url: `/${commonUser.username}/${eventType.slug}` });
await doOnOrgDomain(
{
orgSlug: org.slug,
page,
},
async () => {
await bookUserEvent({ page, user: commonUser, event: eventType });
}
);
// booking should be visible for the Org OWNER even though he is not part of the booking
await assertBookingVisibleFor(orgOwner, page, eventType, true);
// booking should be visible for the Org ADMIN even though he is not part of the booking
await assertBookingVisibleFor(orgAdmin, page, eventType, true);
// booking should be visible for the team host even though he is not part of the booking
await assertBookingVisibleFor(team1owner, page, eventType, true);
// booking should not be visible for other team host
await assertBookingVisibleFor(team2owner, page, eventType, false);
});
});
test.describe("User Event", () => {
test("User personal booking list", async ({ page, users, orgs }) => {
const { org, orgOwner, orgAdmin, team1owner, team2owner, commonUser, team1_teammate1 } =
await createOrg(orgs, users);
const event = await commonUser.getFirstEventAsOwner();
await page.goto(`/${commonUser.username}/${event.slug}`);
// Shouldn't be servable on the non-org domain
await expect(page.locator(`text=${NotFoundPageTextAppDir}`)).toBeVisible();
await doOnOrgDomain(
{
orgSlug: org.slug,
page,
},
async () => {
await bookUserEvent({ page, user: commonUser, event });
}
);
// booking should be visible for the Org OWNER
await assertBookingVisibleFor(orgOwner, page, event, true);
// booking should be visible for the Org ADMIN
await assertBookingVisibleFor(orgAdmin, page, event, true);
// booking should be visible for the team host
await assertBookingVisibleFor(team1owner, page, event, true);
// booking should be visible for other team host because commonUser is part of both teams
await assertBookingVisibleFor(team2owner, page, event, true);
// booking should be visible for commonUser as he is part of the booking
await assertBookingVisibleFor(commonUser, page, event, true);
// booking should not be visible for team1_teammate1
await assertBookingVisibleFor(team1_teammate1, page, event, false);
});
});
});
async function bookUserEvent({
page,
user,
event,
}: {
page: Page;
user: {
username: string | null;
name: string | null;
};
event: { slug: string; title: string };
}) {
await page.goto(`/${user.username}/${event.slug}`);
await selectFirstAvailableTimeSlotNextMonth(page);
await bookTimeSlot(page);
await expect(page.getByTestId("success-page")).toBeVisible();
// The title of the booking
const BookingTitle = `${event.title} between ${user.name} and ${testName}`;
await expect(page.getByTestId("booking-title")).toHaveText(BookingTitle);
// The booker should be in the attendee list
await expect(page.getByTestId(`attendee-name-${testName}`)).toHaveText(testName);
}
async function bookTeamEvent({
page,
team,
event,
}: {
page: Page;
team: {
slug: string | null;
name: string | null;
};
event: { slug: string; title: string };
}) {
// Note that even though the default way to access a team booking in an organization is to not use /team in the URL, but it isn't testable with playwright as the rewrite is taken care of by Next.js config which can't handle on the fly org slug's handling
// So, we are using /team in the URL to access the team booking
// There are separate tests to verify that the next.config.js rewrites are working
// Also there are additional checkly tests that verify absolute e2e flow. They are in __checks__/organization.spec.ts
await page.goto(`/team/${team.slug}/${event.slug}`);
await selectFirstAvailableTimeSlotNextMonth(page);
await bookTimeSlot(page);
await expect(page.getByTestId("success-page")).toBeVisible();
// The title of the booking
const BookingTitle = `${event.title} between ${team.name} and ${testName}`;
await expect(page.getByTestId("booking-title")).toHaveText(BookingTitle);
// The booker should be in the attendee list
await expect(page.getByTestId(`attendee-name-${testName}`)).toHaveText(testName);
}
async function expectPageToBeNotFound({ page, url }: { page: Page; url: string }) {
await page.goto(`${url}`);
await expect(page.locator(`text=${NotFoundPageTextAppDir}`)).toBeVisible();
}
const createOrg = async (orgs: Fixtures["orgs"], userFixture: Fixtures["users"]) => {
const org = await orgs.create({
name: "TestOrg",
});
const orgOwner = await userFixture.create({
name: "org-owner",
organizationId: org.id,
roleInOrganization: MembershipRole.OWNER,
});
const orgAdmin = await userFixture.create({
name: "org-admin",
organizationId: org.id,
roleInOrganization: MembershipRole.ADMIN,
});
const team1owner = await userFixture.create({
name: "team-owner-1",
organizationId: org.id,
roleInOrganization: MembershipRole.MEMBER,
});
const team2owner = await userFixture.create({
name: "team-owner-2",
organizationId: org.id,
roleInOrganization: MembershipRole.MEMBER,
});
const commonUser = await userFixture.create({
name: "commonUser",
organizationId: org.id,
roleInOrganization: MembershipRole.MEMBER,
});
const team1_teammate1 = await userFixture.create({
name: "team1_teammate1",
organizationId: org.id,
roleInOrganization: MembershipRole.MEMBER,
});
const team2_teammate1 = await userFixture.create({
name: "team2_teammate1",
organizationId: org.id,
roleInOrganization: MembershipRole.MEMBER,
});
const teamOne = await prisma.team.create({
data: {
name: "bookings-test-team-1",
slug: slugify(`bookings-test-team-2-${randomString(5)}`),
parentId: org.id,
},
});
const teamTwo = await prisma.team.create({
data: {
name: "bookings-test-team-2",
slug: slugify(`bookings-test-team-2-${randomString(5)}`),
parentId: org.id,
},
});
// create memberships
await prisma.membership.createMany({
data: [
{
userId: team1owner.id,
teamId: teamOne.id,
accepted: true,
role: "OWNER",
},
{
userId: commonUser.id,
teamId: teamOne.id,
accepted: true,
role: "MEMBER",
},
{
userId: team1_teammate1.id,
teamId: teamOne.id,
accepted: true,
role: "MEMBER",
},
{
userId: team2owner.id,
teamId: teamTwo.id,
accepted: true,
role: "OWNER",
},
{
userId: commonUser.id,
teamId: teamTwo.id,
accepted: true,
role: "MEMBER",
},
{
userId: team2_teammate1.id,
teamId: teamTwo.id,
accepted: true,
role: "MEMBER",
},
],
});
return {
org,
orgOwner,
orgAdmin,
team1owner,
team2owner,
commonUser,
team1_teammate1,
team2_teammate1,
teamOne,
teamTwo,
};
};
@@ -40,12 +40,15 @@ export const getHandler = async ({ ctx, input }: GetOptions) => {
};
};
const getUniqueBookings = <T extends { id: number }>(arr: T[]) => {
const unique = new Set<number>();
arr.forEach((booking) => {
unique.add(booking.id);
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;
});
return Array.from(unique.values());
set.clear();
return unique;
};
export async function getBookings({
@@ -125,6 +128,7 @@ export async function getBookings({
],
},
};
const filtersCombined: Prisma.BookingWhereInput[] = !filters
? []
: Object.keys(filters)
@@ -192,171 +196,15 @@ export async function getBookings({
},
};
const userBookingsFilter: Prisma.BookingWhereInput = {
userId: user.id,
};
const userAttendingBookingsFilter: Prisma.BookingWhereInput = {
attendees: {
some: {
email: user.email,
},
},
};
// all (collective + round-robin) team bookings should be visible to team-owner and team-admins
const teamBookingsFilter: Prisma.BookingWhereInput = {
eventType: {
team: {
members: {
some: {
userId: user.id,
role: {
in: ["ADMIN", "OWNER"],
},
},
},
},
},
};
// all (collective + round-robin) team bookings should be visible to org-owner and org-admins
const organizationBookingsFilter: Prisma.BookingWhereInput = {
eventType: {
team: {
parent: {
members: {
some: {
userId: user.id,
role: {
in: ["ADMIN", "OWNER"],
},
},
},
},
},
},
};
// all managed team bookings should be visible to team-owner and team-admins
const teamManagedBookingsFilter: Prisma.BookingWhereInput = {
eventType: {
parent: {
team: {
members: {
some: {
userId: user.id,
role: {
in: ["ADMIN", "OWNER"],
},
},
},
},
},
},
};
// all managed team bookings should be visible to org-owner and org-admins
const organizationManagedBookingsFilter: Prisma.BookingWhereInput = {
eventType: {
parent: {
team: {
parent: {
members: {
some: {
userId: user.id,
role: {
in: ["ADMIN", "OWNER"],
},
},
},
},
},
},
},
};
// show personal booking to org-owner and org-admins
const organizationPersonalBookingsFilter: Prisma.BookingWhereInput = {
AND: [
{
user: {
teams: {
some: {
team: {
parentId: { gt: 0 },
},
},
},
},
},
{
OR: [
{
user: {
teams: {
some: {
team: {
parent: {
members: {
some: {
userId: user.id,
role: {
in: ["ADMIN", "OWNER"],
},
},
},
},
},
},
},
},
},
{
user: {
teams: {
some: {
team: {
members: {
some: {
userId: user.id,
role: {
in: ["ADMIN", "OWNER"],
},
},
},
},
},
},
},
},
],
},
// filter out all (collective + round-robin) event bookings
{
eventType: {
teamId: null,
},
},
// filter out all (managed) event bookings
{
eventType: {
parentId: null,
},
},
],
};
const seatsReferenceFilter: Prisma.BookingWhereInput = {
seatsReferences: {
some: {
attendee: {
email: user.email,
},
},
},
};
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
@@ -364,19 +212,73 @@ export async function getBookings({
prisma.booking.findMany({
where: {
OR: [
userBookingsFilter,
userAttendingBookingsFilter,
teamBookingsFilter,
organizationBookingsFilter,
teamManagedBookingsFilter,
organizationManagedBookingsFilter,
organizationPersonalBookingsFilter,
seatsReferenceFilter,
{
userId: user.id,
},
],
AND: [passedBookingsStatusFilter, ...filtersCombined],
},
orderBy,
take: take + 1,
skip,
}),
prisma.booking.findMany({
where: {
OR: [
{
attendees: {
some: {
email: user.email,
},
},
},
],
AND: [passedBookingsStatusFilter, ...filtersCombined],
},
orderBy,
take: take + 1,
skip,
}),
prisma.booking.findMany({
where: {
OR: [
{
eventType: {
team: {
members: {
some: {
userId: user.id,
role: {
in: ["ADMIN", "OWNER"],
},
},
},
},
},
},
],
AND: [passedBookingsStatusFilter, ...filtersCombined],
},
orderBy,
take: take + 1,
skip,
}),
prisma.booking.findMany({
where: {
OR: [
{
seatsReferences: {
some: {
attendee: {
email: user.email,
},
},
},
},
],
AND: [passedBookingsStatusFilter, ...filtersCombined],
},
orderBy,
select: { id: true },
take: take + 1,
skip,
}),
@@ -443,6 +345,9 @@ 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(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.
@@ -451,7 +356,7 @@ export async function getBookings({
await prisma.booking.findMany({
where: {
id: {
in: plainBookings,
in: plainBookings.map((booking) => booking.id),
},
},
select: bookingSelect,