import { Credential, Prisma } from "@prisma/client"; import { GetTokenResponse } from "google-auth-library/build/src/auth/oauth2client"; import { Auth, calendar_v3, google } from "googleapis"; import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/calendarClient"; import prisma from "@lib/prisma"; export interface ConferenceData { createRequest: calendar_v3.Schema$CreateConferenceRequest; } const googleAuth = (credential: Credential) => { const { client_secret, client_id, redirect_uris } = JSON.parse(process.env.GOOGLE_API_CREDENTIALS!).web; const myGoogleAuth = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); const googleCredentials = credential.key as Auth.Credentials; myGoogleAuth.setCredentials(googleCredentials); // FIXME - type errors IDK Why this is a protected method ¯\_(ツ)_/¯ const isExpired = () => myGoogleAuth.isTokenExpiring(); const refreshAccessToken = () => myGoogleAuth // FIXME - type errors IDK Why this is a protected method ¯\_(ツ)_/¯ .refreshToken(googleCredentials.refresh_token) .then((res: GetTokenResponse) => { const token = res.res?.data; googleCredentials.access_token = token.access_token; googleCredentials.expiry_date = token.expiry_date; return prisma.credential .update({ where: { id: credential.id, }, data: { key: googleCredentials as Prisma.InputJsonValue, }, }) .then(() => { myGoogleAuth.setCredentials(googleCredentials); return myGoogleAuth; }); }) .catch((err) => { console.error("Error refreshing google token", err); return myGoogleAuth; }); return { getToken: () => (!isExpired() ? Promise.resolve(myGoogleAuth) : refreshAccessToken()), }; }; export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAdapter => { const auth = googleAuth(credential); const integrationType = "google_calendar"; return { getAvailability: (dateFrom, dateTo, selectedCalendars) => new Promise((resolve, reject) => auth.getToken().then((myGoogleAuth) => { const calendar = google.calendar({ version: "v3", auth: myGoogleAuth, }); const selectedCalendarIds = selectedCalendars .filter((e) => e.integration === integrationType) .map((e) => e.externalId); if (selectedCalendarIds.length === 0 && selectedCalendars.length > 0) { // Only calendars of other integrations selected resolve([]); return; } (selectedCalendarIds.length === 0 ? calendar.calendarList .list() .then((cals) => cals.data.items?.map((cal) => cal.id).filter(Boolean) || []) : Promise.resolve(selectedCalendarIds) ) .then((calsIds) => { calendar.freebusy.query( { requestBody: { timeMin: dateFrom, timeMax: dateTo, items: calsIds.map((id) => ({ id: id })), }, }, (err, apires) => { if (err) { reject(err); } resolve(Object.values(apires.data.calendars).flatMap((item) => item["busy"])); } ); }) .catch((err) => { console.error("There was an error contacting google calendar service: ", err); reject(err); }); }) ), createEvent: (event: CalendarEvent) => new Promise((resolve, reject) => auth.getToken().then((myGoogleAuth) => { const payload: calendar_v3.Schema$Event = { summary: event.title, description: event.description, start: { dateTime: event.startTime, timeZone: event.organizer.timeZone, }, end: { dateTime: event.endTime, timeZone: event.organizer.timeZone, }, attendees: event.attendees, reminders: { useDefault: false, overrides: [{ method: "email", minutes: 10 }], }, }; if (event.location) { payload["location"] = event.location; } if (event.conferenceData && event.location === "integrations:google:meet") { payload["conferenceData"] = event.conferenceData; } const calendar = google.calendar({ version: "v3", auth: myGoogleAuth, }); calendar.events.insert( { auth: myGoogleAuth, calendarId: "primary", requestBody: payload, conferenceDataVersion: 1, }, function (err, event) { if (err || !event?.data) { console.error("There was an error contacting google calendar service: ", err); return reject(err); } return resolve(event.data); } ); }) ), updateEvent: (uid: string, event: CalendarEvent) => new Promise((resolve, reject) => auth.getToken().then((myGoogleAuth) => { const payload: calendar_v3.Schema$Event = { summary: event.title, description: event.description, start: { dateTime: event.startTime, timeZone: event.organizer.timeZone, }, end: { dateTime: event.endTime, timeZone: event.organizer.timeZone, }, attendees: event.attendees, reminders: { useDefault: true, }, }; if (event.location) { payload["location"] = event.location; } const calendar = google.calendar({ version: "v3", auth: myGoogleAuth, }); calendar.events.update( { auth: myGoogleAuth, calendarId: "primary", eventId: uid, sendNotifications: true, sendUpdates: "all", requestBody: payload, }, function (err, event) { if (err) { console.error("There was an error contacting google calendar service: ", err); return reject(err); } return resolve(event?.data); } ); }) ), deleteEvent: (uid: string) => new Promise((resolve, reject) => auth.getToken().then((myGoogleAuth) => { const calendar = google.calendar({ version: "v3", auth: myGoogleAuth, }); calendar.events.delete( { auth: myGoogleAuth, calendarId: "primary", eventId: uid, sendNotifications: true, sendUpdates: "all", }, function (err, event) { if (err) { console.error("There was an error contacting google calendar service: ", err); return reject(err); } return resolve(event?.data); } ); }) ), listCalendars: () => new Promise((resolve, reject) => auth.getToken().then((myGoogleAuth) => { const calendar = google.calendar({ version: "v3", auth: myGoogleAuth, }); calendar.calendarList .list() .then((cals) => { resolve( cals.data.items?.map((cal) => { const calendar: IntegrationCalendar = { externalId: cal.id ?? "No id", integration: integrationType, name: cal.summary ?? "No name", primary: cal.primary ?? false, }; return calendar; }) || [] ); }) .catch((err) => { console.error("There was an error contacting google calendar service: ", err); reject(err); }); }) ), }; };