Remove cal.cache query parameter to prevent stale calendar data (#24939)

- Remove cal.cache query parameter parsing from booking and slots flows
- Set shouldServeCache to false for all booking availability checks
- Remove _shouldServeCache from schemas and type definitions
- Clean up all references in platform atoms and API endpoints

This ensures fresh Google Calendar data is always fetched for conflict checking,
preventing bookings when there are actual calendar conflicts.
This commit is contained in:
Hariom Balhara
2025-11-05 13:01:22 +00:00
committed by GitHub
parent c196ff1095
commit e8a315ac4e
13 changed files with 56 additions and 91 deletions
@@ -194,11 +194,6 @@ export class CreateBookingInput_2024_04_15 {
@ApiHideProperty()
skipContactOwner?: boolean;
@IsBoolean()
@IsOptional()
@ApiHideProperty()
_shouldServeCache?: boolean;
@IsBoolean()
@IsOptional()
@ApiHideProperty()
@@ -39,8 +39,6 @@ export const bookingCreateBodySchema = z.object({
* Used to identify if the booking is a dry run.
*/
_isDryRun: z.boolean().optional(),
/** Whether to override the cache */
_shouldServeCache: z.boolean().optional(),
tracking: z
.object({
utm_source: z.string().optional(),
@@ -64,8 +64,6 @@ export const mapBookingToMutationInput = ({
const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true";
const reroutingFormResponses = searchParams.get("cal.reroutingFormResponses");
const _isDryRun = isDryRunProp !== undefined ? isDryRunProp : isBookingDryRun(searchParams);
const _cacheParam = searchParams?.get("cal.cache");
const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined;
const dub_id = searchParams?.get("dub_id");
return {
@@ -98,7 +96,6 @@ export const mapBookingToMutationInput = ({
// In case of rerouting, the form responses are actually the responses that we need to update.
reroutingFormResponses: reroutingFormResponses ? JSON.parse(reroutingFormResponses) : undefined,
_isDryRun,
_shouldServeCache,
dub_id,
verificationCode,
};
@@ -520,7 +520,6 @@ async function handler(
reroutingFormResponses,
routingFormResponseId,
_isDryRun: isDryRun = false,
_shouldServeCache: shouldServeCache = false,
...reqBody
} = bookingData;
@@ -665,11 +664,11 @@ async function handler(
}
}
// const shouldServeCache = await cacheService.getShouldServeCache(_shouldServeCache, eventType.team?.id);
const isTeamEventType =
!!eventType.schedulingType && ["COLLECTIVE", "ROUND_ROBIN"].includes(eventType.schedulingType);
const shouldServeCache = false;
loggerWithEventDetails.info(
`Booking eventType ${eventTypeId} started`,
safeStringify({
@@ -1244,9 +1243,9 @@ async function handler(
// This ensures that createMeeting isn't called for static video apps as bookingLocation becomes just a regular value for them.
const { bookingLocation, conferenceCredentialId } = organizerOrFirstDynamicGroupMemberDefaultLocationUrl
? {
bookingLocation: organizerOrFirstDynamicGroupMemberDefaultLocationUrl,
conferenceCredentialId: undefined,
}
bookingLocation: organizerOrFirstDynamicGroupMemberDefaultLocationUrl,
conferenceCredentialId: undefined,
}
: getLocationValueForDB(locationBodyString, eventType.locations);
log.info("locationBodyString", locationBodyString);
@@ -1292,8 +1291,8 @@ async function handler(
const destinationCalendar = eventType.destinationCalendar
? [eventType.destinationCalendar]
: organizerUser.destinationCalendar
? [organizerUser.destinationCalendar]
: null;
? [organizerUser.destinationCalendar]
: null;
let organizerEmail = organizerUser.email || "Email-less";
if (eventType.useEventTypeDestinationCalendarEmail && destinationCalendar?.[0]?.primaryEmail) {
@@ -1912,14 +1911,14 @@ async function handler(
}
const updateManager = !skipCalendarSyncTaskCreation
? await eventManager.reschedule(
evt,
originalRescheduledBooking.uid,
undefined,
changedOrganizer,
previousHostDestinationCalendar,
isBookingRequestedReschedule,
skipDeleteEventsAndMeetings
)
evt,
originalRescheduledBooking.uid,
undefined,
changedOrganizer,
previousHostDestinationCalendar,
isBookingRequestedReschedule,
skipDeleteEventsAndMeetings
)
: placeholderCreatedEvent;
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
// to the default description when we are sending the emails.
@@ -2217,8 +2216,8 @@ async function handler(
const metadata = videoCallUrl
? {
videoCallUrl: getVideoCallUrlFromCalEvent(evt) || videoCallUrl,
}
videoCallUrl: getVideoCallUrlFromCalEvent(evt) || videoCallUrl,
}
: undefined;
const bookingFlowConfig = {
@@ -2307,9 +2306,9 @@ async function handler(
...eventType,
metadata: eventType.metadata
? {
...eventType.metadata,
apps: eventType.metadata?.apps as Prisma.JsonValue,
}
...eventType.metadata,
apps: eventType.metadata?.apps as Prisma.JsonValue,
}
: {},
},
paymentAppCredentials: eventTypePaymentAppCredential as IEventTypePaymentCredentialType,
@@ -2606,7 +2605,7 @@ async function handler(
* We are open to renaming it to something more descriptive.
*/
export class RegularBookingService implements IBookingService {
constructor(private readonly deps: IBookingServiceDependencies) {}
constructor(private readonly deps: IBookingServiceDependencies) { }
async createBooking(input: { bookingData: CreateRegularBookingData; bookingMeta?: CreateBookingMeta }) {
return handler({ bookingData: input.bookingData, ...input.bookingMeta }, this.deps);
@@ -26,7 +26,6 @@ export const useApiV2AvailableSlots = ({
rest.usernameList,
rest.routedTeamMemberIds,
rest.skipContactOwner,
rest._shouldServeCache,
rest.teamMemberEmail,
rest.embedConnectVersion ?? false,
],
@@ -76,8 +76,6 @@ export const useSchedule = ({
? getRoutedTeamMemberIdsFromSearchParams(new URLSearchParams(searchParams.toString()))
: null;
const skipContactOwner = searchParams ? searchParams.get("cal.skipContactOwner") === "true" : false;
const _cacheParam = searchParams?.get("cal.cache");
const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined;
const utils = trpc.useUtils();
const routingFormResponseIdParam = searchParams?.get("cal.routingFormResponseId");
const queuedFormResponseId = searchParams?.get("cal.queuedFormResponseId");
@@ -108,7 +106,6 @@ export const useSchedule = ({
teamMemberEmail,
routedTeamMemberIds,
skipContactOwner,
_shouldServeCache,
...(queuedFormResponseId ? { queuedFormResponseId } : { routingFormResponseId }),
email,
// Ensures that connectVersion causes a refresh of the data
@@ -71,9 +71,6 @@ export const useGetRoutingFormUrlProps = ({ routingFormUrl }: { routingFormUrl?:
...(routingSearchParams.get("cal.isBookingDryRun") && {
["cal.isBookingDryRun"]: routingSearchParams.get("cal.isBookingDryRun") ?? undefined,
}),
...(routingSearchParams.get("cal.cache") && {
["cal.cache"]: routingSearchParams.get("cal.cache") ?? undefined,
}),
...(routingSearchParams.get("cal.routingFormResponseId") && {
["cal.routingFormResponseId"]: routingSearchParams.get("cal.routingFormResponseId") ?? undefined,
}),
@@ -259,7 +259,6 @@ const BookerPlatformWrapperComponent = (
const [routingParams, setRoutingParams] = useState<{
routedTeamMemberIds?: number[];
_shouldServeCache?: boolean;
skipContactOwner?: boolean;
isBookingDryRun?: boolean;
}>({});
@@ -272,15 +271,12 @@ const BookerPlatformWrapperComponent = (
const routedTeamMemberIds = getRoutedTeamMemberIdsFromSearchParams(searchParams);
const skipContactOwner = searchParams.get("cal.skipContactOwner") === "true";
const _cacheParam = searchParams?.get("cal.cache");
const _shouldServeCache = _cacheParam ? _cacheParam === "true" : undefined;
const isBookingDryRun =
searchParams?.get("cal.isBookingDryRun")?.toLowerCase() === "true" ||
searchParams?.get("cal.sandbox")?.toLowerCase() === "true";
setRoutingParams({
...(skipContactOwner ? { skipContactOwner } : {}),
...(routedTeamMemberIds ? { routedTeamMemberIds } : {}),
...(_shouldServeCache ? { _shouldServeCache } : {}),
...(isBookingDryRun ? { isBookingDryRun } : {}),
});
}, [routingFormSearchParams]);
@@ -28,7 +28,6 @@ export const useAvailableSlots = ({
rest.usernameList,
rest.routedTeamMemberIds,
rest.skipContactOwner,
rest._shouldServeCache,
rest.teamMemberEmail,
],
queryFn: () => {
-1
View File
@@ -13,7 +13,6 @@ export type RoutingFormSearchParams = {
["cal.reroutingFormResponses"]?: string;
["cal.skipContactOwner"]?: string;
["cal.isBookingDryRun"]?: string;
["cal.cache"]?: string;
["cal.routingFormResponseId"]?: string;
["cal.crmAppSlug"]?: string;
["cal.crmOwnerRecordType"]?: string;
@@ -176,12 +176,6 @@ export class GetAvailableSlotsInput_2024_04_15 {
@ApiHideProperty()
routingFormResponseId?: number;
@Transform(({ value }) => value && value.toLowerCase() === "true")
@IsBoolean()
@IsOptional()
@ApiHideProperty()
_shouldServeCache?: boolean;
@Transform(({ value }) => value && value.toLowerCase() === "true")
@IsBoolean()
@IsOptional()
@@ -36,7 +36,6 @@ export const getScheduleSchemaObject = z.object({
_enableTroubleshooter: z.boolean().optional(),
_bypassCalendarBusyTimes: z.boolean().optional(),
_silentCalendarFailures: z.boolean().optional(),
_shouldServeCache: z.boolean().optional(),
routingFormResponseId: z.number().optional(),
queuedFormResponseId: z.string().nullish(),
email: z.string().nullish(),
@@ -149,7 +149,7 @@ function withSlotsCache(
}
export class AvailableSlotsService {
constructor(public readonly dependencies: IAvailableSlotsService) {}
constructor(public readonly dependencies: IAvailableSlotsService) { }
private async _getReservedSlotsAndCleanupExpired({
bookerClientUid,
@@ -200,8 +200,8 @@ export class AvailableSlotsService {
usernameList: Array.isArray(input.usernameList)
? input.usernameList
: input.usernameList
? [input.usernameList]
: [],
? [input.usernameList]
: [],
});
const usersWithOldSelectedCalendars = usersForDynamicEventType.map((user) => withSelectedCalendars(user));
@@ -791,15 +791,15 @@ export class AvailableSlotsService {
const bookingLimits =
eventType?.bookingLimits &&
typeof eventType?.bookingLimits === "object" &&
Object.keys(eventType?.bookingLimits).length > 0
typeof eventType?.bookingLimits === "object" &&
Object.keys(eventType?.bookingLimits).length > 0
? parseBookingLimit(eventType?.bookingLimits)
: null;
const durationLimits =
eventType?.durationLimits &&
typeof eventType?.durationLimits === "object" &&
Object.keys(eventType?.durationLimits).length > 0
typeof eventType?.durationLimits === "object" &&
Object.keys(eventType?.durationLimits).length > 0
? parseDurationLimit(eventType?.durationLimits)
: null;
@@ -962,15 +962,14 @@ export class AvailableSlotsService {
_enableTroubleshooter: enableTroubleshooter = false,
_bypassCalendarBusyTimes: bypassBusyCalendarTimes = false,
_silentCalendarFailures: silentCalendarFailures = false,
_shouldServeCache,
routingFormResponseId,
queuedFormResponseId,
} = input;
const orgDetails = input?.orgSlug
? {
currentOrgDomain: input.orgSlug,
isValidOrgDomain: !!input.orgSlug && !RESERVED_SUBDOMAINS.includes(input.orgSlug),
}
currentOrgDomain: input.orgSlug,
isValidOrgDomain: !!input.orgSlug && !RESERVED_SUBDOMAINS.includes(input.orgSlug),
}
: orgDomainConfig(ctx?.req);
if (process.env.INTEGRATION_TEST_MODE === "true") {
@@ -983,10 +982,7 @@ export class AvailableSlotsService {
throw new TRPCError({ code: "NOT_FOUND" });
}
const shouldServeCache = await this.dependencies.cacheService.getShouldServeCache(
_shouldServeCache,
eventType.team?.id
);
const shouldServeCache = false
if (isEventTypeLoggingEnabled({ eventTypeId: eventType.id })) {
logger.settings.minLevel = 2;
}
@@ -1189,10 +1185,10 @@ export class AvailableSlotsService {
const travelSchedules =
isDefaultSchedule && !eventType.useBookerTimezone
? restrictionSchedule.user.travelSchedules.map((schedule) => ({
startDate: dayjs(schedule.startDate),
endDate: schedule.endDate ? dayjs(schedule.endDate) : undefined,
timeZone: schedule.timeZone,
}))
startDate: dayjs(schedule.startDate),
endDate: schedule.endDate ? dayjs(schedule.endDate) : undefined,
timeZone: schedule.timeZone,
}))
: [];
const { dateRanges: restrictionRanges } = buildDateRanges({
@@ -1287,9 +1283,9 @@ export class AvailableSlotsService {
(
item:
| {
time: dayjs.Dayjs;
userIds?: number[] | undefined;
}
time: dayjs.Dayjs;
userIds?: number[] | undefined;
}
| undefined
): item is {
time: dayjs.Dayjs;
@@ -1458,23 +1454,23 @@ export class AvailableSlotsService {
const troubleshooterData = enableTroubleshooter
? {
troubleshooter: {
routedTeamMemberIds: routedTeamMemberIds,
// One that Salesforce asked for
askedContactOwner: contactOwnerEmailFromInput,
// One that we used as per Routing skipContactOwner flag
consideredContactOwner: contactOwnerEmail,
// All hosts that have been checked for availability. If no routedTeamMemberIds are provided, this will be same as hosts.
routedHosts: usersWithCredentials.map((user) => {
return {
userId: user.id,
};
}),
hostsAfterSegmentMatching: allHosts.map((host) => ({
userId: host.user.id,
})),
},
}
troubleshooter: {
routedTeamMemberIds: routedTeamMemberIds,
// One that Salesforce asked for
askedContactOwner: contactOwnerEmailFromInput,
// One that we used as per Routing skipContactOwner flag
consideredContactOwner: contactOwnerEmail,
// All hosts that have been checked for availability. If no routedTeamMemberIds are provided, this will be same as hosts.
routedHosts: usersWithCredentials.map((user) => {
return {
userId: user.id,
};
}),
hostsAfterSegmentMatching: allHosts.map((host) => ({
userId: host.user.id,
})),
},
}
: null;
return {