feat: make actionSource required with ValidActionSource type across booking handlers (#27869)

* feat: make actionSource required with ValidActionSource type across booking handlers

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: update getAuditActionSource to return ValidActionSource, default to SYSTEM

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: revert getAuditActionSource, handleSeats, RegularBookingService back to ActionSource

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2026-02-18 07:34:25 -03:00
1 parent 217c6e6a76
commit a6a428c8cb
11 files changed
+42 -22

No files matched your search

@@ -82,6 +82,7 @@ async function handler(req: NextApiRequest) {
return await handleCancelBooking({
bookingData: { id, allRemainingBookings, cancellationReason, cancelledBy },
userId: req.userId,
actionSource: "API_V1",
});
}
@@ -279,6 +279,7 @@ export class BookingsController_2024_04_15 {
platformCancelUrl: bookingRequest.platformCancelUrl,
platformRescheduleUrl: bookingRequest.platformRescheduleUrl,
platformBookingUrl: bookingRequest.platformBookingUrl,
actionSource: "API_V2",
});
if (!res.onlyRemovedAttendee && res.isPlatformManagedUserBooking) {
void (await this.billingService.cancelUsageByBookingUid(res.bookingUid));
@@ -10,7 +10,7 @@ import {
makeGuestActor,
makeUserActor,
} from "@calcom/features/booking-audit/lib/makeActor";
import type { ActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import type { ValidActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import { BookingReferenceRepository } from "@calcom/features/bookingReference/repositories/BookingReferenceRepository";
import { getBookingEventHandlerService } from "@calcom/features/bookings/di/BookingEventHandlerService.container";
import EventManager from "@calcom/features/bookings/lib/EventManager";
@@ -81,7 +81,7 @@ export type CancelBookingInput = {
userId?: number;
userUuid?: string;
bookingData: z.infer<typeof bookingCancelInput>;
actionSource?: ActionSource;
actionSource: ValidActionSource;
} & PlatformParams;
type Dependencies = {
@@ -173,17 +173,7 @@ async function handler(input: CancelBookingInput, dependencies?: Dependencies) {
const userUuid = input.userUuid ?? null;
// Extract action source once for reuse
const actionSource = input.actionSource ?? "UNKNOWN";
if (actionSource === "UNKNOWN") {
log.warn(
"Booking cancellation with unknown actionSource",
safeStringify({
bookingUid: bookingToDelete.uid,
userUuid,
})
);
}
const actionSource = input.actionSource;
const actorToUse = getAuditActor({
userUuid,
@@ -724,10 +714,15 @@ type BookingCancelServiceDependencies = {
export class BookingCancelService implements IBookingCancelService {
constructor(private readonly deps: BookingCancelServiceDependencies) {}
async cancelBooking(input: { bookingData: CancelRegularBookingData; bookingMeta?: CancelBookingMeta }) {
async cancelBooking(input: {
bookingData: CancelRegularBookingData;
bookingMeta?: CancelBookingMeta;
actionSource: ValidActionSource;
}) {
const cancelBookingInput: CancelBookingInput = {
bookingData: input.bookingData,
...(input.bookingMeta || {}),
actionSource: input.actionSource,
};
return handler(cancelBookingInput, this.deps);
@@ -134,6 +134,7 @@ describe("Cancel Booking", () => {
cancelledBy: organizer.email,
cancellationReason: "No reason",
},
actionSource: "WEBAPP",
});
expectBookingCancelledWebhookToHaveBeenFired({
@@ -264,6 +265,7 @@ describe("Cancel Booking", () => {
cancelledBy: organizer.email,
cancellationReason: "No reason",
},
actionSource: "WEBAPP",
});
expectBookingCancelledWebhookToHaveBeenFired({
@@ -411,6 +413,7 @@ describe("Cancel Booking", () => {
cancelledBy: organizer.email,
cancellationReason: "Testing round robin cancellation with host as attendee",
},
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -563,6 +566,7 @@ describe("Cancel Booking", () => {
cancelledBy: primaryHost.email,
cancellationReason: "Testing EMAIL_HOST workflow sends to secondary host in round robin",
},
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -640,6 +644,7 @@ describe("Cancel Booking", () => {
cancelledBy: organizer.email,
cancellationReason: "Testing past booking cancellation",
},
actionSource: "WEBAPP",
})
).rejects.toThrow("Cannot cancel a booking that has already ended");
});
@@ -708,6 +713,7 @@ describe("Cancel Booking", () => {
uid: uidOfBookingToBeCancelled,
cancelledBy: organizer.email,
},
actionSource: "WEBAPP",
})
).rejects.toThrow("Cancellation reason is required when you are the host");
});
@@ -819,6 +825,7 @@ describe("Cancel Booking", () => {
cancelledBy: organizer.email,
cancellationReason: "No reason",
},
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -945,6 +952,7 @@ describe("Cancel Booking", () => {
cancelledBy: organizer.email,
cancellationReason: "No reason",
},
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -1062,6 +1070,7 @@ describe("Cancel Booking", () => {
cancellationReason: "Attendee cancelled within time threshold",
},
userId: 999,
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -1170,6 +1179,7 @@ describe("Cancel Booking", () => {
cancelledBy: organizer.email,
cancellationReason: "Organization booking cancellation test",
},
actionSource: "WEBAPP",
});
expectBookingCancelledWebhookToHaveBeenFired({
@@ -1292,6 +1302,7 @@ describe("Cancel Booking", () => {
cancellationReason: "Cancelling seated event",
},
userId: organizer.id,
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -1413,6 +1424,7 @@ describe("Cancel Booking", () => {
allRemainingBookings: true,
},
userId: organizer.id,
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -1534,6 +1546,7 @@ describe("Cancel Booking", () => {
cancelSubsequentBookings: true,
},
userId: organizer.id,
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -1647,6 +1660,7 @@ describe("Cancel Booking", () => {
cancellationReason: "Testing booking reference cleanup",
},
userId: organizer.id,
actionSource: "WEBAPP",
});
expect(result.success).toBe(true);
@@ -2,7 +2,7 @@ import { eventTypeAppMetadataOptionalSchema } from "@calcom/app-store/zod-utils"
import { scheduleMandatoryReminder } from "@calcom/ee/workflows/lib/reminders/scheduleMandatoryReminder";
import { sendScheduledEmailsAndSMS } from "@calcom/emails/email-manager";
import type { Actor } from "@calcom/features/booking-audit/lib/dto/types";
import type { ActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import type { ValidActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import { getBookingEventHandlerService } from "@calcom/features/bookings/di/BookingEventHandlerService.container";
import type { EventManagerUser } from "@calcom/features/bookings/lib/EventManager";
import EventManager, { placeholderCreatedEvent } from "@calcom/features/bookings/lib/EventManager";
@@ -48,7 +48,7 @@ async function fireBookingAcceptedEvent({
}: {
actor: Actor;
organizationId: number | null;
actionSource: ActionSource;
actionSource: ValidActionSource;
acceptedBookings: {
uid: string;
oldStatus: BookingStatus;
@@ -133,7 +133,7 @@ export async function handleConfirmation(args: {
emailsEnabled?: boolean;
platformClientParams?: PlatformClientParams;
traceContext: TraceContext;
actionSource: ActionSource;
actionSource: ValidActionSource;
actor: Actor;
}) {
const {
@@ -1976,6 +1976,7 @@ describe("handleSeats", () => {
cancellationReason: "test cancellation reason",
},
userId: organizer.id,
actionSource: "WEBAPP",
});
// Ensure that the booking has been cancelled
@@ -2115,6 +2116,7 @@ describe("handleSeats", () => {
cancellationReason: "test cancellation reason",
},
userId: organizer.id,
actionSource: "WEBAPP",
});
// Ensure that the booking has been cancelled
@@ -2967,6 +2969,7 @@ describe("handleSeats", () => {
cancellationReason: "test cancellation reason",
},
userId: organizer.id,
actionSource: "WEBAPP",
});
// Ensure that the booking has been cancelled
@@ -3,10 +3,12 @@ import type {
CancelBookingMeta,
HandleCancelBookingResponse,
} from "../dto/BookingCancel";
import type { ValidActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
export interface IBookingCancelService {
cancelBooking(input: {
bookingData: CancelRegularBookingData;
bookingMeta?: CancelBookingMeta;
actionSource: ValidActionSource;
}): Promise<HandleCancelBookingResponse>;
}
@@ -121,6 +121,7 @@ export const bookingsRouter = router({
return reportBookingHandler({
ctx,
input,
actionSource: "WEBAPP",
});
}),
reportWrongAssignment: authedProcedure
@@ -2,7 +2,7 @@ import { getUsersCredentialsIncludeServiceAccountKey } from "@calcom/app-store/d
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
import dayjs from "@calcom/dayjs";
import { makeUserActor } from "@calcom/features/booking-audit/lib/makeActor";
import type { ActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import type { ValidActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import { getBookingEventHandlerService } from "@calcom/features/bookings/di/BookingEventHandlerService.container";
import { BookingEmailSmsHandler } from "@calcom/features/bookings/lib/BookingEmailSmsHandler";
import EventManager from "@calcom/features/bookings/lib/EventManager";
@@ -35,7 +35,7 @@ type AddGuestsOptions = {
};
input: TAddGuestsInputSchema;
emailsEnabled?: boolean;
actionSource: ActionSource;
actionSource: ValidActionSource;
};
export type Booking = NonNullable<
@@ -1,4 +1,5 @@
import { PrismaBookingReportRepository } from "@calcom/features/bookingReport/repositories/PrismaBookingReportRepository";
import type { ValidActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import handleCancelBooking from "@calcom/features/bookings/lib/handleCancelBooking";
import { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
import { BookingAccessService } from "@calcom/features/bookings/services/BookingAccessService";
@@ -16,11 +17,12 @@ type ReportBookingOptions = {
user: NonNullable<TrpcSessionUser>;
};
input: TReportBookingInputSchema;
actionSource: ValidActionSource;
};
const log = logger.getSubLogger({ prefix: ["reportBookingHandler"] });
export const reportBookingHandler = async ({ ctx, input }: ReportBookingOptions) => {
export const reportBookingHandler = async ({ ctx, input, actionSource }: ReportBookingOptions) => {
const { user } = ctx;
const { bookingUid, reason, description } = input;
@@ -90,6 +92,7 @@ export const reportBookingHandler = async ({ ctx, input }: ReportBookingOptions)
...(seatReferenceUid ? { seatReferenceUid } : {}),
},
userId: user.id,
actionSource,
});
didCancel = true;
} catch (error) {
@@ -6,7 +6,7 @@ import { getUsersCredentialsIncludeServiceAccountKey } from "@calcom/app-store/d
import dayjs from "@calcom/dayjs";
import { sendRequestRescheduleEmailAndSMS } from "@calcom/emails/email-manager";
import { makeUserActor } from "@calcom/features/booking-audit/lib/makeActor";
import type { ActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import type { ValidActionSource } from "@calcom/features/booking-audit/lib/types/actionSource";
import { getBookingEventHandlerService } from "@calcom/features/bookings/di/BookingEventHandlerService.container";
import { getFeaturesRepository } from "@calcom/features/di/containers/FeaturesRepository";
import { getCalEventResponses } from "@calcom/features/bookings/lib/getCalEventResponses";
@@ -47,7 +47,7 @@ type RequestRescheduleOptions = {
user: NonNullable<TrpcSessionUser>;
};
input: TRequestRescheduleInputSchema;
source: ActionSource;
source: ValidActionSource;
};
const log = logger.getSubLogger({ prefix: ["requestRescheduleHandler"] });
export const requestRescheduleHandler = async ({ ctx, input, source }: RequestRescheduleOptions) => {