From 88384192f8eb90e79367bb492e3b0fd1e26f8be8 Mon Sep 17 00:00:00 2001 From: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Date: Mon, 8 Aug 2022 15:38:02 -0400 Subject: [PATCH] Fixes bug when changing the location of a booking (#3585) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * use correct booking * delete calendar event and update booking reference * use middleware to get booking * updating event instad of deleting and creating a new one * fix issue with google hangout link * remove unsed imports * Apply suggestions from code review * Update bookings.tsx Co-authored-by: CarinaWolli Co-authored-by: Omar López Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Co-authored-by: Peer Richelsen Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../googlecalendar/lib/CalendarService.ts | 42 +++++++++++- packages/core/EventManager.ts | 36 +++++++++++ .../trpc/server/routers/viewer/bookings.tsx | 64 +++++++++++-------- 3 files changed, 114 insertions(+), 28 deletions(-) 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 });