diff --git a/packages/app-store/googlecalendar/lib/CalendarService.ts b/packages/app-store/googlecalendar/lib/CalendarService.ts index 03cf9b17eb..b969d777b2 100644 --- a/packages/app-store/googlecalendar/lib/CalendarService.ts +++ b/packages/app-store/googlecalendar/lib/CalendarService.ts @@ -191,26 +191,62 @@ export default class GoogleCalendarService implements Calendar { payload["location"] = getLocation(event); } + if (event.conferenceData && event.location === "integrations:google:meet") { + payload["conferenceData"] = event.conferenceData; + } + const calendar = google.calendar({ version: "v3", auth: myGoogleAuth, }); + + const selectedCalendar = externalCalendarId + ? externalCalendarId + : event.destinationCalendar?.externalId; + calendar.events.update( { auth: myGoogleAuth, - calendarId: externalCalendarId ? externalCalendarId : event.destinationCalendar?.externalId, + calendarId: selectedCalendar, eventId: uid, sendNotifications: true, sendUpdates: "all", requestBody: payload, + conferenceDataVersion: 1, }, - function (err, event) { + function (err, evt) { if (err) { console.error("There was an error contacting google calendar service: ", err); return reject(err); } - return resolve(event?.data); + + if (evt && evt.data.id && evt.data.hangoutLink && event.location === "integrations:google:meet") { + calendar.events.patch({ + // Update the same event but this time we know the hangout link + calendarId: selectedCalendar, + auth: myGoogleAuth, + eventId: evt.data.id || "", + requestBody: { + description: getRichDescription({ + ...event, + additionalInformation: { hangoutLink: evt.data.hangoutLink }, + }), + }, + }); + return resolve({ + uid: "", + ...evt.data, + id: evt.data.id || "", + additionalInfo: { + hangoutLink: evt.data.hangoutLink || "", + }, + type: "google_calendar", + password: "", + url: "", + }); + } + return resolve(evt?.data); } ); }); diff --git a/packages/core/EventManager.ts b/packages/core/EventManager.ts index 59064232ee..7de51642a4 100644 --- a/packages/core/EventManager.ts +++ b/packages/core/EventManager.ts @@ -156,6 +156,42 @@ export default class EventManager { }; } + public async updateLocation(event: CalendarEvent, booking: PartialBooking): Promise { + const evt = processLocation(event); + const isDedicated = evt.location ? isDedicatedIntegration(evt.location) : null; + + const results: Array>> = []; + // 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 + results.push(...(await this.updateAllCalendarEvents(evt, booking))); + + 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, + externalCalendarId: evt.destinationCalendar?.externalId, + credentialId: evt.destinationCalendar?.credentialId, + }; + }); + + return { + results, + referencesToCreate, + }; + } + /** * Takes a calendarEvent and a rescheduleUid and updates the event that has the * given uid using the data delivered in the given CalendarEvent. diff --git a/packages/trpc/server/routers/viewer/bookings.tsx b/packages/trpc/server/routers/viewer/bookings.tsx index 17dcc93d5f..c47d976afa 100644 --- a/packages/trpc/server/routers/viewer/bookings.tsx +++ b/packages/trpc/server/routers/viewer/bookings.tsx @@ -23,36 +23,44 @@ export const bookingsRouter = createProtectedRouter() .middleware(async ({ ctx, rawInput, next }) => { // Endpoints that just read the logged in user's data - like 'list' don't necessary have any input if (!rawInput) return next({ ctx: { ...ctx, booking: null } }); - const webhookIdAndEventTypeId = commonBookingSchema.safeParse(rawInput); if (!webhookIdAndEventTypeId.success) throw new TRPCError({ code: "PARSE_ERROR" }); const { bookingId } = webhookIdAndEventTypeId.data; const booking = await ctx.prisma.booking.findFirst({ where: { - OR: [ - /* If user is organizer */ - { userId: ctx.user.id, id: bookingId }, - /* Or part of a collective booking */ + id: bookingId, + AND: [ { - eventType: { - schedulingType: SchedulingType.COLLECTIVE, - users: { - some: { - id: ctx.user.id, + OR: [ + /* If user is organizer */ + { userId: ctx.user.id }, + /* Or part of a collective booking */ + { + eventType: { + schedulingType: SchedulingType.COLLECTIVE, + users: { + some: { + id: ctx.user.id, + }, + }, }, }, - }, + ], }, ], }, include: { attendees: true, eventType: true, - user: { - include: { destinationCalendar: true }, - }, destinationCalendar: true, + references: true, + user: { + include: { + destinationCalendar: true, + credentials: true, + }, + }, }, }); return next({ ctx: { ...ctx, booking } }); @@ -73,11 +81,6 @@ export const bookingsRouter = createProtectedRouter() const { booking } = ctx; try { - await ctx.prisma.booking.update({ - where: { id: bookingId }, - data: { location }, - }); - const organizer = await ctx.prisma.user.findFirst({ where: { id: booking.userId || 0, @@ -127,10 +130,9 @@ export const bookingsRouter = createProtectedRouter() }; const eventManager = new EventManager(ctx.user); - // TODO: This is causing duplicate calendar events when updating location - const scheduleResult = await eventManager.create(evt); - const results = scheduleResult.results; + const updatedResult = await eventManager.updateLocation(evt, booking); + const results = updatedResult.results; if (results.length > 0 && results.every((res) => !res.success)) { const error = { errorCode: "BookingUpdateLocationFailed", @@ -138,11 +140,23 @@ export const bookingsRouter = createProtectedRouter() }; logger.error(`Booking ${ctx.user.username} failed`, error, results); } else { + await ctx.prisma.booking.update({ + where: { + id: bookingId, + }, + data: { + location, + references: { + create: updatedResult.referencesToCreate, + }, + }, + }); + const metadata: AdditionalInformation = {}; if (results.length) { - metadata.hangoutLink = results[0].createdEvent?.hangoutLink; - metadata.conferenceData = results[0].createdEvent?.conferenceData; - metadata.entryPoints = results[0].createdEvent?.entryPoints; + metadata.hangoutLink = results[0].updatedEvent?.hangoutLink; + metadata.conferenceData = results[0].updatedEvent?.conferenceData; + metadata.entryPoints = results[0].updatedEvent?.entryPoints; } try { await sendLocationChangeEmails({ ...evt, additionalInformation: metadata });