Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
21ae55b
feat(unified-cal): connection-based unified calendar API with CRUD, f…
sahitya-chandra Mar 12, 2026
a288fb2
style: apply biome formatting to unified calendar API files
sahitya-chandra Mar 12, 2026
77f649a
fix: use @IsTimeZone() validator for timeZone field in CreateEventDat…
sahitya-chandra Mar 12, 2026
0898c10
fix: add delegation auth support, extract freebusy service layer
sahitya-chandra Mar 12, 2026
7c4ebae
test: add unit and integration tests for unified calendar API
sahitya-chandra Mar 12, 2026
73ae5b2
fix: address Devin Review feedback - fix JSDoc and validator pattern
sahitya-chandra Mar 12, 2026
0c4c44c
fix: revert IsAfterFrom to throw BadRequestException per team convention
sahitya-chandra Mar 12, 2026
29611d9
fix: add calendarId query param to createConnectionEvent for API cons…
sahitya-chandra Mar 12, 2026
e18e462
Update apps/api/v2/src/modules/cal-unified-calendars/controllers/cal-…
sahitya-chandra Mar 12, 2026
7cda999
Revert "Update apps/api/v2/src/modules/cal-unified-calendars/controll…
sahitya-chandra Mar 12, 2026
a0c6e42
feat: enhance calendar service with connection-specific methods and i…
sahitya-chandra Mar 13, 2026
4718c7e
test: complete delegation auth tests, document virtual mocks, fix key…
sahitya-chandra Mar 13, 2026
4f39e2f
fix: add defense-in-depth key stripping in listConnections controller
sahitya-chandra Mar 13, 2026
56413ae
feat: add unified calendar API endpoints for connections and events m…
sahitya-chandra Mar 13, 2026
51c5a0b
fix: add try/catch error handling to CRUD helper methods
sahitya-chandra Mar 13, 2026
a134782
fix: map Google API errors to correct HTTP status codes
sahitya-chandra Mar 13, 2026
3d36df5
fix: preserve upstream Google API status codes in error mapping
sahitya-chandra Mar 13, 2026
721aa75
fix: distinguish Google quota/rate-limit 403 from permission 403
sahitya-chandra Mar 13, 2026
bf1ff03
fix: keep dailyLimitExceeded as 403 (non-retriable quota exhaustion)
sahitya-chandra Mar 13, 2026
f371f17
fix: add missing @ApiQuery decorators for calendarId on get/update/de…
sahitya-chandra Mar 13, 2026
e7d8889
ci: retry flaky vitest worker test
sahitya-chandra Mar 13, 2026
db43cb9
fix: update calendarId query parameter to be optional in OpenAPI spec…
sahitya-chandra Mar 13, 2026
db50eda
Merge branch 'main' into devin/1773319698-unified-calendar-api
sahitya-chandra Mar 13, 2026
1ffef99
fix: swap dual decorator order so plural /events/ path appears in Ope…
sahitya-chandra Mar 13, 2026
7cc78ca
fix: split dual decorators into separate methods so both paths appear…
sahitya-chandra Mar 13, 2026
a5fa1c6
fix: update openapi.json with split dual-decorator paths for GET/PATC…
sahitya-chandra Mar 13, 2026
d445bd0
fix: mapGoogleApiError - coerce string code to number and read errors…
sahitya-chandra Mar 13, 2026
84cffd4
fix: mapGoogleApiError - guard against NaN from non-numeric error codes
sahitya-chandra Mar 13, 2026
122b9bb
fix: use read replica for findCredentialWithDelegationByTypeAndUserId…
sahitya-chandra Mar 13, 2026
f2800f1
refactor: address review comments - UnifiedCalendarService, ParseConn…
sahitya-chandra Mar 16, 2026
48d2d7d
chore: regenerate openapi.json after controller refactor to array syn…
sahitya-chandra Mar 16, 2026
9e79dfb
Merge branch 'main' into devin/1773319698-unified-calendar-api
sahitya-chandra Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion apps/api/v2/src/ee/calendars/services/calendars.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { APPS_TYPE_ID_MAPPING } from "@calcom/platform-constants";
import type { ConnectedDestinationCalendars } from "@calcom/platform-libraries";
import {
type EventBusyDate,
getBusyCalendarTimes,
Expand Down Expand Up @@ -51,7 +52,10 @@ export class CalendarsService {
.filter((credential) => !!credential);
}

async getCalendars(userId: number, ensureDefaultSelectedCalendars = false) {
async getCalendars(
userId: number,
ensureDefaultSelectedCalendars = false
): Promise<ConnectedDestinationCalendars> {
const cachedResult = await this.calendarsCacheService.getConnectedAndDestinationCalendarsCache(userId);

if (cachedResult && !ensureDefaultSelectedCalendars) {
Expand Down Expand Up @@ -79,6 +83,26 @@ export class CalendarsService {
return result;
}

/**
* Delegates to getCalendars() (which is cached by CalendarsCacheService) because the
* upstream platform-libraries API only supports fetching all credentials at once —
* a targeted single-credential query is tracked as a future optimisation.
*/
async getCalendarsForConnection(
userId: number,
credentialId: number
): Promise<ConnectedDestinationCalendars> {
const full = await this.getCalendars(userId);
const conn = full.connectedCalendars.find((c) => c.credentialId === credentialId);
if (!conn) {
throw new NotFoundException("Calendar connection not found");
}
return {
...full,
connectedCalendars: [conn],
};
}

async getBusyTimes(
calendarsToLoad: Calendar[],
userId: User["id"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { Module } from "@nestjs/common";
import { BookingReferencesRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/repositories/booking-references.repository";
import { CalendarsRepository } from "@/ee/calendars/calendars.repository";
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
import { CalendarsService } from "@/ee/calendars/services/calendars.service";
import { CalendarsCacheService } from "@/ee/calendars/services/calendars-cache.service";
import { GoogleCalendarService as GCalService } from "@/ee/calendars/services/gcal.service";
import { AppsRepository } from "@/modules/apps/apps.repository";
import { CalUnifiedCalendarsController } from "@/modules/cal-unified-calendars/controllers/cal-unified-calendars.controller";
import { GoogleCalendarService } from "@/modules/cal-unified-calendars/services/google-calendar.service";
import { UnifiedCalendarService } from "@/modules/cal-unified-calendars/services/unified-calendar.service";
import { UnifiedCalendarsFreebusyService } from "@/modules/cal-unified-calendars/services/unified-calendars-freebusy.service";
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { RedisModule } from "@/modules/redis/redis.module";
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
import { TokensModule } from "@/modules/tokens/tokens.module";
import { UsersRepository } from "@/modules/users/users.repository";
import { Module } from "@nestjs/common";

@Module({
imports: [TokensModule, RedisModule],
providers: [
GCalService,
GoogleCalendarService,
UnifiedCalendarService,
UnifiedCalendarsFreebusyService,
AppsRepository,
BookingReferencesRepository_2024_08_13,
CredentialsRepository,
Expand Down
Loading
Loading