fix: Missing bookingId in BOOKING_CANCELLED webhook payload (#22713)
* send bookingId in BOOKING_CANCELLED through requestReschedule * fix: make customInputs nullable in BookingWebhookFactory * fix: make customInputs nullable in BookingWebhookFactory
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import type { Person } from "@calcom/types/Calendar";
|
||||
import type { JsonValue } from "@calcom/types/JsonObject";
|
||||
|
||||
function isObjectButNotArray(obj: unknown): obj is Record<string, unknown> {
|
||||
return typeof obj === "object" && !Array.isArray(obj);
|
||||
}
|
||||
|
||||
type DestinationCalendar = {
|
||||
id: number;
|
||||
integration: string;
|
||||
externalId: string;
|
||||
primaryEmail: string | null;
|
||||
userId: number | null;
|
||||
eventTypeId: number | null;
|
||||
credentialId: number | null;
|
||||
createdAt: Date | null;
|
||||
updatedAt: Date | null;
|
||||
delegationCredentialId: string | null;
|
||||
domainWideDelegationCredentialId: string | null;
|
||||
};
|
||||
|
||||
type Response = {
|
||||
label: string;
|
||||
value: string | boolean | string[] | { value: string; optionValue: string } | Record<string, string>;
|
||||
isHidden?: boolean | undefined;
|
||||
};
|
||||
|
||||
interface BaseWebhookPayload {
|
||||
bookingId: number;
|
||||
title: string;
|
||||
eventSlug: string | null;
|
||||
description: string | null;
|
||||
customInputs: JsonValue | null;
|
||||
responses: Record<string, Response>;
|
||||
userFieldsResponses: Record<string, Response>;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
organizer: Person;
|
||||
attendees: Person[];
|
||||
uid: string;
|
||||
location: string | null;
|
||||
destinationCalendar: DestinationCalendar | null;
|
||||
cancellationReason: string | null;
|
||||
iCalUID: string | null;
|
||||
smsReminderNumber?: string;
|
||||
cancelledBy: string | null;
|
||||
}
|
||||
|
||||
interface CancelledEventPayload extends BaseWebhookPayload {
|
||||
cancelledBy: string;
|
||||
cancellationReason: string;
|
||||
}
|
||||
|
||||
export class BookingWebhookFactory {
|
||||
private getType(params: BaseWebhookPayload) {
|
||||
return params.eventSlug || params.title || "";
|
||||
}
|
||||
|
||||
private getTitle(params: BaseWebhookPayload) {
|
||||
return params.title || "";
|
||||
}
|
||||
|
||||
private getDestinationCalendar(params: BaseWebhookPayload) {
|
||||
return params.destinationCalendar ? [params.destinationCalendar] : [];
|
||||
}
|
||||
|
||||
private getCustomInputs(params: BaseWebhookPayload) {
|
||||
return isObjectButNotArray(params.customInputs) ? params.customInputs : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates base webhook payload with common fields
|
||||
*/
|
||||
private createBasePayload(params: BaseWebhookPayload) {
|
||||
const {
|
||||
bookingId,
|
||||
title,
|
||||
eventSlug,
|
||||
description,
|
||||
customInputs,
|
||||
startTime,
|
||||
endTime,
|
||||
uid,
|
||||
location,
|
||||
organizer,
|
||||
attendees,
|
||||
responses,
|
||||
userFieldsResponses,
|
||||
destinationCalendar,
|
||||
smsReminderNumber,
|
||||
iCalUID,
|
||||
} = params;
|
||||
|
||||
const basePayload = {
|
||||
bookingId,
|
||||
type: this.getType(params),
|
||||
title: this.getTitle(params),
|
||||
description,
|
||||
customInputs: this.getCustomInputs(params),
|
||||
responses,
|
||||
userFieldsResponses,
|
||||
startTime,
|
||||
endTime,
|
||||
organizer,
|
||||
attendees,
|
||||
uid,
|
||||
location,
|
||||
destinationCalendar: this.getDestinationCalendar(params),
|
||||
iCalUID,
|
||||
smsReminderNumber,
|
||||
};
|
||||
|
||||
return basePayload;
|
||||
}
|
||||
|
||||
public createCancelledEventPayload(params: CancelledEventPayload) {
|
||||
const basePayload = this.createBasePayload({
|
||||
...params,
|
||||
});
|
||||
|
||||
return {
|
||||
...basePayload,
|
||||
cancelledBy: params.cancelledBy,
|
||||
cancellationReason: params.cancellationReason,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import type { Person } from "@calcom/types/Calendar";
|
||||
|
||||
import { BookingWebhookFactory } from "../BookingWebhookFactory";
|
||||
|
||||
const createTestOrganizer = (overrides?: Partial<Person>): Person => ({
|
||||
email: "organizer@example.com",
|
||||
name: "Test Organizer",
|
||||
timeZone: "UTC",
|
||||
language: { locale: "en", translate: (() => "") as any },
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const createTestAttendee = (overrides?: Partial<Person>): Person => ({
|
||||
email: "attendee@example.com",
|
||||
name: "Test Attendee",
|
||||
timeZone: "UTC",
|
||||
language: { locale: "en", translate: (() => "") as any },
|
||||
...overrides,
|
||||
});
|
||||
|
||||
const createTestBooking = (overrides?: Record<string, any>) => ({
|
||||
id: 12345,
|
||||
uid: "test-uid-123",
|
||||
title: "Test Event",
|
||||
description: "Test Description",
|
||||
startTime: new Date("2025-01-24T10:00:00Z"),
|
||||
endTime: new Date("2025-01-24T11:00:00Z"),
|
||||
location: "https://meet.example.com",
|
||||
customInputs: { field1: "value1" },
|
||||
responses: { name: { value: "Test User" } },
|
||||
userFieldsResponses: { company: { value: "Test Corp" } },
|
||||
smsReminderNumber: "+1234567890",
|
||||
iCalUID: "ical-uid-123",
|
||||
...overrides,
|
||||
});
|
||||
|
||||
describe("BookingWebhookFactory", () => {
|
||||
describe("createCancelledEventPayload", () => {
|
||||
it("should create a basic cancelled event payload with required fields", () => {
|
||||
const factory = new BookingWebhookFactory();
|
||||
const booking = createTestBooking();
|
||||
const organizer = createTestOrganizer();
|
||||
const attendees = [createTestAttendee()];
|
||||
|
||||
const payload = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: booking.title,
|
||||
eventSlug: "test-event",
|
||||
description: booking.description,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: booking.location,
|
||||
customInputs: booking.customInputs,
|
||||
responses: booking.responses,
|
||||
userFieldsResponses: booking.userFieldsResponses,
|
||||
smsReminderNumber: booking.smsReminderNumber,
|
||||
iCalUID: booking.iCalUID,
|
||||
organizer,
|
||||
attendees,
|
||||
destinationCalendar: null,
|
||||
cancelledBy: "test@example.com",
|
||||
cancellationReason: "User requested",
|
||||
});
|
||||
|
||||
expect(payload.bookingId).toBe(12345);
|
||||
expect(payload.uid).toBe("test-uid-123");
|
||||
expect(payload.title).toBe("Test Event");
|
||||
expect(payload.description).toBe("Test Description");
|
||||
expect(payload.organizer).toEqual(organizer);
|
||||
expect(payload.attendees).toEqual(attendees);
|
||||
expect(payload.type).toBe("test-event");
|
||||
expect(payload.startTime).toBe(booking.startTime.toISOString());
|
||||
expect(payload.endTime).toBe(booking.endTime.toISOString());
|
||||
expect(payload.location).toBe("https://meet.example.com");
|
||||
expect(payload.customInputs).toEqual({ field1: "value1" });
|
||||
expect(payload.responses).toEqual({ name: { value: "Test User" } });
|
||||
expect(payload.userFieldsResponses).toEqual({ company: { value: "Test Corp" } });
|
||||
expect(payload.smsReminderNumber).toBe("+1234567890");
|
||||
expect(payload.iCalUID).toBe("ical-uid-123");
|
||||
expect(payload.destinationCalendar).toEqual([]);
|
||||
expect(payload.cancellationReason).toBe("User requested");
|
||||
expect(payload.cancelledBy).toBe("test@example.com");
|
||||
|
||||
// Verify no extra properties exist
|
||||
const expectedKeys = [
|
||||
"bookingId",
|
||||
"type",
|
||||
"title",
|
||||
"description",
|
||||
"customInputs",
|
||||
"responses",
|
||||
"userFieldsResponses",
|
||||
"startTime",
|
||||
"endTime",
|
||||
"organizer",
|
||||
"attendees",
|
||||
"uid",
|
||||
"location",
|
||||
"destinationCalendar",
|
||||
"iCalUID",
|
||||
"smsReminderNumber",
|
||||
"cancellationReason",
|
||||
"cancelledBy",
|
||||
];
|
||||
const actualKeys = Object.keys(payload).sort();
|
||||
expect(actualKeys).toEqual(expectedKeys.sort());
|
||||
});
|
||||
|
||||
it("should handle responses and userFieldsResponses", () => {
|
||||
const factory = new BookingWebhookFactory();
|
||||
const booking = createTestBooking();
|
||||
const responses = { field1: "response1", field2: "response2" };
|
||||
const userFieldsResponses = { company: "Test Corp", department: "Engineering" };
|
||||
|
||||
const payload = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: booking.title,
|
||||
eventSlug: null,
|
||||
description: null,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: null,
|
||||
customInputs: null,
|
||||
responses,
|
||||
userFieldsResponses,
|
||||
smsReminderNumber: null,
|
||||
iCalUID: null,
|
||||
organizer: createTestOrganizer(),
|
||||
attendees: [createTestAttendee()],
|
||||
destinationCalendar: null,
|
||||
cancelledBy: "user@example.com",
|
||||
cancellationReason: "Test",
|
||||
});
|
||||
|
||||
expect(payload.responses).toEqual(responses);
|
||||
expect(payload.userFieldsResponses).toEqual(userFieldsResponses);
|
||||
});
|
||||
|
||||
it("should handle destination calendar as single object", () => {
|
||||
const factory = new BookingWebhookFactory();
|
||||
const booking = createTestBooking();
|
||||
const destinationCalendar = {
|
||||
id: 1,
|
||||
integration: "google",
|
||||
externalId: "cal123",
|
||||
primaryEmail: null,
|
||||
userId: null,
|
||||
eventTypeId: null,
|
||||
credentialId: null,
|
||||
createdAt: null,
|
||||
updatedAt: null,
|
||||
delegationCredentialId: null,
|
||||
domainWideDelegationCredentialId: null,
|
||||
};
|
||||
|
||||
const payload = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: booking.title,
|
||||
eventSlug: null,
|
||||
description: null,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: null,
|
||||
customInputs: null,
|
||||
responses: {},
|
||||
userFieldsResponses: {},
|
||||
smsReminderNumber: null,
|
||||
iCalUID: null,
|
||||
organizer: createTestOrganizer(),
|
||||
attendees: [],
|
||||
destinationCalendar,
|
||||
cancelledBy: "user@example.com",
|
||||
cancellationReason: "Test",
|
||||
});
|
||||
|
||||
expect(payload.destinationCalendar).toEqual([destinationCalendar]);
|
||||
});
|
||||
|
||||
it("should handle null destination calendar", () => {
|
||||
const factory = new BookingWebhookFactory();
|
||||
const booking = createTestBooking();
|
||||
|
||||
const payload = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: booking.title,
|
||||
eventSlug: null,
|
||||
description: null,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: null,
|
||||
customInputs: null,
|
||||
responses: {},
|
||||
userFieldsResponses: {},
|
||||
smsReminderNumber: null,
|
||||
iCalUID: null,
|
||||
organizer: createTestOrganizer(),
|
||||
attendees: [],
|
||||
destinationCalendar: null,
|
||||
cancelledBy: "user@example.com",
|
||||
cancellationReason: "Test",
|
||||
});
|
||||
|
||||
expect(payload.destinationCalendar).toEqual([]);
|
||||
});
|
||||
|
||||
it("should handle sms reminder number and iCal UID", () => {
|
||||
const factory = new BookingWebhookFactory();
|
||||
const booking = createTestBooking();
|
||||
|
||||
const payload = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: booking.title,
|
||||
eventSlug: null,
|
||||
description: null,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: null,
|
||||
customInputs: null,
|
||||
responses: {},
|
||||
userFieldsResponses: {},
|
||||
smsReminderNumber: "+1234567890",
|
||||
iCalUID: "ical-uid-123",
|
||||
organizer: createTestOrganizer(),
|
||||
attendees: [],
|
||||
destinationCalendar: null,
|
||||
cancelledBy: "user@example.com",
|
||||
cancellationReason: "Test",
|
||||
});
|
||||
|
||||
expect(payload.smsReminderNumber).toBe("+1234567890");
|
||||
expect(payload.iCalUID).toBe("ical-uid-123");
|
||||
});
|
||||
|
||||
it("should derive type from eventSlug when available, otherwise from title", () => {
|
||||
const factory = new BookingWebhookFactory();
|
||||
const booking = createTestBooking();
|
||||
|
||||
// Test with eventSlug provided
|
||||
const payloadWithSlug = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: "Meeting Title",
|
||||
eventSlug: "team-standup",
|
||||
description: null,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: null,
|
||||
customInputs: null,
|
||||
responses: {},
|
||||
userFieldsResponses: {},
|
||||
smsReminderNumber: null,
|
||||
iCalUID: null,
|
||||
organizer: createTestOrganizer(),
|
||||
attendees: [],
|
||||
destinationCalendar: null,
|
||||
cancelledBy: "user@example.com",
|
||||
cancellationReason: "Test",
|
||||
});
|
||||
|
||||
expect(payloadWithSlug.type).toBe("team-standup");
|
||||
|
||||
// Test with eventSlug null, should use title
|
||||
const payloadWithoutSlug = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: "Meeting Title",
|
||||
eventSlug: null,
|
||||
description: null,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: null,
|
||||
customInputs: null,
|
||||
responses: {},
|
||||
userFieldsResponses: {},
|
||||
smsReminderNumber: null,
|
||||
iCalUID: null,
|
||||
organizer: createTestOrganizer(),
|
||||
attendees: [],
|
||||
destinationCalendar: null,
|
||||
cancelledBy: "user@example.com",
|
||||
cancellationReason: "Test",
|
||||
});
|
||||
|
||||
expect(payloadWithoutSlug.type).toBe("Meeting Title");
|
||||
|
||||
// Test with both eventSlug and title empty
|
||||
const payloadEmpty = factory.createCancelledEventPayload({
|
||||
bookingId: booking.id,
|
||||
uid: booking.uid,
|
||||
title: "",
|
||||
eventSlug: "",
|
||||
description: null,
|
||||
startTime: booking.startTime.toISOString(),
|
||||
endTime: booking.endTime.toISOString(),
|
||||
location: null,
|
||||
customInputs: null,
|
||||
responses: {},
|
||||
userFieldsResponses: {},
|
||||
smsReminderNumber: null,
|
||||
iCalUID: null,
|
||||
organizer: createTestOrganizer(),
|
||||
attendees: [],
|
||||
destinationCalendar: null,
|
||||
cancelledBy: "user@example.com",
|
||||
cancellationReason: "Test",
|
||||
});
|
||||
|
||||
expect(payloadEmpty.type).toBe("");
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -14,18 +14,18 @@ import { getDelegationCredentialOrRegularCredential } from "@calcom/lib/delegati
|
||||
import { getBookerBaseUrl } from "@calcom/lib/getBookerUrl/server";
|
||||
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
|
||||
import { getTeamIdFromEventType } from "@calcom/lib/getTeamIdFromEventType";
|
||||
import { isPrismaObjOrUndefined } from "@calcom/lib/isPrismaObj";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import { safeStringify } from "@calcom/lib/safeStringify";
|
||||
import { getUsersCredentialsIncludeServiceAccountKey } from "@calcom/lib/server/getUsersCredentials";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
import { WorkflowRepository } from "@calcom/lib/server/repository/workflow";
|
||||
import { BookingWebhookFactory } from "@calcom/lib/server/service/BookingWebhookFactory";
|
||||
import { deleteMeeting } from "@calcom/lib/videoClient";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import type { WebhookTriggerEvents } from "@calcom/prisma/enums";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
import type { EventTypeMetadata } from "@calcom/prisma/zod-utils";
|
||||
import type { CalendarEvent, Person } from "@calcom/types/Calendar";
|
||||
import type { Person } from "@calcom/types/Calendar";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
@@ -109,7 +109,7 @@ export const requestRescheduleHandler = async ({ ctx, input }: RequestReschedule
|
||||
|
||||
if (bookingBelongsToTeam && bookingToReschedule.eventType?.teamId) {
|
||||
const userTeamIds = userTeams.teams.map((item) => item.teamId);
|
||||
if (userTeamIds.indexOf(bookingToReschedule?.eventType?.teamId) === -1) {
|
||||
if (userTeamIds.indexOf(bookingToReschedule.eventType?.teamId) === -1) {
|
||||
throw new TRPCError({ code: "FORBIDDEN", message: "User isn't a member on the team" });
|
||||
}
|
||||
log.debug(
|
||||
@@ -182,7 +182,7 @@ export const requestRescheduleHandler = async ({ ctx, input }: RequestReschedule
|
||||
const [userAsPeopleType] = usersToPeopleType([user], userTranslation);
|
||||
const organizer = {
|
||||
...userAsPeopleType,
|
||||
email: bookingToReschedule?.userPrimaryEmail ?? userAsPeopleType.email,
|
||||
email: bookingToReschedule.userPrimaryEmail ?? userAsPeopleType.email,
|
||||
};
|
||||
|
||||
const builder = new CalendarEventBuilder();
|
||||
@@ -276,31 +276,38 @@ export const requestRescheduleHandler = async ({ ctx, input }: RequestReschedule
|
||||
eventType?.metadata as EventTypeMetadata
|
||||
);
|
||||
|
||||
const evt: CalendarEvent = {
|
||||
title: bookingToReschedule?.title,
|
||||
type: event && event.slug ? event.slug : bookingToReschedule.title,
|
||||
description: bookingToReschedule?.description || "",
|
||||
customInputs: isPrismaObjOrUndefined(bookingToReschedule.customInputs),
|
||||
...getCalEventResponses({
|
||||
booking: bookingToReschedule,
|
||||
bookingFields: bookingToReschedule.eventType?.bookingFields ?? null,
|
||||
}),
|
||||
startTime: bookingToReschedule?.startTime ? dayjs(bookingToReschedule.startTime).format() : "",
|
||||
endTime: bookingToReschedule?.endTime ? dayjs(bookingToReschedule.endTime).format() : "",
|
||||
const calEventResponses = getCalEventResponses({
|
||||
booking: bookingToReschedule,
|
||||
bookingFields: bookingToReschedule.eventType?.bookingFields ?? null,
|
||||
});
|
||||
|
||||
const webhookFactory = new BookingWebhookFactory();
|
||||
const payload = webhookFactory.createCancelledEventPayload({
|
||||
bookingId: bookingToReschedule.id,
|
||||
title: bookingToReschedule.title,
|
||||
eventSlug: event.slug ?? null,
|
||||
description: bookingToReschedule.description,
|
||||
customInputs: bookingToReschedule.customInputs,
|
||||
responses: calEventResponses.responses,
|
||||
userFieldsResponses: calEventResponses.userFieldsResponses,
|
||||
startTime: bookingToReschedule.startTime ? dayjs(bookingToReschedule.startTime).format() : "",
|
||||
endTime: bookingToReschedule.endTime ? dayjs(bookingToReschedule.endTime).format() : "",
|
||||
organizer,
|
||||
attendees: usersToPeopleType(
|
||||
// username field doesn't exists on attendee but could be in the future
|
||||
bookingToReschedule.attendees as unknown as PersonAttendeeCommonFields[],
|
||||
tAttendees
|
||||
),
|
||||
uid: bookingToReschedule?.uid,
|
||||
location: bookingToReschedule?.location,
|
||||
destinationCalendar: bookingToReschedule?.destinationCalendar
|
||||
? [bookingToReschedule?.destinationCalendar]
|
||||
: [],
|
||||
uid: bookingToReschedule.uid,
|
||||
location: bookingToReschedule.location,
|
||||
destinationCalendar: bookingToReschedule.destinationCalendar,
|
||||
cancellationReason: `Please reschedule. ${cancellationReason}`, // TODO::Add i18-next for this
|
||||
iCalUID: bookingToReschedule?.iCalUID,
|
||||
};
|
||||
iCalUID: bookingToReschedule.iCalUID,
|
||||
...(bookingToReschedule.smsReminderNumber && {
|
||||
smsReminderNumber: bookingToReschedule.smsReminderNumber,
|
||||
}),
|
||||
cancelledBy: user.email,
|
||||
});
|
||||
|
||||
// Send webhook
|
||||
const eventTrigger: WebhookTriggerEvents = "BOOKING_CANCELLED";
|
||||
@@ -308,7 +315,7 @@ export const requestRescheduleHandler = async ({ ctx, input }: RequestReschedule
|
||||
const teamId = await getTeamIdFromEventType({
|
||||
eventType: {
|
||||
team: { id: bookingToReschedule.eventType?.teamId ?? null },
|
||||
parentId: bookingToReschedule?.eventType?.parentId ?? null,
|
||||
parentId: bookingToReschedule.eventType?.parentId ?? null,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -327,13 +334,9 @@ export const requestRescheduleHandler = async ({ ctx, input }: RequestReschedule
|
||||
const webhooks = await getWebhooks(subscriberOptions);
|
||||
|
||||
const promises = webhooks.map((webhook) =>
|
||||
sendPayload(webhook.secret, eventTrigger, new Date().toISOString(), webhook, {
|
||||
...evt,
|
||||
smsReminderNumber: bookingToReschedule.smsReminderNumber || undefined,
|
||||
cancelledBy: user.email,
|
||||
}).catch((e) => {
|
||||
sendPayload(webhook.secret, eventTrigger, new Date().toISOString(), webhook, payload).catch((e) => {
|
||||
log.error(
|
||||
`Error executing webhook for event: ${eventTrigger}, URL: ${webhook.subscriberUrl}, bookingId: ${evt.bookingId}, bookingUid: ${evt.uid}`,
|
||||
`Error executing webhook for event: ${eventTrigger}, URL: ${webhook.subscriberUrl}, bookingId: ${payload.bookingId}, bookingUid: ${payload.uid}`,
|
||||
safeStringify(e)
|
||||
);
|
||||
})
|
||||
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Decouples the types from Prisma
|
||||
*/
|
||||
export type JsonValue = string | number | boolean | null | JsonObject | JsonArray;
|
||||
export type JsonArray = JsonValue[];
|
||||
export type JsonObject = { [Key in string]?: JsonValue };
|
||||
Reference in New Issue
Block a user