2023-03-14 04:19:05 +00:00
import type { DestinationCalendar , Booking } from "@prisma/client" ;
2023-03-23 18:49:28 +00:00
import { cloneDeep , merge } from "lodash" ;
2021-09-22 19:52:38 +00:00
import { v5 as uuidv5 } from "uuid" ;
2023-02-25 03:57:49 +00:00
import type { z } from "zod" ;
2021-09-22 19:52:38 +00:00
2023-03-14 04:19:05 +00:00
import { getCalendar } from "@calcom/app-store/_utils/getCalendar" ;
2022-06-04 17:23:56 +00:00
import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter" ;
2022-08-26 00:48:50 +00:00
import { getEventLocationTypeFromApp } from "@calcom/app-store/locations" ;
2023-01-10 02:01:57 +00:00
import { MeetLocationType } from "@calcom/app-store/locations" ;
2022-03-23 22:00:30 +00:00
import getApps from "@calcom/app-store/utils" ;
import prisma from "@calcom/prisma" ;
2022-09-05 19:13:49 +00:00
import { createdEventSchema } from "@calcom/prisma/zod-utils" ;
2023-04-03 17:13:57 +00:00
import type { NewCalendarEventType } from "@calcom/types/Calendar" ;
import type { AdditionalInformation , CalendarEvent } from "@calcom/types/Calendar" ;
2023-02-25 03:57:49 +00:00
import type { CredentialPayload , CredentialWithAppName } from "@calcom/types/Credential" ;
2022-06-17 18:34:41 +00:00
import type { Event } from "@calcom/types/Event" ;
2023-04-03 17:13:57 +00:00
import type { EventResult } from "@calcom/types/EventManager" ;
import type { CreateUpdateResult , PartialBooking , PartialReference } from "@calcom/types/EventManager" ;
2022-03-23 22:00:30 +00:00
import { createEvent , updateEvent } from "./CalendarManager" ;
import { createMeeting , updateMeeting } from "./videoClient" ;
2021-07-15 01:19:30 +00:00
2021-11-26 11:03:43 +00:00
export const isDedicatedIntegration = ( location : string ) : boolean = > {
2023-01-10 02:01:57 +00:00
return location !== MeetLocationType && location . includes ( "integrations:" ) ;
2021-11-26 11:03:43 +00:00
} ;
export const getLocationRequestFromIntegration = ( location : string ) = > {
2022-08-26 00:48:50 +00:00
const eventLocationType = getEventLocationTypeFromApp ( location ) ;
if ( eventLocationType ) {
2021-11-26 11:03:43 +00:00
const requestId = uuidv5 ( location , uuidv5 . URL ) ;
return {
conferenceData : {
createRequest : {
requestId : requestId ,
} ,
} ,
location ,
} ;
}
return null ;
} ;
export const processLocation = ( event : CalendarEvent ) : CalendarEvent = > {
// If location is set to an integration location
// Build proper transforms for evt object
// Extend evt object with those transformations
2022-08-26 00:48:50 +00:00
// TODO: Rely on linkType:"dynamic" here. static links don't send their type. They send their URL directly.
2021-11-26 11:03:43 +00:00
if ( event . location ? . includes ( "integration" ) ) {
const maybeLocationRequestObject = getLocationRequestFromIntegration ( event . location ) ;
event = merge ( event , maybeLocationRequestObject ) ;
}
return event ;
} ;
2021-07-25 12:19:49 +00:00
2023-02-25 03:57:49 +00:00
export type EventManagerUser = {
2022-10-31 22:06:03 +00:00
credentials : CredentialPayload [ ] ;
2021-12-09 15:51:37 +00:00
destinationCalendar : DestinationCalendar | null ;
} ;
2022-03-23 22:00:30 +00:00
2022-09-05 19:13:49 +00:00
type createdEventSchema = z . infer < typeof createdEventSchema > ;
2021-07-15 01:19:30 +00:00
export default class EventManager {
2022-10-31 22:06:03 +00:00
calendarCredentials : CredentialWithAppName [ ] ;
videoCredentials : CredentialWithAppName [ ] ;
2021-07-15 01:19:30 +00:00
2021-07-24 20:30:14 +00:00
/ * *
* Takes an array of credentials and initializes a new instance of the EventManager .
*
2022-03-23 22:00:30 +00:00
* @param user
2021-07-24 20:30:14 +00:00
* /
2021-12-09 15:51:37 +00:00
constructor ( user : EventManagerUser ) {
2022-10-19 16:11:50 +00:00
const appCredentials = getApps ( user . credentials ) . flatMap ( ( app ) = >
app . credentials . map ( ( creds ) = > ( { . . . creds , appName : app.name } ) )
) ;
2022-11-01 16:07:28 +00:00
// This includes all calendar-related apps, traditional calendars such as Google Calendar
// (type google_calendar) and non-traditional calendars such as CRMs like Close.com
// (type closecom_other_calendar)
2022-03-23 22:00:30 +00:00
this . calendarCredentials = appCredentials . filter ( ( cred ) = > cred . type . endsWith ( "_calendar" ) ) ;
this . videoCredentials = appCredentials . filter ( ( cred ) = > cred . type . endsWith ( "_video" ) ) ;
2021-07-15 01:19:30 +00:00
}
2021-07-24 20:30:14 +00:00
/ * *
* Takes a CalendarEvent and creates all necessary integration entries for it .
* When a video integration is chosen as the event ' s location , a video integration
* event will be scheduled for it as well .
*
* @param event
* /
2022-01-27 20:32:53 +00:00
public async create ( event : CalendarEvent ) : Promise < CreateUpdateResult > {
2021-11-26 11:03:43 +00:00
const evt = processLocation ( event ) ;
2022-12-01 15:20:01 +00:00
// Fallback to cal video if no location is set
if ( ! evt . location ) evt [ "location" ] = "integrations:daily" ;
2023-01-10 02:01:57 +00:00
// Fallback to Cal Video if Google Meet is selected w/o a Google Cal
if ( evt . location === MeetLocationType && evt . destinationCalendar ? . integration !== "google_calendar" ) {
evt [ "location" ] = "integrations:daily" ;
}
2021-11-26 11:03:43 +00:00
const isDedicated = evt . location ? isDedicatedIntegration ( evt . location ) : null ;
2021-07-20 18:07:59 +00:00
2022-06-17 18:34:41 +00:00
const results : Array < EventResult < Exclude < Event , AdditionalInformation > >> = [ ] ;
2023-01-10 02:01:57 +00:00
2021-09-22 22:43:10 +00:00
// If and only if event type is a dedicated meeting, create a dedicated video meeting.
2021-08-01 21:29:15 +00:00
if ( isDedicated ) {
2021-10-25 13:05:21 +00:00
const result = await this . createVideoEvent ( evt ) ;
2022-10-26 20:16:38 +00:00
2022-12-01 15:20:01 +00:00
if ( result ? . createdEvent ) {
2021-11-26 11:03:43 +00:00
evt . videoCallData = result . createdEvent ;
2022-10-26 20:16:38 +00:00
evt . location = result . originalEvent . location ;
result . type = result . createdEvent . type ;
2021-09-22 22:43:10 +00:00
}
2021-12-02 17:18:17 +00:00
2021-09-22 22:43:10 +00:00
results . push ( result ) ;
2021-07-15 01:19:30 +00:00
}
2023-03-14 04:19:05 +00:00
// Some calendar libraries may edit the original event so let's clone it
const clonedCalEvent = cloneDeep ( event ) ;
2021-12-02 17:18:17 +00:00
// Create the calendar event with the proper video call data
2023-03-14 04:19:05 +00:00
results . push ( . . . ( await this . createAllCalendarEvents ( clonedCalEvent ) ) ) ;
2021-12-02 17:18:17 +00:00
2023-04-03 17:13:57 +00:00
// Since the result can be a new calendar event or video event, we have to create a type guard
// https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
const isCalendarResult = (
result : ( typeof results ) [ number ]
) : result is EventResult < NewCalendarEventType > = > {
return result . type . includes ( "_calendar" ) ;
} ;
2022-06-17 18:34:41 +00:00
const referencesToCreate = results . map ( ( result ) = > {
2022-09-05 19:13:49 +00:00
let createdEventObj : createdEventSchema | null = null ;
if ( typeof result ? . createdEvent === "string" ) {
createdEventObj = createdEventSchema . parse ( JSON . parse ( result . createdEvent ) ) ;
}
2022-10-26 20:16:38 +00:00
2023-04-03 17:13:57 +00:00
if ( isCalendarResult ( result ) ) {
evt . iCalUID = result . iCalUID || undefined ;
}
2021-10-25 13:05:21 +00:00
return {
type : result . type ,
2022-09-05 19:13:49 +00:00
uid : createdEventObj ? createdEventObj.id : result.createdEvent?.id?.toString ( ) ? ? "" ,
meetingId : createdEventObj ? createdEventObj.id : result.createdEvent?.id?.toString ( ) ,
meetingPassword : createdEventObj ? createdEventObj.password : result.createdEvent?.password ,
meetingUrl : createdEventObj ? createdEventObj.onlineMeetingUrl : result.createdEvent?.url ,
2022-08-08 19:38:02 +00:00
externalCalendarId : evt.destinationCalendar?.externalId ,
credentialId : evt.destinationCalendar?.credentialId ,
} ;
} ) ;
return {
results ,
referencesToCreate ,
} ;
}
public async updateLocation ( event : CalendarEvent , booking : PartialBooking ) : Promise < CreateUpdateResult > {
const evt = processLocation ( event ) ;
const isDedicated = evt . location ? isDedicatedIntegration ( evt . location ) : null ;
const results : Array < EventResult < Exclude < Event , AdditionalInformation > >> = [ ] ;
// If and only if event type is a dedicated meeting, create a dedicated video meeting.
if ( isDedicated ) {
const result = await this . createVideoEvent ( evt ) ;
if ( result . createdEvent ) {
evt . videoCallData = result . createdEvent ;
}
results . push ( result ) ;
}
// Update the calendar event with the proper video call data
2022-08-12 20:46:40 +00:00
const calendarReference = booking . references . find ( ( reference ) = > reference . type . includes ( "_calendar" ) ) ;
if ( calendarReference ) {
results . push ( . . . ( await this . updateAllCalendarEvents ( evt , booking ) ) ) ;
}
2022-08-08 19:38:02 +00:00
const referencesToCreate = results . map ( ( result ) = > {
return {
type : result . type ,
uid : result.createdEvent?.id?.toString ( ) ? ? "" ,
meetingId : result.createdEvent?.id?.toString ( ) ,
meetingPassword : result.createdEvent?.password ,
meetingUrl : result.createdEvent?.url ,
2022-05-16 20:20:09 +00:00
externalCalendarId : evt.destinationCalendar?.externalId ,
2022-07-18 15:37:47 +00:00
credentialId : evt.destinationCalendar?.credentialId ,
2021-10-25 13:05:21 +00:00
} ;
2021-07-24 20:24:00 +00:00
} ) ;
return {
results ,
referencesToCreate ,
} ;
2021-07-15 01:19:30 +00:00
}
2021-07-24 20:30:14 +00:00
/ * *
* Takes a calendarEvent and a rescheduleUid and updates the event that has the
* given uid using the data delivered in the given CalendarEvent .
*
* @param event
* /
2022-07-12 13:16:34 +00:00
public async reschedule (
2022-04-14 21:25:24 +00:00
event : CalendarEvent ,
rescheduleUid : string ,
2023-03-14 04:19:05 +00:00
newBookingId? : number
2022-04-14 21:25:24 +00:00
) : Promise < CreateUpdateResult > {
2023-03-14 04:19:05 +00:00
const originalEvt = processLocation ( event ) ;
const evt = cloneDeep ( originalEvt ) ;
2021-11-09 16:27:33 +00:00
if ( ! rescheduleUid ) {
throw new Error ( "You called eventManager.update without an `rescheduleUid`. This should never happen." ) ;
2021-10-25 13:05:21 +00:00
}
2021-07-25 12:19:49 +00:00
2021-07-24 20:24:00 +00:00
// Get details of existing booking.
const booking = await prisma . booking . findFirst ( {
where : {
2021-11-09 16:27:33 +00:00
uid : rescheduleUid ,
2021-07-24 20:24:00 +00:00
} ,
select : {
id : true ,
2022-07-18 15:37:47 +00:00
userId : true ,
2023-03-14 04:19:05 +00:00
attendees : true ,
2021-07-24 20:24:00 +00:00
references : {
2022-03-24 23:29:32 +00:00
// NOTE: id field removed from select as we don't require for deletingMany
// but was giving error on recreate for reschedule, probably because promise.all() didn't finished
2021-07-24 20:24:00 +00:00
select : {
type : true ,
uid : true ,
2021-09-22 22:43:10 +00:00
meetingId : true ,
meetingPassword : true ,
meetingUrl : true ,
2022-05-16 20:20:09 +00:00
externalCalendarId : true ,
2022-07-18 15:37:47 +00:00
credentialId : true ,
2021-07-24 20:24:00 +00:00
} ,
} ,
2021-12-09 15:51:37 +00:00
destinationCalendar : true ,
2022-04-14 21:25:24 +00:00
payment : true ,
2023-03-14 04:19:05 +00:00
eventType : {
select : {
seatsPerTimeSlot : true ,
seatsShowAttendees : true ,
} ,
} ,
2021-07-24 20:24:00 +00:00
} ,
} ) ;
2021-10-25 13:05:21 +00:00
if ( ! booking ) {
throw new Error ( "booking not found" ) ;
}
2021-11-26 11:03:43 +00:00
const isDedicated = evt . location ? isDedicatedIntegration ( evt . location ) : null ;
2022-06-17 18:34:41 +00:00
const results : Array < EventResult < Event > > = [ ] ;
2021-09-22 22:43:10 +00:00
// If and only if event type is a dedicated meeting, update the dedicated video meeting.
2021-08-01 21:38:38 +00:00
if ( isDedicated ) {
2021-10-25 13:05:21 +00:00
const result = await this . updateVideoEvent ( evt , booking ) ;
2022-02-10 10:44:46 +00:00
const [ updatedEvent ] = Array . isArray ( result . updatedEvent ) ? result . updatedEvent : [ result . updatedEvent ] ;
2023-03-14 04:19:05 +00:00
2022-02-10 10:44:46 +00:00
if ( updatedEvent ) {
evt . videoCallData = updatedEvent ;
evt . location = updatedEvent . url ;
2021-09-22 22:43:10 +00:00
}
results . push ( result ) ;
2021-07-15 01:19:30 +00:00
}
2021-11-26 11:03:43 +00:00
2023-03-14 04:19:05 +00:00
// There was a case that booking didn't had any reference and we don't want to throw error on function
if ( booking . references . find ( ( reference ) = > reference . type . includes ( "_calendar" ) ) ) {
// Update all calendar events.
results . push ( . . . ( await this . updateAllCalendarEvents ( evt , booking , newBookingId ) ) ) ;
}
2021-12-02 21:41:09 +00:00
2022-04-14 21:25:24 +00:00
const bookingPayment = booking ? . payment ;
// Updating all payment to new
if ( bookingPayment && newBookingId ) {
const paymentIds = bookingPayment . map ( ( payment ) = > payment . id ) ;
await prisma . payment . updateMany ( {
where : {
id : {
in : paymentIds ,
} ,
} ,
data : {
bookingId : newBookingId ,
} ,
} ) ;
}
2021-07-24 20:24:00 +00:00
return {
results ,
referencesToCreate : [ . . . booking . references ] ,
} ;
2021-07-15 01:19:30 +00:00
}
2022-07-12 13:16:34 +00:00
public async updateCalendarAttendees ( event : CalendarEvent , booking : PartialBooking ) {
2023-03-14 04:19:05 +00:00
if ( booking . references . length === 0 ) {
console . error ( "Tried to update references but there wasn't any." ) ;
return ;
}
2022-07-12 13:16:34 +00:00
await this . updateAllCalendarEvents ( event , booking ) ;
}
2021-07-15 01:19:30 +00:00
/ * *
* Creates event entries for all calendar integrations given in the credentials .
2021-07-20 18:07:59 +00:00
* When noMail is true , no mails will be sent . This is used when the event is
* a video meeting because then the mail containing the video credentials will be
* more important than the mails created for these bare calendar events .
2021-07-15 01:19:30 +00:00
*
2021-07-25 15:05:18 +00:00
* When the optional uid is set , it will be used instead of the auto generated uid .
*
2021-07-15 01:19:30 +00:00
* @param event
2021-07-20 18:07:59 +00:00
* @param noMail
2021-07-15 01:19:30 +00:00
* @private
* /
2022-06-17 18:34:41 +00:00
private async createAllCalendarEvents ( event : CalendarEvent ) {
2021-12-09 15:51:37 +00:00
/** Can I use destinationCalendar here? */
/* How can I link a DC to a cred? */
2023-01-31 21:14:19 +00:00
2022-10-15 17:02:24 +00:00
let createdEvents : EventResult < NewCalendarEventType > [ ] = [ ] ;
2021-12-09 15:51:37 +00:00
if ( event . destinationCalendar ) {
2022-07-01 20:55:27 +00:00
if ( event . destinationCalendar . credentialId ) {
2022-10-19 16:11:50 +00:00
const credential = this . calendarCredentials . find (
( c ) = > c . id === event . destinationCalendar ? . credentialId
) ;
2022-07-01 20:55:27 +00:00
if ( credential ) {
2022-11-01 16:07:28 +00:00
const createdEvent = await createEvent ( credential , event ) ;
if ( createdEvent ) {
createdEvents . push ( createdEvent ) ;
}
2022-07-01 20:55:27 +00:00
}
2022-10-15 17:02:24 +00:00
} else {
const destinationCalendarCredentials = this . calendarCredentials . filter (
( c ) = > c . type === event . destinationCalendar ? . integration
) ;
createdEvents = createdEvents . concat (
await Promise . all ( destinationCalendarCredentials . map ( async ( c ) = > await createEvent ( c , event ) ) )
) ;
2022-07-01 20:55:27 +00:00
}
2022-10-15 17:02:24 +00:00
} else {
/ * *
* Not ideal but , if we don ' t find a destination calendar ,
* fallback to the first connected calendar
* /
2022-11-01 16:07:28 +00:00
const [ credential ] = this . calendarCredentials . filter ( ( cred ) = > cred . type === "calendar" ) ;
if ( credential ) {
const createdEvent = await createEvent ( credential , event ) ;
if ( createdEvent ) {
createdEvents . push ( createdEvent ) ;
}
2022-10-15 17:02:24 +00:00
}
2021-12-09 15:51:37 +00:00
}
2022-10-15 17:02:24 +00:00
// Taking care of non-traditional calendar integrations
createdEvents = createdEvents . concat (
await Promise . all (
this . calendarCredentials
. filter ( ( cred ) = > cred . type . includes ( "other_calendar" ) )
. map ( async ( cred ) = > await createEvent ( cred , event ) )
)
) ;
return createdEvents ;
2021-07-15 01:19:30 +00:00
}
2021-07-24 20:30:14 +00:00
/ * *
* Checks which video integration is needed for the event ' s location and returns
* credentials for that - if existing .
* @param event
* @private
* /
2021-10-07 16:12:39 +00:00
2022-10-31 22:06:03 +00:00
private getVideoCredential ( event : CalendarEvent ) : CredentialWithAppName | undefined {
2021-10-25 13:05:21 +00:00
if ( ! event . location ) {
return undefined ;
}
2022-05-02 20:39:35 +00:00
/** @fixme potential bug since Google Meet are saved as `integrations:google:meet` and there are no `google:meet` type in our DB */
2021-07-15 01:19:30 +00:00
const integrationName = event . location . replace ( "integrations:" , "" ) ;
2022-07-06 14:43:57 +00:00
let videoCredential = this . videoCredentials
// Whenever a new video connection is added, latest credentials are added with the highest ID.
2023-03-14 04:19:05 +00:00
// Because you can't rely on having them in the highest first order here, ensure this by sorting in DESC order
2022-07-06 14:43:57 +00:00
. sort ( ( a , b ) = > {
return b . id - a . id ;
} )
2022-10-31 22:06:03 +00:00
. find ( ( credential : CredentialPayload ) = > credential . type . includes ( integrationName ) ) ;
2021-10-07 16:12:39 +00:00
2022-06-04 17:23:56 +00:00
/ * *
* This might happen if someone tries to use a location with a missing credential , so we fallback to Cal Video .
* @todo remove location from event types that has missing credentials
* * /
2022-10-19 16:11:50 +00:00
if ( ! videoCredential ) videoCredential = { . . . FAKE_DAILY_CREDENTIAL , appName : "FAKE" } ;
2022-06-04 17:23:56 +00:00
return videoCredential ;
2021-07-15 01:19:30 +00:00
}
/ * *
* Creates a video event entry for the selected integration location .
*
2021-07-25 15:05:18 +00:00
* When optional uid is set , it will be used instead of the auto generated uid .
*
2021-07-15 01:19:30 +00:00
* @param event
* @private
* /
2022-06-17 18:34:41 +00:00
private createVideoEvent ( event : CalendarEvent ) {
2021-07-15 01:19:30 +00:00
const credential = this . getVideoCredential ( event ) ;
2021-10-26 16:17:24 +00:00
if ( credential ) {
2021-10-25 13:05:21 +00:00
return createMeeting ( credential , event ) ;
2021-07-15 01:19:30 +00:00
} else {
2022-04-26 11:31:57 +00:00
return Promise . reject (
` No suitable credentials given for the requested integration name: ${ event . location } `
) ;
2021-07-15 01:19:30 +00:00
}
}
2021-07-20 18:07:59 +00:00
/ * *
* Updates the event entries for all calendar integrations given in the credentials .
* When noMail is true , no mails will be sent . This is used when the event is
* a video meeting because then the mail containing the video credentials will be
* more important than the mails created for these bare calendar events .
*
* @param event
* @param booking
* @private
* /
2022-10-19 16:11:50 +00:00
private async updateAllCalendarEvents (
2021-07-15 01:19:30 +00:00
event : CalendarEvent ,
2023-03-14 04:19:05 +00:00
booking : PartialBooking ,
newBookingId? : number
2022-06-17 18:34:41 +00:00
) : Promise < Array < EventResult < NewCalendarEventType > >> {
2022-07-18 15:37:47 +00:00
let calendarReference : PartialReference | undefined = undefined ,
credential ;
try {
2023-03-14 04:19:05 +00:00
// If a newBookingId is given, update that calendar event
let newBooking ;
if ( newBookingId ) {
newBooking = await prisma . booking . findUnique ( {
where : {
id : newBookingId ,
} ,
select : {
references : true ,
} ,
} ) ;
}
2023-03-22 14:23:09 +00:00
calendarReference = newBooking ? . references . length
? newBooking . references . find ( ( reference ) = > reference . type . includes ( "_calendar" ) )
: booking . references . find ( ( reference ) = > reference . type . includes ( "_calendar" ) ) ;
2023-03-14 04:19:05 +00:00
2022-11-05 18:58:35 +00:00
if ( ! calendarReference ) {
2023-02-27 20:45:40 +00:00
return [ ] ;
2022-11-05 18:58:35 +00:00
}
2022-07-18 15:37:47 +00:00
const { uid : bookingRefUid , externalCalendarId : bookingExternalCalendarId } = calendarReference ;
2022-11-05 18:58:35 +00:00
if ( ! bookingExternalCalendarId ) {
throw new Error ( "externalCalendarId" ) ;
}
2022-07-18 15:37:47 +00:00
2022-10-19 16:11:50 +00:00
let result = [ ] ;
2022-07-18 15:37:47 +00:00
if ( calendarReference . credentialId ) {
credential = this . calendarCredentials . filter (
( credential ) = > credential . id === calendarReference ? . credentialId
) [ 0 ] ;
result . push ( updateEvent ( credential , event , bookingRefUid , bookingExternalCalendarId ) ) ;
} else {
const credentials = this . calendarCredentials . filter (
( credential ) = > credential . type === calendarReference ? . type
) ;
for ( const credential of credentials ) {
result . push ( updateEvent ( credential , event , bookingRefUid , bookingExternalCalendarId ) ) ;
}
}
2023-03-14 04:19:05 +00:00
// If we are merging two calendar events we should delete the old calendar event
if ( newBookingId ) {
const oldCalendarEvent = booking . references . find ( ( reference ) = > reference . type . includes ( "_calendar" ) ) ;
if ( oldCalendarEvent ? . credentialId ) {
const calendarCredential = await prisma . credential . findUnique ( {
where : {
id : oldCalendarEvent.credentialId ,
} ,
} ) ;
2023-04-05 14:55:57 +00:00
const calendar = await getCalendar ( calendarCredential ) ;
2023-03-14 04:19:05 +00:00
await calendar ? . deleteEvent ( oldCalendarEvent . uid , event , oldCalendarEvent . externalCalendarId ) ;
}
}
2022-10-19 16:11:50 +00:00
// Taking care of non-traditional calendar integrations
result = result . concat (
this . calendarCredentials
. filter ( ( cred ) = > cred . type . includes ( "other_calendar" ) )
. map ( async ( cred ) = > {
const calendarReference = booking . references . find ( ( ref ) = > ref . type === cred . type ) ;
if ( ! calendarReference )
2022-11-05 18:58:35 +00:00
if ( ! calendarReference ) {
return {
appName : cred.appName ,
type : cred . type ,
success : false ,
uid : "" ,
originalEvent : event ,
} ;
}
2022-10-19 16:11:50 +00:00
const { externalCalendarId : bookingExternalCalendarId , meetingId : bookingRefUid } =
calendarReference ;
return await updateEvent ( cred , event , bookingRefUid ? ? null , bookingExternalCalendarId ? ? null ) ;
} )
) ;
2022-07-18 15:37:47 +00:00
return Promise . all ( result ) ;
} catch ( error ) {
let message = ` Tried to 'updateAllCalendarEvents' but there was no '{thing}' for ' ${ credential ? . type } ', userId: ' ${ credential ? . userId } ', bookingId: ' ${ booking ? . id } ' ` ;
2022-11-05 18:58:35 +00:00
if ( error instanceof Error ) {
message = message . replace ( "{thing}" , error . message ) ;
}
2022-07-18 15:37:47 +00:00
console . error ( message ) ;
return Promise . resolve ( [
{
2022-10-19 16:11:50 +00:00
appName : "none" ,
2022-07-18 15:37:47 +00:00
type : calendarReference ? . type || "calendar" ,
2022-06-08 18:23:48 +00:00
success : false ,
uid : "" ,
originalEvent : event ,
2022-07-18 15:37:47 +00:00
} ,
] ) ;
}
2021-07-15 01:19:30 +00:00
}
2021-07-20 18:07:59 +00:00
/ * *
* Updates a single video event .
*
* @param event
* @param booking
* @private
* /
2021-07-15 01:19:30 +00:00
private updateVideoEvent ( event : CalendarEvent , booking : PartialBooking ) {
const credential = this . getVideoCredential ( event ) ;
2021-10-26 16:17:24 +00:00
if ( credential ) {
2021-10-25 13:05:21 +00:00
const bookingRef = booking ? booking . references . filter ( ( ref ) = > ref . type === credential . type ) [ 0 ] : null ;
2021-11-26 11:03:43 +00:00
return updateMeeting ( credential , event , bookingRef ) ;
2021-07-15 01:19:30 +00:00
} else {
2022-04-26 11:31:57 +00:00
return Promise . reject (
` No suitable credentials given for the requested integration name: ${ event . location } `
) ;
2021-07-15 01:19:30 +00:00
}
}
2022-04-14 21:25:24 +00:00
/ * *
* 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 ) ;
}
2023-03-14 04:19:05 +00:00
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
}
2021-07-15 01:19:30 +00:00
}