test: More tests in booking flow (#11510)

This commit is contained in:
Hariom Balhara
2023-09-30 13:28:52 +00:00
committed by GitHub
parent 20898e1505
commit caf1b4c5d7
10 changed files with 1545 additions and 197 deletions
@@ -10,7 +10,7 @@ import "vitest-fetch-mock";
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
import { handleStripePaymentSuccess } from "@calcom/features/ee/payments/api/webhook";
import { HttpError } from "@calcom/lib/http-error";
import type { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import type { SchedulingType } from "@calcom/prisma/enums";
import type { BookingStatus } from "@calcom/prisma/enums";
@@ -74,6 +74,7 @@ type InputUser = typeof TestData.users.example & { id: number } & {
}[];
timeZone: string;
}[];
destinationCalendar?: Prisma.DestinationCalendarCreateInput;
};
export type InputEventType = {
@@ -92,6 +93,7 @@ export type InputEventType = {
beforeEventBuffer?: number;
afterEventBuffer?: number;
requiresConfirmation?: boolean;
destinationCalendar?: Prisma.DestinationCalendarCreateInput;
} & Partial<Omit<Prisma.EventTypeCreateInput, "users">>;
type InputBooking = {
@@ -124,17 +126,31 @@ const Timezones = {
logger.setSettings({ minLevel: "silly" });
async function addEventTypesToDb(
eventTypes: (Prisma.EventTypeCreateInput & {
eventTypes: (Omit<Prisma.EventTypeCreateInput, "users" | "worflows" | "destinationCalendar"> & {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
users?: any[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
workflows?: any[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
destinationCalendar?: any;
})[]
) {
logger.silly("TestData: Add EventTypes to DB", JSON.stringify(eventTypes));
await prismock.eventType.createMany({
data: eventTypes,
});
logger.silly(
"TestData: All EventTypes in DB are",
JSON.stringify({
eventTypes: await prismock.eventType.findMany({
include: {
users: true,
workflows: true,
destinationCalendar: true,
},
}),
})
);
}
async function addEventTypes(eventTypes: InputEventType[], usersStore: InputUser[]) {
@@ -174,6 +190,11 @@ async function addEventTypes(eventTypes: InputEventType[], usersStore: InputUser
...eventType,
workflows: [],
users,
destinationCalendar: eventType.destinationCalendar
? {
create: eventType.destinationCalendar,
}
: eventType.destinationCalendar,
};
});
logger.silly("TestData: Creating EventType", JSON.stringify(eventTypesWithUsers));
@@ -266,6 +287,9 @@ async function addUsersToDb(users: (Prisma.UserCreateInput & { schedules: Prisma
await prismock.user.createMany({
data: users,
});
logger.silly("Added users to Db", {
allUsers: await prismock.user.findMany(),
});
}
async function addUsers(users: InputUser[]) {
@@ -298,6 +322,15 @@ async function addUsers(users: InputUser[]) {
},
};
}
if (user.selectedCalendars) {
newUser.selectedCalendars = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
createMany: {
data: user.selectedCalendars,
},
};
}
return newUser;
});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -569,6 +602,7 @@ export function getOrganizer({
schedules,
credentials,
selectedCalendars,
destinationCalendar,
}: {
name: string;
email: string;
@@ -576,6 +610,7 @@ export function getOrganizer({
schedules: InputUser["schedules"];
credentials?: InputCredential[];
selectedCalendars?: InputSelectedCalendar[];
destinationCalendar?: Prisma.DestinationCalendarCreateInput;
}) {
return {
...TestData.users.example,
@@ -585,6 +620,7 @@ export function getOrganizer({
schedules,
credentials,
selectedCalendars,
destinationCalendar,
};
}
@@ -599,7 +635,7 @@ export function getScenarioData({
{
organizer: ReturnType<typeof getOrganizer>;
eventTypes: ScenarioData["eventTypes"];
apps: ScenarioData["apps"];
apps?: ScenarioData["apps"];
usersApartFromOrganizer?: ScenarioData["users"];
webhooks?: ScenarioData["webhooks"];
bookings?: ScenarioData["bookings"];
@@ -624,7 +660,19 @@ export function getScenarioData({
description: `It's a test event type - ${index + 1}`,
};
}),
users,
users: users.map((user) => {
const newUser = {
...user,
};
if (user.destinationCalendar) {
newUser.destinationCalendar = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
create: user.destinationCalendar,
};
}
return newUser;
}),
apps: [...apps],
webhooks,
bookings: bookings || [],
@@ -651,15 +699,23 @@ export function mockNoTranslations() {
});
}
export function mockCalendarToHaveNoBusySlots(
/**
* @param metadataLookupKey
* @param calendarData Specify uids and other data to be faked to be returned by createEvent and updateEvent
*/
export function mockCalendar(
metadataLookupKey: keyof typeof appStoreMetadata,
calendarData?: {
create: {
uid: string;
create?: {
uid?: string;
};
update?: {
uid: string;
};
busySlots?: { start: `${string}Z`; end: `${string}Z` }[];
creationCrash?: boolean;
updationCrash?: boolean;
getAvailabilityCrash?: boolean;
}
) {
const appStoreLookupKey = metadataLookupKey;
@@ -685,17 +741,17 @@ export function mockCalendarToHaveNoBusySlots(
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
createEvent: async function (...rest: any[]): Promise<NewCalendarEventType> {
if (calendarData?.creationCrash) {
throw new Error("MockCalendarService.createEvent fake error");
}
const [calEvent, credentialId] = rest;
logger.silly(
"mockCalendarToHaveNoBusySlots.createEvent",
JSON.stringify({ calEvent, credentialId })
);
logger.silly("mockCalendar.createEvent", JSON.stringify({ calEvent, credentialId }));
createEventCalls.push(rest);
return Promise.resolve({
type: app.type,
additionalInfo: {},
uid: "PROBABLY_UNUSED_UID",
id: normalizedCalendarData.create.uid,
id: normalizedCalendarData.create?.uid || "FALLBACK_MOCK_ID",
// Password and URL seems useless for CalendarService, plan to remove them if that's the case
password: "MOCK_PASSWORD",
url: "https://UNUSED_URL",
@@ -703,11 +759,11 @@ export function mockCalendarToHaveNoBusySlots(
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
updateEvent: async function (...rest: any[]): Promise<NewCalendarEventType> {
if (calendarData?.updationCrash) {
throw new Error("MockCalendarService.updateEvent fake error");
}
const [uid, event, externalCalendarId] = rest;
logger.silly(
"mockCalendarToHaveNoBusySlots.updateEvent",
JSON.stringify({ uid, event, externalCalendarId })
);
logger.silly("mockCalendar.updateEvent", JSON.stringify({ uid, event, externalCalendarId }));
// eslint-disable-next-line prefer-rest-params
updateEventCalls.push(rest);
return Promise.resolve({
@@ -715,15 +771,18 @@ export function mockCalendarToHaveNoBusySlots(
additionalInfo: {},
uid: "PROBABLY_UNUSED_UID",
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
id: normalizedCalendarData.update!.uid!,
id: normalizedCalendarData.update?.uid || "FALLBACK_MOCK_ID",
// Password and URL seems useless for CalendarService, plan to remove them if that's the case
password: "MOCK_PASSWORD",
url: "https://UNUSED_URL",
});
},
getAvailability: (): Promise<EventBusyDate[]> => {
getAvailability: async (): Promise<EventBusyDate[]> => {
if (calendarData?.getAvailabilityCrash) {
throw new Error("MockCalendarService.getAvailability fake error");
}
return new Promise((resolve) => {
resolve([]);
resolve(calendarData?.busySlots || []);
});
},
};
@@ -736,10 +795,42 @@ export function mockCalendarToHaveNoBusySlots(
};
}
export function mockSuccessfulVideoMeetingCreation({
export function mockCalendarToHaveNoBusySlots(
metadataLookupKey: keyof typeof appStoreMetadata,
calendarData?: {
create: {
uid?: string;
};
update?: {
uid: string;
};
}
) {
calendarData = calendarData || {
create: {
uid: "MOCK_ID",
},
update: {
uid: "UPDATED_MOCK_ID",
},
};
return mockCalendar(metadataLookupKey, { ...calendarData, busySlots: [] });
}
export function mockCalendarToCrashOnCreateEvent(metadataLookupKey: keyof typeof appStoreMetadata) {
return mockCalendar(metadataLookupKey, { creationCrash: true });
}
export function mockCalendarToCrashOnUpdateEvent(metadataLookupKey: keyof typeof appStoreMetadata) {
return mockCalendar(metadataLookupKey, { updationCrash: true });
}
export function mockVideoApp({
metadataLookupKey,
appStoreLookupKey,
videoMeetingData,
creationCrash,
updationCrash,
}: {
metadataLookupKey: string;
appStoreLookupKey?: string;
@@ -748,6 +839,8 @@ export function mockSuccessfulVideoMeetingCreation({
id: string;
url: string;
};
creationCrash?: boolean;
updationCrash?: boolean;
}) {
appStoreLookupKey = appStoreLookupKey || metadataLookupKey;
videoMeetingData = videoMeetingData || {
@@ -772,7 +865,11 @@ export function mockSuccessfulVideoMeetingCreation({
VideoApiAdapter: () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
createMeeting: (...rest: any[]) => {
if (creationCrash) {
throw new Error("MockVideoApiAdapter.createMeeting fake error");
}
createMeetingCalls.push(rest);
return Promise.resolve({
type: appStoreMetadata[metadataLookupKey as keyof typeof appStoreMetadata].type,
...videoMeetingData,
@@ -780,6 +877,9 @@ export function mockSuccessfulVideoMeetingCreation({
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
updateMeeting: async (...rest: any[]) => {
if (updationCrash) {
throw new Error("MockVideoApiAdapter.updateMeeting fake error");
}
const [bookingRef, calEvent] = rest;
updateMeetingCalls.push(rest);
if (!bookingRef.type) {
@@ -808,6 +908,40 @@ export function mockSuccessfulVideoMeetingCreation({
};
}
export function mockSuccessfulVideoMeetingCreation({
metadataLookupKey,
appStoreLookupKey,
videoMeetingData,
}: {
metadataLookupKey: string;
appStoreLookupKey?: string;
videoMeetingData?: {
password: string;
id: string;
url: string;
};
}) {
return mockVideoApp({
metadataLookupKey,
appStoreLookupKey,
videoMeetingData,
});
}
export function mockVideoAppToCrashOnCreateMeeting({
metadataLookupKey,
appStoreLookupKey,
}: {
metadataLookupKey: string;
appStoreLookupKey?: string;
}) {
return mockVideoApp({
metadataLookupKey,
appStoreLookupKey,
creationCrash: true,
});
}
export function mockPaymentApp({
metadataLookupKey,
appStoreLookupKey,
@@ -885,11 +1019,8 @@ export async function mockPaymentSuccessWebhookFromStripe({ externalId }: { exte
try {
await handleStripePaymentSuccess(getMockedStripePaymentEvent({ paymentIntentId: externalId }));
} catch (e) {
if (!(e instanceof HttpError)) {
logger.silly("mockPaymentSuccessWebhookFromStripe:catch", JSON.stringify(e));
} else {
logger.error("mockPaymentSuccessWebhookFromStripe:catch", JSON.stringify(e));
}
logger.silly("mockPaymentSuccessWebhookFromStripe:catch", JSON.stringify(e));
webhookResponse = e as HttpError;
}
return { webhookResponse };
+99 -18
View File
@@ -1,11 +1,12 @@
import prismaMock from "../../../../../tests/libs/__mocks__/prisma";
import type { Booking, BookingReference } from "@prisma/client";
import type { WebhookTriggerEvents } from "@prisma/client";
import type { WebhookTriggerEvents, Booking, BookingReference } from "@prisma/client";
import { expect } from "vitest";
import "vitest-fetch-mock";
import logger from "@calcom/lib/logger";
import { BookingStatus } from "@calcom/prisma/enums";
import type { CalendarEvent } from "@calcom/types/Calendar";
import type { Fixtures } from "@calcom/web/test/fixtures/fixtures";
import type { InputEventType } from "./bookingScenario";
@@ -30,7 +31,9 @@ expect.extend({
to: string
) {
const testEmail = emails.get().find((email) => email.to.includes(to));
const emailsToLog = emails.get().map((email) => ({ to: email.to, html: email.html }));
if (!testEmail) {
logger.silly("All Emails", JSON.stringify({ numEmails: emailsToLog.length, emailsToLog }));
return {
pass: false,
message: () => `No email sent to ${to}`,
@@ -43,6 +46,10 @@ expect.extend({
}
isToAddressExpected = expectedEmail.to === testEmail.to;
if (!isHtmlContained || !isToAddressExpected) {
logger.silly("All Emails", JSON.stringify({ numEmails: emailsToLog.length, emailsToLog }));
}
return {
pass: isHtmlContained && isToAddressExpected,
message: () => {
@@ -150,6 +157,59 @@ export function expectSuccessfulBookingCreationEmails({
);
}
export function expectBrokenIntegrationEmails({
emails,
organizer,
booker,
}: {
emails: Fixtures["emails"];
organizer: { email: string; name: string };
booker: { email: string; name: string };
}) {
// Broken Integration email is only sent to the Organizer
expect(emails).toHaveEmail(
{
htmlToContain: "<title>broken_integration</title>",
to: `${organizer.email}`,
},
`${organizer.email}`
);
// expect(emails).toHaveEmail(
// {
// htmlToContain: "<title>confirmed_event_type_subject</title>",
// to: `${booker.name} <${booker.email}>`,
// },
// `${booker.name} <${booker.email}>`
// );
}
export function expectCalendarEventCreationFailureEmails({
emails,
organizer,
booker,
}: {
emails: Fixtures["emails"];
organizer: { email: string; name: string };
booker: { email: string; name: string };
}) {
expect(emails).toHaveEmail(
{
htmlToContain: "<title>broken_integration</title>",
to: `${organizer.email}`,
},
`${organizer.email}`
);
expect(emails).toHaveEmail(
{
htmlToContain: "<title>calendar_event_creation_failure_subject</title>",
to: `${booker.name} <${booker.email}>`,
},
`${booker.name} <${booker.email}>`
);
}
export function expectSuccessfulBookingRescheduledEmails({
emails,
organizer,
@@ -394,20 +454,28 @@ export function expectSuccessfulCalendarEventCreationInCalendar(
updateEventCalls: any[];
},
expected: {
externalCalendarId: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
calEvent: any;
uid: string;
calendarId: string | null;
videoCallUrl: string;
}
) {
expect(calendarMock.createEventCalls.length).toBe(1);
const call = calendarMock.createEventCalls[0];
const uid = call[0];
const calendarEvent = call[1];
const externalId = call[2];
expect(uid).toBe(expected.uid);
expect(calendarEvent).toEqual(expect.objectContaining(expected.calEvent));
expect(externalId).toBe(expected.externalCalendarId);
const calEvent = call[0];
expect(calEvent).toEqual(
expect.objectContaining({
destinationCalendar: expected.calendarId
? [
expect.objectContaining({
externalId: expected.calendarId,
}),
]
: null,
videoCallData: expect.objectContaining({
url: expected.videoCallUrl,
}),
})
);
}
export function expectSuccessfulCalendarEventUpdationInCalendar(
@@ -419,8 +487,7 @@ export function expectSuccessfulCalendarEventUpdationInCalendar(
},
expected: {
externalCalendarId: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
calEvent: any;
calEvent: Partial<CalendarEvent>;
uid: string;
}
) {
@@ -443,8 +510,7 @@ export function expectSuccessfulVideoMeetingCreationInCalendar(
},
expected: {
externalCalendarId: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
calEvent: any;
calEvent: Partial<CalendarEvent>;
uid: string;
}
) {
@@ -468,8 +534,7 @@ export function expectSuccessfulVideoMeetingUpdationInCalendar(
expected: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bookingRef: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
calEvent: any;
calEvent: Partial<CalendarEvent>;
}
) {
expect(videoMock.updateMeetingCalls.length).toBe(1);
@@ -479,3 +544,19 @@ export function expectSuccessfulVideoMeetingUpdationInCalendar(
expect(bookingRef).toEqual(expect.objectContaining(expected.bookingRef));
expect(calendarEvent).toEqual(expect.objectContaining(expected.calEvent));
}
export async function expectBookingInDBToBeRescheduledFromTo({ from, to }: { from: any; to: any }) {
// Expect previous booking to be cancelled
await expectBookingToBeInDatabase({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...from,
status: BookingStatus.CANCELLED,
});
// Expect new booking to be created
await expectBookingToBeInDatabase({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...to,
status: BookingStatus.ACCEPTED,
});
}
+7 -4
View File
@@ -8,6 +8,7 @@ import dayjs from "@calcom/dayjs";
import { getUid } from "@calcom/lib/CalEventParser";
import logger from "@calcom/lib/logger";
import { getPiiFreeCalendarEvent } from "@calcom/lib/piiFreeData";
import { safeStringify } from "@calcom/lib/safeStringify";
import { performance } from "@calcom/lib/server/perfObserver";
import type {
CalendarEvent,
@@ -103,6 +104,8 @@ export const getConnectedCalendars = async (
}
}
log.error("getConnectedCalendars failed", safeStringify({ error, item }));
return {
integration: cleanIntegrationKeys(item.integration),
credentialId: item.credential.id,
@@ -211,7 +214,7 @@ export const getBusyCalendarTimes = async (
const endDate = dayjs(dateTo).endOf("month").add(14, "hours").format();
results = await getCalendarsEvents(withCredentials, startDate, endDate, selectedCalendars);
} catch (e) {
logger.warn(e);
log.warn(safeStringify(e));
}
return results.reduce((acc, availability) => acc.concat(availability), []);
};
@@ -292,7 +295,7 @@ export const updateEvent = async (
let calWarnings: string[] | undefined = [];
log.debug(
"Updating calendar event",
JSON.stringify({
safeStringify({
bookingRefUid,
calEvent: getPiiFreeCalendarEvent(calEvent),
})
@@ -301,7 +304,7 @@ export const updateEvent = async (
log.error(
"updateEvent failed",
"bookingRefUid is empty",
JSON.stringify({ calEvent: getPiiFreeCalendarEvent(calEvent) })
safeStringify({ calEvent: getPiiFreeCalendarEvent(calEvent) })
);
}
const updatedResult: NewCalendarEventType | NewCalendarEventType[] | undefined =
@@ -318,7 +321,7 @@ export const updateEvent = async (
// await sendBrokenIntegrationEmail(calEvent, "calendar");
log.error(
"updateEvent failed",
JSON.stringify({ e, calEvent: getPiiFreeCalendarEvent(calEvent) })
safeStringify({ e, calEvent: getPiiFreeCalendarEvent(calEvent) })
);
if (e?.calError) {
calError = e.calError;
+3 -24
View File
@@ -1,4 +1,4 @@
import type { Booking, DestinationCalendar } from "@prisma/client";
import type { DestinationCalendar } from "@prisma/client";
// eslint-disable-next-line no-restricted-imports
import { cloneDeep, merge } from "lodash";
import { v5 as uuidv5 } from "uuid";
@@ -10,6 +10,7 @@ import { appKeysSchema as calVideoKeysSchema } from "@calcom/app-store/dailyvide
import { getEventLocationTypeFromApp, MeetLocationType } from "@calcom/app-store/locations";
import getApps from "@calcom/app-store/utils";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import prisma from "@calcom/prisma";
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
import { createdEventSchema } from "@calcom/prisma/zod-utils";
@@ -403,7 +404,7 @@ export default class EventManager {
} else {
logger.silly(
"No destination Calendar found, falling back to first connected calendar",
JSON.stringify({
safeStringify({
calendarCredentials: this.calendarCredentials,
})
);
@@ -659,26 +660,4 @@ export default class EventManager {
);
}
}
/**
* Update event to set a cancelled event placeholder on users calendar
* remove if virtual calendar is already done and user availability its read from there
* and not only in their calendars
* @param event
* @param booking
* @public
*/
public async updateAndSetCancelledPlaceholder(event: CalendarEvent, booking: PartialBooking) {
await this.updateAllCalendarEvents(event, booking);
}
public async rescheduleBookingWithSeats(
originalBooking: Booking,
newTimeSlotBooking?: Booking,
owner?: boolean
) {
// Get originalBooking
// If originalBooking has only one attendee we should do normal reschedule
// Change current event attendees in everyone calendar
}
}
+9
View File
@@ -1,10 +1,13 @@
import type { SelectedCalendar } from "@prisma/client";
import { getCalendar } from "@calcom/app-store/_utils/getCalendar";
import logger from "@calcom/lib/logger";
import { getPiiFreeCredential, getPiiFreeSelectedCalendar } from "@calcom/lib/piiFreeData";
import { performance } from "@calcom/lib/server/perfObserver";
import type { EventBusyDate } from "@calcom/types/Calendar";
import type { CredentialPayload } from "@calcom/types/Credential";
const log = logger.getChildLogger({ prefix: ["getCalendarsEvents"] });
const getCalendarsEvents = async (
withCredentials: CredentialPayload[],
dateFrom: string,
@@ -15,6 +18,7 @@ const getCalendarsEvents = async (
.filter((credential) => credential.type.endsWith("_calendar"))
// filter out invalid credentials - these won't work.
.filter((credential) => !credential.invalid);
const calendars = await Promise.all(calendarCredentials.map((credential) => getCalendar(credential)));
performance.mark("getBusyCalendarTimesStart");
const results = calendars.map(async (c, i) => {
@@ -48,6 +52,11 @@ const getCalendarsEvents = async (
"getBusyCalendarTimesStart",
"getBusyCalendarTimesEnd"
);
log.debug({
calendarCredentials: calendarCredentials.map(getPiiFreeCredential),
selectedCalendars: selectedCalendars.map(getPiiFreeSelectedCalendar),
calendarEvents: awaitedResults,
});
return awaitedResults;
};
+2 -1
View File
@@ -7,6 +7,7 @@ import { sendBrokenIntegrationEmail } from "@calcom/emails";
import { getUid } from "@calcom/lib/CalEventParser";
import logger from "@calcom/lib/logger";
import { getPiiFreeCalendarEvent } from "@calcom/lib/piiFreeData";
import { safeStringify } from "@calcom/lib/safeStringify";
import { prisma } from "@calcom/prisma";
import type { GetRecordingsResponseSchema } from "@calcom/prisma/zod-utils";
import type { CalendarEvent, EventBusyDate } from "@calcom/types/Calendar";
@@ -99,7 +100,7 @@ const createMeeting = async (credential: CredentialPayload, calEvent: CalendarEv
returnObject = { ...returnObject, createdEvent: createdMeeting, success: true };
} catch (err) {
await sendBrokenIntegrationEmail(calEvent, "video");
log.error("createMeeting failed", JSON.stringify({ err, calEvent: getPiiFreeCalendarEvent(calEvent) }));
log.error("createMeeting failed", safeStringify({ err, calEvent: getPiiFreeCalendarEvent(calEvent) }));
// Default to calVideo
const defaultMeeting = await createMeetingWithCalVideo(calEvent);
File diff suppressed because it is too large Load Diff
@@ -60,6 +60,7 @@ import { HttpError } from "@calcom/lib/http-error";
import isOutOfBounds, { BookingDateInPastError } from "@calcom/lib/isOutOfBounds";
import logger from "@calcom/lib/logger";
import { handlePayment } from "@calcom/lib/payment/handlePayment";
import { safeStringify } from "@calcom/lib/safeStringify";
import { checkBookingLimits, checkDurationLimits, getLuckyUser } from "@calcom/lib/server";
import { getBookerUrl } from "@calcom/lib/server/getBookerUrl";
import { getTranslation } from "@calcom/lib/server/i18n";
@@ -213,7 +214,7 @@ function checkForConflicts(busyTimes: BufferedBusyTimes, time: dayjs.ConfigType,
// Check if time is between start and end times
if (dayjs(time).isBetween(startTime, endTime, null, "[)")) {
log.error(
`NAUF: start between a busy time slot ${JSON.stringify({
`NAUF: start between a busy time slot ${safeStringify({
...busyTime,
time: dayjs(time).format(),
})}`
@@ -223,7 +224,7 @@ function checkForConflicts(busyTimes: BufferedBusyTimes, time: dayjs.ConfigType,
// Check if slot end time is between start and end time
if (dayjs(time).add(length, "minutes").isBetween(startTime, endTime)) {
log.error(
`NAUF: Ends between a busy time slot ${JSON.stringify({
`NAUF: Ends between a busy time slot ${safeStringify({
...busyTime,
time: dayjs(time).add(length, "minutes").format(),
})}`
@@ -2123,10 +2124,7 @@ async function handler(
message: "Booking Rescheduling failed",
};
loggerWithEventDetails.error(
`Booking ${organizerUser.name} failed`,
JSON.stringify({ error, results })
);
loggerWithEventDetails.error(`Booking ${organizerUser.name} failed`, safeStringify({ error, results }));
} else {
const metadata: AdditionalInformation = {};
const calendarResult = results.find((result) => result.type.includes("_calendar"));
@@ -2180,7 +2178,7 @@ async function handler(
loggerWithEventDetails.error(
`Booking ${organizerUser.username} failed`,
JSON.stringify({ error, results })
safeStringify({ error, results })
);
} else {
const metadata: AdditionalInformation = {};
+29
View File
@@ -1,3 +1,5 @@
import type { Credential, SelectedCalendar } from "@prisma/client";
import type { CalendarEvent } from "@calcom/types/Calendar";
export function getPiiFreeCalendarEvent(calEvent: CalendarEvent) {
@@ -44,3 +46,30 @@ export function getPiiFreeBooking(booking: {
// .... Add all other props here that we don't want to be logged. It prevents those properties from being logged accidentally
};
}
export function getPiiFreeCredential(credential: Partial<Credential>) {
return {
id: credential.id,
invalid: credential.invalid,
appId: credential.appId,
userId: credential.userId,
type: credential.type,
teamId: credential.teamId,
/**
* Let's just get a boolean value for PII sensitive fields so that we atleast know if it's present or not
*/
key: !!credential.key,
};
}
export function getPiiFreeSelectedCalendar(selectedCalendar: Partial<SelectedCalendar>) {
return {
integration: selectedCalendar.integration,
userId: selectedCalendar.userId,
/**
* Let's just get a boolean value for PII sensitive fields so that we atleast know if it's present or not
*/
externalId: !!selectedCalendar.externalId,
credentialId: !!selectedCalendar.credentialId,
};
}
+8
View File
@@ -0,0 +1,8 @@
export function safeStringify(obj: unknown) {
try {
// Avoid crashing on circular references
return JSON.stringify(obj);
} catch (e) {
return obj;
}
}