Files
Alex van AndelGitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Somay Chauhan
fde1d671fb refactor: rename SelectedSlotsRepository to PrismaSelectedSlotRepository (#22705)
* refactor: rename SelectedSlotsRepository to PrismaSelectedSlotRepository

- Rename class from SelectedSlotsRepository to PrismaSelectedSlotRepository for consistency
- Update all imports and type references throughout the codebase
- Update DI module bindings and variable names
- Maintain type safety without breaking changes

Co-Authored-By: [email protected] <[email protected]>

* refactor: complete PrismaSelectedSlotRepository rename

- Update DI module binding property name from selectedSlotsRepo to selectedSlotRepo
- Update test mock to use PrismaSelectedSlotRepository class name
- Ensure all references are consistently updated

Co-Authored-By: [email protected] <[email protected]>

* fix: rename file to prismaSelectedSlotRepository.ts and fix plural variable references

- Rename apps/api/v2/src/lib/repositories/prisma-selected-slots.repository.ts to prismaSelectedSlotRepository.ts
- Update class name from PrismaSelectedSlotsRepository to PrismaSelectedSlotRepository
- Fix plural variable reference selectedSlotsRepository to selectedSlotRepository in service
- Update all import paths to reference the new file name
- Maintain consistency with camelCase naming convention

Co-Authored-By: [email protected] <[email protected]>

* fix: rename SelectedSlotsRepositoryFixture to SelectedSlotRepositoryFixture for consistency

- Rename test fixture class from SelectedSlotsRepositoryFixture to SelectedSlotRepositoryFixture (singular)
- Update all import statements in test files to use the renamed class
- Resolves naming mismatch between fixture class and variable declarations
- Fixes TypeScript compilation errors in CI tests

Co-Authored-By: [email protected] <[email protected]>

* feat: add SelectedSlotRepositoryInterface and update DI to use interface

Co-Authored-By: [email protected] <[email protected]>

* refactor: rename selectedSlots.ts to selectedSlot.ts and update all imports

Co-Authored-By: [email protected] <[email protected]>

* fix: update test mock import path after file rename

Co-Authored-By: [email protected] <[email protected]>

* Update apps/api/v2/src/lib/modules/available-slots.module.ts

* Update apps/api/v2/src/lib/modules/available-slots.module.ts

* Implement DTO

* dont declare dependencies locally, duplicating

* Small DTO/token fix

* chore: bump @calcom/platform-libraries from 0.0.266 to 0.0.267

* oops.

* Update fixture names also

* Omg these vscode actions preventing saves

* Final fix, hopefully

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Somay Chauhan <[email protected]>
2025-07-25 16:59:32 -07:00

127 lines
4.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { serialize } from "cookie";
import type { NextApiRequest, NextApiResponse } from "next";
import { v4 as uuid } from "uuid";
import dayjs from "@calcom/dayjs";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { MINUTES_TO_BOOK } from "@calcom/lib/constants";
import { PrismaSelectedSlotRepository } from "@calcom/lib/server/repository/PrismaSelectedSlotRepository";
import type { PrismaClient } from "@calcom/prisma";
import { BookingStatus } from "@calcom/prisma/enums";
import { TRPCError } from "@trpc/server";
import type { TReserveSlotInputSchema } from "./reserveSlot.schema";
interface ReserveSlotOptions {
ctx: {
prisma: PrismaClient;
req?: NextApiRequest | undefined;
res?: NextApiResponse | undefined;
};
input: TReserveSlotInputSchema;
}
export const reserveSlotHandler = async ({ ctx, input }: ReserveSlotOptions) => {
const { prisma, req, res } = ctx;
const uid = req?.cookies?.uid || uuid();
const { slotUtcStartDate, slotUtcEndDate, eventTypeId, _isDryRun } = input;
const releaseAt = dayjs.utc().add(parseInt(MINUTES_TO_BOOK), "minutes").format();
const eventType = await prisma.eventType.findUnique({
where: { id: eventTypeId },
select: { users: { select: { id: true } }, seatsPerTimeSlot: true },
});
if (!eventType) {
throw new TRPCError({
message: "Event type not found",
code: "NOT_FOUND",
});
}
let shouldReserveSlot = true;
// If this is a seated event then don't reserve a slot
if (eventType.seatsPerTimeSlot) {
// Check to see if this is the last attendee
const bookingWithAttendees = await prisma.booking.findFirst({
where: {
eventTypeId,
startTime: slotUtcStartDate,
endTime: slotUtcEndDate,
status: BookingStatus.ACCEPTED,
},
select: { attendees: true },
});
const bookingAttendeesLength = bookingWithAttendees?.attendees?.length;
if (bookingAttendeesLength) {
const seatsLeft = eventType.seatsPerTimeSlot - bookingAttendeesLength;
if (seatsLeft < 1) shouldReserveSlot = false;
} else {
// If there is no booking yet then don't reserve the slot
shouldReserveSlot = false;
}
}
// Check for existing reservations for the same slot
const slotsRepo = new PrismaSelectedSlotRepository(prisma);
const reservedBySomeoneElse = await slotsRepo.findReservedByOthers({
slot: {
utcStartIso: slotUtcStartDate,
utcEndIso: slotUtcEndDate,
},
eventTypeId,
uid,
});
if (eventType && shouldReserveSlot && !reservedBySomeoneElse && !_isDryRun) {
try {
await Promise.all(
// FIXME: In case of team event, users doesn't have assignees, those are in hosts. users just have the creator of the event which is wrong.
// Also, we must not block all the users' slots, we must use routedTeamMemberIds if set like we do in getSchedule.
// We could even improve it by identifying the next person being booked now that we have a queue of assignees.
eventType.users.map((user) =>
prisma.selectedSlots.upsert({
where: { selectedSlotUnique: { userId: user.id, slotUtcStartDate, slotUtcEndDate, uid } },
update: {
slotUtcStartDate,
slotUtcEndDate,
releaseAt,
eventTypeId,
},
create: {
userId: user.id,
eventTypeId,
slotUtcStartDate,
slotUtcEndDate,
uid,
releaseAt,
isSeat: eventType.seatsPerTimeSlot !== null,
},
})
)
);
} catch {
throw new TRPCError({
message: "Event type not found",
code: "NOT_FOUND",
});
}
}
// We need this cookie to be accessible from embeds where the booking flow is displayed within an iframe on a different origin.
// For third‑party iframe contexts (embeds on other sites), browsers require SameSite=None and Secure to make the cookie available.
// For local development on http://localhost we fall back to SameSite=Lax to avoid requiring https during development.
const useSecureCookies = WEBAPP_URL.startsWith("https://");
res?.setHeader(
"Set-Cookie",
serialize("uid", uid, {
path: "/",
sameSite: useSecureCookies ? "none" : "lax",
secure: useSecureCookies,
})
);
return {
uid: uid,
};
};