Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 70 additions & 13 deletions packages/features/bookings/lib/get-booking.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { bookingResponsesDbSchema } from "@calcom/features/bookings/lib/getBookingResponsesSchema";
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
import slugify from "@calcom/lib/slugify";
import type { PrismaClient } from "@calcom/prisma";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";

type BookingSelect = {
description: true;
Expand All @@ -24,7 +26,8 @@ function getResponsesFromOldBooking(
) {
const customInputs = rawBooking.customInputs || {};
const responses = Object.keys(customInputs).reduce((acc, label) => {
acc[slugify(label) as keyof typeof acc] = customInputs[label as keyof typeof customInputs];
acc[slugify(label) as keyof typeof acc] =
customInputs[label as keyof typeof customInputs];
return acc;
}, {});
return {
Expand All @@ -43,7 +46,11 @@ function getResponsesFromOldBooking(
};
}

async function getBooking(prisma: PrismaClient, uid: string, isSeatedEvent?: boolean) {
async function getBooking(
prisma: PrismaClient,
uid: string,
isSeatedEvent?: boolean
) {
const rawBooking = await prisma.booking.findUnique({
where: {
uid,
Expand Down Expand Up @@ -80,6 +87,7 @@ async function getBooking(prisma: PrismaClient, uid: string, isSeatedEvent?: boo
user: {
select: {
id: true,
username: true,
},
},
},
Expand All @@ -94,8 +102,12 @@ async function getBooking(prisma: PrismaClient, uid: string, isSeatedEvent?: boo
if (booking) {
// @NOTE: had to do this because Server side cant return [Object objects]
// probably fixable with json.stringify -> json.parse
booking["startTime"] = (booking?.startTime as Date)?.toISOString() as unknown as Date;
booking["endTime"] = (booking?.endTime as Date)?.toISOString() as unknown as Date;
booking["startTime"] = (
booking?.startTime as Date
)?.toISOString() as unknown as Date;
booking["endTime"] = (
booking?.endTime as Date
)?.toISOString() as unknown as Date;
}

return booking;
Expand All @@ -115,7 +127,9 @@ export const getBookingWithResponses = <
) => {
return {
...booking,
responses: isSeatedEvent ? booking.responses : booking.responses || getResponsesFromOldBooking(booking),
responses: isSeatedEvent
? booking.responses
: booking.responses || getResponsesFromOldBooking(booking),
} as Omit<T, "responses"> & { responses: Record<string, any> };
};

Expand All @@ -130,9 +144,15 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
select: {
id: true,
userId: true,
user: {
select: {
organizationId: true,
},
},
eventType: {
select: {
seatsPerTimeSlot: true,
teamId: true,
hosts: {
select: {
userId: true,
Expand All @@ -152,7 +172,10 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
// If no booking is found via the uid, it's probably a booking seat
// that its being rescheduled, which we query next.
let attendeeEmail: string | null = null;
let bookingSeatData: { description?: string; responses: Prisma.JsonValue } | null = null;
let bookingSeatData: {
description?: string;
responses: Prisma.JsonValue;
} | null = null;
if (!theBooking) {
const bookingSeat = await prisma.bookingSeat.findFirst({
where: {
Expand All @@ -175,7 +198,10 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
},
});
if (bookingSeat) {
bookingSeatData = bookingSeat.data as unknown as { description?: string; responses: Prisma.JsonValue };
bookingSeatData = bookingSeat.data as unknown as {
description?: string;
responses: Prisma.JsonValue;
};
bookingSeatReferenceUid = bookingSeat.id;
rescheduleUid = bookingSeat.booking.uid;
attendeeEmail = bookingSeat.attendee.email;
Expand All @@ -185,28 +211,57 @@ export const getBookingForReschedule = async (uid: string, userId?: number) => {
// If we have the booking and not bookingSeat, we need to make sure the booking belongs to the userLoggedIn
// Otherwise, we return null here.
let hasOwnershipOnBooking = false;
if (theBooking && theBooking?.eventType?.seatsPerTimeSlot && bookingSeatReferenceUid === null) {
if (
theBooking &&
theBooking?.eventType?.seatsPerTimeSlot &&
bookingSeatReferenceUid === null
) {
const isOwnerOfBooking = theBooking.userId === userId;

const isHostOfEventType = theBooking?.eventType?.hosts.some((host) => host.userId === userId);
const isHostOfEventType = theBooking?.eventType?.hosts.some(
(host) => host.userId === userId
);

const isUserIdInBooking = theBooking.userId === userId;

if (!isOwnerOfBooking && !isHostOfEventType && !isUserIdInBooking) return null;
let hasOrgAccess = false;
if (userId && theBooking.user?.organizationId) {
const permissionCheckService = new PermissionCheckService();
hasOrgAccess = await permissionCheckService.checkPermission({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sean-brydon we use pbac now to check if user is org admin or not

userId,
teamId: theBooking.user.organizationId,
permission: "booking.readOrgBookings",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
}

if (
!isOwnerOfBooking &&
!isHostOfEventType &&
!isUserIdInBooking &&
!hasOrgAccess
)
return null;
hasOwnershipOnBooking = true;
}

// If we don't have a booking and no rescheduleUid, the ID is invalid,
// and we return null here.
if (!theBooking && !rescheduleUid) return null;

const booking = await getBooking(prisma, rescheduleUid || uid, bookingSeatReferenceUid ? true : false);
const booking = await getBooking(
prisma,
rescheduleUid || uid,
bookingSeatReferenceUid ? true : false
);

if (!booking) return null;

if (bookingSeatReferenceUid) {
booking["description"] = bookingSeatData?.description ?? null;
booking["responses"] = bookingResponsesDbSchema.parse(bookingSeatData?.responses ?? {});
booking["responses"] = bookingResponsesDbSchema.parse(
bookingSeatData?.responses ?? {}
);
}
return {
...booking,
Expand Down Expand Up @@ -244,6 +299,7 @@ export const getBookingForSeatedEvent = async (uid: string) => {
user: {
select: {
id: true,
username: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for rescheduling its better to use the host username that we get from get-booking

},
},
},
Expand Down Expand Up @@ -293,6 +349,7 @@ export const getMultipleDurationValue = (
defaultValue: number
) => {
if (!multipleDurationConfig) return null;
if (multipleDurationConfig.includes(Number(queryDuration))) return Number(queryDuration);
if (multipleDurationConfig.includes(Number(queryDuration)))
return Number(queryDuration);
return defaultValue;
};
Loading