Skip to content

feat: add calendar cache status and actions (#22532)#9

Draft
ShashankFC wants to merge 1 commit into
calendar-cache-foundationfrom
introduce-cache-key-overflow
Draft

feat: add calendar cache status and actions (#22532)#9
ShashankFC wants to merge 1 commit into
calendar-cache-foundationfrom
introduce-cache-key-overflow

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Dec 31, 2025

Copy link
Copy Markdown
Collaborator

Test 1rnnn

Summary by CodeRabbit

  • New Features

    • Added cache management capabilities for integrations, including ability to view cache status with last updated timestamp and delete cached data.
  • Documentation

    • Added localization strings for cache management UI elements.
  • Chores

    • Updated development tooling for cron job execution.
    • Added setup script for Google Cloud webhook configuration.

✏️ Tip: You can customize this high-level summary in your review settings.

nn---n*Replicated from [ai-code-review-evaluation/cal.com-coderabbit#11](https://github.com/ai-code-review-evaluation/cal.com-coderabbit/pull/11)*

* feat: add calendar cache status dropdown

- Add updatedAt field to CalendarCache schema with migration
- Create tRPC cacheStatus endpoint for fetching cache timestamps
- Add action dropdown to CalendarSwitch for Google Calendar entries
- Display formatted last updated timestamp in dropdown
- Add placeholder for cache deletion functionality
- Include translation strings for dropdown content

The dropdown only appears for Google Calendar integrations that have
active cache entries and provides cache management options for future
extensibility.

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: resolve Prisma type incompatibilities in repository files

- Remove problematic satisfies clause in selectedCalendar.ts
- Add missing cacheStatus parameter to ConnectedCalendarList component
- Fixes type errors that were preventing CI from passing

Co-Authored-By: zomars@cal.com <zomars@me.com>

* refactor: integrate cache status into connectedCalendars handler

- Remove separate cacheStatus tRPC endpoint as requested
- Return cache status as separate field in connectedCalendars response
- Update UI components to use cache data from connectedCalendars
- Fix Prisma type incompatibilities in repository files

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: resolve Prisma type incompatibilities and fix data flow for cache status

- Fix Prisma.SortOrder usage in membership.ts orderBy clauses
- Remove problematic satisfies clause in selectedCalendar.ts
- Fix TeamSelect type reference in team.ts
- Update SelectedCalendarsSettingsWebWrapper to properly pass cacheStatus data flow

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Discard changes to packages/lib/server/repository/membership.ts

* Discard changes to packages/lib/server/repository/team.ts

* fix: improve calendar cache dropdown with proper formatting and subscription logic

- Fix timestamp HTML entity encoding with interpolation escapeValue: false
- Only show dropdown for subscribed Google calendars (googleChannelId exists)
- Hide delete option when no cache data exists
- Include updatedAt and googleChannelId fields upstream in user repository
- Update data flow to pass subscription status through components

Co-Authored-By: zomars@cal.com <zomars@me.com>

* feat: update SelectedCalendar.updatedAt when Google webhooks trigger cache refresh

- Add updateManyByCredentialId method to SelectedCalendarRepository
- Update fetchAvailabilityAndSetCache to refresh SelectedCalendar timestamps
- Ensure webhook flow updates both CalendarCache and SelectedCalendar records
- Maintain proper timestamp tracking for calendar cache operations

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Add script to automate Tunnelmole webhook setup

Introduces test-gcal-webhooks.sh to start Tunnelmole, extract the public URL, and update GOOGLE_WEBHOOK_URL in the .env file. Handles process management, rate limits, and ensures environment configuration for Google Calendar webhooks.

* Update dev:cron script to use npx tsx

Replaces 'ts-node' with 'npx tsx' in the dev:cron script for running cron-tester.ts, likely to improve compatibility or leverage tsx features.

* Update cache status string and improve CalendarSwitch UI

Renamed 'last_updated' to 'cache_last_updated' in locale file for clarity and updated CalendarSwitch to use the new string. Also added dark mode text color support for cache status display.

* refactor: move cache management to credential-level dropdown with Remove App

- Create CredentialActionsDropdown component consolidating cache and app removal actions
- Add deleteCache tRPC mutation for credential-level cache deletion
- Update connectedCalendars handler to include cacheUpdatedAt at credential level
- Move dropdown from individual CalendarSwitch to credential level in SelectedCalendarsSettingsWebWrapper
- Remove cache-related props from CalendarSwitch component
- Add translation strings for cache management actions
- Consolidate all credential-level actions (cache management + Remove App) in one dropdown

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: remove duplicate translation keys in common.json

- Remove duplicate cache-related keys at lines 51-56
- Keep properly positioned keys later in file
- Addresses GitHub comment from zomars about duplicate keys

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: rename translation key to cache_last_updated

- Address GitHub comment from zomars
- Rename 'last_updated' to 'cache_last_updated' for specificity
- Update usage in CredentialActionsDropdown component

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: remove duplicate last_updated translation key

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: add confirmation dialog for cache deletion and use repository pattern

- Add confirmation dialog for destructive cache deletion action
- Replace direct Prisma calls with CalendarCacheRepository pattern
- Add getCacheStatusByCredentialIds method to repository interface
- Fix import paths for UI components
- Address GitHub review comments from zomars

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Update CredentialActionsDropdown.tsx

* Update common.json

* Update common.json

* fix: remove nested div wrapper to resolve HTML structure error

- Remove wrapping div around DisconnectIntegration component
- Fixes nested <p> tag validation error preventing Remove App functionality
- Maintains existing confirmation dialog patterns

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Fix API handler response termination logic

Removed unnecessary return values after setting status in the integrations API handler. This clarifies response handling and prevents returning the response object when not needed. Resolves "API handler should not return a value, received object".

* fix: 400 is correct error code for computing slot for past booking (#22574)

* fix

* add test

* chore: release v5.5.1

* Refactor credential disconnect to use confirmation dialog

Replaces the DisconnectIntegration component with an inline confirmation dialog for removing app credentials. Adds disconnect mutation logic and updates UI to improve user experience and consistency.

* Set default value for CalendarCache.updatedAt

Added a default value of NOW() for the updatedAt column in the CalendarCache table to ensure existing and future rows have a valid timestamp. Updated the Prisma schema to reflect this change and provide compatibility for legacy data and raw inserts.

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: emrysal <me@alexvanandel.com>
@ShashankFC

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 8, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jan 8, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request introduces cache management functionality for Google Calendar integrations. It adds a new React component for credential actions, backend endpoints for cache deletion, database tracking of cache update timestamps, and integrates cache status into the calendar UI. A TRPC endpoint and handler are created to support cache deletion operations.

Changes

Cohort / File(s) Summary
Localization & Configuration
apps/web/package.json, apps/web/public/static/locales/en/common.json
Updated dev:cron script from ts-node to npx tsx; added seven new localization strings for cache management (status, last updated, delete, success/error messages, confirmation prompts).
New React Component
packages/features/apps/components/CredentialActionsDropdown.tsx
Introduced new client component for credential actions including cache status display, cache deletion, and app disconnection with confirmation dialogs and TRPC mutations.
Calendar Cache Repository
packages/features/calendar-cache/calendar-cache.repository*.ts
Added getCacheStatusByCredentialIds method to interface, mock, and implementation to fetch cache update timestamps grouped by credential ID.
Database Schema & Migration
packages/prisma/schema.prisma, packages/prisma/migrations/20250715160635_add_calendar_cache_updated_at/migration.sql
Added updatedAt column to CalendarCache model with auto-update behavior and corresponding migration with default NOW() timestamp.
Type Updates
packages/lib/getConnectedDestinationCalendars.ts
Expanded UserWithCalendars type to include updatedAt and googleChannelId in both allSelectedCalendars and userLevelSelectedCalendars collections.
Repository Updates
packages/lib/server/repository/selectedCalendar.ts, packages/lib/server/repository/user.ts
Added updateManyByCredentialId method to SelectedCalendar repository; updated findUnlockedUserForSession to select updatedAt and googleChannelId fields.
Calendar Service
packages/app-store/googlecalendar/lib/CalendarService.ts
Added side-effect to update updatedAt timestamps for all SelectedCalendar records after cache refresh via updateManyByCredentialId.
TRPC API Endpoint & Handler
packages/trpc/server/routers/viewer/calendars/_router.tsx, packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts
Added new deleteCache TRPC mutation endpoint that accepts credentialId and delegates to handler which validates access and deletes associated cache entries.
Connected Calendars Enhancement
packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts
Enriched connected calendars response with cacheUpdatedAt field by fetching cache statuses and mapping them to credentials.
UI Integration
packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx
Replaced DisconnectIntegration component with CredentialActionsDropdown to provide unified credential action management including cache operations.
Testing Infrastructure
scripts/test-gcal-webhooks.sh
Added Bash script to manage Tunnelmole (tmole) URL extraction and configure Google Cloud webhooks for testing.

Sequence Diagram(s)

sequenceDiagram
    participant User as User/Client
    participant Component as CredentialActionsDropdown
    participant TRPC as TRPC Mutation
    participant Handler as deleteCacheHandler
    participant Repo as SelectedCalendarRepo
    participant DB as Database

    User->>Component: Click "Delete cached data"
    Component->>Component: Open confirmation dialog
    User->>Component: Confirm deletion
    Component->>TRPC: deleteCacheMutation({ credentialId })
    TRPC->>Handler: Call deleteCacheHandler
    Handler->>DB: Fetch credential by id & userId
    DB-->>Handler: Credential found (access verified)
    Handler->>DB: Delete calendarCache entries
    DB-->>Handler: Deletion complete
    Handler-->>TRPC: { success: true }
    TRPC-->>Component: onSuccess callback
    Component->>Component: Show success toast
    Component->>Component: Trigger onSuccess prop
Loading
sequenceDiagram
    participant Client as Client
    participant ConnectedCalendarsHandler as connectedCalendars Handler
    participant CacheRepo as CalendarCacheRepository
    participant DB as Database

    Client->>ConnectedCalendarsHandler: Fetch connected calendars
    ConnectedCalendarsHandler->>ConnectedCalendarsHandler: Get connectedCalendars
    ConnectedCalendarsHandler->>ConnectedCalendarsHandler: Extract credentialIds
    ConnectedCalendarsHandler->>CacheRepo: getCacheStatusByCredentialIds(credentialIds)
    CacheRepo->>DB: Query calendarCache with groupBy credentialId
    DB-->>CacheRepo: Return max updatedAt per credential
    CacheRepo-->>ConnectedCalendarsHandler: Cache status map
    ConnectedCalendarsHandler->>ConnectedCalendarsHandler: Enrich each calendar with cacheUpdatedAt
    ConnectedCalendarsHandler-->>Client: enrichedConnectedCalendars with cache info
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Cache management hops along!
With timestamps and deletions strong,
Credentials managed with care,
Calendar calendars everywhere! 🗓️
Updates track, old data goes,
Fresh as morning dew that glows!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes: adding calendar cache status display and action controls (delete cache, disconnect integration) to the UI.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @apps/web/package.json:
- Line 11: The dev script "dev:cron" uses "npx tsx" but "tsx" is not present in
devDependencies; update apps/web/package.json by adding "tsx" (choose a suitable
version or caret/latest) to the devDependencies object so the script runs from
the installed package rather than fetching via npx, and then run npm install /
pnpm install to lock it into package-lock or pnpm-lock.

In @packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts:
- Around line 17-26: Replace the generic throw with a TRPCError: import
TRPCError from '@trpc/server' at the top of deleteCache.handler.ts, then change
the error path after the prisma.credential.findFirst call (the credential check)
to throw new TRPCError({ code: 'NOT_FOUND', message: 'Credential not found or
access denied' }) so clients receive proper TRPC structured errors and status
codes.

In @scripts/test-gcal-webhooks.sh:
- Around line 67-71: The sed invocation is macOS-specific (`sed -i ''`) and
breaks on Linux; update the block that modifies GOOGLE_WEBHOOK_URL (references:
ENV_FILE, TUNNEL_URL, the sed command) to use a portable approach: either detect
the OS (via uname) and choose `sed -i ''` on Darwin vs `sed -i` on Linux, or
avoid in-place sed entirely by writing the modified content to a temp file and
atomically moving it back into place (e.g., write sed output to "$ENV_FILE.tmp"
then mv into "$ENV_FILE"); ensure the else branch remains unchanged and that
file permissions/ownership are preserved.
🧹 Nitpick comments (6)
scripts/test-gcal-webhooks.sh (1)

40-52: Consider using _ for the unused loop variable.

The loop variable i is declared but never used. You can use _ to indicate it's intentionally unused:

♻️ Suggested refactor
   # Wait for URL or error
   echo "Waiting for tmole to initialize..."
-  for i in {1..20}; do
+  for _ in {1..20}; do
     if grep -q "$TM_KEYWORD" "$LOG_FILE"; then
       TUNNEL_URL=$(extract_url_from_log)
       break
     fi
     if grep -q "limited to 10 tunnels per hour" "$LOG_FILE"; then
       echo "❌ Rate limit hit: You've used your 10 free tunnels/hour. Sign up at:"
       echo "👉 https://dashboard.tunnelmole.com/upgrade/run-more-tunnels-faster"
       cleanup
     fi
     sleep 0.5
   done
packages/features/apps/components/CredentialActionsDropdown.tsx (2)

88-94: Verify if escapeValue: false is necessary.

Disabling escape value in the translation interpolation (line 93) could be a security concern if the interpolated value were user-controlled. In this case, the timestamp comes from Intl.DateTimeFormat().format() which produces safe output, but it's worth verifying if this option is actually needed.

Is this required by the i18n library you're using? If not, consider removing it:

                   {t("cache_last_updated", {
                     timestamp: new Intl.DateTimeFormat("en-US", {
                       dateStyle: "short",
                       timeStyle: "short",
                     }).format(new Date(cacheUpdatedAt)),
-                    interpolation: { escapeValue: false },
                   })}

42-65: Consider adding loading states to improve UX.

The mutation handlers don't show loading states, which means users get no feedback while the operations are in progress. Consider disabling the confirm buttons and showing a loading indicator during mutations.

💡 Suggested enhancement
       <ConfirmationDialogContent
         variety="danger"
         title={t("delete_cached_data")}
         confirmBtnText={t("yes_delete_cache")}
+        isPending={deleteCacheMutation.isPending}
         onConfirm={() => {
           deleteCacheMutation.mutate({ credentialId });
           setDeleteModalOpen(false);
         }}>
         {t("confirm_delete_cache")}
       </ConfirmationDialogContent>

And similarly for the disconnect modal:

       <ConfirmationDialogContent
         variety="danger"
         title={t("remove_app")}
         confirmBtnText={t("yes_remove_app")}
+        isPending={disconnectMutation.isPending}
         onConfirm={() => {
           disconnectMutation.mutate({ id: credentialId });
           setDisconnectModalOpen(false);
         }}>
         {t("are_you_sure_you_want_to_remove_this_app")}
       </ConfirmationDialogContent>

Note: Check if ConfirmationDialogContent supports an isPending prop. If not, you may need to adjust the implementation accordingly.

Also applies to: 135-150

packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts (1)

27-29: Consider optimizing for the empty credentials case.

When connectedCalendars is empty, the database query on line 29 could be skipped. While the current implementation is correct (an empty array will return empty results), adding a guard would improve efficiency.

⚡ Optional optimization
 const credentialIds = connectedCalendars.map((cal) => cal.credentialId);
-const cacheRepository = new CalendarCacheRepository();
-const cacheStatuses = await cacheRepository.getCacheStatusByCredentialIds(credentialIds);
+
+const cacheStatuses = credentialIds.length > 0 
+  ? await new CalendarCacheRepository().getCacheStatusByCredentialIds(credentialIds)
+  : [];
packages/trpc/server/routers/viewer/calendars/_router.tsx (1)

28-33: LGTM!

The deleteCache route follows the established pattern with proper authentication, input validation, and dynamic handler import.

Optional: Update handler cache type for consistency

For consistency with the existing pattern, consider adding the deleteCache handler to the CalendarsRouterHandlerCache type:

 type CalendarsRouterHandlerCache = {
   connectedCalendars?: typeof import("./connectedCalendars.handler").connectedCalendarsHandler;
   setDestinationCalendar?: typeof import("./setDestinationCalendar.handler").setDestinationCalendarHandler;
+  deleteCache?: typeof import("./deleteCache.handler").deleteCacheHandler;
 };

Note: This is not strictly necessary given the dynamic imports, but maintains type consistency with other routes.

packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts (1)

28-33: Consider adding logging for audit trail.

While the deletion logic is correct, adding logging would help with debugging and auditing cache deletion operations.

📝 Suggested enhancement with logging
+import logger from "@calcom/lib/logger";
+
+const log = logger.getSubLogger({ prefix: ["deleteCache.handler"] });
+
 export const deleteCacheHandler = async ({ ctx, input }: DeleteCacheOptions) => {
   const { user } = ctx;
   const { credentialId } = input;

   const credential = await prisma.credential.findFirst({
     where: {
       id: credentialId,
       userId: user.id,
     },
   });

   if (!credential) {
     throw new Error("Credential not found or access denied");
   }

-  await prisma.calendarCache.deleteMany({
+  const result = await prisma.calendarCache.deleteMany({
     where: { credentialId },
   });

+  log.info(`User ${user.id} deleted ${result.count} cache entries for credential ${credentialId}`);
+
   return { success: true };
 };
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3784421 and cb78692.

📒 Files selected for processing (17)
  • apps/web/package.json
  • apps/web/public/static/locales/en/common.json
  • packages/app-store/googlecalendar/lib/CalendarService.ts
  • packages/features/apps/components/CredentialActionsDropdown.tsx
  • packages/features/calendar-cache/calendar-cache.repository.interface.ts
  • packages/features/calendar-cache/calendar-cache.repository.mock.ts
  • packages/features/calendar-cache/calendar-cache.repository.ts
  • packages/lib/getConnectedDestinationCalendars.ts
  • packages/lib/server/repository/selectedCalendar.ts
  • packages/lib/server/repository/user.ts
  • packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx
  • packages/prisma/migrations/20250715160635_add_calendar_cache_updated_at/migration.sql
  • packages/prisma/schema.prisma
  • packages/trpc/server/routers/viewer/calendars/_router.tsx
  • packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts
  • packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts
  • scripts/test-gcal-webhooks.sh
🧰 Additional context used
🧬 Code graph analysis (4)
packages/lib/getConnectedDestinationCalendars.ts (1)
packages/types/Calendar.d.ts (1)
  • SelectedCalendar (318-321)
packages/trpc/server/routers/viewer/calendars/_router.tsx (1)
packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts (1)
  • deleteCacheHandler (13-33)
packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts (1)
scripts/prepare-local-for-delegation-credentials-testing.js (1)
  • user (15-17)
packages/features/apps/components/CredentialActionsDropdown.tsx (1)
packages/ui/components/dropdown/Dropdown.tsx (5)
  • Dropdown (12-12)
  • DropdownMenuTrigger (15-26)
  • DropdownMenuContent (34-51)
  • DropdownMenuItem (63-71)
  • DropdownItem (161-181)
🪛 Shellcheck (0.11.0)
scripts/test-gcal-webhooks.sh

[warning] 41-41: i appears unused. Verify use (or export if used externally).

(SC2034)

🔇 Additional comments (20)
packages/lib/server/repository/selectedCalendar.ts (2)

260-260: LGTM!

Simplified the method by passing the query object directly to Prisma instead of constructing an intermediate typed object. This is cleaner and maintains the same functionality.


400-405: LGTM!

The new updateManyByCredentialId method is well-implemented and follows existing repository patterns. It correctly uses Prisma's updateMany for bulk updates of SelectedCalendar records by credential ID.

apps/web/public/static/locales/en/common.json (1)

3382-3388: LGTM!

The new cache management localization strings are well-structured and follow existing conventions. They cover the necessary UI elements for cache status display, deletion actions, and user feedback messages.

packages/prisma/schema.prisma (1)

1718-1719: LGTM! Standard Prisma timestamp tracking.

The addition of updatedAt with @default(now()) and @updatedAt follows best practices for tracking when cache entries are modified. The default value ensures legacy rows are properly handled.

packages/lib/server/repository/user.ts (1)

894-901: LGTM! Field additions align with cache status requirements.

The addition of updatedAt and googleChannelId to the selectedCalendars select provides the necessary data for the UI to display cache status information. This change is consistent with the broader cache management feature introduced in this PR.

packages/app-store/googlecalendar/lib/CalendarService.ts (1)

1022-1024: The code is correct. CalendarCache.updatedAt and SelectedCalendar.updatedAt serve distinct purposes and are not redundant:

  • CalendarCache.updatedAt: Tracks when availability cache data is refreshed; used in the API response as cacheUpdatedAt to indicate cache currency
  • SelectedCalendar.updatedAt: Tracks when calendar metadata/configuration changes; returned in the client API response as part of the user's selected calendars

The empty object {} passed to updateManyByCredentialId() intentionally triggers Prisma's @updatedAt decorator to update the timestamp without modifying other fields. This dual-timestamp approach is necessary because the two models track different concerns at different levels.

Likely an incorrect or invalid review comment.

packages/prisma/migrations/20250715160635_add_calendar_cache_updated_at/migration.sql (1)

7-9: LGTM! Migration is safe for existing data.

The DEFAULT NOW() ensures existing rows receive a timestamp value when the column is added, avoiding any data migration issues.

packages/features/calendar-cache/calendar-cache.repository.mock.ts (1)

27-30: LGTM! Mock implementation follows established patterns.

The method correctly returns an empty array and logs the skip message, consistent with other mock methods in this class.

packages/trpc/server/routers/viewer/calendars/connectedCalendars.handler.ts (4)

1-1: LGTM! Import is correctly added for the new cache status feature.


31-31: LGTM! Efficient use of Map for O(1) lookups during enrichment.


33-36: LGTM! Clean enrichment pattern that preserves immutability.

The spread operator ensures all existing calendar properties are retained, and the fallback to null correctly handles credentials without cache entries.


38-41: LGTM! Return statement correctly uses the enriched calendars.

packages/features/calendar-cache/calendar-cache.repository.interface.ts (1)

30-32: LGTM! Method signature correctly models cache status data.

The return type appropriately accounts for missing cache entries by allowing updatedAt to be null.

packages/features/calendar-cache/calendar-cache.repository.ts (1)

173-188: LGTM! Implementation correctly uses Prisma groupBy for efficient aggregation.

The method returns the maximum updatedAt per credential. Note that credentials without any cache entries won't appear in the results, which is correctly handled by the caller using a Map with fallback to null.

packages/trpc/server/routers/viewer/calendars/_router.tsx (1)

1-2: LGTM!

The zod import is appropriately added to support the inline input schema for the deleteCache route.

packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts (1)

1-11: LGTM!

The imports and type definition are clean and appropriate for the handler's requirements.

packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx (3)

5-5: LGTM!

The import correctly references the new CredentialActionsDropdown component that consolidates cache and disconnect actions.


70-79: LGTM!

The replacement of the previous disconnect-only component with CredentialActionsDropdown is implemented consistently in both locations (AppListCard and Alert). All required props are correctly passed, enabling both cache management and disconnect functionality.

Also applies to: 123-132


98-98: LGTM!

The explicit || null coercion ensures type consistency, preventing undefined from being passed when delegationCredentialId is not present.

packages/lib/getConnectedDestinationCalendars.ts (1)

21-28: LGTM!

The type extensions to UserWithCalendars correctly add updatedAt and googleChannelId fields to support cache status tracking and Google Calendar webhook management, aligning with the PR's broader calendar cache functionality.

Comment thread apps/web/package.json
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf .next",
"dev": "yarn copy-static && next dev --turbopack",
"dev:cron": "ts-node cron-tester.ts",
"dev:cron": "npx tsx cron-tester.ts",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if tsx is available in package.json or workspace dependencies
echo "=== Checking for tsx in apps/web/package.json ==="
cat apps/web/package.json | jq '.dependencies.tsx // .devDependencies.tsx // "NOT FOUND"'

echo -e "\n=== Checking for tsx in workspace root ==="
if [ -f "package.json" ]; then
  cat package.json | jq '.dependencies.tsx // .devDependencies.tsx // "NOT FOUND"'
fi

echo -e "\n=== Checking if tsx is used elsewhere ==="
rg -n "\"tsx\"" --type json -g 'package.json'

Repository: AI-Code-Review-Evals/coderabbit-cal_dot_com

Length of output: 247


🏁 Script executed:

#!/bin/bash
# Read the apps/web/package.json to see the full dependencies structure
echo "=== Full apps/web/package.json ==="
cat apps/web/package.json

echo -e "\n=== Checking all dependency fields for tsx ==="
cat apps/web/package.json | jq 'keys | .[]' | grep -i depend

Repository: AI-Code-Review-Evals/coderabbit-cal_dot_com

Length of output: 7150


Add tsx to devDependencies.

The script uses npx tsx but tsx is not listed in devDependencies. While npx will download it on-demand, this adds unnecessary network dependency and startup delay each time the script runs. Add tsx to devDependencies to ensure consistent, offline-ready builds.

🤖 Prompt for AI Agents
In @apps/web/package.json at line 11, The dev script "dev:cron" uses "npx tsx"
but "tsx" is not present in devDependencies; update apps/web/package.json by
adding "tsx" (choose a suitable version or caret/latest) to the devDependencies
object so the script runs from the installed package rather than fetching via
npx, and then run npm install / pnpm install to lock it into package-lock or
pnpm-lock.

Comment on lines +17 to +26
const credential = await prisma.credential.findFirst({
where: {
id: credentialId,
userId: user.id,
},
});

if (!credential) {
throw new Error("Credential not found or access denied");
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use TRPCError for proper error handling.

The handler throws a generic Error, which doesn't follow TRPC best practices and won't provide proper HTTP status codes or structured error responses to clients.

🔧 Proposed fix using TRPCError

Add the TRPCError import:

 import { prisma } from "@calcom/prisma";
 import type { TrpcSessionUser } from "@calcom/trpc/server/types";
+import { TRPCError } from "@trpc/server";

Then update the error handling:

   if (!credential) {
-    throw new Error("Credential not found or access denied");
+    throw new TRPCError({
+      code: "NOT_FOUND",
+      message: "Credential not found or access denied",
+    });
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const credential = await prisma.credential.findFirst({
where: {
id: credentialId,
userId: user.id,
},
});
if (!credential) {
throw new Error("Credential not found or access denied");
}
const credential = await prisma.credential.findFirst({
where: {
id: credentialId,
userId: user.id,
},
});
if (!credential) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Credential not found or access denied",
});
}
🤖 Prompt for AI Agents
In @packages/trpc/server/routers/viewer/calendars/deleteCache.handler.ts around
lines 17 - 26, Replace the generic throw with a TRPCError: import TRPCError from
'@trpc/server' at the top of deleteCache.handler.ts, then change the error path
after the prisma.credential.findFirst call (the credential check) to throw new
TRPCError({ code: 'NOT_FOUND', message: 'Credential not found or access denied'
}) so clients receive proper TRPC structured errors and status codes.

Comment on lines +67 to +71
if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
else
echo "GOOGLE_WEBHOOK_URL=$TUNNEL_URL" >> "$ENV_FILE"
fi

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix portability issue with sed -i command.

Line 68 uses sed -i '' which is macOS-specific syntax and will fail on Linux. The script should either detect the OS or use a more portable approach.

🔧 Portable fix

Option 1: Detect OS and use appropriate syntax:

 if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
-  sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
+  if [[ "$OSTYPE" == "darwin"* ]]; then
+    sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
+  else
+    sed -i -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
+  fi
 else
   echo "GOOGLE_WEBHOOK_URL=$TUNNEL_URL" >> "$ENV_FILE"
 fi

Option 2: Use a more portable approach with a temp file:

 if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
-  sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
+  sed -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE"
 else
   echo "GOOGLE_WEBHOOK_URL=$TUNNEL_URL" >> "$ENV_FILE"
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
else
echo "GOOGLE_WEBHOOK_URL=$TUNNEL_URL" >> "$ENV_FILE"
fi
if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
else
sed -i -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
fi
else
echo "GOOGLE_WEBHOOK_URL=$TUNNEL_URL" >> "$ENV_FILE"
fi
Suggested change
if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
sed -i '' -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE"
else
echo "GOOGLE_WEBHOOK_URL=$TUNNEL_URL" >> "$ENV_FILE"
fi
if grep -q '^GOOGLE_WEBHOOK_URL=' "$ENV_FILE"; then
sed -E "s|^GOOGLE_WEBHOOK_URL=.*|GOOGLE_WEBHOOK_URL=$TUNNEL_URL|" "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE"
else
echo "GOOGLE_WEBHOOK_URL=$TUNNEL_URL" >> "$ENV_FILE"
fi
🤖 Prompt for AI Agents
In @scripts/test-gcal-webhooks.sh around lines 67 - 71, The sed invocation is
macOS-specific (`sed -i ''`) and breaks on Linux; update the block that modifies
GOOGLE_WEBHOOK_URL (references: ENV_FILE, TUNNEL_URL, the sed command) to use a
portable approach: either detect the OS (via uname) and choose `sed -i ''` on
Darwin vs `sed -i` on Linux, or avoid in-place sed entirely by writing the
modified content to a temp file and atomically moving it back into place (e.g.,
write sed output to "$ENV_FILE.tmp" then mv into "$ENV_FILE"); ensure the else
branch remains unchanged and that file permissions/ownership are preserved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants