9c97b2aa6d
* feat: add usernameInOrg field to webhook organizer payload for organization users - Add usernameInOrg field to CalendarEventBuilder organizer interface - Update handleNewBooking to pass organizerOrganizationProfile.username as usernameInOrg - Include usernameInOrg in webhook payload generation (sendPayload.ts) - Add webhook form variable for usernameInOrg with translation - Update Person type to include usernameInOrg field - Add test case for organization user webhook verification - Maintain backward compatibility by keeping existing username field unchanged Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * feat: add usernameInOrg to additional webhook sending locations - Update handleCancelBooking.ts to include usernameInOrg in organizer payload - Update confirm.handler.ts to include usernameInOrg for booking confirmations - Update getBooking.ts to include usernameInOrg in payment-related webhooks - Maintain backward compatibility with existing username field - All webhook sending locations now include organization profile username Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com> * fixes * Add subteam event test for usernameInOrg * fix eslitn issues --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
286 lines
6.5 KiB
TypeScript
286 lines
6.5 KiB
TypeScript
import type { TFunction } from "i18next";
|
|
|
|
import type { TimeFormat } from "@calcom/lib/timeFormat";
|
|
import type { Prisma } from "@calcom/prisma/client";
|
|
import type { SchedulingType } from "@calcom/prisma/enums";
|
|
import type { CalendarEvent, Person, CalEventResponses, AppsStatus } from "@calcom/types/Calendar";
|
|
import type { VideoCallData } from "@calcom/types/VideoApiAdapter";
|
|
|
|
export class CalendarEventBuilder {
|
|
private event: Partial<CalendarEvent>;
|
|
|
|
constructor(existingEvent?: Partial<CalendarEvent>) {
|
|
this.event = existingEvent || {};
|
|
}
|
|
|
|
static fromEvent(event: Partial<CalendarEvent>) {
|
|
return new CalendarEventBuilder(event);
|
|
}
|
|
|
|
withBasicDetails({
|
|
bookerUrl,
|
|
title,
|
|
startTime,
|
|
endTime,
|
|
additionalNotes,
|
|
}: {
|
|
bookerUrl: string;
|
|
title: string;
|
|
startTime: string;
|
|
endTime: string;
|
|
additionalNotes?: string;
|
|
}) {
|
|
this.event = {
|
|
...this.event,
|
|
bookerUrl,
|
|
title,
|
|
startTime,
|
|
endTime,
|
|
additionalNotes,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withEventType(eventType: {
|
|
slug: string;
|
|
description?: string | null;
|
|
id: number;
|
|
hideCalendarNotes?: boolean;
|
|
hideCalendarEventDetails?: boolean;
|
|
hideOrganizerEmail?: boolean;
|
|
schedulingType?: SchedulingType | null;
|
|
seatsPerTimeSlot?: number | null;
|
|
seatsShowAttendees?: boolean | null;
|
|
seatsShowAvailabilityCount?: boolean | null;
|
|
customReplyToEmail?: string | null;
|
|
disableRescheduling?: boolean;
|
|
disableCancelling?: boolean;
|
|
}) {
|
|
this.event = {
|
|
...this.event,
|
|
type: eventType.slug,
|
|
description: eventType.description,
|
|
eventTypeId: eventType.id,
|
|
hideCalendarNotes: eventType.hideCalendarNotes,
|
|
hideCalendarEventDetails: eventType.hideCalendarEventDetails,
|
|
hideOrganizerEmail: eventType.hideOrganizerEmail,
|
|
schedulingType: eventType.schedulingType,
|
|
seatsPerTimeSlot: eventType.seatsPerTimeSlot,
|
|
// if seats are not enabled we should default true
|
|
seatsShowAttendees: eventType.seatsPerTimeSlot ? eventType.seatsShowAttendees : true,
|
|
seatsShowAvailabilityCount: eventType.seatsPerTimeSlot ? eventType.seatsShowAvailabilityCount : true,
|
|
customReplyToEmail: eventType.customReplyToEmail,
|
|
disableRescheduling: eventType.disableRescheduling ?? false,
|
|
disableCancelling: eventType.disableCancelling ?? false,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withOrganizer(organizer: {
|
|
id: number;
|
|
name: string | null;
|
|
email: string;
|
|
username?: string;
|
|
usernameInOrg?: string;
|
|
timeZone: string;
|
|
timeFormat?: TimeFormat;
|
|
language: {
|
|
translate: TFunction;
|
|
locale: string;
|
|
};
|
|
}) {
|
|
this.event = {
|
|
...this.event,
|
|
organizer: {
|
|
id: organizer.id,
|
|
name: organizer.name || "Nameless",
|
|
email: organizer.email,
|
|
username: organizer.username,
|
|
usernameInOrg: organizer.usernameInOrg,
|
|
timeZone: organizer.timeZone,
|
|
language: organizer.language,
|
|
timeFormat: organizer.timeFormat,
|
|
},
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withAttendees(attendees: Person[]) {
|
|
this.event = {
|
|
...this.event,
|
|
attendees,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withMetadataAndResponses({
|
|
additionalNotes,
|
|
customInputs,
|
|
responses,
|
|
userFieldsResponses,
|
|
}: {
|
|
additionalNotes?: string | null;
|
|
customInputs?: Prisma.JsonObject | null;
|
|
responses?: CalEventResponses | null;
|
|
userFieldsResponses?: CalEventResponses | null;
|
|
}) {
|
|
this.event = {
|
|
...this.event,
|
|
additionalNotes,
|
|
customInputs,
|
|
responses,
|
|
userFieldsResponses,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withLocation({
|
|
location,
|
|
conferenceCredentialId,
|
|
}: {
|
|
location: string | null;
|
|
conferenceCredentialId?: number;
|
|
}) {
|
|
this.event = {
|
|
...this.event,
|
|
location,
|
|
conferenceCredentialId,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withDestinationCalendar(destinationCalendar: CalendarEvent["destinationCalendar"]) {
|
|
this.event = {
|
|
...this.event,
|
|
destinationCalendar,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withIdentifiers({ iCalUID, iCalSequence }: { iCalUID?: string; iCalSequence?: number }) {
|
|
this.event = {
|
|
...this.event,
|
|
iCalUID: iCalUID ?? this.event.iCalUID,
|
|
iCalSequence: iCalSequence ?? this.event.iCalSequence,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withConfirmation({
|
|
requiresConfirmation,
|
|
isConfirmedByDefault,
|
|
}: {
|
|
requiresConfirmation: boolean;
|
|
isConfirmedByDefault: boolean;
|
|
}) {
|
|
this.event = {
|
|
...this.event,
|
|
requiresConfirmation,
|
|
oneTimePassword: isConfirmedByDefault ? null : undefined,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withPlatformVariables({
|
|
platformClientId,
|
|
platformRescheduleUrl,
|
|
platformCancelUrl,
|
|
platformBookingUrl,
|
|
}: {
|
|
platformClientId?: string | null;
|
|
platformRescheduleUrl?: string | null;
|
|
platformCancelUrl?: string | null;
|
|
platformBookingUrl?: string | null;
|
|
}) {
|
|
this.event = {
|
|
...this.event,
|
|
platformClientId,
|
|
platformRescheduleUrl,
|
|
platformCancelUrl,
|
|
platformBookingUrl,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withAppsStatus(appsStatus?: AppsStatus[]) {
|
|
this.event = {
|
|
...this.event,
|
|
appsStatus,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withVideoCallData(videoCallData?: VideoCallData) {
|
|
this.event = {
|
|
...this.event,
|
|
videoCallData,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withTeam(team?: { name: string; members: Person[]; id: number }) {
|
|
this.event = {
|
|
...this.event,
|
|
team,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withRecurring(recurringEvent?: { count: number; freq: number; interval: number }) {
|
|
this.event = {
|
|
...this.event,
|
|
recurringEvent,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withAttendeeSeatId(attendeeSeatId?: string) {
|
|
this.event = {
|
|
...this.event,
|
|
attendeeSeatId,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withUid(uid: string | null) {
|
|
this.event = {
|
|
...this.event,
|
|
uid,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withRecurringEventId(recurringEventId: string) {
|
|
this.event = {
|
|
...this.event,
|
|
existingRecurringEvent: {
|
|
recurringEventId,
|
|
},
|
|
};
|
|
return this;
|
|
}
|
|
|
|
withOneTimePassword(oneTimePassword?: string | null) {
|
|
this.event = {
|
|
...this.event,
|
|
oneTimePassword,
|
|
};
|
|
return this;
|
|
}
|
|
|
|
build(): CalendarEvent | null {
|
|
// Validate required fields
|
|
if (
|
|
!this.event.startTime ||
|
|
!this.event.endTime ||
|
|
!this.event.type ||
|
|
!this.event.bookerUrl ||
|
|
!this.event.title
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return this.event as CalendarEvent;
|
|
}
|
|
}
|