Skip to content

Commit

Permalink
feat: Add recurring GCal meeting series
Browse files Browse the repository at this point in the history
  • Loading branch information
Dschoordsch committed Jan 24, 2024
1 parent e82ef57 commit 8ebf58f
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 16 deletions.
77 changes: 62 additions & 15 deletions packages/server/graphql/mutations/helpers/createGcalEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,23 @@ import makeAppURL from 'parabol-client/utils/makeAppURL'
import appOrigin from '../../../appOrigin'
import {DataLoaderWorker} from '../../graphql'
import standardError from '../../../utils/standardError'
import {CreateGcalEventInput} from '../../public/resolverTypes'
import {CreateGcalEventInput, StandardMutationError} from '../../public/resolverTypes'

const emailRemindMinsBeforeMeeting = 24 * 60
const popupRemindMinsBeforeMeeting = 10

const convertRruleToGcal = (rrule: string | null | undefined) => {
const recurrence = rrule
? [
rrule
.split('\n')
.filter((line) => !line.startsWith('DTSTART') && !line.startsWith('DTEND'))
.join('\n')
]
: []
return recurrence
}

type Input = {
gcalInput?: CreateGcalEventInput | null
meetingId: string
Expand All @@ -17,10 +29,12 @@ type Input = {
dataLoader: DataLoaderWorker
}

const createGcalEvent = async (input: Input) => {
const createGcalEvent = async (
input: Input
): Promise<{gcalSeriesId?: string; error?: StandardMutationError}> => {
const {gcalInput, meetingId, viewerId, dataLoader, teamId, rrule} = input
if (!gcalInput) {
return {error: null}
return {}
}

const {startTimestamp, endTimestamp, title, timeZone, invitees, videoType} = gcalInput
Expand Down Expand Up @@ -58,15 +72,7 @@ const createGcalEvent = async (input: Input) => {
}
}
: undefined
const recurrence = rrule
? [
rrule
.split('\n')
.filter((line) => !line.startsWith('DTSTART') && !line.startsWith('DTEND'))
.join('\n')
]
: undefined
console.log('GEORG RECURRENCE', recurrence)
const recurrence = convertRruleToGcal(rrule)

const eventInput = {
summary: title,
Expand Down Expand Up @@ -97,13 +103,54 @@ const createGcalEvent = async (input: Input) => {
requestBody: eventInput,
conferenceDataVersion: 1
})
console.log('GEORG EVENT', event)
return {gcalSeriesId: event.data.id ?? undefined}
} catch (err) {
const error = err instanceof Error ? err : new Error('Unable to create event in gcal')
return standardError(error, {userId: viewerId})
}
return {
error: null
}

export type UpdateGcalSeriesInput = {
gcalSeriesId: string
title?: string
rrule: string | null
userId: string
teamId: string
dataLoader: DataLoaderWorker
}
export const updateGcalSeries = async (input: UpdateGcalSeriesInput) => {
const {gcalSeriesId, title, rrule, userId, teamId, dataLoader} = input

const gcalAuth = await dataLoader.get('freshGcalAuth').load({teamId, userId})
if (!gcalAuth) {
return standardError(new Error('Could not retrieve Google Calendar auth'), {userId})
}
const {accessToken: access_token, refreshToken: refresh_token, expiresAt} = gcalAuth
const CLIENT_ID = process.env.GOOGLE_OAUTH_CLIENT_ID
const CLIENT_SECRET = process.env.GOOGLE_OAUTH_CLIENT_SECRET
const REDIRECT_URI = appOrigin

const expiry_date = expiresAt ? expiresAt.getTime() : undefined

const oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI)
oauth2Client.setCredentials({access_token, refresh_token, expiry_date})
const calendar = google.calendar({version: 'v3', auth: oauth2Client})
const recurrence = convertRruleToGcal(rrule)

try {
const event = await calendar.events.patch({
calendarId: 'primary',
eventId: gcalSeriesId,
requestBody: {
recurrence,
summary: title
},
conferenceDataVersion: 1
})
return {gcalSeriesId: event.data.id ?? undefined}
} catch (err) {
const error = err instanceof Error ? err : new Error('Unable to create event in gcal')
return standardError(error, {userId})
}
}

Expand Down
11 changes: 10 additions & 1 deletion packages/server/graphql/public/mutations/startRetrospective.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {IntegrationNotifier} from '../../mutations/helpers/notifications/Integra
import {startNewMeetingSeries} from './updateRecurrenceSettings'
import safeCreateRetrospective from '../../mutations/helpers/safeCreateRetrospective'
import {createMeetingSeriesTitle} from '../../mutations/helpers/createMeetingSeriesTitle'
import getKysely from '../../../postgres/getKysely'

const startRetrospective: MutationResolvers['startRetrospective'] = async (
_source,
Expand Down Expand Up @@ -118,14 +119,22 @@ const startRetrospective: MutationResolvers['startRetrospective'] = async (
}
IntegrationNotifier.startMeeting(dataLoader, meetingId, teamId)
analytics.meetingStarted(viewer, meeting, template)
const {error} = await createGcalEvent({
const {error, gcalSeriesId} = await createGcalEvent({
gcalInput,
meetingId,
teamId,
viewerId,
rrule: meetingSeries?.recurrenceRule,
dataLoader
})
if (meetingSeries && gcalSeriesId) {
const pg = getKysely()
await pg
.updateTable('MeetingSeries')
.set({gcalSeriesId})
.where('id', '=', meetingSeries.id)
.execute()
}
const data = {teamId, meetingId, hasGcalError: !!error?.message}
publish(SubscriptionChannel.TEAM, teamId, 'StartRetrospectiveSuccess', data, subOptions)
return data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import publish from '../../../utils/publish'
import standardError from '../../../utils/standardError'
import {MutationResolvers} from '../resolverTypes'
import {MeetingTypeEnum} from '../../../postgres/types/Meeting'
import {updateGcalSeries} from '../../mutations/helpers/createGcalEvent'

export const startNewMeetingSeries = async (
meeting: {
Expand Down Expand Up @@ -138,6 +139,7 @@ const updateRecurrenceSettings: MutationResolvers['updateRecurrenceSettings'] =

if (meeting.meetingSeriesId) {
const meetingSeries = await dataLoader.get('meetingSeries').loadNonNull(meeting.meetingSeriesId)
const {gcalSeriesId, teamId, facilitatorId} = meetingSeries

if (!recurrenceSettings.rrule) {
await stopMeetingSeries(meetingSeries)
Expand All @@ -146,6 +148,16 @@ const updateRecurrenceSettings: MutationResolvers['updateRecurrenceSettings'] =
await updateMeetingSeries(meetingSeries, recurrenceSettings.rrule)
analytics.recurrenceStarted(viewer, meetingSeries)
}
if (gcalSeriesId) {
await updateGcalSeries({
gcalSeriesId,
title: recurrenceSettings.name ?? undefined,
rrule: recurrenceSettings.rrule?.toString() ?? null,
teamId,
userId: facilitatorId,
dataLoader
})
}

if (recurrenceSettings.name) {
await updateMeetingSeriesQuery({title: recurrenceSettings.name}, meetingSeries.id)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {Client} from 'pg'
import getPgConfig from '../getPgConfig'

export async function up() {
const client = new Client(getPgConfig())
await client.connect()
await client.query(`
ALTER TABLE "MeetingSeries"
ADD COLUMN IF NOT EXISTS "gcalSeriesId" VARCHAR(100);
`)
await client.end()
}

export async function down() {
const client = new Client(getPgConfig())
await client.connect()
await client.query(`
ALTER TABLE "MeetingSeries"
DROP COLUMN IF EXISTS "gcalSeriesId";
`)
await client.end()
}

0 comments on commit 8ebf58f

Please sign in to comment.