11/* eslint-disable @typescript-eslint/no-explicit-any */
2- import type { calendar_v3 } from "@googleapis/calendar" ;
3- import type { GaxiosResponse } from "googleapis-common" ;
4- import { RRule } from "rrule" ;
2+
53import { MeetLocationType } from "@calcom/app-store/constants" ;
4+ import { getDestinationCalendarRepository } from "@calcom/features/di/containers/DestinationCalendar" ;
65import { getLocation , getRichDescription } from "@calcom/lib/CalEventParser" ;
76import { ORGANIZER_EMAIL_EXEMPT_DOMAINS } from "@calcom/lib/constants" ;
87import logger from "@calcom/lib/logger" ;
98import { safeStringify } from "@calcom/lib/safeStringify" ;
10- import { getDestinationCalendarRepository } from "@calcom/features/di/containers/DestinationCalendar" ;
119import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar" ;
1210import type { Prisma } from "@calcom/prisma/client" ;
1311import type {
1412 Calendar ,
15- CalendarServiceEvent ,
1613 CalendarEvent ,
14+ CalendarServiceEvent ,
1715 EventBusyDate ,
1816 GetAvailabilityParams ,
1917 IntegrationCalendar ,
2018 NewCalendarEventType ,
2119 SelectedCalendarEventTypeIds ,
2220} from "@calcom/types/Calendar" ;
2321import type { CredentialForCalendarServiceWithEmail } from "@calcom/types/Credential" ;
22+ import type { calendar_v3 } from "@googleapis/calendar" ;
23+ import type { GaxiosResponse } from "googleapis-common" ;
24+ import { RRule } from "rrule" ;
2425
2526import { AxiosLikeResponseToFetchResponse } from "../../_utils/oauth/AxiosLikeResponseToFetchResponse" ;
2627import { CalendarAuth } from "./CalendarAuth" ;
@@ -68,7 +69,8 @@ interface GoogleCalError extends Error {
6869 code ?: number ;
6970}
7071
71- const isGaxiosResponse = ( error : unknown ) : error is GaxiosResponse < calendar_v3 . Schema$Event > =>
72+ const isGaxiosResponse = ( error : unknown ) : error is GaxiosResponse < calendar_v3 . Schema$Event > =>
73+ // biome-ignore lint/suspicious/noPrototypeBuiltins: Object.hasOwn not available in all build targets
7274 typeof error === "object" && ! ! error && Object . prototype . hasOwnProperty . call ( error , "config" ) ;
7375
7476class GoogleCalendarService implements Calendar {
@@ -497,9 +499,7 @@ class GoogleCalendarService implements Calendar {
497499 return apiResponse . json ;
498500 }
499501
500- async getFreeBusyResult (
501- args : FreeBusyArgs ,
502- ) : Promise < calendar_v3 . Schema$FreeBusyResponse > {
502+ async getFreeBusyResult ( args : FreeBusyArgs ) : Promise < calendar_v3 . Schema$FreeBusyResponse > {
503503 return await this . fetchAvailability ( args ) ;
504504 }
505505
@@ -516,22 +516,23 @@ class GoogleCalendarService implements Calendar {
516516 return validCals [ 0 ] ;
517517 }
518518
519- async getFreeBusyData (
520- args : FreeBusyArgs ,
521- ) : Promise < ( EventBusyDate & { id : string } ) [ ] | null > {
519+ async getFreeBusyData ( args : FreeBusyArgs ) : Promise < ( EventBusyDate & { id : string } ) [ ] | null > {
522520 const freeBusyResult = await this . getFreeBusyResult ( args ) ;
523521 if ( ! freeBusyResult . calendars ) return null ;
524522
525- const result = Object . entries ( freeBusyResult . calendars ) . reduce ( ( c , [ id , i ] ) => {
526- i . busy ?. forEach ( ( busyTime ) => {
527- c . push ( {
528- id,
529- start : busyTime . start || "" ,
530- end : busyTime . end || "" ,
523+ const result = Object . entries ( freeBusyResult . calendars ) . reduce (
524+ ( c , [ id , i ] ) => {
525+ i . busy ?. forEach ( ( busyTime ) => {
526+ c . push ( {
527+ id,
528+ start : busyTime . start || "" ,
529+ end : busyTime . end || "" ,
530+ } ) ;
531531 } ) ;
532- } ) ;
533- return c ;
534- } , [ ] as ( EventBusyDate & { id : string } ) [ ] ) ;
532+ return c ;
533+ } ,
534+ [ ] as ( EventBusyDate & { id : string } ) [ ]
535+ ) ;
535536
536537 return result ;
537538 }
@@ -646,7 +647,7 @@ class GoogleCalendarService implements Calendar {
646647 private async fetchAvailabilityData (
647648 calendarIds : string [ ] ,
648649 dateFrom : string ,
649- dateTo : string ,
650+ dateTo : string
650651 ) : Promise < EventBusyDate [ ] > {
651652 // More efficient date difference calculation using native Date objects
652653 // Use Math.floor to match dayjs diff behavior (truncates, doesn't round up)
@@ -657,13 +658,11 @@ class GoogleCalendarService implements Calendar {
657658
658659 // Google API only allows a date range of 90 days for /freebusy
659660 if ( diff <= 90 ) {
660- const freeBusyData = await this . getFreeBusyData (
661- {
662- timeMin : dateFrom ,
663- timeMax : dateTo ,
664- items : calendarIds . map ( ( id ) => ( { id } ) ) ,
665- }
666- ) ;
661+ const freeBusyData = await this . getFreeBusyData ( {
662+ timeMin : dateFrom ,
663+ timeMax : dateTo ,
664+ items : calendarIds . map ( ( id ) => ( { id } ) ) ,
665+ } ) ;
667666
668667 if ( ! freeBusyData ) throw new Error ( "No response from google calendar" ) ;
669668 return freeBusyData . map ( ( freeBusy ) => ( { start : freeBusy . start , end : freeBusy . end } ) ) ;
@@ -685,13 +684,11 @@ class GoogleCalendarService implements Calendar {
685684 currentEndTime = originalEndTime ;
686685 }
687686
688- const chunkData = await this . getFreeBusyData (
689- {
690- timeMin : new Date ( currentStartTime ) . toISOString ( ) ,
691- timeMax : new Date ( currentEndTime ) . toISOString ( ) ,
692- items : calendarIds . map ( ( id ) => ( { id } ) ) ,
693- }
694- ) ;
687+ const chunkData = await this . getFreeBusyData ( {
688+ timeMin : new Date ( currentStartTime ) . toISOString ( ) ,
689+ timeMax : new Date ( currentEndTime ) . toISOString ( ) ,
690+ items : calendarIds . map ( ( id ) => ( { id } ) ) ,
691+ } ) ;
695692
696693 if ( chunkData ) {
697694 busyData . push ( ...chunkData . map ( ( freeBusy ) => ( { start : freeBusy . start , end : freeBusy . end } ) ) ) ;
@@ -757,20 +754,36 @@ class GoogleCalendarService implements Calendar {
757754 primary : cal . primary ?? false ,
758755 readOnly : ! ( cal . accessRole === "writer" || cal . accessRole === "owner" ) && true ,
759756 email : cal . id ?? "" ,
760- } satisfies IntegrationCalendar )
757+ } ) satisfies IntegrationCalendar
761758 ) ;
762759 } catch ( error ) {
763760 this . log . error ( "There was an error getting calendars: " , safeStringify ( error ) ) ;
764761 throw error ;
765762 }
766763 }
767764
768- // It would error if the delegation credential is not set up correctly
769- async testDelegationCredentialSetup ( ) {
765+ async testDelegationCredentialSetup ( ) : Promise < void > {
770766 log . debug ( "Testing delegation credential setup" ) ;
771- const calendar = await this . authedCalendar ( ) ;
772- const cals = await calendar . calendarList . list ( { fields : "items(id)" } ) ;
773- return ! ! cals . data . items ;
767+ try {
768+ const calendar = await this . authedCalendar ( ) ;
769+ const cals = await calendar . calendarList . list ( { fields : "items(id)" } ) ;
770+ if ( ! cals . data . items ) {
771+ throw new Error ( "No calendars found - delegation credential may not have proper access" ) ;
772+ }
773+ } catch ( error ) {
774+ const googleError = error as { code ?: number ; message ?: string } ;
775+ if ( googleError . code === 401 ) {
776+ throw new Error (
777+ `Google API authentication failed: ${ googleError . message || "Invalid credentials or insufficient permissions" } `
778+ ) ;
779+ }
780+ if ( googleError . code === 403 ) {
781+ throw new Error (
782+ `Google API access denied: ${ googleError . message || "The service account may not have domain-wide delegation enabled or the required scopes" } `
783+ ) ;
784+ }
785+ throw error ;
786+ }
774787 }
775788
776789 async createSelectedCalendar (
@@ -885,9 +898,7 @@ class GoogleCalendarService implements Calendar {
885898 * from leaking into the emitted .d.ts file, which would cause TypeScript to load
886899 * all Google API SDK declaration files when type-checking dependent packages.
887900 */
888- export default function BuildCalendarService (
889- credential : CredentialForCalendarServiceWithEmail
890- ) : Calendar {
901+ export default function BuildCalendarService ( credential : CredentialForCalendarServiceWithEmail ) : Calendar {
891902 return new GoogleCalendarService ( credential ) ;
892903}
893904
0 commit comments