chore: Add skeleton for BookingCancelService (#24038)

This commit is contained in:
Hariom Balhara authored and GitHub committed 2025-10-21 12:29:17 +05:30
1 parent 4cee53b07c
commit 5e5e77236a
10 files changed
+158 -14

No files matched your search

@@ -0,0 +1,10 @@
import { BookingCancelService } from "@/lib/services/booking-cancel.service";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { Module } from "@nestjs/common";
@Module({
imports: [PrismaModule],
providers: [BookingCancelService],
exports: [BookingCancelService],
})
export class BookingCancelModule {}
@@ -0,0 +1,15 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { BookingCancelService as BaseBookingCancelService } from "@calcom/features/bookings/lib/handleCancelBooking";
@Injectable()
export class BookingCancelService extends BaseBookingCancelService {
constructor(
prismaWriteService: PrismaWriteService,
) {
super({
prismaClient: prismaWriteService.prisma,
});
}
}
+9
View File
@@ -26,11 +26,20 @@ async function handler(req: NextRequest) {
cookieStore.delete("calcom.csrf_token");
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
const result = await handleCancelBooking({
bookingData,
userId: session?.user?.id || -1,
});
// const bookingCancelService = getBookingCancelService();
// const result = await bookingCancelService.cancelBooking({
// bookingData: bookingData,
// bookingMeta: {
// userId: session?.user?.id || -1,
// },
// });
const statusCode = result.success ? 200 : 400;
return NextResponse.json(result, { status: statusCode });
@@ -0,0 +1,14 @@
import { createContainer } from "@calcom/lib/di/di";
import {
type BookingCancelService,
moduleLoader as bookingCancelServiceModule,
} from "../modules/BookingCancelService.module";
const bookingCancelServiceContainer = createContainer();
export function getBookingCancelService(): BookingCancelService {
bookingCancelServiceModule.loadModule(bookingCancelServiceContainer);
return bookingCancelServiceContainer.get<BookingCancelService>(bookingCancelServiceModule.token);
}
@@ -0,0 +1,24 @@
import { BookingCancelService } from "@calcom/features/bookings/lib/handleCancelBooking";
import { bindModuleToClassOnToken, createModule } from "@calcom/lib/di/di";
import { DI_TOKENS } from "@calcom/lib/di/tokens";
import { moduleLoader as prismaModuleLoader } from "@calcom/prisma/prisma.module";
const thisModule = createModule();
const token = DI_TOKENS.BOOKING_CANCEL_SERVICE;
const moduleToken = DI_TOKENS.BOOKING_CANCEL_SERVICE_MODULE;
const loadModule = bindModuleToClassOnToken({
module: thisModule,
moduleToken,
token,
classs: BookingCancelService,
depsMap: {
prismaClient: prismaModuleLoader,
},
});
export const moduleLoader = {
token,
loadModule,
};
export type { BookingCancelService };
+2
View File
@@ -5,4 +5,6 @@ export const BOOKING_DI_TOKENS = {
RECURRING_BOOKING_SERVICE_MODULE: Symbol("RecurringBookingServiceModule"),
INSTANT_BOOKING_CREATE_SERVICE: Symbol("InstantBookingCreateService"),
INSTANT_BOOKING_CREATE_SERVICE_MODULE: Symbol("InstantBookingCreateServiceModule"),
BOOKING_CANCEL_SERVICE: Symbol("BookingCancelService"),
BOOKING_CANCEL_SERVICE_MODULE: Symbol("BookingCancelServiceModule"),
};
+35
View File
@@ -0,0 +1,35 @@
/**
* Domain types for Booking Cancellation Service
* These types are framework-agnostic and contain only the data required for booking cancellation operations
*/
import type { z } from "zod";
import type { bookingCancelInput } from "@calcom/prisma/zod-utils";
/**
* The booking data required for cancellation
*/
export type CancelRegularBookingData = z.infer<typeof bookingCancelInput>;
/**
* Additional metadata for booking cancellation, typically used for platform integrations
*/
export type CancelBookingMeta = {
userId?: number;
platformClientId?: string;
platformRescheduleUrl?: string;
platformCancelUrl?: string;
platformBookingUrl?: string;
arePlatformEmailsEnabled?: boolean;
};
/**
* The response returned after attempting to cancel a booking
*/
export type HandleCancelBookingResponse = {
success: boolean;
message: string;
onlyRemovedAttendee: boolean;
bookingId: number;
bookingUid: string;
};
@@ -5,10 +5,13 @@ import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApi
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
import dayjs from "@calcom/dayjs";
import { sendCancelledEmailsAndSMS } from "@calcom/emails";
import EventManager from "@calcom/features/bookings/lib/EventManager";
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
import { processNoShowFeeOnCancellation } from "@calcom/features/bookings/lib/payment/processNoShowFeeOnCancellation";
import { processPaymentRefund } from "@calcom/features/bookings/lib/payment/processPaymentRefund";
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
import { sendCancelledReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler";
import { WorkflowRepository } from "@calcom/features/ee/workflows/repositories/WorkflowRepository";
import type { GetSubscriberOptions } from "@calcom/features/webhooks/lib/getWebhooks";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import {
@@ -17,8 +20,6 @@ import {
} from "@calcom/features/webhooks/lib/scheduleTrigger";
import sendPayload from "@calcom/features/webhooks/lib/sendOrSchedulePayload";
import type { EventTypeInfo } from "@calcom/features/webhooks/lib/sendPayload";
import EventManager from "@calcom/features/bookings/lib/EventManager";
import { getBookerBaseUrl } from "@calcom/features/ee/organizations/lib/getBookerUrlServer";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
import { HttpError } from "@calcom/lib/http-error";
@@ -27,10 +28,10 @@ import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { getTranslation } from "@calcom/lib/server/i18n";
import { WorkflowRepository } from "@calcom/features/ee/workflows/repositories/WorkflowRepository";
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
// TODO: Prisma import would be used from DI in a followup PR when we remove `handler` export
import prisma from "@calcom/prisma";
import type { Prisma, WorkflowReminder } from "@calcom/prisma/client";
import type { Prisma, PrismaClient, WorkflowReminder } from "@calcom/prisma/client";
import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { BookingStatus } from "@calcom/prisma/enums";
import { bookingMetadataSchema, bookingCancelInput } from "@calcom/prisma/zod-utils";
@@ -38,10 +39,16 @@ import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
import { getAllWorkflowsFromEventType } from "@calcom/trpc/server/routers/viewer/workflows/util";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type {
CancelRegularBookingData,
CancelBookingMeta,
HandleCancelBookingResponse,
} from "./dto/BookingCancel";
import { getAllCredentialsIncludeServiceAccountKey } from "./getAllCredentialsForUsersOnEvent/getAllCredentials";
import { getBookingToDelete } from "./getBookingToDelete";
import { handleInternalNote } from "./handleInternalNote";
import cancelAttendeeSeat from "./handleSeats/cancel/cancelAttendeeSeat";
import type { IBookingCancelService } from "./interfaces/IBookingCancelService";
const log = logger.getSubLogger({ prefix: ["handleCancelBooking"] });
@@ -60,14 +67,6 @@ export type CancelBookingInput = {
bookingData: z.infer<typeof bookingCancelInput>;
} & PlatformParams;
export type HandleCancelBookingResponse = {
success: boolean;
message: string;
onlyRemovedAttendee: boolean;
bookingId: number;
bookingUid: string;
};
async function handler(input: CancelBookingInput) {
const body = input.bookingData;
const {
@@ -118,7 +117,12 @@ async function handler(input: CancelBookingInput) {
const isCancellationUserHost =
bookingToDelete.userId == userId || bookingToDelete.user.email === cancelledBy;
if (!platformClientId && !cancellationReason?.trim() && isCancellationUserHost && !skipCancellationReasonValidation) {
if (
!platformClientId &&
!cancellationReason?.trim() &&
isCancellationUserHost &&
!skipCancellationReasonValidation
) {
throw new HttpError({
statusCode: 400,
message: "Cancellation reason is required when you are the host",
@@ -588,4 +592,24 @@ async function handler(input: CancelBookingInput) {
} satisfies HandleCancelBookingResponse;
}
type BookingCancelServiceDependencies = {
prismaClient: PrismaClient;
};
/**
* Takes care of cancelling bookings. This includes regular bookings, recurring bookings, seated bookings, etc.
* Handles both individual booking cancellations and bulk cancellations for recurring events.
*/
export class BookingCancelService implements IBookingCancelService {
constructor(private readonly deps: BookingCancelServiceDependencies) {}
async cancelBooking(input: { bookingData: CancelRegularBookingData; bookingMeta?: CancelBookingMeta }) {
const cancelBookingInput: CancelBookingInput = {
bookingData: input.bookingData,
...(input.bookingMeta || {}),
};
// TODO: Deps to be passed to it later when we stop exporting handler
return handler(cancelBookingInput);
}
}
export default handler;
@@ -0,0 +1,12 @@
import type {
CancelRegularBookingData,
CancelBookingMeta,
HandleCancelBookingResponse,
} from "../dto/BookingCancel";
export interface IBookingCancelService {
cancelBooking(input: {
bookingData: CancelRegularBookingData;
bookingMeta?: CancelBookingMeta;
}): Promise<HandleCancelBookingResponse>;
}
-1
View File
@@ -55,7 +55,6 @@ export const DI_TOKENS = {
HOST_REPOSITORY_MODULE: Symbol("HostRepositoryModule"),
ATTRIBUTE_REPOSITORY: Symbol("AttributeRepository"),
ATTRIBUTE_REPOSITORY_MODULE: Symbol("AttributeRepositoryModule"),
// Booking service tokens
...BOOKING_DI_TOKENS,
// Watchlist service tokens
...WATCHLIST_DI_TOKENS,